diff --git a/.github/actions/live-tests-shared/action.yml b/.github/actions/live-tests-shared/action.yml index 6021811c29..a84bad475a 100644 --- a/.github/actions/live-tests-shared/action.yml +++ b/.github/actions/live-tests-shared/action.yml @@ -14,17 +14,17 @@ runs: max-attempts: 60 polling-interval-ms: 10000 - name: Setup Node - uses: actions/setup-node@v3 + uses: actions/setup-node@v4 with: node-version: '20' - name: Build o1js and execute tests env: TEST_TYPE: 'Live integration tests' - USE_LOCAL_NETWORK: 'true' + USE_CUSTOM_LOCAL_NETWORK: 'true' run: | git submodule update --init --recursive npm ci - npm run build:node + npm run build touch profiling.md sh run-ci-tests.sh cat profiling.md >> $GITHUB_STEP_SUMMARY diff --git a/.github/workflows/build-action.yml b/.github/workflows/build-action.yml index f1675138de..43cea697ff 100644 --- a/.github/workflows/build-action.yml +++ b/.github/workflows/build-action.yml @@ -27,9 +27,9 @@ jobs: ] steps: - name: Checkout repository - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Setup Node - uses: actions/setup-node@v3 + uses: actions/setup-node@v4 with: node-version: '18' - name: Build o1js and execute tests @@ -39,7 +39,7 @@ jobs: run: | git submodule update --init --recursive npm ci - npm run build:node + npm run build touch profiling.md sh run-ci-tests.sh cat profiling.md >> $GITHUB_STEP_SUMMARY @@ -49,9 +49,9 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout repository - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Setup Node - uses: actions/setup-node@v3 + uses: actions/setup-node@v4 with: node-version: '18' - name: Install Node dependencies @@ -82,16 +82,16 @@ jobs: needs: [Build-And-Test-Server, Build-And-Test-Web] steps: - name: Checkout repository - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Setup Node - uses: actions/setup-node@v3 + uses: actions/setup-node@v4 with: node-version: '18' - name: Build o1js run: | git submodule update --init --recursive npm ci - npm run build:node + npm run build - name: Publish to NPM if version has changed uses: JS-DevTools/npm-publish@v1 if: github.ref == 'refs/heads/main' @@ -106,9 +106,9 @@ jobs: needs: [Build-And-Test-Server, Build-And-Test-Web] steps: - name: Checkout repository - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Setup Node - uses: actions/setup-node@v3 + uses: actions/setup-node@v4 with: node-version: '18' - name: Build mina-signer diff --git a/.github/workflows/live-tests.yml b/.github/workflows/live-tests.yml index deb6ad2946..d5d00b7615 100644 --- a/.github/workflows/live-tests.yml +++ b/.github/workflows/live-tests.yml @@ -1,4 +1,4 @@ -name: Test o1js against real network +name: Test o1js against the real network on: push: branches: @@ -19,7 +19,7 @@ jobs: if: github.ref == 'refs/heads/main' || (github.event_name == 'pull_request' && github.base_ref == 'main') services: mina-local-network: - image: o1labs/mina-local-network:rampup-latest-lightnet + image: o1labs/mina-local-network:o1js-main-latest-lightnet env: NETWORK_TYPE: 'single-node' PROOF_LEVEL: 'none' @@ -32,11 +32,11 @@ jobs: - /tmp:/root/logs steps: - name: Checkout repository - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Use shared steps for live testing jobs uses: ./.github/actions/live-tests-shared with: - mina-branch-name: rampup + mina-branch-name: o1js-main berkeley-branch: timeout-minutes: 25 @@ -57,7 +57,7 @@ jobs: - /tmp:/root/logs steps: - name: Checkout repository - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Use shared steps for live testing jobs uses: ./.github/actions/live-tests-shared with: @@ -82,7 +82,7 @@ jobs: - /tmp:/root/logs steps: - name: Checkout repository - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Use shared steps for live testing jobs uses: ./.github/actions/live-tests-shared with: diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 0000000000..a3aa4226dd --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,73 @@ +# Purpose: +# Automatically bumps the project's patch version bi-weekly on Tuesdays. +# +# Details: +# - Triggered at 00:00 UTC every Tuesday; runs on even weeks of the year. +# - Sets up the environment by checking out the repo and setting up Node.js. +# - Bumps patch version using `npm version patch`, then creates a new branch 'release/x.x.x'. +# - Pushes changes and creates a PR to `main` using GitHub CLI. +# - Can also be triggered manually via `workflow_dispatch`. +name: Version Bump + +on: + workflow_dispatch: # Allow to manually trigger the workflow + schedule: + - cron: "0 0 * * 2" # At 00:00 UTC every Tuesday + +jobs: + version-bump: + runs-on: ubuntu-latest + + steps: + # Since cronjob syntax doesn't support bi-weekly schedule, we need to check if it's an even week or not + - name: Check if it's an even week + run: | + WEEK_NUM=$(date +'%V') + if [ $((WEEK_NUM % 2)) -eq 0 ]; then + echo "RUN_JOB=true" >> $GITHUB_ENV + else + echo "RUN_JOB=false" >> $GITHUB_ENV + fi + + - name: Check out the repository + uses: actions/checkout@v4 + + - name: Set up Node.js + uses: actions/setup-node@v4 + with: + node-version: "18" + + - name: Configure Git + run: | + git config --local user.email "action@github.com" + git config --local user.name "GitHub Action" + + - name: Bump patch version + if: ${{ env.RUN_JOB }} == 'true' + run: | + git fetch --prune --unshallow + NEW_VERSION=$(npm version patch) + echo "New version: $NEW_VERSION" + echo "NEW_VERSION=$NEW_VERSION" >> $GITHUB_ENV + + - name: Install npm dependencies + if: ${{ env.RUN_JOB }} == 'true' + run: npm install + + - name: Update CHANGELOG.md + if: ${{ env.RUN_JOB }} == 'true' + run: | + npm run update-changelog + git add CHANGELOG.md + git commit -m "Update CHANGELOG for new version $NEW_VERSION" + + - name: Create new release branch + if: ${{ env.RUN_JOB }} == 'true' + run: | + NEW_BRANCH="release/${NEW_VERSION}" + git checkout -b $NEW_BRANCH + git push -u origin $NEW_BRANCH + git push --tags + gh pr create --base main --head $NEW_BRANCH --title "Release $NEW_VERSION" --body "This is an automated PR to update to version $NEW_VERSION" + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN}} diff --git a/CHANGELOG.md b/CHANGELOG.md index b55c81885b..2b30d0e852 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,11 +13,142 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm _Removed_ for now removed features. _Fixed_ for any bug fixes. _Security_ in case of vulnerabilities. + --> +## [Unreleased](https://github.com/o1-labs/o1js/compare/19115a159...HEAD) - --> +## [0.15.1](https://github.com/o1-labs/o1js/compare/1ad7333e9e...19115a159) + +### Breaking changes + +- Rename `Gadgets.rotate()` to `Gadgets.rotate64()` to better reflect the amount of bits the gadget operates on. https://github.com/o1-labs/o1js/pull/1259 +- Rename `Gadgets.{leftShift(), rightShift()}` to `Gadgets.{leftShift64(), rightShift64()}` to better reflect the amount of bits the gadget operates on. https://github.com/o1-labs/o1js/pull/1259 + +### Added + +- Non-native elliptic curve operations exposed through `createForeignCurve()` class factory https://github.com/o1-labs/o1js/pull/1007 +- **ECDSA signature verification** exposed through `createEcdsa()` class factory https://github.com/o1-labs/o1js/pull/1240 https://github.com/o1-labs/o1js/pull/1007 https://github.com/o1-labs/o1js/pull/1307 + - For an example, see `./src/examples/crypto/ecdsa` +- **Keccak/SHA3 hash function** exposed on `Keccak` namespace https://github.com/o1-labs/o1js/pull/1291 +- `Hash` namespace which holds all hash functions https://github.com/o1-labs/o1js/pull/999 + - `Bytes`, provable type to hold a byte array, which serves as input and output for Keccak variants + - `UInt8`, provable type to hold a single byte, which is constrained to be in the 0 to 255 range +- `Gadgets.rotate32()` for rotation over 32 bit values https://github.com/o1-labs/o1js/pull/1259 +- `Gadgets.leftShift32()` for left shift over 32 bit values https://github.com/o1-labs/o1js/pull/1259 +- `Gadgets.divMod32()` division modulo 2^32 that returns the remainder and quotient of the operation https://github.com/o1-labs/o1js/pull/1259 +- `Gadgets.rangeCheck32()` range check for 32 bit values https://github.com/o1-labs/o1js/pull/1259 +- `Gadgets.addMod32()` addition modulo 2^32 https://github.com/o1-labs/o1js/pull/1259 +- Expose new bitwise gadgets on `UInt32` and `UInt64` https://github.com/o1-labs/o1js/pull/1259 + - bitwise XOR via `{UInt32, UInt64}.xor()` + - bitwise NOT via `{UInt32, UInt64}.not()` + - bitwise ROTATE via `{UInt32, UInt64}.rotate()` + - bitwise LEFTSHIFT via `{UInt32, UInt64}.leftShift()` + - bitwise RIGHTSHIFT via `{UInt32, UInt64}.rightShift()` + - bitwise AND via `{UInt32, UInt64}.and()` +- Example for using actions to store a map data structure https://github.com/o1-labs/o1js/pull/1300 +- `Provable.constraintSystem()` and `{ZkProgram,SmartContract}.analyzeMethods()` return a `summary()` method to return a summary of the constraints used by a method https://github.com/o1-labs/o1js/pull/1007 + +### Fixed + +- Fix stack overflows when calling provable methods with large inputs https://github.com/o1-labs/o1js/pull/1334 +- Fix `Local.setProofsEnabled()` which would not get picked up by `deploy()` https://github.com/o1-labs/o1js/pull/1330 +- Remove usage of private class fields in core types like `Field`, for better type compatibility between different o1js versions https://github.com/o1-labs/o1js/pull/1319 + +## [0.15.0](https://github.com/o1-labs/o1js/compare/1ad7333e9e...7acf19d0d) + +### Breaking changes + +- `ZkProgram.compile()` now returns the verification key and its hash, to be consistent with `SmartContract.compile()` https://github.com/o1-labs/o1js/pull/1292 [@rpanic](https://github.com/rpanic) + +### Added + +- **Foreign field arithmetic** exposed through the `createForeignField()` class factory https://github.com/o1-labs/snarkyjs/pull/985 +- `Crypto` namespace which exposes elliptic curve and finite field arithmetic on bigints, as well as example curve parameters https://github.com/o1-labs/o1js/pull/1240 +- `Gadgets.ForeignField.assertMul()` for efficiently constraining products of sums in non-native arithmetic https://github.com/o1-labs/o1js/pull/1262 +- `Unconstrained` for safely maintaining unconstrained values in provable code https://github.com/o1-labs/o1js/pull/1262 +- `Gadgets.rangeCheck8()` to assert that a value fits in 8 bits https://github.com/o1-labs/o1js/pull/1288 + +### Changed + +- Change precondition APIs to use "require" instead of "assert" as the verb, to distinguish them from provable assertions. [@LuffySama-Dev](https://github.com/LuffySama-Dev) + - `this.x.getAndAssertEquals()` is now `this.x.getAndRequireEquals()` https://github.com/o1-labs/o1js/pull/1263 + - `this.x.assertEquals(x)` is now `this.x.requireEquals(x)` https://github.com/o1-labs/o1js/pull/1263 + - `this.account.x.getAndAssertEquals(x)` is now `this.account.x.requireEquals(x)` https://github.com/o1-labs/o1js/pull/1265 + - `this.account.x.assertBetween()` is now `this.account.x.requireBetween()` https://github.com/o1-labs/o1js/pull/1265 + - `this.network.x.getAndAssertEquals()` is now `this.network.x.getAndRequireEquals()` https://github.com/o1-labs/o1js/pull/1265 +- `Provable.constraintSystem()` and `{ZkProgram,SmartContract}.analyzeMethods()` return a `print()` method for pretty-printing the constraint system https://github.com/o1-labs/o1js/pull/1240 + +### Fixed + +- Fix missing recursive verification of proofs in smart contracts https://github.com/o1-labs/o1js/pull/1302 + +## [0.14.2](https://github.com/o1-labs/o1js/compare/26363465d...1ad7333e9e) + +### Breaking changes + +- Change return signature of `ZkProgram.analyzeMethods()` to be a keyed object https://github.com/o1-labs/o1js/pull/1223 + +### Added + +- Provable non-native field arithmetic: + - `Gadgets.ForeignField.{add, sub, sumchain}()` for addition and subtraction https://github.com/o1-labs/o1js/pull/1220 + - `Gadgets.ForeignField.{mul, inv, div}()` for multiplication and division https://github.com/o1-labs/o1js/pull/1223 +- Comprehensive internal testing of constraint system layouts generated by new gadgets https://github.com/o1-labs/o1js/pull/1241 https://github.com/o1-labs/o1js/pull/1220 + +### Changed + +- `Lightnet` namespace API updates with added `listAcquiredKeyPairs()` method https://github.com/o1-labs/o1js/pull/1256 +- Expose raw provable methods of a `ZkProgram` on `zkProgram.rawMethods` https://github.com/o1-labs/o1js/pull/1241 +- Reduce number of constraints needed by `rotate()`, `leftShift()` and, `rightShift()` gadgets https://github.com/o1-labs/o1js/pull/1201 + +### Fixed + +- Add a parameter to `checkZkappTransaction` for block length to check for transaction inclusion. This fixes a case where `Transaction.wait()` only checked the latest block, which led to an error once the transaction was included in a block that was not the latest. https://github.com/o1-labs/o1js/pull/1239 + +## [0.14.1](https://github.com/o1-labs/o1js/compare/e8e7510e1...26363465d) + +### Added + +- `Gadgets.not()`, new provable method to support bitwise not. https://github.com/o1-labs/o1js/pull/1198 +- `Gadgets.leftShift() / Gadgets.rightShift()`, new provable methods to support bitwise shifting. https://github.com/o1-labs/o1js/pull/1194 +- `Gadgets.and()`, new provable method to support bitwise and. https://github.com/o1-labs/o1js/pull/1193 +- `Gadgets.multiRangeCheck()` and `Gadgets.compactMultiRangeCheck()`, two building blocks for non-native arithmetic with bigints of size up to 264 bits. https://github.com/o1-labs/o1js/pull/1216 + +### Fixed + +- Removed array reversal of fetched actions, since they are returned in the correct order. https://github.com/o1-labs/o1js/pull/1258 + +## [0.14.0](https://github.com/o1-labs/o1js/compare/045faa7...e8e7510e1) + +### Breaking changes + +- Constraint optimizations in Field methods and core crypto changes break all verification keys https://github.com/o1-labs/o1js/pull/1171 https://github.com/o1-labs/o1js/pull/1178 + +### Changed + +- `ZkProgram` has moved out of the `Experimental` namespace and is now available as a top-level import directly. `Experimental.ZkProgram` has been deprecated. +- `ZkProgram` gets a new input argument `name: string` which is required in the non-experimental API. The name is used to identify a ZkProgram when caching prover keys. https://github.com/o1-labs/o1js/pull/1200 + +### Added + +- `Lightnet` namespace to interact with the account manager provided by the [lightnet Mina network](https://hub.docker.com/r/o1labs/mina-local-network) https://github.com/o1-labs/o1js/pull/1167 +- Internal support for several custom gates (range check, bitwise operations, foreign field operations) and lookup tables https://github.com/o1-labs/o1js/pull/1176 +- `Gadgets.rangeCheck64()`, new provable method to do efficient 64-bit range checks using lookup tables https://github.com/o1-labs/o1js/pull/1181 +- `Gadgets.rotate()`, new provable method to support bitwise rotation for native field elements. https://github.com/o1-labs/o1js/pull/1182 +- `Gadgets.xor()`, new provable method to support bitwise xor for native field elements. https://github.com/o1-labs/o1js/pull/1177 +- `Proof.dummy()` to create dummy proofs https://github.com/o1-labs/o1js/pull/1188 + - You can use this to write ZkPrograms that handle the base case and the inductive case in the same method. + +### Changed -## [Unreleased](https://github.com/o1-labs/o1js/compare/045faa7...HEAD) +- Use cached prover keys in `compile()` when running in Node.js https://github.com/o1-labs/o1js/pull/1187 + - Caching is configurable by passing a custom `Cache` (new export) to `compile()` + - By default, prover keys are stored in an OS-dependent cache directory; `~/.cache/pickles` on Mac and Linux +- Use cached setup points (SRS and Lagrange bases) when running in Node.js https://github.com/o1-labs/o1js/pull/1197 + - Also, speed up SRS generation by using multiple threads + - Together with caching of prover keys, this speeds up compilation time by roughly + - **86%** when everything is cached + - **34%** when nothing is cached ## [0.13.1](https://github.com/o1-labs/o1js/compare/c2f392fe5...045faa7) @@ -31,6 +162,7 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm - Improve prover performance by ~25% https://github.com/o1-labs/o1js/pull/1092 - Change internal representation of field elements to be JS bigint instead of Uint8Array +- Consolidate internal framework for testing equivalence of two implementations ## [0.13.0](https://github.com/o1-labs/o1js/compare/fbd4b2717...c2f392fe5) diff --git a/CODEOWNERS b/CODEOWNERS new file mode 100644 index 0000000000..d878a07b61 --- /dev/null +++ b/CODEOWNERS @@ -0,0 +1 @@ +/src/lib/gadgets @o1-labs/crypto-eng-reviewers @mitschabaude diff --git a/README-dev.md b/README-dev.md index 7f1446cc65..24de5f1c4e 100644 --- a/README-dev.md +++ b/README-dev.md @@ -144,3 +144,41 @@ act -j Build-And-Test-Server --matrix test_type:"Simple integration tests" -s $G ### Releasing To release a new version of o1js, you must first update the version number in `package.json`. Then, you can create a new pull request to merge your changes into the main branch. Once the pull request is merged, a CI job will automatically publish the new version to npm. + +## Test zkApps against the local blockchain network + +In order to be able to test zkApps against the local blockchain network, you need to spin up such a network first. +You can do so in several ways. + +1. Using [zkapp-cli](https://www.npmjs.com/package/zkapp-cli)'s sub commands: + + ```shell + zk lightnet start # start the local network + # Do your tests and other interactions with the network + zk lightnet logs # manage the logs of the local network + zk lightnet explorer # visualize the local network state + zk lightnet stop # stop the local network + ``` + + Please refer to `zk lightnet --help` for more information. + +2. Using the corresponding [Docker image](https://hub.docker.com/r/o1labs/mina-local-network) manually: + + ```shell + docker run --rm --pull=missing -it \ + --env NETWORK_TYPE="single-node" \ + --env PROOF_LEVEL="none" \ + --env LOG_LEVEL="Trace" \ + -p 3085:3085 \ + -p 5432:5432 \ + -p 8080:8080 \ + -p 8181:8181 \ + -p 8282:8282 \ + o1labs/mina-local-network:o1js-main-latest-lightnet + ``` + + Please refer to the [Docker Hub repository](https://hub.docker.com/r/o1labs/mina-local-network) for more information. + +Next up, you will need the Mina blockchain accounts information in order to be used in your zkApp. +Once the local network is up and running, you can use the [Lightnet](https://github.com/o1-labs/o1js/blob/ec789794b2067addef6b6f9c9a91c6511e07e37c/src/lib/fetch.ts#L1012) `o1js API namespace` to get the accounts information. +The corresponding example can be found here: [src/examples/zkapps/hello_world/run_live.ts](https://github.com/o1-labs/o1js/blob/ec789794b2067addef6b6f9c9a91c6511e07e37c/src/examples/zkapps/hello_world/run_live.ts) diff --git a/package-lock.json b/package-lock.json index 49d8af9547..24af729ad1 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,15 +1,16 @@ { "name": "o1js", - "version": "0.13.1", - "lockfileVersion": 2, + "version": "0.15.1", + "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "o1js", - "version": "0.13.1", + "version": "0.15.1", "license": "Apache-2.0", "dependencies": { "blakejs": "1.2.1", + "cachedir": "^2.4.0", "detect-gpu": "^5.0.5", "isomorphic-fetch": "^3.0.0", "js-sha256": "^0.9.0", @@ -20,6 +21,7 @@ "snarky-run": "src/build/run.js" }, "devDependencies": { + "@noble/hashes": "^1.3.2", "@playwright/test": "^1.25.2", "@types/isomorphic-fetch": "^0.0.36", "@types/jest": "^27.0.0", @@ -46,13 +48,22 @@ "node": ">=16.4.0" } }, + "node_modules/@aashutoshrathi/word-wrap": { + "version": "1.2.6", + "resolved": "https://registry.npmjs.org/@aashutoshrathi/word-wrap/-/word-wrap-1.2.6.tgz", + "integrity": "sha512-1Yjs2SvM8TflER/OD3cOjhWWOZb58A2t7wpE2S9XfBYTiIl+XFhQG2bjy4Pu1I+EAlCNUzRDYDdFwFYUKvXcIA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/@ampproject/remapping": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.2.0.tgz", - "integrity": "sha512-qRmjj8nj9qmLTQXXmaR1cck3UXSRMPrbsLJAasZpF+t3riI71BXed5ebIOYwQntykeZuhjsdweEc9BxH5Jc26w==", + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.2.1.tgz", + "integrity": "sha512-lFMjJTrFL3j7L9yBxwYfCq2k6qqwHyzuUl/XBnif78PWTJYyL/dfowQHWE3sp6U6ZzqWiiIZnpTMO96zhkjwtg==", "dev": true, "dependencies": { - "@jridgewell/gen-mapping": "^0.1.0", + "@jridgewell/gen-mapping": "^0.3.0", "@jridgewell/trace-mapping": "^0.3.9" }, "engines": { @@ -60,47 +71,119 @@ } }, "node_modules/@babel/code-frame": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.18.6.tgz", - "integrity": "sha512-TDCmlK5eOvH+eH7cdAFlNXeVJqWIQ7gW9tY1GJIpUtFb6CmjVyq2VM3u71bOyR8CRihcCgMUYoDNyLXao3+70Q==", + "version": "7.22.13", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.22.13.tgz", + "integrity": "sha512-XktuhWlJ5g+3TJXc5upd9Ks1HutSArik6jf2eAjYFyIOf4ej3RN+184cZbzDvbPnuTJIUhPKKJE3cIsYTiAT3w==", "dev": true, "dependencies": { - "@babel/highlight": "^7.18.6" + "@babel/highlight": "^7.22.13", + "chalk": "^2.4.2" }, "engines": { "node": ">=6.9.0" } }, + "node_modules/@babel/code-frame/node_modules/ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "dependencies": { + "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/code-frame/node_modules/chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, + "dependencies": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/code-frame/node_modules/color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, + "dependencies": { + "color-name": "1.1.3" + } + }, + "node_modules/@babel/code-frame/node_modules/color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", + "dev": true + }, + "node_modules/@babel/code-frame/node_modules/escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "dev": true, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/@babel/code-frame/node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/code-frame/node_modules/supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, "node_modules/@babel/compat-data": { - "version": "7.18.13", - "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.18.13.tgz", - "integrity": "sha512-5yUzC5LqyTFp2HLmDoxGQelcdYgSpP9xsnMWBphAscOdFrHSAVbLNzWiy32sVNDqJRDiJK6klfDnAgu6PAGSHw==", + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.23.3.tgz", + "integrity": "sha512-BmR4bWbDIoFJmJ9z2cZ8Gmm2MXgEDgjdWgpKmKWUt54UGFJdlj31ECtbaDvCG/qVdG3AQ1SfpZEs01lUFbzLOQ==", "dev": true, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/core": { - "version": "7.18.13", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.18.13.tgz", - "integrity": "sha512-ZisbOvRRusFktksHSG6pjj1CSvkPkcZq/KHD45LAkVP/oiHJkNBZWfpvlLmX8OtHDG8IuzsFlVRWo08w7Qxn0A==", - "dev": true, - "dependencies": { - "@ampproject/remapping": "^2.1.0", - "@babel/code-frame": "^7.18.6", - "@babel/generator": "^7.18.13", - "@babel/helper-compilation-targets": "^7.18.9", - "@babel/helper-module-transforms": "^7.18.9", - "@babel/helpers": "^7.18.9", - "@babel/parser": "^7.18.13", - "@babel/template": "^7.18.10", - "@babel/traverse": "^7.18.13", - "@babel/types": "^7.18.13", - "convert-source-map": "^1.7.0", + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.23.3.tgz", + "integrity": "sha512-Jg+msLuNuCJDyBvFv5+OKOUjWMZgd85bKjbICd3zWrKAo+bJ49HJufi7CQE0q0uR8NGyO6xkCACScNqyjHSZew==", + "dev": true, + "dependencies": { + "@ampproject/remapping": "^2.2.0", + "@babel/code-frame": "^7.22.13", + "@babel/generator": "^7.23.3", + "@babel/helper-compilation-targets": "^7.22.15", + "@babel/helper-module-transforms": "^7.23.3", + "@babel/helpers": "^7.23.2", + "@babel/parser": "^7.23.3", + "@babel/template": "^7.22.15", + "@babel/traverse": "^7.23.3", + "@babel/types": "^7.23.3", + "convert-source-map": "^2.0.0", "debug": "^4.1.0", "gensync": "^1.0.0-beta.2", - "json5": "^2.2.1", - "semver": "^6.3.0" + "json5": "^2.2.3", + "semver": "^6.3.1" }, "engines": { "node": ">=6.9.0" @@ -110,209 +193,289 @@ "url": "https://opencollective.com/babel" } }, + "node_modules/@babel/core/node_modules/convert-source-map": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", + "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", + "dev": true + }, + "node_modules/@babel/core/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + } + }, "node_modules/@babel/generator": { - "version": "7.18.13", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.18.13.tgz", - "integrity": "sha512-CkPg8ySSPuHTYPJYo7IRALdqyjM9HCbt/3uOBEFbzyGVP6Mn8bwFPB0jX6982JVNBlYzM1nnPkfjuXSOPtQeEQ==", + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.23.3.tgz", + "integrity": "sha512-keeZWAV4LU3tW0qRi19HRpabC/ilM0HRBBzf9/k8FFiG4KVpiv0FIy4hHfLfFQZNhziCTPTmd59zoyv6DNISzg==", "dev": true, "dependencies": { - "@babel/types": "^7.18.13", + "@babel/types": "^7.23.3", "@jridgewell/gen-mapping": "^0.3.2", + "@jridgewell/trace-mapping": "^0.3.17", "jsesc": "^2.5.1" }, "engines": { "node": ">=6.9.0" } }, - "node_modules/@babel/generator/node_modules/@jridgewell/gen-mapping": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.2.tgz", - "integrity": "sha512-mh65xKQAzI6iBcFzwv28KVWSmCkdRBWoOh+bYQGW3+6OZvbbN3TqMGo5hqYxQniRcH9F2VZIoJCm4pa3BPDK/A==", + "node_modules/@babel/helper-compilation-targets": { + "version": "7.22.15", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.22.15.tgz", + "integrity": "sha512-y6EEzULok0Qvz8yyLkCvVX+02ic+By2UdOhylwUOvOn9dvYc9mKICJuuU1n1XBI02YWsNsnrY1kc6DVbjcXbtw==", "dev": true, "dependencies": { - "@jridgewell/set-array": "^1.0.1", - "@jridgewell/sourcemap-codec": "^1.4.10", - "@jridgewell/trace-mapping": "^0.3.9" + "@babel/compat-data": "^7.22.9", + "@babel/helper-validator-option": "^7.22.15", + "browserslist": "^4.21.9", + "lru-cache": "^5.1.1", + "semver": "^6.3.1" }, "engines": { - "node": ">=6.0.0" + "node": ">=6.9.0" } }, - "node_modules/@babel/helper-compilation-targets": { - "version": "7.18.9", - "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.18.9.tgz", - "integrity": "sha512-tzLCyVmqUiFlcFoAPLA/gL9TeYrF61VLNtb+hvkuVaB5SUjW7jcfrglBIX1vUIoT7CLP3bBlIMeyEsIl2eFQNg==", + "node_modules/@babel/helper-compilation-targets/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", "dev": true, - "dependencies": { - "@babel/compat-data": "^7.18.8", - "@babel/helper-validator-option": "^7.18.6", - "browserslist": "^4.20.2", - "semver": "^6.3.0" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" + "bin": { + "semver": "bin/semver.js" } }, "node_modules/@babel/helper-environment-visitor": { - "version": "7.18.9", - "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.18.9.tgz", - "integrity": "sha512-3r/aACDJ3fhQ/EVgFy0hpj8oHyHpQc+LPtJoY9SzTThAsStm4Ptegq92vqKoE3vD706ZVFWITnMnxucw+S9Ipg==", + "version": "7.22.20", + "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.22.20.tgz", + "integrity": "sha512-zfedSIzFhat/gFhWfHtgWvlec0nqB9YEIVrpuwjruLlXfUSnA8cJB0miHKwqDnQ7d32aKo2xt88/xZptwxbfhA==", "dev": true, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-function-name": { - "version": "7.18.9", - "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.18.9.tgz", - "integrity": "sha512-fJgWlZt7nxGksJS9a0XdSaI4XvpExnNIgRP+rVefWh5U7BL8pPuir6SJUmFKRfjWQ51OtWSzwOxhaH/EBWWc0A==", + "version": "7.23.0", + "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.23.0.tgz", + "integrity": "sha512-OErEqsrxjZTJciZ4Oo+eoZqeW9UIiOcuYKRJA4ZAgV9myA+pOXhhmpfNCKjEH/auVfEYVFJ6y1Tc4r0eIApqiw==", "dev": true, "dependencies": { - "@babel/template": "^7.18.6", - "@babel/types": "^7.18.9" + "@babel/template": "^7.22.15", + "@babel/types": "^7.23.0" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-hoist-variables": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.18.6.tgz", - "integrity": "sha512-UlJQPkFqFULIcyW5sbzgbkxn2FKRgwWiRexcuaR8RNJRy8+LLveqPjwZV/bwrLZCN0eUHD/x8D0heK1ozuoo6Q==", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.22.5.tgz", + "integrity": "sha512-wGjk9QZVzvknA6yKIUURb8zY3grXCcOZt+/7Wcy8O2uctxhplmUPkOdlgoNhmdVee2c92JXbf1xpMtVNbfoxRw==", "dev": true, "dependencies": { - "@babel/types": "^7.18.6" + "@babel/types": "^7.22.5" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-module-imports": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.18.6.tgz", - "integrity": "sha512-0NFvs3VkuSYbFi1x2Vd6tKrywq+z/cLeYC/RJNFrIX/30Bf5aiGYbtvGXolEktzJH8o5E5KJ3tT+nkxuuZFVlA==", + "version": "7.22.15", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.22.15.tgz", + "integrity": "sha512-0pYVBnDKZO2fnSPCrgM/6WMc7eS20Fbok+0r88fp+YtWVLZrp4CkafFGIp+W0VKw4a22sgebPT99y+FDNMdP4w==", "dev": true, "dependencies": { - "@babel/types": "^7.18.6" + "@babel/types": "^7.22.15" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-module-transforms": { - "version": "7.18.9", - "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.18.9.tgz", - "integrity": "sha512-KYNqY0ICwfv19b31XzvmI/mfcylOzbLtowkw+mfvGPAQ3kfCnMLYbED3YecL5tPd8nAYFQFAd6JHp2LxZk/J1g==", + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.23.3.tgz", + "integrity": "sha512-7bBs4ED9OmswdfDzpz4MpWgSrV7FXlc3zIagvLFjS5H+Mk7Snr21vQ6QwrsoCGMfNC4e4LQPdoULEt4ykz0SRQ==", "dev": true, "dependencies": { - "@babel/helper-environment-visitor": "^7.18.9", - "@babel/helper-module-imports": "^7.18.6", - "@babel/helper-simple-access": "^7.18.6", - "@babel/helper-split-export-declaration": "^7.18.6", - "@babel/helper-validator-identifier": "^7.18.6", - "@babel/template": "^7.18.6", - "@babel/traverse": "^7.18.9", - "@babel/types": "^7.18.9" + "@babel/helper-environment-visitor": "^7.22.20", + "@babel/helper-module-imports": "^7.22.15", + "@babel/helper-simple-access": "^7.22.5", + "@babel/helper-split-export-declaration": "^7.22.6", + "@babel/helper-validator-identifier": "^7.22.20" }, "engines": { "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" } }, "node_modules/@babel/helper-plugin-utils": { - "version": "7.18.9", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.18.9.tgz", - "integrity": "sha512-aBXPT3bmtLryXaoJLyYPXPlSD4p1ld9aYeR+sJNOZjJJGiOpb+fKfh3NkcCu7J54nUJwCERPBExCCpyCOHnu/w==", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.22.5.tgz", + "integrity": "sha512-uLls06UVKgFG9QD4OeFYLEGteMIAa5kpTPcFL28yuCIIzsf6ZyKZMllKVOCZFhiZ5ptnwX4mtKdWCBE/uT4amg==", "dev": true, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-simple-access": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.18.6.tgz", - "integrity": "sha512-iNpIgTgyAvDQpDj76POqg+YEt8fPxx3yaNBg3S30dxNKm2SWfYhD0TGrK/Eu9wHpUW63VQU894TsTg+GLbUa1g==", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.22.5.tgz", + "integrity": "sha512-n0H99E/K+Bika3++WNL17POvo4rKWZ7lZEp1Q+fStVbUi8nxPQEBOlTmCOxW/0JsS56SKKQ+ojAe2pHKJHN35w==", "dev": true, "dependencies": { - "@babel/types": "^7.18.6" + "@babel/types": "^7.22.5" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-split-export-declaration": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.18.6.tgz", - "integrity": "sha512-bde1etTx6ZyTmobl9LLMMQsaizFVZrquTEHOqKeQESMKo4PlObf+8+JA25ZsIpZhT/WEd39+vOdLXAFG/nELpA==", + "version": "7.22.6", + "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.22.6.tgz", + "integrity": "sha512-AsUnxuLhRYsisFiaJwvp1QF+I3KjD5FOxut14q/GzovUe6orHLesW2C7d754kRm53h5gqrz6sFl6sxc4BVtE/g==", "dev": true, "dependencies": { - "@babel/types": "^7.18.6" + "@babel/types": "^7.22.5" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-string-parser": { - "version": "7.18.10", - "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.18.10.tgz", - "integrity": "sha512-XtIfWmeNY3i4t7t4D2t02q50HvqHybPqW2ki1kosnvWCwuCMeo81Jf0gwr85jy/neUdg5XDdeFE/80DXiO+njw==", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.22.5.tgz", + "integrity": "sha512-mM4COjgZox8U+JcXQwPijIZLElkgEpO5rsERVDJTc2qfCDfERyob6k5WegS14SX18IIjv+XD+GrqNumY5JRCDw==", "dev": true, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-validator-identifier": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.18.6.tgz", - "integrity": "sha512-MmetCkz9ej86nJQV+sFCxoGGrUbU3q02kgLciwkrt9QqEB7cP39oKEY0PakknEO0Gu20SskMRi+AYZ3b1TpN9g==", + "version": "7.22.20", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.20.tgz", + "integrity": "sha512-Y4OZ+ytlatR8AI+8KZfKuL5urKp7qey08ha31L8b3BwewJAoJamTzyvxPR/5D+KkdJCGPq/+8TukHBlY10FX9A==", "dev": true, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-validator-option": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.18.6.tgz", - "integrity": "sha512-XO7gESt5ouv/LRJdrVjkShckw6STTaB7l9BrpBaAHDeF5YZT+01PCwmR0SJHnkW6i8OwW/EVWRShfi4j2x+KQw==", + "version": "7.22.15", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.22.15.tgz", + "integrity": "sha512-bMn7RmyFjY/mdECUbgn9eoSY4vqvacUnS9i9vGAGttgFWesO6B4CYWA7XlpbWgBt71iv/hfbPlynohStqnu5hA==", "dev": true, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helpers": { - "version": "7.18.9", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.18.9.tgz", - "integrity": "sha512-Jf5a+rbrLoR4eNdUmnFu8cN5eNJT6qdTdOg5IHIzq87WwyRw9PwguLFOWYgktN/60IP4fgDUawJvs7PjQIzELQ==", + "version": "7.23.2", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.23.2.tgz", + "integrity": "sha512-lzchcp8SjTSVe/fPmLwtWVBFC7+Tbn8LGHDVfDp9JGxpAY5opSaEFgt8UQvrnECWOTdji2mOWMz1rOhkHscmGQ==", "dev": true, "dependencies": { - "@babel/template": "^7.18.6", - "@babel/traverse": "^7.18.9", - "@babel/types": "^7.18.9" + "@babel/template": "^7.22.15", + "@babel/traverse": "^7.23.2", + "@babel/types": "^7.23.0" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/highlight": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.18.6.tgz", - "integrity": "sha512-u7stbOuYjaPezCuLj29hNW1v64M2Md2qupEKP1fHc7WdOA3DgLh37suiSrZYY7haUB7iBeQZ9P1uiRF359do3g==", + "version": "7.22.20", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.22.20.tgz", + "integrity": "sha512-dkdMCN3py0+ksCgYmGG8jKeGA/8Tk+gJwSYYlFGxG5lmhfKNoAy004YpLxpS1W2J8m/EK2Ew+yOs9pVRwO89mg==", "dev": true, "dependencies": { - "@babel/helper-validator-identifier": "^7.18.6", - "chalk": "^2.0.0", + "@babel/helper-validator-identifier": "^7.22.20", + "chalk": "^2.4.2", "js-tokens": "^4.0.0" }, "engines": { "node": ">=6.9.0" } }, + "node_modules/@babel/highlight/node_modules/ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "dependencies": { + "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/highlight/node_modules/chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, + "dependencies": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/highlight/node_modules/color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, + "dependencies": { + "color-name": "1.1.3" + } + }, + "node_modules/@babel/highlight/node_modules/color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", + "dev": true + }, + "node_modules/@babel/highlight/node_modules/escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "dev": true, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/@babel/highlight/node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/highlight/node_modules/supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, "node_modules/@babel/parser": { - "version": "7.18.13", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.18.13.tgz", - "integrity": "sha512-dgXcIfMuQ0kgzLB2b9tRZs7TTFFaGM2AbtA4fJgUUYukzGH4jwsS7hzQHEGs67jdehpm22vkgKwvbU+aEflgwg==", + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.23.3.tgz", + "integrity": "sha512-uVsWNvlVsIninV2prNz/3lHCb+5CJ+e+IUBfbjToAHODtfGYLfCFuY4AU7TskI+dAKk+njsPiBjq1gKTvZOBaw==", "dev": true, "bin": { "parser": "bin/babel-parser.js" @@ -469,12 +632,12 @@ } }, "node_modules/@babel/plugin-syntax-typescript": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.18.6.tgz", - "integrity": "sha512-mAWAuq4rvOepWCBid55JuRNvpTNf2UGVgoz4JV0fXEKolsVZDzsa4NqCef758WZJj/GDu0gVGItjKFiClTAmZA==", + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.23.3.tgz", + "integrity": "sha512-9EiNjVJOMwCO+43TqoTrgQ8jMwcAd0sWyXi9RPfIsLTj4R2MADDDQXELhffaUx/uJv2AYcxBgPwH6j4TIA4ytQ==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.18.6" + "@babel/helper-plugin-utils": "^7.22.5" }, "engines": { "node": ">=6.9.0" @@ -484,33 +647,33 @@ } }, "node_modules/@babel/template": { - "version": "7.18.10", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.18.10.tgz", - "integrity": "sha512-TI+rCtooWHr3QJ27kJxfjutghu44DLnasDMwpDqCXVTal9RLp3RSYNh4NdBrRP2cQAoG9A8juOQl6P6oZG4JxA==", + "version": "7.22.15", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.22.15.tgz", + "integrity": "sha512-QPErUVm4uyJa60rkI73qneDacvdvzxshT3kksGqlGWYdOTIUOwJ7RDUL8sGqslY1uXWSL6xMFKEXDS3ox2uF0w==", "dev": true, "dependencies": { - "@babel/code-frame": "^7.18.6", - "@babel/parser": "^7.18.10", - "@babel/types": "^7.18.10" + "@babel/code-frame": "^7.22.13", + "@babel/parser": "^7.22.15", + "@babel/types": "^7.22.15" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/traverse": { - "version": "7.18.13", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.18.13.tgz", - "integrity": "sha512-N6kt9X1jRMLPxxxPYWi7tgvJRH/rtoU+dbKAPDM44RFHiMH8igdsaSBgFeskhSl/kLWLDUvIh1RXCrTmg0/zvA==", - "dev": true, - "dependencies": { - "@babel/code-frame": "^7.18.6", - "@babel/generator": "^7.18.13", - "@babel/helper-environment-visitor": "^7.18.9", - "@babel/helper-function-name": "^7.18.9", - "@babel/helper-hoist-variables": "^7.18.6", - "@babel/helper-split-export-declaration": "^7.18.6", - "@babel/parser": "^7.18.13", - "@babel/types": "^7.18.13", + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.23.3.tgz", + "integrity": "sha512-+K0yF1/9yR0oHdE0StHuEj3uTPzwwbrLGfNOndVJVV2TqA5+j3oljJUb4nmB954FLGjNem976+B+eDuLIjesiQ==", + "dev": true, + "dependencies": { + "@babel/code-frame": "^7.22.13", + "@babel/generator": "^7.23.3", + "@babel/helper-environment-visitor": "^7.22.20", + "@babel/helper-function-name": "^7.23.0", + "@babel/helper-hoist-variables": "^7.22.5", + "@babel/helper-split-export-declaration": "^7.22.6", + "@babel/parser": "^7.23.3", + "@babel/types": "^7.23.3", "debug": "^4.1.0", "globals": "^11.1.0" }, @@ -518,14 +681,23 @@ "node": ">=6.9.0" } }, + "node_modules/@babel/traverse/node_modules/globals": { + "version": "11.12.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", + "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", + "dev": true, + "engines": { + "node": ">=4" + } + }, "node_modules/@babel/types": { - "version": "7.18.13", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.18.13.tgz", - "integrity": "sha512-ePqfTihzW0W6XAU+aMw2ykilisStJfDnsejDCXRchCcMJ4O0+8DhPXf2YUbZ6wjBlsEmZwLK/sPweWtu8hcJYQ==", + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.23.3.tgz", + "integrity": "sha512-OZnvoH2l8PK5eUvEcUyCt/sXgr/h+UWpVuBbOljwcrAgUl6lpchoQ++PHGyQy1AtYnVA6CEq3y5xeEI10brpXw==", "dev": true, "dependencies": { - "@babel/helper-string-parser": "^7.18.10", - "@babel/helper-validator-identifier": "^7.18.6", + "@babel/helper-string-parser": "^7.22.5", + "@babel/helper-validator-identifier": "^7.22.20", "to-fast-properties": "^2.0.0" }, "engines": { @@ -538,433 +710,110 @@ "integrity": "sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==", "dev": true }, - "node_modules/@esbuild/android-arm": { - "version": "0.19.2", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.19.2.tgz", - "integrity": "sha512-tM8yLeYVe7pRyAu9VMi/Q7aunpLwD139EY1S99xbQkT4/q2qa6eA4ige/WJQYdJ8GBL1K33pPFhPfPdJ/WzT8Q==", + "node_modules/@esbuild/linux-x64": { + "version": "0.19.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.19.5.tgz", + "integrity": "sha512-psagl+2RlK1z8zWZOmVdImisMtrUxvwereIdyJTmtmHahJTKb64pAcqoPlx6CewPdvGvUKe2Jw+0Z/0qhSbG1A==", "cpu": [ - "arm" + "x64" ], "dev": true, "optional": true, "os": [ - "android" + "linux" ], "engines": { "node": ">=12" } }, - "node_modules/@esbuild/android-arm64": { - "version": "0.19.2", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.19.2.tgz", - "integrity": "sha512-lsB65vAbe90I/Qe10OjkmrdxSX4UJDjosDgb8sZUKcg3oefEuW2OT2Vozz8ef7wrJbMcmhvCC+hciF8jY/uAkw==", - "cpu": [ - "arm64" - ], + "node_modules/@eslint-community/eslint-utils": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz", + "integrity": "sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==", "dev": true, - "optional": true, - "os": [ - "android" - ], + "dependencies": { + "eslint-visitor-keys": "^3.3.0" + }, "engines": { - "node": ">=12" + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" } }, - "node_modules/@esbuild/android-x64": { - "version": "0.19.2", - "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.19.2.tgz", - "integrity": "sha512-qK/TpmHt2M/Hg82WXHRc/W/2SGo/l1thtDHZWqFq7oi24AjZ4O/CpPSu6ZuYKFkEgmZlFoa7CooAyYmuvnaG8w==", - "cpu": [ - "x64" - ], + "node_modules/@eslint-community/regexpp": { + "version": "4.10.0", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.10.0.tgz", + "integrity": "sha512-Cu96Sd2By9mCNTx2iyKOmq10v22jUVQv0lQnlGNy16oE9589yE+QADPbrMGCkA51cKZSg3Pu/aTJVTGfL/qjUA==", "dev": true, - "optional": true, - "os": [ - "android" - ], "engines": { - "node": ">=12" + "node": "^12.0.0 || ^14.0.0 || >=16.0.0" } }, - "node_modules/@esbuild/darwin-arm64": { - "version": "0.19.2", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.19.2.tgz", - "integrity": "sha512-Ora8JokrvrzEPEpZO18ZYXkH4asCdc1DLdcVy8TGf5eWtPO1Ie4WroEJzwI52ZGtpODy3+m0a2yEX9l+KUn0tA==", - "cpu": [ - "arm64" - ], + "node_modules/@eslint/eslintrc": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.3.tgz", + "integrity": "sha512-yZzuIG+jnVu6hNSzFEN07e8BxF3uAzYtQb6uDkaYZLo6oYZDCq454c5kB8zxnzfCYyP4MIuyBn10L0DqwujTmA==", "dev": true, - "optional": true, - "os": [ - "darwin" - ], + "dependencies": { + "ajv": "^6.12.4", + "debug": "^4.3.2", + "espree": "^9.6.0", + "globals": "^13.19.0", + "ignore": "^5.2.0", + "import-fresh": "^3.2.1", + "js-yaml": "^4.1.0", + "minimatch": "^3.1.2", + "strip-json-comments": "^3.1.1" + }, "engines": { - "node": ">=12" + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" } }, - "node_modules/@esbuild/darwin-x64": { - "version": "0.19.2", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.19.2.tgz", - "integrity": "sha512-tP+B5UuIbbFMj2hQaUr6EALlHOIOmlLM2FK7jeFBobPy2ERdohI4Ka6ZFjZ1ZYsrHE/hZimGuU90jusRE0pwDw==", - "cpu": [ - "x64" - ], + "node_modules/@eslint/js": { + "version": "8.53.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.53.0.tgz", + "integrity": "sha512-Kn7K8dx/5U6+cT1yEhpX1w4PCSg0M+XyRILPgvwcEBjerFWCwQj5sbr3/VmxqV0JGHCBCzyd6LxypEuehypY1w==", "dev": true, - "optional": true, - "os": [ - "darwin" - ], "engines": { - "node": ">=12" + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" } }, - "node_modules/@esbuild/freebsd-arm64": { - "version": "0.19.2", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.19.2.tgz", - "integrity": "sha512-YbPY2kc0acfzL1VPVK6EnAlig4f+l8xmq36OZkU0jzBVHcOTyQDhnKQaLzZudNJQyymd9OqQezeaBgkTGdTGeQ==", - "cpu": [ - "arm64" - ], + "node_modules/@humanwhocodes/config-array": { + "version": "0.11.13", + "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.13.tgz", + "integrity": "sha512-JSBDMiDKSzQVngfRjOdFXgFfklaXI4K9nLF49Auh21lmBWRLIK3+xTErTWD4KU54pb6coM6ESE7Awz/FNU3zgQ==", "dev": true, - "optional": true, - "os": [ - "freebsd" - ], + "dependencies": { + "@humanwhocodes/object-schema": "^2.0.1", + "debug": "^4.1.1", + "minimatch": "^3.0.5" + }, "engines": { - "node": ">=12" + "node": ">=10.10.0" } }, - "node_modules/@esbuild/freebsd-x64": { - "version": "0.19.2", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.19.2.tgz", - "integrity": "sha512-nSO5uZT2clM6hosjWHAsS15hLrwCvIWx+b2e3lZ3MwbYSaXwvfO528OF+dLjas1g3bZonciivI8qKR/Hm7IWGw==", - "cpu": [ - "x64" - ], + "node_modules/@humanwhocodes/module-importer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", + "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", "dev": true, - "optional": true, - "os": [ - "freebsd" - ], "engines": { - "node": ">=12" + "node": ">=12.22" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" } }, - "node_modules/@esbuild/linux-arm": { - "version": "0.19.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.19.2.tgz", - "integrity": "sha512-Odalh8hICg7SOD7XCj0YLpYCEc+6mkoq63UnExDCiRA2wXEmGlK5JVrW50vZR9Qz4qkvqnHcpH+OFEggO3PgTg==", - "cpu": [ - "arm" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/linux-arm64": { - "version": "0.19.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.19.2.tgz", - "integrity": "sha512-ig2P7GeG//zWlU0AggA3pV1h5gdix0MA3wgB+NsnBXViwiGgY77fuN9Wr5uoCrs2YzaYfogXgsWZbm+HGr09xg==", - "cpu": [ - "arm64" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/linux-ia32": { - "version": "0.19.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.19.2.tgz", - "integrity": "sha512-mLfp0ziRPOLSTek0Gd9T5B8AtzKAkoZE70fneiiyPlSnUKKI4lp+mGEnQXcQEHLJAcIYDPSyBvsUbKUG2ri/XQ==", - "cpu": [ - "ia32" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/linux-loong64": { - "version": "0.19.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.19.2.tgz", - "integrity": "sha512-hn28+JNDTxxCpnYjdDYVMNTR3SKavyLlCHHkufHV91fkewpIyQchS1d8wSbmXhs1fiYDpNww8KTFlJ1dHsxeSw==", - "cpu": [ - "loong64" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/linux-mips64el": { - "version": "0.19.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.19.2.tgz", - "integrity": "sha512-KbXaC0Sejt7vD2fEgPoIKb6nxkfYW9OmFUK9XQE4//PvGIxNIfPk1NmlHmMg6f25x57rpmEFrn1OotASYIAaTg==", - "cpu": [ - "mips64el" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/linux-ppc64": { - "version": "0.19.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.19.2.tgz", - "integrity": "sha512-dJ0kE8KTqbiHtA3Fc/zn7lCd7pqVr4JcT0JqOnbj4LLzYnp+7h8Qi4yjfq42ZlHfhOCM42rBh0EwHYLL6LEzcw==", - "cpu": [ - "ppc64" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/linux-riscv64": { - "version": "0.19.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.19.2.tgz", - "integrity": "sha512-7Z/jKNFufZ/bbu4INqqCN6DDlrmOTmdw6D0gH+6Y7auok2r02Ur661qPuXidPOJ+FSgbEeQnnAGgsVynfLuOEw==", - "cpu": [ - "riscv64" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/linux-s390x": { - "version": "0.19.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.19.2.tgz", - "integrity": "sha512-U+RinR6aXXABFCcAY4gSlv4CL1oOVvSSCdseQmGO66H+XyuQGZIUdhG56SZaDJQcLmrSfRmx5XZOWyCJPRqS7g==", - "cpu": [ - "s390x" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/linux-x64": { - "version": "0.19.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.19.2.tgz", - "integrity": "sha512-oxzHTEv6VPm3XXNaHPyUTTte+3wGv7qVQtqaZCrgstI16gCuhNOtBXLEBkBREP57YTd68P0VgDgG73jSD8bwXQ==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/netbsd-x64": { - "version": "0.19.2", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.19.2.tgz", - "integrity": "sha512-WNa5zZk1XpTTwMDompZmvQLHszDDDN7lYjEHCUmAGB83Bgs20EMs7ICD+oKeT6xt4phV4NDdSi/8OfjPbSbZfQ==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "netbsd" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/openbsd-x64": { - "version": "0.19.2", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.19.2.tgz", - "integrity": "sha512-S6kI1aT3S++Dedb7vxIuUOb3oAxqxk2Rh5rOXOTYnzN8JzW1VzBd+IqPiSpgitu45042SYD3HCoEyhLKQcDFDw==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "openbsd" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/sunos-x64": { - "version": "0.19.2", - "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.19.2.tgz", - "integrity": "sha512-VXSSMsmb+Z8LbsQGcBMiM+fYObDNRm8p7tkUDMPG/g4fhFX5DEFmjxIEa3N8Zr96SjsJ1woAhF0DUnS3MF3ARw==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "sunos" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/win32-arm64": { - "version": "0.19.2", - "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.19.2.tgz", - "integrity": "sha512-5NayUlSAyb5PQYFAU9x3bHdsqB88RC3aM9lKDAz4X1mo/EchMIT1Q+pSeBXNgkfNmRecLXA0O8xP+x8V+g/LKg==", - "cpu": [ - "arm64" - ], - "dev": true, - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/win32-ia32": { - "version": "0.19.2", - "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.19.2.tgz", - "integrity": "sha512-47gL/ek1v36iN0wL9L4Q2MFdujR0poLZMJwhO2/N3gA89jgHp4MR8DKCmwYtGNksbfJb9JoTtbkoe6sDhg2QTA==", - "cpu": [ - "ia32" - ], - "dev": true, - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/win32-x64": { - "version": "0.19.2", - "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.19.2.tgz", - "integrity": "sha512-tcuhV7ncXBqbt/Ybf0IyrMcwVOAPDckMK9rXNHtF17UTK18OKLpg08glminN06pt2WCoALhXdLfSPbVvK/6fxw==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@eslint/eslintrc": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-1.0.4.tgz", - "integrity": "sha512-h8Vx6MdxwWI2WM8/zREHMoqdgLNXEL4QX3MWSVMdyNJGvXVOs+6lp+m2hc3FnuMHDc4poxFNI20vCk0OmI4G0Q==", - "dev": true, - "dependencies": { - "ajv": "^6.12.4", - "debug": "^4.3.2", - "espree": "^9.0.0", - "globals": "^13.9.0", - "ignore": "^4.0.6", - "import-fresh": "^3.2.1", - "js-yaml": "^4.1.0", - "minimatch": "^3.0.4", - "strip-json-comments": "^3.1.1" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - } - }, - "node_modules/@eslint/eslintrc/node_modules/globals": { - "version": "13.12.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-13.12.0.tgz", - "integrity": "sha512-uS8X6lSKN2JumVoXrbUz+uG4BYG+eiawqm3qFcT7ammfbUHeCBoJMlHcec/S3krSk73/AE/f0szYFmgAA3kYZg==", - "dev": true, - "dependencies": { - "type-fest": "^0.20.2" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/@eslint/eslintrc/node_modules/ignore": { - "version": "4.0.6", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-4.0.6.tgz", - "integrity": "sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==", - "dev": true, - "engines": { - "node": ">= 4" - } - }, - "node_modules/@eslint/eslintrc/node_modules/type-fest": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", - "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", - "dev": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/@humanwhocodes/config-array": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.6.0.tgz", - "integrity": "sha512-JQlEKbcgEUjBFhLIF4iqM7u/9lwgHRBcpHrmUNCALK0Q3amXN6lxdoXLnF0sm11E9VqTmBALR87IlUg1bZ8A9A==", - "dev": true, - "dependencies": { - "@humanwhocodes/object-schema": "^1.2.0", - "debug": "^4.1.1", - "minimatch": "^3.0.4" - }, - "engines": { - "node": ">=10.10.0" - } - }, - "node_modules/@humanwhocodes/object-schema": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz", - "integrity": "sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==", - "dev": true + "node_modules/@humanwhocodes/object-schema": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-2.0.1.tgz", + "integrity": "sha512-dvuCeX5fC9dXgJn9t+X5atfmgQAzUOWqS1254Gh0m6i8wKd10ebXkfNKiRK+1GWi/yTvvLDHpoxLr0xxxeslWw==", + "dev": true }, "node_modules/@istanbuljs/load-nyc-config": { "version": "1.1.0", @@ -991,6 +840,19 @@ "sprintf-js": "~1.0.2" } }, + "node_modules/@istanbuljs/load-nyc-config/node_modules/find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "dev": true, + "dependencies": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/@istanbuljs/load-nyc-config/node_modules/js-yaml": { "version": "3.14.1", "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", @@ -1004,6 +866,45 @@ "js-yaml": "bin/js-yaml.js" } }, + "node_modules/@istanbuljs/load-nyc-config/node_modules/locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "dev": true, + "dependencies": { + "p-locate": "^4.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@istanbuljs/load-nyc-config/node_modules/p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "dev": true, + "dependencies": { + "p-try": "^2.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@istanbuljs/load-nyc-config/node_modules/p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "dev": true, + "dependencies": { + "p-limit": "^2.2.0" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/@istanbuljs/load-nyc-config/node_modules/resolve-from": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", @@ -1040,75 +941,75 @@ } }, "node_modules/@jest/console/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", "dev": true, - "dependencies": { - "color-convert": "^2.0.1" - }, "engines": { - "node": ">=8" + "node": ">=10" }, "funding": { "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "node_modules/@jest/console/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "node_modules/@jest/console/node_modules/jest-message-util": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-28.1.3.tgz", + "integrity": "sha512-PFdn9Iewbt575zKPf1286Ht9EPoJmYT7P0kY+RibeYZ2XtOr53pDLEFoTWXbd1h4JiGiWpTBC84fc8xMXQMb7g==", "dev": true, "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" + "@babel/code-frame": "^7.12.13", + "@jest/types": "^28.1.3", + "@types/stack-utils": "^2.0.0", + "chalk": "^4.0.0", + "graceful-fs": "^4.2.9", + "micromatch": "^4.0.4", + "pretty-format": "^28.1.3", + "slash": "^3.0.0", + "stack-utils": "^2.0.3" }, "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" + "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" } }, - "node_modules/@jest/console/node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "node_modules/@jest/console/node_modules/jest-util": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-28.1.3.tgz", + "integrity": "sha512-XdqfpHwpcSRko/C35uLYFM2emRAltIIKZiJ9eAmhjsj0CqZMa0p1ib0R5fWIqGhn1a103DebTbpqIaP1qCQ6tQ==", "dev": true, "dependencies": { - "color-name": "~1.1.4" + "@jest/types": "^28.1.3", + "@types/node": "*", + "chalk": "^4.0.0", + "ci-info": "^3.2.0", + "graceful-fs": "^4.2.9", + "picomatch": "^2.2.3" }, "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/@jest/console/node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "node_modules/@jest/console/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true, - "engines": { - "node": ">=8" + "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" } }, - "node_modules/@jest/console/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "node_modules/@jest/console/node_modules/pretty-format": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-28.1.3.tgz", + "integrity": "sha512-8gFb/To0OmxHR9+ZTb14Df2vNxdGCX8g1xWGUTqUw5TiZvcQf5sHKObd5UcPyLLyowNwDAMTF3XWOG1B6mxl1Q==", "dev": true, "dependencies": { - "has-flag": "^4.0.0" + "@jest/schemas": "^28.1.3", + "ansi-regex": "^5.0.1", + "ansi-styles": "^5.0.0", + "react-is": "^18.0.0" }, "engines": { - "node": ">=8" + "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" } }, + "node_modules/@jest/console/node_modules/react-is": { + "version": "18.2.0", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz", + "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==", + "dev": true + }, "node_modules/@jest/core": { "version": "28.1.3", "resolved": "https://registry.npmjs.org/@jest/core/-/core-28.1.3.tgz", @@ -1157,74 +1058,53 @@ } } }, - "node_modules/@jest/core/node_modules/@jest/schemas": { - "version": "28.1.3", - "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-28.1.3.tgz", - "integrity": "sha512-/l/VWsdt/aBXgjshLWOFyFt3IVdYypu5y2Wn2rOO1un6nkqIn8SLXzgIMYXFyYsRWDyF5EthmKJMIdJvk08grg==", - "dev": true, - "dependencies": { - "@sinclair/typebox": "^0.24.1" - }, - "engines": { - "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" - } - }, "node_modules/@jest/core/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", "dev": true, - "dependencies": { - "color-convert": "^2.0.1" - }, "engines": { - "node": ">=8" + "node": ">=10" }, "funding": { "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "node_modules/@jest/core/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "node_modules/@jest/core/node_modules/jest-message-util": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-28.1.3.tgz", + "integrity": "sha512-PFdn9Iewbt575zKPf1286Ht9EPoJmYT7P0kY+RibeYZ2XtOr53pDLEFoTWXbd1h4JiGiWpTBC84fc8xMXQMb7g==", "dev": true, "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" + "@babel/code-frame": "^7.12.13", + "@jest/types": "^28.1.3", + "@types/stack-utils": "^2.0.0", + "chalk": "^4.0.0", + "graceful-fs": "^4.2.9", + "micromatch": "^4.0.4", + "pretty-format": "^28.1.3", + "slash": "^3.0.0", + "stack-utils": "^2.0.3" }, "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" + "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" } }, - "node_modules/@jest/core/node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "node_modules/@jest/core/node_modules/jest-util": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-28.1.3.tgz", + "integrity": "sha512-XdqfpHwpcSRko/C35uLYFM2emRAltIIKZiJ9eAmhjsj0CqZMa0p1ib0R5fWIqGhn1a103DebTbpqIaP1qCQ6tQ==", "dev": true, "dependencies": { - "color-name": "~1.1.4" + "@jest/types": "^28.1.3", + "@types/node": "*", + "chalk": "^4.0.0", + "ci-info": "^3.2.0", + "graceful-fs": "^4.2.9", + "picomatch": "^2.2.3" }, "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/@jest/core/node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "node_modules/@jest/core/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true, - "engines": { - "node": ">=8" + "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" } }, "node_modules/@jest/core/node_modules/pretty-format": { @@ -1242,36 +1122,12 @@ "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" } }, - "node_modules/@jest/core/node_modules/pretty-format/node_modules/ansi-styles": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", - "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", - "dev": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, "node_modules/@jest/core/node_modules/react-is": { "version": "18.2.0", "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz", "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==", "dev": true }, - "node_modules/@jest/core/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/@jest/environment": { "version": "28.1.3", "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-28.1.3.tgz", @@ -1301,26 +1157,17 @@ } }, "node_modules/@jest/expect-utils": { - "version": "29.0.1", - "resolved": "https://registry.npmjs.org/@jest/expect-utils/-/expect-utils-29.0.1.tgz", - "integrity": "sha512-Tw5kUUOKmXGQDmQ9TSgTraFFS7HMC1HG/B7y0AN2G2UzjdAXz9BzK2rmNpCSDl7g7y0Gf/VLBm//blonvhtOTQ==", + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/expect-utils/-/expect-utils-29.7.0.tgz", + "integrity": "sha512-GlsNBWiFQFCVi9QVSx7f5AgMeLxe9YCCs5PuP2O2LdjDAA8Jh9eX7lA1Jq/xdXw3Wb3hyvlFNfZIfcRetSzYcA==", "dev": true, "dependencies": { - "jest-get-type": "^29.0.0" + "jest-get-type": "^29.6.3" }, "engines": { "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/@jest/expect-utils/node_modules/jest-get-type": { - "version": "29.0.0", - "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-29.0.0.tgz", - "integrity": "sha512-83X19z/HuLKYXYHskZlBAShO7UfLFXu/vWajw9ZNJASN32li8yHMaVGAQqxFW1RCFOkB7cubaL6FaJVQqqJLSw==", - "dev": true, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, "node_modules/@jest/expect/node_modules/@jest/expect-utils": { "version": "28.1.3", "resolved": "https://registry.npmjs.org/@jest/expect-utils/-/expect-utils-28.1.3.tgz", @@ -1333,6 +1180,27 @@ "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" } }, + "node_modules/@jest/expect/node_modules/ansi-styles": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/@jest/expect/node_modules/diff-sequences": { + "version": "28.1.1", + "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-28.1.1.tgz", + "integrity": "sha512-FU0iFaH/E23a+a718l8Qa/19bF9p06kgE0KipMOMadwa3SjnaElKzPaUC0vnibs6/B/9ni97s61mcejk8W1fQw==", + "dev": true, + "engines": { + "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" + } + }, "node_modules/@jest/expect/node_modules/expect": { "version": "28.1.3", "resolved": "https://registry.npmjs.org/expect/-/expect-28.1.3.tgz", @@ -1349,6 +1217,21 @@ "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" } }, + "node_modules/@jest/expect/node_modules/jest-diff": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-28.1.3.tgz", + "integrity": "sha512-8RqP1B/OXzjjTWkqMX67iqgwBVJRgCyKD3L9nq+6ZqJMdvjE8RgHktqZ6jNrkdMT+dJuYNI3rhQpxaz7drJHfw==", + "dev": true, + "dependencies": { + "chalk": "^4.0.0", + "diff-sequences": "^28.1.1", + "jest-get-type": "^28.0.2", + "pretty-format": "^28.1.3" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" + } + }, "node_modules/@jest/expect/node_modules/jest-get-type": { "version": "28.0.2", "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-28.0.2.tgz", @@ -1358,54 +1241,197 @@ "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" } }, - "node_modules/@jest/fake-timers": { + "node_modules/@jest/expect/node_modules/jest-matcher-utils": { "version": "28.1.3", - "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-28.1.3.tgz", - "integrity": "sha512-D/wOkL2POHv52h+ok5Oj/1gOG9HSywdoPtFsRCUmlCILXNn5eIWmcnd3DIiWlJnpGvQtmajqBP95Ei0EimxfLw==", + "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-28.1.3.tgz", + "integrity": "sha512-kQeJ7qHemKfbzKoGjHHrRKH6atgxMk8Enkk2iPQ3XwO6oE/KYD8lMYOziCkeSB9G4adPM4nR1DE8Tf5JeWH6Bw==", "dev": true, "dependencies": { - "@jest/types": "^28.1.3", - "@sinonjs/fake-timers": "^9.1.2", - "@types/node": "*", - "jest-message-util": "^28.1.3", - "jest-mock": "^28.1.3", - "jest-util": "^28.1.3" + "chalk": "^4.0.0", + "jest-diff": "^28.1.3", + "jest-get-type": "^28.0.2", + "pretty-format": "^28.1.3" }, "engines": { "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" } }, - "node_modules/@jest/globals": { + "node_modules/@jest/expect/node_modules/jest-message-util": { "version": "28.1.3", - "resolved": "https://registry.npmjs.org/@jest/globals/-/globals-28.1.3.tgz", - "integrity": "sha512-XFU4P4phyryCXu1pbcqMO0GSQcYe1IsalYCDzRNyhetyeyxMcIxa11qPNDpVNLeretItNqEmYYQn1UYz/5x1NA==", + "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-28.1.3.tgz", + "integrity": "sha512-PFdn9Iewbt575zKPf1286Ht9EPoJmYT7P0kY+RibeYZ2XtOr53pDLEFoTWXbd1h4JiGiWpTBC84fc8xMXQMb7g==", "dev": true, "dependencies": { - "@jest/environment": "^28.1.3", - "@jest/expect": "^28.1.3", - "@jest/types": "^28.1.3" + "@babel/code-frame": "^7.12.13", + "@jest/types": "^28.1.3", + "@types/stack-utils": "^2.0.0", + "chalk": "^4.0.0", + "graceful-fs": "^4.2.9", + "micromatch": "^4.0.4", + "pretty-format": "^28.1.3", + "slash": "^3.0.0", + "stack-utils": "^2.0.3" }, "engines": { "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" } }, - "node_modules/@jest/reporters": { + "node_modules/@jest/expect/node_modules/jest-util": { "version": "28.1.3", - "resolved": "https://registry.npmjs.org/@jest/reporters/-/reporters-28.1.3.tgz", - "integrity": "sha512-JuAy7wkxQZVNU/V6g9xKzCGC5LVXx9FDcABKsSXp5MiKPEE2144a/vXTEDoyzjUpZKfVwp08Wqg5A4WfTMAzjg==", + "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-28.1.3.tgz", + "integrity": "sha512-XdqfpHwpcSRko/C35uLYFM2emRAltIIKZiJ9eAmhjsj0CqZMa0p1ib0R5fWIqGhn1a103DebTbpqIaP1qCQ6tQ==", "dev": true, "dependencies": { - "@bcoe/v8-coverage": "^0.2.3", - "@jest/console": "^28.1.3", - "@jest/test-result": "^28.1.3", - "@jest/transform": "^28.1.3", "@jest/types": "^28.1.3", - "@jridgewell/trace-mapping": "^0.3.13", "@types/node": "*", "chalk": "^4.0.0", - "collect-v8-coverage": "^1.0.0", - "exit": "^0.1.2", - "glob": "^7.1.3", + "ci-info": "^3.2.0", + "graceful-fs": "^4.2.9", + "picomatch": "^2.2.3" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" + } + }, + "node_modules/@jest/expect/node_modules/pretty-format": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-28.1.3.tgz", + "integrity": "sha512-8gFb/To0OmxHR9+ZTb14Df2vNxdGCX8g1xWGUTqUw5TiZvcQf5sHKObd5UcPyLLyowNwDAMTF3XWOG1B6mxl1Q==", + "dev": true, + "dependencies": { + "@jest/schemas": "^28.1.3", + "ansi-regex": "^5.0.1", + "ansi-styles": "^5.0.0", + "react-is": "^18.0.0" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" + } + }, + "node_modules/@jest/expect/node_modules/react-is": { + "version": "18.2.0", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz", + "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==", + "dev": true + }, + "node_modules/@jest/fake-timers": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-28.1.3.tgz", + "integrity": "sha512-D/wOkL2POHv52h+ok5Oj/1gOG9HSywdoPtFsRCUmlCILXNn5eIWmcnd3DIiWlJnpGvQtmajqBP95Ei0EimxfLw==", + "dev": true, + "dependencies": { + "@jest/types": "^28.1.3", + "@sinonjs/fake-timers": "^9.1.2", + "@types/node": "*", + "jest-message-util": "^28.1.3", + "jest-mock": "^28.1.3", + "jest-util": "^28.1.3" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" + } + }, + "node_modules/@jest/fake-timers/node_modules/ansi-styles": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/@jest/fake-timers/node_modules/jest-message-util": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-28.1.3.tgz", + "integrity": "sha512-PFdn9Iewbt575zKPf1286Ht9EPoJmYT7P0kY+RibeYZ2XtOr53pDLEFoTWXbd1h4JiGiWpTBC84fc8xMXQMb7g==", + "dev": true, + "dependencies": { + "@babel/code-frame": "^7.12.13", + "@jest/types": "^28.1.3", + "@types/stack-utils": "^2.0.0", + "chalk": "^4.0.0", + "graceful-fs": "^4.2.9", + "micromatch": "^4.0.4", + "pretty-format": "^28.1.3", + "slash": "^3.0.0", + "stack-utils": "^2.0.3" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" + } + }, + "node_modules/@jest/fake-timers/node_modules/jest-util": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-28.1.3.tgz", + "integrity": "sha512-XdqfpHwpcSRko/C35uLYFM2emRAltIIKZiJ9eAmhjsj0CqZMa0p1ib0R5fWIqGhn1a103DebTbpqIaP1qCQ6tQ==", + "dev": true, + "dependencies": { + "@jest/types": "^28.1.3", + "@types/node": "*", + "chalk": "^4.0.0", + "ci-info": "^3.2.0", + "graceful-fs": "^4.2.9", + "picomatch": "^2.2.3" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" + } + }, + "node_modules/@jest/fake-timers/node_modules/pretty-format": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-28.1.3.tgz", + "integrity": "sha512-8gFb/To0OmxHR9+ZTb14Df2vNxdGCX8g1xWGUTqUw5TiZvcQf5sHKObd5UcPyLLyowNwDAMTF3XWOG1B6mxl1Q==", + "dev": true, + "dependencies": { + "@jest/schemas": "^28.1.3", + "ansi-regex": "^5.0.1", + "ansi-styles": "^5.0.0", + "react-is": "^18.0.0" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" + } + }, + "node_modules/@jest/fake-timers/node_modules/react-is": { + "version": "18.2.0", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz", + "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==", + "dev": true + }, + "node_modules/@jest/globals": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/@jest/globals/-/globals-28.1.3.tgz", + "integrity": "sha512-XFU4P4phyryCXu1pbcqMO0GSQcYe1IsalYCDzRNyhetyeyxMcIxa11qPNDpVNLeretItNqEmYYQn1UYz/5x1NA==", + "dev": true, + "dependencies": { + "@jest/environment": "^28.1.3", + "@jest/expect": "^28.1.3", + "@jest/types": "^28.1.3" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" + } + }, + "node_modules/@jest/reporters": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/@jest/reporters/-/reporters-28.1.3.tgz", + "integrity": "sha512-JuAy7wkxQZVNU/V6g9xKzCGC5LVXx9FDcABKsSXp5MiKPEE2144a/vXTEDoyzjUpZKfVwp08Wqg5A4WfTMAzjg==", + "dev": true, + "dependencies": { + "@bcoe/v8-coverage": "^0.2.3", + "@jest/console": "^28.1.3", + "@jest/test-result": "^28.1.3", + "@jest/transform": "^28.1.3", + "@jest/types": "^28.1.3", + "@jridgewell/trace-mapping": "^0.3.13", + "@types/node": "*", + "chalk": "^4.0.0", + "collect-v8-coverage": "^1.0.0", + "exit": "^0.1.2", + "glob": "^7.1.3", "graceful-fs": "^4.2.9", "istanbul-lib-coverage": "^3.0.0", "istanbul-lib-instrument": "^5.1.0", @@ -1434,54 +1460,17 @@ } }, "node_modules/@jest/reporters/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/@jest/reporters/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", "dev": true, - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, "engines": { "node": ">=10" }, "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/@jest/reporters/node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" + "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "node_modules/@jest/reporters/node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, "node_modules/@jest/reporters/node_modules/glob": { "version": "7.2.3", "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", @@ -1502,37 +1491,74 @@ "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/@jest/reporters/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "node_modules/@jest/reporters/node_modules/jest-message-util": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-28.1.3.tgz", + "integrity": "sha512-PFdn9Iewbt575zKPf1286Ht9EPoJmYT7P0kY+RibeYZ2XtOr53pDLEFoTWXbd1h4JiGiWpTBC84fc8xMXQMb7g==", + "dev": true, + "dependencies": { + "@babel/code-frame": "^7.12.13", + "@jest/types": "^28.1.3", + "@types/stack-utils": "^2.0.0", + "chalk": "^4.0.0", + "graceful-fs": "^4.2.9", + "micromatch": "^4.0.4", + "pretty-format": "^28.1.3", + "slash": "^3.0.0", + "stack-utils": "^2.0.3" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" + } + }, + "node_modules/@jest/reporters/node_modules/jest-util": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-28.1.3.tgz", + "integrity": "sha512-XdqfpHwpcSRko/C35uLYFM2emRAltIIKZiJ9eAmhjsj0CqZMa0p1ib0R5fWIqGhn1a103DebTbpqIaP1qCQ6tQ==", "dev": true, + "dependencies": { + "@jest/types": "^28.1.3", + "@types/node": "*", + "chalk": "^4.0.0", + "ci-info": "^3.2.0", + "graceful-fs": "^4.2.9", + "picomatch": "^2.2.3" + }, "engines": { - "node": ">=8" + "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" } }, - "node_modules/@jest/reporters/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "node_modules/@jest/reporters/node_modules/pretty-format": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-28.1.3.tgz", + "integrity": "sha512-8gFb/To0OmxHR9+ZTb14Df2vNxdGCX8g1xWGUTqUw5TiZvcQf5sHKObd5UcPyLLyowNwDAMTF3XWOG1B6mxl1Q==", "dev": true, "dependencies": { - "has-flag": "^4.0.0" + "@jest/schemas": "^28.1.3", + "ansi-regex": "^5.0.1", + "ansi-styles": "^5.0.0", + "react-is": "^18.0.0" }, "engines": { - "node": ">=8" + "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" } }, + "node_modules/@jest/reporters/node_modules/react-is": { + "version": "18.2.0", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz", + "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==", + "dev": true + }, "node_modules/@jest/schemas": { - "version": "29.0.0", - "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-29.0.0.tgz", - "integrity": "sha512-3Ab5HgYIIAnS0HjqJHQYZS+zXc4tUmTmBH3z83ajI6afXp8X3ZtdLX+nXx+I7LNkJD7uN9LAVhgnjDgZa2z0kA==", + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-28.1.3.tgz", + "integrity": "sha512-/l/VWsdt/aBXgjshLWOFyFt3IVdYypu5y2Wn2rOO1un6nkqIn8SLXzgIMYXFyYsRWDyF5EthmKJMIdJvk08grg==", "dev": true, "dependencies": { "@sinclair/typebox": "^0.24.1" }, "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" } }, "node_modules/@jest/source-map": { @@ -1605,74 +1631,21 @@ "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" } }, - "node_modules/@jest/transform/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "node_modules/@jest/transform/node_modules/jest-util": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-28.1.3.tgz", + "integrity": "sha512-XdqfpHwpcSRko/C35uLYFM2emRAltIIKZiJ9eAmhjsj0CqZMa0p1ib0R5fWIqGhn1a103DebTbpqIaP1qCQ6tQ==", "dev": true, "dependencies": { - "color-convert": "^2.0.1" + "@jest/types": "^28.1.3", + "@types/node": "*", + "chalk": "^4.0.0", + "ci-info": "^3.2.0", + "graceful-fs": "^4.2.9", + "picomatch": "^2.2.3" }, "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/@jest/transform/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/@jest/transform/node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/@jest/transform/node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "node_modules/@jest/transform/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/@jest/transform/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" + "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" } }, "node_modules/@jest/types": { @@ -1692,105 +1665,24 @@ "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" } }, - "node_modules/@jest/types/node_modules/@jest/schemas": { - "version": "28.1.3", - "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-28.1.3.tgz", - "integrity": "sha512-/l/VWsdt/aBXgjshLWOFyFt3IVdYypu5y2Wn2rOO1un6nkqIn8SLXzgIMYXFyYsRWDyF5EthmKJMIdJvk08grg==", - "dev": true, - "dependencies": { - "@sinclair/typebox": "^0.24.1" - }, - "engines": { - "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" - } - }, - "node_modules/@jest/types/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/@jest/types/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/@jest/types/node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/@jest/types/node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "node_modules/@jest/types/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/@jest/types/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/@jridgewell/gen-mapping": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.1.1.tgz", - "integrity": "sha512-sQXCasFk+U8lWYEe66WxRDOE9PjVz4vSM51fTu3Hw+ClTpUSQb718772vH3pyS5pShp6lvQM7SxgIDXXXmOX7w==", + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.3.tgz", + "integrity": "sha512-HLhSWOLRi875zjjMG/r+Nv0oCW8umGb0BgEhyX3dDX3egwZtB8PqLnjz3yedt8R5StBrzcg4aBpnh8UA9D1BoQ==", "dev": true, "dependencies": { - "@jridgewell/set-array": "^1.0.0", - "@jridgewell/sourcemap-codec": "^1.4.10" + "@jridgewell/set-array": "^1.0.1", + "@jridgewell/sourcemap-codec": "^1.4.10", + "@jridgewell/trace-mapping": "^0.3.9" }, "engines": { "node": ">=6.0.0" } }, "node_modules/@jridgewell/resolve-uri": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.0.tgz", - "integrity": "sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w==", + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.1.tgz", + "integrity": "sha512-dSYZh7HhCDtCKm4QakX0xFpsRDqjjtZf/kjI/v3T3Nwt5r8/qz/M19F9ySyOqU94SXBmeG9ttTul+YnR4LOxFA==", "dev": true, "engines": { "node": ">=6.0.0" @@ -1806,19 +1698,31 @@ } }, "node_modules/@jridgewell/sourcemap-codec": { - "version": "1.4.14", - "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz", - "integrity": "sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==", + "version": "1.4.15", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz", + "integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==", "dev": true }, "node_modules/@jridgewell/trace-mapping": { - "version": "0.3.15", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.15.tgz", - "integrity": "sha512-oWZNOULl+UbhsgB51uuZzglikfIKSUBO/M9W2OfEjn7cmqoAiCgmv9lyACTUacZwBz0ITnJ2NqjU8Tx0DHL88g==", + "version": "0.3.20", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.20.tgz", + "integrity": "sha512-R8LcPeWZol2zR8mmH3JeKQ6QRCFb7XgUhV9ZlGhHLGyg4wpPiPZNQOOWhFZhxKw8u//yTbNGI42Bx/3paXEQ+Q==", "dev": true, "dependencies": { - "@jridgewell/resolve-uri": "^3.0.3", - "@jridgewell/sourcemap-codec": "^1.4.10" + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, + "node_modules/@noble/hashes": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.3.3.tgz", + "integrity": "sha512-V7/fPHgl+jsVPXqqeOzT8egNj2iBIVt+ECeMMG8TdcnTikP3oaBtUVqpT/gYCR68aEBJSF+XbYUxStjbFMqIIA==", + "dev": true, + "engines": { + "node": ">= 16" + }, + "funding": { + "url": "https://paulmillr.com/funding/" } }, "node_modules/@nodelib/fs.scandir": { @@ -1857,31 +1761,30 @@ } }, "node_modules/@playwright/test": { - "version": "1.25.2", - "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.25.2.tgz", - "integrity": "sha512-6qPznIR4Fw02OMbqXUPMG6bFFg1hDVNEdihKy0t9K0dmRbus1DyP5Q5XFQhGwEHQkLG5hrSfBuu9CW/foqhQHQ==", + "version": "1.39.0", + "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.39.0.tgz", + "integrity": "sha512-3u1iFqgzl7zr004bGPYiN/5EZpRUSFddQBra8Rqll5N0/vfpqlP9I9EXqAoGacuAbX6c9Ulg/Cjqglp5VkK6UQ==", "dev": true, "dependencies": { - "@types/node": "*", - "playwright-core": "1.25.2" + "playwright": "1.39.0" }, "bin": { "playwright": "cli.js" }, "engines": { - "node": ">=14" + "node": ">=16" } }, "node_modules/@sinclair/typebox": { - "version": "0.24.28", - "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.24.28.tgz", - "integrity": "sha512-dgJd3HLOkLmz4Bw50eZx/zJwtBq65nms3N9VBYu5LTjJ883oBFkTyXRlCB/ZGGwqYpJJHA5zW2Ibhl5ngITfow==", + "version": "0.24.51", + "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.24.51.tgz", + "integrity": "sha512-1P1OROm/rdubP5aFDSZQILU0vrLCJ4fvHt6EoqHEM+2D/G5MK3bIaymUKLit8Js9gbns5UyJnkP/TZROLw4tUA==", "dev": true }, "node_modules/@sinonjs/commons": { - "version": "1.8.3", - "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-1.8.3.tgz", - "integrity": "sha512-xkNcLAn/wZaX14RPlwizcKicDk9G3F8m2nU3L7Ukm5zBgTwiT0wsoFAHx9Jq56fJA1z/7uKGtCRu16sOUCLIHQ==", + "version": "1.8.6", + "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-1.8.6.tgz", + "integrity": "sha512-Ky+XkAkqPZSm3NLBeUng77EBQl3cmeJhITaGHdYH8kjVB+aun3S4XBRti2zt17mtt0mIUDiNxYeoJm6drVvBJQ==", "dev": true, "dependencies": { "type-detect": "4.0.8" @@ -1897,31 +1800,31 @@ } }, "node_modules/@types/babel__core": { - "version": "7.1.19", - "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.1.19.tgz", - "integrity": "sha512-WEOTgRsbYkvA/KCsDwVEGkd7WAr1e3g31VHQ8zy5gul/V1qKullU/BU5I68X5v7V3GnB9eotmom4v5a5gjxorw==", + "version": "7.20.4", + "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.4.tgz", + "integrity": "sha512-mLnSC22IC4vcWiuObSRjrLd9XcBTGf59vUSoq2jkQDJ/QQ8PMI9rSuzE+aEV8karUMbskw07bKYoUJCKTUaygg==", "dev": true, "dependencies": { - "@babel/parser": "^7.1.0", - "@babel/types": "^7.0.0", + "@babel/parser": "^7.20.7", + "@babel/types": "^7.20.7", "@types/babel__generator": "*", "@types/babel__template": "*", "@types/babel__traverse": "*" } }, "node_modules/@types/babel__generator": { - "version": "7.6.4", - "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.6.4.tgz", - "integrity": "sha512-tFkciB9j2K755yrTALxD44McOrk+gfpIpvC3sxHjRawj6PfnQxrse4Clq5y/Rq+G3mrBurMax/lG8Qn2t9mSsg==", + "version": "7.6.7", + "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.6.7.tgz", + "integrity": "sha512-6Sfsq+EaaLrw4RmdFWE9Onp63TOUue71AWb4Gpa6JxzgTYtimbM086WnYTy2U67AofR++QKCo08ZP6pwx8YFHQ==", "dev": true, "dependencies": { "@babel/types": "^7.0.0" } }, "node_modules/@types/babel__template": { - "version": "7.4.1", - "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.1.tgz", - "integrity": "sha512-azBFKemX6kMg5Io+/rdGT0dkGreboUVR0Cdm3fz9QJWpaQGJRQXl7C+6hOTCZcMll7KFyEQpgbYI2lHdsS4U7g==", + "version": "7.4.4", + "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.4.tgz", + "integrity": "sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==", "dev": true, "dependencies": { "@babel/parser": "^7.1.0", @@ -1929,18 +1832,18 @@ } }, "node_modules/@types/babel__traverse": { - "version": "7.18.0", - "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.18.0.tgz", - "integrity": "sha512-v4Vwdko+pgymgS+A2UIaJru93zQd85vIGWObM5ekZNdXCKtDYqATlEYnWgfo86Q6I1Lh0oXnksDnMU1cwmlPDw==", + "version": "7.20.4", + "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.20.4.tgz", + "integrity": "sha512-mSM/iKUk5fDDrEV/e83qY+Cr3I1+Q3qqTuEn++HAWYjEa1+NxZr6CNrcJGf2ZTnq4HoFGC3zaTPZTobCzCFukA==", "dev": true, "dependencies": { - "@babel/types": "^7.3.0" + "@babel/types": "^7.20.7" } }, "node_modules/@types/graceful-fs": { - "version": "4.1.5", - "resolved": "https://registry.npmjs.org/@types/graceful-fs/-/graceful-fs-4.1.5.tgz", - "integrity": "sha512-anKkLmZZ+xm4p8JWBf4hElkM4XR+EZeA2M9BAkkTldmcyDY4mbdIJnRghDJH3Ov5ooY7/UAoENtmdMSkaAd7Cw==", + "version": "4.1.9", + "resolved": "https://registry.npmjs.org/@types/graceful-fs/-/graceful-fs-4.1.9.tgz", + "integrity": "sha512-olP3sd1qOEe5dXTSaFvQG+02VdRXcdytWLAZsAq1PecU8uqQAhkrnbli7DagjtXKW/Bl7YJbUsa8MPcuc8LHEQ==", "dev": true, "dependencies": { "@types/node": "*" @@ -1953,91 +1856,102 @@ "dev": true }, "node_modules/@types/istanbul-lib-coverage": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.3.tgz", - "integrity": "sha512-sz7iLqvVUg1gIedBOvlkxPlc8/uVzyS5OwGz1cKjXzkl3FpL3al0crU8YGU1WoHkxn0Wxbw5tyi6hvzJKNzFsw==", + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.6.tgz", + "integrity": "sha512-2QF/t/auWm0lsy8XtKVPG19v3sSOQlJe/YHZgfjb/KBBHOGSV+J2q/S671rcq9uTBrLAXmZpqJiaQbMT+zNU1w==", "dev": true }, "node_modules/@types/istanbul-lib-report": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@types/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz", - "integrity": "sha512-plGgXAPfVKFoYfa9NpYDAkseG+g6Jr294RqeqcqDixSbU34MZVJRi/P+7Y8GDpzkEwLaGZZOpKIEmeVZNtKsrg==", + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@types/istanbul-lib-report/-/istanbul-lib-report-3.0.3.tgz", + "integrity": "sha512-NQn7AHQnk/RSLOxrBbGyJM/aVQ+pjj5HCgasFxc0K/KhoATfQ/47AyUl15I2yBUpihjmas+a+VJBOqecrFH+uA==", "dev": true, "dependencies": { "@types/istanbul-lib-coverage": "*" } }, "node_modules/@types/istanbul-reports": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.1.tgz", - "integrity": "sha512-c3mAZEuK0lvBp8tmuL74XRKn1+y2dcwOUpH7x4WrF6gk1GIgiluDRgMYQtw2OFcBvAJWlt6ASU3tSqxp0Uu0Aw==", + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.4.tgz", + "integrity": "sha512-pk2B1NWalF9toCRu6gjBzR69syFjP4Od8WRAX+0mmf9lAjCRicLOWc+ZrxZHx/0XRjotgkF9t6iaMJ+aXcOdZQ==", "dev": true, "dependencies": { "@types/istanbul-lib-report": "*" } }, "node_modules/@types/jest": { - "version": "27.0.3", - "resolved": "https://registry.npmjs.org/@types/jest/-/jest-27.0.3.tgz", - "integrity": "sha512-cmmwv9t7gBYt7hNKH5Spu7Kuu/DotGa+Ff+JGRKZ4db5eh8PnKS4LuebJ3YLUoyOyIHraTGyULn23YtEAm0VSg==", + "version": "27.5.2", + "resolved": "https://registry.npmjs.org/@types/jest/-/jest-27.5.2.tgz", + "integrity": "sha512-mpT8LJJ4CMeeahobofYWIjFo0xonRS/HfxnVEPMPFSQdGUt1uHCnoPT7Zhb+sjDU2wz0oKV0OLUR0WzrHNgfeA==", "dev": true, "dependencies": { - "jest-diff": "^27.0.0", + "jest-matcher-utils": "^27.0.0", "pretty-format": "^27.0.0" } }, "node_modules/@types/json-schema": { - "version": "7.0.9", - "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.9.tgz", - "integrity": "sha512-qcUXuemtEu+E5wZSJHNxUXeCZhAfXKQ41D+duX+VYPde7xyEVZci+/oXKJL13tnRs9lR2pr4fod59GT6/X1/yQ==", + "version": "7.0.15", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", + "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", "dev": true }, "node_modules/@types/node": { - "version": "18.14.2", - "resolved": "https://registry.npmjs.org/@types/node/-/node-18.14.2.tgz", - "integrity": "sha512-1uEQxww3DaghA0RxqHx0O0ppVlo43pJhepY51OxuQIKHpjbnYLA7vcdwioNPzIqmC2u3I/dmylcqjlh0e7AyUA==", - "dev": true + "version": "18.18.9", + "resolved": "https://registry.npmjs.org/@types/node/-/node-18.18.9.tgz", + "integrity": "sha512-0f5klcuImLnG4Qreu9hPj/rEfFq6YRc5n2mAjSsH+ec/mJL+3voBH0+8T7o8RpFjH7ovc+TRsL/c7OYIQsPTfQ==", + "dev": true, + "dependencies": { + "undici-types": "~5.26.4" + } }, "node_modules/@types/prettier": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/@types/prettier/-/prettier-2.7.0.tgz", - "integrity": "sha512-RI1L7N4JnW5gQw2spvL7Sllfuf1SaHdrZpCHiBlCXjIlufi1SMNnbu2teze3/QE67Fg2tBlH7W+mi4hVNk4p0A==", + "version": "2.7.3", + "resolved": "https://registry.npmjs.org/@types/prettier/-/prettier-2.7.3.tgz", + "integrity": "sha512-+68kP9yzs4LMp7VNh8gdzMSPZFL44MLGqiHWvttYJe+6qnuVr4Ek9wSBQoveqY/r+LwjCcU29kNVkidwim+kYA==", + "dev": true + }, + "node_modules/@types/semver": { + "version": "7.5.5", + "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.5.5.tgz", + "integrity": "sha512-+d+WYC1BxJ6yVOgUgzK8gWvp5qF8ssV5r4nsDcZWKRWcDQLQ619tvWAxJQYGgBrO1MnLJC7a5GtiYsAoQ47dJg==", "dev": true }, "node_modules/@types/stack-utils": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.1.tgz", - "integrity": "sha512-Hl219/BT5fLAaz6NDkSuhzasy49dwQS/DSdu4MdggFB8zcXv7vflBI3xp7FEmkmdDkBUI2bPUNeMttp2knYdxw==", + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.3.tgz", + "integrity": "sha512-9aEbYZ3TbYMznPdcdr3SmIrLXwC/AKZXQeCf9Pgao5CKb8CyHuEX5jzWPTkvregvhRJHcpRO6BFoGW9ycaOkYw==", "dev": true }, "node_modules/@types/yargs": { - "version": "17.0.11", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.11.tgz", - "integrity": "sha512-aB4y9UDUXTSMxmM4MH+YnuR0g5Cph3FLQBoWoMB21DSvFVAxRVEHEMx3TLh+zUZYMCQtKiqazz0Q4Rre31f/OA==", + "version": "17.0.31", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.31.tgz", + "integrity": "sha512-bocYSx4DI8TmdlvxqGpVNXOgCNR1Jj0gNPhhAY+iz1rgKDAaYrAYdFYnhDV1IFuiuVc9HkOwyDcFxaTElF3/wg==", "dev": true, "dependencies": { "@types/yargs-parser": "*" } }, "node_modules/@types/yargs-parser": { - "version": "20.2.1", - "resolved": "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-20.2.1.tgz", - "integrity": "sha512-7tFImggNeNBVMsn0vLrpn1H1uPrUBdnARPTpZoitY37ZrdJREzf7I16tMrlK3hen349gr1NYh8CmZQa7CTG6Aw==", + "version": "21.0.3", + "resolved": "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-21.0.3.tgz", + "integrity": "sha512-I4q9QU9MQv4oEOz4tAHJtNz1cwuLxn2F3xcc2iV5WdqLPpUnj30aUuxt1mAxYTG+oe8CZMV/+6rU4S4gRDzqtQ==", "dev": true }, "node_modules/@typescript-eslint/eslint-plugin": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.5.0.tgz", - "integrity": "sha512-4bV6fulqbuaO9UMXU0Ia0o6z6if+kmMRW8rMRyfqXj/eGrZZRGedS4n0adeGNnjr8LKAM495hrQ7Tea52UWmQA==", - "dev": true, - "dependencies": { - "@typescript-eslint/experimental-utils": "5.5.0", - "@typescript-eslint/scope-manager": "5.5.0", - "debug": "^4.3.2", - "functional-red-black-tree": "^1.0.1", - "ignore": "^5.1.8", - "regexpp": "^3.2.0", - "semver": "^7.3.5", + "version": "5.62.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.62.0.tgz", + "integrity": "sha512-TiZzBSJja/LbhNPvk6yc0JrX9XqhQ0hdh6M2svYfsHGejaKFIAGd9MQ+ERIMzLGlN/kZoYIgdxFV0PuljTKXag==", + "dev": true, + "dependencies": { + "@eslint-community/regexpp": "^4.4.0", + "@typescript-eslint/scope-manager": "5.62.0", + "@typescript-eslint/type-utils": "5.62.0", + "@typescript-eslint/utils": "5.62.0", + "debug": "^4.3.4", + "graphemer": "^1.4.0", + "ignore": "^5.2.0", + "natural-compare-lite": "^1.4.0", + "semver": "^7.3.7", "tsutils": "^3.21.0" }, "engines": { @@ -2057,54 +1971,42 @@ } } }, - "node_modules/@typescript-eslint/eslint-plugin/node_modules/semver": { - "version": "7.3.5", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", - "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", + "node_modules/@typescript-eslint/parser": { + "version": "5.62.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.62.0.tgz", + "integrity": "sha512-VlJEV0fOQ7BExOsHYAGrgbEiZoi8D+Bl2+f6V2RrXerRSylnp+ZBHmPvaIa8cz0Ajx7WO7Z5RqfgYg7ED1nRhA==", "dev": true, + "peer": true, "dependencies": { - "lru-cache": "^6.0.0" - }, - "bin": { - "semver": "bin/semver.js" + "@typescript-eslint/scope-manager": "5.62.0", + "@typescript-eslint/types": "5.62.0", + "@typescript-eslint/typescript-estree": "5.62.0", + "debug": "^4.3.4" }, "engines": { - "node": ">=10" - } - }, - "node_modules/@typescript-eslint/eslint-plugin/node_modules/tslib": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", - "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", - "dev": true - }, - "node_modules/@typescript-eslint/eslint-plugin/node_modules/tsutils": { - "version": "3.21.0", - "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-3.21.0.tgz", - "integrity": "sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==", - "dev": true, - "dependencies": { - "tslib": "^1.8.1" + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" }, - "engines": { - "node": ">= 6" + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "typescript": ">=2.8.0 || >= 3.2.0-dev || >= 3.3.0-dev || >= 3.4.0-dev || >= 3.5.0-dev || >= 3.6.0-dev || >= 3.6.0-beta || >= 3.7.0-dev || >= 3.7.0-beta" + "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } } }, - "node_modules/@typescript-eslint/experimental-utils": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/experimental-utils/-/experimental-utils-5.5.0.tgz", - "integrity": "sha512-kjWeeVU+4lQ1SLYErRKV5yDXbWDPkpbzTUUlfAUifPYvpX0qZlrcCZ96/6oWxt3QxtK5WVhXz+KsnwW9cIW+3A==", + "node_modules/@typescript-eslint/scope-manager": { + "version": "5.62.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.62.0.tgz", + "integrity": "sha512-VXuvVvZeQCQb5Zgf4HAxc04q5j+WrNAtNh9OwCsCgpKqESMTu3tF/jhZ3xG6T4NZwWl65Bg8KuS2uEvhSfLl0w==", "dev": true, "dependencies": { - "@types/json-schema": "^7.0.9", - "@typescript-eslint/scope-manager": "5.5.0", - "@typescript-eslint/types": "5.5.0", - "@typescript-eslint/typescript-estree": "5.5.0", - "eslint-scope": "^5.1.1", - "eslint-utils": "^3.0.0" + "@typescript-eslint/types": "5.62.0", + "@typescript-eslint/visitor-keys": "5.62.0" }, "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" @@ -2112,22 +2014,18 @@ "funding": { "type": "opencollective", "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "eslint": "*" } }, - "node_modules/@typescript-eslint/parser": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.5.0.tgz", - "integrity": "sha512-JsXBU+kgQOAgzUn2jPrLA+Rd0Y1dswOlX3hp8MuRO1hQDs6xgHtbCXEiAu7bz5hyVURxbXcA2draasMbNqrhmg==", + "node_modules/@typescript-eslint/type-utils": { + "version": "5.62.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.62.0.tgz", + "integrity": "sha512-xsSQreu+VnfbqQpW5vnCJdq1Z3Q0U31qiWmRhr98ONQmcp/yhiPJFPq8MXiJVLiksmOKSjIldZzkebzHuCGzew==", "dev": true, - "peer": true, "dependencies": { - "@typescript-eslint/scope-manager": "5.5.0", - "@typescript-eslint/types": "5.5.0", - "@typescript-eslint/typescript-estree": "5.5.0", - "debug": "^4.3.2" + "@typescript-eslint/typescript-estree": "5.62.0", + "@typescript-eslint/utils": "5.62.0", + "debug": "^4.3.4", + "tsutils": "^3.21.0" }, "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" @@ -2137,7 +2035,7 @@ "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0" + "eslint": "*" }, "peerDependenciesMeta": { "typescript": { @@ -2145,27 +2043,10 @@ } } }, - "node_modules/@typescript-eslint/scope-manager": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.5.0.tgz", - "integrity": "sha512-0/r656RmRLo7CbN4Mdd+xZyPJ/fPCKhYdU6mnZx+8msAD8nJSP8EyCFkzbd6vNVZzZvWlMYrSNekqGrCBqFQhg==", - "dev": true, - "dependencies": { - "@typescript-eslint/types": "5.5.0", - "@typescript-eslint/visitor-keys": "5.5.0" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } - }, "node_modules/@typescript-eslint/types": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.5.0.tgz", - "integrity": "sha512-OaYTqkW3GnuHxqsxxJ6KypIKd5Uw7bFiQJZRyNi1jbMJnK3Hc/DR4KwB6KJj6PBRkJJoaNwzMNv9vtTk87JhOg==", + "version": "5.62.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.62.0.tgz", + "integrity": "sha512-87NVngcbVXUahrRTqIK27gD2t5Cu1yuCXxbLcFtCzZGlfyVWWh8mLHkoxzjsB6DDNnvdL+fW8MiwPEJyGJQDgQ==", "dev": true, "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" @@ -2176,17 +2057,17 @@ } }, "node_modules/@typescript-eslint/typescript-estree": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.5.0.tgz", - "integrity": "sha512-pVn8btYUiYrjonhMAO0yG8lm7RApzy2L4RC7Td/mC/qFkyf6vRbGyZozoA94+w6D2Y2GRqpMoCWcwx/EUOzyoQ==", + "version": "5.62.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.62.0.tgz", + "integrity": "sha512-CmcQ6uY7b9y694lKdRB8FEel7JbU/40iSAPomu++SjLMntB+2Leay2LO6i8VnJk58MtE9/nQSFIH6jpyRWyYzA==", "dev": true, "dependencies": { - "@typescript-eslint/types": "5.5.0", - "@typescript-eslint/visitor-keys": "5.5.0", - "debug": "^4.3.2", - "globby": "^11.0.4", + "@typescript-eslint/types": "5.62.0", + "@typescript-eslint/visitor-keys": "5.62.0", + "debug": "^4.3.4", + "globby": "^11.1.0", "is-glob": "^4.0.3", - "semver": "^7.3.5", + "semver": "^7.3.7", "tsutils": "^3.21.0" }, "engines": { @@ -2202,50 +2083,40 @@ } } }, - "node_modules/@typescript-eslint/typescript-estree/node_modules/semver": { - "version": "7.3.5", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", - "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", + "node_modules/@typescript-eslint/utils": { + "version": "5.62.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.62.0.tgz", + "integrity": "sha512-n8oxjeb5aIbPFEtmQxQYOLI0i9n5ySBEY/ZEHHZqKQSFnxio1rv6dthascc9dLuwrL0RC5mPCxB7vnAVGAYWAQ==", "dev": true, "dependencies": { - "lru-cache": "^6.0.0" - }, - "bin": { - "semver": "bin/semver.js" + "@eslint-community/eslint-utils": "^4.2.0", + "@types/json-schema": "^7.0.9", + "@types/semver": "^7.3.12", + "@typescript-eslint/scope-manager": "5.62.0", + "@typescript-eslint/types": "5.62.0", + "@typescript-eslint/typescript-estree": "5.62.0", + "eslint-scope": "^5.1.1", + "semver": "^7.3.7" }, "engines": { - "node": ">=10" - } - }, - "node_modules/@typescript-eslint/typescript-estree/node_modules/tslib": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", - "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", - "dev": true - }, - "node_modules/@typescript-eslint/typescript-estree/node_modules/tsutils": { - "version": "3.21.0", - "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-3.21.0.tgz", - "integrity": "sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==", - "dev": true, - "dependencies": { - "tslib": "^1.8.1" + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" }, - "engines": { - "node": ">= 6" + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "typescript": ">=2.8.0 || >= 3.2.0-dev || >= 3.3.0-dev || >= 3.4.0-dev || >= 3.5.0-dev || >= 3.6.0-dev || >= 3.6.0-beta || >= 3.7.0-dev || >= 3.7.0-beta" + "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0" } }, "node_modules/@typescript-eslint/visitor-keys": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.5.0.tgz", - "integrity": "sha512-4GzJ1kRtsWzHhdM40tv0ZKHNSbkDhF0Woi/TDwVJX6UICwJItvP7ZTXbjTkCdrors7ww0sYe0t+cIKDAJwZ7Kw==", + "version": "5.62.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.62.0.tgz", + "integrity": "sha512-07ny+LHRzQXepkGg6w0mFY41fVUNBrL2Roj/++7V1txKugfjm/Ci/qSND03r2RhlJhJYMcTn9AhhSSqQp0Ysyw==", "dev": true, "dependencies": { - "@typescript-eslint/types": "5.5.0", - "eslint-visitor-keys": "^3.0.0" + "@typescript-eslint/types": "5.62.0", + "eslint-visitor-keys": "^3.3.0" }, "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" @@ -2255,10 +2126,16 @@ "url": "https://opencollective.com/typescript-eslint" } }, + "node_modules/@ungap/structured-clone": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.2.0.tgz", + "integrity": "sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==", + "dev": true + }, "node_modules/acorn": { - "version": "8.6.0", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.6.0.tgz", - "integrity": "sha512-U1riIR+lBSNi3IbxtaHOIKdH8sLFv3NYfNv8sg7ZsNhcfl4HF2++BfqqrNAxoCLQW1iiylOj76ecnaUxz+z9yw==", + "version": "8.11.2", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.11.2.tgz", + "integrity": "sha512-nc0Axzp/0FILLEVsm4fNwLCwMttvhEI263QtVPQcbpfZZ3ts0hLsZGOpE6czNlid7CJ9MlyH8reXkpsf3YUY4w==", "dev": true, "bin": { "acorn": "bin/acorn" @@ -2292,15 +2169,6 @@ "url": "https://github.com/sponsors/epoberezkin" } }, - "node_modules/ansi-colors": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz", - "integrity": "sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==", - "dev": true, - "engines": { - "node": ">=6" - } - }, "node_modules/ansi-escapes": { "version": "4.3.2", "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", @@ -2316,6 +2184,18 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/ansi-escapes/node_modules/type-fest": { + "version": "0.21.3", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz", + "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/ansi-regex": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", @@ -2326,27 +2206,30 @@ } }, "node_modules/ansi-sequence-parser": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/ansi-sequence-parser/-/ansi-sequence-parser-1.1.0.tgz", - "integrity": "sha512-lEm8mt52to2fT8GhciPCGeCXACSz2UwIN4X2e2LJSnZ5uAbn2/dsYdOmUXq0AtWS5cpAupysIneExOgH0Vd2TQ==", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ansi-sequence-parser/-/ansi-sequence-parser-1.1.1.tgz", + "integrity": "sha512-vJXt3yiaUL4UU546s3rPXlsry/RnM730G1+HkpKE012AN0sx1eOrxSu95oKDIonskeLTijMgqWZ3uDEe3NFvyg==", "dev": true }, "node_modules/ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", "dev": true, "dependencies": { - "color-convert": "^1.9.0" + "color-convert": "^2.0.1" }, "engines": { - "node": ">=4" + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, "node_modules/anymatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.2.tgz", - "integrity": "sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg==", + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", + "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", "dev": true, "dependencies": { "normalize-path": "^3.0.0", @@ -2392,76 +2275,6 @@ "@babel/core": "^7.8.0" } }, - "node_modules/babel-jest/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/babel-jest/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/babel-jest/node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/babel-jest/node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "node_modules/babel-jest/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/babel-jest/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/babel-plugin-istanbul": { "version": "6.1.1", "resolved": "https://registry.npmjs.org/babel-plugin-istanbul/-/babel-plugin-istanbul-6.1.1.tgz", @@ -2566,9 +2379,9 @@ } }, "node_modules/browserslist": { - "version": "4.21.3", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.21.3.tgz", - "integrity": "sha512-898rgRXLAyRkM1GryrrBHGkqA5hlpkV5MhtZwg9QXeiyLUYs2k00Un05aX5l2/yJIOObYKOpS2JNo8nJDE7fWQ==", + "version": "4.22.1", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.22.1.tgz", + "integrity": "sha512-FEVc202+2iuClEhZhrWy6ZiAcRLvNMyYcxZ8raemul1DYVOVdFsbqckWLdsixQZCpJlwe77Z3UTalE7jsjnKfQ==", "dev": true, "funding": [ { @@ -2578,13 +2391,17 @@ { "type": "tidelift", "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" } ], "dependencies": { - "caniuse-lite": "^1.0.30001370", - "electron-to-chromium": "^1.4.202", - "node-releases": "^2.0.6", - "update-browserslist-db": "^1.0.5" + "caniuse-lite": "^1.0.30001541", + "electron-to-chromium": "^1.4.535", + "node-releases": "^2.0.13", + "update-browserslist-db": "^1.0.13" }, "bin": { "browserslist": "cli.js" @@ -2620,6 +2437,14 @@ "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", "dev": true }, + "node_modules/cachedir": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/cachedir/-/cachedir-2.4.0.tgz", + "integrity": "sha512-9EtFOZR8g22CL7BWjJ9BUx1+A/djkofnyW3aOXZORNW2kxoUpx2h+uN2cOqwPmFhnpVmxg+KW2OjOSgChTEvsQ==", + "engines": { + "node": ">=6" + } + }, "node_modules/callsites": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", @@ -2639,9 +2464,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001384", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001384.tgz", - "integrity": "sha512-BBWt57kqWbc0GYZXb47wTXpmAgqr5LSibPzNjk/AWMdmJMQhLqOl3c/Kd4OAU/tu4NLfYkMx8Tlq3RVBkOBolQ==", + "version": "1.0.30001562", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001562.tgz", + "integrity": "sha512-kfte3Hym//51EdX4239i+Rmp20EsLIYGdPkERegTgU19hQWCRhsRFGKHTliUlsry53tv17K7n077Kqa0WJU4ng==", "dev": true, "funding": [ { @@ -2651,21 +2476,27 @@ { "type": "tidelift", "url": "https://tidelift.com/funding/github/npm/caniuse-lite" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" } ] }, "node_modules/chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", "dev": true, "dependencies": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" }, "engines": { - "node": ">=4" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" } }, "node_modules/char-regex": { @@ -2678,26 +2509,38 @@ } }, "node_modules/ci-info": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.3.0.tgz", - "integrity": "sha512-riT/3vI5YpVH6/qomlDnJow6TBee2PBKSEpx3O32EGPYbWGIRsIlGRms3Sm74wYE1JMo8RnO04Hb12+v1J5ICw==", - "dev": true + "version": "3.9.0", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.9.0.tgz", + "integrity": "sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/sibiraj-s" + } + ], + "engines": { + "node": ">=8" + } }, "node_modules/cjs-module-lexer": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-1.2.2.tgz", - "integrity": "sha512-cOU9usZw8/dXIXKtwa8pM0OTJQuJkxMN6w30csNRUerHfeQ5R6U3kkU/FtJeIf3M202OHfY2U8ccInBG7/xogA==", + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-1.2.3.tgz", + "integrity": "sha512-0TNiGstbQmCFwt4akjjBg5pLRTSyj/PkWQ1ZoO2zntmg9yLqSRxwEa4iCfQLGjqhiqBfOJa7W/E8wfGrTDmlZQ==", "dev": true }, "node_modules/cliui": { - "version": "7.0.4", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", - "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", + "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", "dev": true, "dependencies": { "string-width": "^4.2.0", - "strip-ansi": "^6.0.0", + "strip-ansi": "^6.0.1", "wrap-ansi": "^7.0.0" + }, + "engines": { + "node": ">=12" } }, "node_modules/co": { @@ -2711,40 +2554,40 @@ } }, "node_modules/collect-v8-coverage": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/collect-v8-coverage/-/collect-v8-coverage-1.0.1.tgz", - "integrity": "sha512-iBPtljfCNcTKNAto0KEtDfZ3qzjJvqE3aTGZsbhjSBlorqpXJlaWWtPO35D+ZImoC3KWejX64o+yPGxhWSTzfg==", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/collect-v8-coverage/-/collect-v8-coverage-1.0.2.tgz", + "integrity": "sha512-lHl4d5/ONEbLlJvaJNtsF/Lz+WvB07u2ycqTYbdrq7UypDXailES4valYb2eWiJFxZlVmpGekfqoxQhzyFdT4Q==", "dev": true }, "node_modules/color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", "dev": true, "dependencies": { - "color-name": "1.1.3" + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" } }, "node_modules/color-name": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", "dev": true }, "node_modules/concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", "dev": true }, "node_modules/convert-source-map": { - "version": "1.8.0", - "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.8.0.tgz", - "integrity": "sha512-+OQdjP49zViI/6i7nIJpA8rAl4sV/JdPfU9nZs3VqOwGIgizICvuN2ru6fMd+4llL0tar18UYJXfZ/TWtmhUjA==", - "dev": true, - "dependencies": { - "safe-buffer": "~5.1.1" - } + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.9.0.tgz", + "integrity": "sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==", + "dev": true }, "node_modules/cross-spawn": { "version": "7.0.3", @@ -2761,9 +2604,9 @@ } }, "node_modules/debug": { - "version": "4.3.3", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.3.tgz", - "integrity": "sha512-/zxw5+vh1Tfv+4Qn7a5nsbcJKPaSvCDhojn6FEl9vupwK2VCSDtEiEtqr8DFtzYFOdz63LBkxec7DYuc2jon6Q==", + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", "dev": true, "dependencies": { "ms": "2.1.2" @@ -2790,18 +2633,18 @@ "dev": true }, "node_modules/deepmerge": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.2.2.tgz", - "integrity": "sha512-FJ3UgI4gIl+PHZm53knsuSFpE+nESMr7M4v9QcgB7S63Kj/6WqMiFQJpBBYz1Pt+66bZpP3Q7Lye0Oo9MPKEdg==", + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz", + "integrity": "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==", "dev": true, "engines": { "node": ">=0.10.0" } }, "node_modules/detect-gpu": { - "version": "5.0.5", - "resolved": "https://registry.npmjs.org/detect-gpu/-/detect-gpu-5.0.5.tgz", - "integrity": "sha512-gKBKx8mj0Fi/8DnjuzxU+aNYF1yWvPxtiOV9o72539wW0HP3ZTJVol/0FUasvdxIY1Bhi19SwIINqXGO+RB/sA==", + "version": "5.0.37", + "resolved": "https://registry.npmjs.org/detect-gpu/-/detect-gpu-5.0.37.tgz", + "integrity": "sha512-EraWs84faI4iskB4qvE39bevMIazEvd1RpoyGLOBesRLbiz6eMeJqqRPHjEFClfRByYZzi9IzU35rBXIO76oDw==", "dependencies": { "webgl-constants": "^1.1.1" } @@ -2849,9 +2692,9 @@ } }, "node_modules/electron-to-chromium": { - "version": "1.4.233", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.233.tgz", - "integrity": "sha512-ejwIKXTg1wqbmkcRJh9Ur3hFGHFDZDw1POzdsVrB2WZjgRuRMHIQQKNpe64N/qh3ZtH2otEoRoS+s6arAAuAAw==", + "version": "1.4.583", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.583.tgz", + "integrity": "sha512-93y1gcONABZ7uqYe/JWDVQP/Pj/sQSunF0HVAPdlg/pfBnOyBMLlQUxWvkqcljJg1+W6cjvPuYD+r1Th9Tn8mA==", "dev": true }, "node_modules/emittery": { @@ -2872,18 +2715,6 @@ "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", "dev": true }, - "node_modules/enquirer": { - "version": "2.3.6", - "resolved": "https://registry.npmjs.org/enquirer/-/enquirer-2.3.6.tgz", - "integrity": "sha512-yjNnPr315/FjS4zIsUxYguYUPP2e1NK4d7E7ZOLiyYCcbFBiTMyID+2wvm2w6+pZ/odMA7cRkjhsPbltwBOrLg==", - "dev": true, - "dependencies": { - "ansi-colors": "^4.1.1" - }, - "engines": { - "node": ">=8.6" - } - }, "node_modules/error-ex": { "version": "1.3.2", "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", @@ -2894,9 +2725,9 @@ } }, "node_modules/esbuild": { - "version": "0.19.2", - "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.19.2.tgz", - "integrity": "sha512-G6hPax8UbFakEj3hWO0Vs52LQ8k3lnBhxZWomUJDxfz3rZTLqF5k/FCzuNdLx2RbpBiQQF9H9onlDDH1lZsnjg==", + "version": "0.19.5", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.19.5.tgz", + "integrity": "sha512-bUxalY7b1g8vNhQKdB24QDmHeY4V4tw/s6Ak5z+jJX9laP5MoQseTOMemAr0gxssjNcH0MCViG8ONI2kksvfFQ==", "dev": true, "hasInstallScript": true, "bin": { @@ -2906,28 +2737,28 @@ "node": ">=12" }, "optionalDependencies": { - "@esbuild/android-arm": "0.19.2", - "@esbuild/android-arm64": "0.19.2", - "@esbuild/android-x64": "0.19.2", - "@esbuild/darwin-arm64": "0.19.2", - "@esbuild/darwin-x64": "0.19.2", - "@esbuild/freebsd-arm64": "0.19.2", - "@esbuild/freebsd-x64": "0.19.2", - "@esbuild/linux-arm": "0.19.2", - "@esbuild/linux-arm64": "0.19.2", - "@esbuild/linux-ia32": "0.19.2", - "@esbuild/linux-loong64": "0.19.2", - "@esbuild/linux-mips64el": "0.19.2", - "@esbuild/linux-ppc64": "0.19.2", - "@esbuild/linux-riscv64": "0.19.2", - "@esbuild/linux-s390x": "0.19.2", - "@esbuild/linux-x64": "0.19.2", - "@esbuild/netbsd-x64": "0.19.2", - "@esbuild/openbsd-x64": "0.19.2", - "@esbuild/sunos-x64": "0.19.2", - "@esbuild/win32-arm64": "0.19.2", - "@esbuild/win32-ia32": "0.19.2", - "@esbuild/win32-x64": "0.19.2" + "@esbuild/android-arm": "0.19.5", + "@esbuild/android-arm64": "0.19.5", + "@esbuild/android-x64": "0.19.5", + "@esbuild/darwin-arm64": "0.19.5", + "@esbuild/darwin-x64": "0.19.5", + "@esbuild/freebsd-arm64": "0.19.5", + "@esbuild/freebsd-x64": "0.19.5", + "@esbuild/linux-arm": "0.19.5", + "@esbuild/linux-arm64": "0.19.5", + "@esbuild/linux-ia32": "0.19.5", + "@esbuild/linux-loong64": "0.19.5", + "@esbuild/linux-mips64el": "0.19.5", + "@esbuild/linux-ppc64": "0.19.5", + "@esbuild/linux-riscv64": "0.19.5", + "@esbuild/linux-s390x": "0.19.5", + "@esbuild/linux-x64": "0.19.5", + "@esbuild/netbsd-x64": "0.19.5", + "@esbuild/openbsd-x64": "0.19.5", + "@esbuild/sunos-x64": "0.19.5", + "@esbuild/win32-arm64": "0.19.5", + "@esbuild/win32-ia32": "0.19.5", + "@esbuild/win32-x64": "0.19.5" } }, "node_modules/escalade": { @@ -2940,58 +2771,61 @@ } }, "node_modules/escape-string-regexp": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", "dev": true, "engines": { - "node": ">=0.8.0" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/eslint": { - "version": "8.3.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.3.0.tgz", - "integrity": "sha512-aIay56Ph6RxOTC7xyr59Kt3ewX185SaGnAr8eWukoPLeriCrvGjvAubxuvaXOfsxhtwV5g0uBOsyhAom4qJdww==", - "dev": true, - "dependencies": { - "@eslint/eslintrc": "^1.0.4", - "@humanwhocodes/config-array": "^0.6.0", - "ajv": "^6.10.0", + "version": "8.53.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.53.0.tgz", + "integrity": "sha512-N4VuiPjXDUa4xVeV/GC/RV3hQW9Nw+Y463lkWaKKXKYMvmRiRDAtfpuPFLN+E1/6ZhyR8J2ig+eVREnYgUsiag==", + "dev": true, + "dependencies": { + "@eslint-community/eslint-utils": "^4.2.0", + "@eslint-community/regexpp": "^4.6.1", + "@eslint/eslintrc": "^2.1.3", + "@eslint/js": "8.53.0", + "@humanwhocodes/config-array": "^0.11.13", + "@humanwhocodes/module-importer": "^1.0.1", + "@nodelib/fs.walk": "^1.2.8", + "@ungap/structured-clone": "^1.2.0", + "ajv": "^6.12.4", "chalk": "^4.0.0", "cross-spawn": "^7.0.2", "debug": "^4.3.2", "doctrine": "^3.0.0", - "enquirer": "^2.3.5", "escape-string-regexp": "^4.0.0", - "eslint-scope": "^7.1.0", - "eslint-utils": "^3.0.0", - "eslint-visitor-keys": "^3.1.0", - "espree": "^9.1.0", - "esquery": "^1.4.0", + "eslint-scope": "^7.2.2", + "eslint-visitor-keys": "^3.4.3", + "espree": "^9.6.1", + "esquery": "^1.4.2", "esutils": "^2.0.2", "fast-deep-equal": "^3.1.3", "file-entry-cache": "^6.0.1", - "functional-red-black-tree": "^1.0.1", - "glob-parent": "^6.0.1", - "globals": "^13.6.0", - "ignore": "^4.0.6", - "import-fresh": "^3.0.0", + "find-up": "^5.0.0", + "glob-parent": "^6.0.2", + "globals": "^13.19.0", + "graphemer": "^1.4.0", + "ignore": "^5.2.0", "imurmurhash": "^0.1.4", "is-glob": "^4.0.0", + "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.0.4", + "minimatch": "^3.1.2", "natural-compare": "^1.4.0", - "optionator": "^0.9.1", - "progress": "^2.0.0", - "regexpp": "^3.2.0", - "semver": "^7.2.1", + "optionator": "^0.9.3", "strip-ansi": "^6.0.1", - "strip-json-comments": "^3.1.0", - "text-table": "^0.2.0", - "v8-compile-cache": "^2.0.3" + "text-table": "^0.2.0" }, "bin": { "eslint": "bin/eslint.js" @@ -3016,240 +2850,89 @@ "node": ">=8.0.0" } }, - "node_modules/eslint-utils": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-3.0.0.tgz", - "integrity": "sha512-uuQC43IGctw68pJA1RgbQS8/NP7rch6Cwd4j3ZBtgo4/8Flj4eGE7ZYSZRN3iq5pVUv6GPdW5Z1RFleo84uLDA==", + "node_modules/eslint-visitor-keys": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", + "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", "dev": true, - "dependencies": { - "eslint-visitor-keys": "^2.0.0" - }, "engines": { - "node": "^10.0.0 || ^12.0.0 || >= 14.0.0" + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" }, "funding": { - "url": "https://github.com/sponsors/mysticatea" - }, - "peerDependencies": { - "eslint": ">=5" + "url": "https://opencollective.com/eslint" } }, - "node_modules/eslint-utils/node_modules/eslint-visitor-keys": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz", - "integrity": "sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw==", + "node_modules/eslint/node_modules/eslint-scope": { + "version": "7.2.2", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.2.tgz", + "integrity": "sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==", "dev": true, + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^5.2.0" + }, "engines": { - "node": ">=10" + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" } }, - "node_modules/eslint-visitor-keys": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.1.0.tgz", - "integrity": "sha512-yWJFpu4DtjsWKkt5GeNBBuZMlNcYVs6vRCLoCVEJrTjaSB6LC98gFipNK/erM2Heg/E8mIK+hXG/pJMLK+eRZA==", + "node_modules/eslint/node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", "dev": true, "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + "node": ">=4.0" } }, - "node_modules/eslint/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "node_modules/espree": { + "version": "9.6.1", + "resolved": "https://registry.npmjs.org/espree/-/espree-9.6.1.tgz", + "integrity": "sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==", "dev": true, "dependencies": { - "color-convert": "^2.0.1" + "acorn": "^8.9.0", + "acorn-jsx": "^5.3.2", + "eslint-visitor-keys": "^3.4.1" }, "engines": { - "node": ">=8" + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" }, "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" + "url": "https://opencollective.com/eslint" } }, - "node_modules/eslint/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "node_modules/esprima": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", "dev": true, - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" + "bin": { + "esparse": "bin/esparse.js", + "esvalidate": "bin/esvalidate.js" }, "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" + "node": ">=4" } }, - "node_modules/eslint/node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "node_modules/esquery": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.5.0.tgz", + "integrity": "sha512-YQLXUplAwJgCydQ78IMJywZCceoqk1oH01OERdSAJc/7U2AylwjhSCLDEtqwg811idIS/9fIU5GjG73IgjKMVg==", "dev": true, "dependencies": { - "color-name": "~1.1.4" + "estraverse": "^5.1.0" }, "engines": { - "node": ">=7.0.0" + "node": ">=0.10" } }, - "node_modules/eslint/node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "node_modules/eslint/node_modules/escape-string-regexp": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", - "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", - "dev": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/eslint/node_modules/eslint-scope": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.1.0.tgz", - "integrity": "sha512-aWwkhnS0qAXqNOgKOK0dJ2nvzEbhEvpy8OlJ9kZ0FeZnA6zpjv1/Vei+puGFFX7zkPCkHHXb7IDX3A+7yPrRWg==", - "dev": true, - "dependencies": { - "esrecurse": "^4.3.0", - "estraverse": "^5.2.0" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - } - }, - "node_modules/eslint/node_modules/estraverse": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", - "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", - "dev": true, - "engines": { - "node": ">=4.0" - } - }, - "node_modules/eslint/node_modules/globals": { - "version": "13.12.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-13.12.0.tgz", - "integrity": "sha512-uS8X6lSKN2JumVoXrbUz+uG4BYG+eiawqm3qFcT7ammfbUHeCBoJMlHcec/S3krSk73/AE/f0szYFmgAA3kYZg==", - "dev": true, - "dependencies": { - "type-fest": "^0.20.2" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/eslint/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/eslint/node_modules/ignore": { - "version": "4.0.6", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-4.0.6.tgz", - "integrity": "sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==", - "dev": true, - "engines": { - "node": ">= 4" - } - }, - "node_modules/eslint/node_modules/semver": { - "version": "7.3.5", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", - "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", - "dev": true, - "dependencies": { - "lru-cache": "^6.0.0" - }, - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/eslint/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/eslint/node_modules/type-fest": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", - "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", - "dev": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/espree": { - "version": "9.1.0", - "resolved": "https://registry.npmjs.org/espree/-/espree-9.1.0.tgz", - "integrity": "sha512-ZgYLvCS1wxOczBYGcQT9DDWgicXwJ4dbocr9uYN+/eresBAUuBu+O4WzB21ufQ/JqQT8gyp7hJ3z8SHii32mTQ==", - "dev": true, - "dependencies": { - "acorn": "^8.6.0", - "acorn-jsx": "^5.3.1", - "eslint-visitor-keys": "^3.1.0" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - } - }, - "node_modules/esprima": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", - "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", - "dev": true, - "bin": { - "esparse": "bin/esparse.js", - "esvalidate": "bin/esvalidate.js" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/esquery": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.4.0.tgz", - "integrity": "sha512-cCDispWt5vHHtwMY2YrAQ4ibFkAL8RbH5YGBnZBc90MolvvfkkQcJro/aZiAQUlQ3qgrYS6D6v8Gc5G5CQsc9w==", - "dev": true, - "dependencies": { - "estraverse": "^5.1.0" - }, - "engines": { - "node": ">=0.10" - } - }, - "node_modules/esquery/node_modules/estraverse": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", - "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "node_modules/esquery/node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", "dev": true, "engines": { "node": ">=4.0" @@ -3327,188 +3010,97 @@ } }, "node_modules/expect": { - "version": "29.0.1", - "resolved": "https://registry.npmjs.org/expect/-/expect-29.0.1.tgz", - "integrity": "sha512-yQgemsjLU+1S8t2A7pXT3Sn/v5/37LY8J+tocWtKEA0iEYYc6gfKbbJJX2fxHZmd7K9WpdbQqXUpmYkq1aewYg==", + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/expect/-/expect-29.7.0.tgz", + "integrity": "sha512-2Zks0hf1VLFYI1kbh0I5jP3KHHyCHpkfyHBzsSXRFgl/Bg9mWYfMW8oD+PdMPlEwy5HNsR9JutYy6pMeOh61nw==", "dev": true, "dependencies": { - "@jest/expect-utils": "^29.0.1", - "jest-get-type": "^29.0.0", - "jest-matcher-utils": "^29.0.1", - "jest-message-util": "^29.0.1", - "jest-util": "^29.0.1" + "@jest/expect-utils": "^29.7.0", + "jest-get-type": "^29.6.3", + "jest-matcher-utils": "^29.7.0", + "jest-message-util": "^29.7.0", + "jest-util": "^29.7.0" }, "engines": { "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/expect/node_modules/@jest/types": { - "version": "29.0.1", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.0.1.tgz", - "integrity": "sha512-ft01rxzVsbh9qZPJ6EFgAIj3PT9FCRfBF9Xljo2/33VDOUjLZr0ZJ2oKANqh9S/K0/GERCsHDAQlBwj7RxA+9g==", + "node_modules/expect/node_modules/@jest/schemas": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-29.6.3.tgz", + "integrity": "sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA==", "dev": true, "dependencies": { - "@jest/schemas": "^29.0.0", - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^17.0.8", - "chalk": "^4.0.0" + "@sinclair/typebox": "^0.27.8" }, "engines": { "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/expect/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } + "node_modules/expect/node_modules/@sinclair/typebox": { + "version": "0.27.8", + "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.27.8.tgz", + "integrity": "sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA==", + "dev": true }, - "node_modules/expect/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "node_modules/expect/node_modules/ansi-styles": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", "dev": true, - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, "engines": { "node": ">=10" }, "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/expect/node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" + "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "node_modules/expect/node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, "node_modules/expect/node_modules/diff-sequences": { - "version": "29.0.0", - "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-29.0.0.tgz", - "integrity": "sha512-7Qe/zd1wxSDL4D/X/FPjOMB+ZMDt71W94KYaq05I2l0oQqgXgs7s4ftYYmV38gBSrPz2vcygxfs1xn0FT+rKNA==", + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-29.6.3.tgz", + "integrity": "sha512-EjePK1srD3P08o2j4f0ExnylqRs5B9tJjcp9t1krH2qRi8CCdsYfwe9JgSLurFBWwq4uOlipzfk5fHNvwFKr8Q==", "dev": true, "engines": { "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/expect/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true, - "engines": { - "node": ">=8" - } - }, "node_modules/expect/node_modules/jest-diff": { - "version": "29.0.1", - "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-29.0.1.tgz", - "integrity": "sha512-l8PYeq2VhcdxG9tl5cU78ClAlg/N7RtVSp0v3MlXURR0Y99i6eFnegmasOandyTmO6uEdo20+FByAjBFEO9nuw==", + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-29.7.0.tgz", + "integrity": "sha512-LMIgiIrhigmPrs03JHpxUh2yISK3vLFPkAodPeo0+BuF7wA2FoQbkEg1u8gBYBThncu7e1oEDUfIXVuTqLRUjw==", "dev": true, "dependencies": { "chalk": "^4.0.0", - "diff-sequences": "^29.0.0", - "jest-get-type": "^29.0.0", - "pretty-format": "^29.0.1" + "diff-sequences": "^29.6.3", + "jest-get-type": "^29.6.3", + "pretty-format": "^29.7.0" }, "engines": { "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/expect/node_modules/jest-get-type": { - "version": "29.0.0", - "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-29.0.0.tgz", - "integrity": "sha512-83X19z/HuLKYXYHskZlBAShO7UfLFXu/vWajw9ZNJASN32li8yHMaVGAQqxFW1RCFOkB7cubaL6FaJVQqqJLSw==", - "dev": true, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, "node_modules/expect/node_modules/jest-matcher-utils": { - "version": "29.0.1", - "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-29.0.1.tgz", - "integrity": "sha512-/e6UbCDmprRQFnl7+uBKqn4G22c/OmwriE5KCMVqxhElKCQUDcFnq5XM9iJeKtzy4DUjxT27y9VHmKPD8BQPaw==", - "dev": true, - "dependencies": { - "chalk": "^4.0.0", - "jest-diff": "^29.0.1", - "jest-get-type": "^29.0.0", - "pretty-format": "^29.0.1" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/expect/node_modules/jest-message-util": { - "version": "29.0.1", - "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-29.0.1.tgz", - "integrity": "sha512-wRMAQt3HrLpxSubdnzOo68QoTfQ+NLXFzU0Heb18ZUzO2S9GgaXNEdQ4rpd0fI9dq2NXkpCk1IUWSqzYKji64A==", - "dev": true, - "dependencies": { - "@babel/code-frame": "^7.12.13", - "@jest/types": "^29.0.1", - "@types/stack-utils": "^2.0.0", - "chalk": "^4.0.0", - "graceful-fs": "^4.2.9", - "micromatch": "^4.0.4", - "pretty-format": "^29.0.1", - "slash": "^3.0.0", - "stack-utils": "^2.0.3" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/expect/node_modules/jest-util": { - "version": "29.0.1", - "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-29.0.1.tgz", - "integrity": "sha512-GIWkgNfkeA9d84rORDHPGGTFBrRD13A38QVSKE0bVrGSnoR1KDn8Kqz+0yI5kezMgbT/7zrWaruWP1Kbghlb2A==", + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-29.7.0.tgz", + "integrity": "sha512-sBkD+Xi9DtcChsI3L3u0+N0opgPYnCRPtGcQYrgXmR+hmt/fYfWAL0xRXYU8eWOdfuLgBe0YCW3AFtnRLagq/g==", "dev": true, "dependencies": { - "@jest/types": "^29.0.1", - "@types/node": "*", "chalk": "^4.0.0", - "ci-info": "^3.2.0", - "graceful-fs": "^4.2.9", - "picomatch": "^2.2.3" + "jest-diff": "^29.7.0", + "jest-get-type": "^29.6.3", + "pretty-format": "^29.7.0" }, "engines": { "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, "node_modules/expect/node_modules/pretty-format": { - "version": "29.0.1", - "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.0.1.tgz", - "integrity": "sha512-iTHy3QZMzuL484mSTYbQIM1AHhEQsH8mXWS2/vd2yFBYnG3EBqGiMONo28PlPgrW7P/8s/1ISv+y7WH306l8cw==", + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz", + "integrity": "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==", "dev": true, "dependencies": { - "@jest/schemas": "^29.0.0", + "@jest/schemas": "^29.6.3", "ansi-styles": "^5.0.0", "react-is": "^18.0.0" }, @@ -3516,36 +3108,12 @@ "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/expect/node_modules/pretty-format/node_modules/ansi-styles": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", - "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", - "dev": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, "node_modules/expect/node_modules/react-is": { "version": "18.2.0", "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz", "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==", "dev": true }, - "node_modules/expect/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/fast-deep-equal": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", @@ -3553,9 +3121,9 @@ "dev": true }, "node_modules/fast-glob": { - "version": "3.2.7", - "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.7.tgz", - "integrity": "sha512-rYGMRwip6lUMvYD3BTScMwT1HtAs2d71SMv66Vrxs0IekGZEjhM0pcMfjQPnknBt2zeCwQMEupiN02ZP4DiT1Q==", + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.2.tgz", + "integrity": "sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==", "dev": true, "dependencies": { "@nodelib/fs.stat": "^2.0.2", @@ -3565,7 +3133,7 @@ "micromatch": "^4.0.4" }, "engines": { - "node": ">=8" + "node": ">=8.6.0" } }, "node_modules/fast-glob/node_modules/glob-parent": { @@ -3589,22 +3157,22 @@ "node_modules/fast-levenshtein": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", - "integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=", + "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", "dev": true }, "node_modules/fastq": { - "version": "1.13.0", - "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.13.0.tgz", - "integrity": "sha512-YpkpUnK8od0o1hmeSc7UUs/eB/vIPWJYjKck2QKIzAf71Vm1AAQ3EbuZB3g2JIy+pg+ERD0vqI79KyZiB2e2Nw==", + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.15.0.tgz", + "integrity": "sha512-wBrocU2LCXXa+lWBt8RoIRD89Fi8OdABODa/kEnyeyjS5aZO5/GNvI5sEINADqP/h8M29UHTHUb53sUu5Ihqdw==", "dev": true, "dependencies": { "reusify": "^1.0.4" } }, "node_modules/fb-watchman": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/fb-watchman/-/fb-watchman-2.0.1.tgz", - "integrity": "sha512-DkPJKQeY6kKwmuMretBhr7G6Vodr7bFwDYTXIkfG1gjvNpaxBTQV3PbXg6bR1c1UP4jPOX0jHUbbHANL9vRjVg==", + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/fb-watchman/-/fb-watchman-2.0.2.tgz", + "integrity": "sha512-p5161BqbuCaSnB8jIbzQHOlpgsPmK5rJVDfDKO91Axs5NC1uu3HRQm6wt9cd9/+GtQQIO53JdGXXoyDpTAsgYA==", "dev": true, "dependencies": { "bser": "2.1.1" @@ -3635,25 +3203,29 @@ } }, "node_modules/find-up": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", - "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", "dev": true, "dependencies": { - "locate-path": "^5.0.0", + "locate-path": "^6.0.0", "path-exists": "^4.0.0" }, "engines": { - "node": ">=8" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/flat-cache": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.0.4.tgz", - "integrity": "sha512-dm9s5Pw7Jc0GvMYbshN6zchCA9RgQlzzEZX3vylR9IqFfS8XciblUXOKfW6SiuJ0e13eDYZoZV5wdrev7P3Nwg==", + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.2.0.tgz", + "integrity": "sha512-CYcENa+FtcUKLmhhqyctpclsq7QF38pKjZHsGNiSQF5r4FtoKDWabFDl3hzaEQMvT1LHEysw5twgLvpYYb4vbw==", "dev": true, "dependencies": { - "flatted": "^3.1.0", + "flatted": "^3.2.9", + "keyv": "^4.5.3", "rimraf": "^3.0.2" }, "engines": { @@ -3661,15 +3233,15 @@ } }, "node_modules/flatted": { - "version": "3.2.4", - "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.4.tgz", - "integrity": "sha512-8/sOawo8tJ4QOBX8YlQBMxL8+RLZfxMQOif9o0KUKTNTjMYElWPE0r/m5VNFxTRd0NSw8qSy8dajrwX4RYI1Hw==", + "version": "3.2.9", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.9.tgz", + "integrity": "sha512-36yxDn5H7OFZQla0/jFJmbIKTdZAQHngCedGxiMmpNfEZM0sdEeT+WczLQrjK6D7o2aiyLYDnkw0R3JK0Qv1RQ==", "dev": true }, "node_modules/fs-extra": { - "version": "10.0.0", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.0.0.tgz", - "integrity": "sha512-C5owb14u9eJwizKGdchcDUQeFtlSHHthBk8pbX9Vc1PFZrLombudjDnNns88aYslCyF6IY5SUw3Roz6xShcEIQ==", + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz", + "integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==", "dev": true, "dependencies": { "graceful-fs": "^4.2.0", @@ -3683,35 +3255,18 @@ "node_modules/fs.realpath": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", "dev": true }, - "node_modules/fsevents": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", - "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", + "node_modules/function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", "dev": true, - "hasInstallScript": true, - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/function-bind": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", - "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", - "dev": true - }, - "node_modules/functional-red-black-tree": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz", - "integrity": "sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc=", - "dev": true - }, "node_modules/gensync": { "version": "1.0.0-beta.2", "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", @@ -3752,9 +3307,9 @@ } }, "node_modules/glob": { - "version": "8.0.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-8.0.3.tgz", - "integrity": "sha512-ull455NHSHI/Y1FqGaaYFaLGkNMMJbavMrEGFXG/PGrg6y7sutWHUHrz6gy6WEBH6akM1M414dWKCNs+IhKdiQ==", + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-8.1.0.tgz", + "integrity": "sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ==", "dev": true, "dependencies": { "fs.realpath": "^1.0.0", @@ -3792,9 +3347,9 @@ } }, "node_modules/glob/node_modules/minimatch": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.1.tgz", - "integrity": "sha512-362NP+zlprccbEt/SkxKfRMHnNY85V74mVnpUpNyr3F35covl09Kec7/sEFLt3RA4oXmewtoaanoIf67SE5Y5g==", + "version": "5.1.6", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz", + "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==", "dev": true, "dependencies": { "brace-expansion": "^2.0.1" @@ -3804,25 +3359,31 @@ } }, "node_modules/globals": { - "version": "11.12.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", - "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", + "version": "13.23.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.23.0.tgz", + "integrity": "sha512-XAmF0RjlrjY23MA51q3HltdlGxUpXPvg0GioKiD9X6HD28iMjo2dKC8Vqwm7lne4GNr78+RHTfliktR6ZH09wA==", "dev": true, + "dependencies": { + "type-fest": "^0.20.2" + }, "engines": { - "node": ">=4" + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/globby": { - "version": "11.0.4", - "resolved": "https://registry.npmjs.org/globby/-/globby-11.0.4.tgz", - "integrity": "sha512-9O4MVG9ioZJ08ffbcyVYyLOJLk5JQ688pJ4eMGLpdWLHq/Wr1D9BlriLQyL0E+jbkuePVZXYFj47QM/v093wHg==", + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", + "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", "dev": true, "dependencies": { "array-union": "^2.1.0", "dir-glob": "^3.0.1", - "fast-glob": "^3.1.1", - "ignore": "^5.1.4", - "merge2": "^1.3.0", + "fast-glob": "^3.2.9", + "ignore": "^5.2.0", + "merge2": "^1.4.1", "slash": "^3.0.0" }, "engines": { @@ -3833,19 +3394,25 @@ } }, "node_modules/graceful-fs": { - "version": "4.2.10", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.10.tgz", - "integrity": "sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA==", + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", + "dev": true + }, + "node_modules/graphemer": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz", + "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==", "dev": true }, "node_modules/handlebars": { - "version": "4.7.7", - "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.7.7.tgz", - "integrity": "sha512-aAcXm5OAfE/8IXkcZvCepKU3VzW1/39Fb5ZuqMtgI/hT8X2YgoMvBY5dLhq/cpOvw7Lk1nK/UF71aLG/ZnVYRA==", + "version": "4.7.8", + "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.7.8.tgz", + "integrity": "sha512-vafaFqs8MZkRrSX7sFVUdo3ap/eNiLnb4IakshzvP56X5Nr1iGKAIqdX6tMlm6HcNRIkr6AxO5jFEoJzzpT8aQ==", "dev": true, "dependencies": { "minimist": "^1.2.5", - "neo-async": "^2.6.0", + "neo-async": "^2.6.2", "source-map": "^0.6.1", "wordwrap": "^1.0.0" }, @@ -3859,25 +3426,25 @@ "uglify-js": "^3.1.4" } }, - "node_modules/has": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", - "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", + "node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", "dev": true, - "dependencies": { - "function-bind": "^1.1.1" - }, "engines": { - "node": ">= 0.4.0" + "node": ">=8" } }, - "node_modules/has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "node_modules/hasown": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.0.tgz", + "integrity": "sha512-vUptKVTpIJhcczKBbgnS+RtcuYMB8+oNzPK2/Hp3hanz8JmpATdmmgLgSaadVREkDm+e2giHwY3ZRkyjSIDDFA==", "dev": true, + "dependencies": { + "function-bind": "^1.1.2" + }, "engines": { - "node": ">=4" + "node": ">= 0.4" } }, "node_modules/howslow": { @@ -3902,9 +3469,9 @@ } }, "node_modules/ignore": { - "version": "5.1.9", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.1.9.tgz", - "integrity": "sha512-2zeMQpbKz5dhZ9IwL0gbxSW5w0NK/MSAMtNuhgIHEPmaU3vPdKPL0UdvUCXs5SS4JAwsBxysK5sFMW8ocFiVjQ==", + "version": "5.2.4", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.4.tgz", + "integrity": "sha512-MAb38BcSbH0eHNBxn7ql2NH/kX33OkB3lZ1BNdh7ENeRChHTYsTvWrMubiIAMNS2llXEEgZ1MUOBtXChP3kaFQ==", "dev": true, "engines": { "node": ">= 4" @@ -3948,7 +3515,7 @@ "node_modules/imurmurhash": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", - "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=", + "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", "dev": true, "engines": { "node": ">=0.8.19" @@ -3957,7 +3524,7 @@ "node_modules/inflight": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", - "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", + "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", "dev": true, "dependencies": { "once": "^1.3.0", @@ -3977,12 +3544,12 @@ "dev": true }, "node_modules/is-core-module": { - "version": "2.10.0", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.10.0.tgz", - "integrity": "sha512-Erxj2n/LDAZ7H8WNJXd9tw38GYM3dv8rk8Zcs+jJuxYTW7sozH+SS8NtrSjVL1/vpLvWi1hxy96IzjJ3EHTJJg==", + "version": "2.13.1", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.13.1.tgz", + "integrity": "sha512-hHrIjvZsftOsvKSn2TRYl63zvxsgE0K+0mYMoH6gD4omR5IWB2KynivBQczo3+wF1cCkjzvptnI9Q0sPU66ilw==", "dev": true, "dependencies": { - "has": "^1.0.3" + "hasown": "^2.0.0" }, "funding": { "url": "https://github.com/sponsors/ljharb" @@ -3991,7 +3558,7 @@ "node_modules/is-extglob": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", - "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", "dev": true, "engines": { "node": ">=0.10.0" @@ -4036,6 +3603,15 @@ "node": ">=0.12.0" } }, + "node_modules/is-path-inside": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz", + "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, "node_modules/is-stream": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", @@ -4051,7 +3627,7 @@ "node_modules/isexe": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", "dev": true }, "node_modules/isomorphic-fetch": { @@ -4063,57 +3639,19 @@ "whatwg-fetch": "^3.4.1" } }, - "node_modules/isomorphic-fetch/node_modules/node-fetch": { - "version": "2.6.7", - "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.7.tgz", - "integrity": "sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ==", - "dependencies": { - "whatwg-url": "^5.0.0" - }, - "engines": { - "node": "4.x || >=6.0.0" - }, - "peerDependencies": { - "encoding": "^0.1.0" - }, - "peerDependenciesMeta": { - "encoding": { - "optional": true - } - } - }, - "node_modules/isomorphic-fetch/node_modules/tr46": { - "version": "0.0.3", - "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", - "integrity": "sha1-gYT9NH2snNwYWZLzpmIuFLnZq2o=" - }, - "node_modules/isomorphic-fetch/node_modules/webidl-conversions": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", - "integrity": "sha1-JFNCdeKnvGvnvIZhHMFq4KVlSHE=" - }, - "node_modules/isomorphic-fetch/node_modules/whatwg-url": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", - "integrity": "sha1-lmRU6HZUYuN2RNNib2dCzotwll0=", - "dependencies": { - "tr46": "~0.0.3", - "webidl-conversions": "^3.0.0" - } - }, "node_modules/istanbul-lib-coverage": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.0.tgz", - "integrity": "sha512-eOeJ5BHCmHYvQK7xt9GkdHuzuCGS1Y6g9Gvnx3Ym33fz/HpLRYxiS0wHNr+m/MBC8B647Xt608vCDEvhl9c6Mw==", + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.2.tgz", + "integrity": "sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg==", "dev": true, "engines": { "node": ">=8" } }, "node_modules/istanbul-lib-instrument": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-5.2.0.tgz", - "integrity": "sha512-6Lthe1hqXHBNsqvgDzGO6l03XNeu3CrG4RqQ1KM9+l5+jNGpEJfIELx1NS3SEHmJQA8np/u+E4EPRKRiu6m19A==", + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-5.2.1.tgz", + "integrity": "sha512-pzqtp31nLv/XFOzXGuvhCb8qhjmTVo5vjVk19XE4CRlSWz0KoeJ3bw9XsA7nOp9YBf4qHjwBxkDzKcME/J29Yg==", "dev": true, "dependencies": { "@babel/core": "^7.12.3", @@ -4126,39 +3664,27 @@ "node": ">=8" } }, - "node_modules/istanbul-lib-report": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz", - "integrity": "sha512-wcdi+uAKzfiGT2abPpKZ0hSU1rGQjUQnLvtY5MpQ7QCTahD3VODhcu4wcfY1YtkGaDD5yuydOLINXsfbus9ROw==", - "dev": true, - "dependencies": { - "istanbul-lib-coverage": "^3.0.0", - "make-dir": "^3.0.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/istanbul-lib-report/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "node_modules/istanbul-lib-instrument/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", "dev": true, - "engines": { - "node": ">=8" + "bin": { + "semver": "bin/semver.js" } }, - "node_modules/istanbul-lib-report/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "node_modules/istanbul-lib-report": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.1.tgz", + "integrity": "sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw==", "dev": true, "dependencies": { - "has-flag": "^4.0.0" + "istanbul-lib-coverage": "^3.0.0", + "make-dir": "^4.0.0", + "supports-color": "^7.1.0" }, "engines": { - "node": ">=8" + "node": ">=10" } }, "node_modules/istanbul-lib-source-maps": { @@ -4176,9 +3702,9 @@ } }, "node_modules/istanbul-reports": { - "version": "3.1.5", - "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.1.5.tgz", - "integrity": "sha512-nUsEMa9pBt/NOHqbcbeJEgqIlY/K7rVWUX6Lql2orY5e9roQOthbR3vtY4zzf2orPELg80fnxxk9zUyPlgwD1w==", + "version": "3.1.6", + "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.1.6.tgz", + "integrity": "sha512-TLgnMkKg3iTDsQ9PbPTdpfAK2DzjF9mqUG7RMgcQl8oFjad8ob4laGxv5XV5U9MAfx8D6tSJiUyuAwzLicaxlg==", "dev": true, "dependencies": { "html-escaper": "^2.0.0", @@ -4257,74 +3783,101 @@ "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" } }, - "node_modules/jest-circus/node_modules/@jest/schemas": { - "version": "28.1.3", - "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-28.1.3.tgz", - "integrity": "sha512-/l/VWsdt/aBXgjshLWOFyFt3IVdYypu5y2Wn2rOO1un6nkqIn8SLXzgIMYXFyYsRWDyF5EthmKJMIdJvk08grg==", + "node_modules/jest-circus/node_modules/ansi-styles": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", "dev": true, - "dependencies": { - "@sinclair/typebox": "^0.24.1" + "engines": { + "node": ">=10" }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/jest-circus/node_modules/diff-sequences": { + "version": "28.1.1", + "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-28.1.1.tgz", + "integrity": "sha512-FU0iFaH/E23a+a718l8Qa/19bF9p06kgE0KipMOMadwa3SjnaElKzPaUC0vnibs6/B/9ni97s61mcejk8W1fQw==", + "dev": true, "engines": { "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" } }, - "node_modules/jest-circus/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "node_modules/jest-circus/node_modules/jest-diff": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-28.1.3.tgz", + "integrity": "sha512-8RqP1B/OXzjjTWkqMX67iqgwBVJRgCyKD3L9nq+6ZqJMdvjE8RgHktqZ6jNrkdMT+dJuYNI3rhQpxaz7drJHfw==", "dev": true, "dependencies": { - "color-convert": "^2.0.1" + "chalk": "^4.0.0", + "diff-sequences": "^28.1.1", + "jest-get-type": "^28.0.2", + "pretty-format": "^28.1.3" }, "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" + "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" } }, - "node_modules/jest-circus/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "node_modules/jest-circus/node_modules/jest-get-type": { + "version": "28.0.2", + "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-28.0.2.tgz", + "integrity": "sha512-ioj2w9/DxSYHfOm5lJKCdcAmPJzQXmbM/Url3rhlghrPvT3tt+7a/+oXc9azkKmLvoiXjtV83bEWqi+vs5nlPA==", + "dev": true, + "engines": { + "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" + } + }, + "node_modules/jest-circus/node_modules/jest-matcher-utils": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-28.1.3.tgz", + "integrity": "sha512-kQeJ7qHemKfbzKoGjHHrRKH6atgxMk8Enkk2iPQ3XwO6oE/KYD8lMYOziCkeSB9G4adPM4nR1DE8Tf5JeWH6Bw==", "dev": true, "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" + "chalk": "^4.0.0", + "jest-diff": "^28.1.3", + "jest-get-type": "^28.0.2", + "pretty-format": "^28.1.3" }, "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" + "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" } }, - "node_modules/jest-circus/node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "node_modules/jest-circus/node_modules/jest-message-util": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-28.1.3.tgz", + "integrity": "sha512-PFdn9Iewbt575zKPf1286Ht9EPoJmYT7P0kY+RibeYZ2XtOr53pDLEFoTWXbd1h4JiGiWpTBC84fc8xMXQMb7g==", "dev": true, "dependencies": { - "color-name": "~1.1.4" + "@babel/code-frame": "^7.12.13", + "@jest/types": "^28.1.3", + "@types/stack-utils": "^2.0.0", + "chalk": "^4.0.0", + "graceful-fs": "^4.2.9", + "micromatch": "^4.0.4", + "pretty-format": "^28.1.3", + "slash": "^3.0.0", + "stack-utils": "^2.0.3" }, "engines": { - "node": ">=7.0.0" + "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" } }, - "node_modules/jest-circus/node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "node_modules/jest-circus/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "node_modules/jest-circus/node_modules/jest-util": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-28.1.3.tgz", + "integrity": "sha512-XdqfpHwpcSRko/C35uLYFM2emRAltIIKZiJ9eAmhjsj0CqZMa0p1ib0R5fWIqGhn1a103DebTbpqIaP1qCQ6tQ==", "dev": true, + "dependencies": { + "@jest/types": "^28.1.3", + "@types/node": "*", + "chalk": "^4.0.0", + "ci-info": "^3.2.0", + "graceful-fs": "^4.2.9", + "picomatch": "^2.2.3" + }, "engines": { - "node": ">=8" + "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" } }, "node_modules/jest-circus/node_modules/pretty-format": { @@ -4342,36 +3895,12 @@ "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" } }, - "node_modules/jest-circus/node_modules/pretty-format/node_modules/ansi-styles": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", - "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", - "dev": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, "node_modules/jest-circus/node_modules/react-is": { "version": "18.2.0", "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz", "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==", "dev": true }, - "node_modules/jest-circus/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/jest-cli": { "version": "28.1.3", "resolved": "https://registry.npmjs.org/jest-cli/-/jest-cli-28.1.3.tgz", @@ -4406,80 +3935,27 @@ } } }, - "node_modules/jest-cli/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "node_modules/jest-cli/node_modules/jest-util": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-28.1.3.tgz", + "integrity": "sha512-XdqfpHwpcSRko/C35uLYFM2emRAltIIKZiJ9eAmhjsj0CqZMa0p1ib0R5fWIqGhn1a103DebTbpqIaP1qCQ6tQ==", "dev": true, "dependencies": { - "color-convert": "^2.0.1" + "@jest/types": "^28.1.3", + "@types/node": "*", + "chalk": "^4.0.0", + "ci-info": "^3.2.0", + "graceful-fs": "^4.2.9", + "picomatch": "^2.2.3" }, "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" + "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" } }, - "node_modules/jest-cli/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/jest-cli/node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/jest-cli/node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "node_modules/jest-cli/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-cli/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-config": { - "version": "28.1.3", - "resolved": "https://registry.npmjs.org/jest-config/-/jest-config-28.1.3.tgz", - "integrity": "sha512-MG3INjByJ0J4AsNBm7T3hsuxKQqFIiRo/AUqb1q9LRKI5UU6Aar9JHbr9Ivn1TVwfUD9KirRoM/T6u8XlcQPHQ==", + "node_modules/jest-config": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/jest-config/-/jest-config-28.1.3.tgz", + "integrity": "sha512-MG3INjByJ0J4AsNBm7T3hsuxKQqFIiRo/AUqb1q9LRKI5UU6Aar9JHbr9Ivn1TVwfUD9KirRoM/T6u8XlcQPHQ==", "dev": true, "dependencies": { "@babel/core": "^7.11.6", @@ -4521,67 +3997,18 @@ } } }, - "node_modules/jest-config/node_modules/@jest/schemas": { - "version": "28.1.3", - "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-28.1.3.tgz", - "integrity": "sha512-/l/VWsdt/aBXgjshLWOFyFt3IVdYypu5y2Wn2rOO1un6nkqIn8SLXzgIMYXFyYsRWDyF5EthmKJMIdJvk08grg==", - "dev": true, - "dependencies": { - "@sinclair/typebox": "^0.24.1" - }, - "engines": { - "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" - } - }, "node_modules/jest-config/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/jest-config/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", "dev": true, - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, "engines": { "node": ">=10" }, "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/jest-config/node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" + "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "node_modules/jest-config/node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, "node_modules/jest-config/node_modules/glob": { "version": "7.2.3", "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", @@ -4602,15 +4029,6 @@ "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/jest-config/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true, - "engines": { - "node": ">=8" - } - }, "node_modules/jest-config/node_modules/jest-get-type": { "version": "28.0.2", "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-28.0.2.tgz", @@ -4620,6 +4038,23 @@ "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" } }, + "node_modules/jest-config/node_modules/jest-util": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-28.1.3.tgz", + "integrity": "sha512-XdqfpHwpcSRko/C35uLYFM2emRAltIIKZiJ9eAmhjsj0CqZMa0p1ib0R5fWIqGhn1a103DebTbpqIaP1qCQ6tQ==", + "dev": true, + "dependencies": { + "@jest/types": "^28.1.3", + "@types/node": "*", + "chalk": "^4.0.0", + "ci-info": "^3.2.0", + "graceful-fs": "^4.2.9", + "picomatch": "^2.2.3" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" + } + }, "node_modules/jest-config/node_modules/pretty-format": { "version": "28.1.3", "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-28.1.3.tgz", @@ -4635,36 +4070,12 @@ "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" } }, - "node_modules/jest-config/node_modules/pretty-format/node_modules/ansi-styles": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", - "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", - "dev": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, "node_modules/jest-config/node_modules/react-is": { "version": "18.2.0", "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz", "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==", "dev": true }, - "node_modules/jest-config/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/jest-diff": { "version": "27.5.1", "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-27.5.1.tgz", @@ -4680,74 +4091,13 @@ "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" } }, - "node_modules/jest-diff/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/jest-diff/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/jest-diff/node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/jest-diff/node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "node_modules/jest-diff/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-diff/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "node_modules/jest-diff/node_modules/jest-get-type": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-27.5.1.tgz", + "integrity": "sha512-2KY95ksYSaK7DMBWQn6dQz3kqAf3BB64y2udeG+hv4KfSOb9qwcYQstTJc1KCbsix+wLZWZYN8t7nwX3GOBLRw==", "dev": true, - "dependencies": { - "has-flag": "^4.0.0" - }, "engines": { - "node": ">=8" + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" } }, "node_modules/jest-docblock": { @@ -4778,81 +4128,40 @@ "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" } }, - "node_modules/jest-each/node_modules/@jest/schemas": { - "version": "28.1.3", - "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-28.1.3.tgz", - "integrity": "sha512-/l/VWsdt/aBXgjshLWOFyFt3IVdYypu5y2Wn2rOO1un6nkqIn8SLXzgIMYXFyYsRWDyF5EthmKJMIdJvk08grg==", - "dev": true, - "dependencies": { - "@sinclair/typebox": "^0.24.1" - }, - "engines": { - "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" - } - }, "node_modules/jest-each/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", "dev": true, - "dependencies": { - "color-convert": "^2.0.1" - }, "engines": { - "node": ">=8" + "node": ">=10" }, "funding": { "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "node_modules/jest-each/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "node_modules/jest-each/node_modules/jest-get-type": { + "version": "28.0.2", + "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-28.0.2.tgz", + "integrity": "sha512-ioj2w9/DxSYHfOm5lJKCdcAmPJzQXmbM/Url3rhlghrPvT3tt+7a/+oXc9azkKmLvoiXjtV83bEWqi+vs5nlPA==", "dev": true, - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" + "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" } }, - "node_modules/jest-each/node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "node_modules/jest-each/node_modules/jest-util": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-28.1.3.tgz", + "integrity": "sha512-XdqfpHwpcSRko/C35uLYFM2emRAltIIKZiJ9eAmhjsj0CqZMa0p1ib0R5fWIqGhn1a103DebTbpqIaP1qCQ6tQ==", "dev": true, "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/jest-each/node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "node_modules/jest-each/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-each/node_modules/jest-get-type": { - "version": "28.0.2", - "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-28.0.2.tgz", - "integrity": "sha512-ioj2w9/DxSYHfOm5lJKCdcAmPJzQXmbM/Url3rhlghrPvT3tt+7a/+oXc9azkKmLvoiXjtV83bEWqi+vs5nlPA==", - "dev": true, + "@jest/types": "^28.1.3", + "@types/node": "*", + "chalk": "^4.0.0", + "ci-info": "^3.2.0", + "graceful-fs": "^4.2.9", + "picomatch": "^2.2.3" + }, "engines": { "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" } @@ -4872,36 +4181,12 @@ "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" } }, - "node_modules/jest-each/node_modules/pretty-format/node_modules/ansi-styles": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", - "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", - "dev": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, "node_modules/jest-each/node_modules/react-is": { "version": "18.2.0", "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz", "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==", "dev": true }, - "node_modules/jest-each/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/jest-environment-node": { "version": "28.1.3", "resolved": "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-28.1.3.tgz", @@ -4919,13 +4204,30 @@ "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" } }, + "node_modules/jest-environment-node/node_modules/jest-util": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-28.1.3.tgz", + "integrity": "sha512-XdqfpHwpcSRko/C35uLYFM2emRAltIIKZiJ9eAmhjsj0CqZMa0p1ib0R5fWIqGhn1a103DebTbpqIaP1qCQ6tQ==", + "dev": true, + "dependencies": { + "@jest/types": "^28.1.3", + "@types/node": "*", + "chalk": "^4.0.0", + "ci-info": "^3.2.0", + "graceful-fs": "^4.2.9", + "picomatch": "^2.2.3" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" + } + }, "node_modules/jest-get-type": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-27.5.1.tgz", - "integrity": "sha512-2KY95ksYSaK7DMBWQn6dQz3kqAf3BB64y2udeG+hv4KfSOb9qwcYQstTJc1KCbsix+wLZWZYN8t7nwX3GOBLRw==", + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-29.6.3.tgz", + "integrity": "sha512-zrteXnqYxfQh7l5FHyL38jL39di8H8rHoecLH3JNxH3BwOrBsNeabdap5e0I23lD4HHI8W5VFBZqG4Eaq5LNcw==", "dev": true, "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, "node_modules/jest-haste-map": { @@ -4953,26 +4255,31 @@ "fsevents": "^2.3.2" } }, - "node_modules/jest-leak-detector": { + "node_modules/jest-haste-map/node_modules/jest-util": { "version": "28.1.3", - "resolved": "https://registry.npmjs.org/jest-leak-detector/-/jest-leak-detector-28.1.3.tgz", - "integrity": "sha512-WFVJhnQsiKtDEo5lG2mM0v40QWnBM+zMdHHyJs8AWZ7J0QZJS59MsyKeJHWhpBZBH32S48FOVvGyOFT1h0DlqA==", + "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-28.1.3.tgz", + "integrity": "sha512-XdqfpHwpcSRko/C35uLYFM2emRAltIIKZiJ9eAmhjsj0CqZMa0p1ib0R5fWIqGhn1a103DebTbpqIaP1qCQ6tQ==", "dev": true, "dependencies": { - "jest-get-type": "^28.0.2", - "pretty-format": "^28.1.3" + "@jest/types": "^28.1.3", + "@types/node": "*", + "chalk": "^4.0.0", + "ci-info": "^3.2.0", + "graceful-fs": "^4.2.9", + "picomatch": "^2.2.3" }, "engines": { "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" } }, - "node_modules/jest-leak-detector/node_modules/@jest/schemas": { + "node_modules/jest-leak-detector": { "version": "28.1.3", - "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-28.1.3.tgz", - "integrity": "sha512-/l/VWsdt/aBXgjshLWOFyFt3IVdYypu5y2Wn2rOO1un6nkqIn8SLXzgIMYXFyYsRWDyF5EthmKJMIdJvk08grg==", + "resolved": "https://registry.npmjs.org/jest-leak-detector/-/jest-leak-detector-28.1.3.tgz", + "integrity": "sha512-WFVJhnQsiKtDEo5lG2mM0v40QWnBM+zMdHHyJs8AWZ7J0QZJS59MsyKeJHWhpBZBH32S48FOVvGyOFT1h0DlqA==", "dev": true, "dependencies": { - "@sinclair/typebox": "^0.24.1" + "jest-get-type": "^28.0.2", + "pretty-format": "^28.1.3" }, "engines": { "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" @@ -5021,169 +4328,250 @@ "dev": true }, "node_modules/jest-matcher-utils": { - "version": "28.1.3", - "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-28.1.3.tgz", - "integrity": "sha512-kQeJ7qHemKfbzKoGjHHrRKH6atgxMk8Enkk2iPQ3XwO6oE/KYD8lMYOziCkeSB9G4adPM4nR1DE8Tf5JeWH6Bw==", + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-27.5.1.tgz", + "integrity": "sha512-z2uTx/T6LBaCoNWNFWwChLBKYxTMcGBRjAt+2SbP929/Fflb9aa5LGma654Rz8z9HLxsrUaYzxE9T/EFIL/PAw==", "dev": true, "dependencies": { "chalk": "^4.0.0", - "jest-diff": "^28.1.3", - "jest-get-type": "^28.0.2", - "pretty-format": "^28.1.3" + "jest-diff": "^27.5.1", + "jest-get-type": "^27.5.1", + "pretty-format": "^27.5.1" }, "engines": { - "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" } }, - "node_modules/jest-matcher-utils/node_modules/@jest/schemas": { - "version": "28.1.3", - "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-28.1.3.tgz", - "integrity": "sha512-/l/VWsdt/aBXgjshLWOFyFt3IVdYypu5y2Wn2rOO1un6nkqIn8SLXzgIMYXFyYsRWDyF5EthmKJMIdJvk08grg==", + "node_modules/jest-matcher-utils/node_modules/jest-get-type": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-27.5.1.tgz", + "integrity": "sha512-2KY95ksYSaK7DMBWQn6dQz3kqAf3BB64y2udeG+hv4KfSOb9qwcYQstTJc1KCbsix+wLZWZYN8t7nwX3GOBLRw==", + "dev": true, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/jest-message-util": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-29.7.0.tgz", + "integrity": "sha512-GBEV4GRADeP+qtB2+6u61stea8mGcOT4mCtrYISZwfu9/ISHFJ/5zOMXYbpBE9RsS5+Gb63DW4FgmnKJ79Kf6w==", "dev": true, "dependencies": { - "@sinclair/typebox": "^0.24.1" + "@babel/code-frame": "^7.12.13", + "@jest/types": "^29.6.3", + "@types/stack-utils": "^2.0.0", + "chalk": "^4.0.0", + "graceful-fs": "^4.2.9", + "micromatch": "^4.0.4", + "pretty-format": "^29.7.0", + "slash": "^3.0.0", + "stack-utils": "^2.0.3" }, "engines": { - "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/jest-matcher-utils/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "node_modules/jest-message-util/node_modules/@jest/schemas": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-29.6.3.tgz", + "integrity": "sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA==", "dev": true, "dependencies": { - "color-convert": "^2.0.1" + "@sinclair/typebox": "^0.27.8" }, "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/jest-matcher-utils/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "node_modules/jest-message-util/node_modules/@jest/types": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.6.3.tgz", + "integrity": "sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw==", "dev": true, "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" + "@jest/schemas": "^29.6.3", + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^3.0.0", + "@types/node": "*", + "@types/yargs": "^17.0.8", + "chalk": "^4.0.0" }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-message-util/node_modules/@sinclair/typebox": { + "version": "0.27.8", + "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.27.8.tgz", + "integrity": "sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA==", + "dev": true + }, + "node_modules/jest-message-util/node_modules/ansi-styles": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", + "dev": true, "engines": { "node": ">=10" }, "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" + "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "node_modules/jest-matcher-utils/node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "node_modules/jest-message-util/node_modules/pretty-format": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz", + "integrity": "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==", "dev": true, "dependencies": { - "color-name": "~1.1.4" + "@jest/schemas": "^29.6.3", + "ansi-styles": "^5.0.0", + "react-is": "^18.0.0" }, "engines": { - "node": ">=7.0.0" + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/jest-matcher-utils/node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "node_modules/jest-message-util/node_modules/react-is": { + "version": "18.2.0", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz", + "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==", "dev": true }, - "node_modules/jest-matcher-utils/node_modules/diff-sequences": { - "version": "28.1.1", - "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-28.1.1.tgz", - "integrity": "sha512-FU0iFaH/E23a+a718l8Qa/19bF9p06kgE0KipMOMadwa3SjnaElKzPaUC0vnibs6/B/9ni97s61mcejk8W1fQw==", + "node_modules/jest-mock": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-28.1.3.tgz", + "integrity": "sha512-o3J2jr6dMMWYVH4Lh/NKmDXdosrsJgi4AviS8oXLujcjpCMBb1FMsblDnOXKZKfSiHLxYub1eS0IHuRXsio9eA==", "dev": true, + "dependencies": { + "@jest/types": "^28.1.3", + "@types/node": "*" + }, "engines": { "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" } }, - "node_modules/jest-matcher-utils/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "node_modules/jest-pnp-resolver": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/jest-pnp-resolver/-/jest-pnp-resolver-1.2.3.tgz", + "integrity": "sha512-+3NpwQEnRoIBtx4fyhblQDPgJI0H1IEIkX7ShLUjPGA7TtUTvI1oiKi3SR4oBR0hQhQR80l4WAe5RrXBwWMA8w==", "dev": true, "engines": { - "node": ">=8" + "node": ">=6" + }, + "peerDependencies": { + "jest-resolve": "*" + }, + "peerDependenciesMeta": { + "jest-resolve": { + "optional": true + } + } + }, + "node_modules/jest-regex-util": { + "version": "28.0.2", + "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-28.0.2.tgz", + "integrity": "sha512-4s0IgyNIy0y9FK+cjoVYoxamT7Zeo7MhzqRGx7YDYmaQn1wucY9rotiGkBzzcMXTtjrCAP/f7f+E0F7+fxPNdw==", + "dev": true, + "engines": { + "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" } }, - "node_modules/jest-matcher-utils/node_modules/jest-diff": { + "node_modules/jest-resolve": { "version": "28.1.3", - "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-28.1.3.tgz", - "integrity": "sha512-8RqP1B/OXzjjTWkqMX67iqgwBVJRgCyKD3L9nq+6ZqJMdvjE8RgHktqZ6jNrkdMT+dJuYNI3rhQpxaz7drJHfw==", + "resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-28.1.3.tgz", + "integrity": "sha512-Z1W3tTjE6QaNI90qo/BJpfnvpxtaFTFw5CDgwpyE/Kz8U/06N1Hjf4ia9quUhCh39qIGWF1ZuxFiBiJQwSEYKQ==", "dev": true, "dependencies": { "chalk": "^4.0.0", - "diff-sequences": "^28.1.1", - "jest-get-type": "^28.0.2", - "pretty-format": "^28.1.3" + "graceful-fs": "^4.2.9", + "jest-haste-map": "^28.1.3", + "jest-pnp-resolver": "^1.2.2", + "jest-util": "^28.1.3", + "jest-validate": "^28.1.3", + "resolve": "^1.20.0", + "resolve.exports": "^1.1.0", + "slash": "^3.0.0" }, "engines": { "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" } }, - "node_modules/jest-matcher-utils/node_modules/jest-get-type": { - "version": "28.0.2", - "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-28.0.2.tgz", - "integrity": "sha512-ioj2w9/DxSYHfOm5lJKCdcAmPJzQXmbM/Url3rhlghrPvT3tt+7a/+oXc9azkKmLvoiXjtV83bEWqi+vs5nlPA==", + "node_modules/jest-resolve-dependencies": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/jest-resolve-dependencies/-/jest-resolve-dependencies-28.1.3.tgz", + "integrity": "sha512-qa0QO2Q0XzQoNPouMbCc7Bvtsem8eQgVPNkwn9LnS+R2n8DaVDPL/U1gngC0LTl1RYXJU0uJa2BMC2DbTfFrHA==", "dev": true, + "dependencies": { + "jest-regex-util": "^28.0.2", + "jest-snapshot": "^28.1.3" + }, "engines": { "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" } }, - "node_modules/jest-matcher-utils/node_modules/pretty-format": { + "node_modules/jest-resolve/node_modules/jest-util": { "version": "28.1.3", - "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-28.1.3.tgz", - "integrity": "sha512-8gFb/To0OmxHR9+ZTb14Df2vNxdGCX8g1xWGUTqUw5TiZvcQf5sHKObd5UcPyLLyowNwDAMTF3XWOG1B6mxl1Q==", + "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-28.1.3.tgz", + "integrity": "sha512-XdqfpHwpcSRko/C35uLYFM2emRAltIIKZiJ9eAmhjsj0CqZMa0p1ib0R5fWIqGhn1a103DebTbpqIaP1qCQ6tQ==", "dev": true, "dependencies": { - "@jest/schemas": "^28.1.3", - "ansi-regex": "^5.0.1", - "ansi-styles": "^5.0.0", - "react-is": "^18.0.0" + "@jest/types": "^28.1.3", + "@types/node": "*", + "chalk": "^4.0.0", + "ci-info": "^3.2.0", + "graceful-fs": "^4.2.9", + "picomatch": "^2.2.3" }, "engines": { "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" } }, - "node_modules/jest-matcher-utils/node_modules/pretty-format/node_modules/ansi-styles": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", - "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", - "dev": true, - "engines": { - "node": ">=10" + "node_modules/jest-runner": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/jest-runner/-/jest-runner-28.1.3.tgz", + "integrity": "sha512-GkMw4D/0USd62OVO0oEgjn23TM+YJa2U2Wu5zz9xsQB1MxWKDOlrnykPxnMsN0tnJllfLPinHTka61u0QhaxBA==", + "dev": true, + "dependencies": { + "@jest/console": "^28.1.3", + "@jest/environment": "^28.1.3", + "@jest/test-result": "^28.1.3", + "@jest/transform": "^28.1.3", + "@jest/types": "^28.1.3", + "@types/node": "*", + "chalk": "^4.0.0", + "emittery": "^0.10.2", + "graceful-fs": "^4.2.9", + "jest-docblock": "^28.1.1", + "jest-environment-node": "^28.1.3", + "jest-haste-map": "^28.1.3", + "jest-leak-detector": "^28.1.3", + "jest-message-util": "^28.1.3", + "jest-resolve": "^28.1.3", + "jest-runtime": "^28.1.3", + "jest-util": "^28.1.3", + "jest-watcher": "^28.1.3", + "jest-worker": "^28.1.3", + "p-limit": "^3.1.0", + "source-map-support": "0.5.13" }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" + "engines": { + "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" } }, - "node_modules/jest-matcher-utils/node_modules/react-is": { - "version": "18.2.0", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz", - "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==", - "dev": true - }, - "node_modules/jest-matcher-utils/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "node_modules/jest-runner/node_modules/ansi-styles": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", "dev": true, - "dependencies": { - "has-flag": "^4.0.0" - }, "engines": { - "node": ">=8" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "node_modules/jest-message-util": { + "node_modules/jest-runner/node_modules/jest-message-util": { "version": "28.1.3", "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-28.1.3.tgz", "integrity": "sha512-PFdn9Iewbt575zKPf1286Ht9EPoJmYT7P0kY+RibeYZ2XtOr53pDLEFoTWXbd1h4JiGiWpTBC84fc8xMXQMb7g==", @@ -5203,77 +4591,147 @@ "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" } }, - "node_modules/jest-message-util/node_modules/@jest/schemas": { + "node_modules/jest-runner/node_modules/jest-util": { "version": "28.1.3", - "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-28.1.3.tgz", - "integrity": "sha512-/l/VWsdt/aBXgjshLWOFyFt3IVdYypu5y2Wn2rOO1un6nkqIn8SLXzgIMYXFyYsRWDyF5EthmKJMIdJvk08grg==", + "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-28.1.3.tgz", + "integrity": "sha512-XdqfpHwpcSRko/C35uLYFM2emRAltIIKZiJ9eAmhjsj0CqZMa0p1ib0R5fWIqGhn1a103DebTbpqIaP1qCQ6tQ==", "dev": true, "dependencies": { - "@sinclair/typebox": "^0.24.1" + "@jest/types": "^28.1.3", + "@types/node": "*", + "chalk": "^4.0.0", + "ci-info": "^3.2.0", + "graceful-fs": "^4.2.9", + "picomatch": "^2.2.3" }, "engines": { "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" } }, - "node_modules/jest-message-util/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "node_modules/jest-runner/node_modules/pretty-format": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-28.1.3.tgz", + "integrity": "sha512-8gFb/To0OmxHR9+ZTb14Df2vNxdGCX8g1xWGUTqUw5TiZvcQf5sHKObd5UcPyLLyowNwDAMTF3XWOG1B6mxl1Q==", "dev": true, "dependencies": { - "color-convert": "^2.0.1" + "@jest/schemas": "^28.1.3", + "ansi-regex": "^5.0.1", + "ansi-styles": "^5.0.0", + "react-is": "^18.0.0" }, "engines": { - "node": ">=8" + "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" + } + }, + "node_modules/jest-runner/node_modules/react-is": { + "version": "18.2.0", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz", + "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==", + "dev": true + }, + "node_modules/jest-runtime": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/jest-runtime/-/jest-runtime-28.1.3.tgz", + "integrity": "sha512-NU+881ScBQQLc1JHG5eJGU7Ui3kLKrmwCPPtYsJtBykixrM2OhVQlpMmFWJjMyDfdkGgBMNjXCGB/ebzsgNGQw==", + "dev": true, + "dependencies": { + "@jest/environment": "^28.1.3", + "@jest/fake-timers": "^28.1.3", + "@jest/globals": "^28.1.3", + "@jest/source-map": "^28.1.2", + "@jest/test-result": "^28.1.3", + "@jest/transform": "^28.1.3", + "@jest/types": "^28.1.3", + "chalk": "^4.0.0", + "cjs-module-lexer": "^1.0.0", + "collect-v8-coverage": "^1.0.0", + "execa": "^5.0.0", + "glob": "^7.1.3", + "graceful-fs": "^4.2.9", + "jest-haste-map": "^28.1.3", + "jest-message-util": "^28.1.3", + "jest-mock": "^28.1.3", + "jest-regex-util": "^28.0.2", + "jest-resolve": "^28.1.3", + "jest-snapshot": "^28.1.3", + "jest-util": "^28.1.3", + "slash": "^3.0.0", + "strip-bom": "^4.0.0" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" + } + }, + "node_modules/jest-runtime/node_modules/ansi-styles": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", + "dev": true, + "engines": { + "node": ">=10" }, "funding": { "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "node_modules/jest-message-util/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "node_modules/jest-runtime/node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", "dev": true, "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" }, "engines": { - "node": ">=10" + "node": "*" }, "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" + "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/jest-message-util/node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "node_modules/jest-runtime/node_modules/jest-message-util": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-28.1.3.tgz", + "integrity": "sha512-PFdn9Iewbt575zKPf1286Ht9EPoJmYT7P0kY+RibeYZ2XtOr53pDLEFoTWXbd1h4JiGiWpTBC84fc8xMXQMb7g==", "dev": true, "dependencies": { - "color-name": "~1.1.4" + "@babel/code-frame": "^7.12.13", + "@jest/types": "^28.1.3", + "@types/stack-utils": "^2.0.0", + "chalk": "^4.0.0", + "graceful-fs": "^4.2.9", + "micromatch": "^4.0.4", + "pretty-format": "^28.1.3", + "slash": "^3.0.0", + "stack-utils": "^2.0.3" }, "engines": { - "node": ">=7.0.0" + "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" } }, - "node_modules/jest-message-util/node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "node_modules/jest-message-util/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "node_modules/jest-runtime/node_modules/jest-util": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-28.1.3.tgz", + "integrity": "sha512-XdqfpHwpcSRko/C35uLYFM2emRAltIIKZiJ9eAmhjsj0CqZMa0p1ib0R5fWIqGhn1a103DebTbpqIaP1qCQ6tQ==", "dev": true, + "dependencies": { + "@jest/types": "^28.1.3", + "@types/node": "*", + "chalk": "^4.0.0", + "ci-info": "^3.2.0", + "graceful-fs": "^4.2.9", + "picomatch": "^2.2.3" + }, "engines": { - "node": ">=8" + "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" } }, - "node_modules/jest-message-util/node_modules/pretty-format": { + "node_modules/jest-runtime/node_modules/pretty-format": { "version": "28.1.3", "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-28.1.3.tgz", "integrity": "sha512-8gFb/To0OmxHR9+ZTb14Df2vNxdGCX8g1xWGUTqUw5TiZvcQf5sHKObd5UcPyLLyowNwDAMTF3XWOG1B6mxl1Q==", @@ -5288,6952 +4746,160 @@ "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" } }, - "node_modules/jest-message-util/node_modules/pretty-format/node_modules/ansi-styles": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", - "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", - "dev": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/jest-message-util/node_modules/react-is": { + "node_modules/jest-runtime/node_modules/react-is": { "version": "18.2.0", "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz", "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==", "dev": true }, - "node_modules/jest-message-util/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "node_modules/jest-snapshot": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-28.1.3.tgz", + "integrity": "sha512-4lzMgtiNlc3DU/8lZfmqxN3AYD6GGLbl+72rdBpXvcV+whX7mDrREzkPdp2RnmfIiWBg1YbuFSkXduF2JcafJg==", "dev": true, "dependencies": { - "has-flag": "^4.0.0" + "@babel/core": "^7.11.6", + "@babel/generator": "^7.7.2", + "@babel/plugin-syntax-typescript": "^7.7.2", + "@babel/traverse": "^7.7.2", + "@babel/types": "^7.3.3", + "@jest/expect-utils": "^28.1.3", + "@jest/transform": "^28.1.3", + "@jest/types": "^28.1.3", + "@types/babel__traverse": "^7.0.6", + "@types/prettier": "^2.1.5", + "babel-preset-current-node-syntax": "^1.0.0", + "chalk": "^4.0.0", + "expect": "^28.1.3", + "graceful-fs": "^4.2.9", + "jest-diff": "^28.1.3", + "jest-get-type": "^28.0.2", + "jest-haste-map": "^28.1.3", + "jest-matcher-utils": "^28.1.3", + "jest-message-util": "^28.1.3", + "jest-util": "^28.1.3", + "natural-compare": "^1.4.0", + "pretty-format": "^28.1.3", + "semver": "^7.3.5" }, "engines": { - "node": ">=8" + "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" } }, - "node_modules/jest-mock": { + "node_modules/jest-snapshot/node_modules/@jest/expect-utils": { "version": "28.1.3", - "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-28.1.3.tgz", - "integrity": "sha512-o3J2jr6dMMWYVH4Lh/NKmDXdosrsJgi4AviS8oXLujcjpCMBb1FMsblDnOXKZKfSiHLxYub1eS0IHuRXsio9eA==", + "resolved": "https://registry.npmjs.org/@jest/expect-utils/-/expect-utils-28.1.3.tgz", + "integrity": "sha512-wvbi9LUrHJLn3NlDW6wF2hvIMtd4JUl2QNVrjq+IBSHirgfrR3o9RnVtxzdEGO2n9JyIWwHnLfby5KzqBGg2YA==", "dev": true, "dependencies": { - "@jest/types": "^28.1.3", - "@types/node": "*" + "jest-get-type": "^28.0.2" }, "engines": { "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" } }, - "node_modules/jest-pnp-resolver": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/jest-pnp-resolver/-/jest-pnp-resolver-1.2.2.tgz", - "integrity": "sha512-olV41bKSMm8BdnuMsewT4jqlZ8+3TCARAXjZGT9jcoSnrfUnRCqnMoF9XEeoWjbzObpqF9dRhHQj0Xb9QdF6/w==", + "node_modules/jest-snapshot/node_modules/ansi-styles": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", "dev": true, "engines": { - "node": ">=6" - }, - "peerDependencies": { - "jest-resolve": "*" + "node": ">=10" }, - "peerDependenciesMeta": { - "jest-resolve": { - "optional": true - } + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "node_modules/jest-regex-util": { - "version": "28.0.2", - "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-28.0.2.tgz", - "integrity": "sha512-4s0IgyNIy0y9FK+cjoVYoxamT7Zeo7MhzqRGx7YDYmaQn1wucY9rotiGkBzzcMXTtjrCAP/f7f+E0F7+fxPNdw==", - "dev": true, - "engines": { - "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" - } - }, - "node_modules/jest-resolve": { - "version": "28.1.3", - "resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-28.1.3.tgz", - "integrity": "sha512-Z1W3tTjE6QaNI90qo/BJpfnvpxtaFTFw5CDgwpyE/Kz8U/06N1Hjf4ia9quUhCh39qIGWF1ZuxFiBiJQwSEYKQ==", + "node_modules/jest-snapshot/node_modules/diff-sequences": { + "version": "28.1.1", + "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-28.1.1.tgz", + "integrity": "sha512-FU0iFaH/E23a+a718l8Qa/19bF9p06kgE0KipMOMadwa3SjnaElKzPaUC0vnibs6/B/9ni97s61mcejk8W1fQw==", "dev": true, - "dependencies": { - "chalk": "^4.0.0", - "graceful-fs": "^4.2.9", - "jest-haste-map": "^28.1.3", - "jest-pnp-resolver": "^1.2.2", - "jest-util": "^28.1.3", - "jest-validate": "^28.1.3", - "resolve": "^1.20.0", - "resolve.exports": "^1.1.0", - "slash": "^3.0.0" - }, "engines": { "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" } }, - "node_modules/jest-resolve-dependencies": { + "node_modules/jest-snapshot/node_modules/expect": { "version": "28.1.3", - "resolved": "https://registry.npmjs.org/jest-resolve-dependencies/-/jest-resolve-dependencies-28.1.3.tgz", - "integrity": "sha512-qa0QO2Q0XzQoNPouMbCc7Bvtsem8eQgVPNkwn9LnS+R2n8DaVDPL/U1gngC0LTl1RYXJU0uJa2BMC2DbTfFrHA==", + "resolved": "https://registry.npmjs.org/expect/-/expect-28.1.3.tgz", + "integrity": "sha512-eEh0xn8HlsuOBxFgIss+2mX85VAS4Qy3OSkjV7rlBWljtA4oWH37glVGyOZSZvErDT/yBywZdPGwCXuTvSG85g==", "dev": true, "dependencies": { - "jest-regex-util": "^28.0.2", - "jest-snapshot": "^28.1.3" + "@jest/expect-utils": "^28.1.3", + "jest-get-type": "^28.0.2", + "jest-matcher-utils": "^28.1.3", + "jest-message-util": "^28.1.3", + "jest-util": "^28.1.3" }, "engines": { "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" } }, - "node_modules/jest-resolve/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/jest-resolve/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/jest-resolve/node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/jest-resolve/node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "node_modules/jest-resolve/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-resolve/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-runner": { + "node_modules/jest-snapshot/node_modules/jest-diff": { "version": "28.1.3", - "resolved": "https://registry.npmjs.org/jest-runner/-/jest-runner-28.1.3.tgz", - "integrity": "sha512-GkMw4D/0USd62OVO0oEgjn23TM+YJa2U2Wu5zz9xsQB1MxWKDOlrnykPxnMsN0tnJllfLPinHTka61u0QhaxBA==", + "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-28.1.3.tgz", + "integrity": "sha512-8RqP1B/OXzjjTWkqMX67iqgwBVJRgCyKD3L9nq+6ZqJMdvjE8RgHktqZ6jNrkdMT+dJuYNI3rhQpxaz7drJHfw==", "dev": true, "dependencies": { - "@jest/console": "^28.1.3", - "@jest/environment": "^28.1.3", - "@jest/test-result": "^28.1.3", - "@jest/transform": "^28.1.3", - "@jest/types": "^28.1.3", - "@types/node": "*", "chalk": "^4.0.0", - "emittery": "^0.10.2", - "graceful-fs": "^4.2.9", - "jest-docblock": "^28.1.1", - "jest-environment-node": "^28.1.3", - "jest-haste-map": "^28.1.3", - "jest-leak-detector": "^28.1.3", - "jest-message-util": "^28.1.3", - "jest-resolve": "^28.1.3", - "jest-runtime": "^28.1.3", - "jest-util": "^28.1.3", - "jest-watcher": "^28.1.3", - "jest-worker": "^28.1.3", - "p-limit": "^3.1.0", - "source-map-support": "0.5.13" + "diff-sequences": "^28.1.1", + "jest-get-type": "^28.0.2", + "pretty-format": "^28.1.3" }, "engines": { "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" } }, - "node_modules/jest-runner/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/jest-runner/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/jest-runner/node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/jest-runner/node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "node_modules/jest-runner/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-runner/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "node_modules/jest-snapshot/node_modules/jest-get-type": { + "version": "28.0.2", + "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-28.0.2.tgz", + "integrity": "sha512-ioj2w9/DxSYHfOm5lJKCdcAmPJzQXmbM/Url3rhlghrPvT3tt+7a/+oXc9azkKmLvoiXjtV83bEWqi+vs5nlPA==", "dev": true, - "dependencies": { - "has-flag": "^4.0.0" - }, "engines": { - "node": ">=8" + "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" } }, - "node_modules/jest-runtime": { + "node_modules/jest-snapshot/node_modules/jest-matcher-utils": { "version": "28.1.3", - "resolved": "https://registry.npmjs.org/jest-runtime/-/jest-runtime-28.1.3.tgz", - "integrity": "sha512-NU+881ScBQQLc1JHG5eJGU7Ui3kLKrmwCPPtYsJtBykixrM2OhVQlpMmFWJjMyDfdkGgBMNjXCGB/ebzsgNGQw==", + "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-28.1.3.tgz", + "integrity": "sha512-kQeJ7qHemKfbzKoGjHHrRKH6atgxMk8Enkk2iPQ3XwO6oE/KYD8lMYOziCkeSB9G4adPM4nR1DE8Tf5JeWH6Bw==", "dev": true, "dependencies": { - "@jest/environment": "^28.1.3", - "@jest/fake-timers": "^28.1.3", - "@jest/globals": "^28.1.3", - "@jest/source-map": "^28.1.2", - "@jest/test-result": "^28.1.3", - "@jest/transform": "^28.1.3", - "@jest/types": "^28.1.3", "chalk": "^4.0.0", - "cjs-module-lexer": "^1.0.0", - "collect-v8-coverage": "^1.0.0", - "execa": "^5.0.0", - "glob": "^7.1.3", - "graceful-fs": "^4.2.9", - "jest-haste-map": "^28.1.3", - "jest-message-util": "^28.1.3", - "jest-mock": "^28.1.3", - "jest-regex-util": "^28.0.2", - "jest-resolve": "^28.1.3", - "jest-snapshot": "^28.1.3", - "jest-util": "^28.1.3", - "slash": "^3.0.0", - "strip-bom": "^4.0.0" - }, - "engines": { - "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" - } - }, - "node_modules/jest-runtime/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/jest-runtime/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/jest-runtime/node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/jest-runtime/node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "node_modules/jest-runtime/node_modules/glob": { - "version": "7.2.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", - "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", - "dev": true, - "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.1.1", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - }, - "engines": { - "node": "*" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/jest-runtime/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-runtime/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-snapshot": { - "version": "28.1.3", - "resolved": "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-28.1.3.tgz", - "integrity": "sha512-4lzMgtiNlc3DU/8lZfmqxN3AYD6GGLbl+72rdBpXvcV+whX7mDrREzkPdp2RnmfIiWBg1YbuFSkXduF2JcafJg==", - "dev": true, - "dependencies": { - "@babel/core": "^7.11.6", - "@babel/generator": "^7.7.2", - "@babel/plugin-syntax-typescript": "^7.7.2", - "@babel/traverse": "^7.7.2", - "@babel/types": "^7.3.3", - "@jest/expect-utils": "^28.1.3", - "@jest/transform": "^28.1.3", - "@jest/types": "^28.1.3", - "@types/babel__traverse": "^7.0.6", - "@types/prettier": "^2.1.5", - "babel-preset-current-node-syntax": "^1.0.0", - "chalk": "^4.0.0", - "expect": "^28.1.3", - "graceful-fs": "^4.2.9", - "jest-diff": "^28.1.3", - "jest-get-type": "^28.0.2", - "jest-haste-map": "^28.1.3", - "jest-matcher-utils": "^28.1.3", - "jest-message-util": "^28.1.3", - "jest-util": "^28.1.3", - "natural-compare": "^1.4.0", - "pretty-format": "^28.1.3", - "semver": "^7.3.5" - }, - "engines": { - "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" - } - }, - "node_modules/jest-snapshot/node_modules/@jest/expect-utils": { - "version": "28.1.3", - "resolved": "https://registry.npmjs.org/@jest/expect-utils/-/expect-utils-28.1.3.tgz", - "integrity": "sha512-wvbi9LUrHJLn3NlDW6wF2hvIMtd4JUl2QNVrjq+IBSHirgfrR3o9RnVtxzdEGO2n9JyIWwHnLfby5KzqBGg2YA==", - "dev": true, - "dependencies": { - "jest-get-type": "^28.0.2" - }, - "engines": { - "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" - } - }, - "node_modules/jest-snapshot/node_modules/@jest/schemas": { - "version": "28.1.3", - "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-28.1.3.tgz", - "integrity": "sha512-/l/VWsdt/aBXgjshLWOFyFt3IVdYypu5y2Wn2rOO1un6nkqIn8SLXzgIMYXFyYsRWDyF5EthmKJMIdJvk08grg==", - "dev": true, - "dependencies": { - "@sinclair/typebox": "^0.24.1" - }, - "engines": { - "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" - } - }, - "node_modules/jest-snapshot/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/jest-snapshot/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/jest-snapshot/node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/jest-snapshot/node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "node_modules/jest-snapshot/node_modules/diff-sequences": { - "version": "28.1.1", - "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-28.1.1.tgz", - "integrity": "sha512-FU0iFaH/E23a+a718l8Qa/19bF9p06kgE0KipMOMadwa3SjnaElKzPaUC0vnibs6/B/9ni97s61mcejk8W1fQw==", - "dev": true, - "engines": { - "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" - } - }, - "node_modules/jest-snapshot/node_modules/expect": { - "version": "28.1.3", - "resolved": "https://registry.npmjs.org/expect/-/expect-28.1.3.tgz", - "integrity": "sha512-eEh0xn8HlsuOBxFgIss+2mX85VAS4Qy3OSkjV7rlBWljtA4oWH37glVGyOZSZvErDT/yBywZdPGwCXuTvSG85g==", - "dev": true, - "dependencies": { - "@jest/expect-utils": "^28.1.3", - "jest-get-type": "^28.0.2", - "jest-matcher-utils": "^28.1.3", - "jest-message-util": "^28.1.3", - "jest-util": "^28.1.3" - }, - "engines": { - "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" - } - }, - "node_modules/jest-snapshot/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-snapshot/node_modules/jest-diff": { - "version": "28.1.3", - "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-28.1.3.tgz", - "integrity": "sha512-8RqP1B/OXzjjTWkqMX67iqgwBVJRgCyKD3L9nq+6ZqJMdvjE8RgHktqZ6jNrkdMT+dJuYNI3rhQpxaz7drJHfw==", - "dev": true, - "dependencies": { - "chalk": "^4.0.0", - "diff-sequences": "^28.1.1", - "jest-get-type": "^28.0.2", - "pretty-format": "^28.1.3" - }, - "engines": { - "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" - } - }, - "node_modules/jest-snapshot/node_modules/jest-get-type": { - "version": "28.0.2", - "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-28.0.2.tgz", - "integrity": "sha512-ioj2w9/DxSYHfOm5lJKCdcAmPJzQXmbM/Url3rhlghrPvT3tt+7a/+oXc9azkKmLvoiXjtV83bEWqi+vs5nlPA==", - "dev": true, - "engines": { - "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" - } - }, - "node_modules/jest-snapshot/node_modules/pretty-format": { - "version": "28.1.3", - "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-28.1.3.tgz", - "integrity": "sha512-8gFb/To0OmxHR9+ZTb14Df2vNxdGCX8g1xWGUTqUw5TiZvcQf5sHKObd5UcPyLLyowNwDAMTF3XWOG1B6mxl1Q==", - "dev": true, - "dependencies": { - "@jest/schemas": "^28.1.3", - "ansi-regex": "^5.0.1", - "ansi-styles": "^5.0.0", - "react-is": "^18.0.0" - }, - "engines": { - "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" - } - }, - "node_modules/jest-snapshot/node_modules/pretty-format/node_modules/ansi-styles": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", - "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", - "dev": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/jest-snapshot/node_modules/react-is": { - "version": "18.2.0", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz", - "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==", - "dev": true - }, - "node_modules/jest-snapshot/node_modules/semver": { - "version": "7.3.7", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.7.tgz", - "integrity": "sha512-QlYTucUYOews+WeEujDoEGziz4K6c47V/Bd+LjSSYcA94p+DmINdf7ncaUinThfvZyu13lN9OY1XDxt8C0Tw0g==", - "dev": true, - "dependencies": { - "lru-cache": "^6.0.0" - }, - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/jest-snapshot/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-util": { - "version": "28.1.3", - "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-28.1.3.tgz", - "integrity": "sha512-XdqfpHwpcSRko/C35uLYFM2emRAltIIKZiJ9eAmhjsj0CqZMa0p1ib0R5fWIqGhn1a103DebTbpqIaP1qCQ6tQ==", - "dev": true, - "dependencies": { - "@jest/types": "^28.1.3", - "@types/node": "*", - "chalk": "^4.0.0", - "ci-info": "^3.2.0", - "graceful-fs": "^4.2.9", - "picomatch": "^2.2.3" - }, - "engines": { - "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" - } - }, - "node_modules/jest-util/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/jest-util/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/jest-util/node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/jest-util/node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "node_modules/jest-util/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-util/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-validate": { - "version": "28.1.3", - "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-28.1.3.tgz", - "integrity": "sha512-SZbOGBWEsaTxBGCOpsRWlXlvNkvTkY0XxRfh7zYmvd8uL5Qzyg0CHAXiXKROflh801quA6+/DsT4ODDthOC/OA==", - "dev": true, - "dependencies": { - "@jest/types": "^28.1.3", - "camelcase": "^6.2.0", - "chalk": "^4.0.0", - "jest-get-type": "^28.0.2", - "leven": "^3.1.0", - "pretty-format": "^28.1.3" - }, - "engines": { - "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" - } - }, - "node_modules/jest-validate/node_modules/@jest/schemas": { - "version": "28.1.3", - "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-28.1.3.tgz", - "integrity": "sha512-/l/VWsdt/aBXgjshLWOFyFt3IVdYypu5y2Wn2rOO1un6nkqIn8SLXzgIMYXFyYsRWDyF5EthmKJMIdJvk08grg==", - "dev": true, - "dependencies": { - "@sinclair/typebox": "^0.24.1" - }, - "engines": { - "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" - } - }, - "node_modules/jest-validate/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/jest-validate/node_modules/camelcase": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", - "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", - "dev": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/jest-validate/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/jest-validate/node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/jest-validate/node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "node_modules/jest-validate/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-validate/node_modules/jest-get-type": { - "version": "28.0.2", - "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-28.0.2.tgz", - "integrity": "sha512-ioj2w9/DxSYHfOm5lJKCdcAmPJzQXmbM/Url3rhlghrPvT3tt+7a/+oXc9azkKmLvoiXjtV83bEWqi+vs5nlPA==", - "dev": true, - "engines": { - "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" - } - }, - "node_modules/jest-validate/node_modules/pretty-format": { - "version": "28.1.3", - "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-28.1.3.tgz", - "integrity": "sha512-8gFb/To0OmxHR9+ZTb14Df2vNxdGCX8g1xWGUTqUw5TiZvcQf5sHKObd5UcPyLLyowNwDAMTF3XWOG1B6mxl1Q==", - "dev": true, - "dependencies": { - "@jest/schemas": "^28.1.3", - "ansi-regex": "^5.0.1", - "ansi-styles": "^5.0.0", - "react-is": "^18.0.0" - }, - "engines": { - "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" - } - }, - "node_modules/jest-validate/node_modules/pretty-format/node_modules/ansi-styles": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", - "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", - "dev": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/jest-validate/node_modules/react-is": { - "version": "18.2.0", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz", - "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==", - "dev": true - }, - "node_modules/jest-validate/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-watcher": { - "version": "28.1.3", - "resolved": "https://registry.npmjs.org/jest-watcher/-/jest-watcher-28.1.3.tgz", - "integrity": "sha512-t4qcqj9hze+jviFPUN3YAtAEeFnr/azITXQEMARf5cMwKY2SMBRnCQTXLixTl20OR6mLh9KLMrgVJgJISym+1g==", - "dev": true, - "dependencies": { - "@jest/test-result": "^28.1.3", - "@jest/types": "^28.1.3", - "@types/node": "*", - "ansi-escapes": "^4.2.1", - "chalk": "^4.0.0", - "emittery": "^0.10.2", - "jest-util": "^28.1.3", - "string-length": "^4.0.1" - }, - "engines": { - "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" - } - }, - "node_modules/jest-watcher/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/jest-watcher/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/jest-watcher/node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/jest-watcher/node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "node_modules/jest-watcher/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-watcher/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-worker": { - "version": "28.1.3", - "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-28.1.3.tgz", - "integrity": "sha512-CqRA220YV/6jCo8VWvAt1KKx6eek1VIHMPeLEbpcfSfkEeWyBNppynM/o6q+Wmw+sOhos2ml34wZbSX3G13//g==", - "dev": true, - "dependencies": { - "@types/node": "*", - "merge-stream": "^2.0.0", - "supports-color": "^8.0.0" - }, - "engines": { - "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" - } - }, - "node_modules/jest-worker/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-worker/node_modules/supports-color": { - "version": "8.1.1", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", - "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", - "dev": true, - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/supports-color?sponsor=1" - } - }, - "node_modules/js-sha256": { - "version": "0.9.0", - "resolved": "https://registry.npmjs.org/js-sha256/-/js-sha256-0.9.0.tgz", - "integrity": "sha512-sga3MHh9sgQN2+pJ9VYZ+1LPwXOxuBJBA5nrR5/ofPfuiJBE2hnjsaN8se8JznOmGLN2p49Pe5U/ttafcs/apA==" - }, - "node_modules/js-tokens": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", - "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", - "dev": true - }, - "node_modules/js-yaml": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", - "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", - "dev": true, - "dependencies": { - "argparse": "^2.0.1" - }, - "bin": { - "js-yaml": "bin/js-yaml.js" - } - }, - "node_modules/jsesc": { - "version": "2.5.2", - "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", - "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==", - "dev": true, - "bin": { - "jsesc": "bin/jsesc" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/json-parse-even-better-errors": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", - "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", - "dev": true - }, - "node_modules/json-schema-traverse": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", - "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", - "dev": true - }, - "node_modules/json-stable-stringify-without-jsonify": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", - "integrity": "sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE=", - "dev": true - }, - "node_modules/json5": { - "version": "2.2.3", - "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", - "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", - "dev": true, - "bin": { - "json5": "lib/cli.js" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/jsonc-parser": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-3.2.0.tgz", - "integrity": "sha512-gfFQZrcTc8CnKXp6Y4/CBT3fTc0OVuDofpre4aEeEpSBPV5X5v4+Vmx+8snU7RLPrNHPKSgLxGo9YuQzz20o+w==", - "dev": true - }, - "node_modules/jsonfile": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", - "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", - "dev": true, - "dependencies": { - "universalify": "^2.0.0" - }, - "optionalDependencies": { - "graceful-fs": "^4.1.6" - } - }, - "node_modules/kleur": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/kleur/-/kleur-3.0.3.tgz", - "integrity": "sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/leven": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/leven/-/leven-3.1.0.tgz", - "integrity": "sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/levn": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", - "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", - "dev": true, - "dependencies": { - "prelude-ls": "^1.2.1", - "type-check": "~0.4.0" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/lines-and-columns": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", - "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", - "dev": true - }, - "node_modules/locate-path": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", - "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", - "dev": true, - "dependencies": { - "p-locate": "^4.1.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/lodash.memoize": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz", - "integrity": "sha1-vMbEmkKihA7Zl/Mj6tpezRguC/4=", - "dev": true - }, - "node_modules/lodash.merge": { - "version": "4.6.2", - "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", - "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", - "dev": true - }, - "node_modules/lru-cache": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", - "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", - "dev": true, - "dependencies": { - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/lunr": { - "version": "2.3.9", - "resolved": "https://registry.npmjs.org/lunr/-/lunr-2.3.9.tgz", - "integrity": "sha512-zTU3DaZaF3Rt9rhN3uBMGQD3dD2/vFQqnvZCDv4dl5iOzq2IZQqTxu90r4E5J+nP70J3ilqVCrbho2eWaeW8Ow==", - "dev": true - }, - "node_modules/make-dir": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", - "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==", - "dev": true, - "dependencies": { - "semver": "^6.0.0" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/make-error": { - "version": "1.3.6", - "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", - "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", - "dev": true - }, - "node_modules/makeerror": { - "version": "1.0.12", - "resolved": "https://registry.npmjs.org/makeerror/-/makeerror-1.0.12.tgz", - "integrity": "sha512-JmqCvUhmt43madlpFzG4BQzG2Z3m6tvQDNKdClZnO3VbIudJYmxsT0FNJMeiB2+JTSlTQTSbU8QdesVmwJcmLg==", - "dev": true, - "dependencies": { - "tmpl": "1.0.5" - } - }, - "node_modules/marked": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/marked/-/marked-4.3.0.tgz", - "integrity": "sha512-PRsaiG84bK+AMvxziE/lCFss8juXjNaWzVbN5tXAm4XjeaS9NAHhop+PjQxz2A9h8Q4M/xGmzP8vqNwy6JeK0A==", - "dev": true, - "bin": { - "marked": "bin/marked.js" - }, - "engines": { - "node": ">= 12" - } - }, - "node_modules/merge-stream": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", - "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", - "dev": true - }, - "node_modules/merge2": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", - "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", - "dev": true, - "engines": { - "node": ">= 8" - } - }, - "node_modules/micromatch": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.4.tgz", - "integrity": "sha512-pRmzw/XUcwXGpD9aI9q/0XOwLNygjETJ8y0ao0wdqprrzDa4YnxLcz7fQRZr8voh8V10kGhABbNcHVk5wHgWwg==", - "dev": true, - "dependencies": { - "braces": "^3.0.1", - "picomatch": "^2.2.3" - }, - "engines": { - "node": ">=8.6" - } - }, - "node_modules/mimic-fn": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", - "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dev": true, - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, - "node_modules/minimist": { - "version": "1.2.7", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.7.tgz", - "integrity": "sha512-bzfL1YUZsP41gmu/qjrEk0Q6i2ix/cVeAhbCbqH9u3zYutS1cLg00qhrD0M2MVdCcx4Sc0UpP2eBWo9rotpq6g==", - "dev": true, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true - }, - "node_modules/natural-compare": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", - "integrity": "sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=", - "dev": true - }, - "node_modules/neo-async": { - "version": "2.6.2", - "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz", - "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==", - "dev": true - }, - "node_modules/node-int64": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz", - "integrity": "sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw==", - "dev": true - }, - "node_modules/node-releases": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.6.tgz", - "integrity": "sha512-PiVXnNuFm5+iYkLBNeq5211hvO38y63T0i2KKh2KnUs3RpzJ+JtODFjkD8yjLwnDkTYF1eKXheUwdssR+NRZdg==", - "dev": true - }, - "node_modules/normalize-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", - "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/npm-run-path": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", - "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", - "dev": true, - "dependencies": { - "path-key": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/once": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", - "dev": true, - "dependencies": { - "wrappy": "1" - } - }, - "node_modules/onetime": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", - "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", - "dev": true, - "dependencies": { - "mimic-fn": "^2.1.0" - }, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/optionator": { - "version": "0.9.1", - "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.1.tgz", - "integrity": "sha512-74RlY5FCnhq4jRxVUPKDaRwrVNXMqsGsiW6AJw4XK8hmtm10wC0ypZBLw5IIp85NZMr91+qd1RvvENwg7jjRFw==", - "dev": true, - "dependencies": { - "deep-is": "^0.1.3", - "fast-levenshtein": "^2.0.6", - "levn": "^0.4.1", - "prelude-ls": "^1.2.1", - "type-check": "^0.4.0", - "word-wrap": "^1.2.3" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/p-limit": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", - "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", - "dev": true, - "dependencies": { - "yocto-queue": "^0.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/p-locate": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", - "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", - "dev": true, - "dependencies": { - "p-limit": "^2.2.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/p-locate/node_modules/p-limit": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", - "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", - "dev": true, - "dependencies": { - "p-try": "^2.0.0" - }, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/p-try": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", - "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/parent-module": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", - "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", - "dev": true, - "dependencies": { - "callsites": "^3.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/parse-json": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", - "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", - "dev": true, - "dependencies": { - "@babel/code-frame": "^7.0.0", - "error-ex": "^1.3.1", - "json-parse-even-better-errors": "^2.3.0", - "lines-and-columns": "^1.1.6" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/path-exists": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", - "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/path-is-absolute": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", - "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/path-key": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", - "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/path-parse": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", - "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", - "dev": true - }, - "node_modules/path-type": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", - "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/picocolors": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", - "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==", - "dev": true - }, - "node_modules/picomatch": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.0.tgz", - "integrity": "sha512-lY1Q/PiJGC2zOv/z391WOTD+Z02bCgsFfvxoXXf6h7kv9o+WmsmzYqrAwY63sNgOxE4xEdq0WyUnXfKeBrSvYw==", - "dev": true, - "engines": { - "node": ">=8.6" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" - } - }, - "node_modules/pirates": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.5.tgz", - "integrity": "sha512-8V9+HQPupnaXMA23c5hvl69zXvTwTzyAYasnkb0Tts4XvO4CliqONMOnvlq26rkhLC3nWDFBJf73LU1e1VZLaQ==", - "dev": true, - "engines": { - "node": ">= 6" - } - }, - "node_modules/pkg-dir": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", - "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", - "dev": true, - "dependencies": { - "find-up": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/playwright-core": { - "version": "1.25.2", - "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.25.2.tgz", - "integrity": "sha512-0yTbUE9lIddkEpLHL3u8PoCL+pWiZtj5A/j3U7YoNjcmKKDGBnCrgHJMzwd2J5vy6l28q4ki3JIuz7McLHhl1A==", - "dev": true, - "bin": { - "playwright": "cli.js" - }, - "engines": { - "node": ">=14" - } - }, - "node_modules/prelude-ls": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", - "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", - "dev": true, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/prettier": { - "version": "2.8.4", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.8.4.tgz", - "integrity": "sha512-vIS4Rlc2FNh0BySk3Wkd6xmwxB0FpOndW5fisM5H8hsZSxU2VWVB5CWIkIjWvrHjIhxk2g3bfMKM87zNTrZddw==", - "dev": true, - "bin": { - "prettier": "bin-prettier.js" - }, - "engines": { - "node": ">=10.13.0" - }, - "funding": { - "url": "https://github.com/prettier/prettier?sponsor=1" - } - }, - "node_modules/pretty-format": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-27.5.1.tgz", - "integrity": "sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ==", - "dev": true, - "dependencies": { - "ansi-regex": "^5.0.1", - "ansi-styles": "^5.0.0", - "react-is": "^17.0.1" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/pretty-format/node_modules/ansi-styles": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", - "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", - "dev": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/progress": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz", - "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==", - "dev": true, - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/prompts": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/prompts/-/prompts-2.4.2.tgz", - "integrity": "sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q==", - "dev": true, - "dependencies": { - "kleur": "^3.0.3", - "sisteransi": "^1.0.5" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/punycode": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", - "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/queue-microtask": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", - "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ] - }, - "node_modules/react-is": { - "version": "17.0.2", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz", - "integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==", - "dev": true - }, - "node_modules/reflect-metadata": { - "version": "0.1.13", - "resolved": "https://registry.npmjs.org/reflect-metadata/-/reflect-metadata-0.1.13.tgz", - "integrity": "sha512-Ts1Y/anZELhSsjMcU605fU9RE4Oi3p5ORujwbIKXfWa+0Zxs510Qrmrce5/Jowq3cHSZSJqBjypxmHarc+vEWg==" - }, - "node_modules/regexpp": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-3.2.0.tgz", - "integrity": "sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg==", - "dev": true, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/mysticatea" - } - }, - "node_modules/replace-in-file": { - "version": "6.3.5", - "resolved": "https://registry.npmjs.org/replace-in-file/-/replace-in-file-6.3.5.tgz", - "integrity": "sha512-arB9d3ENdKva2fxRnSjwBEXfK1npgyci7ZZuwysgAp7ORjHSyxz6oqIjTEv8R0Ydl4Ll7uOAZXL4vbkhGIizCg==", - "dev": true, - "dependencies": { - "chalk": "^4.1.2", - "glob": "^7.2.0", - "yargs": "^17.2.1" - }, - "bin": { - "replace-in-file": "bin/cli.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/replace-in-file/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/replace-in-file/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/replace-in-file/node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/replace-in-file/node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "node_modules/replace-in-file/node_modules/glob": { - "version": "7.2.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", - "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", - "dev": true, - "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.1.1", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - }, - "engines": { - "node": "*" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/replace-in-file/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/replace-in-file/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/require-directory": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", - "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/resolve": { - "version": "1.22.1", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.1.tgz", - "integrity": "sha512-nBpuuYuY5jFsli/JIs1oldw6fOQCBioohqWZg/2hiaOybXOft4lonv85uDOKXdf8rhyK159cxU5cDcK/NKk8zw==", - "dev": true, - "dependencies": { - "is-core-module": "^2.9.0", - "path-parse": "^1.0.7", - "supports-preserve-symlinks-flag": "^1.0.0" - }, - "bin": { - "resolve": "bin/resolve" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/resolve-cwd": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-3.0.0.tgz", - "integrity": "sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg==", - "dev": true, - "dependencies": { - "resolve-from": "^5.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/resolve-cwd/node_modules/resolve-from": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", - "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/resolve-from": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", - "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/resolve.exports": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/resolve.exports/-/resolve.exports-1.1.0.tgz", - "integrity": "sha512-J1l+Zxxp4XK3LUDZ9m60LRJF/mAe4z6a4xyabPHk7pvK5t35dACV32iIjJDFeWZFfZlO29w6SZ67knR0tHzJtQ==", - "dev": true, - "engines": { - "node": ">=10" - } - }, - "node_modules/reusify": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", - "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", - "dev": true, - "engines": { - "iojs": ">=1.0.0", - "node": ">=0.10.0" - } - }, - "node_modules/rimraf": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", - "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", - "dev": true, - "dependencies": { - "glob": "^7.1.3" - }, - "bin": { - "rimraf": "bin.js" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/rimraf/node_modules/glob": { - "version": "7.2.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", - "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", - "dev": true, - "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.1.1", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - }, - "engines": { - "node": "*" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/run-parallel": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", - "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "dependencies": { - "queue-microtask": "^1.2.2" - } - }, - "node_modules/safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", - "dev": true - }, - "node_modules/semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", - "dev": true, - "bin": { - "semver": "bin/semver.js" - } - }, - "node_modules/shebang-command": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", - "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", - "dev": true, - "dependencies": { - "shebang-regex": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/shebang-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", - "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/shiki": { - "version": "0.14.1", - "resolved": "https://registry.npmjs.org/shiki/-/shiki-0.14.1.tgz", - "integrity": "sha512-+Jz4nBkCBe0mEDqo1eKRcCdjRtrCjozmcbTUjbPTX7OOJfEbTZzlUWlZtGe3Gb5oV1/jnojhG//YZc3rs9zSEw==", - "dev": true, - "dependencies": { - "ansi-sequence-parser": "^1.1.0", - "jsonc-parser": "^3.2.0", - "vscode-oniguruma": "^1.7.0", - "vscode-textmate": "^8.0.0" - } - }, - "node_modules/signal-exit": { - "version": "3.0.7", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", - "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", - "dev": true - }, - "node_modules/sisteransi": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz", - "integrity": "sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==", - "dev": true - }, - "node_modules/slash": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", - "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/source-map-support": { - "version": "0.5.13", - "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.13.tgz", - "integrity": "sha512-SHSKFHadjVA5oR4PPqhtAVdcBWwRYVd6g6cAXnIbRiIwc2EhPrTuKUBdSLvlEKyIP3GCf89fltvcZiP9MMFA1w==", - "dev": true, - "dependencies": { - "buffer-from": "^1.0.0", - "source-map": "^0.6.0" - } - }, - "node_modules/sprintf-js": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", - "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==", - "dev": true - }, - "node_modules/stack-utils": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/stack-utils/-/stack-utils-2.0.5.tgz", - "integrity": "sha512-xrQcmYhOsn/1kX+Vraq+7j4oE2j/6BFscZ0etmYg81xuM8Gq0022Pxb8+IqgOFUIaxHs0KaSb7T1+OegiNrNFA==", - "dev": true, - "dependencies": { - "escape-string-regexp": "^2.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/stack-utils/node_modules/escape-string-regexp": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz", - "integrity": "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/string-length": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/string-length/-/string-length-4.0.2.tgz", - "integrity": "sha512-+l6rNN5fYHNhZZy41RXsYptCjA2Igmq4EG7kZAYFQI1E1VTXarr6ZPXBg6eq7Y6eK4FEhY6AJlyuFIb/v/S0VQ==", - "dev": true, - "dependencies": { - "char-regex": "^1.0.2", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/string-width": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "dev": true, - "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dev": true, - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/strip-bom": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-4.0.0.tgz", - "integrity": "sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/strip-final-newline": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz", - "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/strip-json-comments": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", - "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", - "dev": true, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "dev": true, - "dependencies": { - "has-flag": "^3.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/supports-hyperlinks": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/supports-hyperlinks/-/supports-hyperlinks-2.2.0.tgz", - "integrity": "sha512-6sXEzV5+I5j8Bmq9/vUphGRM/RJNT9SCURJLjwfOg51heRtguGWDzcaBlgAzKhQa0EVNpPEKzQuBwZ8S8WaCeQ==", - "dev": true, - "dependencies": { - "has-flag": "^4.0.0", - "supports-color": "^7.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/supports-hyperlinks/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/supports-hyperlinks/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/supports-preserve-symlinks-flag": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", - "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", - "dev": true, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/terminal-link": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/terminal-link/-/terminal-link-2.1.1.tgz", - "integrity": "sha512-un0FmiRUQNr5PJqy9kP7c40F5BOfpGlYTrxonDChEZB7pzZxRNp/bt+ymiy9/npwXya9KH99nJ/GXFIiUkYGFQ==", - "dev": true, - "dependencies": { - "ansi-escapes": "^4.2.1", - "supports-hyperlinks": "^2.0.0" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/test-exclude": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-6.0.0.tgz", - "integrity": "sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==", - "dev": true, - "dependencies": { - "@istanbuljs/schema": "^0.1.2", - "glob": "^7.1.4", - "minimatch": "^3.0.4" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/test-exclude/node_modules/glob": { - "version": "7.2.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", - "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", - "dev": true, - "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.1.1", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - }, - "engines": { - "node": "*" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/text-table": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", - "integrity": "sha1-f17oI66AUgfACvLfSoTsP8+lcLQ=", - "dev": true - }, - "node_modules/tmpl": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.5.tgz", - "integrity": "sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw==", - "dev": true - }, - "node_modules/to-fast-properties": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", - "integrity": "sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/to-regex-range": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", - "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", - "dev": true, - "dependencies": { - "is-number": "^7.0.0" - }, - "engines": { - "node": ">=8.0" - } - }, - "node_modules/ts-jest": { - "version": "28.0.8", - "resolved": "https://registry.npmjs.org/ts-jest/-/ts-jest-28.0.8.tgz", - "integrity": "sha512-5FaG0lXmRPzApix8oFG8RKjAz4ehtm8yMKOTy5HX3fY6W8kmvOrmcY0hKDElW52FJov+clhUbrKAqofnj4mXTg==", - "dev": true, - "dependencies": { - "bs-logger": "0.x", - "fast-json-stable-stringify": "2.x", - "jest-util": "^28.0.0", - "json5": "^2.2.1", - "lodash.memoize": "4.x", - "make-error": "1.x", - "semver": "7.x", - "yargs-parser": "^21.0.1" - }, - "bin": { - "ts-jest": "cli.js" - }, - "engines": { - "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" - }, - "peerDependencies": { - "@babel/core": ">=7.0.0-beta.0 <8", - "@jest/types": "^28.0.0", - "babel-jest": "^28.0.0", - "jest": "^28.0.0", - "typescript": ">=4.3" - }, - "peerDependenciesMeta": { - "@babel/core": { - "optional": true - }, - "@jest/types": { - "optional": true - }, - "babel-jest": { - "optional": true - }, - "esbuild": { - "optional": true - } - } - }, - "node_modules/ts-jest/node_modules/semver": { - "version": "7.3.5", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", - "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", - "dev": true, - "dependencies": { - "lru-cache": "^6.0.0" - }, - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/tslib": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.1.tgz", - "integrity": "sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw==" - }, - "node_modules/type-check": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", - "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", - "dev": true, - "dependencies": { - "prelude-ls": "^1.2.1" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/type-detect": { - "version": "4.0.8", - "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", - "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/type-fest": { - "version": "0.21.3", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz", - "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==", - "dev": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/typedoc": { - "version": "0.24.8", - "resolved": "https://registry.npmjs.org/typedoc/-/typedoc-0.24.8.tgz", - "integrity": "sha512-ahJ6Cpcvxwaxfu4KtjA8qZNqS43wYt6JL27wYiIgl1vd38WW/KWX11YuAeZhuz9v+ttrutSsgK+XO1CjL1kA3w==", - "dev": true, - "dependencies": { - "lunr": "^2.3.9", - "marked": "^4.3.0", - "minimatch": "^9.0.0", - "shiki": "^0.14.1" - }, - "bin": { - "typedoc": "bin/typedoc" - }, - "engines": { - "node": ">= 14.14" - }, - "peerDependencies": { - "typescript": "4.6.x || 4.7.x || 4.8.x || 4.9.x || 5.0.x || 5.1.x" - } - }, - "node_modules/typedoc-plugin-markdown": { - "version": "3.15.3", - "resolved": "https://registry.npmjs.org/typedoc-plugin-markdown/-/typedoc-plugin-markdown-3.15.3.tgz", - "integrity": "sha512-idntFYu3vfaY3eaD+w9DeRd0PmNGqGuNLKihPU9poxFGnATJYGn9dPtEhn2QrTdishFMg7jPXAhos+2T6YCWRQ==", - "dev": true, - "dependencies": { - "handlebars": "^4.7.7" - }, - "peerDependencies": { - "typedoc": ">=0.24.0" - } - }, - "node_modules/typedoc-plugin-merge-modules": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/typedoc-plugin-merge-modules/-/typedoc-plugin-merge-modules-5.0.1.tgz", - "integrity": "sha512-7fiMYDUaeslsGSFDevw+azhD0dFJce0h2g5UuQ8zXljoky+YfmzoNkoTCx+KWaNJo6rz2DzaD2feVJyUhvUegg==", - "dev": true, - "peerDependencies": { - "typedoc": "0.24.x" - } - }, - "node_modules/typedoc/node_modules/brace-expansion": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", - "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", - "dev": true, - "dependencies": { - "balanced-match": "^1.0.0" - } - }, - "node_modules/typedoc/node_modules/minimatch": { - "version": "9.0.3", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.3.tgz", - "integrity": "sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==", - "dev": true, - "dependencies": { - "brace-expansion": "^2.0.1" - }, - "engines": { - "node": ">=16 || 14 >=14.17" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/typescript": { - "version": "5.1.6", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.1.6.tgz", - "integrity": "sha512-zaWCozRZ6DLEWAWFrVDz1H6FVXzUSfTy5FUMWsQlU8Ym5JP9eO4xkTIROFCQvhQf61z6O/G6ugw3SgAnvvm+HA==", - "dev": true, - "bin": { - "tsc": "bin/tsc", - "tsserver": "bin/tsserver" - }, - "engines": { - "node": ">=14.17" - } - }, - "node_modules/uglify-js": { - "version": "3.17.4", - "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.17.4.tgz", - "integrity": "sha512-T9q82TJI9e/C1TAxYvfb16xO120tMVFZrGA3f9/P4424DNu6ypK103y0GPFVa17yotwSyZW5iYXgjYHkGrJW/g==", - "dev": true, - "optional": true, - "bin": { - "uglifyjs": "bin/uglifyjs" - }, - "engines": { - "node": ">=0.8.0" - } - }, - "node_modules/universalify": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.0.tgz", - "integrity": "sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ==", - "dev": true, - "engines": { - "node": ">= 10.0.0" - } - }, - "node_modules/update-browserslist-db": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.5.tgz", - "integrity": "sha512-dteFFpCyvuDdr9S/ff1ISkKt/9YZxKjI9WlRR99c180GaztJtRa/fn18FdxGVKVsnPY7/a/FDN68mcvUmP4U7Q==", - "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/browserslist" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/browserslist" - } - ], - "dependencies": { - "escalade": "^3.1.1", - "picocolors": "^1.0.0" - }, - "bin": { - "browserslist-lint": "cli.js" - }, - "peerDependencies": { - "browserslist": ">= 4.21.0" - } - }, - "node_modules/uri-js": { - "version": "4.4.1", - "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", - "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", - "dev": true, - "dependencies": { - "punycode": "^2.1.0" - } - }, - "node_modules/v8-compile-cache": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.3.0.tgz", - "integrity": "sha512-l8lCEmLcLYZh4nbunNZvQCJc5pv7+RCwa8q/LdUx8u7lsWvPDKmpodJAJNwkAhJC//dFY48KuIEmjtd4RViDrA==", - "dev": true - }, - "node_modules/v8-to-istanbul": { - "version": "9.0.1", - "resolved": "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-9.0.1.tgz", - "integrity": "sha512-74Y4LqY74kLE6IFyIjPtkSTWzUZmj8tdHT9Ii/26dvQ6K9Dl2NbEfj0XgU2sHCtKgt5VupqhlO/5aWuqS+IY1w==", - "dev": true, - "dependencies": { - "@jridgewell/trace-mapping": "^0.3.12", - "@types/istanbul-lib-coverage": "^2.0.1", - "convert-source-map": "^1.6.0" - }, - "engines": { - "node": ">=10.12.0" - } - }, - "node_modules/vscode-oniguruma": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/vscode-oniguruma/-/vscode-oniguruma-1.7.0.tgz", - "integrity": "sha512-L9WMGRfrjOhgHSdOYgCt/yRMsXzLDJSL7BPrOZt73gU0iWO4mpqzqQzOz5srxqTvMBaR0XZTSrVWo4j55Rc6cA==", - "dev": true - }, - "node_modules/vscode-textmate": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/vscode-textmate/-/vscode-textmate-8.0.0.tgz", - "integrity": "sha512-AFbieoL7a5LMqcnOF04ji+rpXadgOXnZsxQr//r83kLPr7biP7am3g9zbaZIaBGwBRWeSvoMD4mgPdX3e4NWBg==", - "dev": true - }, - "node_modules/walker": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/walker/-/walker-1.0.8.tgz", - "integrity": "sha512-ts/8E8l5b7kY0vlWLewOkDXMmPdLcVV4GmOQLyxuSswIJsweeFZtAsMF7k1Nszz+TYBQrlYRmzOnr398y1JemQ==", - "dev": true, - "dependencies": { - "makeerror": "1.0.12" - } - }, - "node_modules/webgl-constants": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/webgl-constants/-/webgl-constants-1.1.1.tgz", - "integrity": "sha512-LkBXKjU5r9vAW7Gcu3T5u+5cvSvh5WwINdr0C+9jpzVB41cjQAP5ePArDtk/WHYdVj0GefCgM73BA7FlIiNtdg==" - }, - "node_modules/whatwg-fetch": { - "version": "3.6.2", - "resolved": "https://registry.npmjs.org/whatwg-fetch/-/whatwg-fetch-3.6.2.tgz", - "integrity": "sha512-bJlen0FcuU/0EMLrdbJ7zOnW6ITZLrZMIarMUVmdKtsGvZna8vxKYaexICWPfZ8qwf9fzNq+UEIZrnSaApt6RA==" - }, - "node_modules/which": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", - "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", - "dev": true, - "dependencies": { - "isexe": "^2.0.0" - }, - "bin": { - "node-which": "bin/node-which" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/word-wrap": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz", - "integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/wordwrap": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz", - "integrity": "sha512-gvVzJFlPycKc5dZN4yPkP8w7Dc37BtP1yczEneOb4uq34pXZcvrtRTmWV8W+Ume+XCxKgbjM+nevkyFPMybd4Q==", - "dev": true - }, - "node_modules/wrap-ansi": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", - "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", - "dev": true, - "dependencies": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" - } - }, - "node_modules/wrap-ansi/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/wrap-ansi/node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/wrap-ansi/node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "node_modules/wrappy": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", - "dev": true - }, - "node_modules/write-file-atomic": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-4.0.2.tgz", - "integrity": "sha512-7KxauUdBmSdWnmpaGFg+ppNjKF8uNLry8LyzjauQDOVONfFLNKrKvQOxZ/VuTIcS/gge/YNahf5RIIQWTSarlg==", - "dev": true, - "dependencies": { - "imurmurhash": "^0.1.4", - "signal-exit": "^3.0.7" - }, - "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" - } - }, - "node_modules/y18n": { - "version": "5.0.8", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", - "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", - "dev": true, - "engines": { - "node": ">=10" - } - }, - "node_modules/yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "dev": true - }, - "node_modules/yargs": { - "version": "17.5.1", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.5.1.tgz", - "integrity": "sha512-t6YAJcxDkNX7NFYiVtKvWUz8l+PaKTLiL63mJYWR2GnHq2gjEWISzsLp9wg3aY36dY1j+gfIEL3pIF+XlJJfbA==", - "dev": true, - "dependencies": { - "cliui": "^7.0.2", - "escalade": "^3.1.1", - "get-caller-file": "^2.0.5", - "require-directory": "^2.1.1", - "string-width": "^4.2.3", - "y18n": "^5.0.5", - "yargs-parser": "^21.0.0" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/yargs-parser": { - "version": "21.1.1", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", - "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", - "dev": true, - "engines": { - "node": ">=12" - } - }, - "node_modules/yocto-queue": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", - "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", - "dev": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - } - }, - "dependencies": { - "@ampproject/remapping": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.2.0.tgz", - "integrity": "sha512-qRmjj8nj9qmLTQXXmaR1cck3UXSRMPrbsLJAasZpF+t3riI71BXed5ebIOYwQntykeZuhjsdweEc9BxH5Jc26w==", - "dev": true, - "requires": { - "@jridgewell/gen-mapping": "^0.1.0", - "@jridgewell/trace-mapping": "^0.3.9" - } - }, - "@babel/code-frame": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.18.6.tgz", - "integrity": "sha512-TDCmlK5eOvH+eH7cdAFlNXeVJqWIQ7gW9tY1GJIpUtFb6CmjVyq2VM3u71bOyR8CRihcCgMUYoDNyLXao3+70Q==", - "dev": true, - "requires": { - "@babel/highlight": "^7.18.6" - } - }, - "@babel/compat-data": { - "version": "7.18.13", - "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.18.13.tgz", - "integrity": "sha512-5yUzC5LqyTFp2HLmDoxGQelcdYgSpP9xsnMWBphAscOdFrHSAVbLNzWiy32sVNDqJRDiJK6klfDnAgu6PAGSHw==", - "dev": true - }, - "@babel/core": { - "version": "7.18.13", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.18.13.tgz", - "integrity": "sha512-ZisbOvRRusFktksHSG6pjj1CSvkPkcZq/KHD45LAkVP/oiHJkNBZWfpvlLmX8OtHDG8IuzsFlVRWo08w7Qxn0A==", - "dev": true, - "requires": { - "@ampproject/remapping": "^2.1.0", - "@babel/code-frame": "^7.18.6", - "@babel/generator": "^7.18.13", - "@babel/helper-compilation-targets": "^7.18.9", - "@babel/helper-module-transforms": "^7.18.9", - "@babel/helpers": "^7.18.9", - "@babel/parser": "^7.18.13", - "@babel/template": "^7.18.10", - "@babel/traverse": "^7.18.13", - "@babel/types": "^7.18.13", - "convert-source-map": "^1.7.0", - "debug": "^4.1.0", - "gensync": "^1.0.0-beta.2", - "json5": "^2.2.1", - "semver": "^6.3.0" - } - }, - "@babel/generator": { - "version": "7.18.13", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.18.13.tgz", - "integrity": "sha512-CkPg8ySSPuHTYPJYo7IRALdqyjM9HCbt/3uOBEFbzyGVP6Mn8bwFPB0jX6982JVNBlYzM1nnPkfjuXSOPtQeEQ==", - "dev": true, - "requires": { - "@babel/types": "^7.18.13", - "@jridgewell/gen-mapping": "^0.3.2", - "jsesc": "^2.5.1" - }, - "dependencies": { - "@jridgewell/gen-mapping": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.2.tgz", - "integrity": "sha512-mh65xKQAzI6iBcFzwv28KVWSmCkdRBWoOh+bYQGW3+6OZvbbN3TqMGo5hqYxQniRcH9F2VZIoJCm4pa3BPDK/A==", - "dev": true, - "requires": { - "@jridgewell/set-array": "^1.0.1", - "@jridgewell/sourcemap-codec": "^1.4.10", - "@jridgewell/trace-mapping": "^0.3.9" - } - } - } - }, - "@babel/helper-compilation-targets": { - "version": "7.18.9", - "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.18.9.tgz", - "integrity": "sha512-tzLCyVmqUiFlcFoAPLA/gL9TeYrF61VLNtb+hvkuVaB5SUjW7jcfrglBIX1vUIoT7CLP3bBlIMeyEsIl2eFQNg==", - "dev": true, - "requires": { - "@babel/compat-data": "^7.18.8", - "@babel/helper-validator-option": "^7.18.6", - "browserslist": "^4.20.2", - "semver": "^6.3.0" - } - }, - "@babel/helper-environment-visitor": { - "version": "7.18.9", - "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.18.9.tgz", - "integrity": "sha512-3r/aACDJ3fhQ/EVgFy0hpj8oHyHpQc+LPtJoY9SzTThAsStm4Ptegq92vqKoE3vD706ZVFWITnMnxucw+S9Ipg==", - "dev": true - }, - "@babel/helper-function-name": { - "version": "7.18.9", - "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.18.9.tgz", - "integrity": "sha512-fJgWlZt7nxGksJS9a0XdSaI4XvpExnNIgRP+rVefWh5U7BL8pPuir6SJUmFKRfjWQ51OtWSzwOxhaH/EBWWc0A==", - "dev": true, - "requires": { - "@babel/template": "^7.18.6", - "@babel/types": "^7.18.9" - } - }, - "@babel/helper-hoist-variables": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.18.6.tgz", - "integrity": "sha512-UlJQPkFqFULIcyW5sbzgbkxn2FKRgwWiRexcuaR8RNJRy8+LLveqPjwZV/bwrLZCN0eUHD/x8D0heK1ozuoo6Q==", - "dev": true, - "requires": { - "@babel/types": "^7.18.6" - } - }, - "@babel/helper-module-imports": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.18.6.tgz", - "integrity": "sha512-0NFvs3VkuSYbFi1x2Vd6tKrywq+z/cLeYC/RJNFrIX/30Bf5aiGYbtvGXolEktzJH8o5E5KJ3tT+nkxuuZFVlA==", - "dev": true, - "requires": { - "@babel/types": "^7.18.6" - } - }, - "@babel/helper-module-transforms": { - "version": "7.18.9", - "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.18.9.tgz", - "integrity": "sha512-KYNqY0ICwfv19b31XzvmI/mfcylOzbLtowkw+mfvGPAQ3kfCnMLYbED3YecL5tPd8nAYFQFAd6JHp2LxZk/J1g==", - "dev": true, - "requires": { - "@babel/helper-environment-visitor": "^7.18.9", - "@babel/helper-module-imports": "^7.18.6", - "@babel/helper-simple-access": "^7.18.6", - "@babel/helper-split-export-declaration": "^7.18.6", - "@babel/helper-validator-identifier": "^7.18.6", - "@babel/template": "^7.18.6", - "@babel/traverse": "^7.18.9", - "@babel/types": "^7.18.9" - } - }, - "@babel/helper-plugin-utils": { - "version": "7.18.9", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.18.9.tgz", - "integrity": "sha512-aBXPT3bmtLryXaoJLyYPXPlSD4p1ld9aYeR+sJNOZjJJGiOpb+fKfh3NkcCu7J54nUJwCERPBExCCpyCOHnu/w==", - "dev": true - }, - "@babel/helper-simple-access": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.18.6.tgz", - "integrity": "sha512-iNpIgTgyAvDQpDj76POqg+YEt8fPxx3yaNBg3S30dxNKm2SWfYhD0TGrK/Eu9wHpUW63VQU894TsTg+GLbUa1g==", - "dev": true, - "requires": { - "@babel/types": "^7.18.6" - } - }, - "@babel/helper-split-export-declaration": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.18.6.tgz", - "integrity": "sha512-bde1etTx6ZyTmobl9LLMMQsaizFVZrquTEHOqKeQESMKo4PlObf+8+JA25ZsIpZhT/WEd39+vOdLXAFG/nELpA==", - "dev": true, - "requires": { - "@babel/types": "^7.18.6" - } - }, - "@babel/helper-string-parser": { - "version": "7.18.10", - "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.18.10.tgz", - "integrity": "sha512-XtIfWmeNY3i4t7t4D2t02q50HvqHybPqW2ki1kosnvWCwuCMeo81Jf0gwr85jy/neUdg5XDdeFE/80DXiO+njw==", - "dev": true - }, - "@babel/helper-validator-identifier": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.18.6.tgz", - "integrity": "sha512-MmetCkz9ej86nJQV+sFCxoGGrUbU3q02kgLciwkrt9QqEB7cP39oKEY0PakknEO0Gu20SskMRi+AYZ3b1TpN9g==", - "dev": true - }, - "@babel/helper-validator-option": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.18.6.tgz", - "integrity": "sha512-XO7gESt5ouv/LRJdrVjkShckw6STTaB7l9BrpBaAHDeF5YZT+01PCwmR0SJHnkW6i8OwW/EVWRShfi4j2x+KQw==", - "dev": true - }, - "@babel/helpers": { - "version": "7.18.9", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.18.9.tgz", - "integrity": "sha512-Jf5a+rbrLoR4eNdUmnFu8cN5eNJT6qdTdOg5IHIzq87WwyRw9PwguLFOWYgktN/60IP4fgDUawJvs7PjQIzELQ==", - "dev": true, - "requires": { - "@babel/template": "^7.18.6", - "@babel/traverse": "^7.18.9", - "@babel/types": "^7.18.9" - } - }, - "@babel/highlight": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.18.6.tgz", - "integrity": "sha512-u7stbOuYjaPezCuLj29hNW1v64M2Md2qupEKP1fHc7WdOA3DgLh37suiSrZYY7haUB7iBeQZ9P1uiRF359do3g==", - "dev": true, - "requires": { - "@babel/helper-validator-identifier": "^7.18.6", - "chalk": "^2.0.0", - "js-tokens": "^4.0.0" - } - }, - "@babel/parser": { - "version": "7.18.13", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.18.13.tgz", - "integrity": "sha512-dgXcIfMuQ0kgzLB2b9tRZs7TTFFaGM2AbtA4fJgUUYukzGH4jwsS7hzQHEGs67jdehpm22vkgKwvbU+aEflgwg==", - "dev": true - }, - "@babel/plugin-syntax-async-generators": { - "version": "7.8.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz", - "integrity": "sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.8.0" - } - }, - "@babel/plugin-syntax-bigint": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-bigint/-/plugin-syntax-bigint-7.8.3.tgz", - "integrity": "sha512-wnTnFlG+YxQm3vDxpGE57Pj0srRU4sHE/mDkt1qv2YJJSeUAec2ma4WLUnUPeKjyrfntVwe/N6dCXpU+zL3Npg==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.8.0" - } - }, - "@babel/plugin-syntax-class-properties": { - "version": "7.12.13", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.12.13.tgz", - "integrity": "sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.12.13" - } - }, - "@babel/plugin-syntax-import-meta": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-meta/-/plugin-syntax-import-meta-7.10.4.tgz", - "integrity": "sha512-Yqfm+XDx0+Prh3VSeEQCPU81yC+JWZ2pDPFSS4ZdpfZhp4MkFMaDC1UqseovEKwSUpnIL7+vK+Clp7bfh0iD7g==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.10.4" - } - }, - "@babel/plugin-syntax-json-strings": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.8.3.tgz", - "integrity": "sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.8.0" - } - }, - "@babel/plugin-syntax-logical-assignment-operators": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-logical-assignment-operators/-/plugin-syntax-logical-assignment-operators-7.10.4.tgz", - "integrity": "sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.10.4" - } - }, - "@babel/plugin-syntax-nullish-coalescing-operator": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-nullish-coalescing-operator/-/plugin-syntax-nullish-coalescing-operator-7.8.3.tgz", - "integrity": "sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.8.0" - } - }, - "@babel/plugin-syntax-numeric-separator": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-numeric-separator/-/plugin-syntax-numeric-separator-7.10.4.tgz", - "integrity": "sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.10.4" - } - }, - "@babel/plugin-syntax-object-rest-spread": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.8.3.tgz", - "integrity": "sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.8.0" - } - }, - "@babel/plugin-syntax-optional-catch-binding": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-catch-binding/-/plugin-syntax-optional-catch-binding-7.8.3.tgz", - "integrity": "sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.8.0" - } - }, - "@babel/plugin-syntax-optional-chaining": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-chaining/-/plugin-syntax-optional-chaining-7.8.3.tgz", - "integrity": "sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.8.0" - } - }, - "@babel/plugin-syntax-top-level-await": { - "version": "7.14.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.14.5.tgz", - "integrity": "sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.14.5" - } - }, - "@babel/plugin-syntax-typescript": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.18.6.tgz", - "integrity": "sha512-mAWAuq4rvOepWCBid55JuRNvpTNf2UGVgoz4JV0fXEKolsVZDzsa4NqCef758WZJj/GDu0gVGItjKFiClTAmZA==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.18.6" - } - }, - "@babel/template": { - "version": "7.18.10", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.18.10.tgz", - "integrity": "sha512-TI+rCtooWHr3QJ27kJxfjutghu44DLnasDMwpDqCXVTal9RLp3RSYNh4NdBrRP2cQAoG9A8juOQl6P6oZG4JxA==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.18.6", - "@babel/parser": "^7.18.10", - "@babel/types": "^7.18.10" - } - }, - "@babel/traverse": { - "version": "7.18.13", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.18.13.tgz", - "integrity": "sha512-N6kt9X1jRMLPxxxPYWi7tgvJRH/rtoU+dbKAPDM44RFHiMH8igdsaSBgFeskhSl/kLWLDUvIh1RXCrTmg0/zvA==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.18.6", - "@babel/generator": "^7.18.13", - "@babel/helper-environment-visitor": "^7.18.9", - "@babel/helper-function-name": "^7.18.9", - "@babel/helper-hoist-variables": "^7.18.6", - "@babel/helper-split-export-declaration": "^7.18.6", - "@babel/parser": "^7.18.13", - "@babel/types": "^7.18.13", - "debug": "^4.1.0", - "globals": "^11.1.0" - } - }, - "@babel/types": { - "version": "7.18.13", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.18.13.tgz", - "integrity": "sha512-ePqfTihzW0W6XAU+aMw2ykilisStJfDnsejDCXRchCcMJ4O0+8DhPXf2YUbZ6wjBlsEmZwLK/sPweWtu8hcJYQ==", - "dev": true, - "requires": { - "@babel/helper-string-parser": "^7.18.10", - "@babel/helper-validator-identifier": "^7.18.6", - "to-fast-properties": "^2.0.0" - } - }, - "@bcoe/v8-coverage": { - "version": "0.2.3", - "resolved": "https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz", - "integrity": "sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==", - "dev": true - }, - "@esbuild/android-arm": { - "version": "0.19.2", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.19.2.tgz", - "integrity": "sha512-tM8yLeYVe7pRyAu9VMi/Q7aunpLwD139EY1S99xbQkT4/q2qa6eA4ige/WJQYdJ8GBL1K33pPFhPfPdJ/WzT8Q==", - "dev": true, - "optional": true - }, - "@esbuild/android-arm64": { - "version": "0.19.2", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.19.2.tgz", - "integrity": "sha512-lsB65vAbe90I/Qe10OjkmrdxSX4UJDjosDgb8sZUKcg3oefEuW2OT2Vozz8ef7wrJbMcmhvCC+hciF8jY/uAkw==", - "dev": true, - "optional": true - }, - "@esbuild/android-x64": { - "version": "0.19.2", - "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.19.2.tgz", - "integrity": "sha512-qK/TpmHt2M/Hg82WXHRc/W/2SGo/l1thtDHZWqFq7oi24AjZ4O/CpPSu6ZuYKFkEgmZlFoa7CooAyYmuvnaG8w==", - "dev": true, - "optional": true - }, - "@esbuild/darwin-arm64": { - "version": "0.19.2", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.19.2.tgz", - "integrity": "sha512-Ora8JokrvrzEPEpZO18ZYXkH4asCdc1DLdcVy8TGf5eWtPO1Ie4WroEJzwI52ZGtpODy3+m0a2yEX9l+KUn0tA==", - "dev": true, - "optional": true - }, - "@esbuild/darwin-x64": { - "version": "0.19.2", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.19.2.tgz", - "integrity": "sha512-tP+B5UuIbbFMj2hQaUr6EALlHOIOmlLM2FK7jeFBobPy2ERdohI4Ka6ZFjZ1ZYsrHE/hZimGuU90jusRE0pwDw==", - "dev": true, - "optional": true - }, - "@esbuild/freebsd-arm64": { - "version": "0.19.2", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.19.2.tgz", - "integrity": "sha512-YbPY2kc0acfzL1VPVK6EnAlig4f+l8xmq36OZkU0jzBVHcOTyQDhnKQaLzZudNJQyymd9OqQezeaBgkTGdTGeQ==", - "dev": true, - "optional": true - }, - "@esbuild/freebsd-x64": { - "version": "0.19.2", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.19.2.tgz", - "integrity": "sha512-nSO5uZT2clM6hosjWHAsS15hLrwCvIWx+b2e3lZ3MwbYSaXwvfO528OF+dLjas1g3bZonciivI8qKR/Hm7IWGw==", - "dev": true, - "optional": true - }, - "@esbuild/linux-arm": { - "version": "0.19.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.19.2.tgz", - "integrity": "sha512-Odalh8hICg7SOD7XCj0YLpYCEc+6mkoq63UnExDCiRA2wXEmGlK5JVrW50vZR9Qz4qkvqnHcpH+OFEggO3PgTg==", - "dev": true, - "optional": true - }, - "@esbuild/linux-arm64": { - "version": "0.19.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.19.2.tgz", - "integrity": "sha512-ig2P7GeG//zWlU0AggA3pV1h5gdix0MA3wgB+NsnBXViwiGgY77fuN9Wr5uoCrs2YzaYfogXgsWZbm+HGr09xg==", - "dev": true, - "optional": true - }, - "@esbuild/linux-ia32": { - "version": "0.19.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.19.2.tgz", - "integrity": "sha512-mLfp0ziRPOLSTek0Gd9T5B8AtzKAkoZE70fneiiyPlSnUKKI4lp+mGEnQXcQEHLJAcIYDPSyBvsUbKUG2ri/XQ==", - "dev": true, - "optional": true - }, - "@esbuild/linux-loong64": { - "version": "0.19.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.19.2.tgz", - "integrity": "sha512-hn28+JNDTxxCpnYjdDYVMNTR3SKavyLlCHHkufHV91fkewpIyQchS1d8wSbmXhs1fiYDpNww8KTFlJ1dHsxeSw==", - "dev": true, - "optional": true - }, - "@esbuild/linux-mips64el": { - "version": "0.19.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.19.2.tgz", - "integrity": "sha512-KbXaC0Sejt7vD2fEgPoIKb6nxkfYW9OmFUK9XQE4//PvGIxNIfPk1NmlHmMg6f25x57rpmEFrn1OotASYIAaTg==", - "dev": true, - "optional": true - }, - "@esbuild/linux-ppc64": { - "version": "0.19.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.19.2.tgz", - "integrity": "sha512-dJ0kE8KTqbiHtA3Fc/zn7lCd7pqVr4JcT0JqOnbj4LLzYnp+7h8Qi4yjfq42ZlHfhOCM42rBh0EwHYLL6LEzcw==", - "dev": true, - "optional": true - }, - "@esbuild/linux-riscv64": { - "version": "0.19.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.19.2.tgz", - "integrity": "sha512-7Z/jKNFufZ/bbu4INqqCN6DDlrmOTmdw6D0gH+6Y7auok2r02Ur661qPuXidPOJ+FSgbEeQnnAGgsVynfLuOEw==", - "dev": true, - "optional": true - }, - "@esbuild/linux-s390x": { - "version": "0.19.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.19.2.tgz", - "integrity": "sha512-U+RinR6aXXABFCcAY4gSlv4CL1oOVvSSCdseQmGO66H+XyuQGZIUdhG56SZaDJQcLmrSfRmx5XZOWyCJPRqS7g==", - "dev": true, - "optional": true - }, - "@esbuild/linux-x64": { - "version": "0.19.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.19.2.tgz", - "integrity": "sha512-oxzHTEv6VPm3XXNaHPyUTTte+3wGv7qVQtqaZCrgstI16gCuhNOtBXLEBkBREP57YTd68P0VgDgG73jSD8bwXQ==", - "dev": true, - "optional": true - }, - "@esbuild/netbsd-x64": { - "version": "0.19.2", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.19.2.tgz", - "integrity": "sha512-WNa5zZk1XpTTwMDompZmvQLHszDDDN7lYjEHCUmAGB83Bgs20EMs7ICD+oKeT6xt4phV4NDdSi/8OfjPbSbZfQ==", - "dev": true, - "optional": true - }, - "@esbuild/openbsd-x64": { - "version": "0.19.2", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.19.2.tgz", - "integrity": "sha512-S6kI1aT3S++Dedb7vxIuUOb3oAxqxk2Rh5rOXOTYnzN8JzW1VzBd+IqPiSpgitu45042SYD3HCoEyhLKQcDFDw==", - "dev": true, - "optional": true - }, - "@esbuild/sunos-x64": { - "version": "0.19.2", - "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.19.2.tgz", - "integrity": "sha512-VXSSMsmb+Z8LbsQGcBMiM+fYObDNRm8p7tkUDMPG/g4fhFX5DEFmjxIEa3N8Zr96SjsJ1woAhF0DUnS3MF3ARw==", - "dev": true, - "optional": true - }, - "@esbuild/win32-arm64": { - "version": "0.19.2", - "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.19.2.tgz", - "integrity": "sha512-5NayUlSAyb5PQYFAU9x3bHdsqB88RC3aM9lKDAz4X1mo/EchMIT1Q+pSeBXNgkfNmRecLXA0O8xP+x8V+g/LKg==", - "dev": true, - "optional": true - }, - "@esbuild/win32-ia32": { - "version": "0.19.2", - "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.19.2.tgz", - "integrity": "sha512-47gL/ek1v36iN0wL9L4Q2MFdujR0poLZMJwhO2/N3gA89jgHp4MR8DKCmwYtGNksbfJb9JoTtbkoe6sDhg2QTA==", - "dev": true, - "optional": true - }, - "@esbuild/win32-x64": { - "version": "0.19.2", - "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.19.2.tgz", - "integrity": "sha512-tcuhV7ncXBqbt/Ybf0IyrMcwVOAPDckMK9rXNHtF17UTK18OKLpg08glminN06pt2WCoALhXdLfSPbVvK/6fxw==", - "dev": true, - "optional": true - }, - "@eslint/eslintrc": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-1.0.4.tgz", - "integrity": "sha512-h8Vx6MdxwWI2WM8/zREHMoqdgLNXEL4QX3MWSVMdyNJGvXVOs+6lp+m2hc3FnuMHDc4poxFNI20vCk0OmI4G0Q==", - "dev": true, - "requires": { - "ajv": "^6.12.4", - "debug": "^4.3.2", - "espree": "^9.0.0", - "globals": "^13.9.0", - "ignore": "^4.0.6", - "import-fresh": "^3.2.1", - "js-yaml": "^4.1.0", - "minimatch": "^3.0.4", - "strip-json-comments": "^3.1.1" - }, - "dependencies": { - "globals": { - "version": "13.12.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-13.12.0.tgz", - "integrity": "sha512-uS8X6lSKN2JumVoXrbUz+uG4BYG+eiawqm3qFcT7ammfbUHeCBoJMlHcec/S3krSk73/AE/f0szYFmgAA3kYZg==", - "dev": true, - "requires": { - "type-fest": "^0.20.2" - } - }, - "ignore": { - "version": "4.0.6", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-4.0.6.tgz", - "integrity": "sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==", - "dev": true - }, - "type-fest": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", - "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", - "dev": true - } - } - }, - "@humanwhocodes/config-array": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.6.0.tgz", - "integrity": "sha512-JQlEKbcgEUjBFhLIF4iqM7u/9lwgHRBcpHrmUNCALK0Q3amXN6lxdoXLnF0sm11E9VqTmBALR87IlUg1bZ8A9A==", - "dev": true, - "requires": { - "@humanwhocodes/object-schema": "^1.2.0", - "debug": "^4.1.1", - "minimatch": "^3.0.4" - } - }, - "@humanwhocodes/object-schema": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz", - "integrity": "sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==", - "dev": true - }, - "@istanbuljs/load-nyc-config": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz", - "integrity": "sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ==", - "dev": true, - "requires": { - "camelcase": "^5.3.1", - "find-up": "^4.1.0", - "get-package-type": "^0.1.0", - "js-yaml": "^3.13.1", - "resolve-from": "^5.0.0" - }, - "dependencies": { - "argparse": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", - "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", - "dev": true, - "requires": { - "sprintf-js": "~1.0.2" - } - }, - "js-yaml": { - "version": "3.14.1", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", - "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", - "dev": true, - "requires": { - "argparse": "^1.0.7", - "esprima": "^4.0.0" - } - }, - "resolve-from": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", - "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", - "dev": true - } - } - }, - "@istanbuljs/schema": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.3.tgz", - "integrity": "sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==", - "dev": true - }, - "@jest/console": { - "version": "28.1.3", - "resolved": "https://registry.npmjs.org/@jest/console/-/console-28.1.3.tgz", - "integrity": "sha512-QPAkP5EwKdK/bxIr6C1I4Vs0rm2nHiANzj/Z5X2JQkrZo6IqvC4ldZ9K95tF0HdidhA8Bo6egxSzUFPYKcEXLw==", - "dev": true, - "requires": { - "@jest/types": "^28.1.3", - "@types/node": "*", - "chalk": "^4.0.0", - "jest-message-util": "^28.1.3", - "jest-util": "^28.1.3", - "slash": "^3.0.0" - }, - "dependencies": { - "ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "requires": { - "color-convert": "^2.0.1" - } - }, - "chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - }, - "color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "requires": { - "color-name": "~1.1.4" - } - }, - "color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true - }, - "supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "requires": { - "has-flag": "^4.0.0" - } - } - } - }, - "@jest/core": { - "version": "28.1.3", - "resolved": "https://registry.npmjs.org/@jest/core/-/core-28.1.3.tgz", - "integrity": "sha512-CIKBrlaKOzA7YG19BEqCw3SLIsEwjZkeJzf5bdooVnW4bH5cktqe3JX+G2YV1aK5vP8N9na1IGWFzYaTp6k6NA==", - "dev": true, - "requires": { - "@jest/console": "^28.1.3", - "@jest/reporters": "^28.1.3", - "@jest/test-result": "^28.1.3", - "@jest/transform": "^28.1.3", - "@jest/types": "^28.1.3", - "@types/node": "*", - "ansi-escapes": "^4.2.1", - "chalk": "^4.0.0", - "ci-info": "^3.2.0", - "exit": "^0.1.2", - "graceful-fs": "^4.2.9", - "jest-changed-files": "^28.1.3", - "jest-config": "^28.1.3", - "jest-haste-map": "^28.1.3", - "jest-message-util": "^28.1.3", - "jest-regex-util": "^28.0.2", - "jest-resolve": "^28.1.3", - "jest-resolve-dependencies": "^28.1.3", - "jest-runner": "^28.1.3", - "jest-runtime": "^28.1.3", - "jest-snapshot": "^28.1.3", - "jest-util": "^28.1.3", - "jest-validate": "^28.1.3", - "jest-watcher": "^28.1.3", - "micromatch": "^4.0.4", - "pretty-format": "^28.1.3", - "rimraf": "^3.0.0", - "slash": "^3.0.0", - "strip-ansi": "^6.0.0" - }, - "dependencies": { - "@jest/schemas": { - "version": "28.1.3", - "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-28.1.3.tgz", - "integrity": "sha512-/l/VWsdt/aBXgjshLWOFyFt3IVdYypu5y2Wn2rOO1un6nkqIn8SLXzgIMYXFyYsRWDyF5EthmKJMIdJvk08grg==", - "dev": true, - "requires": { - "@sinclair/typebox": "^0.24.1" - } - }, - "ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "requires": { - "color-convert": "^2.0.1" - } - }, - "chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - }, - "color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "requires": { - "color-name": "~1.1.4" - } - }, - "color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true - }, - "pretty-format": { - "version": "28.1.3", - "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-28.1.3.tgz", - "integrity": "sha512-8gFb/To0OmxHR9+ZTb14Df2vNxdGCX8g1xWGUTqUw5TiZvcQf5sHKObd5UcPyLLyowNwDAMTF3XWOG1B6mxl1Q==", - "dev": true, - "requires": { - "@jest/schemas": "^28.1.3", - "ansi-regex": "^5.0.1", - "ansi-styles": "^5.0.0", - "react-is": "^18.0.0" - }, - "dependencies": { - "ansi-styles": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", - "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", - "dev": true - } - } - }, - "react-is": { - "version": "18.2.0", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz", - "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==", - "dev": true - }, - "supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "requires": { - "has-flag": "^4.0.0" - } - } - } - }, - "@jest/environment": { - "version": "28.1.3", - "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-28.1.3.tgz", - "integrity": "sha512-1bf40cMFTEkKyEf585R9Iz1WayDjHoHqvts0XFYEqyKM3cFWDpeMoqKKTAF9LSYQModPUlh8FKptoM2YcMWAXA==", - "dev": true, - "requires": { - "@jest/fake-timers": "^28.1.3", - "@jest/types": "^28.1.3", - "@types/node": "*", - "jest-mock": "^28.1.3" - } - }, - "@jest/expect": { - "version": "28.1.3", - "resolved": "https://registry.npmjs.org/@jest/expect/-/expect-28.1.3.tgz", - "integrity": "sha512-lzc8CpUbSoE4dqT0U+g1qODQjBRHPpCPXissXD4mS9+sWQdmmpeJ9zSH1rS1HEkrsMN0fb7nKrJ9giAR1d3wBw==", - "dev": true, - "requires": { - "expect": "^28.1.3", - "jest-snapshot": "^28.1.3" - }, - "dependencies": { - "@jest/expect-utils": { - "version": "28.1.3", - "resolved": "https://registry.npmjs.org/@jest/expect-utils/-/expect-utils-28.1.3.tgz", - "integrity": "sha512-wvbi9LUrHJLn3NlDW6wF2hvIMtd4JUl2QNVrjq+IBSHirgfrR3o9RnVtxzdEGO2n9JyIWwHnLfby5KzqBGg2YA==", - "dev": true, - "requires": { - "jest-get-type": "^28.0.2" - } - }, - "expect": { - "version": "28.1.3", - "resolved": "https://registry.npmjs.org/expect/-/expect-28.1.3.tgz", - "integrity": "sha512-eEh0xn8HlsuOBxFgIss+2mX85VAS4Qy3OSkjV7rlBWljtA4oWH37glVGyOZSZvErDT/yBywZdPGwCXuTvSG85g==", - "dev": true, - "requires": { - "@jest/expect-utils": "^28.1.3", - "jest-get-type": "^28.0.2", - "jest-matcher-utils": "^28.1.3", - "jest-message-util": "^28.1.3", - "jest-util": "^28.1.3" - } - }, - "jest-get-type": { - "version": "28.0.2", - "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-28.0.2.tgz", - "integrity": "sha512-ioj2w9/DxSYHfOm5lJKCdcAmPJzQXmbM/Url3rhlghrPvT3tt+7a/+oXc9azkKmLvoiXjtV83bEWqi+vs5nlPA==", - "dev": true - } - } - }, - "@jest/expect-utils": { - "version": "29.0.1", - "resolved": "https://registry.npmjs.org/@jest/expect-utils/-/expect-utils-29.0.1.tgz", - "integrity": "sha512-Tw5kUUOKmXGQDmQ9TSgTraFFS7HMC1HG/B7y0AN2G2UzjdAXz9BzK2rmNpCSDl7g7y0Gf/VLBm//blonvhtOTQ==", - "dev": true, - "requires": { - "jest-get-type": "^29.0.0" - }, - "dependencies": { - "jest-get-type": { - "version": "29.0.0", - "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-29.0.0.tgz", - "integrity": "sha512-83X19z/HuLKYXYHskZlBAShO7UfLFXu/vWajw9ZNJASN32li8yHMaVGAQqxFW1RCFOkB7cubaL6FaJVQqqJLSw==", - "dev": true - } - } - }, - "@jest/fake-timers": { - "version": "28.1.3", - "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-28.1.3.tgz", - "integrity": "sha512-D/wOkL2POHv52h+ok5Oj/1gOG9HSywdoPtFsRCUmlCILXNn5eIWmcnd3DIiWlJnpGvQtmajqBP95Ei0EimxfLw==", - "dev": true, - "requires": { - "@jest/types": "^28.1.3", - "@sinonjs/fake-timers": "^9.1.2", - "@types/node": "*", - "jest-message-util": "^28.1.3", - "jest-mock": "^28.1.3", - "jest-util": "^28.1.3" - } - }, - "@jest/globals": { - "version": "28.1.3", - "resolved": "https://registry.npmjs.org/@jest/globals/-/globals-28.1.3.tgz", - "integrity": "sha512-XFU4P4phyryCXu1pbcqMO0GSQcYe1IsalYCDzRNyhetyeyxMcIxa11qPNDpVNLeretItNqEmYYQn1UYz/5x1NA==", - "dev": true, - "requires": { - "@jest/environment": "^28.1.3", - "@jest/expect": "^28.1.3", - "@jest/types": "^28.1.3" - } - }, - "@jest/reporters": { - "version": "28.1.3", - "resolved": "https://registry.npmjs.org/@jest/reporters/-/reporters-28.1.3.tgz", - "integrity": "sha512-JuAy7wkxQZVNU/V6g9xKzCGC5LVXx9FDcABKsSXp5MiKPEE2144a/vXTEDoyzjUpZKfVwp08Wqg5A4WfTMAzjg==", - "dev": true, - "requires": { - "@bcoe/v8-coverage": "^0.2.3", - "@jest/console": "^28.1.3", - "@jest/test-result": "^28.1.3", - "@jest/transform": "^28.1.3", - "@jest/types": "^28.1.3", - "@jridgewell/trace-mapping": "^0.3.13", - "@types/node": "*", - "chalk": "^4.0.0", - "collect-v8-coverage": "^1.0.0", - "exit": "^0.1.2", - "glob": "^7.1.3", - "graceful-fs": "^4.2.9", - "istanbul-lib-coverage": "^3.0.0", - "istanbul-lib-instrument": "^5.1.0", - "istanbul-lib-report": "^3.0.0", - "istanbul-lib-source-maps": "^4.0.0", - "istanbul-reports": "^3.1.3", - "jest-message-util": "^28.1.3", - "jest-util": "^28.1.3", - "jest-worker": "^28.1.3", - "slash": "^3.0.0", - "string-length": "^4.0.1", - "strip-ansi": "^6.0.0", - "terminal-link": "^2.0.0", - "v8-to-istanbul": "^9.0.1" - }, - "dependencies": { - "ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "requires": { - "color-convert": "^2.0.1" - } - }, - "chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - }, - "color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "requires": { - "color-name": "~1.1.4" - } - }, - "color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "glob": { - "version": "7.2.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", - "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", - "dev": true, - "requires": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.1.1", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - } - }, - "has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true - }, - "supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "requires": { - "has-flag": "^4.0.0" - } - } - } - }, - "@jest/schemas": { - "version": "29.0.0", - "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-29.0.0.tgz", - "integrity": "sha512-3Ab5HgYIIAnS0HjqJHQYZS+zXc4tUmTmBH3z83ajI6afXp8X3ZtdLX+nXx+I7LNkJD7uN9LAVhgnjDgZa2z0kA==", - "dev": true, - "requires": { - "@sinclair/typebox": "^0.24.1" - } - }, - "@jest/source-map": { - "version": "28.1.2", - "resolved": "https://registry.npmjs.org/@jest/source-map/-/source-map-28.1.2.tgz", - "integrity": "sha512-cV8Lx3BeStJb8ipPHnqVw/IM2VCMWO3crWZzYodSIkxXnRcXJipCdx1JCK0K5MsJJouZQTH73mzf4vgxRaH9ww==", - "dev": true, - "requires": { - "@jridgewell/trace-mapping": "^0.3.13", - "callsites": "^3.0.0", - "graceful-fs": "^4.2.9" - } - }, - "@jest/test-result": { - "version": "28.1.3", - "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-28.1.3.tgz", - "integrity": "sha512-kZAkxnSE+FqE8YjW8gNuoVkkC9I7S1qmenl8sGcDOLropASP+BkcGKwhXoyqQuGOGeYY0y/ixjrd/iERpEXHNg==", - "dev": true, - "requires": { - "@jest/console": "^28.1.3", - "@jest/types": "^28.1.3", - "@types/istanbul-lib-coverage": "^2.0.0", - "collect-v8-coverage": "^1.0.0" - } - }, - "@jest/test-sequencer": { - "version": "28.1.3", - "resolved": "https://registry.npmjs.org/@jest/test-sequencer/-/test-sequencer-28.1.3.tgz", - "integrity": "sha512-NIMPEqqa59MWnDi1kvXXpYbqsfQmSJsIbnd85mdVGkiDfQ9WQQTXOLsvISUfonmnBT+w85WEgneCigEEdHDFxw==", - "dev": true, - "requires": { - "@jest/test-result": "^28.1.3", - "graceful-fs": "^4.2.9", - "jest-haste-map": "^28.1.3", - "slash": "^3.0.0" - } - }, - "@jest/transform": { - "version": "28.1.3", - "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-28.1.3.tgz", - "integrity": "sha512-u5dT5di+oFI6hfcLOHGTAfmUxFRrjK+vnaP0kkVow9Md/M7V/MxqQMOz/VV25UZO8pzeA9PjfTpOu6BDuwSPQA==", - "dev": true, - "requires": { - "@babel/core": "^7.11.6", - "@jest/types": "^28.1.3", - "@jridgewell/trace-mapping": "^0.3.13", - "babel-plugin-istanbul": "^6.1.1", - "chalk": "^4.0.0", - "convert-source-map": "^1.4.0", - "fast-json-stable-stringify": "^2.0.0", - "graceful-fs": "^4.2.9", - "jest-haste-map": "^28.1.3", - "jest-regex-util": "^28.0.2", - "jest-util": "^28.1.3", - "micromatch": "^4.0.4", - "pirates": "^4.0.4", - "slash": "^3.0.0", - "write-file-atomic": "^4.0.1" - }, - "dependencies": { - "ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "requires": { - "color-convert": "^2.0.1" - } - }, - "chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - }, - "color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "requires": { - "color-name": "~1.1.4" - } - }, - "color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true - }, - "supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "requires": { - "has-flag": "^4.0.0" - } - } - } - }, - "@jest/types": { - "version": "28.1.3", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-28.1.3.tgz", - "integrity": "sha512-RyjiyMUZrKz/c+zlMFO1pm70DcIlST8AeWTkoUdZevew44wcNZQHsEVOiCVtgVnlFFD82FPaXycys58cf2muVQ==", - "dev": true, - "requires": { - "@jest/schemas": "^28.1.3", - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^17.0.8", - "chalk": "^4.0.0" - }, - "dependencies": { - "@jest/schemas": { - "version": "28.1.3", - "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-28.1.3.tgz", - "integrity": "sha512-/l/VWsdt/aBXgjshLWOFyFt3IVdYypu5y2Wn2rOO1un6nkqIn8SLXzgIMYXFyYsRWDyF5EthmKJMIdJvk08grg==", - "dev": true, - "requires": { - "@sinclair/typebox": "^0.24.1" - } - }, - "ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "requires": { - "color-convert": "^2.0.1" - } - }, - "chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - }, - "color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "requires": { - "color-name": "~1.1.4" - } - }, - "color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true - }, - "supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "requires": { - "has-flag": "^4.0.0" - } - } - } - }, - "@jridgewell/gen-mapping": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.1.1.tgz", - "integrity": "sha512-sQXCasFk+U8lWYEe66WxRDOE9PjVz4vSM51fTu3Hw+ClTpUSQb718772vH3pyS5pShp6lvQM7SxgIDXXXmOX7w==", - "dev": true, - "requires": { - "@jridgewell/set-array": "^1.0.0", - "@jridgewell/sourcemap-codec": "^1.4.10" - } - }, - "@jridgewell/resolve-uri": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.0.tgz", - "integrity": "sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w==", - "dev": true - }, - "@jridgewell/set-array": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.1.2.tgz", - "integrity": "sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==", - "dev": true - }, - "@jridgewell/sourcemap-codec": { - "version": "1.4.14", - "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz", - "integrity": "sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==", - "dev": true - }, - "@jridgewell/trace-mapping": { - "version": "0.3.15", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.15.tgz", - "integrity": "sha512-oWZNOULl+UbhsgB51uuZzglikfIKSUBO/M9W2OfEjn7cmqoAiCgmv9lyACTUacZwBz0ITnJ2NqjU8Tx0DHL88g==", - "dev": true, - "requires": { - "@jridgewell/resolve-uri": "^3.0.3", - "@jridgewell/sourcemap-codec": "^1.4.10" - } - }, - "@nodelib/fs.scandir": { - "version": "2.1.5", - "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", - "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", - "dev": true, - "requires": { - "@nodelib/fs.stat": "2.0.5", - "run-parallel": "^1.1.9" - } - }, - "@nodelib/fs.stat": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", - "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", - "dev": true - }, - "@nodelib/fs.walk": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", - "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", - "dev": true, - "requires": { - "@nodelib/fs.scandir": "2.1.5", - "fastq": "^1.6.0" - } - }, - "@playwright/test": { - "version": "1.25.2", - "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.25.2.tgz", - "integrity": "sha512-6qPznIR4Fw02OMbqXUPMG6bFFg1hDVNEdihKy0t9K0dmRbus1DyP5Q5XFQhGwEHQkLG5hrSfBuu9CW/foqhQHQ==", - "dev": true, - "requires": { - "@types/node": "*", - "playwright-core": "1.25.2" - } - }, - "@sinclair/typebox": { - "version": "0.24.28", - "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.24.28.tgz", - "integrity": "sha512-dgJd3HLOkLmz4Bw50eZx/zJwtBq65nms3N9VBYu5LTjJ883oBFkTyXRlCB/ZGGwqYpJJHA5zW2Ibhl5ngITfow==", - "dev": true - }, - "@sinonjs/commons": { - "version": "1.8.3", - "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-1.8.3.tgz", - "integrity": "sha512-xkNcLAn/wZaX14RPlwizcKicDk9G3F8m2nU3L7Ukm5zBgTwiT0wsoFAHx9Jq56fJA1z/7uKGtCRu16sOUCLIHQ==", - "dev": true, - "requires": { - "type-detect": "4.0.8" - } - }, - "@sinonjs/fake-timers": { - "version": "9.1.2", - "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-9.1.2.tgz", - "integrity": "sha512-BPS4ynJW/o92PUR4wgriz2Ud5gpST5vz6GQfMixEDK0Z8ZCUv2M7SkBLykH56T++Xs+8ln9zTGbOvNGIe02/jw==", - "dev": true, - "requires": { - "@sinonjs/commons": "^1.7.0" - } - }, - "@types/babel__core": { - "version": "7.1.19", - "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.1.19.tgz", - "integrity": "sha512-WEOTgRsbYkvA/KCsDwVEGkd7WAr1e3g31VHQ8zy5gul/V1qKullU/BU5I68X5v7V3GnB9eotmom4v5a5gjxorw==", - "dev": true, - "requires": { - "@babel/parser": "^7.1.0", - "@babel/types": "^7.0.0", - "@types/babel__generator": "*", - "@types/babel__template": "*", - "@types/babel__traverse": "*" - } - }, - "@types/babel__generator": { - "version": "7.6.4", - "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.6.4.tgz", - "integrity": "sha512-tFkciB9j2K755yrTALxD44McOrk+gfpIpvC3sxHjRawj6PfnQxrse4Clq5y/Rq+G3mrBurMax/lG8Qn2t9mSsg==", - "dev": true, - "requires": { - "@babel/types": "^7.0.0" - } - }, - "@types/babel__template": { - "version": "7.4.1", - "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.1.tgz", - "integrity": "sha512-azBFKemX6kMg5Io+/rdGT0dkGreboUVR0Cdm3fz9QJWpaQGJRQXl7C+6hOTCZcMll7KFyEQpgbYI2lHdsS4U7g==", - "dev": true, - "requires": { - "@babel/parser": "^7.1.0", - "@babel/types": "^7.0.0" - } - }, - "@types/babel__traverse": { - "version": "7.18.0", - "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.18.0.tgz", - "integrity": "sha512-v4Vwdko+pgymgS+A2UIaJru93zQd85vIGWObM5ekZNdXCKtDYqATlEYnWgfo86Q6I1Lh0oXnksDnMU1cwmlPDw==", - "dev": true, - "requires": { - "@babel/types": "^7.3.0" - } - }, - "@types/graceful-fs": { - "version": "4.1.5", - "resolved": "https://registry.npmjs.org/@types/graceful-fs/-/graceful-fs-4.1.5.tgz", - "integrity": "sha512-anKkLmZZ+xm4p8JWBf4hElkM4XR+EZeA2M9BAkkTldmcyDY4mbdIJnRghDJH3Ov5ooY7/UAoENtmdMSkaAd7Cw==", - "dev": true, - "requires": { - "@types/node": "*" - } - }, - "@types/isomorphic-fetch": { - "version": "0.0.36", - "resolved": "https://registry.npmjs.org/@types/isomorphic-fetch/-/isomorphic-fetch-0.0.36.tgz", - "integrity": "sha512-ulw4d+vW1HKn4oErSmNN2HYEcHGq0N1C5exlrMM0CRqX1UUpFhGb5lwiom5j9KN3LBJJDLRmYIZz1ghm7FIzZw==", - "dev": true - }, - "@types/istanbul-lib-coverage": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.3.tgz", - "integrity": "sha512-sz7iLqvVUg1gIedBOvlkxPlc8/uVzyS5OwGz1cKjXzkl3FpL3al0crU8YGU1WoHkxn0Wxbw5tyi6hvzJKNzFsw==", - "dev": true - }, - "@types/istanbul-lib-report": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@types/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz", - "integrity": "sha512-plGgXAPfVKFoYfa9NpYDAkseG+g6Jr294RqeqcqDixSbU34MZVJRi/P+7Y8GDpzkEwLaGZZOpKIEmeVZNtKsrg==", - "dev": true, - "requires": { - "@types/istanbul-lib-coverage": "*" - } - }, - "@types/istanbul-reports": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.1.tgz", - "integrity": "sha512-c3mAZEuK0lvBp8tmuL74XRKn1+y2dcwOUpH7x4WrF6gk1GIgiluDRgMYQtw2OFcBvAJWlt6ASU3tSqxp0Uu0Aw==", - "dev": true, - "requires": { - "@types/istanbul-lib-report": "*" - } - }, - "@types/jest": { - "version": "27.0.3", - "resolved": "https://registry.npmjs.org/@types/jest/-/jest-27.0.3.tgz", - "integrity": "sha512-cmmwv9t7gBYt7hNKH5Spu7Kuu/DotGa+Ff+JGRKZ4db5eh8PnKS4LuebJ3YLUoyOyIHraTGyULn23YtEAm0VSg==", - "dev": true, - "requires": { - "jest-diff": "^27.0.0", - "pretty-format": "^27.0.0" - } - }, - "@types/json-schema": { - "version": "7.0.9", - "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.9.tgz", - "integrity": "sha512-qcUXuemtEu+E5wZSJHNxUXeCZhAfXKQ41D+duX+VYPde7xyEVZci+/oXKJL13tnRs9lR2pr4fod59GT6/X1/yQ==", - "dev": true - }, - "@types/node": { - "version": "18.14.2", - "resolved": "https://registry.npmjs.org/@types/node/-/node-18.14.2.tgz", - "integrity": "sha512-1uEQxww3DaghA0RxqHx0O0ppVlo43pJhepY51OxuQIKHpjbnYLA7vcdwioNPzIqmC2u3I/dmylcqjlh0e7AyUA==", - "dev": true - }, - "@types/prettier": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/@types/prettier/-/prettier-2.7.0.tgz", - "integrity": "sha512-RI1L7N4JnW5gQw2spvL7Sllfuf1SaHdrZpCHiBlCXjIlufi1SMNnbu2teze3/QE67Fg2tBlH7W+mi4hVNk4p0A==", - "dev": true - }, - "@types/stack-utils": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.1.tgz", - "integrity": "sha512-Hl219/BT5fLAaz6NDkSuhzasy49dwQS/DSdu4MdggFB8zcXv7vflBI3xp7FEmkmdDkBUI2bPUNeMttp2knYdxw==", - "dev": true - }, - "@types/yargs": { - "version": "17.0.11", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.11.tgz", - "integrity": "sha512-aB4y9UDUXTSMxmM4MH+YnuR0g5Cph3FLQBoWoMB21DSvFVAxRVEHEMx3TLh+zUZYMCQtKiqazz0Q4Rre31f/OA==", - "dev": true, - "requires": { - "@types/yargs-parser": "*" - } - }, - "@types/yargs-parser": { - "version": "20.2.1", - "resolved": "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-20.2.1.tgz", - "integrity": "sha512-7tFImggNeNBVMsn0vLrpn1H1uPrUBdnARPTpZoitY37ZrdJREzf7I16tMrlK3hen349gr1NYh8CmZQa7CTG6Aw==", - "dev": true - }, - "@typescript-eslint/eslint-plugin": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.5.0.tgz", - "integrity": "sha512-4bV6fulqbuaO9UMXU0Ia0o6z6if+kmMRW8rMRyfqXj/eGrZZRGedS4n0adeGNnjr8LKAM495hrQ7Tea52UWmQA==", - "dev": true, - "requires": { - "@typescript-eslint/experimental-utils": "5.5.0", - "@typescript-eslint/scope-manager": "5.5.0", - "debug": "^4.3.2", - "functional-red-black-tree": "^1.0.1", - "ignore": "^5.1.8", - "regexpp": "^3.2.0", - "semver": "^7.3.5", - "tsutils": "^3.21.0" - }, - "dependencies": { - "semver": { - "version": "7.3.5", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", - "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", - "dev": true, - "requires": { - "lru-cache": "^6.0.0" - } - }, - "tslib": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", - "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", - "dev": true - }, - "tsutils": { - "version": "3.21.0", - "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-3.21.0.tgz", - "integrity": "sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==", - "dev": true, - "requires": { - "tslib": "^1.8.1" - } - } - } - }, - "@typescript-eslint/experimental-utils": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/experimental-utils/-/experimental-utils-5.5.0.tgz", - "integrity": "sha512-kjWeeVU+4lQ1SLYErRKV5yDXbWDPkpbzTUUlfAUifPYvpX0qZlrcCZ96/6oWxt3QxtK5WVhXz+KsnwW9cIW+3A==", - "dev": true, - "requires": { - "@types/json-schema": "^7.0.9", - "@typescript-eslint/scope-manager": "5.5.0", - "@typescript-eslint/types": "5.5.0", - "@typescript-eslint/typescript-estree": "5.5.0", - "eslint-scope": "^5.1.1", - "eslint-utils": "^3.0.0" - } - }, - "@typescript-eslint/parser": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.5.0.tgz", - "integrity": "sha512-JsXBU+kgQOAgzUn2jPrLA+Rd0Y1dswOlX3hp8MuRO1hQDs6xgHtbCXEiAu7bz5hyVURxbXcA2draasMbNqrhmg==", - "dev": true, - "peer": true, - "requires": { - "@typescript-eslint/scope-manager": "5.5.0", - "@typescript-eslint/types": "5.5.0", - "@typescript-eslint/typescript-estree": "5.5.0", - "debug": "^4.3.2" - } - }, - "@typescript-eslint/scope-manager": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.5.0.tgz", - "integrity": "sha512-0/r656RmRLo7CbN4Mdd+xZyPJ/fPCKhYdU6mnZx+8msAD8nJSP8EyCFkzbd6vNVZzZvWlMYrSNekqGrCBqFQhg==", - "dev": true, - "requires": { - "@typescript-eslint/types": "5.5.0", - "@typescript-eslint/visitor-keys": "5.5.0" - } - }, - "@typescript-eslint/types": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.5.0.tgz", - "integrity": "sha512-OaYTqkW3GnuHxqsxxJ6KypIKd5Uw7bFiQJZRyNi1jbMJnK3Hc/DR4KwB6KJj6PBRkJJoaNwzMNv9vtTk87JhOg==", - "dev": true - }, - "@typescript-eslint/typescript-estree": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.5.0.tgz", - "integrity": "sha512-pVn8btYUiYrjonhMAO0yG8lm7RApzy2L4RC7Td/mC/qFkyf6vRbGyZozoA94+w6D2Y2GRqpMoCWcwx/EUOzyoQ==", - "dev": true, - "requires": { - "@typescript-eslint/types": "5.5.0", - "@typescript-eslint/visitor-keys": "5.5.0", - "debug": "^4.3.2", - "globby": "^11.0.4", - "is-glob": "^4.0.3", - "semver": "^7.3.5", - "tsutils": "^3.21.0" - }, - "dependencies": { - "semver": { - "version": "7.3.5", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", - "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", - "dev": true, - "requires": { - "lru-cache": "^6.0.0" - } - }, - "tslib": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", - "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", - "dev": true - }, - "tsutils": { - "version": "3.21.0", - "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-3.21.0.tgz", - "integrity": "sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==", - "dev": true, - "requires": { - "tslib": "^1.8.1" - } - } - } - }, - "@typescript-eslint/visitor-keys": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.5.0.tgz", - "integrity": "sha512-4GzJ1kRtsWzHhdM40tv0ZKHNSbkDhF0Woi/TDwVJX6UICwJItvP7ZTXbjTkCdrors7ww0sYe0t+cIKDAJwZ7Kw==", - "dev": true, - "requires": { - "@typescript-eslint/types": "5.5.0", - "eslint-visitor-keys": "^3.0.0" - } - }, - "acorn": { - "version": "8.6.0", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.6.0.tgz", - "integrity": "sha512-U1riIR+lBSNi3IbxtaHOIKdH8sLFv3NYfNv8sg7ZsNhcfl4HF2++BfqqrNAxoCLQW1iiylOj76ecnaUxz+z9yw==", - "dev": true - }, - "acorn-jsx": { - "version": "5.3.2", - "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", - "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", - "dev": true, - "requires": {} - }, - "ajv": { - "version": "6.12.6", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", - "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", - "dev": true, - "requires": { - "fast-deep-equal": "^3.1.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", - "uri-js": "^4.2.2" - } - }, - "ansi-colors": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz", - "integrity": "sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==", - "dev": true - }, - "ansi-escapes": { - "version": "4.3.2", - "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", - "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==", - "dev": true, - "requires": { - "type-fest": "^0.21.3" - } - }, - "ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "dev": true - }, - "ansi-sequence-parser": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/ansi-sequence-parser/-/ansi-sequence-parser-1.1.0.tgz", - "integrity": "sha512-lEm8mt52to2fT8GhciPCGeCXACSz2UwIN4X2e2LJSnZ5uAbn2/dsYdOmUXq0AtWS5cpAupysIneExOgH0Vd2TQ==", - "dev": true - }, - "ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dev": true, - "requires": { - "color-convert": "^1.9.0" - } - }, - "anymatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.2.tgz", - "integrity": "sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg==", - "dev": true, - "requires": { - "normalize-path": "^3.0.0", - "picomatch": "^2.0.4" - } - }, - "argparse": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", - "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", - "dev": true - }, - "array-union": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", - "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", - "dev": true - }, - "babel-jest": { - "version": "28.1.3", - "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-28.1.3.tgz", - "integrity": "sha512-epUaPOEWMk3cWX0M/sPvCHHCe9fMFAa/9hXEgKP8nFfNl/jlGkE9ucq9NqkZGXLDduCJYS0UvSlPUwC0S+rH6Q==", - "dev": true, - "requires": { - "@jest/transform": "^28.1.3", - "@types/babel__core": "^7.1.14", - "babel-plugin-istanbul": "^6.1.1", - "babel-preset-jest": "^28.1.3", - "chalk": "^4.0.0", - "graceful-fs": "^4.2.9", - "slash": "^3.0.0" - }, - "dependencies": { - "ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "requires": { - "color-convert": "^2.0.1" - } - }, - "chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - }, - "color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "requires": { - "color-name": "~1.1.4" - } - }, - "color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true - }, - "supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "requires": { - "has-flag": "^4.0.0" - } - } - } - }, - "babel-plugin-istanbul": { - "version": "6.1.1", - "resolved": "https://registry.npmjs.org/babel-plugin-istanbul/-/babel-plugin-istanbul-6.1.1.tgz", - "integrity": "sha512-Y1IQok9821cC9onCx5otgFfRm7Lm+I+wwxOx738M/WLPZ9Q42m4IG5W0FNX8WLL2gYMZo3JkuXIH2DOpWM+qwA==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.0.0", - "@istanbuljs/load-nyc-config": "^1.0.0", - "@istanbuljs/schema": "^0.1.2", - "istanbul-lib-instrument": "^5.0.4", - "test-exclude": "^6.0.0" - } - }, - "babel-plugin-jest-hoist": { - "version": "28.1.3", - "resolved": "https://registry.npmjs.org/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-28.1.3.tgz", - "integrity": "sha512-Ys3tUKAmfnkRUpPdpa98eYrAR0nV+sSFUZZEGuQ2EbFd1y4SOLtD5QDNHAq+bb9a+bbXvYQC4b+ID/THIMcU6Q==", - "dev": true, - "requires": { - "@babel/template": "^7.3.3", - "@babel/types": "^7.3.3", - "@types/babel__core": "^7.1.14", - "@types/babel__traverse": "^7.0.6" - } - }, - "babel-preset-current-node-syntax": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/babel-preset-current-node-syntax/-/babel-preset-current-node-syntax-1.0.1.tgz", - "integrity": "sha512-M7LQ0bxarkxQoN+vz5aJPsLBn77n8QgTFmo8WK0/44auK2xlCXrYcUxHFxgU7qW5Yzw/CjmLRK2uJzaCd7LvqQ==", - "dev": true, - "requires": { - "@babel/plugin-syntax-async-generators": "^7.8.4", - "@babel/plugin-syntax-bigint": "^7.8.3", - "@babel/plugin-syntax-class-properties": "^7.8.3", - "@babel/plugin-syntax-import-meta": "^7.8.3", - "@babel/plugin-syntax-json-strings": "^7.8.3", - "@babel/plugin-syntax-logical-assignment-operators": "^7.8.3", - "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3", - "@babel/plugin-syntax-numeric-separator": "^7.8.3", - "@babel/plugin-syntax-object-rest-spread": "^7.8.3", - "@babel/plugin-syntax-optional-catch-binding": "^7.8.3", - "@babel/plugin-syntax-optional-chaining": "^7.8.3", - "@babel/plugin-syntax-top-level-await": "^7.8.3" - } - }, - "babel-preset-jest": { - "version": "28.1.3", - "resolved": "https://registry.npmjs.org/babel-preset-jest/-/babel-preset-jest-28.1.3.tgz", - "integrity": "sha512-L+fupJvlWAHbQfn74coNX3zf60LXMJsezNvvx8eIh7iOR1luJ1poxYgQk1F8PYtNq/6QODDHCqsSnTFSWC491A==", - "dev": true, - "requires": { - "babel-plugin-jest-hoist": "^28.1.3", - "babel-preset-current-node-syntax": "^1.0.0" - } - }, - "balanced-match": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", - "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", - "dev": true - }, - "blakejs": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/blakejs/-/blakejs-1.2.1.tgz", - "integrity": "sha512-QXUSXI3QVc/gJME0dBpXrag1kbzOqCjCX8/b54ntNyW6sjtoqxqRk3LTmXzaJoh71zMsDCjM+47jS7XiwN/+fQ==" - }, - "brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "dev": true, - "requires": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "braces": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", - "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", - "dev": true, - "requires": { - "fill-range": "^7.0.1" - } - }, - "browserslist": { - "version": "4.21.3", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.21.3.tgz", - "integrity": "sha512-898rgRXLAyRkM1GryrrBHGkqA5hlpkV5MhtZwg9QXeiyLUYs2k00Un05aX5l2/yJIOObYKOpS2JNo8nJDE7fWQ==", - "dev": true, - "requires": { - "caniuse-lite": "^1.0.30001370", - "electron-to-chromium": "^1.4.202", - "node-releases": "^2.0.6", - "update-browserslist-db": "^1.0.5" - } - }, - "bs-logger": { - "version": "0.2.6", - "resolved": "https://registry.npmjs.org/bs-logger/-/bs-logger-0.2.6.tgz", - "integrity": "sha512-pd8DCoxmbgc7hyPKOvxtqNcjYoOsABPQdcCUjGp3d42VR2CX1ORhk2A87oqqu5R1kk+76nsxZupkmyd+MVtCog==", - "dev": true, - "requires": { - "fast-json-stable-stringify": "2.x" - } - }, - "bser": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/bser/-/bser-2.1.1.tgz", - "integrity": "sha512-gQxTNE/GAfIIrmHLUE3oJyp5FO6HRBfhjnw4/wMmA63ZGDJnWBmgY/lyQBpnDUkGmAhbSe39tx2d/iTOAfglwQ==", - "dev": true, - "requires": { - "node-int64": "^0.4.0" - } - }, - "buffer-from": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", - "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", - "dev": true - }, - "callsites": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", - "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", - "dev": true - }, - "camelcase": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", - "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", - "dev": true - }, - "caniuse-lite": { - "version": "1.0.30001384", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001384.tgz", - "integrity": "sha512-BBWt57kqWbc0GYZXb47wTXpmAgqr5LSibPzNjk/AWMdmJMQhLqOl3c/Kd4OAU/tu4NLfYkMx8Tlq3RVBkOBolQ==", - "dev": true - }, - "chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "dev": true, - "requires": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - } - }, - "char-regex": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/char-regex/-/char-regex-1.0.2.tgz", - "integrity": "sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw==", - "dev": true - }, - "ci-info": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.3.0.tgz", - "integrity": "sha512-riT/3vI5YpVH6/qomlDnJow6TBee2PBKSEpx3O32EGPYbWGIRsIlGRms3Sm74wYE1JMo8RnO04Hb12+v1J5ICw==", - "dev": true - }, - "cjs-module-lexer": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-1.2.2.tgz", - "integrity": "sha512-cOU9usZw8/dXIXKtwa8pM0OTJQuJkxMN6w30csNRUerHfeQ5R6U3kkU/FtJeIf3M202OHfY2U8ccInBG7/xogA==", - "dev": true - }, - "cliui": { - "version": "7.0.4", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", - "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", - "dev": true, - "requires": { - "string-width": "^4.2.0", - "strip-ansi": "^6.0.0", - "wrap-ansi": "^7.0.0" - } - }, - "co": { - "version": "4.6.0", - "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", - "integrity": "sha512-QVb0dM5HvG+uaxitm8wONl7jltx8dqhfU33DcqtOZcLSVIKSDDLDi7+0LbAKiyI8hD9u42m2YxXSkMGWThaecQ==", - "dev": true - }, - "collect-v8-coverage": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/collect-v8-coverage/-/collect-v8-coverage-1.0.1.tgz", - "integrity": "sha512-iBPtljfCNcTKNAto0KEtDfZ3qzjJvqE3aTGZsbhjSBlorqpXJlaWWtPO35D+ZImoC3KWejX64o+yPGxhWSTzfg==", - "dev": true - }, - "color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "dev": true, - "requires": { - "color-name": "1.1.3" - } - }, - "color-name": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", - "dev": true - }, - "concat-map": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", - "dev": true - }, - "convert-source-map": { - "version": "1.8.0", - "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.8.0.tgz", - "integrity": "sha512-+OQdjP49zViI/6i7nIJpA8rAl4sV/JdPfU9nZs3VqOwGIgizICvuN2ru6fMd+4llL0tar18UYJXfZ/TWtmhUjA==", - "dev": true, - "requires": { - "safe-buffer": "~5.1.1" - } - }, - "cross-spawn": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", - "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", - "dev": true, - "requires": { - "path-key": "^3.1.0", - "shebang-command": "^2.0.0", - "which": "^2.0.1" - } - }, - "debug": { - "version": "4.3.3", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.3.tgz", - "integrity": "sha512-/zxw5+vh1Tfv+4Qn7a5nsbcJKPaSvCDhojn6FEl9vupwK2VCSDtEiEtqr8DFtzYFOdz63LBkxec7DYuc2jon6Q==", - "dev": true, - "requires": { - "ms": "2.1.2" - } - }, - "dedent": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/dedent/-/dedent-0.7.0.tgz", - "integrity": "sha512-Q6fKUPqnAHAyhiUgFU7BUzLiv0kd8saH9al7tnu5Q/okj6dnupxyTgFIBjVzJATdfIAm9NAsvXNzjaKa+bxVyA==", - "dev": true - }, - "deep-is": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", - "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", - "dev": true - }, - "deepmerge": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.2.2.tgz", - "integrity": "sha512-FJ3UgI4gIl+PHZm53knsuSFpE+nESMr7M4v9QcgB7S63Kj/6WqMiFQJpBBYz1Pt+66bZpP3Q7Lye0Oo9MPKEdg==", - "dev": true - }, - "detect-gpu": { - "version": "5.0.5", - "resolved": "https://registry.npmjs.org/detect-gpu/-/detect-gpu-5.0.5.tgz", - "integrity": "sha512-gKBKx8mj0Fi/8DnjuzxU+aNYF1yWvPxtiOV9o72539wW0HP3ZTJVol/0FUasvdxIY1Bhi19SwIINqXGO+RB/sA==", - "requires": { - "webgl-constants": "^1.1.1" - } - }, - "detect-newline": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/detect-newline/-/detect-newline-3.1.0.tgz", - "integrity": "sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA==", - "dev": true - }, - "diff-sequences": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-27.5.1.tgz", - "integrity": "sha512-k1gCAXAsNgLwEL+Y8Wvl+M6oEFj5bgazfZULpS5CneoPPXRaCCW7dm+q21Ky2VEE5X+VeRDBVg1Pcvvsr4TtNQ==", - "dev": true - }, - "dir-glob": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", - "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", - "dev": true, - "requires": { - "path-type": "^4.0.0" - } - }, - "doctrine": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", - "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", - "dev": true, - "requires": { - "esutils": "^2.0.2" - } - }, - "electron-to-chromium": { - "version": "1.4.233", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.233.tgz", - "integrity": "sha512-ejwIKXTg1wqbmkcRJh9Ur3hFGHFDZDw1POzdsVrB2WZjgRuRMHIQQKNpe64N/qh3ZtH2otEoRoS+s6arAAuAAw==", - "dev": true - }, - "emittery": { - "version": "0.10.2", - "resolved": "https://registry.npmjs.org/emittery/-/emittery-0.10.2.tgz", - "integrity": "sha512-aITqOwnLanpHLNXZJENbOgjUBeHocD+xsSJmNrjovKBW5HbSpW3d1pEls7GFQPUWXiwG9+0P4GtHfEqC/4M0Iw==", - "dev": true - }, - "emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "dev": true - }, - "enquirer": { - "version": "2.3.6", - "resolved": "https://registry.npmjs.org/enquirer/-/enquirer-2.3.6.tgz", - "integrity": "sha512-yjNnPr315/FjS4zIsUxYguYUPP2e1NK4d7E7ZOLiyYCcbFBiTMyID+2wvm2w6+pZ/odMA7cRkjhsPbltwBOrLg==", - "dev": true, - "requires": { - "ansi-colors": "^4.1.1" - } - }, - "error-ex": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", - "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", - "dev": true, - "requires": { - "is-arrayish": "^0.2.1" - } - }, - "esbuild": { - "version": "0.19.2", - "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.19.2.tgz", - "integrity": "sha512-G6hPax8UbFakEj3hWO0Vs52LQ8k3lnBhxZWomUJDxfz3rZTLqF5k/FCzuNdLx2RbpBiQQF9H9onlDDH1lZsnjg==", - "dev": true, - "requires": { - "@esbuild/android-arm": "0.19.2", - "@esbuild/android-arm64": "0.19.2", - "@esbuild/android-x64": "0.19.2", - "@esbuild/darwin-arm64": "0.19.2", - "@esbuild/darwin-x64": "0.19.2", - "@esbuild/freebsd-arm64": "0.19.2", - "@esbuild/freebsd-x64": "0.19.2", - "@esbuild/linux-arm": "0.19.2", - "@esbuild/linux-arm64": "0.19.2", - "@esbuild/linux-ia32": "0.19.2", - "@esbuild/linux-loong64": "0.19.2", - "@esbuild/linux-mips64el": "0.19.2", - "@esbuild/linux-ppc64": "0.19.2", - "@esbuild/linux-riscv64": "0.19.2", - "@esbuild/linux-s390x": "0.19.2", - "@esbuild/linux-x64": "0.19.2", - "@esbuild/netbsd-x64": "0.19.2", - "@esbuild/openbsd-x64": "0.19.2", - "@esbuild/sunos-x64": "0.19.2", - "@esbuild/win32-arm64": "0.19.2", - "@esbuild/win32-ia32": "0.19.2", - "@esbuild/win32-x64": "0.19.2" - } - }, - "escalade": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", - "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", - "dev": true - }, - "escape-string-regexp": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", - "dev": true - }, - "eslint": { - "version": "8.3.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.3.0.tgz", - "integrity": "sha512-aIay56Ph6RxOTC7xyr59Kt3ewX185SaGnAr8eWukoPLeriCrvGjvAubxuvaXOfsxhtwV5g0uBOsyhAom4qJdww==", - "dev": true, - "requires": { - "@eslint/eslintrc": "^1.0.4", - "@humanwhocodes/config-array": "^0.6.0", - "ajv": "^6.10.0", - "chalk": "^4.0.0", - "cross-spawn": "^7.0.2", - "debug": "^4.3.2", - "doctrine": "^3.0.0", - "enquirer": "^2.3.5", - "escape-string-regexp": "^4.0.0", - "eslint-scope": "^7.1.0", - "eslint-utils": "^3.0.0", - "eslint-visitor-keys": "^3.1.0", - "espree": "^9.1.0", - "esquery": "^1.4.0", - "esutils": "^2.0.2", - "fast-deep-equal": "^3.1.3", - "file-entry-cache": "^6.0.1", - "functional-red-black-tree": "^1.0.1", - "glob-parent": "^6.0.1", - "globals": "^13.6.0", - "ignore": "^4.0.6", - "import-fresh": "^3.0.0", - "imurmurhash": "^0.1.4", - "is-glob": "^4.0.0", - "js-yaml": "^4.1.0", - "json-stable-stringify-without-jsonify": "^1.0.1", - "levn": "^0.4.1", - "lodash.merge": "^4.6.2", - "minimatch": "^3.0.4", - "natural-compare": "^1.4.0", - "optionator": "^0.9.1", - "progress": "^2.0.0", - "regexpp": "^3.2.0", - "semver": "^7.2.1", - "strip-ansi": "^6.0.1", - "strip-json-comments": "^3.1.0", - "text-table": "^0.2.0", - "v8-compile-cache": "^2.0.3" - }, - "dependencies": { - "ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "requires": { - "color-convert": "^2.0.1" - } - }, - "chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - }, - "color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "requires": { - "color-name": "~1.1.4" - } - }, - "color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "escape-string-regexp": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", - "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", - "dev": true - }, - "eslint-scope": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.1.0.tgz", - "integrity": "sha512-aWwkhnS0qAXqNOgKOK0dJ2nvzEbhEvpy8OlJ9kZ0FeZnA6zpjv1/Vei+puGFFX7zkPCkHHXb7IDX3A+7yPrRWg==", - "dev": true, - "requires": { - "esrecurse": "^4.3.0", - "estraverse": "^5.2.0" - } - }, - "estraverse": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", - "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", - "dev": true - }, - "globals": { - "version": "13.12.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-13.12.0.tgz", - "integrity": "sha512-uS8X6lSKN2JumVoXrbUz+uG4BYG+eiawqm3qFcT7ammfbUHeCBoJMlHcec/S3krSk73/AE/f0szYFmgAA3kYZg==", - "dev": true, - "requires": { - "type-fest": "^0.20.2" - } - }, - "has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true - }, - "ignore": { - "version": "4.0.6", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-4.0.6.tgz", - "integrity": "sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==", - "dev": true - }, - "semver": { - "version": "7.3.5", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", - "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", - "dev": true, - "requires": { - "lru-cache": "^6.0.0" - } - }, - "supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "requires": { - "has-flag": "^4.0.0" - } - }, - "type-fest": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", - "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", - "dev": true - } - } - }, - "eslint-scope": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", - "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", - "dev": true, - "requires": { - "esrecurse": "^4.3.0", - "estraverse": "^4.1.1" - } - }, - "eslint-utils": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-3.0.0.tgz", - "integrity": "sha512-uuQC43IGctw68pJA1RgbQS8/NP7rch6Cwd4j3ZBtgo4/8Flj4eGE7ZYSZRN3iq5pVUv6GPdW5Z1RFleo84uLDA==", - "dev": true, - "requires": { - "eslint-visitor-keys": "^2.0.0" - }, - "dependencies": { - "eslint-visitor-keys": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz", - "integrity": "sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw==", - "dev": true - } - } - }, - "eslint-visitor-keys": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.1.0.tgz", - "integrity": "sha512-yWJFpu4DtjsWKkt5GeNBBuZMlNcYVs6vRCLoCVEJrTjaSB6LC98gFipNK/erM2Heg/E8mIK+hXG/pJMLK+eRZA==", - "dev": true - }, - "espree": { - "version": "9.1.0", - "resolved": "https://registry.npmjs.org/espree/-/espree-9.1.0.tgz", - "integrity": "sha512-ZgYLvCS1wxOczBYGcQT9DDWgicXwJ4dbocr9uYN+/eresBAUuBu+O4WzB21ufQ/JqQT8gyp7hJ3z8SHii32mTQ==", - "dev": true, - "requires": { - "acorn": "^8.6.0", - "acorn-jsx": "^5.3.1", - "eslint-visitor-keys": "^3.1.0" - } - }, - "esprima": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", - "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", - "dev": true - }, - "esquery": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.4.0.tgz", - "integrity": "sha512-cCDispWt5vHHtwMY2YrAQ4ibFkAL8RbH5YGBnZBc90MolvvfkkQcJro/aZiAQUlQ3qgrYS6D6v8Gc5G5CQsc9w==", - "dev": true, - "requires": { - "estraverse": "^5.1.0" - }, - "dependencies": { - "estraverse": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", - "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", - "dev": true - } - } - }, - "esrecurse": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", - "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", - "dev": true, - "requires": { - "estraverse": "^5.2.0" - }, - "dependencies": { - "estraverse": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", - "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", - "dev": true - } - } - }, - "estraverse": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", - "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", - "dev": true - }, - "esutils": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", - "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", - "dev": true - }, - "execa": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", - "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==", - "dev": true, - "requires": { - "cross-spawn": "^7.0.3", - "get-stream": "^6.0.0", - "human-signals": "^2.1.0", - "is-stream": "^2.0.0", - "merge-stream": "^2.0.0", - "npm-run-path": "^4.0.1", - "onetime": "^5.1.2", - "signal-exit": "^3.0.3", - "strip-final-newline": "^2.0.0" - } - }, - "exit": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz", - "integrity": "sha512-Zk/eNKV2zbjpKzrsQ+n1G6poVbErQxJ0LBOJXaKZ1EViLzH+hrLu9cdXI4zw9dBQJslwBEpbQ2P1oS7nDxs6jQ==", - "dev": true - }, - "expect": { - "version": "29.0.1", - "resolved": "https://registry.npmjs.org/expect/-/expect-29.0.1.tgz", - "integrity": "sha512-yQgemsjLU+1S8t2A7pXT3Sn/v5/37LY8J+tocWtKEA0iEYYc6gfKbbJJX2fxHZmd7K9WpdbQqXUpmYkq1aewYg==", - "dev": true, - "requires": { - "@jest/expect-utils": "^29.0.1", - "jest-get-type": "^29.0.0", - "jest-matcher-utils": "^29.0.1", - "jest-message-util": "^29.0.1", - "jest-util": "^29.0.1" - }, - "dependencies": { - "@jest/types": { - "version": "29.0.1", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.0.1.tgz", - "integrity": "sha512-ft01rxzVsbh9qZPJ6EFgAIj3PT9FCRfBF9Xljo2/33VDOUjLZr0ZJ2oKANqh9S/K0/GERCsHDAQlBwj7RxA+9g==", - "dev": true, - "requires": { - "@jest/schemas": "^29.0.0", - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^17.0.8", - "chalk": "^4.0.0" - } - }, - "ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "requires": { - "color-convert": "^2.0.1" - } - }, - "chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - }, - "color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "requires": { - "color-name": "~1.1.4" - } - }, - "color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "diff-sequences": { - "version": "29.0.0", - "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-29.0.0.tgz", - "integrity": "sha512-7Qe/zd1wxSDL4D/X/FPjOMB+ZMDt71W94KYaq05I2l0oQqgXgs7s4ftYYmV38gBSrPz2vcygxfs1xn0FT+rKNA==", - "dev": true - }, - "has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true - }, - "jest-diff": { - "version": "29.0.1", - "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-29.0.1.tgz", - "integrity": "sha512-l8PYeq2VhcdxG9tl5cU78ClAlg/N7RtVSp0v3MlXURR0Y99i6eFnegmasOandyTmO6uEdo20+FByAjBFEO9nuw==", - "dev": true, - "requires": { - "chalk": "^4.0.0", - "diff-sequences": "^29.0.0", - "jest-get-type": "^29.0.0", - "pretty-format": "^29.0.1" - } - }, - "jest-get-type": { - "version": "29.0.0", - "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-29.0.0.tgz", - "integrity": "sha512-83X19z/HuLKYXYHskZlBAShO7UfLFXu/vWajw9ZNJASN32li8yHMaVGAQqxFW1RCFOkB7cubaL6FaJVQqqJLSw==", - "dev": true - }, - "jest-matcher-utils": { - "version": "29.0.1", - "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-29.0.1.tgz", - "integrity": "sha512-/e6UbCDmprRQFnl7+uBKqn4G22c/OmwriE5KCMVqxhElKCQUDcFnq5XM9iJeKtzy4DUjxT27y9VHmKPD8BQPaw==", - "dev": true, - "requires": { - "chalk": "^4.0.0", - "jest-diff": "^29.0.1", - "jest-get-type": "^29.0.0", - "pretty-format": "^29.0.1" - } - }, - "jest-message-util": { - "version": "29.0.1", - "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-29.0.1.tgz", - "integrity": "sha512-wRMAQt3HrLpxSubdnzOo68QoTfQ+NLXFzU0Heb18ZUzO2S9GgaXNEdQ4rpd0fI9dq2NXkpCk1IUWSqzYKji64A==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.12.13", - "@jest/types": "^29.0.1", - "@types/stack-utils": "^2.0.0", - "chalk": "^4.0.0", - "graceful-fs": "^4.2.9", - "micromatch": "^4.0.4", - "pretty-format": "^29.0.1", - "slash": "^3.0.0", - "stack-utils": "^2.0.3" - } - }, - "jest-util": { - "version": "29.0.1", - "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-29.0.1.tgz", - "integrity": "sha512-GIWkgNfkeA9d84rORDHPGGTFBrRD13A38QVSKE0bVrGSnoR1KDn8Kqz+0yI5kezMgbT/7zrWaruWP1Kbghlb2A==", - "dev": true, - "requires": { - "@jest/types": "^29.0.1", - "@types/node": "*", - "chalk": "^4.0.0", - "ci-info": "^3.2.0", - "graceful-fs": "^4.2.9", - "picomatch": "^2.2.3" - } - }, - "pretty-format": { - "version": "29.0.1", - "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.0.1.tgz", - "integrity": "sha512-iTHy3QZMzuL484mSTYbQIM1AHhEQsH8mXWS2/vd2yFBYnG3EBqGiMONo28PlPgrW7P/8s/1ISv+y7WH306l8cw==", - "dev": true, - "requires": { - "@jest/schemas": "^29.0.0", - "ansi-styles": "^5.0.0", - "react-is": "^18.0.0" - }, - "dependencies": { - "ansi-styles": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", - "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", - "dev": true - } - } - }, - "react-is": { - "version": "18.2.0", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz", - "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==", - "dev": true - }, - "supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "requires": { - "has-flag": "^4.0.0" - } - } - } - }, - "fast-deep-equal": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", - "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", - "dev": true - }, - "fast-glob": { - "version": "3.2.7", - "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.7.tgz", - "integrity": "sha512-rYGMRwip6lUMvYD3BTScMwT1HtAs2d71SMv66Vrxs0IekGZEjhM0pcMfjQPnknBt2zeCwQMEupiN02ZP4DiT1Q==", - "dev": true, - "requires": { - "@nodelib/fs.stat": "^2.0.2", - "@nodelib/fs.walk": "^1.2.3", - "glob-parent": "^5.1.2", - "merge2": "^1.3.0", - "micromatch": "^4.0.4" - }, - "dependencies": { - "glob-parent": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", - "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", - "dev": true, - "requires": { - "is-glob": "^4.0.1" - } - } - } - }, - "fast-json-stable-stringify": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", - "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", - "dev": true - }, - "fast-levenshtein": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", - "integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=", - "dev": true - }, - "fastq": { - "version": "1.13.0", - "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.13.0.tgz", - "integrity": "sha512-YpkpUnK8od0o1hmeSc7UUs/eB/vIPWJYjKck2QKIzAf71Vm1AAQ3EbuZB3g2JIy+pg+ERD0vqI79KyZiB2e2Nw==", - "dev": true, - "requires": { - "reusify": "^1.0.4" - } - }, - "fb-watchman": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/fb-watchman/-/fb-watchman-2.0.1.tgz", - "integrity": "sha512-DkPJKQeY6kKwmuMretBhr7G6Vodr7bFwDYTXIkfG1gjvNpaxBTQV3PbXg6bR1c1UP4jPOX0jHUbbHANL9vRjVg==", - "dev": true, - "requires": { - "bser": "2.1.1" - } - }, - "file-entry-cache": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", - "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==", - "dev": true, - "requires": { - "flat-cache": "^3.0.4" - } - }, - "fill-range": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", - "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", - "dev": true, - "requires": { - "to-regex-range": "^5.0.1" - } - }, - "find-up": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", - "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", - "dev": true, - "requires": { - "locate-path": "^5.0.0", - "path-exists": "^4.0.0" - } - }, - "flat-cache": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.0.4.tgz", - "integrity": "sha512-dm9s5Pw7Jc0GvMYbshN6zchCA9RgQlzzEZX3vylR9IqFfS8XciblUXOKfW6SiuJ0e13eDYZoZV5wdrev7P3Nwg==", - "dev": true, - "requires": { - "flatted": "^3.1.0", - "rimraf": "^3.0.2" - } - }, - "flatted": { - "version": "3.2.4", - "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.4.tgz", - "integrity": "sha512-8/sOawo8tJ4QOBX8YlQBMxL8+RLZfxMQOif9o0KUKTNTjMYElWPE0r/m5VNFxTRd0NSw8qSy8dajrwX4RYI1Hw==", - "dev": true - }, - "fs-extra": { - "version": "10.0.0", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.0.0.tgz", - "integrity": "sha512-C5owb14u9eJwizKGdchcDUQeFtlSHHthBk8pbX9Vc1PFZrLombudjDnNns88aYslCyF6IY5SUw3Roz6xShcEIQ==", - "dev": true, - "requires": { - "graceful-fs": "^4.2.0", - "jsonfile": "^6.0.1", - "universalify": "^2.0.0" - } - }, - "fs.realpath": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", - "dev": true - }, - "fsevents": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", - "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", - "dev": true, - "optional": true - }, - "function-bind": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", - "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", - "dev": true - }, - "functional-red-black-tree": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz", - "integrity": "sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc=", - "dev": true - }, - "gensync": { - "version": "1.0.0-beta.2", - "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", - "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", - "dev": true - }, - "get-caller-file": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", - "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", - "dev": true - }, - "get-package-type": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/get-package-type/-/get-package-type-0.1.0.tgz", - "integrity": "sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==", - "dev": true - }, - "get-stream": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", - "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", - "dev": true - }, - "glob": { - "version": "8.0.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-8.0.3.tgz", - "integrity": "sha512-ull455NHSHI/Y1FqGaaYFaLGkNMMJbavMrEGFXG/PGrg6y7sutWHUHrz6gy6WEBH6akM1M414dWKCNs+IhKdiQ==", - "dev": true, - "requires": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^5.0.1", - "once": "^1.3.0" - }, - "dependencies": { - "brace-expansion": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", - "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", - "dev": true, - "requires": { - "balanced-match": "^1.0.0" - } - }, - "minimatch": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.1.tgz", - "integrity": "sha512-362NP+zlprccbEt/SkxKfRMHnNY85V74mVnpUpNyr3F35covl09Kec7/sEFLt3RA4oXmewtoaanoIf67SE5Y5g==", - "dev": true, - "requires": { - "brace-expansion": "^2.0.1" - } - } - } - }, - "glob-parent": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", - "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", - "dev": true, - "requires": { - "is-glob": "^4.0.3" - } - }, - "globals": { - "version": "11.12.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", - "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", - "dev": true - }, - "globby": { - "version": "11.0.4", - "resolved": "https://registry.npmjs.org/globby/-/globby-11.0.4.tgz", - "integrity": "sha512-9O4MVG9ioZJ08ffbcyVYyLOJLk5JQ688pJ4eMGLpdWLHq/Wr1D9BlriLQyL0E+jbkuePVZXYFj47QM/v093wHg==", - "dev": true, - "requires": { - "array-union": "^2.1.0", - "dir-glob": "^3.0.1", - "fast-glob": "^3.1.1", - "ignore": "^5.1.4", - "merge2": "^1.3.0", - "slash": "^3.0.0" - } - }, - "graceful-fs": { - "version": "4.2.10", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.10.tgz", - "integrity": "sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA==", - "dev": true - }, - "handlebars": { - "version": "4.7.7", - "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.7.7.tgz", - "integrity": "sha512-aAcXm5OAfE/8IXkcZvCepKU3VzW1/39Fb5ZuqMtgI/hT8X2YgoMvBY5dLhq/cpOvw7Lk1nK/UF71aLG/ZnVYRA==", - "dev": true, - "requires": { - "minimist": "^1.2.5", - "neo-async": "^2.6.0", - "source-map": "^0.6.1", - "uglify-js": "^3.1.4", - "wordwrap": "^1.0.0" - } - }, - "has": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", - "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", - "dev": true, - "requires": { - "function-bind": "^1.1.1" - } - }, - "has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", - "dev": true - }, - "howslow": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/howslow/-/howslow-0.1.0.tgz", - "integrity": "sha512-AD1ERdUseZEi/XyLBa/9LNv4l4GvCCkNT76KpYp0YEv1up8Ow/ZzLy71OtlSdv6b39wxvzJKq9VZLH/83PrQkQ==", - "dev": true - }, - "html-escaper": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", - "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==", - "dev": true - }, - "human-signals": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz", - "integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==", - "dev": true - }, - "ignore": { - "version": "5.1.9", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.1.9.tgz", - "integrity": "sha512-2zeMQpbKz5dhZ9IwL0gbxSW5w0NK/MSAMtNuhgIHEPmaU3vPdKPL0UdvUCXs5SS4JAwsBxysK5sFMW8ocFiVjQ==", - "dev": true - }, - "import-fresh": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", - "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", - "dev": true, - "requires": { - "parent-module": "^1.0.0", - "resolve-from": "^4.0.0" - } - }, - "import-local": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/import-local/-/import-local-3.1.0.tgz", - "integrity": "sha512-ASB07uLtnDs1o6EHjKpX34BKYDSqnFerfTOJL2HvMqF70LnxpjkzDB8J44oT9pu4AMPkQwf8jl6szgvNd2tRIg==", - "dev": true, - "requires": { - "pkg-dir": "^4.2.0", - "resolve-cwd": "^3.0.0" - } - }, - "imurmurhash": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", - "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=", - "dev": true - }, - "inflight": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", - "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", - "dev": true, - "requires": { - "once": "^1.3.0", - "wrappy": "1" - } - }, - "inherits": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", - "dev": true - }, - "is-arrayish": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", - "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==", - "dev": true - }, - "is-core-module": { - "version": "2.10.0", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.10.0.tgz", - "integrity": "sha512-Erxj2n/LDAZ7H8WNJXd9tw38GYM3dv8rk8Zcs+jJuxYTW7sozH+SS8NtrSjVL1/vpLvWi1hxy96IzjJ3EHTJJg==", - "dev": true, - "requires": { - "has": "^1.0.3" - } - }, - "is-extglob": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", - "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=", - "dev": true - }, - "is-fullwidth-code-point": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", - "dev": true - }, - "is-generator-fn": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-generator-fn/-/is-generator-fn-2.1.0.tgz", - "integrity": "sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ==", - "dev": true - }, - "is-glob": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", - "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", - "dev": true, - "requires": { - "is-extglob": "^2.1.1" - } - }, - "is-number": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", - "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", - "dev": true - }, - "is-stream": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", - "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", - "dev": true - }, - "isexe": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", - "dev": true - }, - "isomorphic-fetch": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/isomorphic-fetch/-/isomorphic-fetch-3.0.0.tgz", - "integrity": "sha512-qvUtwJ3j6qwsF3jLxkZ72qCgjMysPzDfeV240JHiGZsANBYd+EEuu35v7dfrJ9Up0Ak07D7GGSkGhCHTqg/5wA==", - "requires": { - "node-fetch": "^2.6.1", - "whatwg-fetch": "^3.4.1" - }, - "dependencies": { - "node-fetch": { - "version": "2.6.7", - "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.7.tgz", - "integrity": "sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ==", - "requires": { - "whatwg-url": "^5.0.0" - } - }, - "tr46": { - "version": "0.0.3", - "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", - "integrity": "sha1-gYT9NH2snNwYWZLzpmIuFLnZq2o=" - }, - "webidl-conversions": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", - "integrity": "sha1-JFNCdeKnvGvnvIZhHMFq4KVlSHE=" - }, - "whatwg-url": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", - "integrity": "sha1-lmRU6HZUYuN2RNNib2dCzotwll0=", - "requires": { - "tr46": "~0.0.3", - "webidl-conversions": "^3.0.0" - } - } - } - }, - "istanbul-lib-coverage": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.0.tgz", - "integrity": "sha512-eOeJ5BHCmHYvQK7xt9GkdHuzuCGS1Y6g9Gvnx3Ym33fz/HpLRYxiS0wHNr+m/MBC8B647Xt608vCDEvhl9c6Mw==", - "dev": true - }, - "istanbul-lib-instrument": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-5.2.0.tgz", - "integrity": "sha512-6Lthe1hqXHBNsqvgDzGO6l03XNeu3CrG4RqQ1KM9+l5+jNGpEJfIELx1NS3SEHmJQA8np/u+E4EPRKRiu6m19A==", - "dev": true, - "requires": { - "@babel/core": "^7.12.3", - "@babel/parser": "^7.14.7", - "@istanbuljs/schema": "^0.1.2", - "istanbul-lib-coverage": "^3.2.0", - "semver": "^6.3.0" - } - }, - "istanbul-lib-report": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz", - "integrity": "sha512-wcdi+uAKzfiGT2abPpKZ0hSU1rGQjUQnLvtY5MpQ7QCTahD3VODhcu4wcfY1YtkGaDD5yuydOLINXsfbus9ROw==", - "dev": true, - "requires": { - "istanbul-lib-coverage": "^3.0.0", - "make-dir": "^3.0.0", - "supports-color": "^7.1.0" - }, - "dependencies": { - "has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true - }, - "supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "requires": { - "has-flag": "^4.0.0" - } - } - } - }, - "istanbul-lib-source-maps": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-4.0.1.tgz", - "integrity": "sha512-n3s8EwkdFIJCG3BPKBYvskgXGoy88ARzvegkitk60NxRdwltLOTaH7CUiMRXvwYorl0Q712iEjcWB+fK/MrWVw==", - "dev": true, - "requires": { - "debug": "^4.1.1", - "istanbul-lib-coverage": "^3.0.0", - "source-map": "^0.6.1" - } - }, - "istanbul-reports": { - "version": "3.1.5", - "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.1.5.tgz", - "integrity": "sha512-nUsEMa9pBt/NOHqbcbeJEgqIlY/K7rVWUX6Lql2orY5e9roQOthbR3vtY4zzf2orPELg80fnxxk9zUyPlgwD1w==", - "dev": true, - "requires": { - "html-escaper": "^2.0.0", - "istanbul-lib-report": "^3.0.0" - } - }, - "jest": { - "version": "28.1.3", - "resolved": "https://registry.npmjs.org/jest/-/jest-28.1.3.tgz", - "integrity": "sha512-N4GT5on8UkZgH0O5LUavMRV1EDEhNTL0KEfRmDIeZHSV7p2XgLoY9t9VDUgL6o+yfdgYHVxuz81G8oB9VG5uyA==", - "dev": true, - "requires": { - "@jest/core": "^28.1.3", - "@jest/types": "^28.1.3", - "import-local": "^3.0.2", - "jest-cli": "^28.1.3" - } - }, - "jest-changed-files": { - "version": "28.1.3", - "resolved": "https://registry.npmjs.org/jest-changed-files/-/jest-changed-files-28.1.3.tgz", - "integrity": "sha512-esaOfUWJXk2nfZt9SPyC8gA1kNfdKLkQWyzsMlqq8msYSlNKfmZxfRgZn4Cd4MGVUF+7v6dBs0d5TOAKa7iIiA==", - "dev": true, - "requires": { - "execa": "^5.0.0", - "p-limit": "^3.1.0" - } - }, - "jest-circus": { - "version": "28.1.3", - "resolved": "https://registry.npmjs.org/jest-circus/-/jest-circus-28.1.3.tgz", - "integrity": "sha512-cZ+eS5zc79MBwt+IhQhiEp0OeBddpc1n8MBo1nMB8A7oPMKEO+Sre+wHaLJexQUj9Ya/8NOBY0RESUgYjB6fow==", - "dev": true, - "requires": { - "@jest/environment": "^28.1.3", - "@jest/expect": "^28.1.3", - "@jest/test-result": "^28.1.3", - "@jest/types": "^28.1.3", - "@types/node": "*", - "chalk": "^4.0.0", - "co": "^4.6.0", - "dedent": "^0.7.0", - "is-generator-fn": "^2.0.0", - "jest-each": "^28.1.3", - "jest-matcher-utils": "^28.1.3", - "jest-message-util": "^28.1.3", - "jest-runtime": "^28.1.3", - "jest-snapshot": "^28.1.3", - "jest-util": "^28.1.3", - "p-limit": "^3.1.0", - "pretty-format": "^28.1.3", - "slash": "^3.0.0", - "stack-utils": "^2.0.3" - }, - "dependencies": { - "@jest/schemas": { - "version": "28.1.3", - "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-28.1.3.tgz", - "integrity": "sha512-/l/VWsdt/aBXgjshLWOFyFt3IVdYypu5y2Wn2rOO1un6nkqIn8SLXzgIMYXFyYsRWDyF5EthmKJMIdJvk08grg==", - "dev": true, - "requires": { - "@sinclair/typebox": "^0.24.1" - } - }, - "ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "requires": { - "color-convert": "^2.0.1" - } - }, - "chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - }, - "color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "requires": { - "color-name": "~1.1.4" - } - }, - "color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true - }, - "pretty-format": { - "version": "28.1.3", - "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-28.1.3.tgz", - "integrity": "sha512-8gFb/To0OmxHR9+ZTb14Df2vNxdGCX8g1xWGUTqUw5TiZvcQf5sHKObd5UcPyLLyowNwDAMTF3XWOG1B6mxl1Q==", - "dev": true, - "requires": { - "@jest/schemas": "^28.1.3", - "ansi-regex": "^5.0.1", - "ansi-styles": "^5.0.0", - "react-is": "^18.0.0" - }, - "dependencies": { - "ansi-styles": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", - "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", - "dev": true - } - } - }, - "react-is": { - "version": "18.2.0", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz", - "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==", - "dev": true - }, - "supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "requires": { - "has-flag": "^4.0.0" - } - } - } - }, - "jest-cli": { - "version": "28.1.3", - "resolved": "https://registry.npmjs.org/jest-cli/-/jest-cli-28.1.3.tgz", - "integrity": "sha512-roY3kvrv57Azn1yPgdTebPAXvdR2xfezaKKYzVxZ6It/5NCxzJym6tUI5P1zkdWhfUYkxEI9uZWcQdaFLo8mJQ==", - "dev": true, - "requires": { - "@jest/core": "^28.1.3", - "@jest/test-result": "^28.1.3", - "@jest/types": "^28.1.3", - "chalk": "^4.0.0", - "exit": "^0.1.2", - "graceful-fs": "^4.2.9", - "import-local": "^3.0.2", - "jest-config": "^28.1.3", - "jest-util": "^28.1.3", - "jest-validate": "^28.1.3", - "prompts": "^2.0.1", - "yargs": "^17.3.1" - }, - "dependencies": { - "ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "requires": { - "color-convert": "^2.0.1" - } - }, - "chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - }, - "color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "requires": { - "color-name": "~1.1.4" - } - }, - "color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true - }, - "supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "requires": { - "has-flag": "^4.0.0" - } - } - } - }, - "jest-config": { - "version": "28.1.3", - "resolved": "https://registry.npmjs.org/jest-config/-/jest-config-28.1.3.tgz", - "integrity": "sha512-MG3INjByJ0J4AsNBm7T3hsuxKQqFIiRo/AUqb1q9LRKI5UU6Aar9JHbr9Ivn1TVwfUD9KirRoM/T6u8XlcQPHQ==", - "dev": true, - "requires": { - "@babel/core": "^7.11.6", - "@jest/test-sequencer": "^28.1.3", - "@jest/types": "^28.1.3", - "babel-jest": "^28.1.3", - "chalk": "^4.0.0", - "ci-info": "^3.2.0", - "deepmerge": "^4.2.2", - "glob": "^7.1.3", - "graceful-fs": "^4.2.9", - "jest-circus": "^28.1.3", - "jest-environment-node": "^28.1.3", - "jest-get-type": "^28.0.2", - "jest-regex-util": "^28.0.2", - "jest-resolve": "^28.1.3", - "jest-runner": "^28.1.3", - "jest-util": "^28.1.3", - "jest-validate": "^28.1.3", - "micromatch": "^4.0.4", - "parse-json": "^5.2.0", - "pretty-format": "^28.1.3", - "slash": "^3.0.0", - "strip-json-comments": "^3.1.1" - }, - "dependencies": { - "@jest/schemas": { - "version": "28.1.3", - "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-28.1.3.tgz", - "integrity": "sha512-/l/VWsdt/aBXgjshLWOFyFt3IVdYypu5y2Wn2rOO1un6nkqIn8SLXzgIMYXFyYsRWDyF5EthmKJMIdJvk08grg==", - "dev": true, - "requires": { - "@sinclair/typebox": "^0.24.1" - } - }, - "ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "requires": { - "color-convert": "^2.0.1" - } - }, - "chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - }, - "color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "requires": { - "color-name": "~1.1.4" - } - }, - "color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "glob": { - "version": "7.2.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", - "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", - "dev": true, - "requires": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.1.1", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - } - }, - "has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true - }, - "jest-get-type": { - "version": "28.0.2", - "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-28.0.2.tgz", - "integrity": "sha512-ioj2w9/DxSYHfOm5lJKCdcAmPJzQXmbM/Url3rhlghrPvT3tt+7a/+oXc9azkKmLvoiXjtV83bEWqi+vs5nlPA==", - "dev": true - }, - "pretty-format": { - "version": "28.1.3", - "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-28.1.3.tgz", - "integrity": "sha512-8gFb/To0OmxHR9+ZTb14Df2vNxdGCX8g1xWGUTqUw5TiZvcQf5sHKObd5UcPyLLyowNwDAMTF3XWOG1B6mxl1Q==", - "dev": true, - "requires": { - "@jest/schemas": "^28.1.3", - "ansi-regex": "^5.0.1", - "ansi-styles": "^5.0.0", - "react-is": "^18.0.0" - }, - "dependencies": { - "ansi-styles": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", - "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", - "dev": true - } - } - }, - "react-is": { - "version": "18.2.0", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz", - "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==", - "dev": true - }, - "supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "requires": { - "has-flag": "^4.0.0" - } - } - } - }, - "jest-diff": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-27.5.1.tgz", - "integrity": "sha512-m0NvkX55LDt9T4mctTEgnZk3fmEg3NRYutvMPWM/0iPnkFj2wIeF45O1718cMSOFO1vINkqmxqD8vE37uTEbqw==", - "dev": true, - "requires": { - "chalk": "^4.0.0", - "diff-sequences": "^27.5.1", - "jest-get-type": "^27.5.1", - "pretty-format": "^27.5.1" - }, - "dependencies": { - "ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "requires": { - "color-convert": "^2.0.1" - } - }, - "chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - }, - "color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "requires": { - "color-name": "~1.1.4" - } - }, - "color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true - }, - "supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "requires": { - "has-flag": "^4.0.0" - } - } - } - }, - "jest-docblock": { - "version": "28.1.1", - "resolved": "https://registry.npmjs.org/jest-docblock/-/jest-docblock-28.1.1.tgz", - "integrity": "sha512-3wayBVNiOYx0cwAbl9rwm5kKFP8yHH3d/fkEaL02NPTkDojPtheGB7HZSFY4wzX+DxyrvhXz0KSCVksmCknCuA==", - "dev": true, - "requires": { - "detect-newline": "^3.0.0" - } - }, - "jest-each": { - "version": "28.1.3", - "resolved": "https://registry.npmjs.org/jest-each/-/jest-each-28.1.3.tgz", - "integrity": "sha512-arT1z4sg2yABU5uogObVPvSlSMQlDA48owx07BDPAiasW0yYpYHYOo4HHLz9q0BVzDVU4hILFjzJw0So9aCL/g==", - "dev": true, - "requires": { - "@jest/types": "^28.1.3", - "chalk": "^4.0.0", - "jest-get-type": "^28.0.2", - "jest-util": "^28.1.3", - "pretty-format": "^28.1.3" - }, - "dependencies": { - "@jest/schemas": { - "version": "28.1.3", - "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-28.1.3.tgz", - "integrity": "sha512-/l/VWsdt/aBXgjshLWOFyFt3IVdYypu5y2Wn2rOO1un6nkqIn8SLXzgIMYXFyYsRWDyF5EthmKJMIdJvk08grg==", - "dev": true, - "requires": { - "@sinclair/typebox": "^0.24.1" - } - }, - "ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "requires": { - "color-convert": "^2.0.1" - } - }, - "chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - }, - "color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "requires": { - "color-name": "~1.1.4" - } - }, - "color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true - }, - "jest-get-type": { - "version": "28.0.2", - "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-28.0.2.tgz", - "integrity": "sha512-ioj2w9/DxSYHfOm5lJKCdcAmPJzQXmbM/Url3rhlghrPvT3tt+7a/+oXc9azkKmLvoiXjtV83bEWqi+vs5nlPA==", - "dev": true - }, - "pretty-format": { - "version": "28.1.3", - "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-28.1.3.tgz", - "integrity": "sha512-8gFb/To0OmxHR9+ZTb14Df2vNxdGCX8g1xWGUTqUw5TiZvcQf5sHKObd5UcPyLLyowNwDAMTF3XWOG1B6mxl1Q==", - "dev": true, - "requires": { - "@jest/schemas": "^28.1.3", - "ansi-regex": "^5.0.1", - "ansi-styles": "^5.0.0", - "react-is": "^18.0.0" - }, - "dependencies": { - "ansi-styles": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", - "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", - "dev": true - } - } - }, - "react-is": { - "version": "18.2.0", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz", - "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==", - "dev": true - }, - "supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "requires": { - "has-flag": "^4.0.0" - } - } - } - }, - "jest-environment-node": { - "version": "28.1.3", - "resolved": "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-28.1.3.tgz", - "integrity": "sha512-ugP6XOhEpjAEhGYvp5Xj989ns5cB1K6ZdjBYuS30umT4CQEETaxSiPcZ/E1kFktX4GkrcM4qu07IIlDYX1gp+A==", - "dev": true, - "requires": { - "@jest/environment": "^28.1.3", - "@jest/fake-timers": "^28.1.3", - "@jest/types": "^28.1.3", - "@types/node": "*", - "jest-mock": "^28.1.3", - "jest-util": "^28.1.3" - } - }, - "jest-get-type": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-27.5.1.tgz", - "integrity": "sha512-2KY95ksYSaK7DMBWQn6dQz3kqAf3BB64y2udeG+hv4KfSOb9qwcYQstTJc1KCbsix+wLZWZYN8t7nwX3GOBLRw==", - "dev": true - }, - "jest-haste-map": { - "version": "28.1.3", - "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-28.1.3.tgz", - "integrity": "sha512-3S+RQWDXccXDKSWnkHa/dPwt+2qwA8CJzR61w3FoYCvoo3Pn8tvGcysmMF0Bj0EX5RYvAI2EIvC57OmotfdtKA==", - "dev": true, - "requires": { - "@jest/types": "^28.1.3", - "@types/graceful-fs": "^4.1.3", - "@types/node": "*", - "anymatch": "^3.0.3", - "fb-watchman": "^2.0.0", - "fsevents": "^2.3.2", - "graceful-fs": "^4.2.9", - "jest-regex-util": "^28.0.2", - "jest-util": "^28.1.3", - "jest-worker": "^28.1.3", - "micromatch": "^4.0.4", - "walker": "^1.0.8" - } - }, - "jest-leak-detector": { - "version": "28.1.3", - "resolved": "https://registry.npmjs.org/jest-leak-detector/-/jest-leak-detector-28.1.3.tgz", - "integrity": "sha512-WFVJhnQsiKtDEo5lG2mM0v40QWnBM+zMdHHyJs8AWZ7J0QZJS59MsyKeJHWhpBZBH32S48FOVvGyOFT1h0DlqA==", - "dev": true, - "requires": { - "jest-get-type": "^28.0.2", - "pretty-format": "^28.1.3" - }, - "dependencies": { - "@jest/schemas": { - "version": "28.1.3", - "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-28.1.3.tgz", - "integrity": "sha512-/l/VWsdt/aBXgjshLWOFyFt3IVdYypu5y2Wn2rOO1un6nkqIn8SLXzgIMYXFyYsRWDyF5EthmKJMIdJvk08grg==", - "dev": true, - "requires": { - "@sinclair/typebox": "^0.24.1" - } - }, - "ansi-styles": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", - "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", - "dev": true - }, - "jest-get-type": { - "version": "28.0.2", - "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-28.0.2.tgz", - "integrity": "sha512-ioj2w9/DxSYHfOm5lJKCdcAmPJzQXmbM/Url3rhlghrPvT3tt+7a/+oXc9azkKmLvoiXjtV83bEWqi+vs5nlPA==", - "dev": true - }, - "pretty-format": { - "version": "28.1.3", - "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-28.1.3.tgz", - "integrity": "sha512-8gFb/To0OmxHR9+ZTb14Df2vNxdGCX8g1xWGUTqUw5TiZvcQf5sHKObd5UcPyLLyowNwDAMTF3XWOG1B6mxl1Q==", - "dev": true, - "requires": { - "@jest/schemas": "^28.1.3", - "ansi-regex": "^5.0.1", - "ansi-styles": "^5.0.0", - "react-is": "^18.0.0" - } - }, - "react-is": { - "version": "18.2.0", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz", - "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==", - "dev": true - } - } - }, - "jest-matcher-utils": { - "version": "28.1.3", - "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-28.1.3.tgz", - "integrity": "sha512-kQeJ7qHemKfbzKoGjHHrRKH6atgxMk8Enkk2iPQ3XwO6oE/KYD8lMYOziCkeSB9G4adPM4nR1DE8Tf5JeWH6Bw==", - "dev": true, - "requires": { - "chalk": "^4.0.0", - "jest-diff": "^28.1.3", - "jest-get-type": "^28.0.2", - "pretty-format": "^28.1.3" - }, - "dependencies": { - "@jest/schemas": { - "version": "28.1.3", - "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-28.1.3.tgz", - "integrity": "sha512-/l/VWsdt/aBXgjshLWOFyFt3IVdYypu5y2Wn2rOO1un6nkqIn8SLXzgIMYXFyYsRWDyF5EthmKJMIdJvk08grg==", - "dev": true, - "requires": { - "@sinclair/typebox": "^0.24.1" - } - }, - "ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "requires": { - "color-convert": "^2.0.1" - } - }, - "chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - }, - "color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "requires": { - "color-name": "~1.1.4" - } - }, - "color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "diff-sequences": { - "version": "28.1.1", - "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-28.1.1.tgz", - "integrity": "sha512-FU0iFaH/E23a+a718l8Qa/19bF9p06kgE0KipMOMadwa3SjnaElKzPaUC0vnibs6/B/9ni97s61mcejk8W1fQw==", - "dev": true - }, - "has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true - }, - "jest-diff": { - "version": "28.1.3", - "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-28.1.3.tgz", - "integrity": "sha512-8RqP1B/OXzjjTWkqMX67iqgwBVJRgCyKD3L9nq+6ZqJMdvjE8RgHktqZ6jNrkdMT+dJuYNI3rhQpxaz7drJHfw==", - "dev": true, - "requires": { - "chalk": "^4.0.0", - "diff-sequences": "^28.1.1", - "jest-get-type": "^28.0.2", - "pretty-format": "^28.1.3" - } - }, - "jest-get-type": { - "version": "28.0.2", - "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-28.0.2.tgz", - "integrity": "sha512-ioj2w9/DxSYHfOm5lJKCdcAmPJzQXmbM/Url3rhlghrPvT3tt+7a/+oXc9azkKmLvoiXjtV83bEWqi+vs5nlPA==", - "dev": true - }, - "pretty-format": { - "version": "28.1.3", - "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-28.1.3.tgz", - "integrity": "sha512-8gFb/To0OmxHR9+ZTb14Df2vNxdGCX8g1xWGUTqUw5TiZvcQf5sHKObd5UcPyLLyowNwDAMTF3XWOG1B6mxl1Q==", - "dev": true, - "requires": { - "@jest/schemas": "^28.1.3", - "ansi-regex": "^5.0.1", - "ansi-styles": "^5.0.0", - "react-is": "^18.0.0" - }, - "dependencies": { - "ansi-styles": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", - "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", - "dev": true - } - } - }, - "react-is": { - "version": "18.2.0", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz", - "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==", - "dev": true - }, - "supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "requires": { - "has-flag": "^4.0.0" - } - } - } - }, - "jest-message-util": { - "version": "28.1.3", - "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-28.1.3.tgz", - "integrity": "sha512-PFdn9Iewbt575zKPf1286Ht9EPoJmYT7P0kY+RibeYZ2XtOr53pDLEFoTWXbd1h4JiGiWpTBC84fc8xMXQMb7g==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.12.13", - "@jest/types": "^28.1.3", - "@types/stack-utils": "^2.0.0", - "chalk": "^4.0.0", - "graceful-fs": "^4.2.9", - "micromatch": "^4.0.4", - "pretty-format": "^28.1.3", - "slash": "^3.0.0", - "stack-utils": "^2.0.3" - }, - "dependencies": { - "@jest/schemas": { - "version": "28.1.3", - "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-28.1.3.tgz", - "integrity": "sha512-/l/VWsdt/aBXgjshLWOFyFt3IVdYypu5y2Wn2rOO1un6nkqIn8SLXzgIMYXFyYsRWDyF5EthmKJMIdJvk08grg==", - "dev": true, - "requires": { - "@sinclair/typebox": "^0.24.1" - } - }, - "ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "requires": { - "color-convert": "^2.0.1" - } - }, - "chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - }, - "color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "requires": { - "color-name": "~1.1.4" - } - }, - "color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true - }, - "pretty-format": { - "version": "28.1.3", - "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-28.1.3.tgz", - "integrity": "sha512-8gFb/To0OmxHR9+ZTb14Df2vNxdGCX8g1xWGUTqUw5TiZvcQf5sHKObd5UcPyLLyowNwDAMTF3XWOG1B6mxl1Q==", - "dev": true, - "requires": { - "@jest/schemas": "^28.1.3", - "ansi-regex": "^5.0.1", - "ansi-styles": "^5.0.0", - "react-is": "^18.0.0" - }, - "dependencies": { - "ansi-styles": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", - "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", - "dev": true - } - } - }, - "react-is": { - "version": "18.2.0", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz", - "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==", - "dev": true - }, - "supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "requires": { - "has-flag": "^4.0.0" - } - } - } - }, - "jest-mock": { - "version": "28.1.3", - "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-28.1.3.tgz", - "integrity": "sha512-o3J2jr6dMMWYVH4Lh/NKmDXdosrsJgi4AviS8oXLujcjpCMBb1FMsblDnOXKZKfSiHLxYub1eS0IHuRXsio9eA==", - "dev": true, - "requires": { - "@jest/types": "^28.1.3", - "@types/node": "*" - } - }, - "jest-pnp-resolver": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/jest-pnp-resolver/-/jest-pnp-resolver-1.2.2.tgz", - "integrity": "sha512-olV41bKSMm8BdnuMsewT4jqlZ8+3TCARAXjZGT9jcoSnrfUnRCqnMoF9XEeoWjbzObpqF9dRhHQj0Xb9QdF6/w==", - "dev": true, - "requires": {} - }, - "jest-regex-util": { - "version": "28.0.2", - "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-28.0.2.tgz", - "integrity": "sha512-4s0IgyNIy0y9FK+cjoVYoxamT7Zeo7MhzqRGx7YDYmaQn1wucY9rotiGkBzzcMXTtjrCAP/f7f+E0F7+fxPNdw==", - "dev": true - }, - "jest-resolve": { - "version": "28.1.3", - "resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-28.1.3.tgz", - "integrity": "sha512-Z1W3tTjE6QaNI90qo/BJpfnvpxtaFTFw5CDgwpyE/Kz8U/06N1Hjf4ia9quUhCh39qIGWF1ZuxFiBiJQwSEYKQ==", - "dev": true, - "requires": { - "chalk": "^4.0.0", - "graceful-fs": "^4.2.9", - "jest-haste-map": "^28.1.3", - "jest-pnp-resolver": "^1.2.2", - "jest-util": "^28.1.3", - "jest-validate": "^28.1.3", - "resolve": "^1.20.0", - "resolve.exports": "^1.1.0", - "slash": "^3.0.0" - }, - "dependencies": { - "ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "requires": { - "color-convert": "^2.0.1" - } - }, - "chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - }, - "color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "requires": { - "color-name": "~1.1.4" - } - }, - "color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true - }, - "supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "requires": { - "has-flag": "^4.0.0" - } - } - } - }, - "jest-resolve-dependencies": { - "version": "28.1.3", - "resolved": "https://registry.npmjs.org/jest-resolve-dependencies/-/jest-resolve-dependencies-28.1.3.tgz", - "integrity": "sha512-qa0QO2Q0XzQoNPouMbCc7Bvtsem8eQgVPNkwn9LnS+R2n8DaVDPL/U1gngC0LTl1RYXJU0uJa2BMC2DbTfFrHA==", - "dev": true, - "requires": { - "jest-regex-util": "^28.0.2", - "jest-snapshot": "^28.1.3" - } - }, - "jest-runner": { - "version": "28.1.3", - "resolved": "https://registry.npmjs.org/jest-runner/-/jest-runner-28.1.3.tgz", - "integrity": "sha512-GkMw4D/0USd62OVO0oEgjn23TM+YJa2U2Wu5zz9xsQB1MxWKDOlrnykPxnMsN0tnJllfLPinHTka61u0QhaxBA==", - "dev": true, - "requires": { - "@jest/console": "^28.1.3", - "@jest/environment": "^28.1.3", - "@jest/test-result": "^28.1.3", - "@jest/transform": "^28.1.3", - "@jest/types": "^28.1.3", - "@types/node": "*", - "chalk": "^4.0.0", - "emittery": "^0.10.2", - "graceful-fs": "^4.2.9", - "jest-docblock": "^28.1.1", - "jest-environment-node": "^28.1.3", - "jest-haste-map": "^28.1.3", - "jest-leak-detector": "^28.1.3", - "jest-message-util": "^28.1.3", - "jest-resolve": "^28.1.3", - "jest-runtime": "^28.1.3", - "jest-util": "^28.1.3", - "jest-watcher": "^28.1.3", - "jest-worker": "^28.1.3", - "p-limit": "^3.1.0", - "source-map-support": "0.5.13" - }, - "dependencies": { - "ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "requires": { - "color-convert": "^2.0.1" - } - }, - "chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - }, - "color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "requires": { - "color-name": "~1.1.4" - } - }, - "color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true - }, - "supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "requires": { - "has-flag": "^4.0.0" - } - } - } - }, - "jest-runtime": { - "version": "28.1.3", - "resolved": "https://registry.npmjs.org/jest-runtime/-/jest-runtime-28.1.3.tgz", - "integrity": "sha512-NU+881ScBQQLc1JHG5eJGU7Ui3kLKrmwCPPtYsJtBykixrM2OhVQlpMmFWJjMyDfdkGgBMNjXCGB/ebzsgNGQw==", - "dev": true, - "requires": { - "@jest/environment": "^28.1.3", - "@jest/fake-timers": "^28.1.3", - "@jest/globals": "^28.1.3", - "@jest/source-map": "^28.1.2", - "@jest/test-result": "^28.1.3", - "@jest/transform": "^28.1.3", - "@jest/types": "^28.1.3", - "chalk": "^4.0.0", - "cjs-module-lexer": "^1.0.0", - "collect-v8-coverage": "^1.0.0", - "execa": "^5.0.0", - "glob": "^7.1.3", - "graceful-fs": "^4.2.9", - "jest-haste-map": "^28.1.3", - "jest-message-util": "^28.1.3", - "jest-mock": "^28.1.3", - "jest-regex-util": "^28.0.2", - "jest-resolve": "^28.1.3", - "jest-snapshot": "^28.1.3", - "jest-util": "^28.1.3", - "slash": "^3.0.0", - "strip-bom": "^4.0.0" + "jest-diff": "^28.1.3", + "jest-get-type": "^28.0.2", + "pretty-format": "^28.1.3" }, - "dependencies": { - "ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "requires": { - "color-convert": "^2.0.1" - } - }, - "chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - }, - "color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "requires": { - "color-name": "~1.1.4" - } - }, - "color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "glob": { - "version": "7.2.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", - "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", - "dev": true, - "requires": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.1.1", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - } - }, - "has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true - }, - "supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "requires": { - "has-flag": "^4.0.0" - } - } + "engines": { + "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" } }, - "jest-snapshot": { + "node_modules/jest-snapshot/node_modules/jest-message-util": { "version": "28.1.3", - "resolved": "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-28.1.3.tgz", - "integrity": "sha512-4lzMgtiNlc3DU/8lZfmqxN3AYD6GGLbl+72rdBpXvcV+whX7mDrREzkPdp2RnmfIiWBg1YbuFSkXduF2JcafJg==", + "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-28.1.3.tgz", + "integrity": "sha512-PFdn9Iewbt575zKPf1286Ht9EPoJmYT7P0kY+RibeYZ2XtOr53pDLEFoTWXbd1h4JiGiWpTBC84fc8xMXQMb7g==", "dev": true, - "requires": { - "@babel/core": "^7.11.6", - "@babel/generator": "^7.7.2", - "@babel/plugin-syntax-typescript": "^7.7.2", - "@babel/traverse": "^7.7.2", - "@babel/types": "^7.3.3", - "@jest/expect-utils": "^28.1.3", - "@jest/transform": "^28.1.3", + "dependencies": { + "@babel/code-frame": "^7.12.13", "@jest/types": "^28.1.3", - "@types/babel__traverse": "^7.0.6", - "@types/prettier": "^2.1.5", - "babel-preset-current-node-syntax": "^1.0.0", + "@types/stack-utils": "^2.0.0", "chalk": "^4.0.0", - "expect": "^28.1.3", "graceful-fs": "^4.2.9", - "jest-diff": "^28.1.3", - "jest-get-type": "^28.0.2", - "jest-haste-map": "^28.1.3", - "jest-matcher-utils": "^28.1.3", - "jest-message-util": "^28.1.3", - "jest-util": "^28.1.3", - "natural-compare": "^1.4.0", + "micromatch": "^4.0.4", "pretty-format": "^28.1.3", - "semver": "^7.3.5" + "slash": "^3.0.0", + "stack-utils": "^2.0.3" }, - "dependencies": { - "@jest/expect-utils": { - "version": "28.1.3", - "resolved": "https://registry.npmjs.org/@jest/expect-utils/-/expect-utils-28.1.3.tgz", - "integrity": "sha512-wvbi9LUrHJLn3NlDW6wF2hvIMtd4JUl2QNVrjq+IBSHirgfrR3o9RnVtxzdEGO2n9JyIWwHnLfby5KzqBGg2YA==", - "dev": true, - "requires": { - "jest-get-type": "^28.0.2" - } - }, - "@jest/schemas": { - "version": "28.1.3", - "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-28.1.3.tgz", - "integrity": "sha512-/l/VWsdt/aBXgjshLWOFyFt3IVdYypu5y2Wn2rOO1un6nkqIn8SLXzgIMYXFyYsRWDyF5EthmKJMIdJvk08grg==", - "dev": true, - "requires": { - "@sinclair/typebox": "^0.24.1" - } - }, - "ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "requires": { - "color-convert": "^2.0.1" - } - }, - "chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - }, - "color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "requires": { - "color-name": "~1.1.4" - } - }, - "color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "diff-sequences": { - "version": "28.1.1", - "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-28.1.1.tgz", - "integrity": "sha512-FU0iFaH/E23a+a718l8Qa/19bF9p06kgE0KipMOMadwa3SjnaElKzPaUC0vnibs6/B/9ni97s61mcejk8W1fQw==", - "dev": true - }, - "expect": { - "version": "28.1.3", - "resolved": "https://registry.npmjs.org/expect/-/expect-28.1.3.tgz", - "integrity": "sha512-eEh0xn8HlsuOBxFgIss+2mX85VAS4Qy3OSkjV7rlBWljtA4oWH37glVGyOZSZvErDT/yBywZdPGwCXuTvSG85g==", - "dev": true, - "requires": { - "@jest/expect-utils": "^28.1.3", - "jest-get-type": "^28.0.2", - "jest-matcher-utils": "^28.1.3", - "jest-message-util": "^28.1.3", - "jest-util": "^28.1.3" - } - }, - "has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true - }, - "jest-diff": { - "version": "28.1.3", - "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-28.1.3.tgz", - "integrity": "sha512-8RqP1B/OXzjjTWkqMX67iqgwBVJRgCyKD3L9nq+6ZqJMdvjE8RgHktqZ6jNrkdMT+dJuYNI3rhQpxaz7drJHfw==", - "dev": true, - "requires": { - "chalk": "^4.0.0", - "diff-sequences": "^28.1.1", - "jest-get-type": "^28.0.2", - "pretty-format": "^28.1.3" - } - }, - "jest-get-type": { - "version": "28.0.2", - "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-28.0.2.tgz", - "integrity": "sha512-ioj2w9/DxSYHfOm5lJKCdcAmPJzQXmbM/Url3rhlghrPvT3tt+7a/+oXc9azkKmLvoiXjtV83bEWqi+vs5nlPA==", - "dev": true - }, - "pretty-format": { - "version": "28.1.3", - "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-28.1.3.tgz", - "integrity": "sha512-8gFb/To0OmxHR9+ZTb14Df2vNxdGCX8g1xWGUTqUw5TiZvcQf5sHKObd5UcPyLLyowNwDAMTF3XWOG1B6mxl1Q==", - "dev": true, - "requires": { - "@jest/schemas": "^28.1.3", - "ansi-regex": "^5.0.1", - "ansi-styles": "^5.0.0", - "react-is": "^18.0.0" - }, - "dependencies": { - "ansi-styles": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", - "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", - "dev": true - } - } - }, - "react-is": { - "version": "18.2.0", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz", - "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==", - "dev": true - }, - "semver": { - "version": "7.3.7", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.7.tgz", - "integrity": "sha512-QlYTucUYOews+WeEujDoEGziz4K6c47V/Bd+LjSSYcA94p+DmINdf7ncaUinThfvZyu13lN9OY1XDxt8C0Tw0g==", - "dev": true, - "requires": { - "lru-cache": "^6.0.0" - } - }, - "supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "requires": { - "has-flag": "^4.0.0" - } - } + "engines": { + "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" } }, - "jest-util": { + "node_modules/jest-snapshot/node_modules/jest-util": { "version": "28.1.3", "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-28.1.3.tgz", "integrity": "sha512-XdqfpHwpcSRko/C35uLYFM2emRAltIIKZiJ9eAmhjsj0CqZMa0p1ib0R5fWIqGhn1a103DebTbpqIaP1qCQ6tQ==", "dev": true, - "requires": { + "dependencies": { "@jest/types": "^28.1.3", "@types/node": "*", "chalk": "^4.0.0", @@ -12241,64 +4907,89 @@ "graceful-fs": "^4.2.9", "picomatch": "^2.2.3" }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" + } + }, + "node_modules/jest-snapshot/node_modules/pretty-format": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-28.1.3.tgz", + "integrity": "sha512-8gFb/To0OmxHR9+ZTb14Df2vNxdGCX8g1xWGUTqUw5TiZvcQf5sHKObd5UcPyLLyowNwDAMTF3XWOG1B6mxl1Q==", + "dev": true, + "dependencies": { + "@jest/schemas": "^28.1.3", + "ansi-regex": "^5.0.1", + "ansi-styles": "^5.0.0", + "react-is": "^18.0.0" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" + } + }, + "node_modules/jest-snapshot/node_modules/react-is": { + "version": "18.2.0", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz", + "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==", + "dev": true + }, + "node_modules/jest-util": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-29.7.0.tgz", + "integrity": "sha512-z6EbKajIpqGKU56y5KBUgy1dt1ihhQJgWzUlZHArA/+X2ad7Cb5iF+AK1EWVL/Bo7Rz9uurpqw6SiBCefUbCGA==", + "dev": true, + "dependencies": { + "@jest/types": "^29.6.3", + "@types/node": "*", + "chalk": "^4.0.0", + "ci-info": "^3.2.0", + "graceful-fs": "^4.2.9", + "picomatch": "^2.2.3" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-util/node_modules/@jest/schemas": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-29.6.3.tgz", + "integrity": "sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA==", + "dev": true, "dependencies": { - "ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "requires": { - "color-convert": "^2.0.1" - } - }, - "chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - }, - "color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "requires": { - "color-name": "~1.1.4" - } - }, - "color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true - }, - "supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "requires": { - "has-flag": "^4.0.0" - } - } + "@sinclair/typebox": "^0.27.8" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "jest-validate": { + "node_modules/jest-util/node_modules/@jest/types": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.6.3.tgz", + "integrity": "sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw==", + "dev": true, + "dependencies": { + "@jest/schemas": "^29.6.3", + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^3.0.0", + "@types/node": "*", + "@types/yargs": "^17.0.8", + "chalk": "^4.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-util/node_modules/@sinclair/typebox": { + "version": "0.27.8", + "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.27.8.tgz", + "integrity": "sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA==", + "dev": true + }, + "node_modules/jest-validate": { "version": "28.1.3", "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-28.1.3.tgz", "integrity": "sha512-SZbOGBWEsaTxBGCOpsRWlXlvNkvTkY0XxRfh7zYmvd8uL5Qzyg0CHAXiXKROflh801quA6+/DsT4ODDthOC/OA==", "dev": true, - "requires": { + "dependencies": { "@jest/types": "^28.1.3", "camelcase": "^6.2.0", "chalk": "^4.0.0", @@ -12306,111 +4997,70 @@ "leven": "^3.1.0", "pretty-format": "^28.1.3" }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" + } + }, + "node_modules/jest-validate/node_modules/ansi-styles": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/jest-validate/node_modules/camelcase": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", + "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/jest-validate/node_modules/jest-get-type": { + "version": "28.0.2", + "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-28.0.2.tgz", + "integrity": "sha512-ioj2w9/DxSYHfOm5lJKCdcAmPJzQXmbM/Url3rhlghrPvT3tt+7a/+oXc9azkKmLvoiXjtV83bEWqi+vs5nlPA==", + "dev": true, + "engines": { + "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" + } + }, + "node_modules/jest-validate/node_modules/pretty-format": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-28.1.3.tgz", + "integrity": "sha512-8gFb/To0OmxHR9+ZTb14Df2vNxdGCX8g1xWGUTqUw5TiZvcQf5sHKObd5UcPyLLyowNwDAMTF3XWOG1B6mxl1Q==", + "dev": true, "dependencies": { - "@jest/schemas": { - "version": "28.1.3", - "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-28.1.3.tgz", - "integrity": "sha512-/l/VWsdt/aBXgjshLWOFyFt3IVdYypu5y2Wn2rOO1un6nkqIn8SLXzgIMYXFyYsRWDyF5EthmKJMIdJvk08grg==", - "dev": true, - "requires": { - "@sinclair/typebox": "^0.24.1" - } - }, - "ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "requires": { - "color-convert": "^2.0.1" - } - }, - "camelcase": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", - "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", - "dev": true - }, - "chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - }, - "color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "requires": { - "color-name": "~1.1.4" - } - }, - "color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true - }, - "jest-get-type": { - "version": "28.0.2", - "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-28.0.2.tgz", - "integrity": "sha512-ioj2w9/DxSYHfOm5lJKCdcAmPJzQXmbM/Url3rhlghrPvT3tt+7a/+oXc9azkKmLvoiXjtV83bEWqi+vs5nlPA==", - "dev": true - }, - "pretty-format": { - "version": "28.1.3", - "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-28.1.3.tgz", - "integrity": "sha512-8gFb/To0OmxHR9+ZTb14Df2vNxdGCX8g1xWGUTqUw5TiZvcQf5sHKObd5UcPyLLyowNwDAMTF3XWOG1B6mxl1Q==", - "dev": true, - "requires": { - "@jest/schemas": "^28.1.3", - "ansi-regex": "^5.0.1", - "ansi-styles": "^5.0.0", - "react-is": "^18.0.0" - }, - "dependencies": { - "ansi-styles": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", - "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", - "dev": true - } - } - }, - "react-is": { - "version": "18.2.0", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz", - "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==", - "dev": true - }, - "supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "requires": { - "has-flag": "^4.0.0" - } - } + "@jest/schemas": "^28.1.3", + "ansi-regex": "^5.0.1", + "ansi-styles": "^5.0.0", + "react-is": "^18.0.0" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" } }, - "jest-watcher": { + "node_modules/jest-validate/node_modules/react-is": { + "version": "18.2.0", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz", + "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==", + "dev": true + }, + "node_modules/jest-watcher": { "version": "28.1.3", "resolved": "https://registry.npmjs.org/jest-watcher/-/jest-watcher-28.1.3.tgz", "integrity": "sha512-t4qcqj9hze+jviFPUN3YAtAEeFnr/azITXQEMARf5cMwKY2SMBRnCQTXLixTl20OR6mLh9KLMrgVJgJISym+1g==", "dev": true, - "requires": { + "dependencies": { "@jest/test-result": "^28.1.3", "@jest/types": "^28.1.3", "@types/node": "*", @@ -12420,1272 +5070,1722 @@ "jest-util": "^28.1.3", "string-length": "^4.0.1" }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" + } + }, + "node_modules/jest-watcher/node_modules/jest-util": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-28.1.3.tgz", + "integrity": "sha512-XdqfpHwpcSRko/C35uLYFM2emRAltIIKZiJ9eAmhjsj0CqZMa0p1ib0R5fWIqGhn1a103DebTbpqIaP1qCQ6tQ==", + "dev": true, "dependencies": { - "ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "requires": { - "color-convert": "^2.0.1" - } - }, - "chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - }, - "color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "requires": { - "color-name": "~1.1.4" - } - }, - "color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true - }, - "supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "requires": { - "has-flag": "^4.0.0" - } - } + "@jest/types": "^28.1.3", + "@types/node": "*", + "chalk": "^4.0.0", + "ci-info": "^3.2.0", + "graceful-fs": "^4.2.9", + "picomatch": "^2.2.3" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" } }, - "jest-worker": { + "node_modules/jest-worker": { "version": "28.1.3", "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-28.1.3.tgz", "integrity": "sha512-CqRA220YV/6jCo8VWvAt1KKx6eek1VIHMPeLEbpcfSfkEeWyBNppynM/o6q+Wmw+sOhos2ml34wZbSX3G13//g==", "dev": true, - "requires": { + "dependencies": { "@types/node": "*", "merge-stream": "^2.0.0", "supports-color": "^8.0.0" }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" + } + }, + "node_modules/jest-worker/node_modules/supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "dev": true, "dependencies": { - "has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true - }, - "supports-color": { - "version": "8.1.1", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", - "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", - "dev": true, - "requires": { - "has-flag": "^4.0.0" - } - } + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" } }, - "js-sha256": { + "node_modules/js-sha256": { "version": "0.9.0", "resolved": "https://registry.npmjs.org/js-sha256/-/js-sha256-0.9.0.tgz", "integrity": "sha512-sga3MHh9sgQN2+pJ9VYZ+1LPwXOxuBJBA5nrR5/ofPfuiJBE2hnjsaN8se8JznOmGLN2p49Pe5U/ttafcs/apA==" }, - "js-tokens": { + "node_modules/js-tokens": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", "dev": true }, - "js-yaml": { + "node_modules/js-yaml": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", "dev": true, - "requires": { + "dependencies": { "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" } }, - "jsesc": { + "node_modules/jsesc": { "version": "2.5.2", "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==", + "dev": true, + "bin": { + "jsesc": "bin/jsesc" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/json-buffer": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", + "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", "dev": true }, - "json-parse-even-better-errors": { + "node_modules/json-parse-even-better-errors": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", "dev": true }, - "json-schema-traverse": { + "node_modules/json-schema-traverse": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", "dev": true }, - "json-stable-stringify-without-jsonify": { + "node_modules/json-stable-stringify-without-jsonify": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", - "integrity": "sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE=", + "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", "dev": true }, - "json5": { + "node_modules/json5": { "version": "2.2.3", "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", - "dev": true + "dev": true, + "bin": { + "json5": "lib/cli.js" + }, + "engines": { + "node": ">=6" + } }, - "jsonc-parser": { + "node_modules/jsonc-parser": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-3.2.0.tgz", "integrity": "sha512-gfFQZrcTc8CnKXp6Y4/CBT3fTc0OVuDofpre4aEeEpSBPV5X5v4+Vmx+8snU7RLPrNHPKSgLxGo9YuQzz20o+w==", "dev": true }, - "jsonfile": { + "node_modules/jsonfile": { "version": "6.1.0", "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", "dev": true, - "requires": { - "graceful-fs": "^4.1.6", + "dependencies": { "universalify": "^2.0.0" + }, + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, + "node_modules/keyv": { + "version": "4.5.4", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", + "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", + "dev": true, + "dependencies": { + "json-buffer": "3.0.1" } }, - "kleur": { + "node_modules/kleur": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/kleur/-/kleur-3.0.3.tgz", "integrity": "sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==", - "dev": true + "dev": true, + "engines": { + "node": ">=6" + } }, - "leven": { + "node_modules/leven": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/leven/-/leven-3.1.0.tgz", "integrity": "sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==", - "dev": true + "dev": true, + "engines": { + "node": ">=6" + } }, - "levn": { + "node_modules/levn": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", "dev": true, - "requires": { + "dependencies": { "prelude-ls": "^1.2.1", "type-check": "~0.4.0" + }, + "engines": { + "node": ">= 0.8.0" } }, - "lines-and-columns": { + "node_modules/lines-and-columns": { "version": "1.2.4", "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", "dev": true }, - "locate-path": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", - "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "node_modules/locate-path": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", "dev": true, - "requires": { - "p-locate": "^4.1.0" + "dependencies": { + "p-locate": "^5.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "lodash.memoize": { + "node_modules/lodash.memoize": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz", - "integrity": "sha1-vMbEmkKihA7Zl/Mj6tpezRguC/4=", + "integrity": "sha512-t7j+NzmgnQzTAYXcsHYLgimltOV1MXHtlOWf6GjL9Kj8GK5FInw5JotxvbOs+IvV1/Dzo04/fCGfLVs7aXb4Ag==", "dev": true }, - "lodash.merge": { + "node_modules/lodash.merge": { "version": "4.6.2", "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", "dev": true }, - "lru-cache": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", - "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "node_modules/lru-cache": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", + "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", "dev": true, - "requires": { - "yallist": "^4.0.0" + "dependencies": { + "yallist": "^3.0.2" } }, - "lunr": { + "node_modules/lunr": { "version": "2.3.9", "resolved": "https://registry.npmjs.org/lunr/-/lunr-2.3.9.tgz", "integrity": "sha512-zTU3DaZaF3Rt9rhN3uBMGQD3dD2/vFQqnvZCDv4dl5iOzq2IZQqTxu90r4E5J+nP70J3ilqVCrbho2eWaeW8Ow==", "dev": true }, - "make-dir": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", - "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==", + "node_modules/make-dir": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-4.0.0.tgz", + "integrity": "sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==", "dev": true, - "requires": { - "semver": "^6.0.0" + "dependencies": { + "semver": "^7.5.3" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "make-error": { + "node_modules/make-error": { "version": "1.3.6", "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", "dev": true }, - "makeerror": { + "node_modules/makeerror": { "version": "1.0.12", "resolved": "https://registry.npmjs.org/makeerror/-/makeerror-1.0.12.tgz", "integrity": "sha512-JmqCvUhmt43madlpFzG4BQzG2Z3m6tvQDNKdClZnO3VbIudJYmxsT0FNJMeiB2+JTSlTQTSbU8QdesVmwJcmLg==", "dev": true, - "requires": { + "dependencies": { "tmpl": "1.0.5" } }, - "marked": { + "node_modules/marked": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/marked/-/marked-4.3.0.tgz", "integrity": "sha512-PRsaiG84bK+AMvxziE/lCFss8juXjNaWzVbN5tXAm4XjeaS9NAHhop+PjQxz2A9h8Q4M/xGmzP8vqNwy6JeK0A==", - "dev": true + "dev": true, + "bin": { + "marked": "bin/marked.js" + }, + "engines": { + "node": ">= 12" + } }, - "merge-stream": { + "node_modules/merge-stream": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", "dev": true }, - "merge2": { + "node_modules/merge2": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", - "dev": true + "dev": true, + "engines": { + "node": ">= 8" + } }, - "micromatch": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.4.tgz", - "integrity": "sha512-pRmzw/XUcwXGpD9aI9q/0XOwLNygjETJ8y0ao0wdqprrzDa4YnxLcz7fQRZr8voh8V10kGhABbNcHVk5wHgWwg==", + "node_modules/micromatch": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz", + "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==", "dev": true, - "requires": { - "braces": "^3.0.1", - "picomatch": "^2.2.3" + "dependencies": { + "braces": "^3.0.2", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=8.6" } }, - "mimic-fn": { + "node_modules/mimic-fn": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", - "dev": true + "dev": true, + "engines": { + "node": ">=6" + } }, - "minimatch": { + "node_modules/minimatch": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", "dev": true, - "requires": { + "dependencies": { "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" } }, - "minimist": { - "version": "1.2.7", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.7.tgz", - "integrity": "sha512-bzfL1YUZsP41gmu/qjrEk0Q6i2ix/cVeAhbCbqH9u3zYutS1cLg00qhrD0M2MVdCcx4Sc0UpP2eBWo9rotpq6g==", - "dev": true + "node_modules/minimist": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", + "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", + "dev": true, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } }, - "ms": { + "node_modules/ms": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", "dev": true }, - "natural-compare": { + "node_modules/natural-compare": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", - "integrity": "sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=", + "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", + "dev": true + }, + "node_modules/natural-compare-lite": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare-lite/-/natural-compare-lite-1.4.0.tgz", + "integrity": "sha512-Tj+HTDSJJKaZnfiuw+iaF9skdPpTo2GtEly5JHnWV/hfv2Qj/9RKsGISQtLh2ox3l5EAGw487hnBee0sIJ6v2g==", "dev": true }, - "neo-async": { + "node_modules/neo-async": { "version": "2.6.2", "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz", "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==", "dev": true }, - "node-int64": { + "node_modules/node-fetch": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", + "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==", + "dependencies": { + "whatwg-url": "^5.0.0" + }, + "engines": { + "node": "4.x || >=6.0.0" + }, + "peerDependencies": { + "encoding": "^0.1.0" + }, + "peerDependenciesMeta": { + "encoding": { + "optional": true + } + } + }, + "node_modules/node-int64": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz", "integrity": "sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw==", "dev": true }, - "node-releases": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.6.tgz", - "integrity": "sha512-PiVXnNuFm5+iYkLBNeq5211hvO38y63T0i2KKh2KnUs3RpzJ+JtODFjkD8yjLwnDkTYF1eKXheUwdssR+NRZdg==", + "node_modules/node-releases": { + "version": "2.0.13", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.13.tgz", + "integrity": "sha512-uYr7J37ae/ORWdZeQ1xxMJe3NtdmqMC/JZK+geofDrkLUApKRHPd18/TxtBOJ4A0/+uUIliorNrfYV6s1b02eQ==", "dev": true }, - "normalize-path": { + "node_modules/normalize-path": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", - "dev": true + "dev": true, + "engines": { + "node": ">=0.10.0" + } }, - "npm-run-path": { + "node_modules/npm-run-path": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", "dev": true, - "requires": { + "dependencies": { "path-key": "^3.0.0" + }, + "engines": { + "node": ">=8" } }, - "once": { + "node_modules/once": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", "dev": true, - "requires": { + "dependencies": { "wrappy": "1" } }, - "onetime": { + "node_modules/onetime": { "version": "5.1.2", "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", "dev": true, - "requires": { + "dependencies": { "mimic-fn": "^2.1.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "optionator": { - "version": "0.9.1", - "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.1.tgz", - "integrity": "sha512-74RlY5FCnhq4jRxVUPKDaRwrVNXMqsGsiW6AJw4XK8hmtm10wC0ypZBLw5IIp85NZMr91+qd1RvvENwg7jjRFw==", + "node_modules/optionator": { + "version": "0.9.3", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.3.tgz", + "integrity": "sha512-JjCoypp+jKn1ttEFExxhetCKeJt9zhAgAve5FXHixTvFDW/5aEktX9bufBKLRRMdU7bNtpLfcGu94B3cdEJgjg==", "dev": true, - "requires": { + "dependencies": { + "@aashutoshrathi/word-wrap": "^1.2.3", "deep-is": "^0.1.3", "fast-levenshtein": "^2.0.6", "levn": "^0.4.1", "prelude-ls": "^1.2.1", - "type-check": "^0.4.0", - "word-wrap": "^1.2.3" + "type-check": "^0.4.0" + }, + "engines": { + "node": ">= 0.8.0" } }, - "p-limit": { + "node_modules/p-limit": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", "dev": true, - "requires": { + "dependencies": { "yocto-queue": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "p-locate": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", - "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "node_modules/p-locate": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", "dev": true, - "requires": { - "p-limit": "^2.2.0" - }, "dependencies": { - "p-limit": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", - "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", - "dev": true, - "requires": { - "p-try": "^2.0.0" - } - } + "p-limit": "^3.0.2" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "p-try": { + "node_modules/p-try": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", - "dev": true + "dev": true, + "engines": { + "node": ">=6" + } }, - "parent-module": { + "node_modules/parent-module": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", "dev": true, - "requires": { + "dependencies": { "callsites": "^3.0.0" + }, + "engines": { + "node": ">=6" } }, - "parse-json": { + "node_modules/parse-json": { "version": "5.2.0", "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", "dev": true, - "requires": { + "dependencies": { "@babel/code-frame": "^7.0.0", "error-ex": "^1.3.1", "json-parse-even-better-errors": "^2.3.0", "lines-and-columns": "^1.1.6" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "path-exists": { + "node_modules/path-exists": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", - "dev": true + "dev": true, + "engines": { + "node": ">=8" + } }, - "path-is-absolute": { + "node_modules/path-is-absolute": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", - "dev": true + "dev": true, + "engines": { + "node": ">=0.10.0" + } }, - "path-key": { + "node_modules/path-key": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", - "dev": true + "dev": true, + "engines": { + "node": ">=8" + } }, - "path-parse": { + "node_modules/path-parse": { "version": "1.0.7", "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", "dev": true }, - "path-type": { + "node_modules/path-type": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", - "dev": true + "dev": true, + "engines": { + "node": ">=8" + } }, - "picocolors": { + "node_modules/picocolors": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==", "dev": true }, - "picomatch": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.0.tgz", - "integrity": "sha512-lY1Q/PiJGC2zOv/z391WOTD+Z02bCgsFfvxoXXf6h7kv9o+WmsmzYqrAwY63sNgOxE4xEdq0WyUnXfKeBrSvYw==", - "dev": true + "node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true, + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } }, - "pirates": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.5.tgz", - "integrity": "sha512-8V9+HQPupnaXMA23c5hvl69zXvTwTzyAYasnkb0Tts4XvO4CliqONMOnvlq26rkhLC3nWDFBJf73LU1e1VZLaQ==", - "dev": true + "node_modules/pirates": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.6.tgz", + "integrity": "sha512-saLsH7WeYYPiD25LDuLRRY/i+6HaPYr6G1OUlN39otzkSTxKnubR9RTxS3/Kk50s1g2JTgFwWQDQyplC5/SHZg==", + "dev": true, + "engines": { + "node": ">= 6" + } }, - "pkg-dir": { + "node_modules/pkg-dir": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", "dev": true, - "requires": { + "dependencies": { "find-up": "^4.0.0" + }, + "engines": { + "node": ">=8" } }, - "playwright-core": { - "version": "1.25.2", - "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.25.2.tgz", - "integrity": "sha512-0yTbUE9lIddkEpLHL3u8PoCL+pWiZtj5A/j3U7YoNjcmKKDGBnCrgHJMzwd2J5vy6l28q4ki3JIuz7McLHhl1A==", - "dev": true + "node_modules/pkg-dir/node_modules/find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "dev": true, + "dependencies": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/pkg-dir/node_modules/locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "dev": true, + "dependencies": { + "p-locate": "^4.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/pkg-dir/node_modules/p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "dev": true, + "dependencies": { + "p-try": "^2.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/pkg-dir/node_modules/p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "dev": true, + "dependencies": { + "p-limit": "^2.2.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/playwright": { + "version": "1.39.0", + "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.39.0.tgz", + "integrity": "sha512-naE5QT11uC/Oiq0BwZ50gDmy8c8WLPRTEWuSSFVG2egBka/1qMoSqYQcROMT9zLwJ86oPofcTH2jBY/5wWOgIw==", + "dev": true, + "dependencies": { + "playwright-core": "1.39.0" + }, + "bin": { + "playwright": "cli.js" + }, + "engines": { + "node": ">=16" + }, + "optionalDependencies": { + "fsevents": "2.3.2" + } + }, + "node_modules/playwright-core": { + "version": "1.39.0", + "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.39.0.tgz", + "integrity": "sha512-+k4pdZgs1qiM+OUkSjx96YiKsXsmb59evFoqv8SKO067qBA+Z2s/dCzJij/ZhdQcs2zlTAgRKfeiiLm8PQ2qvw==", + "dev": true, + "bin": { + "playwright-core": "cli.js" + }, + "engines": { + "node": ">=16" + } }, - "prelude-ls": { + "node_modules/prelude-ls": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", - "dev": true + "dev": true, + "engines": { + "node": ">= 0.8.0" + } }, - "prettier": { - "version": "2.8.4", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.8.4.tgz", - "integrity": "sha512-vIS4Rlc2FNh0BySk3Wkd6xmwxB0FpOndW5fisM5H8hsZSxU2VWVB5CWIkIjWvrHjIhxk2g3bfMKM87zNTrZddw==", - "dev": true + "node_modules/prettier": { + "version": "2.8.8", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.8.8.tgz", + "integrity": "sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q==", + "dev": true, + "bin": { + "prettier": "bin-prettier.js" + }, + "engines": { + "node": ">=10.13.0" + }, + "funding": { + "url": "https://github.com/prettier/prettier?sponsor=1" + } }, - "pretty-format": { + "node_modules/pretty-format": { "version": "27.5.1", "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-27.5.1.tgz", "integrity": "sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ==", "dev": true, - "requires": { + "dependencies": { "ansi-regex": "^5.0.1", "ansi-styles": "^5.0.0", "react-is": "^17.0.1" }, - "dependencies": { - "ansi-styles": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", - "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", - "dev": true - } + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" } }, - "progress": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz", - "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==", - "dev": true + "node_modules/pretty-format/node_modules/ansi-styles": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } }, - "prompts": { + "node_modules/prompts": { "version": "2.4.2", "resolved": "https://registry.npmjs.org/prompts/-/prompts-2.4.2.tgz", "integrity": "sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q==", "dev": true, - "requires": { + "dependencies": { "kleur": "^3.0.3", "sisteransi": "^1.0.5" + }, + "engines": { + "node": ">= 6" } }, - "punycode": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", - "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==", - "dev": true + "node_modules/punycode": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", + "dev": true, + "engines": { + "node": ">=6" + } }, - "queue-microtask": { + "node_modules/queue-microtask": { "version": "1.2.3", "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", - "dev": true + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] }, - "react-is": { + "node_modules/react-is": { "version": "17.0.2", "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz", "integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==", "dev": true }, - "reflect-metadata": { + "node_modules/reflect-metadata": { "version": "0.1.13", "resolved": "https://registry.npmjs.org/reflect-metadata/-/reflect-metadata-0.1.13.tgz", "integrity": "sha512-Ts1Y/anZELhSsjMcU605fU9RE4Oi3p5ORujwbIKXfWa+0Zxs510Qrmrce5/Jowq3cHSZSJqBjypxmHarc+vEWg==" }, - "regexpp": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-3.2.0.tgz", - "integrity": "sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg==", - "dev": true - }, - "replace-in-file": { + "node_modules/replace-in-file": { "version": "6.3.5", "resolved": "https://registry.npmjs.org/replace-in-file/-/replace-in-file-6.3.5.tgz", "integrity": "sha512-arB9d3ENdKva2fxRnSjwBEXfK1npgyci7ZZuwysgAp7ORjHSyxz6oqIjTEv8R0Ydl4Ll7uOAZXL4vbkhGIizCg==", "dev": true, - "requires": { + "dependencies": { "chalk": "^4.1.2", "glob": "^7.2.0", "yargs": "^17.2.1" }, + "bin": { + "replace-in-file": "bin/cli.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/replace-in-file/node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "dev": true, "dependencies": { - "ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "requires": { - "color-convert": "^2.0.1" - } - }, - "chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - }, - "color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "requires": { - "color-name": "~1.1.4" - } - }, - "color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "glob": { - "version": "7.2.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", - "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", - "dev": true, - "requires": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.1.1", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - } - }, - "has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true - }, - "supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "requires": { - "has-flag": "^4.0.0" - } - } + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, - "require-directory": { + "node_modules/require-directory": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", - "dev": true + "dev": true, + "engines": { + "node": ">=0.10.0" + } }, - "resolve": { - "version": "1.22.1", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.1.tgz", - "integrity": "sha512-nBpuuYuY5jFsli/JIs1oldw6fOQCBioohqWZg/2hiaOybXOft4lonv85uDOKXdf8rhyK159cxU5cDcK/NKk8zw==", + "node_modules/resolve": { + "version": "1.22.8", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.8.tgz", + "integrity": "sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==", "dev": true, - "requires": { - "is-core-module": "^2.9.0", + "dependencies": { + "is-core-module": "^2.13.0", "path-parse": "^1.0.7", "supports-preserve-symlinks-flag": "^1.0.0" + }, + "bin": { + "resolve": "bin/resolve" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "resolve-cwd": { + "node_modules/resolve-cwd": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-3.0.0.tgz", "integrity": "sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg==", "dev": true, - "requires": { + "dependencies": { "resolve-from": "^5.0.0" }, - "dependencies": { - "resolve-from": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", - "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", - "dev": true - } + "engines": { + "node": ">=8" + } + }, + "node_modules/resolve-cwd/node_modules/resolve-from": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", + "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", + "dev": true, + "engines": { + "node": ">=8" } }, - "resolve-from": { + "node_modules/resolve-from": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", - "dev": true + "dev": true, + "engines": { + "node": ">=4" + } }, - "resolve.exports": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/resolve.exports/-/resolve.exports-1.1.0.tgz", - "integrity": "sha512-J1l+Zxxp4XK3LUDZ9m60LRJF/mAe4z6a4xyabPHk7pvK5t35dACV32iIjJDFeWZFfZlO29w6SZ67knR0tHzJtQ==", - "dev": true + "node_modules/resolve.exports": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/resolve.exports/-/resolve.exports-1.1.1.tgz", + "integrity": "sha512-/NtpHNDN7jWhAaQ9BvBUYZ6YTXsRBgfqWFWP7BZBaoMJO/I3G5OFzvTuWNlZC3aPjins1F+TNrLKsGbH4rfsRQ==", + "dev": true, + "engines": { + "node": ">=10" + } }, - "reusify": { + "node_modules/reusify": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", - "dev": true + "dev": true, + "engines": { + "iojs": ">=1.0.0", + "node": ">=0.10.0" + } }, - "rimraf": { + "node_modules/rimraf": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", "dev": true, - "requires": { + "dependencies": { "glob": "^7.1.3" }, + "bin": { + "rimraf": "bin.js" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/rimraf/node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "dev": true, "dependencies": { - "glob": { - "version": "7.2.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", - "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", - "dev": true, - "requires": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.1.1", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - } - } + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, - "run-parallel": { + "node_modules/run-parallel": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", "dev": true, - "requires": { + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "dependencies": { "queue-microtask": "^1.2.2" } }, - "safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", - "dev": true + "node_modules/semver": { + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", + "dev": true, + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } }, - "semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "node_modules/semver/node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dev": true, + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/semver/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", "dev": true }, - "shebang-command": { + "node_modules/shebang-command": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", "dev": true, - "requires": { + "dependencies": { "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" } }, - "shebang-regex": { + "node_modules/shebang-regex": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", - "dev": true + "dev": true, + "engines": { + "node": ">=8" + } }, - "shiki": { - "version": "0.14.1", - "resolved": "https://registry.npmjs.org/shiki/-/shiki-0.14.1.tgz", - "integrity": "sha512-+Jz4nBkCBe0mEDqo1eKRcCdjRtrCjozmcbTUjbPTX7OOJfEbTZzlUWlZtGe3Gb5oV1/jnojhG//YZc3rs9zSEw==", + "node_modules/shiki": { + "version": "0.14.5", + "resolved": "https://registry.npmjs.org/shiki/-/shiki-0.14.5.tgz", + "integrity": "sha512-1gCAYOcmCFONmErGTrS1fjzJLA7MGZmKzrBNX7apqSwhyITJg2O102uFzXUeBxNnEkDA9vHIKLyeKq0V083vIw==", "dev": true, - "requires": { + "dependencies": { "ansi-sequence-parser": "^1.1.0", "jsonc-parser": "^3.2.0", "vscode-oniguruma": "^1.7.0", "vscode-textmate": "^8.0.0" } }, - "signal-exit": { + "node_modules/signal-exit": { "version": "3.0.7", "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", "dev": true }, - "sisteransi": { + "node_modules/sisteransi": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz", "integrity": "sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==", "dev": true }, - "slash": { + "node_modules/slash": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", - "dev": true + "dev": true, + "engines": { + "node": ">=8" + } }, - "source-map": { + "node_modules/source-map": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true + "dev": true, + "engines": { + "node": ">=0.10.0" + } }, - "source-map-support": { + "node_modules/source-map-support": { "version": "0.5.13", "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.13.tgz", "integrity": "sha512-SHSKFHadjVA5oR4PPqhtAVdcBWwRYVd6g6cAXnIbRiIwc2EhPrTuKUBdSLvlEKyIP3GCf89fltvcZiP9MMFA1w==", "dev": true, - "requires": { + "dependencies": { "buffer-from": "^1.0.0", "source-map": "^0.6.0" } }, - "sprintf-js": { + "node_modules/sprintf-js": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==", "dev": true }, - "stack-utils": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/stack-utils/-/stack-utils-2.0.5.tgz", - "integrity": "sha512-xrQcmYhOsn/1kX+Vraq+7j4oE2j/6BFscZ0etmYg81xuM8Gq0022Pxb8+IqgOFUIaxHs0KaSb7T1+OegiNrNFA==", + "node_modules/stack-utils": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/stack-utils/-/stack-utils-2.0.6.tgz", + "integrity": "sha512-XlkWvfIm6RmsWtNJx+uqtKLS8eqFbxUg0ZzLXqY0caEy9l7hruX8IpiDnjsLavoBgqCCR71TqWO8MaXYheJ3RQ==", "dev": true, - "requires": { + "dependencies": { "escape-string-regexp": "^2.0.0" }, - "dependencies": { - "escape-string-regexp": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz", - "integrity": "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==", - "dev": true - } + "engines": { + "node": ">=10" + } + }, + "node_modules/stack-utils/node_modules/escape-string-regexp": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz", + "integrity": "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==", + "dev": true, + "engines": { + "node": ">=8" } }, - "string-length": { + "node_modules/string-length": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/string-length/-/string-length-4.0.2.tgz", "integrity": "sha512-+l6rNN5fYHNhZZy41RXsYptCjA2Igmq4EG7kZAYFQI1E1VTXarr6ZPXBg6eq7Y6eK4FEhY6AJlyuFIb/v/S0VQ==", "dev": true, - "requires": { + "dependencies": { "char-regex": "^1.0.2", "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" } }, - "string-width": { + "node_modules/string-width": { "version": "4.2.3", "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", "dev": true, - "requires": { + "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" } }, - "strip-ansi": { + "node_modules/strip-ansi": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", "dev": true, - "requires": { + "dependencies": { "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" } }, - "strip-bom": { + "node_modules/strip-bom": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-4.0.0.tgz", "integrity": "sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w==", - "dev": true + "dev": true, + "engines": { + "node": ">=8" + } }, - "strip-final-newline": { + "node_modules/strip-final-newline": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz", "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==", - "dev": true + "dev": true, + "engines": { + "node": ">=6" + } }, - "strip-json-comments": { + "node_modules/strip-json-comments": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", - "dev": true + "dev": true, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } }, - "supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", "dev": true, - "requires": { - "has-flag": "^3.0.0" + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" } }, - "supports-hyperlinks": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/supports-hyperlinks/-/supports-hyperlinks-2.2.0.tgz", - "integrity": "sha512-6sXEzV5+I5j8Bmq9/vUphGRM/RJNT9SCURJLjwfOg51heRtguGWDzcaBlgAzKhQa0EVNpPEKzQuBwZ8S8WaCeQ==", + "node_modules/supports-hyperlinks": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/supports-hyperlinks/-/supports-hyperlinks-2.3.0.tgz", + "integrity": "sha512-RpsAZlpWcDwOPQA22aCH4J0t7L8JmAvsCxfOSEwm7cQs3LshN36QaTkwd70DnBOXDWGssw2eUoc8CaRWT0XunA==", "dev": true, - "requires": { + "dependencies": { "has-flag": "^4.0.0", "supports-color": "^7.0.0" }, - "dependencies": { - "has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true - }, - "supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "requires": { - "has-flag": "^4.0.0" - } - } + "engines": { + "node": ">=8" } }, - "supports-preserve-symlinks-flag": { + "node_modules/supports-preserve-symlinks-flag": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", - "dev": true + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } }, - "terminal-link": { + "node_modules/terminal-link": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/terminal-link/-/terminal-link-2.1.1.tgz", "integrity": "sha512-un0FmiRUQNr5PJqy9kP7c40F5BOfpGlYTrxonDChEZB7pzZxRNp/bt+ymiy9/npwXya9KH99nJ/GXFIiUkYGFQ==", "dev": true, - "requires": { + "dependencies": { "ansi-escapes": "^4.2.1", "supports-hyperlinks": "^2.0.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "test-exclude": { + "node_modules/test-exclude": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-6.0.0.tgz", "integrity": "sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==", "dev": true, - "requires": { + "dependencies": { "@istanbuljs/schema": "^0.1.2", "glob": "^7.1.4", "minimatch": "^3.0.4" }, + "engines": { + "node": ">=8" + } + }, + "node_modules/test-exclude/node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "dev": true, "dependencies": { - "glob": { - "version": "7.2.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", - "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", - "dev": true, - "requires": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.1.1", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - } - } + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, - "text-table": { + "node_modules/text-table": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", - "integrity": "sha1-f17oI66AUgfACvLfSoTsP8+lcLQ=", + "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==", "dev": true }, - "tmpl": { + "node_modules/tmpl": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.5.tgz", "integrity": "sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw==", "dev": true }, - "to-fast-properties": { + "node_modules/to-fast-properties": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", "integrity": "sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==", - "dev": true + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/tr46": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", + "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==" + }, + "node_modules/ts-jest": { + "version": "28.0.8", + "resolved": "https://registry.npmjs.org/ts-jest/-/ts-jest-28.0.8.tgz", + "integrity": "sha512-5FaG0lXmRPzApix8oFG8RKjAz4ehtm8yMKOTy5HX3fY6W8kmvOrmcY0hKDElW52FJov+clhUbrKAqofnj4mXTg==", + "dev": true, + "dependencies": { + "bs-logger": "0.x", + "fast-json-stable-stringify": "2.x", + "jest-util": "^28.0.0", + "json5": "^2.2.1", + "lodash.memoize": "4.x", + "make-error": "1.x", + "semver": "7.x", + "yargs-parser": "^21.0.1" + }, + "bin": { + "ts-jest": "cli.js" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" + }, + "peerDependencies": { + "@babel/core": ">=7.0.0-beta.0 <8", + "@jest/types": "^28.0.0", + "babel-jest": "^28.0.0", + "jest": "^28.0.0", + "typescript": ">=4.3" + }, + "peerDependenciesMeta": { + "@babel/core": { + "optional": true + }, + "@jest/types": { + "optional": true + }, + "babel-jest": { + "optional": true + }, + "esbuild": { + "optional": true + } + } }, - "to-regex-range": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", - "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "node_modules/ts-jest/node_modules/jest-util": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-28.1.3.tgz", + "integrity": "sha512-XdqfpHwpcSRko/C35uLYFM2emRAltIIKZiJ9eAmhjsj0CqZMa0p1ib0R5fWIqGhn1a103DebTbpqIaP1qCQ6tQ==", "dev": true, - "requires": { - "is-number": "^7.0.0" + "dependencies": { + "@jest/types": "^28.1.3", + "@types/node": "*", + "chalk": "^4.0.0", + "ci-info": "^3.2.0", + "graceful-fs": "^4.2.9", + "picomatch": "^2.2.3" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" } }, - "ts-jest": { - "version": "28.0.8", - "resolved": "https://registry.npmjs.org/ts-jest/-/ts-jest-28.0.8.tgz", - "integrity": "sha512-5FaG0lXmRPzApix8oFG8RKjAz4ehtm8yMKOTy5HX3fY6W8kmvOrmcY0hKDElW52FJov+clhUbrKAqofnj4mXTg==", + "node_modules/tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==" + }, + "node_modules/tsutils": { + "version": "3.21.0", + "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-3.21.0.tgz", + "integrity": "sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==", "dev": true, - "requires": { - "bs-logger": "0.x", - "fast-json-stable-stringify": "2.x", - "jest-util": "^28.0.0", - "json5": "^2.2.1", - "lodash.memoize": "4.x", - "make-error": "1.x", - "semver": "7.x", - "yargs-parser": "^21.0.1" - }, "dependencies": { - "semver": { - "version": "7.3.5", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", - "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", - "dev": true, - "requires": { - "lru-cache": "^6.0.0" - } - } + "tslib": "^1.8.1" + }, + "engines": { + "node": ">= 6" + }, + "peerDependencies": { + "typescript": ">=2.8.0 || >= 3.2.0-dev || >= 3.3.0-dev || >= 3.4.0-dev || >= 3.5.0-dev || >= 3.6.0-dev || >= 3.6.0-beta || >= 3.7.0-dev || >= 3.7.0-beta" } }, - "tslib": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.1.tgz", - "integrity": "sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw==" + "node_modules/tsutils/node_modules/tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", + "dev": true }, - "type-check": { + "node_modules/type-check": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", "dev": true, - "requires": { + "dependencies": { "prelude-ls": "^1.2.1" + }, + "engines": { + "node": ">= 0.8.0" } }, - "type-detect": { + "node_modules/type-detect": { "version": "4.0.8", "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==", - "dev": true + "dev": true, + "engines": { + "node": ">=4" + } }, - "type-fest": { - "version": "0.21.3", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz", - "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==", - "dev": true + "node_modules/type-fest": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", + "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } }, - "typedoc": { + "node_modules/typedoc": { "version": "0.24.8", "resolved": "https://registry.npmjs.org/typedoc/-/typedoc-0.24.8.tgz", "integrity": "sha512-ahJ6Cpcvxwaxfu4KtjA8qZNqS43wYt6JL27wYiIgl1vd38WW/KWX11YuAeZhuz9v+ttrutSsgK+XO1CjL1kA3w==", "dev": true, - "requires": { + "dependencies": { "lunr": "^2.3.9", "marked": "^4.3.0", "minimatch": "^9.0.0", "shiki": "^0.14.1" }, - "dependencies": { - "brace-expansion": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", - "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", - "dev": true, - "requires": { - "balanced-match": "^1.0.0" - } - }, - "minimatch": { - "version": "9.0.3", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.3.tgz", - "integrity": "sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==", - "dev": true, - "requires": { - "brace-expansion": "^2.0.1" - } - } + "bin": { + "typedoc": "bin/typedoc" + }, + "engines": { + "node": ">= 14.14" + }, + "peerDependencies": { + "typescript": "4.6.x || 4.7.x || 4.8.x || 4.9.x || 5.0.x || 5.1.x" } }, - "typedoc-plugin-markdown": { - "version": "3.15.3", - "resolved": "https://registry.npmjs.org/typedoc-plugin-markdown/-/typedoc-plugin-markdown-3.15.3.tgz", - "integrity": "sha512-idntFYu3vfaY3eaD+w9DeRd0PmNGqGuNLKihPU9poxFGnATJYGn9dPtEhn2QrTdishFMg7jPXAhos+2T6YCWRQ==", + "node_modules/typedoc-plugin-markdown": { + "version": "3.17.1", + "resolved": "https://registry.npmjs.org/typedoc-plugin-markdown/-/typedoc-plugin-markdown-3.17.1.tgz", + "integrity": "sha512-QzdU3fj0Kzw2XSdoL15ExLASt2WPqD7FbLeaqwT70+XjKyTshBnUlQA5nNREO1C2P8Uen0CDjsBLMsCQ+zd0lw==", "dev": true, - "requires": { + "dependencies": { "handlebars": "^4.7.7" + }, + "peerDependencies": { + "typedoc": ">=0.24.0" } }, - "typedoc-plugin-merge-modules": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/typedoc-plugin-merge-modules/-/typedoc-plugin-merge-modules-5.0.1.tgz", - "integrity": "sha512-7fiMYDUaeslsGSFDevw+azhD0dFJce0h2g5UuQ8zXljoky+YfmzoNkoTCx+KWaNJo6rz2DzaD2feVJyUhvUegg==", + "node_modules/typedoc-plugin-merge-modules": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/typedoc-plugin-merge-modules/-/typedoc-plugin-merge-modules-5.1.0.tgz", + "integrity": "sha512-jXH27L/wlxFjErgBXleh3opVgjVTXFEuBo68Yfl18S9Oh/IqxK6NV94jlEJ9hl4TXc9Zm2l7Rfk41CEkcCyvFQ==", + "dev": true, + "peerDependencies": { + "typedoc": "0.24.x || 0.25.x" + } + }, + "node_modules/typedoc/node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/typedoc/node_modules/minimatch": { + "version": "9.0.3", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.3.tgz", + "integrity": "sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==", "dev": true, - "requires": {} + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } }, - "typescript": { + "node_modules/typescript": { "version": "5.1.6", "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.1.6.tgz", "integrity": "sha512-zaWCozRZ6DLEWAWFrVDz1H6FVXzUSfTy5FUMWsQlU8Ym5JP9eO4xkTIROFCQvhQf61z6O/G6ugw3SgAnvvm+HA==", - "dev": true + "dev": true, + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } }, - "uglify-js": { + "node_modules/uglify-js": { "version": "3.17.4", "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.17.4.tgz", "integrity": "sha512-T9q82TJI9e/C1TAxYvfb16xO120tMVFZrGA3f9/P4424DNu6ypK103y0GPFVa17yotwSyZW5iYXgjYHkGrJW/g==", "dev": true, - "optional": true + "optional": true, + "bin": { + "uglifyjs": "bin/uglifyjs" + }, + "engines": { + "node": ">=0.8.0" + } }, - "universalify": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.0.tgz", - "integrity": "sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ==", + "node_modules/undici-types": { + "version": "5.26.5", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", + "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==", "dev": true }, - "update-browserslist-db": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.5.tgz", - "integrity": "sha512-dteFFpCyvuDdr9S/ff1ISkKt/9YZxKjI9WlRR99c180GaztJtRa/fn18FdxGVKVsnPY7/a/FDN68mcvUmP4U7Q==", + "node_modules/universalify": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", + "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", + "dev": true, + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/update-browserslist-db": { + "version": "1.0.13", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.13.tgz", + "integrity": "sha512-xebP81SNcPuNpPP3uzeW1NYXxI3rxyJzF3pD6sH4jE7o/IX+WtSpwnVU+qIsDPyk0d3hmFQ7mjqc6AtV604hbg==", "dev": true, - "requires": { + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "dependencies": { "escalade": "^3.1.1", "picocolors": "^1.0.0" + }, + "bin": { + "update-browserslist-db": "cli.js" + }, + "peerDependencies": { + "browserslist": ">= 4.21.0" } }, - "uri-js": { + "node_modules/uri-js": { "version": "4.4.1", "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", "dev": true, - "requires": { + "dependencies": { "punycode": "^2.1.0" } }, - "v8-compile-cache": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.3.0.tgz", - "integrity": "sha512-l8lCEmLcLYZh4nbunNZvQCJc5pv7+RCwa8q/LdUx8u7lsWvPDKmpodJAJNwkAhJC//dFY48KuIEmjtd4RViDrA==", - "dev": true - }, - "v8-to-istanbul": { - "version": "9.0.1", - "resolved": "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-9.0.1.tgz", - "integrity": "sha512-74Y4LqY74kLE6IFyIjPtkSTWzUZmj8tdHT9Ii/26dvQ6K9Dl2NbEfj0XgU2sHCtKgt5VupqhlO/5aWuqS+IY1w==", + "node_modules/v8-to-istanbul": { + "version": "9.1.3", + "resolved": "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-9.1.3.tgz", + "integrity": "sha512-9lDD+EVI2fjFsMWXc6dy5JJzBsVTcQ2fVkfBvncZ6xJWG9wtBhOldG+mHkSL0+V1K/xgZz0JDO5UT5hFwHUghg==", "dev": true, - "requires": { + "dependencies": { "@jridgewell/trace-mapping": "^0.3.12", "@types/istanbul-lib-coverage": "^2.0.1", - "convert-source-map": "^1.6.0" + "convert-source-map": "^2.0.0" + }, + "engines": { + "node": ">=10.12.0" } }, - "vscode-oniguruma": { + "node_modules/v8-to-istanbul/node_modules/convert-source-map": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", + "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", + "dev": true + }, + "node_modules/vscode-oniguruma": { "version": "1.7.0", "resolved": "https://registry.npmjs.org/vscode-oniguruma/-/vscode-oniguruma-1.7.0.tgz", "integrity": "sha512-L9WMGRfrjOhgHSdOYgCt/yRMsXzLDJSL7BPrOZt73gU0iWO4mpqzqQzOz5srxqTvMBaR0XZTSrVWo4j55Rc6cA==", "dev": true }, - "vscode-textmate": { + "node_modules/vscode-textmate": { "version": "8.0.0", "resolved": "https://registry.npmjs.org/vscode-textmate/-/vscode-textmate-8.0.0.tgz", "integrity": "sha512-AFbieoL7a5LMqcnOF04ji+rpXadgOXnZsxQr//r83kLPr7biP7am3g9zbaZIaBGwBRWeSvoMD4mgPdX3e4NWBg==", "dev": true }, - "walker": { + "node_modules/walker": { "version": "1.0.8", "resolved": "https://registry.npmjs.org/walker/-/walker-1.0.8.tgz", "integrity": "sha512-ts/8E8l5b7kY0vlWLewOkDXMmPdLcVV4GmOQLyxuSswIJsweeFZtAsMF7k1Nszz+TYBQrlYRmzOnr398y1JemQ==", "dev": true, - "requires": { + "dependencies": { "makeerror": "1.0.12" } }, - "webgl-constants": { + "node_modules/webgl-constants": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/webgl-constants/-/webgl-constants-1.1.1.tgz", "integrity": "sha512-LkBXKjU5r9vAW7Gcu3T5u+5cvSvh5WwINdr0C+9jpzVB41cjQAP5ePArDtk/WHYdVj0GefCgM73BA7FlIiNtdg==" }, - "whatwg-fetch": { - "version": "3.6.2", - "resolved": "https://registry.npmjs.org/whatwg-fetch/-/whatwg-fetch-3.6.2.tgz", - "integrity": "sha512-bJlen0FcuU/0EMLrdbJ7zOnW6ITZLrZMIarMUVmdKtsGvZna8vxKYaexICWPfZ8qwf9fzNq+UEIZrnSaApt6RA==" + "node_modules/webidl-conversions": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", + "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==" + }, + "node_modules/whatwg-fetch": { + "version": "3.6.19", + "resolved": "https://registry.npmjs.org/whatwg-fetch/-/whatwg-fetch-3.6.19.tgz", + "integrity": "sha512-d67JP4dHSbm2TrpFj8AbO8DnL1JXL5J9u0Kq2xW6d0TFDbCA3Muhdt8orXC22utleTVj7Prqt82baN6RBvnEgw==" + }, + "node_modules/whatwg-url": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", + "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", + "dependencies": { + "tr46": "~0.0.3", + "webidl-conversions": "^3.0.0" + } }, - "which": { + "node_modules/which": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", "dev": true, - "requires": { + "dependencies": { "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" } }, - "word-wrap": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz", - "integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==", - "dev": true - }, - "wordwrap": { + "node_modules/wordwrap": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz", "integrity": "sha512-gvVzJFlPycKc5dZN4yPkP8w7Dc37BtP1yczEneOb4uq34pXZcvrtRTmWV8W+Ume+XCxKgbjM+nevkyFPMybd4Q==", "dev": true }, - "wrap-ansi": { + "node_modules/wrap-ansi": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", "dev": true, - "requires": { + "dependencies": { "ansi-styles": "^4.0.0", "string-width": "^4.1.0", "strip-ansi": "^6.0.0" }, - "dependencies": { - "ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "requires": { - "color-convert": "^2.0.1" - } - }, - "color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "requires": { - "color-name": "~1.1.4" - } - }, - "color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - } + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" } }, - "wrappy": { + "node_modules/wrappy": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", "dev": true }, - "write-file-atomic": { + "node_modules/write-file-atomic": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-4.0.2.tgz", "integrity": "sha512-7KxauUdBmSdWnmpaGFg+ppNjKF8uNLry8LyzjauQDOVONfFLNKrKvQOxZ/VuTIcS/gge/YNahf5RIIQWTSarlg==", "dev": true, - "requires": { + "dependencies": { "imurmurhash": "^0.1.4", "signal-exit": "^3.0.7" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" } }, - "y18n": { + "node_modules/y18n": { "version": "5.0.8", "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", - "dev": true + "dev": true, + "engines": { + "node": ">=10" + } }, - "yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "node_modules/yallist": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", "dev": true }, - "yargs": { - "version": "17.5.1", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.5.1.tgz", - "integrity": "sha512-t6YAJcxDkNX7NFYiVtKvWUz8l+PaKTLiL63mJYWR2GnHq2gjEWISzsLp9wg3aY36dY1j+gfIEL3pIF+XlJJfbA==", + "node_modules/yargs": { + "version": "17.7.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", + "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", "dev": true, - "requires": { - "cliui": "^7.0.2", + "dependencies": { + "cliui": "^8.0.1", "escalade": "^3.1.1", "get-caller-file": "^2.0.5", "require-directory": "^2.1.1", "string-width": "^4.2.3", "y18n": "^5.0.5", - "yargs-parser": "^21.0.0" + "yargs-parser": "^21.1.1" + }, + "engines": { + "node": ">=12" } }, - "yargs-parser": { + "node_modules/yargs-parser": { "version": "21.1.1", "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", - "dev": true + "dev": true, + "engines": { + "node": ">=12" + } }, - "yocto-queue": { + "node_modules/yocto-queue": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", - "dev": true + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } } } } diff --git a/package.json b/package.json index 33d4096bc4..ac4a5736e9 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "o1js", "description": "TypeScript framework for zk-SNARKs and zkApps", - "version": "0.13.1", + "version": "0.15.1", "license": "Apache-2.0", "homepage": "https://github.com/o1-labs/o1js/", "keywords": [ @@ -42,36 +42,34 @@ "node": ">=16.4.0" }, "scripts": { - "type-check": "tsc --noEmit", - "dev": "npx tsc -p tsconfig.node.json && node src/build/copy-to-dist.js", + "dev": "npx tsc -p tsconfig.test.json && node src/build/copy-to-dist.js", "build": "node src/build/copy-artifacts.js && rimraf ./dist/node && npm run dev && node src/build/buildNode.js", "build:bindings": "./src/bindings/scripts/build-snarkyjs-node.sh", "build:update-bindings": "./src/bindings/scripts/update-snarkyjs-bindings.sh", "build:wasm": "./src/bindings/scripts/update-wasm-and-types.sh", - "build:test": "npx tsc -p tsconfig.test.json && cp src/snarky.d.ts dist/node/snarky.d.ts", - "build:node": "npm run build", "build:web": "rimraf ./dist/web && node src/build/buildWeb.js", - "build:examples": "rimraf ./dist/examples && npx tsc -p tsconfig.examples.json || exit 0", - "build:docs": "npx typedoc", - "serve:web": "cp src/bindings/compiled/web_bindings/server.js src/bindings/compiled/web_bindings/index.html src/examples/simple_zkapp.js dist/web && node dist/web/server.js", + "build:examples": "npm run build && rimraf ./dist/examples && npx tsc -p tsconfig.examples.json", + "build:docs": "npx typedoc --tsconfig ./tsconfig.web.json", "prepublish:web": "NODE_ENV=production node src/build/buildWeb.js", - "prepublish:node": "npm run build && NODE_ENV=production node src/build/buildNode.js", + "prepublish:node": "node src/build/copy-artifacts.js && rimraf ./dist/node && npx tsc -p tsconfig.node.json && node src/build/copy-to-dist.js && NODE_ENV=production node src/build/buildNode.js", "prepublishOnly": "npm run prepublish:web && npm run prepublish:node", - "dump-vks": "./run src/examples/vk_regression.ts --bundle --dump ./src/examples/regression_test.json", + "dump-vks": "./run tests/vk-regression/vk-regression.ts --bundle --dump", "format": "prettier --write --ignore-unknown **/*", - "test": "./run-jest-tests.sh", "clean": "rimraf ./dist && rimraf ./src/bindings/compiled/_node_bindings", "clean-all": "npm run clean && rimraf ./tests/report && rimraf ./tests/test-artifacts", + "test": "./run-jest-tests.sh", "test:integration": "./run-integration-tests.sh", "test:unit": "./run-unit-tests.sh", "test:e2e": "rimraf ./tests/report && rimraf ./tests/test-artifacts && npx playwright test", - "e2e:prepare-server": "npm run build:examples && (cp -rf dist/examples dist/web || :) && node src/build/e2eTestsBuildHelper.js && cp -rf src/bindings/compiled/web_bindings/index.html src/bindings/compiled/web_bindings/server.js tests/artifacts/html/*.html tests/artifacts/javascript/*.js dist/web", + "e2e:prepare-server": "npm run build:examples && (cp -rf dist/examples dist/web || :) && node src/build/e2eTestsBuildHelper.js && cp -rf src/examples/plain-html/index.html src/examples/plain-html/server.js tests/artifacts/html/*.html tests/artifacts/javascript/*.js dist/web", "e2e:run-server": "node dist/web/server.js", "e2e:install": "npx playwright install --with-deps", - "e2e:show-report": "npx playwright show-report tests/report" + "e2e:show-report": "npx playwright show-report tests/report", + "update-changelog": "./update-changelog.sh" }, "author": "O(1) Labs", "devDependencies": { + "@noble/hashes": "^1.3.2", "@playwright/test": "^1.25.2", "@types/isomorphic-fetch": "^0.0.36", "@types/jest": "^27.0.0", @@ -96,6 +94,7 @@ }, "dependencies": { "blakejs": "1.2.1", + "cachedir": "^2.4.0", "detect-gpu": "^5.0.5", "isomorphic-fetch": "^3.0.0", "js-sha256": "^0.9.0", diff --git a/run-ci-tests.sh b/run-ci-tests.sh index 4a494ddd62..971b889036 100755 --- a/run-ci-tests.sh +++ b/run-ci-tests.sh @@ -8,6 +8,7 @@ case $TEST_TYPE in ./run src/examples/simple_zkapp.ts --bundle ./run src/examples/zkapps/reducer/reducer_composite.ts --bundle ./run src/examples/zkapps/composability.ts --bundle + ./run src/tests/fake-proof.ts ;; "Voting integration tests") @@ -42,7 +43,7 @@ case $TEST_TYPE in "Verification Key Regression Check") echo "Running Regression checks" - ./run ./src/examples/vk_regression.ts --bundle + ./run ./tests/vk-regression/vk-regression.ts --bundle ;; "CommonJS test") diff --git a/run-debug b/run-debug new file mode 100755 index 0000000000..05642ff1c3 --- /dev/null +++ b/run-debug @@ -0,0 +1 @@ +node --inspect-brk --enable-source-maps src/build/run.js $@ diff --git a/run-minimal-mina-tests.sh b/run-minimal-mina-tests.sh index 01ebacee11..327b5adfd3 100755 --- a/run-minimal-mina-tests.sh +++ b/run-minimal-mina-tests.sh @@ -1,4 +1,6 @@ #!/usr/bin/env bash set -e +npm run dev + ./run src/tests/inductive-proofs-small.ts --bundle diff --git a/run-unit-tests.sh b/run-unit-tests.sh index 349496a06f..08eaf3e4a3 100755 --- a/run-unit-tests.sh +++ b/run-unit-tests.sh @@ -2,8 +2,7 @@ set -e shopt -s globstar # to expand '**' into nested directories./ -# run the build:test -npm run build:test +npm run build # find all unit tests in dist/node and run them # TODO it would be nice to make this work on Mac diff --git a/src/bindings b/src/bindings index 9910553ff7..1db0524c5c 160000 --- a/src/bindings +++ b/src/bindings @@ -1 +1 @@ -Subproject commit 9910553ff7746a8b13100ba6cf34eb7749f03226 +Subproject commit 1db0524c5c7c93f0df9db3fb3bdfa0414bdaf90b diff --git a/src/build/buildExample.js b/src/build/buildExample.js index b58cda4e2c..2f3a37e374 100644 --- a/src/build/buildExample.js +++ b/src/build/buildExample.js @@ -18,6 +18,9 @@ async function buildAndImport(srcPath, { keepFile = false }) { async function build(srcPath, isWeb = false) { let tsConfig = findTsConfig() ?? defaultTsConfig; + // TODO hack because ts.transpileModule doesn't treat module = 'nodenext' correctly + // but `tsc` demands it to be `nodenext` + tsConfig.compilerOptions.module = 'esnext'; let outfile = srcPath.replace('.ts', '.tmp.js'); @@ -46,6 +49,9 @@ async function build(srcPath, isWeb = false) { async function buildOne(srcPath) { let tsConfig = findTsConfig() ?? defaultTsConfig; + // TODO hack because ts.transpileModule doesn't treat module = 'nodenext' correctly + // but `tsc` demands it to be `nodenext` + tsConfig.compilerOptions.module = 'esnext'; let outfile = path.resolve( './dist/node', @@ -74,7 +80,7 @@ const defaultTsConfig = { target: 'esnext', importHelpers: true, strict: true, - moduleResolution: 'node', + moduleResolution: 'nodenext', esModuleInterop: true, skipLibCheck: true, forceConsistentCasingInFileNames: true, diff --git a/src/build/copy-to-dist.js b/src/build/copy-to-dist.js index 96bc96937b..6218414713 100644 --- a/src/build/copy-to-dist.js +++ b/src/build/copy-to-dist.js @@ -2,7 +2,11 @@ import { copyFromTo } from './utils.js'; await copyFromTo( - ['src/snarky.d.ts', 'src/bindings/compiled/_node_bindings'], + [ + 'src/snarky.d.ts', + 'src/bindings/compiled/_node_bindings', + 'src/bindings/compiled/node_bindings/plonk_wasm.d.cts', + ], 'src/', 'dist/node/' ); diff --git a/src/build/jsLayoutToTypes.mjs b/src/build/jsLayoutToTypes.mjs index 2f0b87caef..d76907da9b 100644 --- a/src/build/jsLayoutToTypes.mjs +++ b/src/build/jsLayoutToTypes.mjs @@ -106,11 +106,22 @@ function writeType(typeData, isJson, withTypeMap) { }; } -function writeTsContent(types, isJson, leavesRelPath) { +function writeTsContent({ + jsLayout: types, + isJson, + isProvable, + leavesRelPath, +}) { let output = ''; let dependencies = new Set(); let converters = {}; let exports = new Set(isJson ? [] : ['customTypes']); + + let fromLayout = isProvable ? 'provableFromLayout' : 'signableFromLayout'; + let FromLayout = isProvable ? 'ProvableFromLayout' : 'SignableFromLayout'; + let GenericType = isProvable ? 'GenericProvableExtended' : 'GenericSignable'; + let GeneratedType = isProvable ? 'ProvableExtended' : 'Signable'; + for (let [Type, value] of Object.entries(types)) { let inner = writeType(value, isJson); exports.add(Type); @@ -118,7 +129,7 @@ function writeTsContent(types, isJson, leavesRelPath) { mergeObject(converters, inner.converters); output += `type ${Type} = ${inner.output};\n\n`; if (!isJson) { - output += `let ${Type} = provableFromLayout<${Type}, Json.${Type}>(jsLayout.${Type} as any);\n\n`; + output += `let ${Type} = ${fromLayout}<${Type}, Json.${Type}>(jsLayout.${Type} as any);\n\n`; } } @@ -135,8 +146,8 @@ function writeTsContent(types, isJson, leavesRelPath) { import { ${[...imports].join(', ')} } from '${importPath}'; ${ !isJson - ? "import { GenericProvableExtended } from '../../lib/generic.js';\n" + - "import { ProvableFromLayout, GenericLayout } from '../../lib/from-layout.js';\n" + + ? `import { ${GenericType} } from '../../lib/generic.js';\n` + + `import { ${FromLayout}, GenericLayout } from '../../lib/from-layout.js';\n` + "import * as Json from './transaction-json.js';\n" + "import { jsLayout } from './js-layout.js';\n" : '' @@ -147,7 +158,7 @@ ${ !isJson ? 'export { Json };\n' + `export * from '${leavesRelPath}';\n` + - 'export { provableFromLayout, toJSONEssential, emptyValue, Layout, TypeMap };\n' + `export { ${fromLayout}, toJSONEssential, empty, Layout, TypeMap };\n` : `export * from '${leavesRelPath}';\n` + 'export { TypeMap };\n' } @@ -158,7 +169,7 @@ ${ (!isJson || '') && ` const TypeMap: { - [K in keyof TypeMap]: ProvableExtended; + [K in keyof TypeMap]: ${GeneratedType}; } = { ${[...typeMapKeys].join(', ')} } @@ -168,14 +179,14 @@ const TypeMap: { ${ (!isJson || '') && ` -type ProvableExtended = GenericProvableExtended; +type ${GeneratedType} = ${GenericType}; type Layout = GenericLayout; type CustomTypes = { ${customTypes - .map((c) => `${c.typeName}: ProvableExtended<${c.type}, ${c.jsonType}>;`) + .map((c) => `${c.typeName}: ${GeneratedType}<${c.type}, ${c.jsonType}>;`) .join(' ')} } let customTypes: CustomTypes = { ${customTypeNames.join(', ')} }; -let { provableFromLayout, toJSONEssential, emptyValue } = ProvableFromLayout< +let { ${fromLayout}, toJSONEssential, empty } = ${FromLayout}< TypeMap, Json.TypeMap >(TypeMap, customTypes); @@ -196,25 +207,27 @@ async function writeTsFile(content, relPath) { let genPath = '../../bindings/mina-transaction/gen'; await ensureDir(genPath); -let jsonTypesContent = writeTsContent( +let jsonTypesContent = writeTsContent({ jsLayout, - true, - '../transaction-leaves-json.js' -); + isJson: true, + leavesRelPath: '../transaction-leaves-json.js', +}); await writeTsFile(jsonTypesContent, `${genPath}/transaction-json.ts`); -let jsTypesContent = writeTsContent( +let jsTypesContent = writeTsContent({ jsLayout, - false, - '../transaction-leaves.js' -); + isJson: false, + isProvable: true, + leavesRelPath: '../transaction-leaves.js', +}); await writeTsFile(jsTypesContent, `${genPath}/transaction.ts`); -jsTypesContent = writeTsContent( +jsTypesContent = writeTsContent({ jsLayout, - false, - '../transaction-leaves-bigint.js' -); + isJson: false, + isProvable: false, + leavesRelPath: '../transaction-leaves-bigint.js', +}); await writeTsFile(jsTypesContent, `${genPath}/transaction-bigint.ts`); await writeTsFile( diff --git a/src/examples/README.md b/src/examples/README.md new file mode 100644 index 0000000000..24add4d6b8 --- /dev/null +++ b/src/examples/README.md @@ -0,0 +1,25 @@ +# o1js Examples + +This folder contains many examples for using o1js. Take a look around! + +## Running examples + +You can run most examples using Node.js from the root directory, using the `./run` script: + +``` +./run src/examples/some-example.ts +``` + +Some examples depend on other files in addition to `"o1js"`. For those examples, you need to add the `--bundle` option to bundle them before running: + +``` +./run src/examples/multi-file-example.ts --bundle +``` + +Most of the examples do not depend on Node.js specific APIs, and can also be run in a browser. To do so, use: + +``` +./run-in-browser.js src/examples/web-compatible-example.ts +``` + +After running the above, navigate to http://localhost:8000 and open your browser's developer console to see the example executing. diff --git a/src/examples/api_exploration.ts b/src/examples/api_exploration.ts index a797656d2c..43e2284950 100644 --- a/src/examples/api_exploration.ts +++ b/src/examples/api_exploration.ts @@ -149,8 +149,8 @@ console.assert(!signature.verify(pubKey, msg1).toBoolean()); */ /* You can initialize elements as literals as follows: */ -let g0 = new Group(-1, 2); -let g1 = new Group({ x: -2, y: 2 }); +let g0 = Group.from(-1, 2); +let g1 = new Group({ x: -1, y: 2 }); /* There is also a predefined generator. */ let g2 = Group.generator; diff --git a/src/examples/benchmarks/foreign-field.ts b/src/examples/benchmarks/foreign-field.ts new file mode 100644 index 0000000000..fb32439e0f --- /dev/null +++ b/src/examples/benchmarks/foreign-field.ts @@ -0,0 +1,31 @@ +import { Crypto, Provable, createForeignField } from 'o1js'; + +class ForeignScalar extends createForeignField( + Crypto.CurveParams.Secp256k1.modulus +) {} + +function main() { + let s = Provable.witness( + ForeignScalar.Canonical.provable, + ForeignScalar.random + ); + let t = Provable.witness( + ForeignScalar.Canonical.provable, + ForeignScalar.random + ); + s.mul(t); +} + +console.time('running constant version'); +main(); +console.timeEnd('running constant version'); + +console.time('running witness generation & checks'); +Provable.runAndCheck(main); +console.timeEnd('running witness generation & checks'); + +console.time('creating constraint system'); +let cs = Provable.constraintSystem(main); +console.timeEnd('creating constraint system'); + +console.log(cs.summary()); diff --git a/src/examples/benchmarks/hash-witness.ts b/src/examples/benchmarks/hash-witness.ts index 7e87e96917..08adaf87f7 100644 --- a/src/examples/benchmarks/hash-witness.ts +++ b/src/examples/benchmarks/hash-witness.ts @@ -2,7 +2,7 @@ * benchmark witness generation for an all-mul circuit */ import { Field, Provable, Poseidon } from 'o1js'; -import { tic, toc } from './tic-toc.js'; +import { tic, toc } from '../utils/tic-toc.js'; // parameters let nPermutations = 1 << 12; // 2^12 x 11 rows < 2^16 rows, should just fit in a circuit diff --git a/src/examples/benchmarks/keccak-witness.ts b/src/examples/benchmarks/keccak-witness.ts new file mode 100644 index 0000000000..cedd6982ca --- /dev/null +++ b/src/examples/benchmarks/keccak-witness.ts @@ -0,0 +1,10 @@ +import { Hash, Bytes, Provable } from 'o1js'; + +let Bytes32 = Bytes(32); + +console.time('keccak witness'); +Provable.runAndCheck(() => { + let bytes = Provable.witness(Bytes32.provable, () => Bytes32.random()); + Hash.Keccak256.hash(bytes); +}); +console.timeEnd('keccak witness'); diff --git a/src/examples/benchmarks/mul-web.ts b/src/examples/benchmarks/mul-web.ts index d3b69f7575..43c977ff74 100644 --- a/src/examples/benchmarks/mul-web.ts +++ b/src/examples/benchmarks/mul-web.ts @@ -1,9 +1,8 @@ /** * benchmark a circuit filled with generic gates */ -import { Circuit, Field, Provable, circuitMain, Experimental } from 'o1js'; -import { tic, toc } from './tic-toc.js'; -let { ZkProgram } = Experimental; +import { Circuit, Field, Provable, circuitMain, ZkProgram } from 'o1js'; +import { tic, toc } from '../utils/tic-toc.js'; // parameters let nMuls = (1 << 16) + (1 << 15); // not quite 2^17 generic gates = not quite 2^16 rows @@ -39,6 +38,7 @@ function simpleKimchiCircuit(nMuls: number) { function picklesCircuit(nMuls: number) { return ZkProgram({ + name: 'mul-chain', methods: { run: { privateInputs: [], diff --git a/src/examples/benchmarks/mul-witness.ts b/src/examples/benchmarks/mul-witness.ts index dd4b164929..17e2165fbd 100644 --- a/src/examples/benchmarks/mul-witness.ts +++ b/src/examples/benchmarks/mul-witness.ts @@ -2,7 +2,7 @@ * benchmark witness generation for an all-mul circuit */ import { Field, Provable } from 'o1js'; -import { tic, toc } from './tic-toc.js'; +import { tic, toc } from '../utils/tic-toc.js'; // parameters let nMuls = (1 << 16) + (1 << 15); // not quite 2^17 generic gates = not quite 2^16 rows diff --git a/src/examples/benchmarks/mul.ts b/src/examples/benchmarks/mul.ts index a4fe3ac742..3f92f8c27c 100644 --- a/src/examples/benchmarks/mul.ts +++ b/src/examples/benchmarks/mul.ts @@ -1,9 +1,8 @@ /** * benchmark a circuit filled with generic gates */ -import { Circuit, Field, Provable, circuitMain, Experimental } from 'o1js'; -import { tic, toc } from '../zkapps/tictoc.js'; -let { ZkProgram } = Experimental; +import { Circuit, Field, Provable, circuitMain, ZkProgram } from 'o1js'; +import { tic, toc } from '../utils/tic-toc.node.js'; // parameters let nMuls = (1 << 16) + (1 << 15); // not quite 2^17 generic gates = not quite 2^16 rows @@ -37,6 +36,7 @@ function simpleKimchiCircuit(nMuls: number) { function picklesCircuit(nMuls: number) { return ZkProgram({ + name: 'mul-chain', methods: { run: { privateInputs: [], diff --git a/src/examples/circuit/README.md b/src/examples/circuit/README.md new file mode 100644 index 0000000000..1eecf13b11 --- /dev/null +++ b/src/examples/circuit/README.md @@ -0,0 +1,7 @@ +# `Circuit` examples + +These examples show how to use `Circuit`, which is a simple API to write a single circuit and create proofs for it. + +In contrast to `ZkProgram`, `Circuit` does not pass through Pickles, but creates a proof with Kimchi directly. Therefore, it does not support recursion, but is also much faster. + +Note that `Circuit` proofs are not compatible with Mina zkApps. diff --git a/src/examples/ex00_preimage.ts b/src/examples/circuit/preimage.ts similarity index 72% rename from src/examples/ex00_preimage.ts rename to src/examples/circuit/preimage.ts index 8f225429a8..22b3099225 100644 --- a/src/examples/ex00_preimage.ts +++ b/src/examples/circuit/preimage.ts @@ -1,19 +1,11 @@ -import { - Poseidon, - Field, - Circuit, - circuitMain, - public_, - isReady, -} from 'o1js'; - -/* Exercise 0: - -Public input: a hash value h -Prove: - I know a value x such that hash(x) = h -*/ - +import { Poseidon, Field, Circuit, circuitMain, public_ } from 'o1js'; + +/** + * Public input: a hash value h + * + * Prove: + * I know a value x such that hash(x) = h + */ class Main extends Circuit { @circuitMain static main(preimage: Field, @public_ hash: Field) { @@ -21,8 +13,6 @@ class Main extends Circuit { } } -await isReady; - console.log('generating keypair...'); const kp = await Main.generateKeypair(); diff --git a/src/examples/ex02_root.ts b/src/examples/circuit/root.ts similarity index 66% rename from src/examples/ex02_root.ts rename to src/examples/circuit/root.ts index 2bdaaadfd8..15ab297ee4 100644 --- a/src/examples/ex02_root.ts +++ b/src/examples/circuit/root.ts @@ -1,17 +1,15 @@ -import { Field, Circuit, circuitMain, public_, isReady } from 'o1js'; - -await isReady; - -/* Exercise 2: - -Public input: a field element x -Prove: - I know a value y that is a cube root of x. -*/ - +import { Field, Circuit, circuitMain, public_, Gadgets } from 'o1js'; + +/** + * Public input: a field element x + * + * Prove: + * I know a value y < 2^64 that is a cube root of x. + */ class Main extends Circuit { @circuitMain - static main(y: Field, @public_ x: Field) { + static main(@public_ x: Field, y: Field) { + Gadgets.rangeCheck64(y); let y3 = y.square().mul(y); y3.assertEquals(x); } @@ -24,8 +22,8 @@ console.timeEnd('generating keypair...'); console.log('prove...'); console.time('prove...'); -const x = new Field(8); -const y = new Field(2); +const x = Field(8); +const y = Field(2); const proof = await Main.prove([y], [x], kp); console.timeEnd('prove...'); diff --git a/src/examples/constraint_system.ts b/src/examples/constraint_system.ts index 6acb7fde53..f2da63ad3b 100644 --- a/src/examples/constraint_system.ts +++ b/src/examples/constraint_system.ts @@ -2,7 +2,7 @@ import { Field, Poseidon, Provable } from 'o1js'; let hash = Poseidon.hash([Field(1), Field(-1)]); -let { rows, digest, gates, publicInputSize } = Provable.constraintSystem(() => { +let { rows, digest, publicInputSize, print } = Provable.constraintSystem(() => { let x = Provable.witness(Field, () => Field(1)); let y = Provable.witness(Field, () => Field(-1)); x.add(y).assertEquals(Field(0)); @@ -10,5 +10,5 @@ let { rows, digest, gates, publicInputSize } = Provable.constraintSystem(() => { z.assertEquals(hash); }); -console.log(JSON.stringify(gates)); +print(); console.log({ rows, digest, publicInputSize }); diff --git a/src/examples/crypto/README.md b/src/examples/crypto/README.md new file mode 100644 index 0000000000..c2f913defa --- /dev/null +++ b/src/examples/crypto/README.md @@ -0,0 +1,6 @@ +# Crypto examples + +These examples show how to use some of the crypto primitives that are supported in provable o1js code. + +- Non-native field arithmetic: `foreign-field.ts` +- Non-native ECDSA verification: `ecdsa.ts` diff --git a/src/examples/crypto/ecdsa/ecdsa.ts b/src/examples/crypto/ecdsa/ecdsa.ts new file mode 100644 index 0000000000..45639c41cb --- /dev/null +++ b/src/examples/crypto/ecdsa/ecdsa.ts @@ -0,0 +1,54 @@ +import { + ZkProgram, + Crypto, + createEcdsa, + createForeignCurve, + Bool, + Keccak, + Bytes, +} from 'o1js'; + +export { keccakAndEcdsa, ecdsa, Secp256k1, Ecdsa, Bytes32 }; + +class Secp256k1 extends createForeignCurve(Crypto.CurveParams.Secp256k1) {} +class Scalar extends Secp256k1.Scalar {} +class Ecdsa extends createEcdsa(Secp256k1) {} +class Bytes32 extends Bytes(32) {} + +const keccakAndEcdsa = ZkProgram({ + name: 'ecdsa', + publicInput: Bytes32.provable, + publicOutput: Bool, + + methods: { + verifyEcdsa: { + privateInputs: [Ecdsa.provable, Secp256k1.provable], + method(message: Bytes32, signature: Ecdsa, publicKey: Secp256k1) { + return signature.verify(message, publicKey); + }, + }, + + sha3: { + privateInputs: [], + method(message: Bytes32) { + Keccak.nistSha3(256, message); + return Bool(true); + }, + }, + }, +}); + +const ecdsa = ZkProgram({ + name: 'ecdsa-only', + publicInput: Scalar.provable, + publicOutput: Bool, + + methods: { + verifySignedHash: { + privateInputs: [Ecdsa.provable, Secp256k1.provable], + method(message: Scalar, signature: Ecdsa, publicKey: Secp256k1) { + return signature.verifySignedHash(message, publicKey); + }, + }, + }, +}); diff --git a/src/examples/crypto/ecdsa/run.ts b/src/examples/crypto/ecdsa/run.ts new file mode 100644 index 0000000000..2a497de373 --- /dev/null +++ b/src/examples/crypto/ecdsa/run.ts @@ -0,0 +1,41 @@ +import { Secp256k1, Ecdsa, keccakAndEcdsa, ecdsa, Bytes32 } from './ecdsa.js'; +import assert from 'assert'; + +// create an example ecdsa signature + +let privateKey = Secp256k1.Scalar.random(); +let publicKey = Secp256k1.generator.scale(privateKey); + +let message = Bytes32.fromString("what's up"); + +let signature = Ecdsa.sign(message.toBytes(), privateKey.toBigInt()); + +// investigate the constraint system generated by ECDSA verify + +console.time('ecdsa verify only (build constraint system)'); +let csEcdsa = ecdsa.analyzeMethods().verifySignedHash; +console.timeEnd('ecdsa verify only (build constraint system)'); +console.log(csEcdsa.summary()); + +console.time('keccak only (build constraint system)'); +let csKeccak = keccakAndEcdsa.analyzeMethods().sha3; +console.timeEnd('keccak only (build constraint system)'); +console.log(csKeccak.summary()); + +console.time('keccak + ecdsa verify (build constraint system)'); +let cs = keccakAndEcdsa.analyzeMethods().verifyEcdsa; +console.timeEnd('keccak + ecdsa verify (build constraint system)'); +console.log(cs.summary()); + +// compile and prove + +console.time('keccak + ecdsa verify (compile)'); +await keccakAndEcdsa.compile(); +console.timeEnd('keccak + ecdsa verify (compile)'); + +console.time('keccak + ecdsa verify (prove)'); +let proof = await keccakAndEcdsa.verifyEcdsa(message, signature, publicKey); +console.timeEnd('keccak + ecdsa verify (prove)'); + +proof.publicOutput.assertTrue('signature verifies'); +assert(await keccakAndEcdsa.verify(proof), 'proof verifies'); diff --git a/src/examples/crypto/foreign-field.ts b/src/examples/crypto/foreign-field.ts new file mode 100644 index 0000000000..bffdae7654 --- /dev/null +++ b/src/examples/crypto/foreign-field.ts @@ -0,0 +1,116 @@ +/** + * This example explores the ForeignField API! + * + * We shed light on the subtleties of different variants of foreign field: + * Unreduced, AlmostReduced, and Canonical. + */ +import assert from 'assert'; +import { + createForeignField, + AlmostForeignField, + CanonicalForeignField, + Scalar, + SmartContract, + method, + Provable, + state, + State, +} from 'o1js'; + +// Let's create a small finite field: F_17 + +class SmallField extends createForeignField(17n) {} + +let x = SmallField.from(16); +x.assertEquals(-1); // 16 = -1 (mod 17) +x.mul(x).assertEquals(1); // 16 * 16 = 15 * 17 + 1 = 1 (mod 17) + +// most arithmetic operations return "unreduced" fields, i.e., fields that could be larger than the modulus: + +let z = x.add(x); +assert(z instanceof SmallField.Unreduced); + +// note: "unreduced" doesn't usually mean that the underlying witness is larger than the modulus. +// it just means we haven't _proved_ so.. which means a malicious prover _could_ have managed to make it larger. + +// unreduced fields can be added and subtracted, but not be used in multiplcation: + +z.add(1).sub(x).assertEquals(0); // works + +assert((z as any).mul === undefined); // z.mul() is not defined +assert((z as any).inv === undefined); +assert((z as any).div === undefined); + +// to do multiplication, you need "almost reduced" fields: + +let y: AlmostForeignField = z.assertAlmostReduced(); // adds constraints to prove that z is, in fact, reduced +assert(y instanceof SmallField.AlmostReduced); + +y.mul(y).assertEquals(4); // y.mul() is defined +assert(y.mul(y) instanceof SmallField.Unreduced); // but y.mul() returns an unreduced field again + +y.inv().mul(y).assertEquals(1); // y.inv() is defined (and returns an AlmostReduced field!) + +// to do many multiplications, it's more efficient to reduce fields in batches of 3 elements: +// (in fact, asserting that 3 elements are reduced is almost as cheap as asserting that 1 element is reduced) + +let z1 = y.mul(7); +let z2 = y.add(11); +let z3 = y.sub(13); + +let [z1r, z2r, z3r] = SmallField.assertAlmostReduced(z1, z2, z3); + +z1r.mul(z2r); +z2r.div(z3r); + +// here we get to the reason _why_ we have different variants of foreign fields: +// always proving that they are reduced after every operation would be super inefficient! + +// fields created from constants are already reduced -- in fact, they are _fully reduced_ or "canonical": + +let constant: CanonicalForeignField = SmallField.from(1); +assert(constant instanceof SmallField.Canonical); + +SmallField.from(10000n) satisfies CanonicalForeignField; // works because `from()` takes the input mod p +SmallField.from(-1) satisfies CanonicalForeignField; // works because `from()` takes the input mod p + +// canonical fields are a special case of almost reduced fields at the type level: +constant satisfies AlmostForeignField; +constant.mul(constant); + +// the cheapest way to prove that an existing field element is canonical is to show that it is equal to a constant: + +let u = z.add(x); +let uCanonical = u.assertEquals(-3); +assert(uCanonical instanceof SmallField.Canonical); + +// to use the different variants of foreign fields as smart contract inputs, you might want to create a class for them: +class AlmostSmallField extends SmallField.AlmostReduced {} + +class MyContract extends SmartContract { + @state(AlmostSmallField.provable) x = State(); + + @method myMethod(y: AlmostSmallField) { + let x = y.mul(2); + Provable.log(x); + this.x.set(x.assertAlmostReduced()); + } +} +MyContract.analyzeMethods(); // works + +// btw - we support any finite field up to 259 bits. for example, the seqp256k1 base field: +let Fseqp256k1 = createForeignField((1n << 256n) - (1n << 32n) - 0b1111010001n); + +// or the Pallas scalar field, to do arithmetic on scalars: +let Fq = createForeignField(Scalar.ORDER); + +// also, you can use a number that's not a prime. +// for example, you might want to create a UInt256 type: +let UInt256 = createForeignField(1n << 256n); + +// and now you can do arithmetic modulo 2^256! +let a = UInt256.from(1n << 255n); +let b = UInt256.from((1n << 255n) + 7n); +a.add(b).assertEquals(7); + +// have fun proving finite field algorithms! diff --git a/src/examples/deploy/.gitignore b/src/examples/deploy/.gitignore deleted file mode 100644 index 378eac25d3..0000000000 --- a/src/examples/deploy/.gitignore +++ /dev/null @@ -1 +0,0 @@ -build diff --git a/src/examples/deploy/compile.ts b/src/examples/deploy/compile.ts deleted file mode 100644 index 5cf9dd9025..0000000000 --- a/src/examples/deploy/compile.ts +++ /dev/null @@ -1,26 +0,0 @@ -import { isReady, PrivateKey, shutdown } from 'o1js'; -import SimpleZkapp from './simple_zkapp.js'; -import { writeFileSync, mkdirSync, existsSync } from 'node:fs'; -import { fileURLToPath } from 'node:url'; -import { dirname } from 'node:path'; - -await isReady; - -let zkappKey = PrivateKey.random(); -let zkappAddress = zkappKey.toPublicKey(); - -let { verificationKey } = await SimpleZkapp.compile(); -storeArtifact(SimpleZkapp, { verificationKey }); - -shutdown(); - -function storeArtifact(SmartContract: Function, json: unknown) { - let thisFolder = dirname(fileURLToPath(import.meta.url)); - if (!existsSync(`${thisFolder}/build`)) { - mkdirSync(`${thisFolder}/build`); - } - writeFileSync( - `${thisFolder}/build/${SmartContract.name}.json`, - JSON.stringify(json) - ); -} diff --git a/src/examples/deploy/deploy.ts b/src/examples/deploy/deploy.ts deleted file mode 100644 index e820d4a020..0000000000 --- a/src/examples/deploy/deploy.ts +++ /dev/null @@ -1,36 +0,0 @@ -import { isReady, PrivateKey, shutdown, Mina } from 'o1js'; -import SimpleZkapp from './simple_zkapp.js'; -import { readFileSync, existsSync } from 'node:fs'; -import { fileURLToPath } from 'node:url'; -import { dirname } from 'node:path'; - -await isReady; - -// TODO: get keys from somewhere else; for now we assume the account is already funded -let zkappKey = PrivateKey.random(); -let zkappAddress = zkappKey.toPublicKey(); - -// read verification key from disk -let artifact = readArtifact(SimpleZkapp); -if (artifact === undefined) - throw Error('No verification key found! Use compile.ts first'); -let { verificationKey } = artifact; - -// produce and log the transaction json; the fee payer is a dummy which has to be added later, by the signing logic -let tx = await Mina.transaction(() => { - new SimpleZkapp(zkappAddress).deploy({ verificationKey }); -}); -let transactionJson = tx.sign([zkappKey]).toJSON(); - -console.log(transactionJson); - -shutdown(); - -function readArtifact(SmartContract: Function) { - let thisFolder = dirname(fileURLToPath(import.meta.url)); - let jsonFile = `${thisFolder}/build/${SmartContract.name}.json`; - if (!existsSync(`${thisFolder}/build`) || !existsSync(jsonFile)) { - return undefined; - } - return JSON.parse(readFileSync(jsonFile, 'utf-8')); -} diff --git a/src/examples/deploy/simple_zkapp.ts b/src/examples/deploy/simple_zkapp.ts deleted file mode 100644 index 496a47cc9d..0000000000 --- a/src/examples/deploy/simple_zkapp.ts +++ /dev/null @@ -1,19 +0,0 @@ -import { Field, state, State, method, SmartContract } from 'o1js'; - -export { SimpleZkapp as default }; - -const initialState = 10; - -class SimpleZkapp extends SmartContract { - @state(Field) x = State(); - - init() { - super.init(); - this.x.set(Field(initialState)); - } - - @method update(y: Field) { - let x = this.x.get(); - this.x.set(x.add(y)); - } -} diff --git a/src/examples/ex01_small_preimage.ts b/src/examples/ex01_small_preimage.ts deleted file mode 100644 index cfdbd18ce4..0000000000 --- a/src/examples/ex01_small_preimage.ts +++ /dev/null @@ -1,32 +0,0 @@ -import { - Poseidon, - Field, - Circuit, - circuitMain, - public_, - isReady, -} from 'o1js'; - -await isReady; - -/* Exercise 1: - -Public input: a hash value h -Prove: - I know a value x < 2^32 such that hash(x) = h -*/ - -class Main extends Circuit { - @circuitMain - static main(preimage: Field, @public_ hash: Field) { - preimage.toBits(32); - Poseidon.hash([preimage]).assertEquals(hash); - } -} - -const kp = await Main.generateKeypair(); - -const preimage = Field.fromBits(Field.random().toBits().slice(0, 32)); -const hash = Poseidon.hash([preimage]); -const pi = await Main.prove([preimage], [hash], kp); -console.log('proof', pi); diff --git a/src/examples/internals/README.md b/src/examples/internals/README.md new file mode 100644 index 0000000000..ddc6d96fe6 --- /dev/null +++ b/src/examples/internals/README.md @@ -0,0 +1,5 @@ +# Examples: Internals + +This folder contains examples which highlight inner workings and less-documented behaviours of o1js. + +These examples might be useful for advanced users and contributors. diff --git a/src/examples/internals/advanced-provable-types.ts b/src/examples/internals/advanced-provable-types.ts new file mode 100644 index 0000000000..dc3b95b530 --- /dev/null +++ b/src/examples/internals/advanced-provable-types.ts @@ -0,0 +1,167 @@ +/** + * This example explains some inner workings of provable types at the hand of a particularly + * complex type: `AccountUpdate`. + */ +import assert from 'assert/strict'; +import { + AccountUpdate, + PrivateKey, + Provable, + Empty, + ProvableExtended, +} from 'o1js'; +import { expect } from 'expect'; + +/** + * Example of a complex provable type: `AccountUpdate` + */ +AccountUpdate satisfies Provable; +console.log(`an account update has ${AccountUpdate.sizeInFields()} fields`); + +let address = PrivateKey.random().toPublicKey(); +let accountUpdate = AccountUpdate.defaultAccountUpdate(address); +accountUpdate.body.callDepth = 5; +accountUpdate.lazyAuthorization = { + kind: 'lazy-signature', + privateKey: PrivateKey.random(), +}; + +/** + * Every provable type can be disassembled into its provable/in-circuit part (fields) + * and a non-provable part (auxiliary). + * + * The parts can be assembled back together to create a new object which is deeply equal to the old one. + */ +let fields = AccountUpdate.toFields(accountUpdate); +let aux = AccountUpdate.toAuxiliary(accountUpdate); +let accountUpdateRecovered = AccountUpdate.fromFields(fields, aux); +expect(accountUpdateRecovered.body).toEqual(accountUpdate.body); +expect(accountUpdateRecovered.lazyAuthorization).toEqual( + accountUpdate.lazyAuthorization +); + +/** + * Provable types which implement `ProvableExtended` can also be serialized to/from JSON. + * + * However, `AccountUpdate` specifically is a wrapper around an actual, core provable extended type. + * It has additional properties, like lazySignature, which are not part of the JSON representation + * and therefore aren't recovered. + */ +AccountUpdate satisfies ProvableExtended; +let json = AccountUpdate.toJSON(accountUpdate); +accountUpdateRecovered = AccountUpdate.fromJSON(json); +expect(accountUpdateRecovered.body).toEqual(accountUpdate.body); +expect(accountUpdateRecovered.lazyAuthorization).not.toEqual( + accountUpdate.lazyAuthorization +); + +/** + * Provable.runAndCheck() can be used to run a circuit in "prover mode". + * That means + * -) witness() and asProver() blocks are executed + * -) constraints are checked; failing assertions throw an error + */ +Provable.runAndCheck(() => { + /** + * Provable.witness() is used to introduce all values to the circuit which are not hard-coded constants. + * + * Under the hood, it disassembles and reassembles the provable type with toFields(), toAuxiliary() and fromFields(). + */ + let accountUpdateWitness = Provable.witness( + AccountUpdate, + () => accountUpdate + ); + + /** + * The witness is "provably equal" to the original. + * (this, under hood, calls assertEqual on all fields returned by .toFields()). + */ + Provable.assertEqual(AccountUpdate, accountUpdateWitness, accountUpdate); + + /** + * Auxiliary parts are also recovered in the witness. + * Note, though, that this can't be enforced as part of a proof! + */ + assert( + accountUpdateWitness.body.callDepth === 5, + 'when witness block is executed, witness() recreates auxiliary parts of provable type' + ); + Provable.assertEqual( + PrivateKey, + (accountUpdateWitness.lazyAuthorization as any).privateKey, + (accountUpdate.lazyAuthorization as any).privateKey + ); +}); + +/** + * Provable.constraintSystem() runs the circuit in "compile mode". + * -) witness() and asProver() blocks are not executed + * -) fields don't have actual values attached to them; they're purely abstract variables + * -) constraints are not checked + */ +let result = Provable.constraintSystem(() => { + /** + * In compile mode, witness() returns + * - abstract variables without values for fields + * - dummy data for auxiliary + */ + let accountUpdateWitness = Provable.witness( + AccountUpdate, + (): AccountUpdate => { + throw 'not executed anyway'; + } + ); + + /** + * Dummy data can take a different form depending on the provable type, + * but in most cases it's "all-zeroes" + */ + assert( + accountUpdateWitness.body.callDepth === 0, + 'when witness block is not executed, witness() returns dummy data' + ); + Provable.assertEqual(AccountUpdate, accountUpdateWitness, accountUpdate); +}); + +/** + * Provable.constraintSystem() is a great way to investigate how many constraints operations take. + * + * Note that even just witnessing stuff takes constraints, for provable types which define a check() method. + * Bools are proved to be 0 or 1, UInt64 is proved to be within [0, 2^64), etc. + */ +console.log( + `witnessing an account update and comparing it to another one creates ${result.rows} rows` +); + +/** + * For account updates specifically, we typically don't want all the subfield checks. That's because + * account updates are usually tied the _public input_. The public input is checked on the verifier side + * already, including the well-formedness of its parts, so there's no need to include that in the proof. + * + * This is why we have this custom way of witnessing account updates, with the `skipCheck` option. + */ +result = Provable.constraintSystem(() => { + let { accountUpdate: accountUpdateWitness } = AccountUpdate.witness( + Empty, + () => ({ accountUpdate, result: undefined }), + { skipCheck: true } + ); + Provable.assertEqual(AccountUpdate, accountUpdateWitness, accountUpdate); +}); +console.log( + `without all the checks on subfields, witnessing and comparing only creates ${result.rows} rows` +); + +/** + * To relate an account update to the hash which is the public input, we need to perform the hash in-circuit. + * This is takes several 100 constraints, and is basically the minimal size of a zkApp method. + */ +result = Provable.constraintSystem(() => { + let { accountUpdate: accountUpdateWitness } = AccountUpdate.witness( + Empty, + () => ({ accountUpdate, result: undefined }), + { skipCheck: true } + ); + accountUpdateWitness.hash(); +}); +console.log(`hashing a witnessed account update creates ${result.rows} rows`); diff --git a/src/examples/nullifier.ts b/src/examples/nullifier.ts index e90a7c50cf..8f3ea81001 100644 --- a/src/examples/nullifier.ts +++ b/src/examples/nullifier.ts @@ -18,8 +18,8 @@ class PayoutOnlyOnce extends SmartContract { @state(Field) nullifierMessage = State(); @method payout(nullifier: Nullifier) { - let nullifierRoot = this.nullifierRoot.getAndAssertEquals(); - let nullifierMessage = this.nullifierMessage.getAndAssertEquals(); + let nullifierRoot = this.nullifierRoot.getAndRequireEquals(); + let nullifierMessage = this.nullifierMessage.getAndRequireEquals(); // verify the nullifier nullifier.verify([nullifierMessage]); @@ -38,7 +38,7 @@ class PayoutOnlyOnce extends SmartContract { this.nullifierRoot.set(newRoot); // we pay out a reward - let balance = this.account.balance.getAndAssertEquals(); + let balance = this.account.balance.getAndRequireEquals(); let halfBalance = balance.div(2); // finally, we send the payout to the public key associated with the nullifier diff --git a/src/examples/party-witness.ts b/src/examples/party-witness.ts deleted file mode 100644 index a33bdac352..0000000000 --- a/src/examples/party-witness.ts +++ /dev/null @@ -1,74 +0,0 @@ -import { - AccountUpdate, - PrivateKey, - Circuit, - provable, - isReady, - Provable, -} from 'o1js'; - -await isReady; - -let address = PrivateKey.random().toPublicKey(); - -let accountUpdate = AccountUpdate.defaultAccountUpdate(address); -accountUpdate.body.callDepth = 5; -accountUpdate.lazyAuthorization = { - kind: 'lazy-signature', - privateKey: PrivateKey.random(), -}; - -let fields = AccountUpdate.toFields(accountUpdate); -let aux = AccountUpdate.toAuxiliary(accountUpdate); - -let accountUpdateRaw = AccountUpdate.fromFields(fields, aux); -let json = AccountUpdate.toJSON(accountUpdateRaw); - -if (address.toBase58() !== json.body.publicKey) throw Error('fail'); - -let Null = provable(null); - -Provable.runAndCheck(() => { - let accountUpdateWitness = AccountUpdate.witness(Null, () => ({ - accountUpdate, - result: null, - })).accountUpdate; - console.assert(accountUpdateWitness.body.callDepth === 5); - Provable.assertEqual(AccountUpdate, accountUpdateWitness, accountUpdate); - Provable.assertEqual( - PrivateKey, - (accountUpdateWitness.lazyAuthorization as any).privateKey, - (accountUpdate.lazyAuthorization as any).privateKey - ); -}); - -let result = Provable.witness(() => { - let accountUpdateWitness = AccountUpdate.witness(Null, () => ({ - accountUpdate, - result: null, - })).accountUpdate; - console.assert(accountUpdateWitness.body.callDepth === 0); - Provable.assertEqual(AccountUpdate, accountUpdateWitness, accountUpdate); -}); - -console.log(`an account update has ${AccountUpdate.sizeInFields()} fields`); -console.log( - `witnessing an account update and comparing it to another one creates ${result.rows} rows` -); - -result = Provable.witness(() => { - let accountUpdateWitness = AccountUpdate.witness( - Null, - () => ({ - accountUpdate, - result: null, - }), - { skipCheck: true } - ).accountUpdate; - console.assert(accountUpdateWitness.body.callDepth === 0); - Provable.assertEqual(AccountUpdate, accountUpdateWitness, accountUpdate); -}); - -console.log( - `without all the checks on subfields, witnessing and comparing only creates ${result.rows} rows` -); diff --git a/src/examples/plain-html/index.html b/src/examples/plain-html/index.html new file mode 100644 index 0000000000..0678fde35a --- /dev/null +++ b/src/examples/plain-html/index.html @@ -0,0 +1,15 @@ + + + + + hello-snarkyjs + + + + +
Check out the console (F12)
+ + diff --git a/src/examples/plain-html/server.js b/src/examples/plain-html/server.js new file mode 100644 index 0000000000..dc7679626c --- /dev/null +++ b/src/examples/plain-html/server.js @@ -0,0 +1,42 @@ +import fs from 'node:fs/promises'; +import path from 'node:path'; +import http from 'node:http'; + +const port = 8000; +const defaultHeaders = { + 'content-type': 'text/html', + 'Cross-Origin-Embedder-Policy': 'require-corp', + 'Cross-Origin-Opener-Policy': 'same-origin', +}; + +const server = http.createServer(async (req, res) => { + let file = '.' + req.url; + console.log(file); + + if (file === './') file = './index.html'; + let content; + try { + content = await fs.readFile(path.resolve('./dist/web', file), 'utf8'); + } catch (err) { + res.writeHead(404, defaultHeaders); + res.write('404'); + res.end(); + return; + } + + const extension = path.basename(file).split('.').pop(); + const contentType = { + html: 'text/html', + js: 'application/javascript', + map: 'application/json', + }[extension]; + const headers = { ...defaultHeaders, 'content-type': contentType }; + + res.writeHead(200, headers); + res.write(content); + res.end(); +}); + +server.listen(port, () => { + console.log(`Server is running on: http://localhost:${port}`); +}); diff --git a/src/examples/primitive_constraint_system.ts b/src/examples/primitive_constraint_system.ts deleted file mode 100644 index 17735faa04..0000000000 --- a/src/examples/primitive_constraint_system.ts +++ /dev/null @@ -1,66 +0,0 @@ -import { Field, Group, Poseidon, Provable, Scalar } from 'o1js'; - -function mock(obj: { [K: string]: (...args: any) => void }, name: string) { - let methodKeys = Object.keys(obj); - - return { - analyzeMethods() { - let cs: Record< - string, - { - rows: number; - digest: string; - } - > = {}; - for (let key of methodKeys) { - let { rows, digest } = Provable.constraintSystem(obj[key]); - cs[key] = { - digest, - rows, - }; - } - - return cs; - }, - async compile() { - return { - verificationKey: { data: '', hash: '' }, - }; - }, - name, - digest: () => name, - }; -} - -const GroupMock = { - add() { - let g1 = Provable.witness(Group, () => Group.generator); - let g2 = Provable.witness(Group, () => Group.generator); - g1.add(g2); - }, - sub() { - let g1 = Provable.witness(Group, () => Group.generator); - let g2 = Provable.witness(Group, () => Group.generator); - g1.sub(g2); - }, - scale() { - let g1 = Provable.witness(Group, () => Group.generator); - let s = Provable.witness(Scalar, () => Scalar.from(5n)); - g1.scale(s); - }, - equals() { - let g1 = Provable.witness(Group, () => Group.generator); - let g2 = Provable.witness(Group, () => Group.generator); - g1.equals(g2).assertTrue(); - g1.equals(g2).assertFalse(); - g1.equals(g2).assertEquals(true); - g1.equals(g2).assertEquals(false); - }, - assertions() { - let g1 = Provable.witness(Group, () => Group.generator); - let g2 = Provable.witness(Group, () => Group.generator); - g1.assertEquals(g2); - }, -}; - -export const GroupCS = mock(GroupMock, 'Group Primitive'); diff --git a/src/examples/print_constraint_system.ts b/src/examples/print_constraint_system.ts deleted file mode 100644 index 6a6064a4b2..0000000000 --- a/src/examples/print_constraint_system.ts +++ /dev/null @@ -1,23 +0,0 @@ -import { - Poseidon, - Field, - Circuit, - circuitMain, - public_, - isReady, -} from 'o1js'; - -class Main extends Circuit { - @circuitMain - static main(preimage: Field, @public_ hash: Field) { - Poseidon.hash([preimage]).assertEquals(hash); - } -} - -await isReady; - -console.log('generating keypair...'); -let kp = await Main.generateKeypair(); - -let cs = kp.constraintSystem(); -console.dir(cs, { depth: Infinity }); diff --git a/src/examples/schnorr_sign.ts b/src/examples/schnorr_sign.ts deleted file mode 100644 index 5739108f10..0000000000 --- a/src/examples/schnorr_sign.ts +++ /dev/null @@ -1,72 +0,0 @@ -import { - Field, - Scalar, - Group, - Circuit, - public_, - circuitMain, - prop, - CircuitValue, - Signature, - isReady, -} from 'o1js'; - -await isReady; - -class Witness extends CircuitValue { - @prop signature: Signature; - @prop acc: Group; - @prop r: Scalar; -} - -// Public input: -// [newAcc: curve_point] -// Prove: -// I know [prevAcc: curve_point] and [signature : Signature] such that -// the signature verifies against Brave's public key and [newAcc] is -// a re-randomization of [prevAcc] - -export class Main extends Circuit { - @circuitMain - static main(w: Witness, @public_ newAcc: Group) { - let H = new Group({ x: -1, y: 2 }); - let r: Scalar = Provable.witness(Scalar, () => w.r); - let mask = H.scale(r); - let prevAcc: Group = Provable.witness(Group, () => w.acc); - let pubKey = Group.generator; // TODO: some literal group element - let signature = Provable.witness(Signature, () => w.signature); - //signature.verify(pubKey, [prevAcc.x, prevAcc.y, signature.r]).assertEquals(true); - prevAcc.add(mask).assertEquals(newAcc); - } -} - -class Circ extends Circuit { - @circuitMain - static main(@public_ x: Field) { - let acc = x; - for (let i = 0; i < 1000; ++i) { - acc = acc.mul(acc); - } - } -} - -function testSigning() { - const _msg = [Field.random()]; - const privKey = Scalar.random(); - const _pubKey = Group.generator.scale(privKey); - //const s = Signature.create(privKey, msg); - //console.log('signing worked', s.verify(pubKey, msg).toBoolean()); -} - -export function main() { - const before = new Date(); - const kp = Circ.generateKeypair(); - const after = new Date(); - testSigning(); - console.log('keypairgen', after.getTime() - before.getTime()); - console.log('random', Field.random()); - const proof = Circ.prove([], [new Field(2)], kp); - console.log(proof, kp); - let ok = Circ.verify([Field(2)], kp.verificationKey(), proof); - console.log('verified', ok); -} diff --git a/src/examples/simple_zkapp.ts b/src/examples/simple_zkapp.ts index 95593e4273..26f379770d 100644 --- a/src/examples/simple_zkapp.ts +++ b/src/examples/simple_zkapp.ts @@ -11,7 +11,7 @@ import { Bool, PublicKey, } from 'o1js'; -import { getProfiler } from './profiler.js'; +import { getProfiler } from './utils/profiler.js'; const doProofs = true; @@ -28,11 +28,11 @@ class SimpleZkapp extends SmartContract { } @method update(y: Field): Field { - this.account.provedState.assertEquals(Bool(true)); - this.network.timestamp.assertBetween(beforeGenesis, UInt64.MAXINT()); + this.account.provedState.requireEquals(Bool(true)); + this.network.timestamp.requireBetween(beforeGenesis, UInt64.MAXINT()); this.emitEvent('update', y); let x = this.x.get(); - this.x.assertEquals(x); + this.x.requireEquals(x); let newX = x.add(y); this.x.set(newX); return newX; @@ -43,7 +43,7 @@ class SimpleZkapp extends SmartContract { * @param caller the privileged account */ @method payout(caller: PrivateKey) { - this.account.provedState.assertEquals(Bool(true)); + this.account.provedState.requireEquals(Bool(true)); // check that caller is the privileged account let callerAddress = caller.toPublicKey(); @@ -51,10 +51,10 @@ class SimpleZkapp extends SmartContract { // assert that the caller account is new - this way, payout can only happen once let callerAccountUpdate = AccountUpdate.create(callerAddress); - callerAccountUpdate.account.isNew.assertEquals(Bool(true)); + callerAccountUpdate.account.isNew.requireEquals(Bool(true)); // pay out half of the zkapp balance to the caller let balance = this.account.balance.get(); - this.account.balance.assertEquals(balance); + this.account.balance.requireEquals(balance); let halfBalance = balance.div(2); this.send({ to: callerAccountUpdate, amount: halfBalance }); @@ -77,7 +77,9 @@ let zkappKey = PrivateKey.random(); let zkappAddress = zkappKey.toPublicKey(); // a special account that is allowed to pull out half of the zkapp balance, once -let privilegedKey = PrivateKey.random(); +let privilegedKey = PrivateKey.fromBase58( + 'EKEeoESE2A41YQnSht9f7mjiKpJSeZ4jnfHXYatYi8xJdYSxWBep' +); let privilegedAddress = privilegedKey.toPublicKey(); let initialBalance = 10_000_000_000; @@ -86,7 +88,9 @@ let zkapp = new SimpleZkapp(zkappAddress); if (doProofs) { console.log('compile'); + console.time('compile'); await SimpleZkapp.compile(); + console.timeEnd('compile'); } console.log('deploy'); diff --git a/src/examples/simple_zkapp.web.ts b/src/examples/simple_zkapp.web.ts new file mode 100644 index 0000000000..9a10d1e070 --- /dev/null +++ b/src/examples/simple_zkapp.web.ts @@ -0,0 +1,130 @@ +import { + Field, + state, + State, + method, + UInt64, + PrivateKey, + SmartContract, + Mina, + AccountUpdate, + Bool, + PublicKey, +} from 'o1js'; + +const doProofs = true; + +const beforeGenesis = UInt64.from(Date.now()); + +class SimpleZkapp extends SmartContract { + @state(Field) x = State(); + + events = { update: Field, payout: UInt64, payoutReceiver: PublicKey }; + + @method init() { + super.init(); + this.x.set(initialState); + } + + @method update(y: Field): Field { + this.account.provedState.requireEquals(Bool(true)); + this.network.timestamp.requireBetween(beforeGenesis, UInt64.MAXINT()); + this.emitEvent('update', y); + let x = this.x.get(); + this.x.requireEquals(x); + let newX = x.add(y); + this.x.set(newX); + return newX; + } + + /** + * This method allows a certain privileged account to claim half of the zkapp balance, but only once + * @param caller the privileged account + */ + @method payout(caller: PrivateKey) { + this.account.provedState.requireEquals(Bool(true)); + + // check that caller is the privileged account + let callerAddress = caller.toPublicKey(); + callerAddress.assertEquals(privilegedAddress); + + // assert that the caller account is new - this way, payout can only happen once + let callerAccountUpdate = AccountUpdate.create(callerAddress); + callerAccountUpdate.account.isNew.requireEquals(Bool(true)); + // pay out half of the zkapp balance to the caller + let balance = this.account.balance.get(); + this.account.balance.requireEquals(balance); + let halfBalance = balance.div(2); + this.send({ to: callerAccountUpdate, amount: halfBalance }); + + // emit some events + this.emitEvent('payoutReceiver', callerAddress); + this.emitEvent('payout', halfBalance); + } +} + +let Local = Mina.LocalBlockchain({ proofsEnabled: doProofs }); +Mina.setActiveInstance(Local); + +// a test account that pays all the fees, and puts additional funds into the zkapp +let { privateKey: senderKey, publicKey: sender } = Local.testAccounts[0]; + +// the zkapp account +let zkappKey = PrivateKey.random(); +let zkappAddress = zkappKey.toPublicKey(); + +// a special account that is allowed to pull out half of the zkapp balance, once +let privilegedKey = PrivateKey.fromBase58( + 'EKEeoESE2A41YQnSht9f7mjiKpJSeZ4jnfHXYatYi8xJdYSxWBep' +); +let privilegedAddress = privilegedKey.toPublicKey(); + +let initialBalance = 10_000_000_000; +let initialState = Field(1); +let zkapp = new SimpleZkapp(zkappAddress); + +if (doProofs) { + console.log('compile'); + await SimpleZkapp.compile(); +} + +console.log('deploy'); +let tx = await Mina.transaction(sender, () => { + let senderUpdate = AccountUpdate.fundNewAccount(sender); + senderUpdate.send({ to: zkappAddress, amount: initialBalance }); + zkapp.deploy({ zkappKey }); +}); +await tx.prove(); +await tx.sign([senderKey]).send(); + +console.log('initial state: ' + zkapp.x.get()); +console.log(`initial balance: ${zkapp.account.balance.get().div(1e9)} MINA`); + +let account = Mina.getAccount(zkappAddress); +console.log('account state is proved:', account.zkapp?.provedState.toBoolean()); + +console.log('update'); +tx = await Mina.transaction(sender, () => { + zkapp.update(Field(3)); +}); +await tx.prove(); +await tx.sign([senderKey]).send(); + +// pay more into the zkapp -- this doesn't need a proof +console.log('receive'); +tx = await Mina.transaction(sender, () => { + let payerAccountUpdate = AccountUpdate.createSigned(sender); + payerAccountUpdate.send({ to: zkappAddress, amount: UInt64.from(8e9) }); +}); +await tx.sign([senderKey]).send(); + +console.log('payout'); +tx = await Mina.transaction(sender, () => { + AccountUpdate.fundNewAccount(sender); + zkapp.payout(privilegedKey); +}); +await tx.prove(); +await tx.sign([senderKey]).send(); + +console.log('final state: ' + zkapp.x.get()); +console.log(`final balance: ${zkapp.account.balance.get().div(1e9)} MINA`); diff --git a/src/examples/utils/README.md b/src/examples/utils/README.md new file mode 100644 index 0000000000..5a990bb239 --- /dev/null +++ b/src/examples/utils/README.md @@ -0,0 +1 @@ +This folder doesn't contain stand-alone examples, but utilities used in our examples. diff --git a/src/examples/profiler.ts b/src/examples/utils/profiler.ts similarity index 97% rename from src/examples/profiler.ts rename to src/examples/utils/profiler.ts index 9a93e417ee..c7f1e6c5a5 100644 --- a/src/examples/profiler.ts +++ b/src/examples/utils/profiler.ts @@ -1,4 +1,3 @@ -import { time } from 'console'; import fs from 'fs'; export { getProfiler }; diff --git a/src/examples/zkapps/tictoc.ts b/src/examples/utils/tic-toc.node.ts similarity index 52% rename from src/examples/zkapps/tictoc.ts rename to src/examples/utils/tic-toc.node.ts index 35bd2de501..9e8d50634a 100644 --- a/src/examples/zkapps/tictoc.ts +++ b/src/examples/utils/tic-toc.node.ts @@ -1,4 +1,8 @@ -// helper for printing timings +/** + * Helper for printing timings, in the spirit of Python's `tic` and `toc`. + * + * This is a slightly nicer version of './tic-tic.ts' which only works in Node. + */ export { tic, toc }; @@ -7,11 +11,11 @@ let i = 0; function tic(label = `Run command ${i++}`) { process.stdout.write(`${label}... `); - timingStack.push([label, Date.now()]); + timingStack.push([label, performance.now()]); } function toc() { let [label, start] = timingStack.pop()!; - let time = (Date.now() - start) / 1000; + let time = (performance.now() - start) / 1000; process.stdout.write(`\r${label}... ${time.toFixed(3)} sec\n`); } diff --git a/src/examples/benchmarks/tic-toc.ts b/src/examples/utils/tic-toc.ts similarity index 61% rename from src/examples/benchmarks/tic-toc.ts rename to src/examples/utils/tic-toc.ts index 9c037770cd..4b66514d8d 100644 --- a/src/examples/benchmarks/tic-toc.ts +++ b/src/examples/utils/tic-toc.ts @@ -1,14 +1,20 @@ +/** + * Helper for printing timings, in the spirit of Python's `tic` and `toc`. + */ + export { tic, toc }; let timingStack: [string, number][] = []; let i = 0; + function tic(label = `Run command ${i++}`) { console.log(`${label}... `); - timingStack.push([label, Date.now()]); + timingStack.push([label, performance.now()]); } + function toc() { let [label, start] = timingStack.pop()!; - let time = (Date.now() - start) / 1000; + let time = (performance.now() - start) / 1000; console.log(`\r${label}... ${time.toFixed(3)} sec\n`); return time; } diff --git a/src/examples/zkapps/composability.ts b/src/examples/zkapps/composability.ts index 54870bc2d1..253a992224 100644 --- a/src/examples/zkapps/composability.ts +++ b/src/examples/zkapps/composability.ts @@ -12,7 +12,7 @@ import { state, State, } from 'o1js'; -import { getProfiler } from '../profiler.js'; +import { getProfiler } from '../utils/profiler.js'; const doProofs = true; diff --git a/src/examples/zkapps/dex/dex-with-actions.ts b/src/examples/zkapps/dex/dex-with-actions.ts index 4e31bf5453..34353a2c4d 100644 --- a/src/examples/zkapps/dex/dex-with-actions.ts +++ b/src/examples/zkapps/dex/dex-with-actions.ts @@ -91,10 +91,10 @@ class Dex extends SmartContract { // get balances of X and Y token let dexX = AccountUpdate.create(this.address, tokenX.token.id); - let x = dexX.account.balance.getAndAssertEquals(); + let x = dexX.account.balance.getAndRequireEquals(); let dexY = AccountUpdate.create(this.address, tokenY.token.id); - let y = dexY.account.balance.getAndAssertEquals(); + let y = dexY.account.balance.getAndRequireEquals(); // // assert dy === [dx * y/x], or x === 0 let isXZero = x.equals(UInt64.zero); @@ -112,7 +112,7 @@ class Dex extends SmartContract { // update l supply let l = this.totalSupply.get(); - this.totalSupply.assertEquals(l); + this.totalSupply.requireEquals(l); this.totalSupply.set(l.add(dl)); // emit event @@ -160,7 +160,7 @@ class Dex extends SmartContract { this.token.burn({ address: this.sender, amount: dl }); // TODO: preconditioning on the state here ruins concurrent interactions, // there should be another `finalize` DEX method which reduces actions & updates state - this.totalSupply.set(this.totalSupply.getAndAssertEquals().sub(dl)); + this.totalSupply.set(this.totalSupply.getAndRequireEquals().sub(dl)); // emit event this.typedEvents.emit('redeem-liquidity', { address: this.sender, dl }); @@ -171,8 +171,8 @@ class Dex extends SmartContract { * the current action state and token supply */ @method assertActionsAndSupply(actionState: Field, totalSupply: UInt64) { - this.account.actionState.assertEquals(actionState); - this.totalSupply.assertEquals(totalSupply); + this.account.actionState.requireEquals(actionState); + this.totalSupply.requireEquals(totalSupply); } /** @@ -236,7 +236,7 @@ class DexTokenHolder extends SmartContract { @method redeemLiquidityFinalize() { // get redeem actions let dex = new Dex(this.address); - let fromActionState = this.redeemActionState.getAndAssertEquals(); + let fromActionState = this.redeemActionState.getAndRequireEquals(); let actions = dex.reducer.getActions({ fromActionState }); // get total supply of liquidity tokens _before_ applying these actions @@ -251,7 +251,7 @@ class DexTokenHolder extends SmartContract { }); // get our token balance - let x = this.account.balance.getAndAssertEquals(); + let x = this.account.balance.getAndRequireEquals(); let redeemActionState = dex.reducer.forEach( actions, @@ -296,8 +296,8 @@ class DexTokenHolder extends SmartContract { // get balances of X and Y token let dexX = AccountUpdate.create(this.address, tokenX.token.id); - let x = dexX.account.balance.getAndAssertEquals(); - let y = this.account.balance.getAndAssertEquals(); + let x = dexX.account.balance.getAndRequireEquals(); + let y = this.account.balance.getAndRequireEquals(); // send x from user to us (i.e., to the same address as this but with the other token) tokenX.transfer(user, dexX, dx); diff --git a/src/examples/zkapps/dex/dex.ts b/src/examples/zkapps/dex/dex.ts index e391bdeb35..f9906fb96e 100644 --- a/src/examples/zkapps/dex/dex.ts +++ b/src/examples/zkapps/dex/dex.ts @@ -61,10 +61,10 @@ function createDex({ // TODO: this creates extra account updates. we need to reuse these by passing them to or returning them from transfer() // but for that, we need the @method argument generalization let dexXUpdate = AccountUpdate.create(this.address, tokenX.token.id); - let dexXBalance = dexXUpdate.account.balance.getAndAssertEquals(); + let dexXBalance = dexXUpdate.account.balance.getAndRequireEquals(); let dexYUpdate = AccountUpdate.create(this.address, tokenY.token.id); - let dexYBalance = dexYUpdate.account.balance.getAndAssertEquals(); + let dexYBalance = dexYUpdate.account.balance.getAndRequireEquals(); // // assert dy === [dx * y/x], or x === 0 let isXZero = dexXBalance.equals(UInt64.zero); @@ -103,7 +103,7 @@ function createDex({ // update l supply let l = this.totalSupply.get(); - this.totalSupply.assertEquals(l); + this.totalSupply.requireEquals(l); this.totalSupply.set(l.add(dl)); return dl; } @@ -195,7 +195,7 @@ function createDex({ // this makes sure there is enough l to burn (user balance stays >= 0), so l stays >= 0, so l was >0 before this.token.burn({ address: user, amount: dl }); let l = this.totalSupply.get(); - this.totalSupply.assertEquals(l); + this.totalSupply.requireEquals(l); this.totalSupply.set(l.sub(dl)); return l; } @@ -227,7 +227,7 @@ function createDex({ // in return, we give dy back let y = this.account.balance.get(); - this.account.balance.assertEquals(y); + this.account.balance.requireEquals(y); // we can safely divide by l here because the Dex contract logic wouldn't allow burnLiquidity if not l>0 let dy = y.mul(dl).div(l); // just subtract the balance, user gets their part one level higher @@ -256,7 +256,7 @@ function createDex({ // in return for dl, we give back dx, the X token part let x = this.account.balance.get(); - this.account.balance.assertEquals(x); + this.account.balance.requireEquals(x); let dx = x.mul(dl).div(l); // just subtract the balance, user gets their part one level higher this.balance.subInPlace(dx); @@ -276,7 +276,7 @@ function createDex({ // get balances let x = tokenX.getBalance(this.address); let y = this.account.balance.get(); - this.account.balance.assertEquals(y); + this.account.balance.requireEquals(y); // send x from user to us (i.e., to the same address as this but with the other token) tokenX.transfer(user, this.address, dx); // compute and send dy @@ -300,7 +300,7 @@ function createDex({ let tokenX = new TokenContract(otherTokenAddress); let x = tokenX.getBalance(this.address); let y = this.account.balance.get(); - this.account.balance.assertEquals(y); + this.account.balance.requireEquals(y); tokenX.transfer(user, this.address, dx); // this formula has been changed - we just give the user an additional 15 token @@ -394,7 +394,7 @@ class TokenContract extends SmartContract { amount: UInt64.MAXINT(), }); // assert that the receiving account is new, so this can be only done once - receiver.account.isNew.assertEquals(Bool(true)); + receiver.account.isNew.requireEquals(Bool(true)); // pay fees for opened account this.balance.subInPlace(Mina.accountCreationFee()); } @@ -410,7 +410,7 @@ class TokenContract extends SmartContract { amount: UInt64.from(10n ** 6n), }); // assert that the receiving account is new, so this can be only done once - receiver.account.isNew.assertEquals(Bool(true)); + receiver.account.isNew.requireEquals(Bool(true)); // pay fees for opened account this.balance.subInPlace(Mina.accountCreationFee()); } @@ -477,7 +477,7 @@ class TokenContract extends SmartContract { @method getBalance(publicKey: PublicKey): UInt64 { let accountUpdate = AccountUpdate.create(publicKey, this.token.id); let balance = accountUpdate.account.balance.get(); - accountUpdate.account.balance.assertEquals( + accountUpdate.account.balance.requireEquals( accountUpdate.account.balance.get() ); return balance; diff --git a/src/examples/zkapps/dex/erc20.ts b/src/examples/zkapps/dex/erc20.ts index 94944d85f9..b18ba43dea 100644 --- a/src/examples/zkapps/dex/erc20.ts +++ b/src/examples/zkapps/dex/erc20.ts @@ -83,7 +83,7 @@ class TrivialCoin extends SmartContract implements Erc20 { amount: this.SUPPLY, }); // assert that the receiving account is new, so this can be only done once - receiver.account.isNew.assertEquals(Bool(true)); + receiver.account.isNew.requireEquals(Bool(true)); // pay fees for opened account this.balance.subInPlace(Mina.accountCreationFee()); @@ -115,7 +115,7 @@ class TrivialCoin extends SmartContract implements Erc20 { balanceOf(owner: PublicKey): UInt64 { let account = Account(owner, this.token.id); let balance = account.balance.get(); - account.balance.assertEquals(balance); + account.balance.requireEquals(balance); return balance; } allowance(owner: PublicKey, spender: PublicKey): UInt64 { @@ -193,7 +193,7 @@ class TrivialCoin extends SmartContract implements Erc20 { zkapp.requireSignature(); } - // for letting a zkapp do whatever it wants, as long as no tokens are transfered + // for letting a zkapp do whatever it wants, as long as no tokens are transferred // TODO: atm, we have to restrict the zkapp to have no children // -> need to be able to witness a general layout of account updates @method approveZkapp(callback: Experimental.Callback) { diff --git a/src/examples/zkapps/dex/happy-path-with-actions.ts b/src/examples/zkapps/dex/happy-path-with-actions.ts index e3d4ce5be1..52eab4f2a0 100644 --- a/src/examples/zkapps/dex/happy-path-with-actions.ts +++ b/src/examples/zkapps/dex/happy-path-with-actions.ts @@ -9,7 +9,7 @@ import { } from './dex-with-actions.js'; import { TokenContract } from './dex.js'; import { expect } from 'expect'; -import { tic, toc } from '../tictoc.js'; +import { tic, toc } from '../../utils/tic-toc.node.js'; await isReady; diff --git a/src/examples/zkapps/dex/happy-path-with-proofs.ts b/src/examples/zkapps/dex/happy-path-with-proofs.ts index 7e81df9c44..f58f478fcf 100644 --- a/src/examples/zkapps/dex/happy-path-with-proofs.ts +++ b/src/examples/zkapps/dex/happy-path-with-proofs.ts @@ -1,8 +1,8 @@ import { isReady, Mina, AccountUpdate, UInt64 } from 'o1js'; import { createDex, TokenContract, addresses, keys, tokenIds } from './dex.js'; import { expect } from 'expect'; -import { tic, toc } from '../tictoc.js'; -import { getProfiler } from '../../profiler.js'; +import { tic, toc } from '../../utils/tic-toc.node.js'; +import { getProfiler } from '../../utils/profiler.js'; await isReady; diff --git a/src/examples/zkapps/dex/run-berkeley.ts b/src/examples/zkapps/dex/run-berkeley.ts index 1a15eaaaba..540382bd67 100644 --- a/src/examples/zkapps/dex/run-berkeley.ts +++ b/src/examples/zkapps/dex/run-berkeley.ts @@ -15,7 +15,7 @@ import { } from './dex-with-actions.js'; import { TokenContract } from './dex.js'; import { expect } from 'expect'; -import { tic, toc } from '../tictoc.js'; +import { tic, toc } from '../../utils/tic-toc.node.js'; await isReady; diff --git a/src/examples/zkapps/dex/run.ts b/src/examples/zkapps/dex/run.ts index 5fe5471d37..4899e3fdb2 100644 --- a/src/examples/zkapps/dex/run.ts +++ b/src/examples/zkapps/dex/run.ts @@ -10,7 +10,7 @@ import { import { createDex, TokenContract, addresses, keys, tokenIds } from './dex.js'; import { expect } from 'expect'; -import { getProfiler } from '../../profiler.js'; +import { getProfiler } from '../../utils/profiler.js'; await isReady; let proofsEnabled = false; diff --git a/src/examples/zkapps/dex/upgradability.ts b/src/examples/zkapps/dex/upgradability.ts index af1eeeca22..02f4f2e78d 100644 --- a/src/examples/zkapps/dex/upgradability.ts +++ b/src/examples/zkapps/dex/upgradability.ts @@ -8,7 +8,7 @@ import { } from 'o1js'; import { createDex, TokenContract, addresses, keys, tokenIds } from './dex.js'; import { expect } from 'expect'; -import { getProfiler } from '../../profiler.js'; +import { getProfiler } from '../../utils/profiler.js'; await isReady; diff --git a/src/examples/zkapps/escrow/escrow.ts b/src/examples/zkapps/escrow/escrow.ts index a702a5f7a0..1917dca854 100644 --- a/src/examples/zkapps/escrow/escrow.ts +++ b/src/examples/zkapps/escrow/escrow.ts @@ -11,7 +11,7 @@ export class Escrow extends SmartContract { // add your deposit logic circuit here // that will adjust the amount - const payerUpdate = AccountUpdate.create(user); + const payerUpdate = AccountUpdate.createSigned(user); payerUpdate.send({ to: this.address, amount: UInt64.from(1000000) }); } diff --git a/src/examples/zkapps/hashing/hash.ts b/src/examples/zkapps/hashing/hash.ts new file mode 100644 index 0000000000..9ad9947dfa --- /dev/null +++ b/src/examples/zkapps/hashing/hash.ts @@ -0,0 +1,49 @@ +import { + Hash, + Field, + SmartContract, + state, + State, + method, + Permissions, + Bytes, +} from 'o1js'; + +let initialCommitment: Field = Field(0); + +export class HashStorage extends SmartContract { + @state(Field) commitment = State(); + + init() { + super.init(); + this.account.permissions.set({ + ...Permissions.default(), + editState: Permissions.proofOrSignature(), + }); + this.commitment.set(initialCommitment); + } + + @method SHA256(xs: Bytes) { + const shaHash = Hash.SHA3_256.hash(xs); + const commitment = Hash.hash(shaHash.toFields()); + this.commitment.set(commitment); + } + + @method SHA384(xs: Bytes) { + const shaHash = Hash.SHA3_384.hash(xs); + const commitment = Hash.hash(shaHash.toFields()); + this.commitment.set(commitment); + } + + @method SHA512(xs: Bytes) { + const shaHash = Hash.SHA3_512.hash(xs); + const commitment = Hash.hash(shaHash.toFields()); + this.commitment.set(commitment); + } + + @method Keccak256(xs: Bytes) { + const shaHash = Hash.Keccak256.hash(xs); + const commitment = Hash.hash(shaHash.toFields()); + this.commitment.set(commitment); + } +} diff --git a/src/examples/zkapps/hashing/run.ts b/src/examples/zkapps/hashing/run.ts new file mode 100644 index 0000000000..9350211d68 --- /dev/null +++ b/src/examples/zkapps/hashing/run.ts @@ -0,0 +1,73 @@ +import { HashStorage } from './hash.js'; +import { Mina, PrivateKey, AccountUpdate, Bytes } from 'o1js'; + +let txn; +let proofsEnabled = true; +// setup local ledger +let Local = Mina.LocalBlockchain({ proofsEnabled }); +Mina.setActiveInstance(Local); + +if (proofsEnabled) { + console.log('Proofs enabled'); + HashStorage.compile(); +} + +// test accounts that pays all the fees, and puts additional funds into the zkapp +const feePayer = Local.testAccounts[0]; + +// zkapp account +const zkAppPrivateKey = PrivateKey.random(); +const zkAppAddress = zkAppPrivateKey.toPublicKey(); +const zkAppInstance = new HashStorage(zkAppAddress); + +// 0, 1, 2, 3, ..., 32 +const hashData = Bytes.from(Array.from({ length: 32 }, (_, i) => i)); + +console.log('Deploying Hash Example....'); +txn = await Mina.transaction(feePayer.publicKey, () => { + AccountUpdate.fundNewAccount(feePayer.publicKey); + zkAppInstance.deploy(); +}); +await txn.sign([feePayer.privateKey, zkAppPrivateKey]).send(); + +const initialState = + Mina.getAccount(zkAppAddress).zkapp?.appState?.[0].toString(); + +let currentState; +console.log('Initial State', initialState); + +console.log(`Updating commitment from ${initialState} using SHA256 ...`); +txn = await Mina.transaction(feePayer.publicKey, () => { + zkAppInstance.SHA256(hashData); +}); +await txn.prove(); +await txn.sign([feePayer.privateKey]).send(); +currentState = Mina.getAccount(zkAppAddress).zkapp?.appState?.[0].toString(); +console.log(`Current state successfully updated to ${currentState}`); + +console.log(`Updating commitment from ${initialState} using SHA384 ...`); +txn = await Mina.transaction(feePayer.publicKey, () => { + zkAppInstance.SHA384(hashData); +}); +await txn.prove(); +await txn.sign([feePayer.privateKey]).send(); +currentState = Mina.getAccount(zkAppAddress).zkapp?.appState?.[0].toString(); +console.log(`Current state successfully updated to ${currentState}`); + +console.log(`Updating commitment from ${initialState} using SHA512 ...`); +txn = await Mina.transaction(feePayer.publicKey, () => { + zkAppInstance.SHA512(hashData); +}); +await txn.prove(); +await txn.sign([feePayer.privateKey]).send(); +currentState = Mina.getAccount(zkAppAddress).zkapp?.appState?.[0].toString(); +console.log(`Current state successfully updated to ${currentState}`); + +console.log(`Updating commitment from ${initialState} using Keccak256...`); +txn = await Mina.transaction(feePayer.publicKey, () => { + zkAppInstance.Keccak256(hashData); +}); +await txn.prove(); +await txn.sign([feePayer.privateKey]).send(); +currentState = Mina.getAccount(zkAppAddress).zkapp?.appState?.[0].toString(); +console.log(`Current state successfully updated to ${currentState}`); diff --git a/src/examples/zkapps/hello_world/hello_world.ts b/src/examples/zkapps/hello_world/hello_world.ts index 8ba43875c0..77eb191d9d 100644 --- a/src/examples/zkapps/hello_world/hello_world.ts +++ b/src/examples/zkapps/hello_world/hello_world.ts @@ -1,13 +1,8 @@ -import { - Field, - PrivateKey, - SmartContract, - State, - method, - state, -} from 'o1js'; +import { Field, PrivateKey, SmartContract, State, method, state } from 'o1js'; -export const adminPrivateKey = PrivateKey.random(); +export const adminPrivateKey = PrivateKey.fromBase58( + 'EKFcef5HKXAn7V2rQntLiXtJr15dkxrsrQ1G4pnYemhMEAWYbkZW' +); export const adminPublicKey = adminPrivateKey.toPublicKey(); export class HelloWorld extends SmartContract { @@ -21,12 +16,12 @@ export class HelloWorld extends SmartContract { @method update(squared: Field, admin: PrivateKey) { const x = this.x.get(); - this.x.assertNothing(); + this.x.requireNothing(); x.square().assertEquals(squared); this.x.set(squared); const adminPk = admin.toPublicKey(); - this.account.delegate.assertEquals(adminPk); + this.account.delegate.requireEquals(adminPk); } } diff --git a/src/examples/zkapps/hello_world/run.ts b/src/examples/zkapps/hello_world/run.ts index 7371795ead..41a73dbb4d 100644 --- a/src/examples/zkapps/hello_world/run.ts +++ b/src/examples/zkapps/hello_world/run.ts @@ -1,5 +1,5 @@ import { AccountUpdate, Field, Mina, PrivateKey } from 'o1js'; -import { getProfiler } from '../../profiler.js'; +import { getProfiler } from '../../utils/profiler.js'; import { HelloWorld, adminPrivateKey } from './hello_world.js'; const HelloWorldProfier = getProfiler('Hello World'); diff --git a/src/examples/zkapps/hello_world/run_live.ts b/src/examples/zkapps/hello_world/run_live.ts index 4f7a9eb4a4..c54d323a8a 100644 --- a/src/examples/zkapps/hello_world/run_live.ts +++ b/src/examples/zkapps/hello_world/run_live.ts @@ -1,28 +1,34 @@ // Live integration test against real Mina network. -import { AccountUpdate, Field, Mina, PrivateKey, fetchAccount } from 'o1js'; +import { + AccountUpdate, + Field, + Lightnet, + Mina, + PrivateKey, + fetchAccount, +} from 'o1js'; import { HelloWorld, adminPrivateKey } from './hello_world.js'; -const useLocalNetwork = process.env.USE_LOCAL_NETWORK === 'true'; -const senderKey = useLocalNetwork - ? PrivateKey.fromBase58( - 'EKEnVLUhYHDJvgmgQu5SzaV8MWKNfhAXYSkLBRk5KEfudWZRbs4P' - ) - : PrivateKey.random(); -const sender = senderKey.toPublicKey(); +const useCustomLocalNetwork = process.env.USE_CUSTOM_LOCAL_NETWORK === 'true'; const zkAppKey = PrivateKey.random(); const zkAppAddress = zkAppKey.toPublicKey(); const transactionFee = 100_000_000; // Network configuration -const network = Mina.Network( - useLocalNetwork +const network = Mina.Network({ + mina: useCustomLocalNetwork ? 'http://localhost:8080/graphql' - : 'https://proxy.berkeley.minaexplorer.com/graphql' -); + : 'https://proxy.berkeley.minaexplorer.com/graphql', + lightnetAccountManager: 'http://localhost:8181', +}); Mina.setActiveInstance(network); // Fee payer setup -if (!useLocalNetwork) { +const senderKey = useCustomLocalNetwork + ? (await Lightnet.acquireKeyPair()).privateKey + : PrivateKey.random(); +const sender = senderKey.toPublicKey(); +if (!useCustomLocalNetwork) { console.log(`Funding the fee payer account.`); await Mina.faucet(sender); } @@ -91,3 +97,9 @@ try { ); } console.log('Success!'); + +// Tear down +const keyPairReleaseMessage = await Lightnet.releaseKeyPair({ + publicKey: sender.toBase58(), +}); +if (keyPairReleaseMessage) console.info(keyPairReleaseMessage); diff --git a/src/examples/zkapps/local_events_zkapp.ts b/src/examples/zkapps/local_events_zkapp.ts index 5cec5d1a54..993ed31e1a 100644 --- a/src/examples/zkapps/local_events_zkapp.ts +++ b/src/examples/zkapps/local_events_zkapp.ts @@ -40,7 +40,7 @@ class SimpleZkapp extends SmartContract { }); this.emitEvent('simpleEvent', y); let x = this.x.get(); - this.x.assertEquals(x); + this.x.requireEquals(x); this.x.set(x.add(y)); } } @@ -91,7 +91,7 @@ let events = await zkapp.fetchEvents(UInt32.from(0)); console.log(events); console.log('---- emitted events: ----'); // fetches all events from zkapp starting block height 0 and ending at block height 10 -events = await zkapp.fetchEvents(UInt32.from(0), UInt64.from(10)); +events = await zkapp.fetchEvents(UInt32.from(0), UInt32.from(10)); console.log(events); console.log('---- emitted events: ----'); // fetches all events diff --git a/src/examples/zkapps/merkle_tree/merkle_zkapp.ts b/src/examples/zkapps/merkle_tree/merkle_zkapp.ts index de1c42e995..3bcb1bfa22 100644 --- a/src/examples/zkapps/merkle_tree/merkle_zkapp.ts +++ b/src/examples/zkapps/merkle_tree/merkle_zkapp.ts @@ -75,7 +75,7 @@ class Leaderboard extends SmartContract { // we fetch the on-chain commitment let commitment = this.commitment.get(); - this.commitment.assertEquals(commitment); + this.commitment.requireEquals(commitment); // we check that the account is within the committed Merkle Tree path.calculateRoot(account.hash()).assertEquals(commitment); diff --git a/src/examples/zkapps/reducer/map.ts b/src/examples/zkapps/reducer/map.ts new file mode 100644 index 0000000000..890555be92 --- /dev/null +++ b/src/examples/zkapps/reducer/map.ts @@ -0,0 +1,193 @@ +import { + Field, + Struct, + method, + PrivateKey, + SmartContract, + Mina, + AccountUpdate, + Reducer, + provable, + PublicKey, + Bool, + Poseidon, + Provable, +} from 'o1js'; + +/* + +This contract emulates a "mapping" data structure, which is a key-value store, similar to a dictionary or hash table or `new Map()` in JavaScript. +In this example, the keys are public keys, and the values are arbitrary field elements. + +This utilizes the `Reducer` as an append online list of actions, which are then looked at to find the value corresponding to a specific key. + + +```ts +// js +const map = new Map(); +map.set(key, value); +map.get(key); + +// contract +zkApp.deploy(); // ... deploy the zkapp +zkApp.set(key, value); // ... set a key-value pair +zkApp.get(key); // ... get a value by key +``` +*/ + +class Option extends Struct({ + isSome: Bool, + value: Field, +}) {} + +const KeyValuePair = provable({ + key: Field, + value: Field, +}); + +class StorageContract extends SmartContract { + reducer = Reducer({ + actionType: KeyValuePair, + }); + + @method set(key: PublicKey, value: Field) { + this.reducer.dispatch({ key: Poseidon.hash(key.toFields()), value }); + } + + @method get(key: PublicKey): Option { + let pendingActions = this.reducer.getActions({ + fromActionState: Reducer.initialActionState, + }); + + let keyHash = Poseidon.hash(key.toFields()); + + let { state: optionValue } = this.reducer.reduce( + pendingActions, + Option, + (state, action) => { + let currentMatch = keyHash.equals(action.key); + return { + isSome: currentMatch.or(state.isSome), + value: Provable.if(currentMatch, action.value, state.value), + }; + }, + { + state: Option.empty(), + actionState: Reducer.initialActionState, + }, + { maxTransactionsWithActions: k } + ); + + return optionValue; + } +} + +let k = 1 << 4; + +let Local = Mina.LocalBlockchain(); +Mina.setActiveInstance(Local); +let cs = StorageContract.analyzeMethods(); + +console.log(`method size for a "mapping" contract with ${k} entries`); +console.log('get rows:', cs['get'].rows); +console.log('set rows:', cs['set'].rows); + +// a test account that pays all the fees +let feePayerKey = Local.testAccounts[0].privateKey; +let feePayer = Local.testAccounts[0].publicKey; + +// the zkapp account +let zkappKey = PrivateKey.random(); +let zkappAddress = zkappKey.toPublicKey(); +let zkapp = new StorageContract(zkappAddress); + +await StorageContract.compile(); + +let tx = await Mina.transaction(feePayer, () => { + AccountUpdate.fundNewAccount(feePayer); + zkapp.deploy(); +}); +await tx.sign([feePayerKey, zkappKey]).send(); + +console.log('deployed'); + +let map: { key: PublicKey; value: Field }[] = [ + { + key: PrivateKey.random().toPublicKey(), + value: Field(192), + }, + { + key: PrivateKey.random().toPublicKey(), + value: Field(151), + }, + { + key: PrivateKey.random().toPublicKey(), + value: Field(781), + }, +]; + +let key = map[0].key; +let value = map[0].value; +console.log(`setting key ${key.toBase58()} with value ${value}`); + +tx = await Mina.transaction(feePayer, () => { + zkapp.set(key, value); +}); +await tx.prove(); +await tx.sign([feePayerKey]).send(); + +key = map[1].key; +value = map[1].value; +console.log(`setting key ${key.toBase58()} with value ${value}`); + +tx = await Mina.transaction(feePayer, () => { + zkapp.set(key, value); +}); +await tx.prove(); +await tx.sign([feePayerKey]).send(); + +key = map[2].key; +value = map[2].value; +console.log(`setting key ${key.toBase58()} with value ${value}`); + +tx = await Mina.transaction(feePayer, () => { + zkapp.set(key, value); +}); +await tx.prove(); +await tx.sign([feePayerKey]).send(); + +key = map[0].key; +value = map[0].value; +console.log(`getting key ${key.toBase58()} with value ${value}`); + +let result: any; +tx = await Mina.transaction(feePayer, () => { + result = zkapp.get(key); +}); +await tx.prove(); +await tx.sign([feePayerKey]).send(); + +console.log('found correct match?', result.isSome.toBoolean()); +console.log('matches expected value?', result.value.equals(value).toBoolean()); + +key = map[1].key; +value = map[1].value; +console.log(`getting key ${key.toBase58()} with value ${value}`); + +tx = await Mina.transaction(feePayer, () => { + result = zkapp.get(key); +}); +await tx.prove(); +await tx.sign([feePayerKey]).send(); + +console.log('found correct match?', result.isSome.toBoolean()); +console.log('matches expected value?', result.value.equals(value).toBoolean()); + +console.log(`getting key invalid key`); +tx = await Mina.transaction(feePayer, () => { + result = zkapp.get(PrivateKey.random().toPublicKey()); +}); +await tx.prove(); +await tx.sign([feePayerKey]).send(); + +console.log('should be isSome(false)', result.isSome.toBoolean()); diff --git a/src/examples/zkapps/reducer/reducer.ts b/src/examples/zkapps/reducer/reducer.ts index 9b3d46dec7..a9bbbb063d 100644 --- a/src/examples/zkapps/reducer/reducer.ts +++ b/src/examples/zkapps/reducer/reducer.ts @@ -33,9 +33,9 @@ class CounterZkapp extends SmartContract { @method rollupIncrements() { // get previous counter & actions hash, assert that they're the same as on-chain values let counter = this.counter.get(); - this.counter.assertEquals(counter); + this.counter.requireEquals(counter); let actionState = this.actionState.get(); - this.actionState.assertEquals(actionState); + this.actionState.requireEquals(actionState); // compute the new counter and hash from pending actions let pendingActions = this.reducer.getActions({ diff --git a/src/examples/zkapps/reducer/reducer_composite.ts b/src/examples/zkapps/reducer/reducer_composite.ts index b9df1b0728..20495848fe 100644 --- a/src/examples/zkapps/reducer/reducer_composite.ts +++ b/src/examples/zkapps/reducer/reducer_composite.ts @@ -14,7 +14,7 @@ import { Provable, } from 'o1js'; import assert from 'node:assert/strict'; -import { getProfiler } from '../../profiler.js'; +import { getProfiler } from '../../utils/profiler.js'; await isReady; @@ -44,9 +44,9 @@ class CounterZkapp extends SmartContract { @method rollupIncrements() { // get previous counter & actions hash, assert that they're the same as on-chain values let counter = this.counter.get(); - this.counter.assertEquals(counter); + this.counter.requireEquals(counter); let actionState = this.actionState.get(); - this.actionState.assertEquals(actionState); + this.actionState.requireEquals(actionState); // compute the new counter and hash from pending actions let pendingActions = this.reducer.getActions({ diff --git a/src/examples/zkapps/set_local_preconditions_zkapp.ts b/src/examples/zkapps/set_local_preconditions_zkapp.ts index 2b660e3333..2b11b2a494 100644 --- a/src/examples/zkapps/set_local_preconditions_zkapp.ts +++ b/src/examples/zkapps/set_local_preconditions_zkapp.ts @@ -26,7 +26,7 @@ await isReady; class SimpleZkapp extends SmartContract { @method blockheightEquals(y: UInt32) { let length = this.network.blockchainLength.get(); - this.network.blockchainLength.assertEquals(length); + this.network.blockchainLength.requireEquals(length); length.assertEquals(y); } diff --git a/src/examples/zkapps/simple_zkapp_with_proof.ts b/src/examples/zkapps/simple_zkapp_with_proof.ts index 78365ae19c..8fbfaeee59 100644 --- a/src/examples/zkapps/simple_zkapp_with_proof.ts +++ b/src/examples/zkapps/simple_zkapp_with_proof.ts @@ -39,7 +39,7 @@ class NotSoSimpleZkapp extends SmartContract { oldProof.verify(); trivialProof.verify(); let x = this.x.get(); - this.x.assertEquals(x); + this.x.requireEquals(x); this.x.set(x.add(y)); } } diff --git a/src/examples/sudoku/index.ts b/src/examples/zkapps/sudoku/index.ts similarity index 100% rename from src/examples/sudoku/index.ts rename to src/examples/zkapps/sudoku/index.ts diff --git a/src/examples/sudoku/sudoku-lib.js b/src/examples/zkapps/sudoku/sudoku-lib.js similarity index 100% rename from src/examples/sudoku/sudoku-lib.js rename to src/examples/zkapps/sudoku/sudoku-lib.js diff --git a/src/examples/sudoku/sudoku.ts b/src/examples/zkapps/sudoku/sudoku.ts similarity index 95% rename from src/examples/sudoku/sudoku.ts rename to src/examples/zkapps/sudoku/sudoku.ts index 003b84c3f2..9af8952ecf 100644 --- a/src/examples/sudoku/sudoku.ts +++ b/src/examples/zkapps/sudoku/sudoku.ts @@ -8,7 +8,7 @@ import { isReady, Poseidon, Struct, - Circuit, + Provable, } from 'o1js'; export { Sudoku, SudokuZkApp }; @@ -98,7 +98,7 @@ class SudokuZkApp extends SmartContract { // finally, we check that the sudoku is the one that was originally deployed let sudokuHash = this.sudokuHash.get(); // get the hash from the blockchain - this.sudokuHash.assertEquals(sudokuHash); // precondition that links this.sudokuHash.get() to the actual on-chain state + this.sudokuHash.requireEquals(sudokuHash); // precondition that links this.sudokuHash.get() to the actual on-chain state sudokuInstance .hash() .assertEquals(sudokuHash, 'sudoku matches the one committed on-chain'); diff --git a/src/examples/zkapps/voting/member.ts b/src/examples/zkapps/voting/member.ts index 1618914b3c..c4b0f6e6cd 100644 --- a/src/examples/zkapps/voting/member.ts +++ b/src/examples/zkapps/voting/member.ts @@ -52,8 +52,8 @@ export class Member extends CircuitValue { return this; } - static empty() { - return new Member(PublicKey.empty(), UInt64.zero); + static empty any>(): InstanceType { + return new Member(PublicKey.empty(), UInt64.zero) as any; } static from(publicKey: PublicKey, balance: UInt64) { diff --git a/src/examples/zkapps/voting/membership.ts b/src/examples/zkapps/voting/membership.ts index 45a5705e3d..9853b70664 100644 --- a/src/examples/zkapps/voting/membership.ts +++ b/src/examples/zkapps/voting/membership.ts @@ -94,7 +94,7 @@ export class Membership_ extends SmartContract { let accountUpdate = AccountUpdate.create(member.publicKey); - accountUpdate.account.balance.assertEquals( + accountUpdate.account.balance.requireEquals( accountUpdate.account.balance.get() ); @@ -110,7 +110,7 @@ export class Membership_ extends SmartContract { ); let accumulatedMembers = this.accumulatedMembers.get(); - this.accumulatedMembers.assertEquals(accumulatedMembers); + this.accumulatedMembers.requireEquals(accumulatedMembers); // checking if the member already exists within the accumulator let { state: exists } = this.reducer.reduce( @@ -148,7 +148,7 @@ export class Membership_ extends SmartContract { // Preconditions: Item exists in committed storage let committedMembers = this.committedMembers.get(); - this.committedMembers.assertEquals(committedMembers); + this.committedMembers.requireEquals(committedMembers); return member.witness .calculateRootSlow(member.getHash()) @@ -162,10 +162,10 @@ export class Membership_ extends SmartContract { // Commit to the items accumulated so far. This is a periodic update let accumulatedMembers = this.accumulatedMembers.get(); - this.accumulatedMembers.assertEquals(accumulatedMembers); + this.accumulatedMembers.requireEquals(accumulatedMembers); let committedMembers = this.committedMembers.get(); - this.committedMembers.assertEquals(committedMembers); + this.committedMembers.requireEquals(committedMembers); let pendingActions = this.reducer.getActions({ fromActionState: accumulatedMembers, diff --git a/src/examples/zkapps/voting/preconditions.ts b/src/examples/zkapps/voting/preconditions.ts index 946ae73d57..c8dccfab59 100644 --- a/src/examples/zkapps/voting/preconditions.ts +++ b/src/examples/zkapps/voting/preconditions.ts @@ -17,7 +17,7 @@ export class ElectionPreconditions { export class ParticipantPreconditions { minMina: UInt64; - maxMina: UInt64; // have to make this "generic" so it applys for both candidate and voter instances + maxMina: UInt64; // have to make this "generic" so it applies for both candidate and voter instances static get default(): ParticipantPreconditions { return new ParticipantPreconditions(UInt64.zero, UInt64.MAXINT()); diff --git a/src/examples/zkapps/voting/run.ts b/src/examples/zkapps/voting/run.ts index e0e2c9836a..b2a3a9ec65 100644 --- a/src/examples/zkapps/voting/run.ts +++ b/src/examples/zkapps/voting/run.ts @@ -8,7 +8,7 @@ import { import { OffchainStorage } from './off_chain_storage.js'; import { Member } from './member.js'; import { testSet } from './test.js'; -import { getProfiler } from '../../profiler.js'; +import { getProfiler } from '../../utils/profiler.js'; console.log('Running Voting script...'); diff --git a/src/examples/zkapps/voting/voting.ts b/src/examples/zkapps/voting/voting.ts index c16f118a7f..2cf12f1666 100644 --- a/src/examples/zkapps/voting/voting.ts +++ b/src/examples/zkapps/voting/voting.ts @@ -116,7 +116,7 @@ export class Voting_ extends SmartContract { @method voterRegistration(member: Member) { let currentSlot = this.network.globalSlotSinceGenesis.get(); - this.network.globalSlotSinceGenesis.assertBetween( + this.network.globalSlotSinceGenesis.requireBetween( currentSlot, currentSlot.add(10) ); @@ -133,7 +133,7 @@ export class Voting_ extends SmartContract { let accountUpdate = AccountUpdate.create(member.publicKey); - accountUpdate.account.balance.assertEquals( + accountUpdate.account.balance.requireEquals( accountUpdate.account.balance.get() ); @@ -165,7 +165,7 @@ export class Voting_ extends SmartContract { @method candidateRegistration(member: Member) { let currentSlot = this.network.globalSlotSinceGenesis.get(); - this.network.globalSlotSinceGenesis.assertBetween( + this.network.globalSlotSinceGenesis.requireBetween( currentSlot, currentSlot.add(10) ); @@ -182,7 +182,7 @@ export class Voting_ extends SmartContract { // this snippet pulls the account data of an address from the network let accountUpdate = AccountUpdate.create(member.publicKey); - accountUpdate.account.balance.assertEquals( + accountUpdate.account.balance.requireEquals( accountUpdate.account.balance.get() ); @@ -229,7 +229,7 @@ export class Voting_ extends SmartContract { @method vote(candidate: Member, voter: Member) { let currentSlot = this.network.globalSlotSinceGenesis.get(); - this.network.globalSlotSinceGenesis.assertBetween( + this.network.globalSlotSinceGenesis.requireBetween( currentSlot, currentSlot.add(10) ); @@ -266,10 +266,10 @@ export class Voting_ extends SmartContract { @method countVotes() { let accumulatedVotes = this.accumulatedVotes.get(); - this.accumulatedVotes.assertEquals(accumulatedVotes); + this.accumulatedVotes.requireEquals(accumulatedVotes); let committedVotes = this.committedVotes.get(); - this.committedVotes.assertEquals(committedVotes); + this.committedVotes.requireEquals(committedVotes); let { state: newCommittedVotes, actionState: newAccumulatedVotes } = this.reducer.reduce( diff --git a/src/examples/zkprogram/README.md b/src/examples/zkprogram/README.md new file mode 100644 index 0000000000..5386fe855f --- /dev/null +++ b/src/examples/zkprogram/README.md @@ -0,0 +1,3 @@ +# ZkProgram + +These examples focus on how to use `ZkProgram`, our main API for creating proofs outside of smart contract. diff --git a/src/examples/zkprogram/gadgets.ts b/src/examples/zkprogram/gadgets.ts new file mode 100644 index 0000000000..1e2508c804 --- /dev/null +++ b/src/examples/zkprogram/gadgets.ts @@ -0,0 +1,78 @@ +import { Field, Provable, Gadgets, ZkProgram } from 'o1js'; + +let cs = Provable.constraintSystem(() => { + let f = Provable.witness(Field, () => Field(12)); + + let res1 = Gadgets.rotate64(f, 2, 'left'); + let res2 = Gadgets.rotate64(f, 2, 'right'); + + res1.assertEquals(Field(48)); + res2.assertEquals(Field(3)); + + Provable.log(res1); + Provable.log(res2); +}); +console.log('constraint system: ', cs); + +const BitwiseProver = ZkProgram({ + name: 'bitwise', + methods: { + rot: { + privateInputs: [], + method: () => { + let a = Provable.witness(Field, () => Field(48)); + let actualLeft = Gadgets.rotate64(a, 2, 'left'); + let actualRight = Gadgets.rotate64(a, 2, 'right'); + + let expectedLeft = Field(192); + actualLeft.assertEquals(expectedLeft); + + let expectedRight = Field(12); + actualRight.assertEquals(expectedRight); + }, + }, + xor: { + privateInputs: [], + method: () => { + let a = Provable.witness(Field, () => Field(5)); + let b = Provable.witness(Field, () => Field(2)); + let actual = Gadgets.xor(a, b, 4); + let expected = Field(7); + actual.assertEquals(expected); + }, + }, + and: { + privateInputs: [], + method: () => { + let a = Provable.witness(Field, () => Field(3)); + let b = Provable.witness(Field, () => Field(5)); + let actual = Gadgets.and(a, b, 4); + let expected = Field(1); + actual.assertEquals(expected); + }, + }, + }, +}); + +console.log('compiling..'); + +console.time('compile'); +await BitwiseProver.compile(); +console.timeEnd('compile'); + +console.log('proving..'); + +console.time('rotation prove'); +let rotProof = await BitwiseProver.rot(); +console.timeEnd('rotation prove'); +if (!(await BitwiseProver.verify(rotProof))) throw Error('rot: Invalid proof'); + +console.time('xor prove'); +let xorProof = await BitwiseProver.xor(); +console.timeEnd('xor prove'); +if (!(await BitwiseProver.verify(xorProof))) throw Error('xor: Invalid proof'); + +console.time('and prove'); +let andProof = await BitwiseProver.and(); +console.timeEnd('and prove'); +if (!(await BitwiseProver.verify(andProof))) throw Error('and: Invalid proof'); diff --git a/src/examples/program-with-input.ts b/src/examples/zkprogram/program-with-input.ts similarity index 92% rename from src/examples/program-with-input.ts rename to src/examples/zkprogram/program-with-input.ts index 71dfafbc2c..16aab5ab1c 100644 --- a/src/examples/program-with-input.ts +++ b/src/examples/zkprogram/program-with-input.ts @@ -1,7 +1,7 @@ import { SelfProof, Field, - Experimental, + ZkProgram, verify, isReady, Proof, @@ -11,7 +11,8 @@ import { await isReady; -let MyProgram = Experimental.ZkProgram({ +let MyProgram = ZkProgram({ + name: 'example-with-input', publicInput: Field, methods: { @@ -35,13 +36,13 @@ let MyProgram = Experimental.ZkProgram({ MyProgram.publicInputType satisfies typeof Field; MyProgram.publicOutputType satisfies Provable; -let MyProof = Experimental.ZkProgram.Proof(MyProgram); +let MyProof = ZkProgram.Proof(MyProgram); console.log('program digest', MyProgram.digest()); console.log('compiling MyProgram...'); let { verificationKey } = await MyProgram.compile(); -console.log('verification key', verificationKey.slice(0, 10) + '..'); +console.log('verification key', verificationKey.data.slice(0, 10) + '..'); console.log('proving base case...'); let proof = await MyProgram.baseCase(Field(0)); diff --git a/src/examples/program.ts b/src/examples/zkprogram/program.ts similarity index 91% rename from src/examples/program.ts rename to src/examples/zkprogram/program.ts index 111ed0b5a8..7cc09602b1 100644 --- a/src/examples/program.ts +++ b/src/examples/zkprogram/program.ts @@ -1,7 +1,7 @@ import { SelfProof, Field, - Experimental, + ZkProgram, verify, isReady, Proof, @@ -12,7 +12,8 @@ import { await isReady; -let MyProgram = Experimental.ZkProgram({ +let MyProgram = ZkProgram({ + name: 'example-with-output', publicOutput: Field, methods: { @@ -36,13 +37,13 @@ let MyProgram = Experimental.ZkProgram({ MyProgram.publicInputType satisfies Provable; MyProgram.publicOutputType satisfies typeof Field; -let MyProof = Experimental.ZkProgram.Proof(MyProgram); +let MyProof = ZkProgram.Proof(MyProgram); console.log('program digest', MyProgram.digest()); console.log('compiling MyProgram...'); let { verificationKey } = await MyProgram.compile(); -console.log('verification key', verificationKey.slice(0, 10) + '..'); +console.log('verification key', verificationKey.data.slice(0, 10) + '..'); console.log('proving base case...'); let proof = await MyProgram.baseCase(); diff --git a/src/index.ts b/src/index.ts index 0813d3da4d..243fe7dbfa 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,13 +1,25 @@ export type { ProvablePure } from './snarky.js'; export { Ledger } from './snarky.js'; export { Field, Bool, Group, Scalar } from './lib/core.js'; +export { + createForeignField, + ForeignField, + AlmostForeignField, + CanonicalForeignField, +} from './lib/foreign-field.js'; +export { createForeignCurve, ForeignCurve } from './lib/foreign-curve.js'; +export { createEcdsa, EcdsaSignature } from './lib/foreign-ecdsa.js'; export { Poseidon, TokenSymbol } from './lib/hash.js'; +export { Keccak } from './lib/keccak.js'; +export { Hash } from './lib/hashes-combined.js'; + export * from './lib/signature.js'; export type { ProvableExtended, FlexibleProvable, FlexibleProvablePure, InferProvable, + Unconstrained, } from './lib/circuit_value.js'; export { CircuitValue, @@ -20,7 +32,9 @@ export { } from './lib/circuit_value.js'; export { Provable } from './lib/provable.js'; export { Circuit, Keypair, public_, circuitMain } from './lib/circuit.js'; -export { UInt32, UInt64, Int64, Sign } from './lib/int.js'; +export { UInt32, UInt64, Int64, Sign, UInt8 } from './lib/int.js'; +export { Bytes } from './lib/provable-types/provable-types.js'; +export { Gadgets } from './lib/gadgets/gadgets.js'; export { Types } from './bindings/mina-transaction/types.js'; export * as Mina from './lib/mina.js'; @@ -30,7 +44,6 @@ export { method, declareMethods, Account, - VerificationKey, Reducer, } from './lib/zkapp.js'; export { state, State, declareState } from './lib/state.js'; @@ -43,7 +56,9 @@ export { Empty, Undefined, Void, + VerificationKey, } from './lib/proof_system.js'; +export { Cache, CacheHeader } from './lib/proof-system/cache.js'; export { Token, @@ -65,6 +80,7 @@ export { setGraphqlEndpoints, setArchiveGraphqlEndpoint, sendZkapp, + Lightnet, } from './lib/fetch.js'; export * as Encryption from './lib/encryption.js'; export * as Encoding from './bindings/lib/encoding.js'; @@ -74,8 +90,12 @@ export { MerkleMap, MerkleMapWitness } from './lib/merkle_map.js'; export { Nullifier } from './lib/nullifier.js'; +import { ExperimentalZkProgram, ZkProgram } from './lib/proof_system.js'; +export { ZkProgram }; + +export { Crypto } from './lib/crypto.js'; + // experimental APIs -import { ZkProgram } from './lib/proof_system.js'; import { Callback } from './lib/zkapp.js'; import { createChildAccountUpdate } from './lib/account_update.js'; import { memoizeWitness } from './lib/provable.js'; @@ -85,7 +105,6 @@ const Experimental_ = { Callback, createChildAccountUpdate, memoizeWitness, - ZkProgram, }; type Callback_ = Callback; @@ -95,14 +114,17 @@ type Callback_ = Callback; * (Not unstable in the sense that they are less functional or tested than other parts.) */ namespace Experimental { - export let ZkProgram = Experimental_.ZkProgram; + /** @deprecated `ZkProgram` has moved out of the Experimental namespace and is now directly available as a top-level import `ZkProgram`. + * The old `Experimental.ZkProgram` API has been deprecated in favor of the new `ZkProgram` top-level import. + */ + export let ZkProgram = ExperimentalZkProgram; export let createChildAccountUpdate = Experimental_.createChildAccountUpdate; export let memoizeWitness = Experimental_.memoizeWitness; export let Callback = Experimental_.Callback; export type Callback = Callback_; } -Error.stackTraceLimit = 1000; +Error.stackTraceLimit = 100000; // deprecated stuff export { isReady, shutdown }; diff --git a/src/lib/account_update.ts b/src/lib/account_update.ts index 292b77d0cb..23f5e66c08 100644 --- a/src/lib/account_update.ts +++ b/src/lib/account_update.ts @@ -445,7 +445,7 @@ const Body = { tokenId?: Field, mayUseToken?: MayUseToken ): Body { - let { body } = Types.AccountUpdate.emptyValue(); + let { body } = Types.AccountUpdate.empty(); body.publicKey = publicKey; if (tokenId) { body.tokenId = tokenId; @@ -463,7 +463,7 @@ const Body = { }, dummy(): Body { - return Types.AccountUpdate.emptyValue().body; + return Types.AccountUpdate.empty().body; }, }; @@ -1277,6 +1277,9 @@ class AccountUpdate implements Types.AccountUpdate { return [{ lazyAuthorization, children, parent, id, label }, aux]; } static toInput = Types.AccountUpdate.toInput; + static empty() { + return AccountUpdate.dummy(); + } static check = Types.AccountUpdate.check; static fromFields(fields: Field[], [other, aux]: any[]): AccountUpdate { let accountUpdate = Types.AccountUpdate.fromFields(fields, aux); @@ -1502,6 +1505,15 @@ class AccountUpdate implements Types.AccountUpdate { body[key] = JSON.stringify(body[key]) as any; } } + if (body.authorizationKind?.isProved === false) { + delete (body as any).authorizationKind?.verificationKeyHash; + } + if ( + body.authorizationKind?.isProved === false && + body.authorizationKind?.isSigned === false + ) { + delete (body as any).authorizationKind; + } if ( jsonUpdate.authorization !== undefined || body.authorizationKind?.isProved === true || @@ -1509,6 +1521,7 @@ class AccountUpdate implements Types.AccountUpdate { ) { (body as any).authorization = jsonUpdate.authorization; } + body.mayUseToken = { parentsOwnToken: this.body.mayUseToken.parentsOwnToken.toBoolean(), inheritFromParent: this.body.mayUseToken.inheritFromParent.toBoolean(), diff --git a/src/lib/bool.ts b/src/lib/bool.ts index 387a4908da..8957e89d53 100644 --- a/src/lib/bool.ts +++ b/src/lib/bool.ts @@ -11,7 +11,7 @@ import { defineBinable } from '../bindings/lib/binable.js'; import { NonNegativeInteger } from '../bindings/crypto/non-negative.js'; import { asProver } from './provable-context.js'; -export { BoolVar, Bool, isBool }; +export { BoolVar, Bool }; // same representation, but use a different name to communicate intent / constraints type BoolVar = FieldVar; @@ -34,7 +34,7 @@ class Bool { value: BoolVar; constructor(x: boolean | Bool | BoolVar) { - if (Bool.#isBool(x)) { + if (x instanceof Bool) { this.value = x.value; return; } @@ -75,7 +75,7 @@ class Bool { if (this.isConstant() && isConstant(y)) { return new Bool(this.toBoolean() && toBoolean(y)); } - return new Bool(Snarky.bool.and(this.value, Bool.#toVar(y))); + return new Bool(Snarky.bool.and(this.value, toFieldVar(y))); } /** @@ -87,7 +87,7 @@ class Bool { if (this.isConstant() && isConstant(y)) { return new Bool(this.toBoolean() || toBoolean(y)); } - return new Bool(Snarky.bool.or(this.value, Bool.#toVar(y))); + return new Bool(Snarky.bool.or(this.value, toFieldVar(y))); } /** @@ -102,7 +102,7 @@ class Bool { } return; } - Snarky.bool.assertEqual(this.value, Bool.#toVar(y)); + Snarky.bool.assertEqual(this.value, toFieldVar(y)); } catch (err) { throw withMessage(err, message); } @@ -144,7 +144,7 @@ class Bool { if (this.isConstant() && isConstant(y)) { return new Bool(this.toBoolean() === toBoolean(y)); } - return new Bool(Snarky.bool.equals(this.value, Bool.#toVar(y))); + return new Bool(Snarky.bool.equals(this.value, toFieldVar(y))); } /** @@ -194,14 +194,14 @@ class Bool { } static toField(x: Bool | boolean): Field { - return new Field(Bool.#toVar(x)); + return new Field(toFieldVar(x)); } /** * Boolean negation. */ static not(x: Bool | boolean): Bool { - if (Bool.#isBool(x)) { + if (x instanceof Bool) { return x.not(); } return new Bool(!x); @@ -211,7 +211,7 @@ class Bool { * Boolean AND operation. */ static and(x: Bool | boolean, y: Bool | boolean): Bool { - if (Bool.#isBool(x)) { + if (x instanceof Bool) { return x.and(y); } return new Bool(x).and(y); @@ -221,7 +221,7 @@ class Bool { * Boolean OR operation. */ static or(x: Bool | boolean, y: Bool | boolean): Bool { - if (Bool.#isBool(x)) { + if (x instanceof Bool) { return x.or(y); } return new Bool(x).or(y); @@ -231,7 +231,7 @@ class Bool { * Asserts if both {@link Bool} are equal. */ static assertEqual(x: Bool, y: Bool | boolean): void { - if (Bool.#isBool(x)) { + if (x instanceof Bool) { x.assertEquals(y); return; } @@ -242,7 +242,7 @@ class Bool { * Checks two {@link Bool} for equality. */ static equal(x: Bool | boolean, y: Bool | boolean): Bool { - if (Bool.#isBool(x)) { + if (x instanceof Bool) { return x.equals(y); } return new Bool(x).equals(y); @@ -295,6 +295,10 @@ class Bool { return 1; } + static empty() { + return new Bool(false); + } + static toInput(x: Bool): { packed: [Field, number][] } { return { packed: [[x.toField(), 1] as [Field, number]] }; } @@ -314,9 +318,7 @@ class Bool { return BoolBinable.readBytes(bytes, offset); } - static sizeInBytes() { - return 1; - } + static sizeInBytes = 1; static check(x: Bool): void { Snarky.field.assertBoolean(x.value); @@ -340,15 +342,6 @@ class Bool { return new Bool(x.value); }, }; - - static #isBool(x: boolean | Bool | BoolVar): x is Bool { - return x instanceof Bool; - } - - static #toVar(x: boolean | Bool): BoolVar { - if (Bool.#isBool(x)) return x.value; - return FieldVar.constant(B(x)); - } } const BoolBinable = defineBinable({ @@ -360,6 +353,8 @@ const BoolBinable = defineBinable({ }, }); +// internal helper functions + function isConstant(x: boolean | Bool): x is boolean | ConstantBool { if (typeof x === 'boolean') { return true; @@ -368,10 +363,6 @@ function isConstant(x: boolean | Bool): x is boolean | ConstantBool { return x.isConstant(); } -function isBool(x: unknown) { - return x instanceof Bool; -} - function toBoolean(x: boolean | Bool): boolean { if (typeof x === 'boolean') { return x; @@ -379,6 +370,11 @@ function toBoolean(x: boolean | Bool): boolean { return x.toBoolean(); } +function toFieldVar(x: boolean | Bool): BoolVar { + if (x instanceof Bool) return x.value; + return FieldVar.constant(B(x)); +} + // TODO: This is duplicated function withMessage(error: unknown, message?: string) { if (message === undefined || !(error instanceof Error)) return error; diff --git a/src/lib/circuit.ts b/src/lib/circuit.ts index 232f3f4f0d..dd697cc967 100644 --- a/src/lib/circuit.ts +++ b/src/lib/circuit.ts @@ -1,3 +1,4 @@ +import 'reflect-metadata'; import { ProvablePure, Snarky } from '../snarky.js'; import { MlFieldArray, MlFieldConstArray } from './ml/fields.js'; import { withThreadPool } from '../bindings/js/wrapper.js'; diff --git a/src/lib/circuit_value.ts b/src/lib/circuit_value.ts index 9f83d5fd02..d42f267cdf 100644 --- a/src/lib/circuit_value.ts +++ b/src/lib/circuit_value.ts @@ -1,9 +1,10 @@ import 'reflect-metadata'; -import { ProvablePure } from '../snarky.js'; +import { ProvablePure, Snarky } from '../snarky.js'; import { Field, Bool, Scalar, Group } from './core.js'; import { provable, provablePure, + provableTuple, HashInput, NonMethods, } from '../bindings/lib/provable-snarky.js'; @@ -14,6 +15,9 @@ import type { IsPure, } from '../bindings/lib/provable-snarky.js'; import { Provable } from './provable.js'; +import { assert } from './errors.js'; +import { inCheckedComputation } from './provable-context.js'; +import { Proof } from './proof_system.js'; // external API export { @@ -32,21 +36,23 @@ export { // internal API export { + provableTuple, AnyConstructor, cloneCircuitValue, circuitValueEquals, toConstant, - isConstant, InferProvable, HashInput, InferJson, InferredProvable, + Unconstrained, }; type ProvableExtension = { toInput: (x: T) => { fields?: Field[]; packed?: [Field, number][] }; toJSON: (x: T) => TJson; fromJSON: (x: TJson) => T; + empty: () => T; }; type ProvableExtended = Provable & @@ -244,6 +250,15 @@ abstract class CircuitValue { } return Object.assign(Object.create(this.prototype), props); } + + static empty(): InstanceType { + const fields: [string, any][] = (this as any).prototype._fields ?? []; + let props: any = {}; + fields.forEach(([key, propType]) => { + props[key] = propType.empty(); + }); + return Object.assign(Object.create(this.prototype), props); + } } function prop(this: any, target: any, key: string) { @@ -372,6 +387,7 @@ function Struct< }; toJSON: (x: T) => J; fromJSON: (x: J) => T; + empty: () => T; } { class Struct_ { static type = provable(type); @@ -429,6 +445,15 @@ function Struct< let struct = Object.create(this.prototype); return Object.assign(struct, value); } + /** + * Create an instance of this struct filled with default values + * @returns an empty instance of this struct + */ + static empty(): T { + let value = this.type.empty(); + let struct = Object.create(this.prototype); + return Object.assign(struct, value); + } /** * This method is for internal use, you will probably not need it. * Method to make assertions which should be always made whenever a struct of this type is created in a proof. @@ -452,6 +477,93 @@ function Struct< return Struct_ as any; } +/** + * Container which holds an unconstrained value. This can be used to pass values + * between the out-of-circuit blocks in provable code. + * + * Invariants: + * - An `Unconstrained`'s value can only be accessed in auxiliary contexts. + * - An `Unconstrained` can be empty when compiling, but never empty when running as the prover. + * (there is no way to create an empty `Unconstrained` in the prover) + * + * @example + * ```ts + * let x = Unconstrained.from(0n); + * + * class MyContract extends SmartContract { + * `@method` myMethod(x: Unconstrained) { + * + * Provable.witness(Field, () => { + * // we can access and modify `x` here + * let newValue = x.get() + otherField.toBigInt(); + * x.set(newValue); + * + * // ... + * }); + * + * // throws an error! + * x.get(); + * } + * ``` + */ +class Unconstrained { + private option: + | { isSome: true; value: T } + | { isSome: false; value: undefined }; + + private constructor(isSome: boolean, value?: T) { + this.option = { isSome, value: value as any }; + } + + /** + * Read an unconstrained value. + * + * Note: Can only be called outside provable code. + */ + get(): T { + if (inCheckedComputation() && !Snarky.run.inProverBlock()) + throw Error(`You cannot use Unconstrained.get() in provable code. + +The only place where you can read unconstrained values is in Provable.witness() +and Provable.asProver() blocks, which execute outside the proof. +`); + assert(this.option.isSome, 'Empty `Unconstrained`'); // never triggered + return this.option.value; + } + + /** + * Modify the unconstrained value. + */ + set(value: T) { + this.option = { isSome: true, value }; + } + + /** + * Create an `Unconstrained` with the given `value`. + */ + static from(value: T) { + return new Unconstrained(true, value); + } + + /** + * Create an `Unconstrained` from a witness computation. + */ + static witness(compute: () => T) { + return Provable.witness( + Unconstrained.provable, + () => new Unconstrained(true, compute()) + ); + } + + static provable: Provable> = { + sizeInFields: () => 0, + toFields: () => [], + toAuxiliary: (t?: any) => [t ?? new Unconstrained(false)], + fromFields: (_, [t]) => t, + check: () => {}, + }; +} + let primitives = new Set([Field, Bool, Scalar, Group]); function isPrimitive(obj: any) { for (let P of primitives) { @@ -485,10 +597,13 @@ function cloneCircuitValue(obj: T): T { ) as any as T; if (ArrayBuffer.isView(obj)) return new (obj.constructor as any)(obj); - // o1js primitives aren't cloned + // o1js primitives and proofs aren't cloned if (isPrimitive(obj)) { return obj; } + if (obj instanceof Proof) { + return obj; + } // cloning strategy that works for plain objects AND classes whose constructor only assigns properties let propertyDescriptors: Record = {}; @@ -577,8 +692,3 @@ function toConstant(type: Provable, value: T): T { type.toAuxiliary(value) ); } - -function isConstant(type: FlexibleProvable, value: T): boolean; -function isConstant(type: Provable, value: T): boolean { - return type.toFields(value).every((x) => x.isConstant()); -} diff --git a/src/lib/circuit_value.unit-test.ts b/src/lib/circuit_value.unit-test.ts index 6372c52a7f..79fbbafa9d 100644 --- a/src/lib/circuit_value.unit-test.ts +++ b/src/lib/circuit_value.unit-test.ts @@ -1,4 +1,4 @@ -import { provable, Struct } from './circuit_value.js'; +import { provable, Struct, Unconstrained } from './circuit_value.js'; import { UInt32 } from './int.js'; import { PrivateKey, PublicKey } from './signature.js'; import { expect } from 'expect'; @@ -96,6 +96,7 @@ class MyStructPure extends Struct({ class MyTuple extends Struct([PublicKey, String]) {} let targetString = 'some particular string'; +let targetBigint = 99n; let gotTargetString = false; // create a smart contract and pass auxiliary data to a method @@ -106,11 +107,22 @@ class MyContract extends SmartContract { // this works because MyStructPure only contains field elements @state(MyStructPure) x = State(); - @method myMethod(value: MyStruct, tuple: MyTuple, update: AccountUpdate) { + @method myMethod( + value: MyStruct, + tuple: MyTuple, + update: AccountUpdate, + unconstrained: Unconstrained + ) { // check if we can pass in string values if (value.other === targetString) gotTargetString = true; value.uint[0].assertEquals(UInt32.zero); + // cannot access unconstrained values in provable code + if (Provable.inCheckedComputation()) + expect(() => unconstrained.get()).toThrow( + 'You cannot use Unconstrained.get() in provable code.' + ); + Provable.asProver(() => { let err = 'wrong value in prover'; if (tuple[1] !== targetString) throw Error(err); @@ -119,6 +131,9 @@ class MyContract extends SmartContract { if (update.lazyAuthorization?.kind !== 'lazy-signature') throw Error(err); if (update.lazyAuthorization.privateKey?.toBase58() !== key.toBase58()) throw Error(err); + + // check if we can pass in unconstrained values + if (unconstrained.get() !== targetBigint) throw Error(err); }); } } @@ -141,7 +156,8 @@ let tx = await transaction(() => { uint: [UInt32.from(0), UInt32.from(10)], }, [address, targetString], - accountUpdate + accountUpdate, + Unconstrained.from(targetBigint) ); }); diff --git a/src/lib/crypto.ts b/src/lib/crypto.ts new file mode 100644 index 0000000000..3786284a9e --- /dev/null +++ b/src/lib/crypto.ts @@ -0,0 +1,31 @@ +import { CurveParams as CurveParams_ } from '../bindings/crypto/elliptic-curve-examples.js'; +import { + CurveAffine, + createCurveAffine, +} from '../bindings/crypto/elliptic_curve.js'; + +// crypto namespace +const Crypto = { + /** + * Create elliptic curve arithmetic methods. + */ + createCurve(params: Crypto.CurveParams): Crypto.Curve { + return createCurveAffine(params); + }, + /** + * Parameters defining an elliptic curve in short Weierstraß form + * y^2 = x^3 + ax + b + */ + CurveParams: CurveParams_, +}; + +namespace Crypto { + /** + * Parameters defining an elliptic curve in short Weierstraß form + * y^2 = x^3 + ax + b + */ + export type CurveParams = CurveParams_; + + export type Curve = CurveAffine; +} +export { Crypto }; diff --git a/src/lib/errors.ts b/src/lib/errors.ts index 80307985c8..b7e65c0338 100644 --- a/src/lib/errors.ts +++ b/src/lib/errors.ts @@ -274,6 +274,9 @@ function Bug(message: string) { /** * Make an assertion. When failing, this will communicate to users it's not their fault but indicates an internal bug. */ -function assert(condition: boolean, message = 'Failed assertion.') { +function assert( + condition: boolean, + message = 'Failed assertion.' +): asserts condition { if (!condition) throw Bug(message); } diff --git a/src/lib/events.ts b/src/lib/events.ts index 3e92a30389..70fce76400 100644 --- a/src/lib/events.ts +++ b/src/lib/events.ts @@ -1,8 +1,8 @@ import { prefixes } from '../bindings/crypto/constants.js'; import { prefixToField } from '../bindings/lib/binable.js'; import { - GenericField, GenericProvableExtended, + GenericSignableField, } from '../bindings/lib/generic.js'; export { createEvents, dataAsHash }; @@ -15,7 +15,7 @@ function createEvents({ Field, Poseidon, }: { - Field: GenericField; + Field: GenericSignableField; Poseidon: Poseidon; }) { type Event = Field[]; @@ -60,7 +60,7 @@ function createEvents({ const EventsProvable = { ...Events, ...dataAsHash({ - emptyValue: Events.empty, + empty: Events.empty, toJSON(data: Field[][]) { return data.map((row) => row.map((e) => Field.toJSON(e))); }, @@ -107,7 +107,7 @@ function createEvents({ const SequenceEventsProvable = { ...Actions, ...dataAsHash({ - emptyValue: Actions.empty, + empty: Actions.empty, toJSON(data: Field[][]) { return data.map((row) => row.map((e) => Field.toJSON(e))); }, @@ -123,18 +123,16 @@ function createEvents({ } function dataAsHash({ - emptyValue, + empty, toJSON, fromJSON, }: { - emptyValue: () => { data: T; hash: Field }; + empty: () => { data: T; hash: Field }; toJSON: (value: T) => J; fromJSON: (json: J) => { data: T; hash: Field }; -}): GenericProvableExtended<{ data: T; hash: Field }, J, Field> & { - emptyValue(): { data: T; hash: Field }; -} { +}): GenericProvableExtended<{ data: T; hash: Field }, J, Field> { return { - emptyValue, + empty, sizeInFields() { return 1; }, @@ -142,7 +140,7 @@ function dataAsHash({ return [hash]; }, toAuxiliary(value) { - return [value?.data ?? emptyValue().data]; + return [value?.data ?? empty().data]; }, fromFields([hash], [data]) { return { data, hash }; diff --git a/src/lib/fetch.ts b/src/lib/fetch.ts index c9b3d5cd66..88b87c7e87 100644 --- a/src/lib/fetch.ts +++ b/src/lib/fetch.ts @@ -2,7 +2,7 @@ import 'isomorphic-fetch'; import { Field } from './core.js'; import { UInt32, UInt64 } from './int.js'; import { Actions, TokenId } from './account_update.js'; -import { PublicKey } from './signature.js'; +import { PublicKey, PrivateKey } from './signature.js'; import { NetworkValue } from './precondition.js'; import { Types } from '../bindings/mina-transaction/types.js'; import { ActionStates } from './mina.js'; @@ -38,11 +38,13 @@ export { setMinaGraphqlFallbackEndpoints, setArchiveGraphqlEndpoint, setArchiveGraphqlFallbackEndpoints, + setLightnetAccountManagerEndpoint, sendZkappQuery, sendZkapp, removeJsonQuotes, fetchEvents, fetchActions, + Lightnet }; type NetworkConfig = { @@ -50,6 +52,7 @@ type NetworkConfig = { minaFallbackEndpoints: string[]; archiveEndpoint: string; archiveFallbackEndpoints: string[]; + lightnetAccountManagerEndpoint: string; }; let networkConfig = { @@ -57,6 +60,7 @@ let networkConfig = { minaFallbackEndpoints: [] as string[], archiveEndpoint: '', archiveFallbackEndpoints: [] as string[], + lightnetAccountManagerEndpoint: '', } satisfies NetworkConfig; function checkForValidUrl(url: string) { @@ -114,6 +118,20 @@ function setArchiveGraphqlFallbackEndpoints(graphqlEndpoints: string[]) { networkConfig.archiveFallbackEndpoints = graphqlEndpoints; } +/** + * Sets up the lightnet account manager endpoint to be used for accounts acquisition and releasing. + * + * @param endpoint Account manager endpoint. + */ +function setLightnetAccountManagerEndpoint(endpoint: string) { + if (!checkForValidUrl(endpoint)) { + throw new Error( + `Invalid account manager endpoint: ${endpoint}. Please specify a valid URL.` + ); + } + networkConfig.lightnetAccountManagerEndpoint = endpoint; +} + /** * Gets account information on the specified publicKey by performing a GraphQL query * to the specified endpoint. This will call the 'GetAccountInfo' query which fetches @@ -463,8 +481,8 @@ type LastBlockQueryFailureCheckResponse = { }[]; }; -const lastBlockQueryFailureCheck = `{ - bestChain(maxLength: 1) { +const lastBlockQueryFailureCheck = (length: number) => `{ + bestChain(maxLength: ${length}) { transactions { zkappCommands { hash @@ -478,10 +496,11 @@ const lastBlockQueryFailureCheck = `{ }`; async function fetchLatestBlockZkappStatus( + blockLength: number, graphqlEndpoint = networkConfig.minaEndpoint ) { let [resp, error] = await makeGraphqlRequest( - lastBlockQueryFailureCheck, + lastBlockQueryFailureCheck(blockLength), graphqlEndpoint, networkConfig.minaFallbackEndpoints ); @@ -495,9 +514,8 @@ async function fetchLatestBlockZkappStatus( return bestChain; } -async function checkZkappTransaction(txnId: string) { - let bestChainBlocks = await fetchLatestBlockZkappStatus(); - +async function checkZkappTransaction(txnId: string, blockLength = 20) { + let bestChainBlocks = await fetchLatestBlockZkappStatus(blockLength); for (let block of bestChainBlocks.bestChain) { for (let zkappCommand of block.transactions.zkappCommands) { if (zkappCommand.hash === txnId) { @@ -933,10 +951,8 @@ async function fetchActions( break; } } - // Archive Node API returns actions in the latest order, so we reverse the array to get the actions in chronological order. - fetchedActions.reverse(); - let actionsList: { actions: string[][]; hash: string }[] = []; + let actionsList: { actions: string[][]; hash: string }[] = []; // correct for archive node sending one block too many if ( fetchedActions.length !== 0 && @@ -993,6 +1009,131 @@ async function fetchActions( return actionsList; } +namespace Lightnet { + /** + * Gets random key pair (public and private keys) from account manager + * that operates with accounts configured in target network Genesis Ledger. + * + * If an error is returned by the specified endpoint, an error is thrown. Otherwise, + * the data is returned. + * + * @param options.isRegularAccount Whether to acquire key pair of regular or zkApp account (one with already configured verification key) + * @param options.lightnetAccountManagerEndpoint Account manager endpoint to fetch from + * @returns Key pair + */ + export async function acquireKeyPair( + options: { + isRegularAccount?: boolean; + lightnetAccountManagerEndpoint?: string; + } = {} + ): Promise<{ + publicKey: PublicKey; + privateKey: PrivateKey; + }> { + const { + isRegularAccount = true, + lightnetAccountManagerEndpoint = networkConfig.lightnetAccountManagerEndpoint, + } = options; + const response = await fetch( + `${lightnetAccountManagerEndpoint}/acquire-account?isRegularAccount=${isRegularAccount}`, + { + method: 'GET', + headers: { + 'Content-Type': 'application/json', + }, + } + ); + + if (response.ok) { + const data = await response.json(); + if (data) { + return { + publicKey: PublicKey.fromBase58(data.pk), + privateKey: PrivateKey.fromBase58(data.sk), + }; + } + } + + throw new Error('Failed to acquire the key pair'); + } + + /** + * Releases previously acquired key pair by public key. + * + * @param options.publicKey Public key of previously acquired key pair to release + * @param options.lightnetAccountManagerEndpoint Account manager endpoint to fetch from + * @returns Response message from the account manager as string or null if the request failed + */ + export async function releaseKeyPair(options: { + publicKey: string; + lightnetAccountManagerEndpoint?: string; + }): Promise { + const { + publicKey, + lightnetAccountManagerEndpoint = networkConfig.lightnetAccountManagerEndpoint, + } = options; + const response = await fetch( + `${lightnetAccountManagerEndpoint}/release-account`, + { + method: 'PUT', + headers: { + 'Content-Type': 'application/json', + }, + body: JSON.stringify({ + pk: publicKey, + }), + } + ); + + if (response.ok) { + const data = await response.json(); + if (data) { + return data.message as string; + } + } + + return null; + } + + /** + * Gets previously acquired key pairs list. + * + * @param options.lightnetAccountManagerEndpoint Account manager endpoint to fetch from + * @returns Key pairs list or null if the request failed + */ + export async function listAcquiredKeyPairs(options: { + lightnetAccountManagerEndpoint?: string; + }): Promise | null> { + const { + lightnetAccountManagerEndpoint = networkConfig.lightnetAccountManagerEndpoint, + } = options; + const response = await fetch( + `${lightnetAccountManagerEndpoint}/list-acquired-accounts`, + { + method: 'GET', + headers: { + 'Content-Type': 'application/json', + }, + } + ); + + if (response.ok) { + const data = await response.json(); + if (data) { + return data.map((account: any) => ({ + publicKey: PublicKey.fromBase58(account.pk), + privateKey: PrivateKey.fromBase58(account.sk), + })); + } + } + + return null; + } +} + function updateActionState(actions: string[][], actionState: Field) { let actionHash = Actions.fromJSON(actions).hash; return Actions.updateSequenceState(actionState, actionHash); diff --git a/src/lib/field.ts b/src/lib/field.ts index 8b7bf25df1..a46c00b840 100644 --- a/src/lib/field.ts +++ b/src/lib/field.ts @@ -11,14 +11,17 @@ export { Field }; // internal API export { - ConstantField, FieldType, FieldVar, FieldConst, - isField, + ConstantField, + VarField, + VarFieldVar, withMessage, readVarMessage, toConstantField, + toFp, + checkBitLength, }; type FieldConst = [0, bigint]; @@ -27,7 +30,7 @@ function constToBigint(x: FieldConst): Fp { return x[1]; } function constFromBigint(x: Fp): FieldConst { - return [0, x]; + return [0, Fp(x)]; } const FieldConst = { @@ -38,7 +41,7 @@ const FieldConst = { }, [0]: constFromBigint(0n), [1]: constFromBigint(1n), - [-1]: constFromBigint(Fp(-1n)), + [-1]: constFromBigint(-1n), }; enum FieldType { @@ -68,6 +71,7 @@ type FieldVar = | [FieldType.Scale, FieldConst, FieldVar]; type ConstantFieldVar = [FieldType.Constant, FieldConst]; +type VarFieldVar = [FieldType.Var, number]; const FieldVar = { constant(x: bigint | FieldConst): ConstantFieldVar { @@ -77,13 +81,25 @@ const FieldVar = { isConstant(x: FieldVar): x is ConstantFieldVar { return x[0] === FieldType.Constant; }, - // TODO: handle (special) constants + isVar(x: FieldVar): x is VarFieldVar { + return x[0] === FieldType.Var; + }, add(x: FieldVar, y: FieldVar): FieldVar { + if (FieldVar.isConstant(x) && x[1][1] === 0n) return y; + if (FieldVar.isConstant(y) && y[1][1] === 0n) return x; + if (FieldVar.isConstant(x) && FieldVar.isConstant(y)) { + return FieldVar.constant(Fp.add(x[1][1], y[1][1])); + } return [FieldType.Add, x, y]; }, - // TODO: handle (special) constants - scale(c: FieldConst, x: FieldVar): FieldVar { - return [FieldType.Scale, c, x]; + scale(c: bigint | FieldConst, x: FieldVar): FieldVar { + let c0 = typeof c === 'bigint' ? FieldConst.fromBigint(c) : c; + if (c0[1] === 0n) return FieldVar.constant(0n); + if (c0[1] === 1n) return x; + if (FieldVar.isConstant(x)) { + return FieldVar.constant(Fp.mul(c0[1], x[1][1])); + } + return [FieldType.Scale, c0, x]; }, [0]: [FieldType.Constant, FieldConst[0]] satisfies ConstantFieldVar, [1]: [FieldType.Constant, FieldConst[1]] satisfies ConstantFieldVar, @@ -91,6 +107,7 @@ const FieldVar = { }; type ConstantField = Field & { value: ConstantFieldVar }; +type VarField = Field & { value: VarFieldVar }; /** * A {@link Field} is an element of a prime order [finite field](https://en.wikipedia.org/wiki/Finite_field). @@ -138,7 +155,7 @@ class Field { * Coerce anything "field-like" (bigint, number, string, and {@link Field}) to a Field. */ constructor(x: bigint | number | string | Field | FieldVar | FieldConst) { - if (Field.#isField(x)) { + if (x instanceof Field) { this.value = x.value; return; } @@ -158,21 +175,9 @@ class Field { } // helpers - static #isField( - x: bigint | number | string | Field | FieldVar | FieldConst - ): x is Field { - return x instanceof Field; - } - static #toConst(x: bigint | number | string | ConstantField): FieldConst { - if (Field.#isField(x)) return x.value[1]; - return FieldConst.fromBigint(Fp(x)); - } - static #toVar(x: bigint | number | string | Field): FieldVar { - if (Field.#isField(x)) return x.value; - return FieldVar.constant(Fp(x)); - } + static from(x: bigint | number | string | Field): Field { - if (Field.#isField(x)) return x; + if (x instanceof Field) return x; return new Field(x); } @@ -198,10 +203,6 @@ class Field { return this.value[0] === FieldType.Constant; } - #toConstant(name: string): ConstantField { - return toConstantField(this, name, 'x', 'field element'); - } - /** * Create a {@link Field} element equivalent to this {@link Field} element's value, * but is a constant. @@ -216,7 +217,7 @@ class Field { * @return A constant {@link Field} element equivalent to this {@link Field} element. */ toConstant(): ConstantField { - return this.#toConstant('toConstant'); + return toConstant(this, 'toConstant'); } /** @@ -233,7 +234,7 @@ class Field { * @return A bigint equivalent to the bigint representation of the Field. */ toBigInt() { - let x = this.#toConstant('toBigInt'); + let x = toConstant(this, 'toBigInt'); return FieldConst.toBigint(x.value[1]); } @@ -251,7 +252,7 @@ class Field { * @return A string equivalent to the string representation of the Field. */ toString() { - return this.#toConstant('toString').toBigInt().toString(); + return toConstant(this, 'toString').toBigInt().toString(); } /** @@ -272,14 +273,14 @@ class Field { } return; } - Snarky.field.assertEqual(this.value, Field.#toVar(y)); + Snarky.field.assertEqual(this.value, toFieldVar(y)); } catch (err) { throw withMessage(err, message); } } /** - * Add a "field-like" value to this {@link Field} element. + * Add a field-like value to this {@link Field} element. * * @example * ```ts @@ -311,7 +312,7 @@ class Field { return new Field(Fp.add(this.toBigInt(), toFp(y))); } // return new AST node Add(x, y) - let z = Snarky.field.add(this.value, Field.#toVar(y)); + let z = Snarky.field.add(this.value, toFieldVar(y)); return new Field(z); } @@ -438,7 +439,7 @@ class Field { } // if one of the factors is constant, return Scale AST node if (isConstant(y)) { - let z = Snarky.field.scale(Field.#toConst(y), this.value); + let z = Snarky.field.scale(toFieldConst(y), this.value); return new Field(z); } if (this.isConstant()) { @@ -520,7 +521,8 @@ class Field { * @return A {@link Field} element equivalent to the modular division of the two value. */ div(y: Field | bigint | number | string) { - // TODO this is the same as snarky-ml but could use 1 constraint instead of 2 + // this intentionally uses 2 constraints instead of 1 to avoid an unconstrained output when dividing 0/0 + // (in this version, division by 0 is strictly not allowed) return this.mul(Field.from(y).inv()); } @@ -615,7 +617,7 @@ class Field { // ^^^ these prove that b = Bool(x === 0): // if x = 0, the 2nd equation implies b = 1 // if x != 0, the 1st implies b = 0 - return Bool.Unsafe.ofField(new Field(b)); + return new Bool(b); } /** @@ -633,10 +635,6 @@ class Field { */ equals(y: Field | bigint | number | string): Bool { // x == y is equivalent to x - y == 0 - // TODO: this is less efficient than possible for equivalence with snarky-ml - return this.sub(y).isZero(); - // more efficient code is commented below - /* // if one of the two is constant, we just need the two constraints in `isZero` if (this.isConstant() || isConstant(y)) { return this.sub(y).isZero(); @@ -647,28 +645,6 @@ class Field { ); Snarky.field.assertEqual(this.sub(y).value, xMinusY); return new Field(xMinusY).isZero(); - */ - } - - // internal base method for all comparisons - #compare(y: FieldVar) { - // TODO: support all bit lengths - let maxLength = Fp.sizeInBits - 2; - asProver(() => { - let actualLength = Math.max( - this.toBigInt().toString(2).length, - new Field(y).toBigInt().toString(2).length - ); - if (actualLength > maxLength) - throw Error( - `Provable comparison functions can only be used on Fields of size <= ${maxLength} bits, got ${actualLength} bits.` - ); - }); - let [, less, lessOrEqual] = Snarky.field.compare(maxLength, this.value, y); - return { - less: Bool.Unsafe.ofField(new Field(less)), - lessOrEqual: Bool.Unsafe.ofField(new Field(lessOrEqual)), - }; } /** @@ -698,7 +674,7 @@ class Field { if (this.isConstant() && isConstant(y)) { return new Bool(this.toBigInt() < toFp(y)); } - return this.#compare(Field.#toVar(y)).less; + return compare(this, toFieldVar(y)).less; } /** @@ -728,7 +704,7 @@ class Field { if (this.isConstant() && isConstant(y)) { return new Bool(this.toBigInt() <= toFp(y)); } - return this.#compare(Field.#toVar(y)).lessOrEqual; + return compare(this, toFieldVar(y)).lessOrEqual; } /** @@ -755,8 +731,7 @@ class Field { * @return A {@link Bool} representing if this {@link Field} is greater than another "field-like" value. */ greaterThan(y: Field | bigint | number | string) { - // TODO: this is less efficient than possible for equivalence with ml - return this.lessThanOrEqual(y).not(); + return Field.from(y).lessThan(this); } /** @@ -783,8 +758,7 @@ class Field { * @return A {@link Bool} representing if this {@link Field} is greater than or equal another "field-like" value. */ greaterThanOrEqual(y: Field | bigint | number | string) { - // TODO: this is less efficient than possible for equivalence with ml - return this.lessThan(y).not(); + return Field.from(y).lessThanOrEqual(this); } /** @@ -808,7 +782,7 @@ class Field { } return; } - let { less } = this.#compare(Field.#toVar(y)); + let { less } = compare(this, toFieldVar(y)); less.assertTrue(); } catch (err) { throw withMessage(err, message); @@ -836,7 +810,7 @@ class Field { } return; } - let { lessOrEqual } = this.#compare(Field.#toVar(y)); + let { lessOrEqual } = compare(this, toFieldVar(y)); lessOrEqual.assertTrue(); } catch (err) { throw withMessage(err, message); @@ -930,15 +904,6 @@ class Field { } } - static #checkBitLength(name: string, length: number) { - if (length > Fp.sizeInBits) - throw Error( - `${name}: bit length must be ${Fp.sizeInBits} or less, got ${length}` - ); - if (length <= 0) - throw Error(`${name}: bit length must be positive, got ${length}`); - } - /** * Returns an array of {@link Bool} elements representing [little endian binary representation](https://en.wikipedia.org/wiki/Endianness) of this {@link Field} element. * @@ -953,7 +918,7 @@ class Field { * @return An array of {@link Bool} element representing little endian binary representation of this {@link Field}. */ toBits(length?: number) { - if (length !== undefined) Field.#checkBitLength('Field.toBits()', length); + if (length !== undefined) checkBitLength('Field.toBits()', length); if (this.isConstant()) { let bits = Fp.toBits(this.toBigInt()); if (length !== undefined) { @@ -964,7 +929,7 @@ class Field { return bits.map((b) => new Bool(b)); } let [, ...bits] = Snarky.field.toBits(length ?? Fp.sizeInBits, this.value); - return bits.map((b) => Bool.Unsafe.ofField(new Field(b))); + return bits.map((b) => new Bool(b)); } /** @@ -980,7 +945,7 @@ class Field { */ static fromBits(bits: (Bool | boolean)[]) { let length = bits.length; - Field.#checkBitLength('Field.fromBits()', length); + checkBitLength('Field.fromBits()', length); if (bits.every((b) => typeof b === 'boolean' || b.toField().isConstant())) { let bits_ = bits .map((b) => (typeof b === 'boolean' ? b : b.toBoolean())) @@ -1008,7 +973,7 @@ class Field { * @return A {@link Field} element that is equal to the `length` of this {@link Field} element. */ rangeCheckHelper(length: number) { - Field.#checkBitLength('Field.rangeCheckHelper()', length); + checkBitLength('Field.rangeCheckHelper()', length); if (length % 16 !== 0) throw Error( 'Field.rangeCheckHelper(): `length` has to be a multiple of 16.' @@ -1036,11 +1001,9 @@ class Field { * @return A {@link Field} element that is equal to the result of AST that was previously on this {@link Field} element. */ seal() { - // TODO: this is just commented for constraint equivalence with the old version - // uncomment to sometimes save constraints - // if (this.isConstant()) return this; + if (this.isConstant()) return this; let x = Snarky.field.seal(this.value); - return new Field(x); + return VarField(x); } /** @@ -1149,6 +1112,10 @@ class Field { // ProvableExtended + static empty() { + return new Field(0n); + } + /** * Serialize the {@link Field} to a JSON string, e.g. for printing. Trying to print a {@link Field} without this function will directly stringify the Field object, resulting in unreadable output. * @@ -1163,7 +1130,7 @@ class Field { * @return A string equivalent to the JSON representation of the {@link Field}. */ toJSON() { - return this.#toConstant('toJSON').toString(); + return toConstant(this, 'toJSON').toString(); } /** @@ -1254,15 +1221,14 @@ class Field { } /** - * **Warning**: This function is mainly for internal use. Normally it is not intended to be used by a zkApp developer. - * - * As all {@link Field} elements have 31 bits, this function returns 31. - * - * @return The size of a {@link Field} element - 31. + * The size of a {@link Field} element in bytes - 32. */ - static sizeInBytes() { - return Fp.sizeInBytes(); - } + static sizeInBytes = Fp.sizeInBytes; + + /** + * The size of a {@link Field} element in bits - 255. + */ + static sizeInBits = Fp.sizeInBits; } const FieldBinable = defineBinable({ @@ -1278,9 +1244,7 @@ const FieldBinable = defineBinable({ }, }); -function isField(x: unknown): x is Field { - return x instanceof Field; -} +// internal helper functions function isConstant( x: bigint | number | string | Field @@ -1300,12 +1264,57 @@ function toFp(x: bigint | number | string | Field): Fp { return (x as Field).toBigInt(); } +function toFieldConst(x: bigint | number | string | ConstantField): FieldConst { + if (x instanceof Field) return x.value[1]; + return FieldConst.fromBigint(Fp(x)); +} + +function toFieldVar(x: bigint | number | string | Field): FieldVar { + if (x instanceof Field) return x.value; + return FieldVar.constant(Fp(x)); +} + function withMessage(error: unknown, message?: string) { if (message === undefined || !(error instanceof Error)) return error; error.message = `${message}\n${error.message}`; return error; } +// internal base method for all comparisons +function compare(x: Field, y: FieldVar) { + // TODO: support all bit lengths + let maxLength = Fp.sizeInBits - 2; + asProver(() => { + let actualLength = Math.max( + x.toBigInt().toString(2).length, + new Field(y).toBigInt().toString(2).length + ); + if (actualLength > maxLength) + throw Error( + `Provable comparison functions can only be used on Fields of size <= ${maxLength} bits, got ${actualLength} bits.` + ); + }); + let [, less, lessOrEqual] = Snarky.field.compare(maxLength, x.value, y); + return { less: new Bool(less), lessOrEqual: new Bool(lessOrEqual) }; +} + +function checkBitLength( + name: string, + length: number, + maxLength = Fp.sizeInBits +) { + if (length > maxLength) + throw Error( + `${name}: bit length must be ${maxLength} or less, got ${length}` + ); + if (length < 0) + throw Error(`${name}: bit length must be non-negative, got ${length}`); +} + +function toConstant(x: Field, name: string): ConstantField { + return toConstantField(x, name, 'x', 'field element'); +} + function toConstantField( x: Field, methodName: string, @@ -1350,3 +1359,7 @@ there is \`Provable.asProver(() => { ... })\` which allows you to use ${varName} Warning: whatever happens inside asProver() will not be part of the zk proof. `; } + +function VarField(x: VarFieldVar): VarField { + return new Field(x) as VarField; +} diff --git a/src/lib/field.unit-test.ts b/src/lib/field.unit-test.ts index f28f688bb3..8f7d8843f5 100644 --- a/src/lib/field.unit-test.ts +++ b/src/lib/field.unit-test.ts @@ -7,7 +7,16 @@ import { Provable } from './provable.js'; import { Binable } from '../bindings/lib/binable.js'; import { ProvableExtended } from './circuit_value.js'; import { FieldType } from './field.js'; -import { createEquivalenceTesters, throwError } from './testing/equivalent.js'; +import { + equivalentProvable as equivalent, + oneOf, + field, + bigintField, + throwError, + unit, + bool, + Spec, +} from './testing/equivalent.js'; // types Field satisfies Provable; @@ -56,73 +65,75 @@ test(Random.field, Random.int(-5, 5), (x, k) => { deepEqual(Field(x + BigInt(k) * Field.ORDER), Field(x)); }); +// Field | bigint parameter +let fieldOrBigint = oneOf(field, bigintField); + // special generator let SmallField = Random.reject( Random.field, (x) => x.toString(2).length > Fp.sizeInBits - 2 ); - -let { equivalent1, equivalent2, equivalentVoid1, equivalentVoid2 } = - createEquivalenceTesters(Field, Field); +let smallField: Spec = { ...field, rng: SmallField }; +let smallBigint: Spec = { ...bigintField, rng: SmallField }; +let smallFieldOrBigint = oneOf(smallField, smallBigint); // arithmetic, both in- and outside provable code -equivalent2((x, y) => x.add(y), Fp.add); -equivalent1((x) => x.neg(), Fp.negate); -equivalent2((x, y) => x.sub(y), Fp.sub); -equivalent2((x, y) => x.mul(y), Fp.mul); +let equivalent1 = equivalent({ from: [field], to: field }); +let equivalent2 = equivalent({ from: [field, fieldOrBigint], to: field }); + +equivalent2(Fp.add, (x, y) => x.add(y)); +equivalent1(Fp.negate, (x) => x.neg()); +equivalent2(Fp.sub, (x, y) => x.sub(y)); +equivalent2(Fp.mul, (x, y) => x.mul(y)); equivalent1( - (x) => x.inv(), - (x) => Fp.inverse(x) ?? throwError('division by 0') + (x) => Fp.inverse(x) ?? throwError('division by 0'), + (x) => x.inv() ); equivalent2( - (x, y) => x.div(y), - (x, y) => Fp.div(x, y) ?? throwError('division by 0') + (x, y) => Fp.div(x, y) ?? throwError('division by 0'), + (x, y) => x.div(y) ); -equivalent1((x) => x.square(), Fp.square); +equivalent1(Fp.square, (x) => x.square()); equivalent1( - (x) => x.sqrt(), - (x) => Fp.sqrt(x) ?? throwError('no sqrt') + (x) => Fp.sqrt(x) ?? throwError('no sqrt'), + (x) => x.sqrt() ); -equivalent2( - (x, y) => x.equals(y).toField(), - (x, y) => BigInt(x === y) +equivalent({ from: [field, fieldOrBigint], to: bool })( + (x, y) => x === y, + (x, y) => x.equals(y) ); -equivalent2( - (x, y) => x.lessThan(y).toField(), - (x, y) => BigInt(x < y), - SmallField + +equivalent({ from: [smallField, smallFieldOrBigint], to: bool })( + (x, y) => x < y, + (x, y) => x.lessThan(y) ); -equivalent2( - (x, y) => x.lessThanOrEqual(y).toField(), - (x, y) => BigInt(x <= y), - SmallField +equivalent({ from: [smallField, smallFieldOrBigint], to: bool })( + (x, y) => x <= y, + (x, y) => x.lessThanOrEqual(y) ); -equivalentVoid2( - (x, y) => x.assertEquals(y), - (x, y) => x === y || throwError('not equal') +equivalent({ from: [field, fieldOrBigint], to: unit })( + (x, y) => x === y || throwError('not equal'), + (x, y) => x.assertEquals(y) ); -equivalentVoid2( - (x, y) => x.assertNotEquals(y), - (x, y) => x !== y || throwError('equal') +equivalent({ from: [field, fieldOrBigint], to: unit })( + (x, y) => x !== y || throwError('equal'), + (x, y) => x.assertNotEquals(y) ); -equivalentVoid2( - (x, y) => x.assertLessThan(y), +equivalent({ from: [smallField, smallFieldOrBigint], to: unit })( (x, y) => x < y || throwError('not less than'), - SmallField + (x, y) => x.assertLessThan(y) ); -equivalentVoid2( - (x, y) => x.assertLessThanOrEqual(y), +equivalent({ from: [smallField, smallFieldOrBigint], to: unit })( (x, y) => x <= y || throwError('not less than or equal'), - SmallField + (x, y) => x.assertLessThanOrEqual(y) ); -equivalentVoid1( - (x) => x.assertBool(), - (x) => x === 0n || x === 1n || throwError('not boolean') +equivalent({ from: [field], to: unit })( + (x) => x === 0n || x === 1n || throwError('not boolean'), + (x) => x.assertBool() ); -equivalent1( - (x) => x.isEven().toField(), - (x) => BigInt((x & 1n) === 0n), - SmallField +equivalent({ from: [smallField], to: bool })( + (x) => (x & 1n) === 0n, + (x) => x.isEven() ); // non-constant field vars diff --git a/src/lib/foreign-curve.ts b/src/lib/foreign-curve.ts new file mode 100644 index 0000000000..08fb733bfd --- /dev/null +++ b/src/lib/foreign-curve.ts @@ -0,0 +1,312 @@ +import { + CurveParams, + CurveAffine, + createCurveAffine, +} from '../bindings/crypto/elliptic_curve.js'; +import type { Group } from './group.js'; +import { ProvablePureExtended } from './circuit_value.js'; +import { AlmostForeignField, createForeignField } from './foreign-field.js'; +import { EllipticCurve, Point } from './gadgets/elliptic-curve.js'; +import { Field3 } from './gadgets/foreign-field.js'; +import { assert } from './gadgets/common.js'; +import { Provable } from './provable.js'; +import { provableFromClass } from '../bindings/lib/provable-snarky.js'; + +// external API +export { createForeignCurve, ForeignCurve }; + +// internal API +export { toPoint, FlexiblePoint }; + +type FlexiblePoint = { + x: AlmostForeignField | Field3 | bigint | number; + y: AlmostForeignField | Field3 | bigint | number; +}; +function toPoint({ x, y }: ForeignCurve): Point { + return { x: x.value, y: y.value }; +} + +class ForeignCurve { + x: AlmostForeignField; + y: AlmostForeignField; + + /** + * Create a new {@link ForeignCurve} from an object representing the (affine) x and y coordinates. + * + * @example + * ```ts + * let x = new ForeignCurve({ x: 1n, y: 1n }); + * ``` + * + * **Important**: By design, there is no way for a `ForeignCurve` to represent the zero point. + * + * **Warning**: This fails for a constant input which does not represent an actual point on the curve. + */ + constructor(g: { + x: AlmostForeignField | Field3 | bigint | number; + y: AlmostForeignField | Field3 | bigint | number; + }) { + this.x = new this.Constructor.Field(g.x); + this.y = new this.Constructor.Field(g.y); + // don't allow constants that aren't on the curve + if (this.isConstant()) { + this.assertOnCurve(); + this.assertInSubgroup(); + } + } + + /** + * Coerce the input to a {@link ForeignCurve}. + */ + static from(g: ForeignCurve | FlexiblePoint) { + if (g instanceof this) return g; + return new this(g); + } + + /** + * The constant generator point. + */ + static get generator() { + return new this(this.Bigint.one); + } + /** + * The size of the curve's base field. + */ + static get modulus() { + return this.Bigint.modulus; + } + /** + * The size of the curve's base field. + */ + get modulus() { + return this.Constructor.Bigint.modulus; + } + + /** + * Checks whether this curve point is constant. + * + * See {@link FieldVar} to understand constants vs variables. + */ + isConstant() { + return Provable.isConstant(this.Constructor.provable, this); + } + + /** + * Convert this curve point to a point with bigint coordinates. + */ + toBigint() { + return this.Constructor.Bigint.fromNonzero({ + x: this.x.toBigInt(), + y: this.y.toBigInt(), + }); + } + + /** + * Elliptic curve addition. + * + * ```ts + * let r = p.add(q); // r = p + q + * ``` + * + * **Important**: this is _incomplete addition_ and does not handle the degenerate cases: + * - Inputs are equal, `g = h` (where you would use {@link double}). + * In this case, the result of this method is garbage and can be manipulated arbitrarily by a malicious prover. + * - Inputs are inverses of each other, `g = -h`, so that the result would be the zero point. + * In this case, the proof fails. + * + * If you want guaranteed soundness regardless of the input, use {@link addSafe} instead. + * + * @throws if the inputs are inverses of each other. + */ + add(h: ForeignCurve | FlexiblePoint) { + let Curve = this.Constructor.Bigint; + let h_ = this.Constructor.from(h); + let p = EllipticCurve.add(toPoint(this), toPoint(h_), Curve); + return new this.Constructor(p); + } + + /** + * Safe elliptic curve addition. + * + * This is the same as {@link add}, but additionally proves that the inputs are not equal. + * Therefore, the method is guaranteed to either fail or return a valid addition result. + * + * **Beware**: this is more expensive than {@link add}, and is still incomplete in that + * it does not succeed on equal or inverse inputs. + * + * @throws if the inputs are equal or inverses of each other. + */ + addSafe(h: ForeignCurve | FlexiblePoint) { + let h_ = this.Constructor.from(h); + + // prove that we have x1 != x2 => g != +-h + let x1 = this.x.assertCanonical(); + let x2 = h_.x.assertCanonical(); + x1.equals(x2).assertFalse(); + + return this.add(h_); + } + + /** + * Elliptic curve doubling. + * + * @example + * ```ts + * let r = p.double(); // r = 2 * p + * ``` + */ + double() { + let Curve = this.Constructor.Bigint; + let p = EllipticCurve.double(toPoint(this), Curve); + return new this.Constructor(p); + } + + /** + * Elliptic curve negation. + * + * @example + * ```ts + * let r = p.negate(); // r = -p + * ``` + */ + negate(): ForeignCurve { + return new this.Constructor({ x: this.x, y: this.y.neg() }); + } + + /** + * Elliptic curve scalar multiplication, where the scalar is represented as a {@link ForeignField} element. + * + * **Important**: this proves that the result of the scalar multiplication is not the zero point. + * + * @throws if the scalar multiplication results in the zero point; for example, if the scalar is zero. + * + * @example + * ```ts + * let r = p.scale(s); // r = s * p + * ``` + */ + scale(scalar: AlmostForeignField | bigint | number) { + let Curve = this.Constructor.Bigint; + let scalar_ = this.Constructor.Scalar.from(scalar); + let p = EllipticCurve.scale(scalar_.value, toPoint(this), Curve); + return new this.Constructor(p); + } + + static assertOnCurve(g: ForeignCurve) { + EllipticCurve.assertOnCurve(toPoint(g), this.Bigint); + } + + /** + * Assert that this point lies on the elliptic curve, which means it satisfies the equation + * `y^2 = x^3 + ax + b` + */ + assertOnCurve() { + this.Constructor.assertOnCurve(this); + } + + static assertInSubgroup(g: ForeignCurve) { + if (this.Bigint.hasCofactor) { + EllipticCurve.assertInSubgroup(toPoint(g), this.Bigint); + } + } + + /** + * Assert that this point lies in the subgroup defined by `order*P = 0`. + * + * Note: this is a no-op if the curve has cofactor equal to 1. Otherwise + * it performs the full scalar multiplication `order*P` and is expensive. + */ + assertInSubgroup() { + this.Constructor.assertInSubgroup(this); + } + + /** + * Check that this is a valid element of the target subgroup of the curve: + * - Check that the coordinates are valid field elements + * - Use {@link assertOnCurve()} to check that the point lies on the curve + * - If the curve has cofactor unequal to 1, use {@link assertInSubgroup()}. + */ + static check(g: ForeignCurve) { + // more efficient than the automatic check, which would do this for each field separately + this.Field.assertAlmostReduced(g.x, g.y); + this.assertOnCurve(g); + this.assertInSubgroup(g); + } + + // dynamic subclassing infra + get Constructor() { + return this.constructor as typeof ForeignCurve; + } + static _Bigint?: CurveAffine; + static _Field?: typeof AlmostForeignField; + static _Scalar?: typeof AlmostForeignField; + static _provable?: ProvablePureExtended< + ForeignCurve, + { x: string; y: string } + >; + + /** + * Curve arithmetic on JS bigints. + */ + static get Bigint() { + assert(this._Bigint !== undefined, 'ForeignCurve not initialized'); + return this._Bigint; + } + /** + * The base field of this curve as a {@link ForeignField}. + */ + static get Field() { + assert(this._Field !== undefined, 'ForeignCurve not initialized'); + return this._Field; + } + /** + * The scalar field of this curve as a {@link ForeignField}. + */ + static get Scalar() { + assert(this._Scalar !== undefined, 'ForeignCurve not initialized'); + return this._Scalar; + } + /** + * `Provable` + */ + static get provable() { + assert(this._provable !== undefined, 'ForeignCurve not initialized'); + return this._provable; + } +} + +/** + * Create a class representing an elliptic curve group, which is different from the native {@link Group}. + * + * ```ts + * const Curve = createForeignCurve(Crypto.CurveParams.Secp256k1); + * ``` + * + * `createForeignCurve(params)` takes curve parameters {@link CurveParams} as input. + * We support `modulus` and `order` to be prime numbers up to 259 bits. + * + * The returned {@link ForeignCurve} class represents a _non-zero curve point_ and supports standard + * elliptic curve operations like point addition and scalar multiplication. + * + * {@link ForeignCurve} also includes to associated foreign fields: `ForeignCurve.Field` and `ForeignCurve.Scalar`, see {@link createForeignField}. + */ +function createForeignCurve(params: CurveParams): typeof ForeignCurve { + const FieldUnreduced = createForeignField(params.modulus); + const ScalarUnreduced = createForeignField(params.order); + class Field extends FieldUnreduced.AlmostReduced {} + class Scalar extends ScalarUnreduced.AlmostReduced {} + + const BigintCurve = createCurveAffine(params); + + class Curve extends ForeignCurve { + static _Bigint = BigintCurve; + static _Field = Field; + static _Scalar = Scalar; + static _provable = provableFromClass(Curve, { + x: Field.provable, + y: Field.provable, + }); + } + + return Curve; +} diff --git a/src/lib/foreign-curve.unit-test.ts b/src/lib/foreign-curve.unit-test.ts new file mode 100644 index 0000000000..9cdac03730 --- /dev/null +++ b/src/lib/foreign-curve.unit-test.ts @@ -0,0 +1,42 @@ +import { createForeignCurve } from './foreign-curve.js'; +import { Fq } from '../bindings/crypto/finite_field.js'; +import { Vesta as V } from '../bindings/crypto/elliptic_curve.js'; +import { Provable } from './provable.js'; +import { Field } from './field.js'; +import { Crypto } from './crypto.js'; + +class Vesta extends createForeignCurve(Crypto.CurveParams.Vesta) {} +class Fp extends Vesta.Scalar {} + +let g = { x: Fq.negate(1n), y: 2n, infinity: false }; +let h = V.toAffine(V.negate(V.double(V.add(V.fromAffine(g), V.one)))); +let scalar = Field.random().toBigInt(); +let p = V.toAffine(V.scale(V.fromAffine(h), scalar)); + +function main() { + let g0 = Provable.witness(Vesta.provable, () => new Vesta(g)); + let one = Provable.witness(Vesta.provable, () => Vesta.generator); + let h0 = g0.add(one).double().negate(); + Provable.assertEqual(Vesta.provable, h0, new Vesta(h)); + + h0.assertOnCurve(); + h0.assertInSubgroup(); + + let scalar0 = Provable.witness(Fp.provable, () => new Fp(scalar)); + let p0 = h0.scale(scalar0); + Provable.assertEqual(Vesta.provable, p0, new Vesta(p)); +} + +console.time('running constant version'); +main(); +console.timeEnd('running constant version'); + +console.time('running witness generation & checks'); +Provable.runAndCheck(main); +console.timeEnd('running witness generation & checks'); + +console.time('creating constraint system'); +let cs = Provable.constraintSystem(main); +console.timeEnd('creating constraint system'); + +console.log(cs.summary()); diff --git a/src/lib/foreign-ecdsa.ts b/src/lib/foreign-ecdsa.ts new file mode 100644 index 0000000000..bccbaa77ab --- /dev/null +++ b/src/lib/foreign-ecdsa.ts @@ -0,0 +1,258 @@ +import { provableFromClass } from '../bindings/lib/provable-snarky.js'; +import { CurveParams } from '../bindings/crypto/elliptic_curve.js'; +import { ProvablePureExtended } from './circuit_value.js'; +import { + FlexiblePoint, + ForeignCurve, + createForeignCurve, + toPoint, +} from './foreign-curve.js'; +import { AlmostForeignField } from './foreign-field.js'; +import { assert } from './gadgets/common.js'; +import { Field3 } from './gadgets/foreign-field.js'; +import { Ecdsa } from './gadgets/elliptic-curve.js'; +import { l } from './gadgets/range-check.js'; +import { Keccak } from './keccak.js'; +import { Bytes } from './provable-types/provable-types.js'; +import { UInt8 } from './int.js'; + +// external API +export { createEcdsa, EcdsaSignature }; + +type FlexibleSignature = + | EcdsaSignature + | { + r: AlmostForeignField | Field3 | bigint | number; + s: AlmostForeignField | Field3 | bigint | number; + }; + +class EcdsaSignature { + r: AlmostForeignField; + s: AlmostForeignField; + + /** + * Create a new {@link EcdsaSignature} from an object containing the scalars r and s. + * @param signature + */ + constructor(signature: { + r: AlmostForeignField | Field3 | bigint | number; + s: AlmostForeignField | Field3 | bigint | number; + }) { + this.r = new this.Constructor.Curve.Scalar(signature.r); + this.s = new this.Constructor.Curve.Scalar(signature.s); + } + + /** + * Coerce the input to a {@link EcdsaSignature}. + */ + static from(signature: FlexibleSignature): EcdsaSignature { + if (signature instanceof this) return signature; + return new this(signature); + } + + /** + * Create an {@link EcdsaSignature} from a raw 130-char hex string as used in + * [Ethereum transactions](https://ethereum.org/en/developers/docs/transactions/#typed-transaction-envelope). + */ + static fromHex(rawSignature: string): EcdsaSignature { + let s = Ecdsa.Signature.fromHex(rawSignature); + return new this(s); + } + + /** + * Convert this signature to an object with bigint fields. + */ + toBigInt() { + return { r: this.r.toBigInt(), s: this.s.toBigInt() }; + } + + /** + * Verify the ECDSA signature given the message (an array of bytes) and public key (a {@link Curve} point). + * + * **Important:** This method returns a {@link Bool} which indicates whether the signature is valid. + * So, to actually prove validity of a signature, you need to assert that the result is true. + * + * @throws if one of the signature scalars is zero or if the public key is not on the curve. + * + * @example + * ```ts + * // create classes for your curve + * class Secp256k1 extends createForeignCurve(Crypto.CurveParams.Secp256k1) {} + * class Scalar extends Secp256k1.Scalar {} + * class Ecdsa extends createEcdsa(Secp256k1) {} + * + * let message = 'my message'; + * let messageBytes = new TextEncoder().encode(message); + * + * // outside provable code: create inputs + * let privateKey = Scalar.random(); + * let publicKey = Secp256k1.generator.scale(privateKey); + * let signature = Ecdsa.sign(messageBytes, privateKey.toBigInt()); + * + * // ... + * // in provable code: create input witnesses (or use method inputs, or constants) + * let pk = Provable.witness(Secp256k1.provable, () => publicKey); + * let msg = Provable.witness(Provable.Array(Field, 9), () => messageBytes.map(Field)); + * let sig = Provable.witness(Ecdsa.provable, () => signature); + * + * // verify signature + * let isValid = sig.verify(msg, pk); + * isValid.assertTrue('signature verifies'); + * ``` + */ + verify(message: Bytes, publicKey: FlexiblePoint) { + let msgHashBytes = Keccak.ethereum(message); + let msgHash = keccakOutputToScalar(msgHashBytes, this.Constructor.Curve); + return this.verifySignedHash(msgHash, publicKey); + } + + /** + * Verify the ECDSA signature given the message hash (a {@link Scalar}) and public key (a {@link Curve} point). + * + * This is a building block of {@link EcdsaSignature.verify}, where the input message is also hashed. + * In contrast, this method just takes the message hash (a curve scalar) as input, giving you flexibility in + * choosing the hashing algorithm. + */ + verifySignedHash( + msgHash: AlmostForeignField | bigint, + publicKey: FlexiblePoint + ) { + let msgHash_ = this.Constructor.Curve.Scalar.from(msgHash); + let publicKey_ = this.Constructor.Curve.from(publicKey); + return Ecdsa.verify( + this.Constructor.Curve.Bigint, + toObject(this), + msgHash_.value, + toPoint(publicKey_) + ); + } + + /** + * Create an {@link EcdsaSignature} by signing a message with a private key. + * + * Note: This method is not provable, and only takes JS bigints as input. + */ + static sign(message: (bigint | number)[] | Uint8Array, privateKey: bigint) { + let msgHashBytes = Keccak.ethereum(message); + let msgHash = keccakOutputToScalar(msgHashBytes, this.Curve); + return this.signHash(msgHash.toBigInt(), privateKey); + } + + /** + * Create an {@link EcdsaSignature} by signing a message hash with a private key. + * + * This is a building block of {@link EcdsaSignature.sign}, where the input message is also hashed. + * In contrast, this method just takes the message hash (a curve scalar) as input, giving you flexibility in + * choosing the hashing algorithm. + * + * Note: This method is not provable, and only takes JS bigints as input. + */ + static signHash(msgHash: bigint, privateKey: bigint) { + let { r, s } = Ecdsa.sign(this.Curve.Bigint, msgHash, privateKey); + return new this({ r, s }); + } + + static check(signature: EcdsaSignature) { + // more efficient than the automatic check, which would do this for each scalar separately + this.Curve.Scalar.assertAlmostReduced(signature.r, signature.s); + } + + // dynamic subclassing infra + get Constructor() { + return this.constructor as typeof EcdsaSignature; + } + static _Curve?: typeof ForeignCurve; + static _provable?: ProvablePureExtended< + EcdsaSignature, + { r: string; s: string } + >; + + /** + * The {@link ForeignCurve} on which the ECDSA signature is defined. + */ + static get Curve() { + assert(this._Curve !== undefined, 'EcdsaSignature not initialized'); + return this._Curve; + } + /** + * `Provable` + */ + static get provable() { + assert(this._provable !== undefined, 'EcdsaSignature not initialized'); + return this._provable; + } +} + +/** + * Create a class {@link EcdsaSignature} for verifying ECDSA signatures on the given curve. + */ +function createEcdsa( + curve: CurveParams | typeof ForeignCurve +): typeof EcdsaSignature { + let Curve0: typeof ForeignCurve = + 'b' in curve ? createForeignCurve(curve) : curve; + class Curve extends Curve0 {} + + class Signature extends EcdsaSignature { + static _Curve = Curve; + static _provable = provableFromClass(Signature, { + r: Curve.Scalar.provable, + s: Curve.Scalar.provable, + }); + } + + return Signature; +} + +function toObject(signature: EcdsaSignature) { + return { r: signature.r.value, s: signature.s.value }; +} + +/** + * Provable method to convert keccak256 hash output to ECDSA scalar = "message hash" + * + * Spec from [Wikipedia](https://en.wikipedia.org/wiki/Elliptic_Curve_Digital_Signature_Algorithm): + * + * > Let z be the L_n leftmost bits of e, where L_{n} is the bit length of the group order n. + * > (Note that z can be greater than n but not longer.) + * + * The output z is used as input to a multiplication: + * + * > Calculate u_1 = z s^(-1) mod n ... + * + * That means we don't need to reduce z mod n: The fact that it has bitlength <= n makes it + * almost reduced which is enough for the multiplication to be correct. + * (using a weaker notion of "almost reduced" than what we usually prove, but sufficient for all uses of it: `z < 2^ceil(log(n))`) + * + * In summary, this method just: + * - takes a 32 bytes hash + * - converts them to 3 limbs which collectively have L_n <= 256 bits + */ +function keccakOutputToScalar(hash: Bytes, Curve: typeof ForeignCurve) { + const L_n = Curve.Scalar.sizeInBits; + // keep it simple for now, avoid dealing with dropping bits + // TODO: what does "leftmost bits" mean? big-endian or little-endian? + // @noble/curves uses a right shift, dropping the least significant bits: + // https://github.com/paulmillr/noble-curves/blob/4007ee975bcc6410c2e7b504febc1d5d625ed1a4/src/abstract/weierstrass.ts#L933 + assert(L_n === 256, `Scalar sizes ${L_n} !== 256 not supported`); + assert(hash.length === 32, `hash length ${hash.length} !== 32 not supported`); + + // piece together into limbs + // bytes are big-endian, so the first byte is the most significant + assert(l === 88n); + let x2 = bytesToLimbBE(hash.bytes.slice(0, 10)); + let x1 = bytesToLimbBE(hash.bytes.slice(10, 21)); + let x0 = bytesToLimbBE(hash.bytes.slice(21, 32)); + + return new Curve.Scalar.AlmostReduced([x0, x1, x2]); +} + +function bytesToLimbBE(bytes_: UInt8[]) { + let bytes = bytes_.map((x) => x.value); + let n = bytes.length; + let limb = bytes[0]; + for (let i = 1; i < n; i++) { + limb = limb.mul(1n << 8n).add(bytes[i]); + } + return limb.seal(); +} diff --git a/src/lib/foreign-field.ts b/src/lib/foreign-field.ts new file mode 100644 index 0000000000..788b95129e --- /dev/null +++ b/src/lib/foreign-field.ts @@ -0,0 +1,739 @@ +import { + mod, + Fp, + FiniteField, + createField, +} from '../bindings/crypto/finite_field.js'; +import { Field, FieldVar, checkBitLength, withMessage } from './field.js'; +import { Provable } from './provable.js'; +import { Bool } from './bool.js'; +import { Tuple, TupleMap, TupleN } from './util/types.js'; +import { Field3 } from './gadgets/foreign-field.js'; +import { Gadgets } from './gadgets/gadgets.js'; +import { ForeignField as FF } from './gadgets/foreign-field.js'; +import { assert } from './gadgets/common.js'; +import { l3, l } from './gadgets/range-check.js'; +import { ProvablePureExtended } from './circuit_value.js'; + +// external API +export { createForeignField }; +export type { + ForeignField, + UnreducedForeignField, + AlmostForeignField, + CanonicalForeignField, +}; + +class ForeignField { + static _Bigint: FiniteField | undefined = undefined; + static _modulus: bigint | undefined = undefined; + + // static parameters + static get Bigint() { + assert(this._Bigint !== undefined, 'ForeignField class not initialized.'); + return this._Bigint; + } + static get modulus() { + assert(this._modulus !== undefined, 'ForeignField class not initialized.'); + return this._modulus; + } + get modulus() { + return (this.constructor as typeof ForeignField).modulus; + } + static get sizeInBits() { + return this.modulus.toString(2).length; + } + + /** + * The internal representation of a foreign field element, as a tuple of 3 limbs. + */ + value: Field3; + + get Constructor() { + return this.constructor as typeof ForeignField; + } + + /** + * Sibling classes that represent different ranges of field elements. + */ + static _variants: + | { + unreduced: typeof UnreducedForeignField; + almostReduced: typeof AlmostForeignField; + canonical: typeof CanonicalForeignField; + } + | undefined = undefined; + + /** + * Constructor for unreduced field elements. + */ + static get Unreduced() { + assert(this._variants !== undefined, 'ForeignField class not initialized.'); + return this._variants.unreduced; + } + /** + * Constructor for field elements that are "almost reduced", i.e. lie in the range [0, 2^ceil(log2(p))). + */ + static get AlmostReduced() { + assert(this._variants !== undefined, 'ForeignField class not initialized.'); + return this._variants.almostReduced; + } + /** + * Constructor for field elements that are fully reduced, i.e. lie in the range [0, p). + */ + static get Canonical() { + assert(this._variants !== undefined, 'ForeignField class not initialized.'); + return this._variants.canonical; + } + + /** + * Create a new {@link ForeignField} from a bigint, number, string or another ForeignField. + * @example + * ```ts + * let x = new ForeignField(5); + * ``` + */ + constructor(x: ForeignField | Field3 | bigint | number | string) { + const p = this.modulus; + if (x instanceof ForeignField) { + this.value = x.value; + return; + } + // Field3 + if (Array.isArray(x)) { + this.value = x; + return; + } + // constant + this.value = Field3.from(mod(BigInt(x), p)); + } + + /** + * Coerce the input to a {@link ForeignField}. + */ + static from(x: bigint | number | string): CanonicalForeignField; + static from(x: ForeignField | bigint | number | string): ForeignField; + static from(x: ForeignField | bigint | number | string): ForeignField { + if (x instanceof this) return x; + return new this.Canonical(x); + } + + /** + * Checks whether this field element is a constant. + * + * See {@link FieldVar} to understand constants vs variables. + */ + isConstant() { + return Field3.isConstant(this.value); + } + + /** + * Convert this field element to a constant. + * + * See {@link FieldVar} to understand constants vs variables. + * + * **Warning**: This function is only useful in {@link Provable.witness} or {@link Provable.asProver} blocks, + * that is, in situations where the prover computes a value outside provable code. + */ + toConstant(): ForeignField { + let constantLimbs = Tuple.map(this.value, (l) => l.toConstant()); + return new this.Constructor(constantLimbs); + } + + /** + * Convert this field element to a bigint. + */ + toBigInt() { + return Field3.toBigint(this.value); + } + + /** + * Assert that this field element lies in the range [0, 2^k), + * where k = ceil(log2(p)) and p is the foreign field modulus. + * + * Returns the field element as a {@link AlmostForeignField}. + * + * For a more efficient version of this for multiple field elements, see {@link assertAlmostReduced}. + * + * Note: this does not ensure that the field elements is in the canonical range [0, p). + * To assert that stronger property, there is {@link assertCanonical}. + * You should typically use {@link assertAlmostReduced} though, because it is cheaper to prove and sufficient for + * ensuring validity of all our non-native field arithmetic methods. + */ + assertAlmostReduced() { + // TODO: this is not very efficient, but the only way to abstract away the complicated + // range check assumptions and also not introduce a global context of pending range checks. + // we plan to get rid of bounds checks anyway, then this is just a multi-range check + let [x] = this.Constructor.assertAlmostReduced(this); + return x; + } + + /** + * Assert that one or more field elements lie in the range [0, 2^k), + * where k = ceil(log2(p)) and p is the foreign field modulus. + * + * This is most efficient than when checking a multiple of 3 field elements at once. + */ + static assertAlmostReduced>( + ...xs: T + ): TupleMap { + Gadgets.ForeignField.assertAlmostReduced( + xs.map((x) => x.value), + this.modulus, + { skipMrc: true } + ); + return Tuple.map(xs, this.AlmostReduced.unsafeFrom); + } + + /** + * Assert that this field element is fully reduced, + * i.e. lies in the range [0, p), where p is the foreign field modulus. + * + * Returns the field element as a {@link CanonicalForeignField}. + */ + assertCanonical() { + this.assertLessThan(this.modulus); + return this.Constructor.Canonical.unsafeFrom(this); + } + + // arithmetic with full constraints, for safe use + + /** + * Finite field addition + * @example + * ```ts + * x.add(2); // x + 2 mod p + * ``` + */ + add(y: ForeignField | bigint | number) { + return this.Constructor.sum([this, y], [1]); + } + + /** + * Finite field negation + * @example + * ```ts + * x.neg(); // -x mod p = p - x + * ``` + */ + neg() { + // this gets a special implementation because negation proves that the return value is almost reduced. + // it shows that r = f - x >= 0 or r = 0 (for x=0) over the integers, which implies r < f + // see also `Gadgets.ForeignField.assertLessThan()` + let xNeg = Gadgets.ForeignField.neg(this.value, this.modulus); + return new this.Constructor.AlmostReduced(xNeg); + } + + /** + * Finite field subtraction + * @example + * ```ts + * x.sub(1); // x - 1 mod p + * ``` + */ + sub(y: ForeignField | bigint | number) { + return this.Constructor.sum([this, y], [-1]); + } + + /** + * Sum (or difference) of multiple finite field elements. + * + * @example + * ```ts + * let z = ForeignField.sum([3, 2, 1], [-1, 1]); // 3 - 2 + 1 + * z.assertEquals(2); + * ``` + * + * This method expects a list of ForeignField-like values, `x0,...,xn`, + * and a list of "operations" `op1,...,opn` where every op is 1 or -1 (plus or minus), + * and returns + * + * `x0 + op1*x1 + ... + opn*xn` + * + * where the sum is computed in finite field arithmetic. + * + * **Important:** For more than two summands, this is significantly more efficient + * than chaining calls to {@link ForeignField.add} and {@link ForeignField.sub}. + * + */ + static sum(xs: (ForeignField | bigint | number)[], operations: (1 | -1)[]) { + const p = this.modulus; + let fields = xs.map((x) => toLimbs(x, p)); + let ops = operations.map((op) => (op === 1 ? 1n : -1n)); + let z = Gadgets.ForeignField.sum(fields, ops, p); + return new this.Unreduced(z); + } + + // convenience methods + + /** + * Assert equality with a ForeignField-like value + * + * @example + * ```ts + * x.assertEquals(0, "x is zero"); + * ``` + * + * Since asserting equality can also serve as a range check, + * this method returns `x` with the appropriate type: + * + * @example + * ```ts + * let xChecked = x.assertEquals(1, "x is 1"); + * xChecked satisfies CanonicalForeignField; + * ``` + */ + assertEquals( + y: bigint | number | CanonicalForeignField, + message?: string + ): CanonicalForeignField; + assertEquals(y: AlmostForeignField, message?: string): AlmostForeignField; + assertEquals(y: ForeignField, message?: string): ForeignField; + assertEquals( + y: ForeignField | bigint | number, + message?: string + ): ForeignField { + const p = this.modulus; + try { + if (this.isConstant() && isConstant(y)) { + let x = this.toBigInt(); + let y0 = mod(toBigInt(y), p); + if (x !== y0) { + throw Error(`ForeignField.assertEquals(): ${x} != ${y0}`); + } + return new this.Constructor.Canonical(this.value); + } + Provable.assertEqual( + this.Constructor.provable, + this, + new this.Constructor(y) + ); + if (isConstant(y) || y instanceof this.Constructor.Canonical) { + return new this.Constructor.Canonical(this.value); + } else if (y instanceof this.Constructor.AlmostReduced) { + return new this.Constructor.AlmostReduced(this.value); + } else { + return this; + } + } catch (err) { + throw withMessage(err, message); + } + } + + /** + * Assert that this field element is less than a constant c: `x < c`. + * + * The constant must satisfy `0 <= c < 2^264`, otherwise an error is thrown. + * + * @example + * ```ts + * x.assertLessThan(10); + * ``` + */ + assertLessThan(c: bigint | number, message?: string) { + assert( + c >= 0 && c < 1n << l3, + `ForeignField.assertLessThan(): expected c <= c < 2^264, got ${c}` + ); + try { + Gadgets.ForeignField.assertLessThan(this.value, toBigInt(c)); + } catch (err) { + throw withMessage(err, message); + } + } + + // bit packing + + /** + * Unpack a field element to its bits, as a {@link Bool}[] array. + * + * This method is provable! + */ + toBits(length?: number) { + const sizeInBits = this.Constructor.sizeInBits; + if (length === undefined) length = sizeInBits; + checkBitLength('ForeignField.toBits()', length, sizeInBits); + let [l0, l1, l2] = this.value; + let limbSize = Number(l); + let xBits = l0.toBits(Math.min(length, limbSize)); + length -= limbSize; + if (length <= 0) return xBits; + let yBits = l1.toBits(Math.min(length, limbSize)); + length -= limbSize; + if (length <= 0) return [...xBits, ...yBits]; + let zBits = l2.toBits(Math.min(length, limbSize)); + return [...xBits, ...yBits, ...zBits]; + } + + /** + * Create a field element from its bits, as a `Bool[]` array. + * + * This method is provable! + */ + static fromBits(bits: Bool[]) { + let length = bits.length; + checkBitLength('ForeignField.fromBits()', length, this.sizeInBits); + let limbSize = Number(l); + let l0 = Field.fromBits(bits.slice(0 * limbSize, 1 * limbSize)); + let l1 = Field.fromBits(bits.slice(1 * limbSize, 2 * limbSize)); + let l2 = Field.fromBits(bits.slice(2 * limbSize, 3 * limbSize)); + // note: due to the check on the number of bits, we know we return an "almost valid" field element + return new this.AlmostReduced([l0, l1, l2]); + } + + static random() { + return new this.Canonical(this.Bigint.random()); + } + + /** + * Instance version of `Provable.toFields`, see {@link Provable.toFields} + */ + toFields(): Field[] { + return this.value; + } + + static check(_: ForeignField) { + throw Error('ForeignField.check() not implemented: must use a subclass'); + } + + static _provable: any = undefined; + + /** + * `Provable`, see {@link Provable} + */ + static get provable() { + assert(this._provable !== undefined, 'ForeignField class not initialized.'); + return this._provable; + } +} + +class ForeignFieldWithMul extends ForeignField { + /** + * Finite field multiplication + * @example + * ```ts + * x.mul(y); // x*y mod p + * ``` + */ + mul(y: AlmostForeignField | bigint | number) { + const p = this.modulus; + let z = Gadgets.ForeignField.mul(this.value, toLimbs(y, p), p); + return new this.Constructor.Unreduced(z); + } + + /** + * Multiplicative inverse in the finite field + * @example + * ```ts + * let z = x.inv(); // 1/x mod p + * z.mul(x).assertEquals(1); + * ``` + */ + inv() { + const p = this.modulus; + let z = Gadgets.ForeignField.inv(this.value, p); + return new this.Constructor.AlmostReduced(z); + } + + /** + * Division in the finite field, i.e. `x*y^(-1) mod p` where `y^(-1)` is the finite field inverse. + * @example + * ```ts + * let z = x.div(y); // x/y mod p + * z.mul(y).assertEquals(x); + * ``` + */ + div(y: AlmostForeignField | bigint | number) { + const p = this.modulus; + let z = Gadgets.ForeignField.div(this.value, toLimbs(y, p), p); + return new this.Constructor.AlmostReduced(z); + } +} + +class UnreducedForeignField extends ForeignField { + type: 'Unreduced' | 'AlmostReduced' | 'FullyReduced' = 'Unreduced'; + + static _provable: + | ProvablePureExtended + | undefined = undefined; + static get provable() { + assert(this._provable !== undefined, 'ForeignField class not initialized.'); + return this._provable; + } + + static check(x: ForeignField) { + Gadgets.multiRangeCheck(x.value); + } +} + +class AlmostForeignField extends ForeignFieldWithMul { + type: 'AlmostReduced' | 'FullyReduced' = 'AlmostReduced'; + + constructor(x: AlmostForeignField | Field3 | bigint | number | string) { + super(x); + } + + static _provable: + | ProvablePureExtended + | undefined = undefined; + static get provable() { + assert(this._provable !== undefined, 'ForeignField class not initialized.'); + return this._provable; + } + + static check(x: ForeignField) { + Gadgets.multiRangeCheck(x.value); + x.assertAlmostReduced(); + } + + /** + * Coerce the input to an {@link AlmostForeignField} without additional assertions. + * + * **Warning:** Only use if you know what you're doing. + */ + static unsafeFrom(x: ForeignField) { + return new this(x.value); + } + + /** + * Check equality with a constant value. + * + * @example + * ```ts + * let isXZero = x.equals(0); + * ``` + */ + equals(y: bigint | number) { + return FF.equals(this.value, BigInt(y), this.modulus); + } +} + +class CanonicalForeignField extends ForeignFieldWithMul { + type = 'FullyReduced' as const; + + constructor(x: CanonicalForeignField | Field3 | bigint | number | string) { + super(x); + } + + static _provable: + | ProvablePureExtended + | undefined = undefined; + static get provable() { + assert(this._provable !== undefined, 'ForeignField class not initialized.'); + return this._provable; + } + + static check(x: ForeignField) { + Gadgets.multiRangeCheck(x.value); + x.assertCanonical(); + } + + /** + * Coerce the input to a {@link CanonicalForeignField} without additional assertions. + * + * **Warning:** Only use if you know what you're doing. + */ + static unsafeFrom(x: ForeignField) { + return new this(x.value); + } + + /** + * Check equality with a ForeignField-like value. + * + * @example + * ```ts + * let isEqual = x.equals(y); + * ``` + * + * Note: This method only exists on canonical fields; on unreduced fields, it would be easy to + * misuse, because not being exactly equal does not imply being unequal modulo p. + */ + equals(y: CanonicalForeignField | bigint | number) { + let [x0, x1, x2] = this.value; + let [y0, y1, y2] = toLimbs(y, this.modulus); + let x01 = x0.add(x1.mul(1n << l)).seal(); + let y01 = y0.add(y1.mul(1n << l)).seal(); + return x01.equals(y01).and(x2.equals(y2)); + } +} + +function toLimbs( + x: bigint | number | string | ForeignField, + p: bigint +): Field3 { + if (x instanceof ForeignField) return x.value; + return Field3.from(mod(BigInt(x), p)); +} + +function toBigInt(x: bigint | string | number | ForeignField) { + if (x instanceof ForeignField) return x.toBigInt(); + return BigInt(x); +} + +function isConstant(x: bigint | number | string | ForeignField) { + if (x instanceof ForeignField) return x.isConstant(); + return true; +} + +/** + * Create a class representing a prime order finite field, which is different from the native {@link Field}. + * + * ```ts + * const SmallField = createForeignField(17n); // the finite field F_17 + * ``` + * + * `createForeignField(p)` takes the prime modulus `p` of the finite field as input, as a bigint. + * We support prime moduli up to a size of 259 bits. + * + * The returned {@link ForeignField} class supports arithmetic modulo `p` (addition and multiplication), + * as well as helper methods like `assertEquals()` and `equals()`. + * + * _Advanced details:_ + * + * Internally, a foreign field element is represented as three native field elements, each of which + * represents a limb of 88 bits. Therefore, being a valid foreign field element means that all 3 limbs + * fit in 88 bits, and the foreign field element altogether is smaller than the modulus p. + * + * Since the full `x < p` check is expensive, by default we only prove a weaker assertion, `x < 2^ceil(log2(p))`, + * see {@link ForeignField.assertAlmostReduced} for more details. + * + * This weaker assumption is what we call "almost reduced", and it is represented by the {@link AlmostForeignField} class. + * Note that only {@link AlmostForeignField} supports multiplication and inversion, while {@link UnreducedForeignField} + * only supports addition and subtraction. + * + * This function returns the `Unreduced` class, which will cause the minimum amount of range checks to be created by default. + * If you want to do multiplication, you have two options: + * - create your field elements using the {@link ForeignField.AlmostReduced} constructor, or using the `.provable` type on that class. + * ```ts + * let x = Provable.witness(ForeignField.AlmostReduced.provable, () => ForeignField.from(5)); + * ``` + * - create your field elements normally and convert them using `x.assertAlmostReduced()`. + * ```ts + * let xChecked = x.assertAlmostReduced(); // asserts x < 2^ceil(log2(p)); returns `AlmostForeignField` + * ``` + * + * Similarly, there is a separate class {@link CanonicalForeignField} which represents fully reduced, "canonical" field elements. + * To convert to a canonical field element, use {@link ForeignField.assertCanonical}: + * + * ```ts + * x.assertCanonical(); // asserts x < p; returns `CanonicalForeignField` + * ``` + * You will likely not need canonical fields most of the time. + * + * Base types for all of these classes are separately exported as {@link UnreducedForeignField}, {@link AlmostForeignField} and {@link CanonicalForeignField}., + * + * @param modulus the modulus of the finite field you are instantiating + */ +function createForeignField(modulus: bigint): typeof UnreducedForeignField { + assert( + modulus > 0n, + `ForeignField: modulus must be positive, got ${modulus}` + ); + assert( + modulus < foreignFieldMax, + `ForeignField: modulus exceeds the max supported size of 2^${foreignFieldMaxBits}` + ); + + let Bigint = createField(modulus); + + class UnreducedField extends UnreducedForeignField { + static _Bigint = Bigint; + static _modulus = modulus; + static _provable = provable(UnreducedField); + + // bind public static methods to the class so that they have `this` defined + static from = ForeignField.from.bind(UnreducedField); + static sum = ForeignField.sum.bind(UnreducedField); + static fromBits = ForeignField.fromBits.bind(UnreducedField); + } + + class AlmostField extends AlmostForeignField { + static _Bigint = Bigint; + static _modulus = modulus; + static _provable = provable(AlmostField); + + // bind public static methods to the class so that they have `this` defined + static from = ForeignField.from.bind(AlmostField); + static sum = ForeignField.sum.bind(AlmostField); + static fromBits = ForeignField.fromBits.bind(AlmostField); + static unsafeFrom = AlmostForeignField.unsafeFrom.bind(AlmostField); + } + + class CanonicalField extends CanonicalForeignField { + static _Bigint = Bigint; + static _modulus = modulus; + static _provable = provable(CanonicalField); + + // bind public static methods to the class so that they have `this` defined + static from = ForeignField.from.bind(CanonicalField); + static sum = ForeignField.sum.bind(CanonicalField); + static fromBits = ForeignField.fromBits.bind(CanonicalField); + static unsafeFrom = CanonicalForeignField.unsafeFrom.bind(CanonicalField); + } + + let variants = { + unreduced: UnreducedField, + almostReduced: AlmostField, + canonical: CanonicalField, + }; + UnreducedField._variants = variants; + AlmostField._variants = variants; + CanonicalField._variants = variants; + + return UnreducedField; +} + +// the max foreign field modulus is f_max = floor(sqrt(p * 2^t)), where t = 3*limbBits = 264 and p is the native modulus +// see RFC: https://github.com/o1-labs/proof-systems/blob/1fdb1fd1d112f9d4ee095dbb31f008deeb8150b0/book/src/rfcs/foreign_field_mul.md +// since p = 2^254 + eps for both Pasta fields with eps small, a fairly tight lower bound is +// f_max >= sqrt(2^254 * 2^264) = 2^259 +const foreignFieldMaxBits = (BigInt(Fp.sizeInBits - 1) + 3n * l) / 2n; +const foreignFieldMax = 1n << foreignFieldMaxBits; + +// provable + +type Constructor = new (...args: any[]) => T; + +function provable( + Class: Constructor & { check(x: ForeignField): void } +): ProvablePureExtended { + return { + toFields(x) { + return x.value; + }, + toAuxiliary(): [] { + return []; + }, + sizeInFields() { + return 3; + }, + fromFields(fields) { + let limbs = TupleN.fromArray(3, fields); + return new Class(limbs); + }, + check(x: ForeignField) { + Class.check(x); + }, + // ugh + toJSON(x: ForeignField) { + return x.toBigInt().toString(); + }, + fromJSON(x: string) { + // TODO be more strict about allowed values + return new Class(x); + }, + empty() { + return new Class(0n); + }, + toInput(x) { + let l_ = Number(l); + return { + packed: [ + [x.value[0], l_], + [x.value[1], l_], + [x.value[2], l_], + ], + }; + }, + }; +} diff --git a/src/lib/foreign-field.unit-test.ts b/src/lib/foreign-field.unit-test.ts new file mode 100644 index 0000000000..26f85a5699 --- /dev/null +++ b/src/lib/foreign-field.unit-test.ts @@ -0,0 +1,191 @@ +import { ProvablePure } from '../snarky.js'; +import { Field, Group } from './core.js'; +import { ForeignField, createForeignField } from './foreign-field.js'; +import { Scalar as Fq, Group as G } from '../provable/curve-bigint.js'; +import { expect } from 'expect'; +import { + bool, + equivalentProvable as equivalent, + equivalent as equivalentNonProvable, + first, + spec, + throwError, + unit, +} from './testing/equivalent.js'; +import { test, Random } from './testing/property.js'; +import { Provable } from './provable.js'; +import { Circuit, circuitMain } from './circuit.js'; +import { Scalar } from './scalar.js'; +import { l } from './gadgets/range-check.js'; +import { assert } from './gadgets/common.js'; + +// toy example - F_17 + +class SmallField extends createForeignField(17n) {} + +let x = SmallField.from(16); +x.assertEquals(-1); // 16 = -1 (mod 17) +x.mul(x).assertEquals(1); // 16 * 16 = 15 * 17 + 1 = 1 (mod 17) + +// invalid example - modulus too large + +expect(() => createForeignField(1n << 260n)).toThrow( + 'modulus exceeds the max supported size' +); + +// real example - foreign field arithmetic in the Pallas scalar field + +class ForeignScalar extends createForeignField(Fq.modulus) {} + +// types +ForeignScalar.provable satisfies ProvablePure; + +// basic constructor / IO +{ + let s0 = 1n + ((1n + (1n << l)) << l); + let scalar = new ForeignScalar(s0); + + expect(scalar.value).toEqual([Field(1), Field(1), Field(1)]); + expect(scalar.toBigInt()).toEqual(s0); +} + +test(Random.scalar, (x0, assert) => { + let x = new ForeignScalar(x0); + assert(x.toBigInt() === x0); + assert(x.isConstant()); +}); + +// test equivalence of in-SNARK and out-of-SNARK operations + +let f = spec({ + rng: Random.scalar, + there: ForeignScalar.from, + back: (x) => x.toBigInt(), + provable: ForeignScalar.AlmostReduced.provable, +}); +let u264 = spec({ + rng: Random.bignat(1n << 264n), + there: ForeignScalar.from, + back: (x) => x.toBigInt(), + provable: ForeignScalar.Unreduced.provable, +}); + +// arithmetic +equivalent({ from: [f, f], to: u264 })(Fq.add, (x, y) => x.add(y)); +equivalent({ from: [f, f], to: u264 })(Fq.sub, (x, y) => x.sub(y)); +equivalent({ from: [f], to: u264 })(Fq.negate, (x) => x.neg()); +equivalent({ from: [f, f], to: u264 })(Fq.mul, (x, y) => x.mul(y)); +equivalent({ from: [f], to: f })( + (x) => Fq.inverse(x) ?? throwError('division by 0'), + (x) => x.inv() +); +equivalent({ from: [f, f], to: f })( + (x, y) => Fq.div(x, y) ?? throwError('division by 0'), + (x, y) => x.div(y) +); + +// equality with a constant +equivalent({ from: [f, first(f)], to: bool })( + (x, y) => x === y, + (x, y) => x.equals(y) +); +equivalent({ from: [f, f], to: unit })( + (x, y) => x === y || throwError('not equal'), + (x, y) => x.assertEquals(y) +); +// doesn't fail in provable mode just because the range check is not checked by runAndCheck +// TODO check all gates +equivalentNonProvable({ from: [f, first(u264)], to: unit })( + (x, y) => x < y || throwError('not less than'), + (x, y) => x.assertLessThan(y) +); + +// toBits / fromBits +equivalent({ from: [f], to: f })( + (x) => x, + (x) => { + let bits = x.toBits(); + expect(bits.length).toEqual(255); + return ForeignScalar.fromBits(bits); + } +); + +// scalar shift in foreign field arithmetic vs in the exponent + +let scalarShift = Fq(1n + 2n ** 255n); +let oneHalf = Fq.inverse(2n)!; + +function unshift(s: ForeignField) { + return s.sub(scalarShift).assertAlmostReduced().mul(oneHalf); +} +function scaleShifted(point: Group, shiftedScalar: Scalar) { + let oneHalfGroup = point.scale(oneHalf); + let shiftGroup = oneHalfGroup.scale(scalarShift); + return oneHalfGroup.scale(shiftedScalar).sub(shiftGroup); +} + +let scalarBigint = Fq.random(); +let pointBigint = G.scale(G.generatorMina, scalarBigint); + +// perform a "scalar unshift" in foreign field arithmetic, +// then convert to scalar from bits (which shifts it back) and scale a point by the scalar +function main0() { + let ffScalar = Provable.witness( + ForeignScalar.provable, + () => new ForeignScalar(scalarBigint) + ); + let bitsUnshifted = unshift(ffScalar).toBits(); + let scalar = Scalar.fromBits(bitsUnshifted); + + let generator = Provable.witness(Group, () => Group.generator); + let point = generator.scale(scalar); + point.assertEquals(Group(pointBigint)); +} + +// go directly from foreign scalar to scalar and perform a shifted scale +// = same end result as main0 +function main1() { + let ffScalar = Provable.witness( + ForeignScalar.provable, + () => new ForeignScalar(scalarBigint) + ); + let bits = ffScalar.toBits(); + let scalarShifted = Scalar.fromBits(bits); + + let generator = Provable.witness(Group, () => Group.generator); + let point = scaleShifted(generator, scalarShifted); + point.assertEquals(Group(pointBigint)); +} + +// check provable and non-provable versions are correct +main0(); +main1(); +Provable.runAndCheck(main0); +Provable.runAndCheck(main1); + +// using foreign field arithmetic should result in much fewer constraints +let { rows: rows0 } = Provable.constraintSystem(main0); +let { rows: rows1 } = Provable.constraintSystem(main1); +expect(rows0 + 100).toBeLessThan(rows1); + +// test with proving + +class Main extends Circuit { + @circuitMain + static main() { + main0(); + } +} + +let kp = await Main.generateKeypair(); + +let cs = kp.constraintSystem(); +assert( + cs.length === 1 << 13, + `should have ${cs.length} = 2^13 rows, the smallest supported number` +); + +let proof = await Main.prove([], [], kp); + +let ok = await Main.verify([], kp.verificationKey(), proof); +assert(ok, 'proof should verify'); diff --git a/src/lib/gadgets/arithmetic.ts b/src/lib/gadgets/arithmetic.ts new file mode 100644 index 0000000000..414ddfe814 --- /dev/null +++ b/src/lib/gadgets/arithmetic.ts @@ -0,0 +1,48 @@ +import { provableTuple } from '../circuit_value.js'; +import { Field } from '../core.js'; +import { assert } from '../errors.js'; +import { Provable } from '../provable.js'; +import { rangeCheck32 } from './range-check.js'; + +export { divMod32, addMod32 }; + +function divMod32(n: Field) { + if (n.isConstant()) { + assert( + n.toBigInt() < 1n << 64n, + `n needs to fit into 64 bit, but got ${n.toBigInt()}` + ); + + let nBigInt = n.toBigInt(); + let q = nBigInt >> 32n; + let r = nBigInt - (q << 32n); + return { + remainder: new Field(r), + quotient: new Field(q), + }; + } + + let [quotient, remainder] = Provable.witness( + provableTuple([Field, Field]), + () => { + let nBigInt = n.toBigInt(); + let q = nBigInt >> 32n; + let r = nBigInt - (q << 32n); + return [new Field(q), new Field(r)]; + } + ); + + rangeCheck32(quotient); + rangeCheck32(remainder); + + n.assertEquals(quotient.mul(1n << 32n).add(remainder)); + + return { + remainder, + quotient, + }; +} + +function addMod32(x: Field, y: Field) { + return divMod32(x.add(y)).remainder; +} diff --git a/src/lib/gadgets/arithmetic.unit-test.ts b/src/lib/gadgets/arithmetic.unit-test.ts new file mode 100644 index 0000000000..075a5d6e32 --- /dev/null +++ b/src/lib/gadgets/arithmetic.unit-test.ts @@ -0,0 +1,59 @@ +import { ZkProgram } from '../proof_system.js'; +import { + equivalentProvable as equivalent, + equivalentAsync, + field, + record, +} from '../testing/equivalent.js'; +import { Field } from '../core.js'; +import { Gadgets } from './gadgets.js'; +import { provable } from '../circuit_value.js'; +import { assert } from './common.js'; + +let Arithmetic = ZkProgram({ + name: 'arithmetic', + publicOutput: provable({ + remainder: Field, + quotient: Field, + }), + methods: { + divMod32: { + privateInputs: [Field], + method(a: Field) { + return Gadgets.divMod32(a); + }, + }, + }, +}); + +await Arithmetic.compile(); + +const divMod32Helper = (x: bigint) => { + let quotient = x >> 32n; + let remainder = x - (quotient << 32n); + return { remainder, quotient }; +}; +const divMod32Output = record({ remainder: field, quotient: field }); + +equivalent({ + from: [field], + to: divMod32Output, +})( + (x) => { + assert(x < 1n << 64n, `x needs to fit in 64bit, but got ${x}`); + return divMod32Helper(x); + }, + (x) => { + return Gadgets.divMod32(x); + } +); + +await equivalentAsync({ from: [field], to: divMod32Output }, { runs: 3 })( + (x) => { + assert(x < 1n << 64n, `x needs to fit in 64bit, but got ${x}`); + return divMod32Helper(x); + }, + async (x) => { + return (await Arithmetic.divMod32(x)).publicOutput; + } +); diff --git a/src/lib/gadgets/basic.ts b/src/lib/gadgets/basic.ts new file mode 100644 index 0000000000..8f6f218935 --- /dev/null +++ b/src/lib/gadgets/basic.ts @@ -0,0 +1,134 @@ +/** + * Basic gadgets that only use generic gates + */ +import { Fp } from '../../bindings/crypto/finite_field.js'; +import type { Field, VarField } from '../field.js'; +import { existsOne, toVar } from './common.js'; +import { Gates } from '../gates.js'; +import { TupleN } from '../util/types.js'; +import { Snarky } from '../../snarky.js'; + +export { assertBoolean, arrayGet, assertOneOf }; + +/** + * Assert that x is either 0 or 1. + */ +function assertBoolean(x: VarField) { + Snarky.field.assertBoolean(x.value); +} + +// TODO: create constant versions of these and expose on Gadgets + +/** + * Get value from array in O(n) rows. + * + * Assumes that index is in [0, n), returns an unconstrained result otherwise. + * + * Note: This saves 0.5*n constraints compared to equals() + switch() + */ +function arrayGet(array: Field[], index: Field) { + let i = toVar(index); + + // witness result + let a = existsOne(() => array[Number(i.toBigInt())].toBigInt()); + + // we prove a === array[j] + z[j]*(i - j) for some z[j], for all j. + // setting j = i, this implies a === array[i] + // thanks to our assumption that the index i is within bounds, we know that j = i for some j + let n = array.length; + for (let j = 0; j < n; j++) { + let zj = existsOne(() => { + let zj = Fp.div( + Fp.sub(a.toBigInt(), array[j].toBigInt()), + Fp.sub(i.toBigInt(), Fp.fromNumber(j)) + ); + return zj ?? 0n; + }); + // prove that z[j]*(i - j) === a - array[j] + // TODO abstract this logic into a general-purpose assertMul() gadget, + // which is able to use the constant coefficient + // (snarky's assert_r1cs somehow leads to much more constraints than this) + if (array[j].isConstant()) { + // zj*i + (-j)*zj + 0*i + array[j] === a + assertBilinear(zj, i, [1n, -BigInt(j), 0n, array[j].toBigInt()], a); + } else { + let aMinusAj = toVar(a.sub(array[j])); + // zj*i + (-j)*zj + 0*i + 0 === (a - array[j]) + assertBilinear(zj, i, [1n, -BigInt(j), 0n, 0n], aMinusAj); + } + } + + return a; +} + +/** + * Assert that a value equals one of a finite list of constants: + * `(x - c1)*(x - c2)*...*(x - cn) === 0` + * + * TODO: what prevents us from getting the same efficiency with snarky DSL code? + */ +function assertOneOf(x: Field, allowed: [bigint, bigint, ...bigint[]]) { + let xv = toVar(x); + let [c1, c2, ...c] = allowed; + let n = c.length; + if (n === 0) { + // (x - c1)*(x - c2) === 0 + assertBilinear(xv, xv, [1n, -(c1 + c2), 0n, c1 * c2]); + return; + } + // z = (x - c1)*(x - c2) + let z = bilinear(xv, xv, [1n, -(c1 + c2), 0n, c1 * c2]); + + for (let i = 0; i < n; i++) { + if (i < n - 1) { + // z = z*(x - c) + z = bilinear(z, xv, [1n, -c[i], 0n, 0n]); + } else { + // z*(x - c) === 0 + assertBilinear(z, xv, [1n, -c[i], 0n, 0n]); + } + } +} + +// low-level helpers to create generic gates + +/** + * Compute bilinear function of x and y: + * `z = a*x*y + b*x + c*y + d` + */ +function bilinear(x: VarField, y: VarField, [a, b, c, d]: TupleN) { + let z = existsOne(() => { + let x0 = x.toBigInt(); + let y0 = y.toBigInt(); + return a * x0 * y0 + b * x0 + c * y0 + d; + }); + // b*x + c*y - z + a*x*y + d === 0 + Gates.generic( + { left: b, right: c, out: -1n, mul: a, const: d }, + { left: x, right: y, out: z } + ); + return z; +} + +/** + * Assert bilinear equation on x, y and z: + * `a*x*y + b*x + c*y + d === z` + * + * The default for z is 0. + */ +function assertBilinear( + x: VarField, + y: VarField, + [a, b, c, d]: TupleN, + z?: VarField +) { + // b*x + c*y - z? + a*x*y + d === 0 + Gates.generic( + { left: b, right: c, out: z === undefined ? 0n : -1n, mul: a, const: d }, + { left: x, right: y, out: z === undefined ? emptyCell() : z } + ); +} + +function emptyCell() { + return existsOne(() => 0n); +} diff --git a/src/lib/gadgets/bitwise.ts b/src/lib/gadgets/bitwise.ts new file mode 100644 index 0000000000..7b34a3e85e --- /dev/null +++ b/src/lib/gadgets/bitwise.ts @@ -0,0 +1,357 @@ +import { Provable } from '../provable.js'; +import { Field as Fp } from '../../provable/field-bigint.js'; +import { Field } from '../field.js'; +import { Gates } from '../gates.js'; +import { + MAX_BITS, + assert, + divideWithRemainder, + toVar, + exists, + bitSlice, +} from './common.js'; +import { rangeCheck32, rangeCheck64 } from './range-check.js'; +import { divMod32 } from './arithmetic.js'; + +export { + xor, + not, + rotate64, + rotate32, + and, + rightShift64, + leftShift64, + leftShift32, +}; + +function not(a: Field, length: number, checked: boolean = false) { + // check that input length is positive + assert(length > 0, `Input length needs to be positive values.`); + + // Check that length does not exceed maximum field size in bits + assert( + length < Field.sizeInBits, + `Length ${length} exceeds maximum of ${Field.sizeInBits} bits.` + ); + + // obtain pad length until the length is a multiple of 16 for n-bit length lookup table + let padLength = Math.ceil(length / 16) * 16; + + // handle constant case + if (a.isConstant()) { + let max = 1n << BigInt(padLength); + assert( + a.toBigInt() < max, + `${a.toBigInt()} does not fit into ${padLength} bits` + ); + return new Field(Fp.not(a.toBigInt(), length)); + } + + // create a bitmask with all ones + let allOnes = new Field(2n ** BigInt(length) - 1n); + + if (checked) { + return xor(a, allOnes, length); + } else { + return allOnes.sub(a).seal(); + } +} + +function xor(a: Field, b: Field, length: number) { + // check that both input lengths are positive + assert(length > 0, `Input lengths need to be positive values.`); + + // check that length does not exceed maximum 254 size in bits + assert(length <= 254, `Length ${length} exceeds maximum of 254 bits.`); + + // obtain pad length until the length is a multiple of 16 for n-bit length lookup table + let padLength = Math.ceil(length / 16) * 16; + + // handle constant case + if (a.isConstant() && b.isConstant()) { + let max = 1n << BigInt(padLength); + + assert(a.toBigInt() < max, `${a} does not fit into ${padLength} bits`); + assert(b.toBigInt() < max, `${b} does not fit into ${padLength} bits`); + + return new Field(a.toBigInt() ^ b.toBigInt()); + } + + // calculate expected xor output + let outputXor = Provable.witness( + Field, + () => new Field(a.toBigInt() ^ b.toBigInt()) + ); + + // builds the xor gadget chain + buildXor(a, b, outputXor, padLength); + + // return the result of the xor operation + return outputXor; +} + +// builds a xor chain +function buildXor(a: Field, b: Field, out: Field, padLength: number) { + // construct the chain of XORs until padLength is 0 + while (padLength !== 0) { + // slices the inputs into 4x 4bit-sized chunks + let slices = exists(15, () => { + let a0 = a.toBigInt(); + let b0 = b.toBigInt(); + let out0 = out.toBigInt(); + return [ + // slices of a + bitSlice(a0, 0, 4), + bitSlice(a0, 4, 4), + bitSlice(a0, 8, 4), + bitSlice(a0, 12, 4), + + // slices of b + bitSlice(b0, 0, 4), + bitSlice(b0, 4, 4), + bitSlice(b0, 8, 4), + bitSlice(b0, 12, 4), + + // slices of expected output + bitSlice(out0, 0, 4), + bitSlice(out0, 4, 4), + bitSlice(out0, 8, 4), + bitSlice(out0, 12, 4), + + // next values + a0 >> 16n, + b0 >> 16n, + out0 >> 16n, + ]; + }); + + // prettier-ignore + let [ + in1_0, in1_1, in1_2, in1_3, + in2_0, in2_1, in2_2, in2_3, + out0, out1, out2, out3, + aNext, bNext, outNext + ] = slices; + + // assert that the xor of the slices is correct, 16 bit at a time + // prettier-ignore + Gates.xor( + a, b, out, + in1_0, in1_1, in1_2, in1_3, + in2_0, in2_1, in2_2, in2_3, + out0, out1, out2, out3 + ); + + // update the values for the next loop iteration + a = aNext; + b = bNext; + out = outNext; + padLength = padLength - 16; + } + + // inputs are zero and length is zero, add the zero check - we reached the end of our chain + Gates.zero(a, b, out); + + let zero = new Field(0); + zero.assertEquals(a); + zero.assertEquals(b); + zero.assertEquals(out); +} + +function and(a: Field, b: Field, length: number) { + // check that both input lengths are positive + assert(length > 0, `Input lengths need to be positive values.`); + + // check that length does not exceed maximum field size in bits + assert( + length <= Field.sizeInBits, + `Length ${length} exceeds maximum of ${Field.sizeInBits} bits.` + ); + + // obtain pad length until the length is a multiple of 16 for n-bit length lookup table + let padLength = Math.ceil(length / 16) * 16; + + // handle constant case + if (a.isConstant() && b.isConstant()) { + let max = 1n << BigInt(padLength); + + assert(a.toBigInt() < max, `${a} does not fit into ${padLength} bits`); + assert(b.toBigInt() < max, `${b} does not fit into ${padLength} bits`); + + return new Field(a.toBigInt() & b.toBigInt()); + } + + // calculate expect and output + let outputAnd = Provable.witness( + Field, + () => new Field(a.toBigInt() & b.toBigInt()) + ); + + // compute values for gate + // explanation: https://o1-labs.github.io/proof-systems/specs/kimchi.html?highlight=gates#and + let sum = a.add(b); + let xorOutput = xor(a, b, length); + outputAnd.mul(2).add(xorOutput).assertEquals(sum); + + // return the result of the and operation + return outputAnd; +} + +function rotate64( + field: Field, + bits: number, + direction: 'left' | 'right' = 'left' +) { + // Check that the rotation bits are in range + assert( + bits >= 0 && bits <= MAX_BITS, + `rotation: expected bits to be between 0 and 64, got ${bits}` + ); + + if (field.isConstant()) { + assert( + field.toBigInt() < 1n << BigInt(MAX_BITS), + `rotation: expected field to be at most 64 bits, got ${field.toBigInt()}` + ); + return new Field(Fp.rot(field.toBigInt(), BigInt(bits), direction)); + } + const [rotated] = rot64(field, bits, direction); + return rotated; +} + +function rotate32( + field: Field, + bits: number, + direction: 'left' | 'right' = 'left' +) { + assert(bits <= 32 && bits > 0, 'bits must be between 0 and 32'); + + if (field.isConstant()) { + assert( + field.toBigInt() < 1n << 32n, + `rotation: expected field to be at most 32 bits, got ${field.toBigInt()}` + ); + return new Field(Fp.rot(field.toBigInt(), BigInt(bits), direction, 32n)); + } + + let { quotient: excess, remainder: shifted } = divMod32( + field.mul(1n << BigInt(direction === 'left' ? bits : 32 - bits)) + ); + + let rotated = shifted.add(excess); + + rangeCheck32(rotated); + + return rotated; +} + +function rot64( + field: Field, + bits: number, + direction: 'left' | 'right' = 'left' +): [Field, Field, Field] { + const rotationBits = direction === 'right' ? MAX_BITS - bits : bits; + const big2Power64 = 2n ** BigInt(MAX_BITS); + const big2PowerRot = 2n ** BigInt(rotationBits); + + const [rotated, excess, shifted, bound] = Provable.witness( + Provable.Array(Field, 4), + () => { + const f = field.toBigInt(); + + // Obtain rotated output, excess, and shifted for the equation: + // f * 2^rot = excess * 2^64 + shifted + const { quotient: excess, remainder: shifted } = divideWithRemainder( + f * big2PowerRot, + big2Power64 + ); + + // Compute rotated value as: rotated = excess + shifted + const rotated = shifted + excess; + // Compute bound to check excess < 2^rot + const bound = excess + big2Power64 - big2PowerRot; + return [rotated, excess, shifted, bound].map(Field.from); + } + ); + + // flush zero var to prevent broken gate chain (zero is used in rangeCheck64) + // TODO this is an abstraction leak, but not clear to me how to improve + toVar(0n); + + // slice the bound into chunks + let boundSlices = exists(12, () => { + let bound0 = bound.toBigInt(); + return [ + bitSlice(bound0, 52, 12), // bits 52-64 + bitSlice(bound0, 40, 12), // bits 40-52 + bitSlice(bound0, 28, 12), // bits 28-40 + bitSlice(bound0, 16, 12), // bits 16-28 + + bitSlice(bound0, 14, 2), // bits 14-16 + bitSlice(bound0, 12, 2), // bits 12-14 + bitSlice(bound0, 10, 2), // bits 10-12 + bitSlice(bound0, 8, 2), // bits 8-10 + bitSlice(bound0, 6, 2), // bits 6-8 + bitSlice(bound0, 4, 2), // bits 4-6 + bitSlice(bound0, 2, 2), // bits 2-4 + bitSlice(bound0, 0, 2), // bits 0-2 + ]; + }); + let [b52, b40, b28, b16, b14, b12, b10, b8, b6, b4, b2, b0] = boundSlices; + + // Compute current row + Gates.rotate( + field, + rotated, + excess, + [b52, b40, b28, b16], + [b14, b12, b10, b8, b6, b4, b2, b0], + big2PowerRot + ); + // Compute next row + rangeCheck64(shifted); + // note: range-checking `shifted` and `field` is enough. + // * excess < 2^rot follows from the bound check and the rotation equation in the gate + // * rotated < 2^64 follows from rotated = excess + shifted (because shifted has to be a multiple of 2^rot) + // for a proof, see https://github.com/o1-labs/o1js/pull/1201 + return [rotated, excess, shifted]; +} + +function rightShift64(field: Field, bits: number) { + assert( + bits >= 0 && bits <= MAX_BITS, + `rightShift: expected bits to be between 0 and 64, got ${bits}` + ); + + if (field.isConstant()) { + assert( + field.toBigInt() < 2n ** BigInt(MAX_BITS), + `rightShift: expected field to be at most 64 bits, got ${field.toBigInt()}` + ); + return new Field(Fp.rightShift(field.toBigInt(), bits)); + } + const [, excess] = rot64(field, bits, 'right'); + return excess; +} + +function leftShift64(field: Field, bits: number) { + assert( + bits >= 0 && bits <= MAX_BITS, + `rightShift: expected bits to be between 0 and 64, got ${bits}` + ); + + if (field.isConstant()) { + assert( + field.toBigInt() < 2n ** BigInt(MAX_BITS), + `rightShift: expected field to be at most 64 bits, got ${field.toBigInt()}` + ); + return new Field(Fp.leftShift(field.toBigInt(), bits)); + } + const [, , shifted] = rot64(field, bits, 'left'); + return shifted; +} + +function leftShift32(field: Field, bits: number) { + let { remainder: shifted } = divMod32(field.mul(1n << BigInt(bits))); + return shifted; +} diff --git a/src/lib/gadgets/bitwise.unit-test.ts b/src/lib/gadgets/bitwise.unit-test.ts new file mode 100644 index 0000000000..f6f186e941 --- /dev/null +++ b/src/lib/gadgets/bitwise.unit-test.ts @@ -0,0 +1,277 @@ +import { ZkProgram } from '../proof_system.js'; +import { + equivalentProvable as equivalent, + equivalentAsync, + field, + fieldWithRng, +} from '../testing/equivalent.js'; +import { Fp, mod } from '../../bindings/crypto/finite_field.js'; +import { Field } from '../core.js'; +import { Gadgets } from './gadgets.js'; +import { Random } from '../testing/property.js'; +import { + constraintSystem, + contains, + equals, + ifNotAllConstant, + repeat, + and, + withoutGenerics, +} from '../testing/constraint-system.js'; +import { GateType } from '../../snarky.js'; + +const maybeField = { + ...field, + rng: Random.map(Random.oneOf(Random.field, Random.field.invalid), (x) => + mod(x, Field.ORDER) + ), +}; + +let uint = (length: number) => fieldWithRng(Random.biguint(length)); + +let Bitwise = ZkProgram({ + name: 'bitwise', + publicOutput: Field, + methods: { + xor: { + privateInputs: [Field, Field], + method(a: Field, b: Field) { + return Gadgets.xor(a, b, 254); + }, + }, + notUnchecked: { + privateInputs: [Field], + method(a: Field) { + return Gadgets.not(a, 254, false); + }, + }, + notChecked: { + privateInputs: [Field], + method(a: Field) { + return Gadgets.not(a, 254, true); + }, + }, + and: { + privateInputs: [Field, Field], + method(a: Field, b: Field) { + return Gadgets.and(a, b, 64); + }, + }, + rot32: { + privateInputs: [Field], + method(a: Field) { + return Gadgets.rotate32(a, 12, 'left'); + }, + }, + rot64: { + privateInputs: [Field], + method(a: Field) { + return Gadgets.rotate64(a, 12, 'left'); + }, + }, + leftShift64: { + privateInputs: [Field], + method(a: Field) { + return Gadgets.leftShift64(a, 12); + }, + }, + leftShift32: { + privateInputs: [Field], + method(a: Field) { + Gadgets.rangeCheck32(a); + return Gadgets.leftShift32(a, 12); + }, + }, + rightShift64: { + privateInputs: [Field], + method(a: Field) { + return Gadgets.rightShift64(a, 12); + }, + }, + }, +}); +await Bitwise.compile(); + +[2, 4, 8, 16, 32, 64, 128].forEach((length) => { + equivalent({ from: [uint(length), uint(length)], to: field })( + (x, y) => x ^ y, + (x, y) => Gadgets.xor(x, y, length) + ); + equivalent({ from: [uint(length), uint(length)], to: field })( + (x, y) => x & y, + (x, y) => Gadgets.and(x, y, length) + ); + // NOT unchecked + equivalent({ from: [uint(length)], to: field })( + (x) => Fp.not(x, length), + (x) => Gadgets.not(x, length, false) + ); + // NOT checked + equivalent({ from: [uint(length)], to: field })( + (x) => Fp.not(x, length), + (x) => Gadgets.not(x, length, true) + ); +}); + +[2, 4, 8, 16, 32, 64].forEach((length) => { + equivalent({ from: [uint(length)], to: field })( + (x) => Fp.rot(x, 12n, 'left'), + (x) => Gadgets.rotate64(x, 12, 'left') + ); + equivalent({ from: [uint(length)], to: field })( + (x) => Fp.leftShift(x, 12), + (x) => Gadgets.leftShift64(x, 12) + ); + equivalent({ from: [uint(length)], to: field })( + (x) => Fp.rightShift(x, 12), + (x) => Gadgets.rightShift64(x, 12) + ); +}); + +[2, 4, 8, 16, 32].forEach((length) => { + equivalent({ from: [uint(length)], to: field })( + (x) => Fp.rot(x, 12n, 'left', 32n), + (x) => Gadgets.rotate32(x, 12, 'left') + ); + equivalent({ from: [uint(length)], to: field })( + (x) => Fp.leftShift(x, 12, 32), + (x) => Gadgets.leftShift32(x, 12) + ); +}); + +const runs = 2; + +await equivalentAsync({ from: [uint(64), uint(64)], to: field }, { runs })( + (x, y) => { + return x ^ y; + }, + async (x, y) => { + let proof = await Bitwise.xor(x, y); + return proof.publicOutput; + } +); + +await equivalentAsync({ from: [maybeField], to: field }, { runs })( + (x) => { + return Fp.not(x, 254); + }, + async (x) => { + let proof = await Bitwise.notUnchecked(x); + return proof.publicOutput; + } +); +await equivalentAsync({ from: [maybeField], to: field }, { runs })( + (x) => { + if (x > 2n ** 254n) throw Error('Does not fit into 254 bit'); + return Fp.not(x, 254); + }, + async (x) => { + let proof = await Bitwise.notChecked(x); + return proof.publicOutput; + } +); + +await equivalentAsync({ from: [maybeField, maybeField], to: field }, { runs })( + (x, y) => { + if (x >= 2n ** 64n || y >= 2n ** 64n) + throw Error('Does not fit into 64 bits'); + return x & y; + }, + async (x, y) => { + let proof = await Bitwise.and(x, y); + return proof.publicOutput; + } +); + +await equivalentAsync({ from: [field], to: field }, { runs })( + (x) => { + if (x >= 2n ** 64n) throw Error('Does not fit into 64 bits'); + return Fp.rot(x, 12n, 'left'); + }, + async (x) => { + let proof = await Bitwise.rot64(x); + return proof.publicOutput; + } +); + +await equivalentAsync({ from: [uint(32)], to: uint(32) }, { runs })( + (x) => { + return Fp.rot(x, 12n, 'left', 32n); + }, + async (x) => { + let proof = await Bitwise.rot32(x); + return proof.publicOutput; + } +); + +await equivalentAsync({ from: [field], to: field }, { runs })( + (x) => { + if (x >= 2n ** 64n) throw Error('Does not fit into 64 bits'); + return Fp.leftShift(x, 12); + }, + async (x) => { + let proof = await Bitwise.leftShift64(x); + return proof.publicOutput; + } +); + +await equivalentAsync({ from: [field], to: field }, { runs })( + (x) => { + if (x >= 1n << 32n) throw Error('Does not fit into 32 bits'); + return Fp.leftShift(x, 12, 32); + }, + async (x) => { + let proof = await Bitwise.leftShift32(x); + return proof.publicOutput; + } +); + +await equivalentAsync({ from: [field], to: field }, { runs })( + (x) => { + if (x >= 2n ** 64n) throw Error('Does not fit into 64 bits'); + return Fp.rightShift(x, 12); + }, + async (x) => { + let proof = await Bitwise.rightShift64(x); + return proof.publicOutput; + } +); + +// check that gate chains stay intact + +function xorChain(bits: number) { + return repeat(Math.ceil(bits / 16), 'Xor16').concat('Zero'); +} + +constraintSystem.fromZkProgram( + Bitwise, + 'xor', + ifNotAllConstant(contains(xorChain(254))) +); + +constraintSystem.fromZkProgram( + Bitwise, + 'notChecked', + ifNotAllConstant(contains(xorChain(254))) +); + +constraintSystem.fromZkProgram( + Bitwise, + 'notUnchecked', + ifNotAllConstant(contains('Generic')) +); + +constraintSystem.fromZkProgram( + Bitwise, + 'and', + ifNotAllConstant(contains(xorChain(64))) +); + +let rotChain: GateType[] = ['Rot64', 'RangeCheck0']; +let isJustRotate = ifNotAllConstant( + and(contains(rotChain), withoutGenerics(equals(rotChain))) +); + +constraintSystem.fromZkProgram(Bitwise, 'rot64', isJustRotate); +constraintSystem.fromZkProgram(Bitwise, 'leftShift64', isJustRotate); +constraintSystem.fromZkProgram(Bitwise, 'rightShift64', isJustRotate); diff --git a/src/lib/gadgets/common.ts b/src/lib/gadgets/common.ts new file mode 100644 index 0000000000..9da4490723 --- /dev/null +++ b/src/lib/gadgets/common.ts @@ -0,0 +1,79 @@ +import { Field, FieldConst, FieldVar, VarField } from '../field.js'; +import { Tuple, TupleN } from '../util/types.js'; +import { Snarky } from '../../snarky.js'; +import { MlArray } from '../ml/base.js'; + +const MAX_BITS = 64 as const; + +export { + MAX_BITS, + exists, + existsOne, + toVars, + toVar, + isVar, + assert, + bitSlice, + divideWithRemainder, +}; + +function existsOne(compute: () => bigint) { + let varMl = Snarky.existsVar(() => FieldConst.fromBigint(compute())); + return VarField(varMl); +} + +function exists TupleN>( + n: N, + compute: C +) { + let varsMl = Snarky.exists(n, () => + MlArray.mapTo(compute(), FieldConst.fromBigint) + ); + let vars = MlArray.mapFrom(varsMl, VarField); + return TupleN.fromArray(n, vars); +} + +/** + * Given a Field, collapse its AST to a pure Var. See {@link FieldVar}. + * + * This is useful to prevent rogue Generic gates added in the middle of gate chains, + * which are caused by snarky auto-resolving constants, adds and scales to vars. + * + * Same as `Field.seal()` with the difference that `seal()` leaves constants as is. + */ +function toVar(x: Field | bigint): VarField { + // don't change existing vars + if (isVar(x)) return x; + let xVar = existsOne(() => Field.from(x).toBigInt()); + xVar.assertEquals(x); + return xVar; +} + +function isVar(x: Field | bigint): x is VarField { + return x instanceof Field && FieldVar.isVar(x.value); +} + +/** + * Apply {@link toVar} to each element of a tuple. + */ +function toVars>( + fields: T +): { [k in keyof T]: VarField } { + return Tuple.map(fields, toVar); +} + +function assert(stmt: boolean, message?: string): asserts stmt { + if (!stmt) { + throw Error(message ?? 'Assertion failed'); + } +} + +function bitSlice(x: bigint, start: number, length: number) { + return (x >> BigInt(start)) & ((1n << BigInt(length)) - 1n); +} + +function divideWithRemainder(numerator: bigint, denominator: bigint) { + const quotient = numerator / denominator; + const remainder = numerator - denominator * quotient; + return { quotient, remainder }; +} diff --git a/src/lib/gadgets/ecdsa.unit-test.ts b/src/lib/gadgets/ecdsa.unit-test.ts new file mode 100644 index 0000000000..b03266b1a2 --- /dev/null +++ b/src/lib/gadgets/ecdsa.unit-test.ts @@ -0,0 +1,183 @@ +import { createCurveAffine } from '../../bindings/crypto/elliptic_curve.js'; +import { + Ecdsa, + EllipticCurve, + Point, + initialAggregator, + verifyEcdsaConstant, +} from './elliptic-curve.js'; +import { Field3 } from './foreign-field.js'; +import { CurveParams } from '../../bindings/crypto/elliptic-curve-examples.js'; +import { Provable } from '../provable.js'; +import { ZkProgram } from '../proof_system.js'; +import { assert } from './common.js'; +import { foreignField, uniformForeignField } from './test-utils.js'; +import { + First, + Second, + bool, + equivalentProvable, + fromRandom, + map, + oneOf, + record, +} from '../testing/equivalent.js'; +import { Bool } from '../bool.js'; +import { Random } from '../testing/random.js'; + +// quick tests +const Secp256k1 = createCurveAffine(CurveParams.Secp256k1); +const Pallas = createCurveAffine(CurveParams.Pallas); +const Vesta = createCurveAffine(CurveParams.Vesta); +let curves = [Secp256k1, Pallas, Vesta]; + +for (let Curve of curves) { + // prepare test inputs + let field = foreignField(Curve.Field); + let scalar = foreignField(Curve.Scalar); + let privateKey = uniformForeignField(Curve.Scalar); + + // correct signature shape, but independently random components, which will never form a valid signature + let badSignature = record({ + signature: record({ r: scalar, s: scalar }), + msg: scalar, + publicKey: record({ x: field, y: field }), + }); + + let signatureInputs = record({ privateKey, msg: scalar }); + + let signature = map( + { from: signatureInputs, to: badSignature }, + ({ privateKey, msg }) => { + let publicKey = Curve.scale(Curve.one, privateKey); + let signature = Ecdsa.sign(Curve, msg, privateKey); + return { signature, msg, publicKey }; + } + ); + + // with 30% prob, test the version without GLV even if the curve supports it + let noGlv = fromRandom(Random.map(Random.fraction(), (f) => f < 0.3)); + + // provable method we want to test + const verify = (s: Second, noGlv: boolean) => { + // invalid public key can lead to either a failing constraint, or verify() returning false + EllipticCurve.assertOnCurve(s.publicKey, Curve); + + let hasGlv = Curve.hasEndomorphism; + if (noGlv) Curve.hasEndomorphism = false; // hack to force non-GLV version + try { + return Ecdsa.verify(Curve, s.signature, s.msg, s.publicKey); + } finally { + Curve.hasEndomorphism = hasGlv; + } + }; + + // input validation equivalent to the one implicit in verify() + const checkInputs = ({ + signature: { r, s }, + publicKey, + }: First) => { + assert(r !== 0n && s !== 0n, 'invalid signature'); + let pk = Curve.fromNonzero(publicKey); + assert(Curve.isOnCurve(pk), 'invalid public key'); + return true; + }; + + // positive test + equivalentProvable({ from: [signature, noGlv], to: bool, verbose: true })( + () => true, + verify, + `${Curve.name}: verifies` + ); + + // negative test + equivalentProvable({ from: [badSignature, noGlv], to: bool, verbose: true })( + (s) => checkInputs(s) && false, + verify, + `${Curve.name}: fails` + ); + + // test against constant implementation, with both invalid and valid signatures + equivalentProvable({ + from: [oneOf(signature, badSignature), noGlv], + to: bool, + verbose: true, + })( + ({ signature, publicKey, msg }) => { + checkInputs({ signature, publicKey, msg }); + return verifyEcdsaConstant(Curve, signature, msg, publicKey); + }, + verify, + `${Curve.name}: verify` + ); +} + +// full end-to-end test with proving + +let publicKey = Point.from({ + x: 49781623198970027997721070672560275063607048368575198229673025608762959476014n, + y: 44999051047832679156664607491606359183507784636787036192076848057884504239143n, +}); + +let signature = Ecdsa.Signature.fromHex( + '0x82de9950cc5aac0dca7210cb4b77320ac9e844717d39b1781e9d941d920a12061da497b3c134f50b2fce514d66e20c5e43f9615f097395a5527041d14860a52f1b' +); + +let msgHash = + Field3.from( + 0x3e91cd8bd233b3df4e4762b329e2922381da770df1b31276ec77d0557be7fcefn + ); + +const ia = initialAggregator(Secp256k1); +const config = { G: { windowSize: 4 }, P: { windowSize: 3 }, ia }; + +let program = ZkProgram({ + name: 'ecdsa', + publicOutput: Bool, + methods: { + ecdsa: { + privateInputs: [], + method() { + let signature_ = Provable.witness( + Ecdsa.Signature.provable, + () => signature + ); + let msgHash_ = Provable.witness(Field3.provable, () => msgHash); + let publicKey_ = Provable.witness(Point.provable, () => publicKey); + + return Ecdsa.verify( + Secp256k1, + signature_, + msgHash_, + publicKey_, + config + ); + }, + }, + }, +}); + +console.time('ecdsa verify (constant)'); +program.rawMethods.ecdsa(); +console.timeEnd('ecdsa verify (constant)'); + +console.time('ecdsa verify (witness gen / check)'); +Provable.runAndCheck(program.rawMethods.ecdsa); +console.timeEnd('ecdsa verify (witness gen / check)'); + +console.time('ecdsa verify (build constraint system)'); +let cs = program.analyzeMethods().ecdsa; +console.timeEnd('ecdsa verify (build constraint system)'); + +console.log(cs.summary()); + +console.time('ecdsa verify (compile)'); +await program.compile(); +console.timeEnd('ecdsa verify (compile)'); + +console.time('ecdsa verify (prove)'); +let proof = await program.ecdsa(); +console.timeEnd('ecdsa verify (prove)'); + +assert(await program.verify(proof), 'proof verifies'); +proof.publicOutput.assertTrue('signature verifies'); diff --git a/src/lib/gadgets/elliptic-curve.ts b/src/lib/gadgets/elliptic-curve.ts new file mode 100644 index 0000000000..d2ee6eefc8 --- /dev/null +++ b/src/lib/gadgets/elliptic-curve.ts @@ -0,0 +1,815 @@ +import { inverse, mod } from '../../bindings/crypto/finite_field.js'; +import { Field } from '../field.js'; +import { Provable } from '../provable.js'; +import { assert, exists } from './common.js'; +import { Field3, ForeignField, split, weakBound } from './foreign-field.js'; +import { l, l2, multiRangeCheck } from './range-check.js'; +import { sha256 } from 'js-sha256'; +import { + bigIntToBits, + bigIntToBytes, + bytesToBigInt, +} from '../../bindings/crypto/bigint-helpers.js'; +import { + CurveAffine, + affineAdd, + affineDouble, +} from '../../bindings/crypto/elliptic_curve.js'; +import { Bool } from '../bool.js'; +import { provable } from '../circuit_value.js'; +import { assertPositiveInteger } from '../../bindings/crypto/non-negative.js'; +import { arrayGet, assertBoolean } from './basic.js'; + +// external API +export { EllipticCurve, Point, Ecdsa }; + +// internal API +export { verifyEcdsaConstant, initialAggregator, simpleMapToCurve }; + +const EllipticCurve = { + add, + double, + negate, + assertOnCurve, + scale, + assertInSubgroup, + multiScalarMul, +}; + +/** + * Non-zero elliptic curve point in affine coordinates. + */ +type Point = { x: Field3; y: Field3 }; +type point = { x: bigint; y: bigint }; + +namespace Ecdsa { + /** + * ECDSA signature consisting of two curve scalars. + */ + export type Signature = { r: Field3; s: Field3 }; + export type signature = { r: bigint; s: bigint }; +} + +function add(p1: Point, p2: Point, Curve: { modulus: bigint }) { + let { x: x1, y: y1 } = p1; + let { x: x2, y: y2 } = p2; + let f = Curve.modulus; + + // constant case + if (Point.isConstant(p1) && Point.isConstant(p2)) { + let p3 = affineAdd(Point.toBigint(p1), Point.toBigint(p2), f); + return Point.from(p3); + } + + // witness and range-check slope, x3, y3 + let witnesses = exists(9, () => { + let [x1_, x2_, y1_, y2_] = Field3.toBigints(x1, x2, y1, y2); + let denom = inverse(mod(x1_ - x2_, f), f); + + let m = denom !== undefined ? mod((y1_ - y2_) * denom, f) : 0n; + let m2 = mod(m * m, f); + let x3 = mod(m2 - x1_ - x2_, f); + let y3 = mod(m * (x1_ - x3) - y1_, f); + + return [...split(m), ...split(x3), ...split(y3)]; + }); + let [m0, m1, m2, x30, x31, x32, y30, y31, y32] = witnesses; + let m: Field3 = [m0, m1, m2]; + let x3: Field3 = [x30, x31, x32]; + let y3: Field3 = [y30, y31, y32]; + ForeignField.assertAlmostReduced([m, x3, y3], f); + + // (x1 - x2)*m = y1 - y2 + let deltaX = ForeignField.Sum(x1).sub(x2); + let deltaY = ForeignField.Sum(y1).sub(y2); + ForeignField.assertMul(deltaX, m, deltaY, f); + + // m^2 = x1 + x2 + x3 + let xSum = ForeignField.Sum(x1).add(x2).add(x3); + ForeignField.assertMul(m, m, xSum, f); + + // (x1 - x3)*m = y1 + y3 + let deltaX1X3 = ForeignField.Sum(x1).sub(x3); + let ySum = ForeignField.Sum(y1).add(y3); + ForeignField.assertMul(deltaX1X3, m, ySum, f); + + return { x: x3, y: y3 }; +} + +function double(p1: Point, Curve: { modulus: bigint; a: bigint }) { + let { x: x1, y: y1 } = p1; + let f = Curve.modulus; + + // constant case + if (Point.isConstant(p1)) { + let p3 = affineDouble(Point.toBigint(p1), f); + return Point.from(p3); + } + + // witness and range-check slope, x3, y3 + let witnesses = exists(9, () => { + let [x1_, y1_] = Field3.toBigints(x1, y1); + let denom = inverse(mod(2n * y1_, f), f); + + let m = denom !== undefined ? mod(3n * mod(x1_ ** 2n, f) * denom, f) : 0n; + let m2 = mod(m * m, f); + let x3 = mod(m2 - 2n * x1_, f); + let y3 = mod(m * (x1_ - x3) - y1_, f); + + return [...split(m), ...split(x3), ...split(y3)]; + }); + let [m0, m1, m2, x30, x31, x32, y30, y31, y32] = witnesses; + let m: Field3 = [m0, m1, m2]; + let x3: Field3 = [x30, x31, x32]; + let y3: Field3 = [y30, y31, y32]; + ForeignField.assertAlmostReduced([m, x3, y3], f); + + // x1^2 = x1x1 + let x1x1 = ForeignField.mul(x1, x1, f); + + // 2*y1*m = 3*x1x1 + a + let y1Times2 = ForeignField.Sum(y1).add(y1); + let x1x1Times3PlusA = ForeignField.Sum(x1x1).add(x1x1).add(x1x1); + if (Curve.a !== 0n) + x1x1Times3PlusA = x1x1Times3PlusA.add(Field3.from(Curve.a)); + ForeignField.assertMul(y1Times2, m, x1x1Times3PlusA, f); + + // m^2 = 2*x1 + x3 + let xSum = ForeignField.Sum(x1).add(x1).add(x3); + ForeignField.assertMul(m, m, xSum, f); + + // (x1 - x3)*m = y1 + y3 + let deltaX1X3 = ForeignField.Sum(x1).sub(x3); + let ySum = ForeignField.Sum(y1).add(y3); + ForeignField.assertMul(deltaX1X3, m, ySum, f); + + return { x: x3, y: y3 }; +} + +function negate({ x, y }: Point, Curve: { modulus: bigint }) { + return { x, y: ForeignField.negate(y, Curve.modulus) }; +} + +function assertOnCurve( + p: Point, + { modulus: f, a, b }: { modulus: bigint; b: bigint; a: bigint } +) { + let { x, y } = p; + let x2 = ForeignField.mul(x, x, f); + let y2 = ForeignField.mul(y, y, f); + let y2MinusB = ForeignField.Sum(y2).sub(Field3.from(b)); + + // (x^2 + a) * x = y^2 - b + let x2PlusA = ForeignField.Sum(x2); + if (a !== 0n) x2PlusA = x2PlusA.add(Field3.from(a)); + let message: string | undefined; + if (Point.isConstant(p)) { + message = `assertOnCurve(): (${x}, ${y}) is not on the curve.`; + } + ForeignField.assertMul(x2PlusA, x, y2MinusB, f, message); +} + +/** + * EC scalar multiplication, `scalar*point` + * + * The result is constrained to be not zero. + */ +function scale( + scalar: Field3, + point: Point, + Curve: CurveAffine, + config: { + mode?: 'assert-nonzero' | 'assert-zero'; + windowSize?: number; + multiples?: Point[]; + } = { mode: 'assert-nonzero' } +) { + config.windowSize ??= Point.isConstant(point) ? 4 : 3; + return multiScalarMul([scalar], [point], Curve, [config], config.mode); +} + +// checks whether the elliptic curve point g is in the subgroup defined by [order]g = 0 +function assertInSubgroup(p: Point, Curve: CurveAffine) { + if (!Curve.hasCofactor) return; + scale(Field3.from(Curve.order), p, Curve, { mode: 'assert-zero' }); +} + +// check whether a point equals a constant point +// TODO implement the full case of two vars +function equals(p1: Point, p2: point, Curve: { modulus: bigint }) { + let xEquals = ForeignField.equals(p1.x, p2.x, Curve.modulus); + let yEquals = ForeignField.equals(p1.y, p2.y, Curve.modulus); + return xEquals.and(yEquals); +} + +/** + * Verify an ECDSA signature. + * + * Details about the `config` parameter: + * - For both the generator point `G` and public key `P`, `config` allows you to specify: + * - the `windowSize` which is used in scalar multiplication for this point. + * this flexibility is good because the optimal window size is different for constant and non-constant points. + * empirically, `windowSize=4` for constants and 3 for variables leads to the fewest constraints. + * our defaults reflect that the generator is always constant and the public key is variable in typical applications. + * - a table of multiples of those points, of length `2^windowSize`, which is used in the scalar multiplication gadget to speed up the computation. + * if these are not provided, they are computed on the fly. + * for the constant G, computing multiples costs no constraints, so passing them in makes no real difference. + * for variable public key, there is a possible use case: if the public key is a public input, then its multiples could also be. + * in that case, passing them in would avoid computing them in-circuit and save a few constraints. + * - The initial aggregator `ia`, see {@link initialAggregator}. By default, `ia` is computed deterministically on the fly. + */ +function verifyEcdsa( + Curve: CurveAffine, + signature: Ecdsa.Signature, + msgHash: Field3, + publicKey: Point, + config: { + G?: { windowSize: number; multiples?: Point[] }; + P?: { windowSize: number; multiples?: Point[] }; + ia?: point; + } = { G: { windowSize: 4 }, P: { windowSize: 3 } } +) { + // constant case + if ( + EcdsaSignature.isConstant(signature) && + Field3.isConstant(msgHash) && + Point.isConstant(publicKey) + ) { + let isValid = verifyEcdsaConstant( + Curve, + EcdsaSignature.toBigint(signature), + Field3.toBigint(msgHash), + Point.toBigint(publicKey) + ); + return new Bool(isValid); + } + + // provable case + // note: usually we don't check validity of inputs, like that the public key is a valid curve point + // we make an exception for the two non-standard conditions r != 0 and s != 0, + // which are unusual to capture in types and could be considered part of the verification algorithm + let { r, s } = signature; + ForeignField.inv(r, Curve.order); // proves r != 0 (important, because r = 0 => u2 = 0 kills the private key contribution) + let sInv = ForeignField.inv(s, Curve.order); // proves s != 0 + let u1 = ForeignField.mul(msgHash, sInv, Curve.order); + let u2 = ForeignField.mul(r, sInv, Curve.order); + + let G = Point.from(Curve.one); + let R = multiScalarMul( + [u1, u2], + [G, publicKey], + Curve, + config && [config.G, config.P], + 'assert-nonzero', + config?.ia + ); + // this ^ already proves that R != 0 (part of ECDSA verification) + + // reduce R.x modulo the curve order + let Rx = ForeignField.mul(R.x, Field3.from(1n), Curve.order); + + // we have to prove that Rx is canonical, because we check signature validity based on whether Rx _exactly_ equals the input r. + // if we allowed non-canonical Rx, the prover could make verify() return false on a valid signature, by adding a multiple of `Curve.order` to Rx. + ForeignField.assertLessThan(Rx, Curve.order); + + return Provable.equal(Field3.provable, Rx, r); +} + +/** + * Bigint implementation of ECDSA verify + */ +function verifyEcdsaConstant( + Curve: CurveAffine, + { r, s }: Ecdsa.signature, + msgHash: bigint, + publicKey: point +) { + let pk = Curve.from(publicKey); + if (Curve.equal(pk, Curve.zero)) return false; + if (Curve.hasCofactor && !Curve.isInSubgroup(pk)) return false; + if (r < 1n || r >= Curve.order) return false; + if (s < 1n || s >= Curve.order) return false; + + let sInv = Curve.Scalar.inverse(s); + assert(sInv !== undefined); + let u1 = Curve.Scalar.mul(msgHash, sInv); + let u2 = Curve.Scalar.mul(r, sInv); + + let R = Curve.add(Curve.scale(Curve.one, u1), Curve.scale(pk, u2)); + if (Curve.equal(R, Curve.zero)) return false; + + return Curve.Scalar.equal(R.x, r); +} + +/** + * Multi-scalar multiplication: + * + * s_0 * P_0 + ... + s_(n-1) * P_(n-1) + * + * where P_i are any points. + * + * By default, we prove that the result is not zero. + * + * If you set the `mode` parameter to `'assert-zero'`, on the other hand, + * we assert that the result is zero and just return the constant zero point. + * + * Implementation: We double all points together and leverage a precomputed table of size 2^c to avoid all but every cth addition. + * + * Note: this algorithm targets a small number of points, like 2 needed for ECDSA verification. + * + * TODO: could use lookups for picking precomputed multiples, instead of O(2^c) provable switch + * TODO: custom bit representation for the scalar that avoids 0, to get rid of the degenerate addition case + */ +function multiScalarMul( + scalars: Field3[], + points: Point[], + Curve: CurveAffine, + tableConfigs: ( + | { windowSize?: number; multiples?: Point[] } + | undefined + )[] = [], + mode: 'assert-nonzero' | 'assert-zero' = 'assert-nonzero', + ia?: point +): Point { + let n = points.length; + assert(scalars.length === n, 'Points and scalars lengths must match'); + assertPositiveInteger(n, 'Expected at least 1 point and scalar'); + let useGlv = Curve.hasEndomorphism; + + // constant case + if (scalars.every(Field3.isConstant) && points.every(Point.isConstant)) { + // TODO dedicated MSM + let s = scalars.map(Field3.toBigint); + let P = points.map(Point.toBigint); + let sum = Curve.zero; + for (let i = 0; i < n; i++) { + if (useGlv) { + sum = Curve.add(sum, Curve.Endo.scale(P[i], s[i])); + } else { + sum = Curve.add(sum, Curve.scale(P[i], s[i])); + } + } + if (mode === 'assert-zero') { + assert(sum.infinity, 'scalar multiplication: expected zero result'); + return Point.from(Curve.zero); + } + assert(!sum.infinity, 'scalar multiplication: expected non-zero result'); + return Point.from(sum); + } + + // parse or build point tables + let windowSizes = points.map((_, i) => tableConfigs[i]?.windowSize ?? 1); + let tables = points.map((P, i) => + getPointTable(Curve, P, windowSizes[i], tableConfigs[i]?.multiples) + ); + + let maxBits = Curve.Scalar.sizeInBits; + + if (useGlv) { + maxBits = Curve.Endo.decomposeMaxBits; + + // decompose scalars and handle signs + let n2 = 2 * n; + let scalars2: Field3[] = Array(n2); + let points2: Point[] = Array(n2); + let windowSizes2: number[] = Array(n2); + let tables2: Point[][] = Array(n2); + let mrcStack: Field[] = []; + + for (let i = 0; i < n; i++) { + let [s0, s1] = decomposeNoRangeCheck(Curve, scalars[i]); + scalars2[2 * i] = s0.abs; + scalars2[2 * i + 1] = s1.abs; + + let table = tables[i]; + let endoTable = table.map((P, i) => { + if (i === 0) return P; + let [phiP, betaXBound] = endomorphism(Curve, P); + mrcStack.push(betaXBound); + return phiP; + }); + tables2[2 * i] = table.map((P) => + negateIf(s0.isNegative, P, Curve.modulus) + ); + tables2[2 * i + 1] = endoTable.map((P) => + negateIf(s1.isNegative, P, Curve.modulus) + ); + points2[2 * i] = tables2[2 * i][1]; + points2[2 * i + 1] = tables2[2 * i + 1][1]; + + windowSizes2[2 * i] = windowSizes2[2 * i + 1] = windowSizes[i]; + } + reduceMrcStack(mrcStack); + // from now on, everything is the same as if these were the original points and scalars + points = points2; + tables = tables2; + scalars = scalars2; + windowSizes = windowSizes2; + n = n2; + } + + // slice scalars + let scalarChunks = scalars.map((s, i) => + slice(s, { maxBits, chunkSize: windowSizes[i] }) + ); + + ia ??= initialAggregator(Curve); + let sum = Point.from(ia); + + for (let i = maxBits - 1; i >= 0; i--) { + // add in multiple of each point + for (let j = 0; j < n; j++) { + let windowSize = windowSizes[j]; + if (i % windowSize === 0) { + // pick point to add based on the scalar chunk + let sj = scalarChunks[j][i / windowSize]; + let sjP = + windowSize === 1 + ? points[j] + : arrayGetGeneric(Point.provable, tables[j], sj); + + // ec addition + let added = add(sum, sjP, Curve); + + // handle degenerate case (if sj = 0, Gj is all zeros and the add result is garbage) + sum = Provable.if(sj.equals(0), Point.provable, sum, added); + } + } + + if (i === 0) break; + + // jointly double all points + // (note: the highest couple of bits will not create any constraints because sum is constant; no need to handle that explicitly) + sum = double(sum, Curve); + } + + // the sum is now 2^(b-1)*IA + sum_i s_i*P_i + // we assert that sum != 2^(b-1)*IA, and add -2^(b-1)*IA to get our result + let iaFinal = Curve.scale(Curve.fromNonzero(ia), 1n << BigInt(maxBits - 1)); + let isZero = equals(sum, iaFinal, Curve); + + if (mode === 'assert-nonzero') { + isZero.assertFalse(); + sum = add(sum, Point.from(Curve.negate(iaFinal)), Curve); + } else { + isZero.assertTrue(); + // for type consistency with the 'assert-nonzero' case + sum = Point.from(Curve.zero); + } + + return sum; +} + +function negateIf(condition: Field, P: Point, f: bigint) { + let y = Provable.if( + Bool.Unsafe.ofField(condition), + Field3.provable, + ForeignField.negate(P.y, f), + P.y + ); + return { x: P.x, y }; +} + +function endomorphism(Curve: CurveAffine, P: Point) { + let beta = Field3.from(Curve.Endo.base); + let betaX = ForeignField.mul(beta, P.x, Curve.modulus); + return [{ x: betaX, y: P.y }, weakBound(betaX[2], Curve.modulus)] as const; +} + +/** + * Decompose s = s0 + s1*lambda where s0, s1 are guaranteed to be small + * + * Note: This assumes that s0 and s1 are range-checked externally; in scalar multiplication this happens because they are split into chunks. + */ +function decomposeNoRangeCheck(Curve: CurveAffine, s: Field3) { + assert( + Curve.Endo.decomposeMaxBits < l2, + 'decomposed scalars assumed to be < 2*88 bits' + ); + // witness s0, s1 + let witnesses = exists(6, () => { + let [s0, s1] = Curve.Endo.decompose(Field3.toBigint(s)); + let [s00, s01] = split(s0.abs); + let [s10, s11] = split(s1.abs); + // prettier-ignore + return [ + s0.isNegative ? 1n : 0n, s00, s01, + s1.isNegative ? 1n : 0n, s10, s11, + ]; + }); + let [s0Negative, s00, s01, s1Negative, s10, s11] = witnesses; + // we can hard-code highest limb to zero + // (in theory this would allow us to hard-code the high quotient limb to zero in the ffmul below, and save 2 RCs.. but not worth it) + let s0: Field3 = [s00, s01, Field.from(0n)]; + let s1: Field3 = [s10, s11, Field.from(0n)]; + assertBoolean(s0Negative); + assertBoolean(s1Negative); + + // prove that s1*lambda = s - s0 + let lambda = Provable.if( + Bool.Unsafe.ofField(s1Negative), + Field3.provable, + Field3.from(Curve.Scalar.negate(Curve.Endo.scalar)), + Field3.from(Curve.Endo.scalar) + ); + let rhs = Provable.if( + Bool.Unsafe.ofField(s0Negative), + Field3.provable, + ForeignField.Sum(s).add(s0).finish(Curve.order), + ForeignField.Sum(s).sub(s0).finish(Curve.order) + ); + ForeignField.assertMul(s1, lambda, rhs, Curve.order); + + return [ + { isNegative: s0Negative, abs: s0 }, + { isNegative: s1Negative, abs: s1 }, + ] as const; +} + +/** + * Sign a message hash using ECDSA. + */ +function signEcdsa(Curve: CurveAffine, msgHash: bigint, privateKey: bigint) { + let { Scalar } = Curve; + let k = Scalar.random(); + let R = Curve.scale(Curve.one, k); + let r = Scalar.mod(R.x); + let kInv = Scalar.inverse(k); + assert(kInv !== undefined); + let s = Scalar.mul(kInv, Scalar.add(msgHash, Scalar.mul(r, privateKey))); + return { r, s }; +} + +/** + * Given a point P, create the list of multiples [0, P, 2P, 3P, ..., (2^windowSize-1) * P]. + * This method is provable, but won't create any constraints given a constant point. + */ +function getPointTable( + Curve: CurveAffine, + P: Point, + windowSize: number, + table?: Point[] +): Point[] { + assertPositiveInteger(windowSize, 'invalid window size'); + let n = 1 << windowSize; // n >= 2 + + assert(table === undefined || table.length === n, 'invalid table'); + if (table !== undefined) return table; + + table = [Point.from(Curve.zero), P]; + if (n === 2) return table; + + let Pi = double(P, Curve); + table.push(Pi); + for (let i = 3; i < n; i++) { + Pi = add(Pi, P, Curve); + table.push(Pi); + } + return table; +} + +/** + * For EC scalar multiplication we use an initial point which is subtracted + * at the end, to avoid encountering the point at infinity. + * + * This is a simple hash-to-group algorithm which finds that initial point. + * It's important that this point has no known discrete logarithm so that nobody + * can create an invalid proof of EC scaling. + */ +function initialAggregator(Curve: CurveAffine) { + // hash that identifies the curve + let h = sha256.create(); + h.update('initial-aggregator'); + h.update(bigIntToBytes(Curve.modulus)); + h.update(bigIntToBytes(Curve.order)); + h.update(bigIntToBytes(Curve.a)); + h.update(bigIntToBytes(Curve.b)); + let bytes = h.array(); + + // bytes represent a 256-bit number + // use that as x coordinate + const F = Curve.Field; + let x = F.mod(bytesToBigInt(bytes)); + return simpleMapToCurve(x, Curve); +} + +function random(Curve: CurveAffine) { + let x = Curve.Field.random(); + return simpleMapToCurve(x, Curve); +} + +/** + * Given an x coordinate (base field element), increment it until we find one with + * a y coordinate that satisfies the curve equation, and return the point. + * + * If the curve has a cofactor, multiply by it to get a point in the correct subgroup. + */ +function simpleMapToCurve(x: bigint, Curve: CurveAffine) { + const F = Curve.Field; + let y: bigint | undefined = undefined; + + // increment x until we find a y coordinate + while (y === undefined) { + x = F.add(x, 1n); + // solve y^2 = x^3 + ax + b + let x3 = F.mul(F.square(x), x); + let y2 = F.add(x3, F.mul(Curve.a, x) + Curve.b); + y = F.sqrt(y2); + } + let p = { x, y, infinity: false }; + + // clear cofactor + if (Curve.hasCofactor) { + p = Curve.scale(p, Curve.cofactor!); + } + return p; +} + +/** + * Provable method for slicing a 3x88-bit bigint into smaller bit chunks of length `chunkSize` + * + * This serves as a range check that the input is in [0, 2^maxBits) + */ +function slice( + [x0, x1, x2]: Field3, + { maxBits, chunkSize }: { maxBits: number; chunkSize: number } +) { + let l_ = Number(l); + assert(maxBits <= 3 * l_, `expected max bits <= 3*${l_}, got ${maxBits}`); + + // first limb + let result0 = sliceField(x0, Math.min(l_, maxBits), chunkSize); + if (maxBits <= l_) return result0.chunks; + maxBits -= l_; + + // second limb + let result1 = sliceField(x1, Math.min(l_, maxBits), chunkSize, result0); + if (maxBits <= l_) return result0.chunks.concat(result1.chunks); + maxBits -= l_; + + // third limb + let result2 = sliceField(x2, maxBits, chunkSize, result1); + return result0.chunks.concat(result1.chunks, result2.chunks); +} + +/** + * Provable method for slicing a field element into smaller bit chunks of length `chunkSize`. + * + * This serves as a range check that the input is in [0, 2^maxBits) + * + * If `chunkSize` does not divide `maxBits`, the last chunk will be smaller. + * We return the number of free bits in the last chunk, and optionally accept such a result from a previous call, + * so that this function can be used to slice up a bigint of multiple limbs into homogeneous chunks. + * + * TODO: atm this uses expensive boolean checks for each bit. + * For larger chunks, we should use more efficient range checks. + */ +function sliceField( + x: Field, + maxBits: number, + chunkSize: number, + leftover?: { chunks: Field[]; leftoverSize: number } +) { + let bits = exists(maxBits, () => { + let bits = bigIntToBits(x.toBigInt()); + // normalize length + if (bits.length > maxBits) bits = bits.slice(0, maxBits); + if (bits.length < maxBits) + bits = bits.concat(Array(maxBits - bits.length).fill(false)); + return bits.map(BigInt); + }); + + let chunks = []; + let sum = Field.from(0n); + + // if there's a leftover chunk from a previous sliceField() call, we complete it + if (leftover !== undefined) { + let { chunks: previous, leftoverSize: size } = leftover; + let remainingChunk = Field.from(0n); + for (let i = 0; i < size; i++) { + let bit = bits[i]; + Bool.check(Bool.Unsafe.ofField(bit)); + remainingChunk = remainingChunk.add(bit.mul(1n << BigInt(i))); + } + sum = remainingChunk = remainingChunk.seal(); + let chunk = previous[previous.length - 1]; + previous[previous.length - 1] = chunk.add( + remainingChunk.mul(1n << BigInt(chunkSize - size)) + ); + } + + let i = leftover?.leftoverSize ?? 0; + for (; i < maxBits; i += chunkSize) { + // prove that chunk has `chunkSize` bits + // TODO: this inner sum should be replaced with a more efficient range check when possible + let chunk = Field.from(0n); + let size = Math.min(maxBits - i, chunkSize); // last chunk might be smaller + for (let j = 0; j < size; j++) { + let bit = bits[i + j]; + Bool.check(Bool.Unsafe.ofField(bit)); + chunk = chunk.add(bit.mul(1n << BigInt(j))); + } + chunk = chunk.seal(); + // prove that chunks add up to x + sum = sum.add(chunk.mul(1n << BigInt(i))); + chunks.push(chunk); + } + sum.assertEquals(x); + + let leftoverSize = i - maxBits; + return { chunks, leftoverSize } as const; +} + +/** + * Get value from array in O(n) constraints. + * + * Assumes that index is in [0, n), returns an unconstrained result otherwise. + */ +function arrayGetGeneric(type: Provable, array: T[], index: Field) { + // witness result + let a = Provable.witness(type, () => array[Number(index)]); + let aFields = type.toFields(a); + + // constrain each field of the result + let size = type.sizeInFields(); + let arrays = array.map(type.toFields); + + for (let j = 0; j < size; j++) { + let arrayFieldsJ = arrays.map((x) => x[j]); + arrayGet(arrayFieldsJ, index).assertEquals(aFields[j]); + } + return a; +} + +// type/conversion helpers + +const Point = { + from({ x, y }: point): Point { + return { x: Field3.from(x), y: Field3.from(y) }; + }, + toBigint({ x, y }: Point) { + return { x: Field3.toBigint(x), y: Field3.toBigint(y), infinity: false }; + }, + isConstant: (P: Point) => Provable.isConstant(Point.provable, P), + + /** + * Random point on the curve. + */ + random(Curve: CurveAffine) { + return Point.from(random(Curve)); + }, + + provable: provable({ x: Field3.provable, y: Field3.provable }), +}; + +const EcdsaSignature = { + from({ r, s }: Ecdsa.signature): Ecdsa.Signature { + return { r: Field3.from(r), s: Field3.from(s) }; + }, + toBigint({ r, s }: Ecdsa.Signature): Ecdsa.signature { + return { r: Field3.toBigint(r), s: Field3.toBigint(s) }; + }, + isConstant: (S: Ecdsa.Signature) => + Provable.isConstant(EcdsaSignature.provable, S), + + /** + * Create an {@link EcdsaSignature} from a raw 130-char hex string as used in + * [Ethereum transactions](https://ethereum.org/en/developers/docs/transactions/#typed-transaction-envelope). + */ + fromHex(rawSignature: string): Ecdsa.Signature { + let prefix = rawSignature.slice(0, 2); + let signature = rawSignature.slice(2, 130); + if (prefix !== '0x' || signature.length < 128) { + throw Error( + `Signature.fromHex(): Invalid signature, expected hex string 0x... of length at least 130.` + ); + } + let r = BigInt(`0x${signature.slice(0, 64)}`); + let s = BigInt(`0x${signature.slice(64)}`); + return EcdsaSignature.from({ r, s }); + }, + + provable: provable({ r: Field3.provable, s: Field3.provable }), +}; + +const Ecdsa = { + sign: signEcdsa, + verify: verifyEcdsa, + Signature: EcdsaSignature, +}; + +// MRC stack + +function reduceMrcStack(xs: Field[]) { + let n = xs.length; + let nRemaining = n % 3; + let nFull = (n - nRemaining) / 3; + for (let i = 0; i < nFull; i++) { + multiRangeCheck([xs[3 * i], xs[3 * i + 1], xs[3 * i + 2]]); + } + let remaining: Field3 = [Field.from(0n), Field.from(0n), Field.from(0n)]; + for (let i = 0; i < nRemaining; i++) { + remaining[i] = xs[3 * nFull + i]; + } + multiRangeCheck(remaining); +} diff --git a/src/lib/gadgets/elliptic-curve.unit-test.ts b/src/lib/gadgets/elliptic-curve.unit-test.ts new file mode 100644 index 0000000000..39a6d41ce0 --- /dev/null +++ b/src/lib/gadgets/elliptic-curve.unit-test.ts @@ -0,0 +1,82 @@ +import { CurveParams } from '../../bindings/crypto/elliptic-curve-examples.js'; +import { createCurveAffine } from '../../bindings/crypto/elliptic_curve.js'; +import { + array, + equivalentProvable, + map, + onlyIf, + spec, + unit, +} from '../testing/equivalent.js'; +import { Random } from '../testing/random.js'; +import { assert } from './common.js'; +import { EllipticCurve, Point, simpleMapToCurve } from './elliptic-curve.js'; +import { foreignField, throwError } from './test-utils.js'; + +// provable equivalence tests +const Secp256k1 = createCurveAffine(CurveParams.Secp256k1); +const Pallas = createCurveAffine(CurveParams.Pallas); +const Vesta = createCurveAffine(CurveParams.Vesta); +let curves = [Secp256k1, Pallas, Vesta]; + +for (let Curve of curves) { + // prepare test inputs + let field = foreignField(Curve.Field); + let scalar = foreignField(Curve.Scalar); + + // point shape, but with independently random components, which will never form a valid point + let badPoint = spec({ + rng: Random.record({ + x: field.rng, + y: field.rng, + infinity: Random.constant(false), + }), + there: Point.from, + back: Point.toBigint, + provable: Point.provable, + }); + + // valid random point + let point = map({ from: field, to: badPoint }, (x) => + simpleMapToCurve(x, Curve) + ); + + // two random points that are not equal, so are a valid input to EC addition + let unequalPair = onlyIf(array(point, 2), ([p, q]) => !Curve.equal(p, q)); + + // test ec gadgets witness generation + + equivalentProvable({ from: [unequalPair], to: point, verbose: true })( + ([p, q]) => Curve.add(p, q), + ([p, q]) => EllipticCurve.add(p, q, Curve), + `${Curve.name} add` + ); + + equivalentProvable({ from: [point], to: point, verbose: true })( + Curve.double, + (p) => EllipticCurve.double(p, Curve), + `${Curve.name} double` + ); + + equivalentProvable({ from: [point], to: point, verbose: true })( + Curve.negate, + (p) => EllipticCurve.negate(p, Curve), + `${Curve.name} negate` + ); + + equivalentProvable({ from: [point], to: unit, verbose: true })( + (p) => Curve.isOnCurve(p) || throwError('expect on curve'), + (p) => EllipticCurve.assertOnCurve(p, Curve), + `${Curve.name} on curve` + ); + + equivalentProvable({ from: [point, scalar], to: point, verbose: true })( + (p, s) => { + let sp = Curve.scale(p, s); + assert(!sp.infinity, 'expect nonzero'); + return sp; + }, + (p, s) => EllipticCurve.scale(s, p, Curve), + `${Curve.name} scale` + ); +} diff --git a/src/lib/gadgets/foreign-field.ts b/src/lib/gadgets/foreign-field.ts new file mode 100644 index 0000000000..51b815dfc8 --- /dev/null +++ b/src/lib/gadgets/foreign-field.ts @@ -0,0 +1,711 @@ +/** + * Foreign field arithmetic gadgets. + */ +import { + inverse as modInverse, + mod, +} from '../../bindings/crypto/finite_field.js'; +import { provableTuple } from '../../bindings/lib/provable-snarky.js'; +import { Bool } from '../bool.js'; +import { Unconstrained } from '../circuit_value.js'; +import { Field } from '../field.js'; +import { Gates, foreignFieldAdd } from '../gates.js'; +import { Tuple, TupleN } from '../util/types.js'; +import { assertOneOf } from './basic.js'; +import { assert, bitSlice, exists, toVar, toVars } from './common.js'; +import { + l, + lMask, + multiRangeCheck, + l2, + l2Mask, + l3, + compactMultiRangeCheck, +} from './range-check.js'; + +// external API +export { ForeignField, Field3 }; + +// internal API +export { bigint3, Sign, split, combine, weakBound, Sum, assertMul }; + +/** + * A 3-tuple of Fields, representing a 3-limb bigint. + */ +type Field3 = [Field, Field, Field]; +type bigint3 = [bigint, bigint, bigint]; +type Sign = -1n | 1n; + +const ForeignField = { + add(x: Field3, y: Field3, f: bigint) { + return sum([x, y], [1n], f); + }, + sub(x: Field3, y: Field3, f: bigint) { + return sum([x, y], [-1n], f); + }, + negate(x: Field3, f: bigint) { + return sum([Field3.from(0n), x], [-1n], f); + }, + sum, + Sum(x: Field3) { + return new Sum(x); + }, + + mul: multiply, + inv: inverse, + div: divide, + assertMul, + + assertAlmostReduced, + + assertLessThan(x: Field3, f: bigint) { + assert(f > 0n, 'assertLessThan: upper bound must be positive'); + + // constant case + if (Field3.isConstant(x)) { + assert(Field3.toBigint(x) < f, 'assertLessThan: got x >= f'); + return; + } + // provable case + // we can just use negation `(f - 1) - x`. because the result is range-checked, it proves that x < f: + // `f - 1 - x \in [0, 2^3l) => x <= x + (f - 1 - x) = f - 1 < f` + // (note: ffadd can't add higher multiples of (f - 1). it must always use an overflow of -1, except for x = 0) + ForeignField.negate(x, f - 1n); + }, + + equals, +}; + +/** + * computes x[0] + sign[0] * x[1] + ... + sign[n-1] * x[n] modulo f + * + * assumes that inputs are range checked, does range check on the result. + */ +function sum(x: Field3[], sign: Sign[], f: bigint) { + assert(x.length === sign.length + 1, 'inputs and operators match'); + + // constant case + if (x.every(Field3.isConstant)) { + let xBig = x.map(Field3.toBigint); + let sum = sign.reduce((sum, s, i) => sum + s * xBig[i + 1], xBig[0]); + return Field3.from(mod(sum, f)); + } + // provable case - create chain of ffadd rows + x = x.map(toVars); + let result = x[0]; + for (let i = 0; i < sign.length; i++) { + ({ result } = singleAdd(result, x[i + 1], sign[i], f)); + } + // final zero row to hold result + Gates.zero(...result); + + // range check result + multiRangeCheck(result); + + return result; +} + +/** + * core building block for non-native addition + * + * **warning**: this just adds the `foreignFieldAdd` row; + * it _must_ be chained with a second row that holds the result in its first 3 cells. + * + * the second row could, for example, be `zero`, `foreignFieldMul`, or another `foreignFieldAdd`. + */ +function singleAdd(x: Field3, y: Field3, sign: Sign, f: bigint) { + let f_ = split(f); + + let [r0, r1, r2, overflow, carry] = exists(5, () => { + let x_ = toBigint3(x); + let y_ = toBigint3(y); + + // figure out if there's overflow + let r = combine(x_) + sign * combine(y_); + let overflow = 0n; + if (sign === 1n && r >= f) overflow = 1n; + if (sign === -1n && r < 0n) overflow = -1n; + if (f === 0n) overflow = 0n; // special case where overflow doesn't change anything + + // do the add with carry + // note: this "just works" with negative r01 + let r01 = combine2(x_) + sign * combine2(y_) - overflow * combine2(f_); + let carry = r01 >> l2; + r01 &= l2Mask; + let [r0, r1] = split2(r01); + let r2 = x_[2] + sign * y_[2] - overflow * f_[2] + carry; + + return [r0, r1, r2, overflow, carry]; + }); + + foreignFieldAdd({ left: x, right: y, overflow, carry, modulus: f_, sign }); + + return { result: [r0, r1, r2] satisfies Field3, overflow }; +} + +function multiply(a: Field3, b: Field3, f: bigint): Field3 { + assert(f < 1n << 259n, 'Foreign modulus fits in 259 bits'); + + // constant case + if (Field3.isConstant(a) && Field3.isConstant(b)) { + let ab = Field3.toBigint(a) * Field3.toBigint(b); + return Field3.from(mod(ab, f)); + } + + // provable case + let { r01, r2, q } = multiplyNoRangeCheck(a, b, f); + + // limb range checks on quotient and remainder + multiRangeCheck(q); + let r = compactMultiRangeCheck(r01, r2); + return r; +} + +function inverse(x: Field3, f: bigint): Field3 { + assert(f < 1n << 259n, 'Foreign modulus fits in 259 bits'); + + // constant case + if (Field3.isConstant(x)) { + let xInv = modInverse(Field3.toBigint(x), f); + assert(xInv !== undefined, 'inverse exists'); + return Field3.from(xInv); + } + + // provable case + let xInv = exists(3, () => { + let xInv = modInverse(Field3.toBigint(x), f); + return xInv === undefined ? [0n, 0n, 0n] : split(xInv); + }); + multiRangeCheck(xInv); + // we need to bound xInv because it's a multiplication input + let xInv2Bound = weakBound(xInv[2], f); + + let one: Field2 = [Field.from(1n), Field.from(0n)]; + assertMulInternal(x, xInv, one, f); + + // range check on result bound + // TODO: this uses two RCs too many.. need global RC stack + multiRangeCheck([xInv2Bound, Field.from(0n), Field.from(0n)]); + + return xInv; +} + +function divide( + x: Field3, + y: Field3, + f: bigint, + { allowZeroOverZero = false } = {} +) { + assert(f < 1n << 259n, 'Foreign modulus fits in 259 bits'); + + // constant case + if (Field3.isConstant(x) && Field3.isConstant(y)) { + let yInv = modInverse(Field3.toBigint(y), f); + assert(yInv !== undefined, 'inverse exists'); + return Field3.from(mod(Field3.toBigint(x) * yInv, f)); + } + + // provable case + // to show that z = x/y, we prove that z*y = x and y != 0 (the latter avoids the unconstrained 0/0 case) + let z = exists(3, () => { + let yInv = modInverse(Field3.toBigint(y), f); + if (yInv === undefined) return [0n, 0n, 0n]; + return split(mod(Field3.toBigint(x) * yInv, f)); + }); + multiRangeCheck(z); + let z2Bound = weakBound(z[2], f); + assertMulInternal(z, y, x, f); + + // range check on result bound + multiRangeCheck([z2Bound, Field.from(0n), Field.from(0n)]); + + if (!allowZeroOverZero) { + ForeignField.equals(y, 0n, f).assertFalse(); + } + return z; +} + +/** + * Common logic for gadgets that expect a certain multiplication result a priori, instead of just using the remainder. + */ +function assertMulInternal( + x: Field3, + y: Field3, + xy: Field3 | Field2, + f: bigint, + message?: string +) { + let { r01, r2, q } = multiplyNoRangeCheck(x, y, f); + + // range check on quotient + multiRangeCheck(q); + + // bind remainder to input xy + if (xy.length === 2) { + let [xy01, xy2] = xy; + r01.assertEquals(xy01, message); + r2.assertEquals(xy2, message); + } else { + let xy01 = xy[0].add(xy[1].mul(1n << l)); + r01.assertEquals(xy01, message); + r2.assertEquals(xy[2], message); + } +} + +/** + * Core building block for all gadgets using foreign field multiplication. + */ +function multiplyNoRangeCheck(a: Field3, b: Field3, f: bigint) { + // notation follows https://github.com/o1-labs/rfcs/blob/main/0006-ffmul-revised.md + let f_ = (1n << l3) - f; + let [f_0, f_1, f_2] = split(f_); + let f2 = f >> l2; + let f2Bound = (1n << l) - f2 - 1n; + + let witnesses = exists(21, () => { + // convert inputs to bigints + let [a0, a1, a2] = toBigint3(a); + let [b0, b1, b2] = toBigint3(b); + + // compute q and r such that a*b = q*f + r + let ab = combine([a0, a1, a2]) * combine([b0, b1, b2]); + let q = ab / f; + let r = ab - q * f; + + let [q0, q1, q2] = split(q); + let [r0, r1, r2] = split(r); + let r01 = combine2([r0, r1]); + + // compute product terms + let p0 = a0 * b0 + q0 * f_0; + let p1 = a0 * b1 + a1 * b0 + q0 * f_1 + q1 * f_0; + let p2 = a0 * b2 + a1 * b1 + a2 * b0 + q0 * f_2 + q1 * f_1 + q2 * f_0; + + let [p10, p110, p111] = split(p1); + let p11 = combine2([p110, p111]); + + // carry bottom limbs + let c0 = (p0 + (p10 << l) - r01) >> l2; + + // carry top limb + let c1 = (p2 - r2 + p11 + c0) >> l; + + // split high carry + let c1_00 = bitSlice(c1, 0, 12); + let c1_12 = bitSlice(c1, 12, 12); + let c1_24 = bitSlice(c1, 24, 12); + let c1_36 = bitSlice(c1, 36, 12); + let c1_48 = bitSlice(c1, 48, 12); + let c1_60 = bitSlice(c1, 60, 12); + let c1_72 = bitSlice(c1, 72, 12); + let c1_84 = bitSlice(c1, 84, 2); + let c1_86 = bitSlice(c1, 86, 2); + let c1_88 = bitSlice(c1, 88, 2); + let c1_90 = bitSlice(c1, 90, 1); + + // quotient high bound + let q2Bound = q2 + f2Bound; + + // prettier-ignore + return [ + r01, r2, + q0, q1, q2, + q2Bound, + p10, p110, p111, + c0, + c1_00, c1_12, c1_24, c1_36, c1_48, c1_60, c1_72, + c1_84, c1_86, c1_88, c1_90, + ]; + }); + + // prettier-ignore + let [ + r01, r2, + q0, q1, q2, + q2Bound, + p10, p110, p111, + c0, + c1_00, c1_12, c1_24, c1_36, c1_48, c1_60, c1_72, + c1_84, c1_86, c1_88, c1_90, + ] = witnesses; + + let q: Field3 = [q0, q1, q2]; + + // ffmul gate. this already adds the following zero row. + Gates.foreignFieldMul({ + left: a, + right: b, + remainder: [r01, r2], + quotient: q, + quotientHiBound: q2Bound, + product1: [p10, p110, p111], + carry0: c0, + carry1p: [c1_00, c1_12, c1_24, c1_36, c1_48, c1_60, c1_72], + carry1c: [c1_84, c1_86, c1_88, c1_90], + foreignFieldModulus2: f2, + negForeignFieldModulus: [f_0, f_1, f_2], + }); + + // multi-range check on internal values + multiRangeCheck([p10, p110, q2Bound]); + + // note: this function is supposed to be the most flexible interface to the ffmul gate. + // that's why we don't add range checks on q and r here, because there are valid use cases + // for not range-checking either of them -- for example, they could be wired to other + // variables that are already range-checked, or to constants / public inputs. + return { r01, r2, q }; +} + +function weakBound(x: Field, f: bigint) { + // if f0, f1 === 0, we can use a stronger bound x[2] < f2 + // because this is true for all field elements x in [0,f) + if ((f & l2Mask) === 0n) { + return x.add(lMask + 1n - (f >> l2)); + } + // otherwise, we use x[2] < f2 + 1, so we allow x[2] === f2 + return x.add(lMask - (f >> l2)); +} + +/** + * Apply range checks and weak bounds checks to a list of Field3s. + * Optimal if the list length is a multiple of 3. + */ +function assertAlmostReduced(xs: Field3[], f: bigint, skipMrc = false) { + let bounds: Field[] = []; + + for (let x of xs) { + if (!skipMrc) multiRangeCheck(x); + + bounds.push(weakBound(x[2], f)); + if (TupleN.hasLength(3, bounds)) { + multiRangeCheck(bounds); + bounds = []; + } + } + if (TupleN.hasLength(1, bounds)) { + multiRangeCheck([...bounds, Field.from(0n), Field.from(0n)]); + } + if (TupleN.hasLength(2, bounds)) { + multiRangeCheck([...bounds, Field.from(0n)]); + } +} + +/** + * check whether x = c mod f + * + * c is a constant, and we require c in [0, f) + * + * assumes that x is almost reduced mod f, so we know that x might be c or c + f, but not c + 2f, c + 3f, ... + */ +function equals(x: Field3, c: bigint, f: bigint) { + assert(c >= 0n && c < f, 'equals: c must be in [0, f)'); + + // constant case + if (Field3.isConstant(x)) { + return new Bool(mod(Field3.toBigint(x), f) === c); + } + + // provable case + if (f >= 1n << l2) { + // check whether x = 0 or x = f + let x01 = toVar(x[0].add(x[1].mul(1n << l))); + let [c01, c2] = [c & l2Mask, c >> l2]; + let [cPlusF01, cPlusF2] = [(c + f) & l2Mask, (c + f) >> l2]; + + // (x01, x2) = (c01, c2) + let isC = x01.equals(c01).and(x[2].equals(c2)); + // (x01, x2) = (cPlusF01, cPlusF2) + let isCPlusF = x01.equals(cPlusF01).and(x[2].equals(cPlusF2)); + + return isC.or(isCPlusF); + } else { + // if f < 2^2l, the approach above doesn't work (we don't know from x[2] = 0 that x < 2f), + // so in that case we assert that x < f and then check whether it's equal to c + ForeignField.assertLessThan(x, f); + let x012 = toVar(x[0].add(x[1].mul(1n << l)).add(x[2].mul(1n << l2))); + return x012.equals(c); + } +} + +const Field3 = { + /** + * Turn a bigint into a 3-tuple of Fields + */ + from(x: bigint): Field3 { + return Tuple.map(split(x), Field.from); + }, + + /** + * Turn a 3-tuple of Fields into a bigint + */ + toBigint(x: Field3): bigint { + return combine(toBigint3(x)); + }, + + /** + * Turn several 3-tuples of Fields into bigints + */ + toBigints>(...xs: T) { + return Tuple.map(xs, Field3.toBigint); + }, + + /** + * Check whether a 3-tuple of Fields is constant + */ + isConstant(x: Field3) { + return x.every((x) => x.isConstant()); + }, + + /** + * Provable interface for `Field3 = [Field, Field, Field]`. + * + * Note: Witnessing this creates a plain tuple of field elements without any implicit + * range checks. + */ + provable: provableTuple([Field, Field, Field]), +}; + +type Field2 = [Field, Field]; +const Field2 = { + toBigint(x: Field2): bigint { + return combine2(Tuple.map(x, (x) => x.toBigInt())); + }, +}; + +function toBigint3(x: Field3): bigint3 { + return Tuple.map(x, (x) => x.toBigInt()); +} + +function combine([x0, x1, x2]: bigint3) { + return x0 + (x1 << l) + (x2 << l2); +} +function split(x: bigint): bigint3 { + return [x & lMask, (x >> l) & lMask, (x >> l2) & lMask]; +} + +function combine2([x0, x1]: bigint3 | [bigint, bigint]) { + return x0 + (x1 << l); +} +function split2(x: bigint): [bigint, bigint] { + return [x & lMask, (x >> l) & lMask]; +} + +/** + * Optimized multiplication of sums, like (x + y)*z = a + b + c + * + * We use several optimizations over naive summing and then multiplying: + * + * - we skip the range check on the remainder sum, because ffmul is sound with r being a sum of range-checked values + * - we replace the range check on the input sums with an extra low limb sum using generic gates + * - we chain the first input's sum into the ffmul gate + * + * As usual, all values are assumed to be range checked, and the left and right multiplication inputs + * are assumed to be bounded such that `l * r < 2^264 * (native modulus)`. + * However, all extra checks that are needed on the _sums_ are handled here. + */ +function assertMul( + x: Field3 | Sum, + y: Field3 | Sum, + xy: Field3 | Sum, + f: bigint, + message?: string +) { + x = Sum.fromUnfinished(x); + y = Sum.fromUnfinished(y); + xy = Sum.fromUnfinished(xy); + + // conservative estimate to ensure that multiplication bound is satisfied + // we assume that all summands si are bounded with si[2] <= f[2] checks, which implies si < 2^k where k := ceil(log(f)) + // our assertion below gives us + // |x|*|y| + q*f + |r| < (x.length * y.length) 2^2k + 2^2k + 2^2k < 3 * 2^(2*258) < 2^264 * (native modulus) + assert( + BigInt(Math.ceil(Math.sqrt(x.length * y.length))) * f < 1n << 258n, + `Foreign modulus is too large for multiplication of sums of lengths ${x.length} and ${y.length}` + ); + + // finish the y and xy sums with a zero gate + let y0 = y.finishForMulInput(f); + let xy0 = xy.finish(f); + + // x is chained into the ffmul gate + let x0 = x.finishForMulInput(f, true); + + // constant case + if ( + Field3.isConstant(x0) && + Field3.isConstant(y0) && + Field3.isConstant(xy0) + ) { + let x_ = Field3.toBigint(x0); + let y_ = Field3.toBigint(y0); + let xy_ = Field3.toBigint(xy0); + assert( + mod(x_ * y_, f) === xy_, + message ?? 'assertMul(): incorrect multiplication result' + ); + return; + } + + assertMulInternal(x0, y0, xy0, f, message); +} + +class Sum { + #result?: Field3; + #summands: Field3[]; + #ops: Sign[] = []; + + constructor(x: Field3) { + this.#summands = [x]; + } + + get result() { + assert(this.#result !== undefined, 'sum not finished'); + return this.#result; + } + + get length() { + return this.#summands.length; + } + + add(y: Field3) { + assert(this.#result === undefined, 'sum already finished'); + this.#ops.push(1n); + this.#summands.push(y); + return this; + } + + sub(y: Field3) { + assert(this.#result === undefined, 'sum already finished'); + this.#ops.push(-1n); + this.#summands.push(y); + return this; + } + + #return(x: Field3) { + this.#result = x; + return x; + } + + isConstant() { + return this.#summands.every(Field3.isConstant); + } + + finish(f: bigint, isChained = false) { + assert(this.#result === undefined, 'sum already finished'); + let signs = this.#ops; + let n = signs.length; + if (n === 0) return this.#return(this.#summands[0]); + + // constant case + if (this.isConstant()) { + return this.#return(sum(this.#summands, signs, f)); + } + + // provable case + let x = this.#summands.map(toVars); + let result = x[0]; + + for (let i = 0; i < n; i++) { + ({ result } = singleAdd(result, x[i + 1], signs[i], f)); + } + if (!isChained) Gates.zero(...result); + + this.#result = result; + return result; + } + + // TODO this is complex and should be removed once we fix the ffadd gate to constrain all limbs individually + finishForMulInput(f: bigint, isChained = false) { + assert(this.#result === undefined, 'sum already finished'); + let signs = this.#ops; + let n = signs.length; + if (n === 0) return this.#return(this.#summands[0]); + + // constant case + if (this.isConstant()) { + return this.#return(sum(this.#summands, signs, f)); + } + + // provable case + let xs = this.#summands.map(toVars); + + // since the sum becomes a multiplication input, we need to constrain all limbs _individually_. + // sadly, ffadd only constrains the low and middle limb together. + // we could fix it with a RC just for the lower two limbs + // but it's cheaper to add generic gates which handle the lowest limb separately, and avoids the unfilled MRC slot + let f0 = f & lMask; + + // generic gates for low limbs + let x0 = xs[0][0]; + let x0s: Field[] = []; + let overflows: Field[] = []; + let xRef = Unconstrained.witness(() => Field3.toBigint(xs[0])); + + // this loop mirrors the computation that a chain of ffadd gates does, + // but everything is done only on the lowest limb and using generic gates. + // the output is a sequence of low limbs (x0) and overflows, which will be wired to the ffadd results at each step. + for (let i = 0; i < n; i++) { + // compute carry and overflow + let [carry, overflow] = exists(2, () => { + // this duplicates some of the logic in singleAdd + let x = xRef.get(); + let x0 = x & lMask; + let xi = toBigint3(xs[i + 1]); + let sign = signs[i]; + + // figure out if there's overflow + x += sign * combine(xi); + let overflow = 0n; + if (sign === 1n && x >= f) overflow = 1n; + if (sign === -1n && x < 0n) overflow = -1n; + if (f === 0n) overflow = 0n; + xRef.set(x - overflow * f); + + // add with carry, only on the lowest limb + x0 = x0 + sign * xi[0] - overflow * f0; + let carry = x0 >> l; + return [carry, overflow]; + }); + overflows.push(overflow); + + // constrain carry + assertOneOf(carry, [0n, 1n, -1n]); + + // x0 <- x0 + s*xi0 - o*f0 - c*2^l + x0 = toVar( + x0 + .add(xs[i + 1][0].mul(signs[i])) + .sub(overflow.mul(f0)) + .sub(carry.mul(1n << l)) + ); + x0s.push(x0); + } + + // ffadd chain + let x = xs[0]; + for (let i = 0; i < n; i++) { + let { result, overflow } = singleAdd(x, xs[i + 1], signs[i], f); + // wire low limb and overflow to previous values + result[0].assertEquals(x0s[i]); + overflow.assertEquals(overflows[i]); + x = result; + } + if (!isChained) Gates.zero(...x); + + this.#result = x; + return x; + } + + rangeCheck() { + assert(this.#result !== undefined, 'sum not finished'); + if (this.#ops.length > 0) multiRangeCheck(this.#result); + } + + static fromUnfinished(x: Field3 | Sum) { + if (x instanceof Sum) { + assert(x.#result === undefined, 'sum already finished'); + return x; + } + return new Sum(x); + } +} diff --git a/src/lib/gadgets/foreign-field.unit-test.ts b/src/lib/gadgets/foreign-field.unit-test.ts new file mode 100644 index 0000000000..135fca61a1 --- /dev/null +++ b/src/lib/gadgets/foreign-field.unit-test.ts @@ -0,0 +1,360 @@ +import type { FiniteField } from '../../bindings/crypto/finite_field.js'; +import { exampleFields } from '../../bindings/crypto/finite-field-examples.js'; +import { + array, + equivalent, + equivalentAsync, + equivalentProvable, + fromRandom, + record, + unit, +} from '../testing/equivalent.js'; +import { Random } from '../testing/random.js'; +import { Gadgets } from './gadgets.js'; +import { ZkProgram } from '../proof_system.js'; +import { Provable } from '../provable.js'; +import { assert } from './common.js'; +import { + allConstant, + and, + constraintSystem, + contains, + equals, + ifNotAllConstant, + not, + or, + repeat, + withoutGenerics, +} from '../testing/constraint-system.js'; +import { GateType } from '../../snarky.js'; +import { AnyTuple } from '../util/types.js'; +import { + foreignField, + throwError, + unreducedForeignField, +} from './test-utils.js'; +import { l2 } from './range-check.js'; + +const { ForeignField, Field3 } = Gadgets; + +let sign = fromRandom(Random.oneOf(1n as const, -1n as const)); + +let fields = [ + exampleFields.small, + exampleFields.babybear, + exampleFields.f25519, + exampleFields.secp256k1, + exampleFields.secq256k1, + exampleFields.bls12_381_scalar, + exampleFields.Fq, + exampleFields.Fp, +]; + +// tests for witness generation + +for (let F of fields) { + let f = foreignField(F); + let eq2 = equivalentProvable({ from: [f, f], to: f }); + + eq2(F.add, (x, y) => ForeignField.add(x, y, F.modulus), 'add'); + eq2(F.sub, (x, y) => ForeignField.sub(x, y, F.modulus), 'sub'); + eq2(F.mul, (x, y) => ForeignField.mul(x, y, F.modulus), 'mul'); + equivalentProvable({ from: [f], to: f })( + (x) => F.inverse(x) ?? throwError('no inverse'), + (x) => ForeignField.inv(x, F.modulus), + 'inv' + ); + eq2( + (x, y) => F.div(x, y) ?? throwError('no inverse'), + (x, y) => ForeignField.div(x, y, F.modulus), + 'div' + ); + equivalentProvable({ from: [f, f], to: unit })( + (x, y) => assertMulExampleNaive(Field3.from(x), Field3.from(y), F.modulus), + (x, y) => assertMulExample(x, y, F.modulus), + 'assertMul' + ); + // test for assertMul which mostly tests the negative case because for random inputs, we expect + // (x - y) * z != a + b + equivalentProvable({ from: [f, f, f, f, f], to: unit })( + (x, y, z, a, b) => assert(F.mul(F.sub(x, y), z) === F.add(a, b)), + (x, y, z, a, b) => + ForeignField.assertMul( + ForeignField.Sum(x).sub(y), + z, + ForeignField.Sum(a).add(b), + F.modulus + ), + 'assertMul negative' + ); + + // tests with inputs that aren't reduced mod f + let big264 = unreducedForeignField(264, F); // this is the max size supported by our range checks / ffadd + let big258 = unreducedForeignField(258, F); // rough max size supported by ffmul + + equivalentProvable({ from: [big264, big264], to: big264 })( + F.add, + (x, y) => ForeignField.add(x, y, F.modulus), + 'add unreduced' + ); + // subtraction doesn't work with unreduced y because the range check on the result prevents x-y < -f + equivalentProvable({ from: [big264, f], to: big264 })( + F.sub, + (x, y) => ForeignField.sub(x, y, F.modulus), + 'sub unreduced' + ); + equivalentProvable({ from: [big258, big258], to: f })( + F.mul, + (x, y) => ForeignField.mul(x, y, F.modulus), + 'mul unreduced' + ); + equivalentProvable({ from: [big258], to: f })( + (x) => F.inverse(x) ?? throwError('no inverse'), + (x) => ForeignField.inv(x, F.modulus), + 'inv unreduced' + ); + // the div() gadget doesn't work with unreduced x because the backwards check (x/y)*y === x fails + // and it's not valid with unreduced y because we only assert y != 0, y != f but it can be 2f, 3f, etc. + // the combination of inv() and mul() is more flexible (but much more expensive, ~40 vs ~30 constraints) + equivalentProvable({ from: [big258, big258], to: f })( + (x, y) => F.div(x, y) ?? throwError('no inverse'), + (x, y) => ForeignField.mul(x, ForeignField.inv(y, F.modulus), F.modulus), + 'div unreduced' + ); + + equivalent({ from: [big264], to: unit })( + (x) => assertWeakBound(x, F.modulus), + (x) => ForeignField.assertAlmostReduced([x], F.modulus) + ); + + // sumchain of 5 + equivalentProvable({ from: [array(f, 5), array(sign, 4)], to: f })( + (xs, signs) => sum(xs, signs, F), + (xs, signs) => ForeignField.sum(xs, signs, F.modulus), + 'sumchain 5' + ); + + // sumchain up to 100 + let operands = array(record({ x: f, sign }), Random.nat(100)); + + equivalentProvable({ from: [f, operands], to: f })( + (x0, ts) => { + let xs = [x0, ...ts.map((t) => t.x)]; + let signs = ts.map((t) => t.sign); + return sum(xs, signs, F); + }, + (x0, ts) => { + let xs = [x0, ...ts.map((t) => t.x)]; + let signs = ts.map((t) => t.sign); + return ForeignField.sum(xs, signs, F.modulus); + }, + 'sumchain long' + ); +} + +// setup zk program tests + +let F = exampleFields.secp256k1; +let f = foreignField(F); +let big264 = unreducedForeignField(264, F); +let chainLength = 5; +let signs = [1n, -1n, -1n, 1n] satisfies (-1n | 1n)[]; + +let ffProgram = ZkProgram({ + name: 'foreign-field', + publicOutput: Field3.provable, + methods: { + sumchain: { + privateInputs: [Provable.Array(Field3.provable, chainLength)], + method(xs) { + return ForeignField.sum(xs, signs, F.modulus); + }, + }, + mulWithBoundsCheck: { + privateInputs: [Field3.provable, Field3.provable], + method(x, y) { + ForeignField.assertAlmostReduced([x, y], F.modulus); + return ForeignField.mul(x, y, F.modulus); + }, + }, + mul: { + privateInputs: [Field3.provable, Field3.provable], + method(x, y) { + return ForeignField.mul(x, y, F.modulus); + }, + }, + inv: { + privateInputs: [Field3.provable], + method(x) { + return ForeignField.inv(x, F.modulus); + }, + }, + div: { + privateInputs: [Field3.provable, Field3.provable], + method(x, y) { + return ForeignField.div(x, y, F.modulus); + }, + }, + }, +}); + +// tests for constraint system + +function addChain(length: number) { + return repeat(length - 1, 'ForeignFieldAdd').concat('Zero'); +} +let mrc: GateType[] = ['RangeCheck0', 'RangeCheck0', 'RangeCheck1', 'Zero']; + +constraintSystem.fromZkProgram( + ffProgram, + 'sumchain', + ifNotAllConstant( + and( + contains([addChain(chainLength), mrc]), + withoutGenerics(equals([...addChain(chainLength), ...mrc])) + ) + ) +); + +let mulChain: GateType[] = ['ForeignFieldMul', 'Zero']; +let mulLayout = ifNotAllConstant( + and( + contains([mulChain, mrc, mrc, mrc]), + withoutGenerics(equals([...mulChain, ...repeat(3, mrc)])) + ) +); +let invLayout = ifNotAllConstant( + and( + contains([mrc, mulChain, mrc, mrc, mrc]), + withoutGenerics(equals([...mrc, ...mulChain, ...repeat(3, mrc)])) + ) +); + +constraintSystem.fromZkProgram(ffProgram, 'mul', mulLayout); +constraintSystem.fromZkProgram(ffProgram, 'inv', invLayout); +constraintSystem.fromZkProgram(ffProgram, 'div', invLayout); + +// tests with proving + +const runs = 2; + +await ffProgram.compile(); + +await equivalentAsync({ from: [array(f, chainLength)], to: f }, { runs })( + (xs) => sum(xs, signs, F), + async (xs) => { + let proof = await ffProgram.sumchain(xs); + assert(await ffProgram.verify(proof), 'verifies'); + return proof.publicOutput; + }, + 'prove chain' +); + +await equivalentAsync({ from: [big264, big264], to: f }, { runs })( + (x, y) => { + assertWeakBound(x, F.modulus); + assertWeakBound(y, F.modulus); + return F.mul(x, y); + }, + async (x, y) => { + let proof = await ffProgram.mulWithBoundsCheck(x, y); + assert(await ffProgram.verify(proof), 'verifies'); + return proof.publicOutput; + }, + 'prove mul' +); + +await equivalentAsync({ from: [f, f], to: f }, { runs })( + (x, y) => F.div(x, y) ?? throwError('no inverse'), + async (x, y) => { + let proof = await ffProgram.div(x, y); + assert(await ffProgram.verify(proof), 'verifies'); + return proof.publicOutput; + }, + 'prove div' +); + +// assert mul example +// (x - y) * (x + y) = x^2 - y^2 + +function assertMulExample(x: Gadgets.Field3, y: Gadgets.Field3, f: bigint) { + // witness x^2, y^2 + let x2 = Provable.witness(Field3.provable, () => ForeignField.mul(x, x, f)); + let y2 = Provable.witness(Field3.provable, () => ForeignField.mul(y, y, f)); + + // assert (x - y) * (x + y) = x^2 - y^2 + let xMinusY = ForeignField.Sum(x).sub(y); + let xPlusY = ForeignField.Sum(x).add(y); + let x2MinusY2 = ForeignField.Sum(x2).sub(y2); + ForeignField.assertMul(xMinusY, xPlusY, x2MinusY2, f); +} + +function assertMulExampleNaive( + x: Gadgets.Field3, + y: Gadgets.Field3, + f: bigint +) { + // witness x^2, y^2 + let x2 = Provable.witness(Field3.provable, () => ForeignField.mul(x, x, f)); + let y2 = Provable.witness(Field3.provable, () => ForeignField.mul(y, y, f)); + + // assert (x - y) * (x + y) = x^2 - y^2 + let lhs = ForeignField.mul( + ForeignField.sub(x, y, f), + ForeignField.add(x, y, f), + f + ); + let rhs = ForeignField.sub(x2, y2, f); + Provable.assertEqual(Field3.provable, lhs, rhs); +} + +let from2 = { from: [f, f] satisfies AnyTuple }; +let gates = constraintSystem.size(from2, (x, y) => + assertMulExample(x, y, F.modulus) +); +let gatesNaive = constraintSystem.size(from2, (x, y) => + assertMulExampleNaive(x, y, F.modulus) +); +// the assertMul() version should save 11.5 rows: +// -2*1.5 rows by replacing input MRCs with low-limb ffadd +// -2*4 rows for avoiding the MRC on both mul() and sub() outputs +// -1 row for chaining one ffadd into ffmul +// +0.5 rows for having to combine the two lower result limbs before wiring to ffmul remainder +assert(gates + 11 <= gatesNaive, 'assertMul() saves at least 11 constraints'); + +let addChainedIntoMul: GateType[] = ['ForeignFieldAdd', ...mulChain]; + +constraintSystem( + 'assert mul', + from2, + (x, y) => assertMulExample(x, y, F.modulus), + or( + and( + contains([addChain(2), addChain(2), addChainedIntoMul]), + // assertMul() doesn't use any range checks besides on internal values and the quotient + containsNTimes(2, mrc) + ), + allConstant + ) +); + +// helper + +function containsNTimes(n: number, pattern: readonly GateType[]) { + return and( + contains(repeat(n, pattern)), + not(contains(repeat(n + 1, pattern))) + ); +} + +function sum(xs: bigint[], signs: (1n | -1n)[], F: FiniteField) { + let sum = xs[0]; + for (let i = 0; i < signs.length; i++) { + sum = signs[i] === 1n ? F.add(sum, xs[i + 1]) : F.sub(sum, xs[i + 1]); + } + return sum; +} + +function assertWeakBound(x: bigint, f: bigint) { + assert(x >= 0n && x >> l2 <= f >> l2, 'weak bound'); +} diff --git a/src/lib/gadgets/gadgets.ts b/src/lib/gadgets/gadgets.ts new file mode 100644 index 0000000000..356f3371d7 --- /dev/null +++ b/src/lib/gadgets/gadgets.ts @@ -0,0 +1,814 @@ +/** + * Wrapper file for various gadgets, with a namespace and doccomments. + */ +import { + compactMultiRangeCheck, + multiRangeCheck, + rangeCheck16, + rangeCheck64, + rangeCheck32, + rangeCheckN, + isInRangeN, + rangeCheck8, +} from './range-check.js'; +import { + not, + rotate32, + rotate64, + xor, + and, + leftShift64, + rightShift64, + leftShift32, +} from './bitwise.js'; +import { Field } from '../core.js'; +import { ForeignField, Field3, Sum } from './foreign-field.js'; +import { divMod32, addMod32 } from './arithmetic.js'; + +export { Gadgets }; + +const Gadgets = { + /** + * Asserts that the input value is in the range [0, 2^64). + * + * This function proves that the provided field element can be represented with 64 bits. + * If the field element exceeds 64 bits, an error is thrown. + * + * @param x - The value to be range-checked. + * + * @throws Throws an error if the input value exceeds 64 bits. + * + * @example + * ```ts + * const x = Provable.witness(Field, () => Field(12345678n)); + * Gadgets.rangeCheck64(x); // successfully proves 64-bit range + * + * const xLarge = Provable.witness(Field, () => Field(12345678901234567890123456789012345678n)); + * Gadgets.rangeCheck64(xLarge); // throws an error since input exceeds 64 bits + * ``` + * + * **Note**: Small "negative" field element inputs are interpreted as large integers close to the field size, + * and don't pass the 64-bit check. If you want to prove that a value lies in the int64 range [-2^63, 2^63), + * you could use `rangeCheck64(x.add(1n << 63n))`. + */ + rangeCheck64(x: Field) { + return rangeCheck64(x); + }, + + /** + * Asserts that the input value is in the range [0, 2^32). + * + * This function proves that the provided field element can be represented with 32 bits. + * If the field element exceeds 32 bits, an error is thrown. + * + * @param x - The value to be range-checked. + * + * @throws Throws an error if the input value exceeds 32 bits. + * + * @example + * ```ts + * const x = Provable.witness(Field, () => Field(12345678n)); + * Gadgets.rangeCheck32(x); // successfully proves 32-bit range + * + * const xLarge = Provable.witness(Field, () => Field(12345678901234567890123456789012345678n)); + * Gadgets.rangeCheck32(xLarge); // throws an error since input exceeds 32 bits + * ``` + * + * **Note**: Small "negative" field element inputs are interpreted as large integers close to the field size, + * and don't pass the 32-bit check. If you want to prove that a value lies in the int32 range [-2^31, 2^31), + * you could use `rangeCheck32(x.add(1n << 31n))`. + */ + rangeCheck32(x: Field) { + return rangeCheck32(x); + }, + + /** + * Asserts that the input value is in the range [0, 2^n). `n` must be a multiple of 16. + * + * This function proves that the provided field element can be represented with `n` bits. + * If the field element exceeds `n` bits, an error is thrown. + * + * @param x - The value to be range-checked. + * @param n - The number of bits to be considered for the range check. + * @param message - Optional message to be displayed when the range check fails. + * + * @throws Throws an error if the input value exceeds `n` bits. + * + * @example + * ```ts + * const x = Provable.witness(Field, () => Field(12345678n)); + * Gadgets.rangeCheckN(32, x); // successfully proves 32-bit range + * + * const xLarge = Provable.witness(Field, () => Field(12345678901234567890123456789012345678n)); + * Gadgets.rangeCheckN(32, xLarge); // throws an error since input exceeds 32 bits + * ``` + */ + rangeCheckN(n: number, x: Field, message?: string) { + return rangeCheckN(n, x, message); + }, + + /** + * Checks whether the input value is in the range [0, 2^n). `n` must be a multiple of 16. + * + * This function proves that the provided field element can be represented with `n` bits. + * If the field element exceeds `n` bits, `Bool(false)` is returned and `Bool(true)` otherwise. + * + * @param x - The value to be range-checked. + * @param n - The number of bits to be considered for the range check. + * + * @returns a Bool indicating whether the input value is in the range [0, 2^n). + * + * @example + * ```ts + * const x = Provable.witness(Field, () => Field(12345678n)); + * let inRange = Gadgets.isInRangeN(32, x); // return Bool(true) + * ``` + */ + isInRangeN(n: number, x: Field) { + return isInRangeN(n, x); + }, + /* + * Asserts that the input value is in the range [0, 2^16). + * + * See {@link Gadgets.rangeCheck64} for analogous details and usage examples. + */ + rangeCheck16(x: Field) { + return rangeCheck16(x); + }, + + /** + * Asserts that the input value is in the range [0, 2^8). + * + * See {@link Gadgets.rangeCheck64} for analogous details and usage examples. + */ + rangeCheck8(x: Field) { + return rangeCheck8(x); + }, + + /** + * A (left and right) rotation operates similarly to the shift operation (`<<` for left and `>>` for right) in JavaScript, + * with the distinction that the bits are circulated to the opposite end of a 64-bit representation rather than being discarded. + * For a left rotation, this means that bits shifted off the left end reappear at the right end. + * Conversely, for a right rotation, bits shifted off the right end reappear at the left end. + * + * It’s important to note that these operations are performed considering the big-endian 64-bit representation of the number, + * where the most significant (64th) bit is on the left end and the least significant bit is on the right end. + * The `direction` parameter is a string that accepts either `'left'` or `'right'`, determining the direction of the rotation. + * + * **Important:** The gadget assumes that its input is at most 64 bits in size. + * + * If the input exceeds 64 bits, the gadget is invalid and fails to prove correct execution of the rotation. + * To safely use `rotate64()`, you need to make sure that the value passed in is range-checked to 64 bits; + * for example, using {@link Gadgets.rangeCheck64}. + * + * You can find more details about the implementation in the [Mina book](https://o1-labs.github.io/proof-systems/specs/kimchi.html?highlight=gates#rotation) + * + * @param field {@link Field} element to rotate. + * @param bits amount of bits to rotate this {@link Field} element with. + * @param direction left or right rotation direction. + * + * @throws Throws an error if the input value exceeds 64 bits. + * + * @example + * ```ts + * const x = Provable.witness(Field, () => Field(0b001100)); + * const y = Gadgets.rotate64(x, 2, 'left'); // left rotation by 2 bits + * const z = Gadgets.rotate64(x, 2, 'right'); // right rotation by 2 bits + * y.assertEquals(0b110000); + * z.assertEquals(0b000011); + * + * const xLarge = Provable.witness(Field, () => Field(12345678901234567890123456789012345678n)); + * Gadgets.rotate64(xLarge, 32, "left"); // throws an error since input exceeds 64 bits + * ``` + */ + rotate64(field: Field, bits: number, direction: 'left' | 'right' = 'left') { + return rotate64(field, bits, direction); + }, + /** + * A (left and right) rotation operates similarly to the shift operation (`<<` for left and `>>` for right) in JavaScript, + * with the distinction that the bits are circulated to the opposite end of a 32-bit representation rather than being discarded. + * For a left rotation, this means that bits shifted off the left end reappear at the right end. + * Conversely, for a right rotation, bits shifted off the right end reappear at the left end. + * + * It’s important to note that these operations are performed considering the big-endian 32-bit representation of the number, + * where the most significant (32th) bit is on the left end and the least significant bit is on the right end. + * The `direction` parameter is a string that accepts either `'left'` or `'right'`, determining the direction of the rotation. + * + * **Important:** The gadget assumes that its input is at most 32 bits in size. + * + * If the input exceeds 32 bits, the gadget is invalid and fails to prove correct execution of the rotation. + * To safely use `rotate32()`, you need to make sure that the value passed in is range-checked to 32 bits; + * for example, using {@link Gadgets.rangeCheck32}. + * + * + * @param field {@link Field} element to rotate. + * @param bits amount of bits to rotate this {@link Field} element with. + * @param direction left or right rotation direction. + * + * @throws Throws an error if the input value exceeds 32 bits. + * + * @example + * ```ts + * const x = Provable.witness(Field, () => Field(0b001100)); + * const y = Gadgets.rotate32(x, 2, 'left'); // left rotation by 2 bits + * const z = Gadgets.rotate32(x, 2, 'right'); // right rotation by 2 bits + * y.assertEquals(0b110000); + * z.assertEquals(0b000011); + * + * const xLarge = Provable.witness(Field, () => Field(12345678901234567890123456789012345678n)); + * Gadgets.rotate32(xLarge, 32, "left"); // throws an error since input exceeds 32 bits + * ``` + */ + rotate32(field: Field, bits: number, direction: 'left' | 'right' = 'left') { + return rotate32(field, bits, direction); + }, + /** + * Bitwise XOR gadget on {@link Field} elements. Equivalent to the [bitwise XOR `^` operator in JavaScript](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Bitwise_XOR). + * A XOR gate works by comparing two bits and returning `1` if two bits differ, and `0` if two bits are equal. + * + * This gadget builds a chain of XOR gates recursively. Each XOR gate can verify 16 bit at most. If your input elements exceed 16 bit, another XOR gate will be added to the chain. + * + * The `length` parameter lets you define how many bits should be compared. `length` is rounded to the nearest multiple of 16, `paddedLength = ceil(length / 16) * 16`, and both input values are constrained to fit into `paddedLength` bits. The output is guaranteed to have at most `paddedLength` bits as well. + * + * **Note:** Specifying a larger `length` parameter adds additional constraints. + * + * It is also important to mention that specifying a smaller `length` allows the verifier to infer the length of the original input data (e.g. smaller than 16 bit if only one XOR gate has been used). + * A zkApp developer should consider these implications when choosing the `length` parameter and carefully weigh the trade-off between increased amount of constraints and security. + * + * **Important:** Both {@link Field} elements need to fit into `2^paddedLength - 1`. Otherwise, an error is thrown and no proof can be generated. + * + * For example, with `length = 2` (`paddedLength = 16`), `xor()` will fail for any input that is larger than `2**16`. + * + * You can find more details about the implementation in the [Mina book](https://o1-labs.github.io/proof-systems/specs/kimchi.html?highlight=gates#xor-1) + * + * @param a {@link Field} element to compare. + * @param b {@link Field} element to compare. + * @param length amount of bits to compare. + * + * @throws Throws an error if the input values exceed `2^paddedLength - 1`. + * + * @example + * ```ts + * let a = Field(0b0101); + * let b = Field(0b0011); + * + * let c = Gadgets.xor(a, b, 4); // xor-ing 4 bits + * c.assertEquals(0b0110); + * ``` + */ + xor(a: Field, b: Field, length: number) { + return xor(a, b, length); + }, + + /** + * Bitwise NOT gate on {@link Field} elements. Similar to the [bitwise + * NOT `~` operator in JavaScript](https://developer.mozilla.org/en-US/docs/ + * Web/JavaScript/Reference/Operators/Bitwise_NOT). + * + * **Note:** The NOT gate only operates over the amount + * of bits specified by the `length` parameter. + * + * A NOT gate works by returning `1` in each bit position if the + * corresponding bit of the operand is `0`, and returning `0` if the + * corresponding bit of the operand is `1`. + * + * The `length` parameter lets you define how many bits to NOT. + * + * **Note:** Specifying a larger `length` parameter adds additional constraints. The operation will fail if the length or the input value is larger than 254. + * + * NOT is implemented in two different ways. If the `checked` parameter is set to `true` + * the {@link Gadgets.xor} gadget is reused with a second argument to be an + * all one bitmask the same length. This approach needs as many rows as an XOR would need + * for a single negation. If the `checked` parameter is set to `false`, NOT is + * implemented as a subtraction of the input from the all one bitmask. This + * implementation is returned by default if no `checked` parameter is provided. + * + * You can find more details about the implementation in the [Mina book](https://o1-labs.github.io/proof-systems/specs/kimchi.html?highlight=gates#not) + * + * @example + * ```ts + * // not-ing 4 bits with the unchecked version + * let a = Field(0b0101); + * let b = Gadgets.not(a,4,false); + * + * b.assertEquals(0b1010); + * + * // not-ing 4 bits with the checked version utilizing the xor gadget + * let a = Field(0b0101); + * let b = Gadgets.not(a,4,true); + * + * b.assertEquals(0b1010); + * ``` + * + * @param a - The value to apply NOT to. The operation will fail if the value is larger than 254. + * @param length - The number of bits to be considered for the NOT operation. + * @param checked - Optional boolean to determine if the checked or unchecked not implementation is used. If it + * is set to `true` the {@link Gadgets.xor} gadget is reused. If it is set to `false`, NOT is implemented + * as a subtraction of the input from the all one bitmask. It is set to `false` by default if no parameter is provided. + * + * @throws Throws an error if the input value exceeds 254 bits. + */ + not(a: Field, length: number, checked: boolean = false) { + return not(a, length, checked); + }, + + /** + * Performs a left shift operation on the provided {@link Field} element. + * This operation is similar to the `<<` shift operation in JavaScript, + * where bits are shifted to the left, and the overflowing bits are discarded. + * + * It’s important to note that these operations are performed considering the big-endian 64-bit representation of the number, + * where the most significant (64th) bit is on the left end and the least significant bit is on the right end. + * + * **Important:** The gadgets assumes that its input is at most 64 bits in size. + * + * If the input exceeds 64 bits, the gadget is invalid and fails to prove correct execution of the shift. + * Therefore, to safely use `leftShift()`, you need to make sure that the values passed in are range checked to 64 bits. + * For example, this can be done with {@link Gadgets.rangeCheck64}. + * + * @param field {@link Field} element to shift. + * @param bits Amount of bits to shift the {@link Field} element to the left. The amount should be between 0 and 64 (or else the shift will fail). + * + * @throws Throws an error if the input value exceeds 64 bits. + * + * @example + * ```ts + * const x = Provable.witness(Field, () => Field(0b001100)); // 12 in binary + * const y = Gadgets.leftShift64(x, 2); // left shift by 2 bits + * y.assertEquals(0b110000); // 48 in binary + * + * const xLarge = Provable.witness(Field, () => Field(12345678901234567890123456789012345678n)); + * leftShift64(xLarge, 32); // throws an error since input exceeds 64 bits + * ``` + */ + leftShift64(field: Field, bits: number) { + return leftShift64(field, bits); + }, + + /** + * Performs a left shift operation on the provided {@link Field} element. + * This operation is similar to the `<<` shift operation in JavaScript, + * where bits are shifted to the left, and the overflowing bits are discarded. + * + * It’s important to note that these operations are performed considering the big-endian 32-bit representation of the number, + * where the most significant (32th) bit is on the left end and the least significant bit is on the right end. + * + * **Important:** The gadgets assumes that its input is at most 32 bits in size. + * + * The output is range checked to 32 bits. + * + * @param field {@link Field} element to shift. + * @param bits Amount of bits to shift the {@link Field} element to the left. The amount should be between 0 and 32 (or else the shift will fail). + * + * @example + * ```ts + * const x = Provable.witness(Field, () => Field(0b001100)); // 12 in binary + * const y = Gadgets.leftShift32(x, 2); // left shift by 2 bits + * y.assertEquals(0b110000); // 48 in binary + * ``` + */ + leftShift32(field: Field, bits: number) { + return leftShift32(field, bits); + }, + /** + * Performs a right shift operation on the provided {@link Field} element. + * This is similar to the `>>` shift operation in JavaScript, where bits are moved to the right. + * The `rightShift64` function utilizes the rotation method internally to implement this operation. + * + * * It’s important to note that these operations are performed considering the big-endian 64-bit representation of the number, + * where the most significant (64th) bit is on the left end and the least significant bit is on the right end. + * + * **Important:** The gadgets assumes that its input is at most 64 bits in size. + * + * If the input exceeds 64 bits, the gadget is invalid and fails to prove correct execution of the shift. + * To safely use `rightShift64()`, you need to make sure that the value passed in is range-checked to 64 bits; + * for example, using {@link Gadgets.rangeCheck64}. + * + * @param field {@link Field} element to shift. + * @param bits Amount of bits to shift the {@link Field} element to the right. The amount should be between 0 and 64 (or else the shift will fail). + * + * @throws Throws an error if the input value exceeds 64 bits. + * + * @example + * ```ts + * const x = Provable.witness(Field, () => Field(0b001100)); // 12 in binary + * const y = Gadgets.rightShift64(x, 2); // right shift by 2 bits + * y.assertEquals(0b000011); // 3 in binary + * + * const xLarge = Provable.witness(Field, () => Field(12345678901234567890123456789012345678n)); + * rightShift64(xLarge, 32); // throws an error since input exceeds 64 bits + * ``` + */ + rightShift64(field: Field, bits: number) { + return rightShift64(field, bits); + }, + /** + * Bitwise AND gadget on {@link Field} elements. Equivalent to the [bitwise AND `&` operator in JavaScript](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Bitwise_AND). + * The AND gate works by comparing two bits and returning `1` if both bits are `1`, and `0` otherwise. + * + * It can be checked by a double generic gate that verifies the following relationship between the values below (in the process it also invokes the {@link Gadgets.xor} gadget which will create additional constraints depending on `length`). + * + * The generic gate verifies:\ + * `a + b = sum` and the conjunction equation `2 * and = sum - xor`\ + * Where:\ + * `a + b = sum`\ + * `a ^ b = xor`\ + * `a & b = and` + * + * You can find more details about the implementation in the [Mina book](https://o1-labs.github.io/proof-systems/specs/kimchi.html?highlight=gates#and) + * + * The `length` parameter lets you define how many bits should be compared. `length` is rounded to the nearest multiple of 16, `paddedLength = ceil(length / 16) * 16`, and both input values are constrained to fit into `paddedLength` bits. The output is guaranteed to have at most `paddedLength` bits as well. + * + * **Note:** Specifying a larger `length` parameter adds additional constraints. + * + * **Note:** Both {@link Field} elements need to fit into `2^paddedLength - 1`. Otherwise, an error is thrown and no proof can be generated. + * For example, with `length = 2` (`paddedLength = 16`), `and()` will fail for any input that is larger than `2**16`. + * + * @example + * ```typescript + * let a = Field(3); // ... 000011 + * let b = Field(5); // ... 000101 + * + * let c = Gadgets.and(a, b, 2); // ... 000001 + * c.assertEquals(1); + * ``` + */ + and(a: Field, b: Field, length: number) { + return and(a, b, length); + }, + + /** + * Multi-range check. + * + * Proves that x, y, z are all in the range [0, 2^88). + * + * This takes 4 rows, so it checks 88*3/4 = 66 bits per row. This is slightly more efficient + * than 64-bit range checks, which can do 64 bits in 1 row. + * + * In particular, the 3x88-bit range check supports bigints up to 264 bits, which in turn is enough + * to support foreign field multiplication with moduli up to 2^259. + * + * @example + * ```ts + * Gadgets.multiRangeCheck([x, y, z]); + * ``` + * + * @throws Throws an error if one of the input values exceeds 88 bits. + */ + multiRangeCheck(limbs: Field3) { + multiRangeCheck(limbs); + }, + + /** + * Compact multi-range check + * + * This is a variant of {@link multiRangeCheck} where the first two variables are passed in + * combined form xy = x + 2^88*y. + * + * The gadget + * - splits up xy into x and y + * - proves that xy = x + 2^88*y + * - proves that x, y, z are all in the range [0, 2^88). + * + * The split form [x, y, z] is returned. + * + * @example + * ```ts + * let [x, y] = Gadgets.compactMultiRangeCheck([xy, z]); + * ``` + * + * @throws Throws an error if `xy` exceeds 2*88 = 176 bits, or if z exceeds 88 bits. + */ + compactMultiRangeCheck(xy: Field, z: Field) { + return compactMultiRangeCheck(xy, z); + }, + + /** + * Gadgets for foreign field operations. + * + * A _foreign field_ is a finite field different from the native field of the proof system. + * + * The `ForeignField` namespace exposes operations like modular addition and multiplication, + * which work for any finite field of size less than 2^259. + * + * Foreign field elements are represented as 3 limbs of native field elements. + * Each limb holds 88 bits of the total, in little-endian order. + * + * All `ForeignField` gadgets expect that their input limbs are constrained to the range [0, 2^88). + * Range checks on outputs are added by the gadget itself. + */ + ForeignField: { + /** + * Foreign field addition: `x + y mod f` + * + * The modulus `f` does not need to be prime. + * + * Inputs and outputs are 3-tuples of native Fields. + * Each input limb is assumed to be in the range [0, 2^88), and the gadget is invalid if this is not the case. + * The result limbs are guaranteed to be in the same range. + * + * @example + * ```ts + * let x = Provable.witness(Field3.provable, () => Field3.from(9n)); + * let y = Provable.witness(Field3.provable, () => Field3.from(10n)); + * + * // range check x and y + * Gadgets.multiRangeCheck(x); + * Gadgets.multiRangeCheck(y); + * + * // compute x + y mod 17 + * let z = ForeignField.add(x, y, 17n); + * + * Provable.log(z); // ['2', '0', '0'] = limb representation of 2 = 9 + 10 mod 17 + * ``` + * + * **Warning**: The gadget does not assume that inputs are reduced modulo f, + * and does not prove that the result is reduced modulo f. + * It only guarantees that the result is in the correct residue class. + * + * @param x left summand + * @param y right summand + * @param f modulus + * @returns x + y mod f + */ + add(x: Field3, y: Field3, f: bigint) { + return ForeignField.add(x, y, f); + }, + + /** + * Foreign field subtraction: `x - y mod f` + * + * See {@link Gadgets.ForeignField.add} for assumptions and usage examples. + * + * @throws fails if `x - y < -f`, where the result cannot be brought back to a positive number by adding `f` once. + */ + sub(x: Field3, y: Field3, f: bigint) { + return ForeignField.sub(x, y, f); + }, + + /** + * Foreign field negation: `-x mod f = f - x` + * + * See {@link ForeignField.add} for assumptions and usage examples. + * + * @throws fails if `x > f`, where `f - x < 0`. + */ + neg(x: Field3, f: bigint) { + return ForeignField.negate(x, f); + }, + + /** + * Foreign field sum: `xs[0] + signs[0] * xs[1] + ... + signs[n-1] * xs[n] mod f` + * + * This gadget takes a list of inputs and a list of signs (of size one less than the inputs), + * and computes a chain of additions or subtractions, depending on the sign. + * A sign is of type `1n | -1n`, where `1n` represents addition and `-1n` represents subtraction. + * + * **Note**: For 3 or more inputs, `sum()` uses fewer constraints than a sequence of `add()` and `sub()` calls, + * because we can avoid range checks on intermediate results. + * + * See {@link Gadgets.ForeignField.add} for assumptions on inputs. + * + * @example + * ```ts + * let x = Provable.witness(Field3.provable, () => Field3.from(4n)); + * let y = Provable.witness(Field3.provable, () => Field3.from(5n)); + * let z = Provable.witness(Field3.provable, () => Field3.from(10n)); + * + * // range check x, y, z + * Gadgets.multiRangeCheck(x); + * Gadgets.multiRangeCheck(y); + * Gadgets.multiRangeCheck(z); + * + * // compute x + y - z mod 17 + * let sum = ForeignField.sum([x, y, z], [1n, -1n], 17n); + * + * Provable.log(sum); // ['16', '0', '0'] = limb representation of 16 = 4 + 5 - 10 mod 17 + * ``` + */ + sum(xs: Field3[], signs: (1n | -1n)[], f: bigint) { + return ForeignField.sum(xs, signs, f); + }, + + /** + * Foreign field multiplication: `x * y mod f` + * + * The modulus `f` does not need to be prime, but has to be smaller than 2^259. + * + * **Assumptions**: In addition to the assumption that input limbs are in the range [0, 2^88), as in all foreign field gadgets, + * this assumes an additional bound on the inputs: `x * y < 2^264 * p`, where p is the native modulus. + * We usually assert this bound by proving that `x[2] < f[2] + 1`, where `x[2]` is the most significant limb of x. + * To do this, we use an 88-bit range check on `2^88 - x[2] - (f[2] + 1)`, and same for y. + * The implication is that x and y are _almost_ reduced modulo f. + * + * All of the above assumptions are checked by {@link Gadgets.ForeignField.assertAlmostReduced}. + * + * **Warning**: This gadget does not add the extra bound check on the result. + * So, to use the result in another foreign field multiplication, you have to add the bound check on it yourself, again. + * + * @example + * ```ts + * // example modulus: secp256k1 prime + * let f = (1n << 256n) - (1n << 32n) - 0b1111010001n; + * + * let x = Provable.witness(Field3.provable, () => Field3.from(f - 1n)); + * let y = Provable.witness(Field3.provable, () => Field3.from(f - 2n)); + * + * // range check x, y and prove additional bounds x[2] <= f[2] + * ForeignField.assertAlmostReduced([x, y], f); + * + * // compute x * y mod f + * let z = ForeignField.mul(x, y, f); + * + * Provable.log(z); // ['2', '0', '0'] = limb representation of 2 = (-1)*(-2) mod f + * ``` + */ + mul(x: Field3, y: Field3, f: bigint) { + return ForeignField.mul(x, y, f); + }, + + /** + * Foreign field inverse: `x^(-1) mod f` + * + * See {@link Gadgets.ForeignField.mul} for assumptions on inputs and usage examples. + * + * This gadget adds an extra bound check on the result, so it can be used directly in another foreign field multiplication. + */ + inv(x: Field3, f: bigint) { + return ForeignField.inv(x, f); + }, + + /** + * Foreign field division: `x * y^(-1) mod f` + * + * See {@link Gadgets.ForeignField.mul} for assumptions on inputs and usage examples. + * + * This gadget adds an extra bound check on the result, so it can be used directly in another foreign field multiplication. + * + * @throws Different than {@link Gadgets.ForeignField.mul}, this fails on unreduced input `x`, because it checks that `x === (x/y)*y` and the right side will be reduced. + */ + div(x: Field3, y: Field3, f: bigint) { + return ForeignField.div(x, y, f); + }, + + /** + * Optimized multiplication of sums in a foreign field, for example: `(x - y)*z = a + b + c mod f` + * + * Note: This is much more efficient than using {@link Gadgets.ForeignField.add} and {@link Gadgets.ForeignField.sub} separately to + * compute the multiplication inputs and outputs, and then using {@link Gadgets.ForeignField.mul} to constrain the result. + * + * The sums passed into this method are "lazy sums" created with {@link Gadgets.ForeignField.Sum}. + * You can also pass in plain {@link Field3} elements. + * + * **Assumptions**: The assumptions on the _summands_ are analogous to the assumptions described in {@link Gadgets.ForeignField.mul}: + * - each summand's limbs are in the range [0, 2^88) + * - summands that are part of a multiplication input satisfy `x[2] <= f[2]` + * + * @throws if the modulus is so large that the second assumption no longer suffices for validity of the multiplication. + * For small sums and moduli < 2^256, this will not fail. + * + * @throws if the provided multiplication result is not correct modulo f. + * + * @example + * ```ts + * // range-check x, y, z, a, b, c + * ForeignField.assertAlmostReduced([x, y, z], f); + * Gadgets.multiRangeCheck(a); + * Gadgets.multiRangeCheck(b); + * Gadgets.multiRangeCheck(c); + * + * // create lazy input sums + * let xMinusY = ForeignField.Sum(x).sub(y); + * let aPlusBPlusC = ForeignField.Sum(a).add(b).add(c); + * + * // assert that (x - y)*z = a + b + c mod f + * ForeignField.assertMul(xMinusY, z, aPlusBPlusC, f); + * ``` + */ + assertMul(x: Field3 | Sum, y: Field3 | Sum, z: Field3 | Sum, f: bigint) { + return ForeignField.assertMul(x, y, z, f); + }, + + /** + * Lazy sum of {@link Field3} elements, which can be used as input to {@link Gadgets.ForeignField.assertMul}. + */ + Sum(x: Field3) { + return ForeignField.Sum(x); + }, + + /** + * Prove that each of the given {@link Field3} elements is "almost" reduced modulo f, + * i.e., satisfies the assumptions required by {@link Gadgets.ForeignField.mul} and other gadgets: + * - each limb is in the range [0, 2^88) + * - the most significant limb is less or equal than the modulus, x[2] <= f[2] + * + * **Note**: This method is most efficient when the number of input elements is a multiple of 3. + * + * @throws if any of the assumptions is violated. + * + * @example + * ```ts + * let x = Provable.witness(Field3.provable, () => Field3.from(4n)); + * let y = Provable.witness(Field3.provable, () => Field3.from(5n)); + * let z = Provable.witness(Field3.provable, () => Field3.from(10n)); + * + * ForeignField.assertAlmostReduced([x, y, z], f); + * + * // now we can use x, y, z as inputs to foreign field multiplication + * let xy = ForeignField.mul(x, y, f); + * let xyz = ForeignField.mul(xy, z, f); + * + * // since xy is an input to another multiplication, we need to prove that it is almost reduced again! + * ForeignField.assertAlmostReduced([xy], f); // TODO: would be more efficient to batch this with 2 other elements + * ``` + */ + assertAlmostReduced(xs: Field3[], f: bigint, { skipMrc = false } = {}) { + ForeignField.assertAlmostReduced(xs, f, skipMrc); + }, + + /** + * Prove that x < f for any constant f < 2^264. + * + * If f is a finite field modulus, this means that the given field element is fully reduced modulo f. + * This is a stronger statement than {@link ForeignField.assertAlmostReduced} + * and also uses more constraints; it should not be needed in most use cases. + * + * **Note**: This assumes that the limbs of x are in the range [0, 2^88), in contrast to + * {@link ForeignField.assertAlmostReduced} which adds that check itself. + * + * @throws if x is greater or equal to f. + * + * @example + * ```ts + * let x = Provable.witness(Field3.provable, () => Field3.from(0x1235n)); + * + * // range check limbs of x + * Gadgets.multiRangeCheck(x); + * + * // prove that x is fully reduced mod f + * Gadgets.ForeignField.assertLessThan(x, f); + * ``` + */ + assertLessThan(x: Field3, f: bigint) { + ForeignField.assertLessThan(x, f); + }, + }, + + /** + * Helper methods to interact with 3-limb vectors of Fields. + * + * **Note:** This interface does not contain any provable methods. + */ + Field3, + /** + * Division modulo 2^32. The operation decomposes a {@link Field} element in the range [0, 2^64) into two 32-bit limbs, `remainder` and `quotient`, using the following equation: `n = quotient * 2^32 + remainder`. + * + * **Note:** The gadget acts as a proof that the input is in the range [0, 2^64). If the input exceeds 64 bits, the gadget fails. + * + * Asserts that both `remainder` and `quotient` are in the range [0, 2^32) using {@link Gadgets.rangeCheck32}. + * + * @example + * ```ts + * let n = Field((1n << 32n) + 8n) + * let { remainder, quotient } = Gadgets.divMod32(n); + * // remainder = 8, quotient = 1 + * + * n.assertEquals(quotient.mul(1n << 32n).add(remainder)); + * ``` + */ + divMod32, + + /** + * Addition modulo 2^32. The operation adds two {@link Field} elements in the range [0, 2^64] and returns the result modulo 2^32. + * + * Asserts that the result is in the range [0, 2^32) using {@link Gadgets.rangeCheck32}. + * + * It uses {@link Gadgets.divMod32} internally by adding the two {@link Field} elements and then decomposing the result into `remainder` and `quotient` and returning the `remainder`. + * + * **Note:** The gadget assumes both inputs to be in the range [0, 2^64). When called with non-range-checked inputs, be aware that the sum `a + b` can overflow the native field and the gadget can succeed but return an invalid result. + * + * @example + * ```ts + * let a = Field(8n); + * let b = Field(1n << 32n); + * + * Gadgets.addMod32(a, b).assertEquals(Field(8n)); + * ``` + * */ + addMod32, +}; + +export namespace Gadgets { + /** + * A 3-tuple of Fields, representing a 3-limb bigint. + */ + export type Field3 = [Field, Field, Field]; + + export namespace ForeignField { + /** + * Lazy sum of {@link Field3} elements, which can be used as input to {@link Gadgets.ForeignField.assertMul}. + */ + export type Sum = Sum_; + } +} +type Sum_ = Sum; diff --git a/src/lib/gadgets/range-check.ts b/src/lib/gadgets/range-check.ts new file mode 100644 index 0000000000..1d33dae0f8 --- /dev/null +++ b/src/lib/gadgets/range-check.ts @@ -0,0 +1,331 @@ +import { Snarky } from '../../snarky.js'; +import { Fp } from '../../bindings/crypto/finite_field.js'; +import { Field as FieldProvable } from '../../provable/field-bigint.js'; +import { Field } from '../field.js'; +import { Gates } from '../gates.js'; +import { assert, bitSlice, exists, toVar, toVars } from './common.js'; +import { Bool } from '../bool.js'; + +export { + rangeCheck64, + rangeCheck32, + multiRangeCheck, + compactMultiRangeCheck, + rangeCheckHelper, + rangeCheckN, + isInRangeN, + rangeCheck8, + rangeCheck16, +}; +export { l, l2, l3, lMask, l2Mask }; + +/** + * Asserts that x is in the range [0, 2^32) + */ +function rangeCheck32(x: Field) { + if (x.isConstant()) { + if (x.toBigInt() >= 1n << 32n) { + throw Error(`rangeCheck32: expected field to fit in 32 bits, got ${x}`); + } + return; + } + + let actual = rangeCheckHelper(32, x); + actual.assertEquals(x); +} + +/** + * Asserts that x is in the range [0, 2^64) + */ +function rangeCheck64(x: Field) { + if (x.isConstant()) { + if (x.toBigInt() >= 1n << 64n) { + throw Error(`rangeCheck64: expected field to fit in 64 bits, got ${x}`); + } + return; + } + + // crumbs (2-bit limbs) + let [x0, x2, x4, x6, x8, x10, x12, x14] = exists(8, () => { + let xx = x.toBigInt(); + return [ + bitSlice(xx, 0, 2), + bitSlice(xx, 2, 2), + bitSlice(xx, 4, 2), + bitSlice(xx, 6, 2), + bitSlice(xx, 8, 2), + bitSlice(xx, 10, 2), + bitSlice(xx, 12, 2), + bitSlice(xx, 14, 2), + ]; + }); + + // 12-bit limbs + let [x16, x28, x40, x52] = exists(4, () => { + let xx = x.toBigInt(); + return [ + bitSlice(xx, 16, 12), + bitSlice(xx, 28, 12), + bitSlice(xx, 40, 12), + bitSlice(xx, 52, 12), + ]; + }); + + Gates.rangeCheck0( + x, + [new Field(0), new Field(0), x52, x40, x28, x16], + [x14, x12, x10, x8, x6, x4, x2, x0], + false // not using compact mode + ); +} + +// default bigint limb size +const l = 88n; +const l2 = 2n * l; +const l3 = 3n * l; +const lMask = (1n << l) - 1n; +const l2Mask = (1n << l2) - 1n; + +/** + * Asserts that x, y, z \in [0, 2^88) + */ +function multiRangeCheck([x, y, z]: [Field, Field, Field]) { + if (x.isConstant() && y.isConstant() && z.isConstant()) { + if (x.toBigInt() >> l || y.toBigInt() >> l || z.toBigInt() >> l) { + throw Error(`Expected fields to fit in ${l} bits, got ${x}, ${y}, ${z}`); + } + return; + } + // ensure we are using pure variables + [x, y, z] = toVars([x, y, z]); + let zero = toVar(0n); + + let [x64, x76] = rangeCheck0Helper(x); + let [y64, y76] = rangeCheck0Helper(y); + rangeCheck1Helper({ x64, x76, y64, y76, z, yz: zero }); +} + +/** + * Compact multi-range-check - checks + * - xy = x + 2^88*y + * - x, y, z \in [0, 2^88) + * + * Returns the full limbs x, y, z + */ +function compactMultiRangeCheck(xy: Field, z: Field): [Field, Field, Field] { + // constant case + if (xy.isConstant() && z.isConstant()) { + if (xy.toBigInt() >> l2 || z.toBigInt() >> l) { + throw Error( + `Expected fields to fit in ${l2} and ${l} bits respectively, got ${xy}, ${z}` + ); + } + let [x, y] = splitCompactLimb(xy.toBigInt()); + return [new Field(x), new Field(y), z]; + } + // ensure we are using pure variables + [xy, z] = toVars([xy, z]); + + let [x, y] = exists(2, () => splitCompactLimb(xy.toBigInt())); + + let [z64, z76] = rangeCheck0Helper(z, false); + let [x64, x76] = rangeCheck0Helper(x, true); + rangeCheck1Helper({ x64: z64, x76: z76, y64: x64, y76: x76, z: y, yz: xy }); + + return [x, y, z]; +} + +function splitCompactLimb(x01: bigint): [bigint, bigint] { + return [x01 & lMask, x01 >> l]; +} + +function rangeCheck0Helper(x: Field, isCompact = false): [Field, Field] { + // crumbs (2-bit limbs) + let [x0, x2, x4, x6, x8, x10, x12, x14] = exists(8, () => { + let xx = x.toBigInt(); + return [ + bitSlice(xx, 0, 2), + bitSlice(xx, 2, 2), + bitSlice(xx, 4, 2), + bitSlice(xx, 6, 2), + bitSlice(xx, 8, 2), + bitSlice(xx, 10, 2), + bitSlice(xx, 12, 2), + bitSlice(xx, 14, 2), + ]; + }); + + // 12-bit limbs + let [x16, x28, x40, x52, x64, x76] = exists(6, () => { + let xx = x.toBigInt(); + return [ + bitSlice(xx, 16, 12), + bitSlice(xx, 28, 12), + bitSlice(xx, 40, 12), + bitSlice(xx, 52, 12), + bitSlice(xx, 64, 12), + bitSlice(xx, 76, 12), + ]; + }); + + Gates.rangeCheck0( + x, + [x76, x64, x52, x40, x28, x16], + [x14, x12, x10, x8, x6, x4, x2, x0], + isCompact + ); + + // the two highest 12-bit limbs are returned because another gate + // is needed to add lookups for them + return [x64, x76]; +} + +function rangeCheck1Helper(inputs: { + x64: Field; + x76: Field; + y64: Field; + y76: Field; + z: Field; + yz: Field; +}) { + let { x64, x76, y64, y76, z, yz } = inputs; + + // create limbs for current row + let [z22, z24, z26, z28, z30, z32, z34, z36, z38, z50, z62, z74, z86] = + exists(13, () => { + let zz = z.toBigInt(); + return [ + bitSlice(zz, 22, 2), + bitSlice(zz, 24, 2), + bitSlice(zz, 26, 2), + bitSlice(zz, 28, 2), + bitSlice(zz, 30, 2), + bitSlice(zz, 32, 2), + bitSlice(zz, 34, 2), + bitSlice(zz, 36, 2), + bitSlice(zz, 38, 12), + bitSlice(zz, 50, 12), + bitSlice(zz, 62, 12), + bitSlice(zz, 74, 12), + bitSlice(zz, 86, 2), + ]; + }); + + // create limbs for next row + let [z0, z2, z4, z6, z8, z10, z12, z14, z16, z18, z20] = exists(11, () => { + let zz = z.toBigInt(); + return [ + bitSlice(zz, 0, 2), + bitSlice(zz, 2, 2), + bitSlice(zz, 4, 2), + bitSlice(zz, 6, 2), + bitSlice(zz, 8, 2), + bitSlice(zz, 10, 2), + bitSlice(zz, 12, 2), + bitSlice(zz, 14, 2), + bitSlice(zz, 16, 2), + bitSlice(zz, 18, 2), + bitSlice(zz, 20, 2), + ]; + }); + + Gates.rangeCheck1( + z, + yz, + [z86, z74, z62, z50, z38, z36, z34, z32, z30, z28, z26, z24, z22], + [z20, z18, z16, x76, x64, y76, y64, z14, z12, z10, z8, z6, z4, z2, z0] + ); +} + +/** + * Helper function that creates a new {@link Field} element from the first `length` bits of this {@link Field} element. + */ +function rangeCheckHelper(length: number, x: Field) { + assert( + length <= Fp.sizeInBits, + `bit length must be ${Fp.sizeInBits} or less, got ${length}` + ); + assert(length > 0, `bit length must be positive, got ${length}`); + assert(length % 16 === 0, '`length` has to be a multiple of 16.'); + + let lengthDiv16 = length / 16; + if (x.isConstant()) { + let bits = FieldProvable.toBits(x.toBigInt()) + .slice(0, length) + .concat(Array(Fp.sizeInBits - length).fill(false)); + return new Field(FieldProvable.fromBits(bits)); + } + let y = Snarky.field.truncateToBits16(lengthDiv16, x.value); + return new Field(y); +} + +/** + * Asserts that x is in the range [0, 2^n) + */ +function rangeCheckN(n: number, x: Field, message: string = '') { + assert( + n <= Fp.sizeInBits, + `bit length must be ${Fp.sizeInBits} or less, got ${n}` + ); + assert(n > 0, `bit length must be positive, got ${n}`); + assert(n % 16 === 0, '`length` has to be a multiple of 16.'); + + if (x.isConstant()) { + if (x.toBigInt() >= 1n << BigInt(n)) { + throw Error( + `rangeCheckN: expected field to fit in ${n} bits, got ${x}.\n${message}` + ); + } + return; + } + + let actual = rangeCheckHelper(n, x); + actual.assertEquals(x, message); +} + +/** + * Checks that x is in the range [0, 2^n) and returns a Boolean indicating whether the check passed. + */ +function isInRangeN(n: number, x: Field) { + assert( + n <= Fp.sizeInBits, + `bit length must be ${Fp.sizeInBits} or less, got ${n}` + ); + assert(n > 0, `bit length must be positive, got ${n}`); + assert(n % 16 === 0, '`length` has to be a multiple of 16.'); + + if (x.isConstant()) { + return new Bool(x.toBigInt() < 1n << BigInt(n)); + } + + let actual = rangeCheckHelper(n, x); + return actual.equals(x); +} + +function rangeCheck16(x: Field) { + if (x.isConstant()) { + assert( + x.toBigInt() < 1n << 16n, + `rangeCheck16: expected field to fit in 8 bits, got ${x}` + ); + return; + } + // check that x fits in 16 bits + x.rangeCheckHelper(16).assertEquals(x); +} + +function rangeCheck8(x: Field) { + if (x.isConstant()) { + assert( + x.toBigInt() < 1n << 8n, + `rangeCheck8: expected field to fit in 8 bits, got ${x}` + ); + return; + } + + // check that x fits in 16 bits + x.rangeCheckHelper(16).assertEquals(x); + // check that 2^8 x fits in 16 bits + let x256 = x.mul(1 << 8).seal(); + x256.rangeCheckHelper(16).assertEquals(x256); +} diff --git a/src/lib/gadgets/range-check.unit-test.ts b/src/lib/gadgets/range-check.unit-test.ts new file mode 100644 index 0000000000..1caea18f17 --- /dev/null +++ b/src/lib/gadgets/range-check.unit-test.ts @@ -0,0 +1,157 @@ +import { mod } from '../../bindings/crypto/finite_field.js'; +import { Field } from '../../lib/core.js'; +import { ZkProgram } from '../proof_system.js'; +import { + Spec, + boolean, + equivalentAsync, + fieldWithRng, +} from '../testing/equivalent.js'; +import { Random } from '../testing/property.js'; +import { assert } from './common.js'; +import { Gadgets } from './gadgets.js'; +import { l } from './range-check.js'; +import { + constraintSystem, + contains, + equals, + ifNotAllConstant, + withoutGenerics, +} from '../testing/constraint-system.js'; + +let uint = (n: number | bigint): Spec => { + let uint = Random.bignat((1n << BigInt(n)) - 1n); + return fieldWithRng(uint); +}; + +let maybeUint = (n: number | bigint): Spec => { + let uint = Random.bignat((1n << BigInt(n)) - 1n); + return fieldWithRng( + Random.map(Random.oneOf(uint, uint.invalid), (x) => mod(x, Field.ORDER)) + ); +}; + +// constraint system sanity check + +constraintSystem( + 'range check 64', + { from: [Field] }, + Gadgets.rangeCheck64, + ifNotAllConstant(withoutGenerics(equals(['RangeCheck0']))) +); + +constraintSystem( + 'range check 8', + { from: [Field] }, + Gadgets.rangeCheck8, + ifNotAllConstant(withoutGenerics(equals(['EndoMulScalar', 'EndoMulScalar']))) +); + +constraintSystem( + 'multi-range check', + { from: [Field, Field, Field] }, + (x, y, z) => Gadgets.multiRangeCheck([x, y, z]), + ifNotAllConstant( + contains(['RangeCheck0', 'RangeCheck0', 'RangeCheck1', 'Zero']) + ) +); + +constraintSystem( + 'compact multi-range check', + { from: [Field, Field] }, + Gadgets.compactMultiRangeCheck, + ifNotAllConstant( + contains(['RangeCheck0', 'RangeCheck0', 'RangeCheck1', 'Zero']) + ) +); + +// TODO: make a ZkFunction or something that doesn't go through Pickles +// -------------------------- +// RangeCheck64 Gate +// -------------------------- + +let RangeCheck = ZkProgram({ + name: 'range-check', + methods: { + check64: { + privateInputs: [Field], + method(x) { + Gadgets.rangeCheck64(x); + }, + }, + check8: { + privateInputs: [Field], + method(x) { + Gadgets.rangeCheck8(x); + }, + }, + checkMulti: { + privateInputs: [Field, Field, Field], + method(x, y, z) { + Gadgets.multiRangeCheck([x, y, z]); + }, + }, + checkCompact: { + privateInputs: [Field, Field], + method(xy, z) { + let [x, y] = Gadgets.compactMultiRangeCheck(xy, z); + x.add(y.mul(1n << l)).assertEquals(xy); + }, + }, + }, +}); + +await RangeCheck.compile(); + +// TODO: we use this as a test because there's no way to check custom gates quickly :( +const runs = 2; + +await equivalentAsync({ from: [maybeUint(64)], to: boolean }, { runs })( + (x) => { + assert(x < 1n << 64n); + return true; + }, + async (x) => { + let proof = await RangeCheck.check64(x); + return await RangeCheck.verify(proof); + } +); + +await equivalentAsync({ from: [maybeUint(8)], to: boolean }, { runs })( + (x) => { + assert(x < 1n << 8n); + return true; + }, + async (x) => { + let proof = await RangeCheck.check8(x); + return await RangeCheck.verify(proof); + } +); + +await equivalentAsync( + { from: [maybeUint(l), uint(l), uint(l)], to: boolean }, + { runs } +)( + (x, y, z) => { + assert(!(x >> l) && !(y >> l) && !(z >> l), 'multi: not out of range'); + return true; + }, + async (x, y, z) => { + let proof = await RangeCheck.checkMulti(x, y, z); + return await RangeCheck.verify(proof); + } +); + +await equivalentAsync( + { from: [maybeUint(2n * l), uint(l)], to: boolean }, + { runs } +)( + (xy, z) => { + assert(!(xy >> (2n * l)) && !(z >> l), 'compact: not out of range'); + return true; + }, + async (xy, z) => { + let proof = await RangeCheck.checkCompact(xy, z); + return await RangeCheck.verify(proof); + } +); diff --git a/src/lib/gadgets/test-utils.ts b/src/lib/gadgets/test-utils.ts new file mode 100644 index 0000000000..96c78866e3 --- /dev/null +++ b/src/lib/gadgets/test-utils.ts @@ -0,0 +1,73 @@ +import type { FiniteField } from '../../bindings/crypto/finite_field.js'; +import { ProvableSpec, spec } from '../testing/equivalent.js'; +import { Random } from '../testing/random.js'; +import { Gadgets } from './gadgets.js'; +import { assert } from './common.js'; +import { Bytes } from '../provable-types/provable-types.js'; + +export { + foreignField, + unreducedForeignField, + uniformForeignField, + bytes, + throwError, +}; + +const { Field3 } = Gadgets; + +// test input specs + +function foreignField(F: FiniteField): ProvableSpec { + return { + rng: Random.otherField(F), + there: Field3.from, + back: Field3.toBigint, + provable: Field3.provable, + }; +} + +// for testing with inputs > f +function unreducedForeignField( + maxBits: number, + F: FiniteField +): ProvableSpec { + return { + rng: Random.bignat(1n << BigInt(maxBits)), + there: Field3.from, + back: Field3.toBigint, + provable: Field3.provable, + assertEqual(x, y, message) { + // need weak equality here because, while ffadd works on bigints larger than the modulus, + // it can't fully reduce them + assert(F.equal(x, y), message); + }, + }; +} + +// for fields that must follow an unbiased distribution, like private keys +function uniformForeignField( + F: FiniteField +): ProvableSpec { + return { + rng: Random(F.random), + there: Field3.from, + back: Field3.toBigint, + provable: Field3.provable, + }; +} + +function bytes(length: number) { + const Bytes_ = Bytes(length); + return spec({ + rng: Random.map(Random.bytes(length), (x) => Uint8Array.from(x)), + there: Bytes_.from, + back: (x) => x.toBytes(), + provable: Bytes_.provable, + }); +} + +// helper + +function throwError(message: string): T { + throw Error(message); +} diff --git a/src/lib/gates.ts b/src/lib/gates.ts new file mode 100644 index 0000000000..a8900cfe49 --- /dev/null +++ b/src/lib/gates.ts @@ -0,0 +1,267 @@ +import { Snarky } from '../snarky.js'; +import { FieldConst, type Field } from './field.js'; +import { exists } from './gadgets/common.js'; +import { MlArray, MlTuple } from './ml/base.js'; +import { TupleN } from './util/types.js'; + +export { + Gates, + rangeCheck0, + rangeCheck1, + xor, + zero, + rotate, + generic, + foreignFieldAdd, + foreignFieldMul, + KimchiGateType, +}; + +const Gates = { + rangeCheck0, + rangeCheck1, + xor, + zero, + rotate, + generic, + foreignFieldAdd, + foreignFieldMul, + raw, +}; + +function rangeCheck0( + x: Field, + xLimbs12: TupleN, + xLimbs2: TupleN, + isCompact: boolean +) { + Snarky.gates.rangeCheck0( + x.value, + MlTuple.mapTo(xLimbs12, (x) => x.value), + MlTuple.mapTo(xLimbs2, (x) => x.value), + isCompact ? FieldConst[1] : FieldConst[0] + ); +} + +/** + * the rangeCheck1 gate is used in combination with the rangeCheck0, + * for doing a 3x88-bit range check + */ +function rangeCheck1( + v2: Field, + v12: Field, + vCurr: TupleN, + vNext: TupleN +) { + Snarky.gates.rangeCheck1( + v2.value, + v12.value, + MlTuple.mapTo(vCurr, (x) => x.value), + MlTuple.mapTo(vNext, (x) => x.value) + ); +} + +function rotate( + field: Field, + rotated: Field, + excess: Field, + limbs: [Field, Field, Field, Field], + crumbs: [Field, Field, Field, Field, Field, Field, Field, Field], + two_to_rot: bigint +) { + Snarky.gates.rotate( + field.value, + rotated.value, + excess.value, + MlArray.to(limbs.map((x) => x.value)), + MlArray.to(crumbs.map((x) => x.value)), + FieldConst.fromBigint(two_to_rot) + ); +} + +/** + * Asserts that 16 bit limbs of input two elements are the correct XOR output + */ +function xor( + input1: Field, + input2: Field, + outputXor: Field, + in1_0: Field, + in1_1: Field, + in1_2: Field, + in1_3: Field, + in2_0: Field, + in2_1: Field, + in2_2: Field, + in2_3: Field, + out0: Field, + out1: Field, + out2: Field, + out3: Field +) { + Snarky.gates.xor( + input1.value, + input2.value, + outputXor.value, + in1_0.value, + in1_1.value, + in1_2.value, + in1_3.value, + in2_0.value, + in2_1.value, + in2_2.value, + in2_3.value, + out0.value, + out1.value, + out2.value, + out3.value + ); +} + +/** + * [Generic gate](https://o1-labs.github.io/proof-systems/specs/kimchi.html?highlight=foreignfield#double-generic-gate) + * The vanilla PLONK gate that allows us to do operations like: + * * addition of two registers (into an output register) + * * multiplication of two registers + * * equality of a register with a constant + * + * More generally, the generic gate controls the coefficients (denoted `c_`) in the equation: + * + * `c_l*l + c_r*r + c_o*o + c_m*l*r + c_c === 0` + */ +function generic( + coefficients: { + left: bigint; + right: bigint; + out: bigint; + mul: bigint; + const: bigint; + }, + inputs: { left: Field; right: Field; out: Field } +) { + Snarky.gates.generic( + FieldConst.fromBigint(coefficients.left), + inputs.left.value, + FieldConst.fromBigint(coefficients.right), + inputs.right.value, + FieldConst.fromBigint(coefficients.out), + inputs.out.value, + FieldConst.fromBigint(coefficients.mul), + FieldConst.fromBigint(coefficients.const) + ); +} + +function zero(a: Field, b: Field, c: Field) { + raw(KimchiGateType.Zero, [a, b, c], []); +} + +/** + * bigint addition which allows for field overflow and carry + * + * - `l01 + sign*r01 - overflow*f01 - carry*2^2l === r01` + * - `l2 + sign*r2 - overflow*f2 + carry === r2` + * - overflow is 0 or sign + * - carry is 0, 1 or -1 + * + * assumes that the result is placed in the first 3 cells of the next row! + */ +function foreignFieldAdd({ + left, + right, + overflow, + carry, + modulus, + sign, +}: { + left: TupleN; + right: TupleN; + overflow: Field; + carry: Field; + modulus: TupleN; + sign: 1n | -1n; +}) { + Snarky.gates.foreignFieldAdd( + MlTuple.mapTo(left, (x) => x.value), + MlTuple.mapTo(right, (x) => x.value), + overflow.value, + carry.value, + MlTuple.mapTo(modulus, FieldConst.fromBigint), + FieldConst.fromBigint(sign) + ); +} + +/** + * Foreign field multiplication + */ +function foreignFieldMul(inputs: { + left: TupleN; + right: TupleN; + remainder: TupleN; + quotient: TupleN; + quotientHiBound: Field; + product1: TupleN; + carry0: Field; + carry1p: TupleN; + carry1c: TupleN; + foreignFieldModulus2: bigint; + negForeignFieldModulus: TupleN; +}) { + let { + left, + right, + remainder, + quotient, + quotientHiBound, + product1, + carry0, + carry1p, + carry1c, + foreignFieldModulus2, + negForeignFieldModulus, + } = inputs; + + Snarky.gates.foreignFieldMul( + MlTuple.mapTo(left, (x) => x.value), + MlTuple.mapTo(right, (x) => x.value), + MlTuple.mapTo(remainder, (x) => x.value), + MlTuple.mapTo(quotient, (x) => x.value), + quotientHiBound.value, + MlTuple.mapTo(product1, (x) => x.value), + carry0.value, + MlTuple.mapTo(carry1p, (x) => x.value), + MlTuple.mapTo(carry1c, (x) => x.value), + FieldConst.fromBigint(foreignFieldModulus2), + MlTuple.mapTo(negForeignFieldModulus, FieldConst.fromBigint) + ); +} + +function raw(kind: KimchiGateType, values: Field[], coefficients: bigint[]) { + let n = values.length; + let padding = exists(15 - n, () => Array(15 - n).fill(0n)); + Snarky.gates.raw( + kind, + MlArray.to(values.concat(padding).map((x) => x.value)), + MlArray.to(coefficients.map(FieldConst.fromBigint)) + ); +} + +enum KimchiGateType { + Zero, + Generic, + Poseidon, + CompleteAdd, + VarBaseMul, + EndoMul, + EndoMulScalar, + Lookup, + CairoClaim, + CairoInstruction, + CairoFlags, + CairoTransition, + RangeCheck0, + RangeCheck1, + ForeignFieldAdd, + ForeignFieldMul, + Xor16, + Rot64, +} diff --git a/src/lib/group.ts b/src/lib/group.ts index 71f95fdae8..67b021097e 100644 --- a/src/lib/group.ts +++ b/src/lib/group.ts @@ -1,8 +1,8 @@ -import { Field, FieldVar, isField } from './field.js'; +import { Field, FieldVar } from './field.js'; import { Scalar } from './scalar.js'; import { Snarky } from '../snarky.js'; import { Field as Fp } from '../provable/field-bigint.js'; -import { Pallas } from '../bindings/crypto/elliptic_curve.js'; +import { GroupAffine, Pallas } from '../bindings/crypto/elliptic_curve.js'; import { Provable } from './provable.js'; import { Bool } from './bool.js'; @@ -35,10 +35,7 @@ class Group { * ``` */ static get zero() { - return new Group({ - x: 0, - y: 0, - }); + return new Group({ x: 0, y: 0 }); } /** @@ -51,10 +48,10 @@ class Group { x: FieldVar | Field | number | string | bigint; y: FieldVar | Field | number | string | bigint; }) { - this.x = isField(x) ? x : new Field(x); - this.y = isField(y) ? y : new Field(y); + this.x = x instanceof Field ? x : new Field(x); + this.y = y instanceof Field ? y : new Field(y); - if (this.#isConstant()) { + if (isConstant(this)) { // we also check the zero element (0, 0) here if (this.x.equals(0).and(this.y.equals(0)).toBoolean()) return; @@ -75,39 +72,6 @@ class Group { } } - // helpers - static #fromAffine({ - x, - y, - infinity, - }: { - x: bigint; - y: bigint; - infinity: boolean; - }) { - return infinity ? Group.zero : new Group({ x, y }); - } - - static #fromProjective({ x, y, z }: { x: bigint; y: bigint; z: bigint }) { - return this.#fromAffine(Pallas.toAffine({ x, y, z })); - } - - #toTuple(): [0, FieldVar, FieldVar] { - return [0, this.x.value, this.y.value]; - } - - #isConstant() { - return this.x.isConstant() && this.y.isConstant(); - } - - #toProjective() { - return Pallas.fromAffine({ - x: this.x.toBigInt(), - y: this.y.toBigInt(), - infinity: false, - }); - } - /** * Checks if this element is the `zero` element `{x: 0, y: 0}`. */ @@ -125,15 +89,15 @@ class Group { * ``` */ add(g: Group) { - if (this.#isConstant() && g.#isConstant()) { + if (isConstant(this) && isConstant(g)) { // we check if either operand is zero, because adding zero to g just results in g (and vise versa) if (this.isZero().toBoolean()) { return g; } else if (g.isZero().toBoolean()) { return this; } else { - let g_proj = Pallas.add(this.#toProjective(), g.#toProjective()); - return Group.#fromProjective(g_proj); + let g_proj = Pallas.add(toProjective(this), toProjective(g)); + return fromProjective(g_proj); } } else { const { x: x1, y: y1 } = this; @@ -173,10 +137,10 @@ class Group { return s.mul(x1.sub(x3)).sub(y1); }); - let [, x, y] = Snarky.group.ecadd( - Group.from(x1.seal(), y1.seal()).#toTuple(), - Group.from(x2.seal(), y2.seal()).#toTuple(), - Group.from(x3, y3).#toTuple(), + let [, x, y] = Snarky.gates.ecAdd( + toTuple(Group.from(x1.seal(), y1.seal())), + toTuple(Group.from(x2.seal(), y2.seal())), + toTuple(Group.from(x3, y3)), inf.toField().value, same_x.value, s.value, @@ -187,27 +151,15 @@ class Group { // similarly to the constant implementation, we check if either operand is zero // and the implementation above (original OCaml implementation) returns something wild -> g + 0 != g where it should be g + 0 = g let gIsZero = g.isZero(); - let thisIsZero = this.isZero(); - - let bothZero = gIsZero.and(thisIsZero); - - let onlyGisZero = gIsZero.and(thisIsZero.not()); - let onlyThisIsZero = thisIsZero.and(gIsZero.not()); - + let onlyThisIsZero = this.isZero().and(gIsZero.not()); let isNegation = inf; + let isNormalAddition = gIsZero.or(onlyThisIsZero).or(isNegation).not(); - let isNewElement = bothZero - .not() - .and(isNegation.not()) - .and(onlyThisIsZero.not()) - .and(onlyGisZero.not()); - - const zero_g = Group.zero; - + // note: gIsZero and isNegation are not mutually exclusive, but if both are true, we add 1*0 + 1*0 = 0 which is correct return Provable.switch( - [bothZero, onlyGisZero, onlyThisIsZero, isNegation, isNewElement], + [gIsZero, onlyThisIsZero, isNegation, isNormalAddition], Group, - [zero_g, this, g, zero_g, new Group({ x, y })] + [this, g, Group.zero, new Group({ x, y })] ); } } @@ -239,13 +191,13 @@ class Group { scale(s: Scalar | number | bigint) { let scalar = Scalar.from(s); - if (this.#isConstant() && scalar.isConstant()) { - let g_proj = Pallas.scale(this.#toProjective(), scalar.toBigInt()); - return Group.#fromProjective(g_proj); + if (isConstant(this) && scalar.isConstant()) { + let g_proj = Pallas.scale(toProjective(this), scalar.toBigInt()); + return fromProjective(g_proj); } else { let [, ...bits] = scalar.value; bits.reverse(); - let [, x, y] = Snarky.group.scale(this.#toTuple(), [0, ...bits]); + let [, x, y] = Snarky.group.scale(toTuple(this), [0, ...bits]); return new Group({ x, y }); } } @@ -471,3 +423,29 @@ class Group { } } } + +// internal helpers + +function isConstant(g: Group) { + return g.x.isConstant() && g.y.isConstant(); +} + +function toTuple(g: Group): [0, FieldVar, FieldVar] { + return [0, g.x.value, g.y.value]; +} + +function toProjective(g: Group) { + return Pallas.fromAffine({ + x: g.x.toBigInt(), + y: g.y.toBigInt(), + infinity: false, + }); +} + +function fromProjective({ x, y, z }: { x: bigint; y: bigint; z: bigint }) { + return fromAffine(Pallas.toAffine({ x, y, z })); +} + +function fromAffine({ x, y, infinity }: GroupAffine) { + return infinity ? Group.zero : new Group({ x, y }); +} diff --git a/src/lib/hash-generic.ts b/src/lib/hash-generic.ts index c5e240ae0a..7e76c8ceee 100644 --- a/src/lib/hash-generic.ts +++ b/src/lib/hash-generic.ts @@ -1,4 +1,4 @@ -import { GenericField } from '../bindings/lib/generic.js'; +import { GenericSignableField } from '../bindings/lib/generic.js'; import { prefixToField } from '../bindings/lib/binable.js'; export { createHashHelpers, HashHelpers }; @@ -11,7 +11,7 @@ type Hash = { type HashHelpers = ReturnType>; function createHashHelpers( - Field: GenericField, + Field: GenericSignableField, Hash: Hash ) { function salt(prefix: string) { diff --git a/src/lib/hash.ts b/src/lib/hash.ts index 9abf35b662..8f51d97d0f 100644 --- a/src/lib/hash.ts +++ b/src/lib/hash.ts @@ -6,6 +6,7 @@ import { Provable } from './provable.js'; import { MlFieldArray } from './ml/fields.js'; import { Poseidon as PoseidonBigint } from '../bindings/crypto/poseidon.js'; import { assert } from './errors.js'; +import { rangeCheckN } from './gadgets/range-check.js'; // external API export { Poseidon, TokenSymbol }; @@ -13,7 +14,7 @@ export { Poseidon, TokenSymbol }; // internal API export { HashInput, - Hash, + HashHelpers, emptyHashWithPrefix, hashWithPrefix, salt, @@ -23,19 +24,19 @@ export { }; class Sponge { - private sponge: unknown; + #sponge: unknown; constructor() { let isChecked = Provable.inCheckedComputation(); - this.sponge = Snarky.poseidon.sponge.create(isChecked); + this.#sponge = Snarky.poseidon.sponge.create(isChecked); } absorb(x: Field) { - Snarky.poseidon.sponge.absorb(this.sponge, x.value); + Snarky.poseidon.sponge.absorb(this.#sponge, x.value); } squeeze(): Field { - return Field(Snarky.poseidon.sponge.squeeze(this.sponge)); + return Field(Snarky.poseidon.sponge.squeeze(this.#sponge)); } } @@ -64,7 +65,7 @@ const Poseidon = { if (isConstant(input)) { let result = PoseidonBigint.hashToGroup(toBigints(input)); assert(result !== undefined, 'hashToGroup works on all inputs'); - let { x, y } = result!; + let { x, y } = result; return { x: Field(x), y: { x0: Field(y.x0), x1: Field(y.x1) }, @@ -105,8 +106,8 @@ function hashConstant(input: Field[]) { return Field(PoseidonBigint.hash(toBigints(input))); } -const Hash = createHashHelpers(Field, Poseidon); -let { salt, emptyHashWithPrefix, hashWithPrefix } = Hash; +const HashHelpers = createHashHelpers(Field, Poseidon); +let { salt, emptyHashWithPrefix, hashWithPrefix } = HashHelpers; // same as Random_oracle.prefix_to_field in OCaml function prefixToField(prefix: string) { @@ -166,8 +167,7 @@ const TokenSymbolPure: ProvableExtended< return 1; }, check({ field }: TokenSymbol) { - let actual = field.rangeCheckHelper(48); - actual.assertEquals(field); + rangeCheckN(48, field); }, toJSON({ symbol }) { return symbol; @@ -179,12 +179,11 @@ const TokenSymbolPure: ProvableExtended< toInput({ field }) { return { packed: [[field, 48]] }; }, + empty() { + return { symbol: '', field: Field(0n) }; + }, }; class TokenSymbol extends Struct(TokenSymbolPure) { - static get empty() { - return { symbol: '', field: Field(0) }; - } - static from(symbol: string): TokenSymbol { let bytesLength = new TextEncoder().encode(symbol).length; if (bytesLength > 6) diff --git a/src/lib/hashes-combined.ts b/src/lib/hashes-combined.ts new file mode 100644 index 0000000000..8440a25b32 --- /dev/null +++ b/src/lib/hashes-combined.ts @@ -0,0 +1,127 @@ +import { Poseidon } from './hash.js'; +import { Keccak } from './keccak.js'; +import { Bytes } from './provable-types/provable-types.js'; + +export { Hash }; + +/** + * A collection of hash functions which can be used in provable code. + */ +const Hash = { + /** + * Hashes the given field elements using [Poseidon](https://eprint.iacr.org/2019/458.pdf). Alias for `Poseidon.hash()`. + * + * ```ts + * let hash = Hash.hash([a, b, c]); + * ``` + * + * **Important:** This is by far the most efficient hash function o1js has available in provable code. + * Use it by default, if no compatibility concerns require you to use a different hash function. + * + * The Poseidon implementation operates over the native [Pallas base field](https://electriccoin.co/blog/the-pasta-curves-for-halo-2-and-beyond/) + * and uses parameters generated specifically for the [Mina](https://minaprotocol.com) blockchain. + * + * We use a `rate` of 2, which means that 2 field elements are hashed per permutation. + * A permutation causes 11 rows in the constraint system. + * + * You can find the full set of Poseidon parameters [here](https://github.com/o1-labs/o1js-bindings/blob/main/crypto/constants.ts). + */ + hash: Poseidon.hash, + + /** + * The [Poseidon](https://eprint.iacr.org/2019/458.pdf) hash function. + * + * See {@link Hash.hash} for details and usage examples. + */ + Poseidon, + + /** + * The SHA3 hash function with an output length of 256 bits. + */ + SHA3_256: { + /** + * Hashes the given bytes using SHA3-256. + * + * This is an alias for `Keccak.nistSha3(256, bytes)`.\ + * See {@link Keccak.nistSha3} for details and usage examples. + */ + hash(bytes: Bytes) { + return Keccak.nistSha3(256, bytes); + }, + }, + + /** + * The SHA3 hash function with an output length of 384 bits. + */ + SHA3_384: { + /** + * Hashes the given bytes using SHA3-384. + * + * This is an alias for `Keccak.nistSha3(384, bytes)`.\ + * See {@link Keccak.nistSha3} for details and usage examples. + */ + hash(bytes: Bytes) { + return Keccak.nistSha3(384, bytes); + }, + }, + + /** + * The SHA3 hash function with an output length of 512 bits. + */ + SHA3_512: { + /** + * Hashes the given bytes using SHA3-512. + * + * This is an alias for `Keccak.nistSha3(512, bytes)`.\ + * See {@link Keccak.nistSha3} for details and usage examples. + */ + hash(bytes: Bytes) { + return Keccak.nistSha3(512, bytes); + }, + }, + + /** + * The pre-NIST Keccak hash function with an output length of 256 bits. + */ + Keccak256: { + /** + * Hashes the given bytes using Keccak-256. + * + * This is an alias for `Keccak.preNist(256, bytes)`.\ + * See {@link Keccak.preNist} for details and usage examples. + */ + hash(bytes: Bytes) { + return Keccak.preNist(256, bytes); + }, + }, + + /** + * The pre-NIST Keccak hash function with an output length of 384 bits. + */ + Keccak384: { + /** + * Hashes the given bytes using Keccak-384. + * + * This is an alias for `Keccak.preNist(384, bytes)`.\ + * See {@link Keccak.preNist} for details and usage examples. + */ + hash(bytes: Bytes) { + return Keccak.preNist(384, bytes); + }, + }, + + /** + * The pre-NIST Keccak hash function with an output length of 512 bits. + */ + Keccak512: { + /** + * Hashes the given bytes using Keccak-512. + * + * This is an alias for `Keccak.preNist(512, bytes)`.\ + * See {@link Keccak.preNist} for details and usage examples. + */ + hash(bytes: Bytes) { + return Keccak.preNist(512, bytes); + }, + }, +}; diff --git a/src/lib/int.test.ts b/src/lib/int.test.ts index cc93726496..1914a9fede 100644 --- a/src/lib/int.test.ts +++ b/src/lib/int.test.ts @@ -1,28 +1,15 @@ import { - isReady, Provable, - shutdown, Int64, UInt64, UInt32, + UInt8, Field, Bool, Sign, } from 'o1js'; describe('int', () => { - beforeAll(async () => { - await isReady; - }); - - afterAll(async () => { - // Use a timeout to defer the execution of `shutdown()` until Jest processes all tests. - // `shutdown()` exits the process when it's done cleanup so we want to delay it's execution until Jest is done - setTimeout(async () => { - await shutdown(); - }, 0); - }); - const NUMBERMAX = 2 ** 53 - 1; // JavaScript numbers can only safely store integers in the range -(2^53 − 1) to 2^53 − 1 describe('Int64', () => { @@ -2150,4 +2137,850 @@ describe('int', () => { }); }); }); + + describe('UInt8', () => { + const NUMBERMAX = UInt8.MAXINT().value; + + describe('Inside circuit', () => { + describe('add', () => { + it('1+1=2', () => { + expect(() => { + Provable.runAndCheck(() => { + const x = Provable.witness(UInt8, () => new UInt8(1)); + const y = Provable.witness(UInt8, () => new UInt8(1)); + x.add(y).assertEquals(2); + }); + }).not.toThrow(); + }); + + it('100+100=200', () => { + expect(() => { + Provable.runAndCheck(() => { + const x = Provable.witness(UInt8, () => new UInt8(100)); + const y = Provable.witness(UInt8, () => new UInt8(100)); + x.add(y).assertEquals(new UInt8(200)); + }); + }).not.toThrow(); + }); + + it('(MAXINT/2+MAXINT/2) adds to MAXINT', () => { + const n = ((1n << 8n) - 2n) / 2n; + expect(() => { + Provable.runAndCheck(() => { + const x = Provable.witness(UInt8, () => new UInt8(n)); + const y = Provable.witness(UInt8, () => new UInt8(n)); + x.add(y).add(1).assertEquals(UInt8.MAXINT()); + }); + }).not.toThrow(); + }); + + it('should throw on overflow addition', () => { + expect(() => { + Provable.runAndCheck(() => { + const x = Provable.witness(UInt8, () => UInt8.MAXINT()); + const y = Provable.witness(UInt8, () => new UInt8(1)); + x.add(y); + }); + }).toThrow(); + }); + }); + + describe('sub', () => { + it('1-1=0', () => { + expect(() => { + Provable.runAndCheck(() => { + const x = Provable.witness(UInt8, () => new UInt8(1)); + const y = Provable.witness(UInt8, () => new UInt8(1)); + x.sub(y).assertEquals(new UInt8(0)); + }); + }).not.toThrow(); + }); + + it('100-50=50', () => { + expect(() => { + Provable.runAndCheck(() => { + const x = Provable.witness(UInt8, () => new UInt8(100)); + const y = Provable.witness(UInt8, () => new UInt8(50)); + x.sub(y).assertEquals(new UInt8(50)); + }); + }).not.toThrow(); + }); + + it('should throw on sub if results in negative number', () => { + expect(() => { + Provable.runAndCheck(() => { + const x = Provable.witness(UInt8, () => new UInt8(0)); + const y = Provable.witness(UInt8, () => new UInt8(1)); + x.sub(y); + }); + }).toThrow(); + }); + }); + + describe('mul', () => { + it('1x2=2', () => { + expect(() => { + Provable.runAndCheck(() => { + const x = Provable.witness(UInt8, () => new UInt8(1)); + const y = Provable.witness(UInt8, () => new UInt8(2)); + x.mul(y).assertEquals(new UInt8(2)); + }); + }).not.toThrow(); + }); + + it('1x0=0', () => { + expect(() => { + Provable.runAndCheck(() => { + const x = Provable.witness(UInt8, () => new UInt8(1)); + const y = Provable.witness(UInt8, () => new UInt8(0)); + x.mul(y).assertEquals(new UInt8(0)); + }); + }).not.toThrow(); + }); + + it('12x20=240', () => { + expect(() => { + Provable.runAndCheck(() => { + const x = Provable.witness(UInt8, () => new UInt8(12)); + const y = Provable.witness(UInt8, () => new UInt8(20)); + x.mul(y).assertEquals(new UInt8(240)); + }); + }).not.toThrow(); + }); + + it('MAXINTx1=MAXINT', () => { + expect(() => { + Provable.runAndCheck(() => { + const x = Provable.witness(UInt8, () => UInt8.MAXINT()); + const y = Provable.witness(UInt8, () => new UInt8(1)); + x.mul(y).assertEquals(UInt8.MAXINT()); + }); + }).not.toThrow(); + }); + + it('should throw on overflow multiplication', () => { + expect(() => { + Provable.runAndCheck(() => { + const x = Provable.witness(UInt8, () => UInt8.MAXINT()); + const y = Provable.witness(UInt8, () => new UInt8(2)); + x.mul(y); + }); + }).toThrow(); + }); + }); + + describe('div', () => { + it('2/1=2', () => { + expect(() => { + Provable.runAndCheck(() => { + const x = Provable.witness(UInt8, () => new UInt8(2)); + const y = Provable.witness(UInt8, () => new UInt8(1)); + x.div(y).assertEquals(new UInt8(2)); + }); + }).not.toThrow(); + }); + + it('0/1=0', () => { + expect(() => { + Provable.runAndCheck(() => { + const x = Provable.witness(UInt8, () => new UInt8(0)); + const y = Provable.witness(UInt8, () => new UInt8(1)); + x.div(y).assertEquals(new UInt8(0)); + }); + }).not.toThrow(); + }); + + it('20/10=2', () => { + expect(() => { + Provable.runAndCheck(() => { + const x = Provable.witness(UInt8, () => new UInt8(20)); + const y = Provable.witness(UInt8, () => new UInt8(10)); + x.div(y).assertEquals(new UInt8(2)); + }); + }).not.toThrow(); + }); + + it('MAXINT/1=MAXINT', () => { + expect(() => { + Provable.runAndCheck(() => { + const x = Provable.witness(UInt8, () => UInt8.MAXINT()); + const y = Provable.witness(UInt8, () => new UInt8(1)); + x.div(y).assertEquals(UInt8.MAXINT()); + }); + }).not.toThrow(); + }); + + it('should throw on division by zero', () => { + expect(() => { + Provable.runAndCheck(() => { + const x = Provable.witness(UInt8, () => UInt8.MAXINT()); + const y = Provable.witness(UInt8, () => new UInt8(0)); + x.div(y); + }); + }).toThrow(); + }); + }); + + describe('mod', () => { + it('1%1=0', () => { + expect(() => { + Provable.runAndCheck(() => { + const x = Provable.witness(UInt8, () => new UInt8(1)); + const y = Provable.witness(UInt8, () => new UInt8(1)); + x.mod(y).assertEquals(new UInt8(0)); + }); + }).not.toThrow(); + }); + + it('50%32=18', () => { + expect(() => { + Provable.runAndCheck(() => { + const x = Provable.witness(UInt8, () => new UInt8(50)); + const y = Provable.witness(UInt8, () => new UInt8(32)); + x.mod(y).assertEquals(new UInt8(18)); + }); + }).not.toThrow(); + }); + + it('MAXINT%7=3', () => { + expect(() => { + Provable.runAndCheck(() => { + const x = Provable.witness(UInt8, () => UInt8.MAXINT()); + const y = Provable.witness(UInt8, () => new UInt8(7)); + x.mod(y).assertEquals(new UInt8(3)); + }); + }).not.toThrow(); + }); + + it('should throw on mod by zero', () => { + expect(() => { + Provable.runAndCheck(() => { + const x = Provable.witness(UInt8, () => UInt8.MAXINT()); + const y = Provable.witness(UInt8, () => new UInt8(0)); + x.mod(y).assertEquals(new UInt8(1)); + }); + }).toThrow(); + }); + }); + + describe('assertLt', () => { + it('1<2=true', () => { + expect(() => { + Provable.runAndCheck(() => { + const x = Provable.witness(UInt8, () => new UInt8(1)); + const y = Provable.witness(UInt8, () => new UInt8(2)); + x.assertLessThan(y); + }); + }).not.toThrow(); + }); + + it('1<1=false', () => { + expect(() => { + Provable.runAndCheck(() => { + const x = Provable.witness(UInt8, () => new UInt8(1)); + const y = Provable.witness(UInt8, () => new UInt8(1)); + x.assertLessThan(y); + }); + }).toThrow(); + }); + + it('2<1=false', () => { + expect(() => { + Provable.runAndCheck(() => { + const x = Provable.witness(UInt8, () => new UInt8(2)); + const y = Provable.witness(UInt8, () => new UInt8(1)); + x.assertLessThan(y); + }); + }).toThrow(); + }); + + it('10<100=true', () => { + expect(() => { + Provable.runAndCheck(() => { + const x = Provable.witness(UInt8, () => new UInt8(10)); + const y = Provable.witness(UInt8, () => new UInt8(100)); + x.assertLessThan(y); + }); + }).not.toThrow(); + }); + + it('100<10=false', () => { + expect(() => { + Provable.runAndCheck(() => { + const x = Provable.witness(UInt8, () => new UInt8(100)); + const y = Provable.witness(UInt8, () => new UInt8(10)); + x.assertLessThan(y); + }); + }).toThrow(); + }); + + it('MAXINT { + expect(() => { + Provable.runAndCheck(() => { + const x = Provable.witness(UInt8, () => UInt8.MAXINT()); + const y = Provable.witness(UInt8, () => UInt8.MAXINT()); + x.assertLessThan(y); + }); + }).toThrow(); + }); + }); + + describe('assertLessThanOrEqual', () => { + it('1<=1=true', () => { + expect(() => { + Provable.runAndCheck(() => { + const x = Provable.witness(UInt8, () => new UInt8(1)); + const y = Provable.witness(UInt8, () => new UInt8(1)); + x.assertLessThanOrEqual(y); + }); + }).not.toThrow(); + }); + + it('2<=1=false', () => { + expect(() => { + Provable.runAndCheck(() => { + const x = Provable.witness(UInt8, () => new UInt8(2)); + const y = Provable.witness(UInt8, () => new UInt8(1)); + x.assertLessThanOrEqual(y); + }); + }).toThrow(); + }); + + it('10<=100=true', () => { + expect(() => { + Provable.runAndCheck(() => { + const x = Provable.witness(UInt8, () => new UInt8(10)); + const y = Provable.witness(UInt8, () => new UInt8(100)); + x.assertLessThanOrEqual(y); + }); + }).not.toThrow(); + }); + + it('100<=10=false', () => { + expect(() => { + Provable.runAndCheck(() => { + const x = Provable.witness(UInt8, () => new UInt8(100)); + const y = Provable.witness(UInt8, () => new UInt8(10)); + x.assertLessThanOrEqual(y); + }); + }).toThrow(); + }); + + it('MAXINT<=MAXINT=true', () => { + expect(() => { + Provable.runAndCheck(() => { + const x = Provable.witness(UInt8, () => UInt8.MAXINT()); + const y = Provable.witness(UInt8, () => UInt8.MAXINT()); + x.assertLessThanOrEqual(y); + }); + }).not.toThrow(); + }); + }); + + describe('assertGreaterThan', () => { + it('2>1=true', () => { + expect(() => { + Provable.runAndCheck(() => { + const x = Provable.witness(UInt8, () => new UInt8(2)); + const y = Provable.witness(UInt8, () => new UInt8(1)); + x.assertGreaterThan(y); + }); + }).not.toThrow(); + }); + + it('1>1=false', () => { + expect(() => { + Provable.runAndCheck(() => { + const x = Provable.witness(UInt8, () => new UInt8(1)); + const y = Provable.witness(UInt8, () => new UInt8(1)); + x.assertGreaterThan(y); + }); + }).toThrow(); + }); + + it('1>2=false', () => { + expect(() => { + Provable.runAndCheck(() => { + const x = Provable.witness(UInt8, () => new UInt8(1)); + const y = Provable.witness(UInt8, () => new UInt8(2)); + x.assertGreaterThan(y); + }); + }).toThrow(); + }); + + it('100>10=true', () => { + expect(() => { + Provable.runAndCheck(() => { + const x = Provable.witness(UInt8, () => new UInt8(100)); + const y = Provable.witness(UInt8, () => new UInt8(10)); + x.assertGreaterThan(y); + }); + }).not.toThrow(); + }); + + it('10>100=false', () => { + expect(() => { + Provable.runAndCheck(() => { + const x = Provable.witness(UInt8, () => new UInt8(1000)); + const y = Provable.witness(UInt8, () => new UInt8(100000)); + x.assertGreaterThan(y); + }); + }).toThrow(); + }); + + it('MAXINT>MAXINT=false', () => { + expect(() => { + Provable.runAndCheck(() => { + const x = Provable.witness(UInt8, () => UInt8.MAXINT()); + const y = Provable.witness(UInt8, () => UInt8.MAXINT()); + x.assertGreaterThan(y); + }); + }).toThrow(); + }); + }); + + describe('assertGreaterThanOrEqual', () => { + it('1<=1=true', () => { + expect(() => { + Provable.runAndCheck(() => { + const x = Provable.witness(UInt8, () => new UInt8(1)); + const y = Provable.witness(UInt8, () => new UInt8(1)); + x.assertGreaterThanOrEqual(y); + }); + }).not.toThrow(); + }); + + it('1>=2=false', () => { + expect(() => { + Provable.runAndCheck(() => { + const x = Provable.witness(UInt8, () => new UInt8(1)); + const y = Provable.witness(UInt8, () => new UInt8(2)); + x.assertGreaterThanOrEqual(y); + }); + }).toThrow(); + }); + + it('100>=10=true', () => { + expect(() => { + Provable.runAndCheck(() => { + const x = Provable.witness(UInt8, () => new UInt8(100)); + const y = Provable.witness(UInt8, () => new UInt8(10)); + x.assertGreaterThanOrEqual(y); + }); + }).not.toThrow(); + }); + + it('10>=100=false', () => { + expect(() => { + Provable.runAndCheck(() => { + const x = Provable.witness(UInt8, () => new UInt8(10)); + const y = Provable.witness(UInt8, () => new UInt8(100)); + x.assertGreaterThanOrEqual(y); + }); + }).toThrow(); + }); + + it('MAXINT>=MAXINT=true', () => { + expect(() => { + Provable.runAndCheck(() => { + const x = Provable.witness(UInt8, () => UInt8.MAXINT()); + const y = Provable.witness(UInt8, () => UInt8.MAXINT()); + x.assertGreaterThanOrEqual(y); + }); + }).not.toThrow(); + }); + }); + + describe('from() ', () => { + describe('fromNumber()', () => { + it('should be the same as Field(1)', () => { + expect(() => { + Provable.runAndCheck(() => { + const x = Provable.witness(UInt8, () => UInt8.from(1)); + const y = Provable.witness(UInt8, () => new UInt8(1)); + x.assertEquals(y); + }); + }).not.toThrow(); + }); + }); + }); + }); + + describe('Outside of circuit', () => { + describe('add', () => { + it('1+1=2', () => { + expect(new UInt8(1).add(1).toString()).toEqual('2'); + }); + + it('50+50=100', () => { + expect(new UInt8(50).add(50).toString()).toEqual('100'); + }); + + it('(MAXINT/2+MAXINT/2) adds to MAXINT', () => { + const value = ((1n << 8n) - 2n) / 2n; + expect( + new UInt8(value).add(new UInt8(value)).add(new UInt8(1)).toString() + ).toEqual(UInt8.MAXINT().toString()); + }); + + it('should throw on overflow addition', () => { + expect(() => { + UInt8.MAXINT().add(1); + }).toThrow(); + }); + }); + + describe('sub', () => { + it('1-1=0', () => { + expect(new UInt8(1).sub(1).toString()).toEqual('0'); + }); + + it('100-50=50', () => { + expect(new UInt8(100).sub(50).toString()).toEqual('50'); + }); + + it('should throw on sub if results in negative number', () => { + expect(() => { + UInt8.from(0).sub(1); + }).toThrow(); + }); + }); + + describe('mul', () => { + it('1x2=2', () => { + expect(new UInt8(1).mul(2).toString()).toEqual('2'); + }); + + it('1x0=0', () => { + expect(new UInt8(1).mul(0).toString()).toEqual('0'); + }); + + it('12x20=240', () => { + expect(new UInt8(12).mul(20).toString()).toEqual('240'); + }); + + it('MAXINTx1=MAXINT', () => { + expect(UInt8.MAXINT().mul(1).toString()).toEqual( + UInt8.MAXINT().toString() + ); + }); + + it('should throw on overflow multiplication', () => { + expect(() => { + UInt8.MAXINT().mul(2); + }).toThrow(); + }); + }); + + describe('div', () => { + it('2/1=2', () => { + expect(new UInt8(2).div(1).toString()).toEqual('2'); + }); + + it('0/1=0', () => { + expect(new UInt8(0).div(1).toString()).toEqual('0'); + }); + + it('20/10=2', () => { + expect(new UInt8(20).div(10).toString()).toEqual('2'); + }); + + it('MAXINT/1=MAXINT', () => { + expect(UInt8.MAXINT().div(1).toString()).toEqual( + UInt8.MAXINT().toString() + ); + }); + + it('should throw on division by zero', () => { + expect(() => { + UInt8.MAXINT().div(0); + }).toThrow(); + }); + }); + + describe('mod', () => { + it('1%1=0', () => { + expect(new UInt8(1).mod(1).toString()).toEqual('0'); + }); + + it('50%32=18', () => { + expect(new UInt8(50).mod(32).toString()).toEqual('18'); + }); + + it('MAXINT%7=3', () => { + expect(UInt8.MAXINT().mod(7).toString()).toEqual('3'); + }); + + it('should throw on mod by zero', () => { + expect(() => { + UInt8.MAXINT().mod(0); + }).toThrow(); + }); + }); + + describe('lessThan', () => { + it('1<2=true', () => { + expect(new UInt8(1).lessThan(new UInt8(2))).toEqual(Bool(true)); + }); + + it('1<1=false', () => { + expect(new UInt8(1).lessThan(new UInt8(1))).toEqual(Bool(false)); + }); + + it('2<1=false', () => { + expect(new UInt8(2).lessThan(new UInt8(1))).toEqual(Bool(false)); + }); + + it('10<100=true', () => { + expect(new UInt8(10).lessThan(new UInt8(100))).toEqual(Bool(true)); + }); + + it('100<10=false', () => { + expect(new UInt8(100).lessThan(new UInt8(10))).toEqual(Bool(false)); + }); + + it('MAXINT { + expect(UInt8.MAXINT().lessThan(UInt8.MAXINT())).toEqual(Bool(false)); + }); + }); + + describe('lessThanOrEqual', () => { + it('1<=1=true', () => { + expect(new UInt8(1).lessThanOrEqual(new UInt8(1))).toEqual( + Bool(true) + ); + }); + + it('2<=1=false', () => { + expect(new UInt8(2).lessThanOrEqual(new UInt8(1))).toEqual( + Bool(false) + ); + }); + + it('10<=100=true', () => { + expect(new UInt8(10).lessThanOrEqual(new UInt8(100))).toEqual( + Bool(true) + ); + }); + + it('100<=10=false', () => { + expect(new UInt8(100).lessThanOrEqual(new UInt8(10))).toEqual( + Bool(false) + ); + }); + + it('MAXINT<=MAXINT=true', () => { + expect(UInt8.MAXINT().lessThanOrEqual(UInt8.MAXINT())).toEqual( + Bool(true) + ); + }); + }); + + describe('assertLessThanOrEqual', () => { + it('1<=1=true', () => { + expect(() => { + new UInt8(1).assertLessThanOrEqual(new UInt8(1)); + }).not.toThrow(); + }); + + it('2<=1=false', () => { + expect(() => { + new UInt8(2).assertLessThanOrEqual(new UInt8(1)); + }).toThrow(); + }); + + it('10<=100=true', () => { + expect(() => { + new UInt8(10).assertLessThanOrEqual(new UInt8(100)); + }).not.toThrow(); + }); + + it('100<=10=false', () => { + expect(() => { + new UInt8(100).assertLessThanOrEqual(new UInt8(10)); + }).toThrow(); + }); + + it('MAXINT<=MAXINT=true', () => { + expect(() => { + UInt8.MAXINT().assertLessThanOrEqual(UInt8.MAXINT()); + }).not.toThrow(); + }); + }); + + describe('greaterThan', () => { + it('2>1=true', () => { + expect(new UInt8(2).greaterThan(new UInt8(1))).toEqual(Bool(true)); + }); + + it('1>1=false', () => { + expect(new UInt8(1).greaterThan(new UInt8(1))).toEqual(Bool(false)); + }); + + it('1>2=false', () => { + expect(new UInt8(1).greaterThan(new UInt8(2))).toEqual(Bool(false)); + }); + + it('100>10=true', () => { + expect(new UInt8(100).greaterThan(new UInt8(10))).toEqual(Bool(true)); + }); + + it('10>100=false', () => { + expect(new UInt8(10).greaterThan(new UInt8(100))).toEqual( + Bool(false) + ); + }); + + it('MAXINT>MAXINT=false', () => { + expect(UInt8.MAXINT().greaterThan(UInt8.MAXINT())).toEqual( + Bool(false) + ); + }); + }); + + describe('assertGreaterThan', () => { + it('1>1=false', () => { + expect(() => { + new UInt8(1).assertGreaterThan(new UInt8(1)); + }).toThrow(); + }); + + it('2>1=true', () => { + expect(() => { + new UInt8(2).assertGreaterThan(new UInt8(1)); + }).not.toThrow(); + }); + + it('10>100=false', () => { + expect(() => { + new UInt8(10).assertGreaterThan(new UInt8(100)); + }).toThrow(); + }); + + it('100000>1000=true', () => { + expect(() => { + new UInt8(100).assertGreaterThan(new UInt8(10)); + }).not.toThrow(); + }); + + it('MAXINT>MAXINT=false', () => { + expect(() => { + UInt8.MAXINT().assertGreaterThan(UInt8.MAXINT()); + }).toThrow(); + }); + }); + + describe('greaterThanOrEqual', () => { + it('2>=1=true', () => { + expect(new UInt8(2).greaterThanOrEqual(new UInt8(1))).toEqual( + Bool(true) + ); + }); + + it('1>=1=true', () => { + expect(new UInt8(1).greaterThanOrEqual(new UInt8(1))).toEqual( + Bool(true) + ); + }); + + it('1>=2=false', () => { + expect(new UInt8(1).greaterThanOrEqual(new UInt8(2))).toEqual( + Bool(false) + ); + }); + + it('100>=10=true', () => { + expect(new UInt8(100).greaterThanOrEqual(new UInt8(10))).toEqual( + Bool(true) + ); + }); + + it('10>=100=false', () => { + expect(new UInt8(10).greaterThanOrEqual(new UInt8(100))).toEqual( + Bool(false) + ); + }); + + it('MAXINT>=MAXINT=true', () => { + expect(UInt8.MAXINT().greaterThanOrEqual(UInt8.MAXINT())).toEqual( + Bool(true) + ); + }); + }); + + describe('assertGreaterThanOrEqual', () => { + it('1>=1=true', () => { + expect(() => { + new UInt8(1).assertGreaterThanOrEqual(new UInt8(1)); + }).not.toThrow(); + }); + + it('2>=1=true', () => { + expect(() => { + new UInt8(2).assertGreaterThanOrEqual(new UInt8(1)); + }).not.toThrow(); + }); + + it('10>=100=false', () => { + expect(() => { + new UInt8(10).assertGreaterThanOrEqual(new UInt8(100)); + }).toThrow(); + }); + + it('100>=10=true', () => { + expect(() => { + new UInt8(100).assertGreaterThanOrEqual(new UInt8(10)); + }).not.toThrow(); + }); + + it('MAXINT>=MAXINT=true', () => { + expect(() => { + UInt32.MAXINT().assertGreaterThanOrEqual(UInt32.MAXINT()); + }).not.toThrow(); + }); + }); + + describe('toString()', () => { + it('should be the same as Field(0)', async () => { + const x = new UInt8(0); + const y = Field(0); + expect(x.toString()).toEqual(y.toString()); + }); + it('should be the same as 2^8-1', async () => { + const x = new UInt8(NUMBERMAX.toBigInt()); + const y = Field(String(NUMBERMAX)); + expect(x.toString()).toEqual(y.toString()); + }); + }); + + describe('check()', () => { + it('should pass checking a MAXINT', () => { + expect(() => { + UInt8.check(UInt8.MAXINT()); + }).not.toThrow(); + }); + + it('should throw checking over MAXINT', () => { + const x = UInt8.MAXINT(); + expect(() => { + UInt8.check(x.add(1)); + }).toThrow(); + }); + }); + + describe('from() ', () => { + describe('fromNumber()', () => { + it('should be the same as Field(1)', () => { + const x = UInt8.from(1); + expect(x.value).toEqual(Field(1)); + }); + + it('should be the same as 2^53-1', () => { + const x = UInt8.from(NUMBERMAX); + expect(x.value).toEqual(NUMBERMAX); + }); + }); + }); + }); + }); }); diff --git a/src/lib/int.ts b/src/lib/int.ts index 39744a84cf..9bae3afdf1 100644 --- a/src/lib/int.ts +++ b/src/lib/int.ts @@ -1,11 +1,13 @@ import { Field, Bool } from './core.js'; -import { AnyConstructor, CircuitValue, prop } from './circuit_value.js'; +import { AnyConstructor, CircuitValue, Struct, prop } from './circuit_value.js'; import { Types } from '../bindings/mina-transaction/types.js'; import { HashInput } from './hash.js'; import { Provable } from './provable.js'; +import { Gadgets } from './gadgets/gadgets.js'; +import { FieldVar, withMessage } from './field.js'; // external API -export { UInt32, UInt64, Int64, Sign }; +export { UInt8, UInt32, UInt64, Int64, Sign }; /** * A 64 bit unsigned integer with values ranging from 0 to 18,446,744,073,709,551,615. @@ -14,6 +16,12 @@ class UInt64 extends CircuitValue { @prop value: Field; static NUM_BITS = 64; + constructor(x: UInt64 | UInt32 | Field | number | string | bigint) { + if (x instanceof UInt64 || x instanceof UInt32) x = x.value; + else if (!(x instanceof Field)) x = Field(x); + super(x); + } + /** * Static method to create a {@link UInt64} with value `0`. */ @@ -66,12 +74,13 @@ class UInt64 extends CircuitValue { } static check(x: UInt64) { - let actual = x.value.rangeCheckHelper(64); - actual.assertEquals(x.value); + Gadgets.rangeCheckN(UInt64.NUM_BITS, x.value); } + static toInput(x: UInt64): HashInput { return { packed: [[x.value, 64]] }; } + /** * Encodes this structure into a JSON-like object. */ @@ -140,11 +149,11 @@ class UInt64 extends CircuitValue { () => new Field(x.toBigInt() / y_.toBigInt()) ); - q.rangeCheckHelper(UInt64.NUM_BITS).assertEquals(q); + Gadgets.rangeCheckN(UInt64.NUM_BITS, q); // TODO: Could be a bit more efficient let r = x.sub(q.mul(y_)).seal(); - r.rangeCheckHelper(UInt64.NUM_BITS).assertEquals(r); + Gadgets.rangeCheckN(UInt64.NUM_BITS, r); let r_ = new UInt64(r); let q_ = new UInt64(q); @@ -180,7 +189,7 @@ class UInt64 extends CircuitValue { */ mul(y: UInt64 | number) { let z = this.value.mul(UInt64.from(y).value); - z.rangeCheckHelper(UInt64.NUM_BITS).assertEquals(z); + Gadgets.rangeCheckN(UInt64.NUM_BITS, z); return new UInt64(z); } @@ -189,7 +198,7 @@ class UInt64 extends CircuitValue { */ add(y: UInt64 | number) { let z = this.value.add(UInt64.from(y).value); - z.rangeCheckHelper(UInt64.NUM_BITS).assertEquals(z); + Gadgets.rangeCheckN(UInt64.NUM_BITS, z); return new UInt64(z); } @@ -198,10 +207,169 @@ class UInt64 extends CircuitValue { */ sub(y: UInt64 | number) { let z = this.value.sub(UInt64.from(y).value); - z.rangeCheckHelper(UInt64.NUM_BITS).assertEquals(z); + Gadgets.rangeCheckN(UInt64.NUM_BITS, z); return new UInt64(z); } + /** + * Bitwise XOR gadget on {@link Field} elements. Equivalent to the [bitwise XOR `^` operator in JavaScript](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Bitwise_XOR). + * A XOR gate works by comparing two bits and returning `1` if two bits differ, and `0` if two bits are equal. + * + * This gadget builds a chain of XOR gates recursively. + * + * You can find more details about the implementation in the [Mina book](https://o1-labs.github.io/proof-systems/specs/kimchi.html?highlight=gates#xor-1) + * + * @param x {@link UInt64} element to XOR. + * + * @example + * ```ts + * let a = UInt64.from(0b0101); + * let b = UInt64.from(0b0011); + * + * let c = a.xor(b); + * c.assertEquals(0b0110); + * ``` + */ + xor(x: UInt64) { + return new UInt64(Gadgets.xor(this.value, x.value, UInt64.NUM_BITS)); + } + + /** + * Bitwise NOT gate on {@link Field} elements. Similar to the [bitwise + * NOT `~` operator in JavaScript](https://developer.mozilla.org/en-US/docs/ + * Web/JavaScript/Reference/Operators/Bitwise_NOT). + * + * **Note:** The NOT gate operates over 64 bit for UInt64 types. + * + * A NOT gate works by returning `1` in each bit position if the + * corresponding bit of the operand is `0`, and returning `0` if the + * corresponding bit of the operand is `1`. + * + * NOT is implemented as a subtraction of the input from the all one bitmask + * + * You can find more details about the implementation in the [Mina book](https://o1-labs.github.io/proof-systems/specs/kimchi.html?highlight=gates#not) + * + * @example + * ```ts + * // NOTing 4 bits with the unchecked version + * let a = UInt64.from(0b0101); + * let b = a.not(false); + * + * console.log(b.toBigInt().toString(2)); + * // 1111111111111111111111111111111111111111111111111111111111111010 + * + * ``` + * + * @param a - The value to apply NOT to. + * + */ + not() { + return new UInt64(Gadgets.not(this.value, UInt64.NUM_BITS, false)); + } + + /** + * A (left and right) rotation operates similarly to the shift operation (`<<` for left and `>>` for right) in JavaScript, + * with the distinction that the bits are circulated to the opposite end of a 64-bit representation rather than being discarded. + * For a left rotation, this means that bits shifted off the left end reappear at the right end. + * Conversely, for a right rotation, bits shifted off the right end reappear at the left end. + * + * It’s important to note that these operations are performed considering the big-endian 64-bit representation of the number, + * where the most significant (64th) bit is on the left end and the least significant bit is on the right end. + * The `direction` parameter is a string that accepts either `'left'` or `'right'`, determining the direction of the rotation. + * + * To safely use `rotate()`, you need to make sure that the value passed in is range-checked to 64 bits; + * for example, using {@link Gadgets.rangeCheck64}. + * + * You can find more details about the implementation in the [Mina book](https://o1-labs.github.io/proof-systems/specs/kimchi.html?highlight=gates#rotation) + * + * @param bits amount of bits to rotate this {@link UInt64} element with. + * @param direction left or right rotation direction. + * + * + * @example + * ```ts + * const x = UInt64.from(0b001100); + * const y = x.rotate(2, 'left'); + * const z = x.rotate(2, 'right'); // right rotation by 2 bits + * y.assertEquals(0b110000); + * z.assertEquals(0b000011); + * ``` + */ + rotate(bits: number, direction: 'left' | 'right' = 'left') { + return new UInt64(Gadgets.rotate64(this.value, bits, direction)); + } + + /** + * Performs a left shift operation on the provided {@link UInt64} element. + * This operation is similar to the `<<` shift operation in JavaScript, + * where bits are shifted to the left, and the overflowing bits are discarded. + * + * It’s important to note that these operations are performed considering the big-endian 64-bit representation of the number, + * where the most significant (64th) bit is on the left end and the least significant bit is on the right end. + * + * @param bits Amount of bits to shift the {@link UInt64} element to the left. The amount should be between 0 and 64 (or else the shift will fail). + * + * @example + * ```ts + * const x = UInt64.from(0b001100); // 12 in binary + * const y = x.leftShift(2); // left shift by 2 bits + * y.assertEquals(0b110000); // 48 in binary + * ``` + */ + leftShift(bits: number) { + return new UInt64(Gadgets.leftShift64(this.value, bits)); + } + + /** + * Performs a left right operation on the provided {@link UInt64} element. + * This operation is similar to the `>>` shift operation in JavaScript, + * where bits are shifted to the right, and the overflowing bits are discarded. + * + * It’s important to note that these operations are performed considering the big-endian 64-bit representation of the number, + * where the most significant (64th) bit is on the left end and the least significant bit is on the right end. + * + * @param bits Amount of bits to shift the {@link UInt64} element to the right. The amount should be between 0 and 64 (or else the shift will fail). + * + * @example + * ```ts + * const x = UInt64.from(0b001100); // 12 in binary + * const y = x.rightShift(2); // left shift by 2 bits + * y.assertEquals(0b000011); // 48 in binary + * ``` + */ + rightShift(bits: number) { + return new UInt64(Gadgets.leftShift64(this.value, bits)); + } + + /** + * Bitwise AND gadget on {@link UInt64} elements. Equivalent to the [bitwise AND `&` operator in JavaScript](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Bitwise_AND). + * The AND gate works by comparing two bits and returning `1` if both bits are `1`, and `0` otherwise. + * + * It can be checked by a double generic gate that verifies the following relationship between the values below. + * + * The generic gate verifies:\ + * `a + b = sum` and the conjunction equation `2 * and = sum - xor`\ + * Where:\ + * `a + b = sum`\ + * `a ^ b = xor`\ + * `a & b = and` + * + * You can find more details about the implementation in the [Mina book](https://o1-labs.github.io/proof-systems/specs/kimchi.html?highlight=gates#and) + * + * + * @example + * ```typescript + * let a = UInt64.from(3); // ... 000011 + * let b = UInt64.from(5); // ... 000101 + * + * let c = a.and(b); // ... 000001 + * c.assertEquals(1); + * ``` + */ + and(x: UInt64) { + return new UInt64(Gadgets.and(this.value, x.value, UInt64.NUM_BITS)); + } + /** * @deprecated Use {@link lessThanOrEqual} instead. * @@ -213,12 +381,11 @@ class UInt64 extends CircuitValue { } else { let xMinusY = this.value.sub(y.value).seal(); let yMinusX = xMinusY.neg(); - let xMinusYFits = xMinusY - .rangeCheckHelper(UInt64.NUM_BITS) - .equals(xMinusY); - let yMinusXFits = yMinusX - .rangeCheckHelper(UInt64.NUM_BITS) - .equals(yMinusX); + + let xMinusYFits = Gadgets.isInRangeN(UInt64.NUM_BITS, xMinusY); + + let yMinusXFits = Gadgets.isInRangeN(UInt64.NUM_BITS, yMinusX); + xMinusYFits.or(yMinusXFits).assertEquals(true); // x <= y if y - x fits in 64 bits return yMinusXFits; @@ -234,12 +401,11 @@ class UInt64 extends CircuitValue { } else { let xMinusY = this.value.sub(y.value).seal(); let yMinusX = xMinusY.neg(); - let xMinusYFits = xMinusY - .rangeCheckHelper(UInt64.NUM_BITS) - .equals(xMinusY); - let yMinusXFits = yMinusX - .rangeCheckHelper(UInt64.NUM_BITS) - .equals(yMinusX); + + let xMinusYFits = Gadgets.isInRangeN(UInt64.NUM_BITS, xMinusY); + + let yMinusXFits = Gadgets.isInRangeN(UInt64.NUM_BITS, yMinusX); + xMinusYFits.or(yMinusXFits).assertEquals(true); // x <= y if y - x fits in 64 bits return yMinusXFits; @@ -269,7 +435,7 @@ class UInt64 extends CircuitValue { return; } let yMinusX = y.value.sub(this.value).seal(); - yMinusX.rangeCheckHelper(UInt64.NUM_BITS).assertEquals(yMinusX, message); + Gadgets.rangeCheckN(UInt64.NUM_BITS, yMinusX, message); } /** @@ -377,6 +543,12 @@ class UInt32 extends CircuitValue { @prop value: Field; static NUM_BITS = 32; + constructor(x: UInt32 | Field | number | string | bigint) { + if (x instanceof UInt32) x = x.value; + else if (!(x instanceof Field)) x = Field(x); + super(x); + } + /** * Static method to create a {@link UInt32} with value `0`. */ @@ -411,8 +583,7 @@ class UInt32 extends CircuitValue { } static check(x: UInt32) { - let actual = x.value.rangeCheckHelper(32); - actual.assertEquals(x.value); + Gadgets.rangeCheck32(x.value); } static toInput(x: UInt32): HashInput { return { packed: [[x.value, 32]] }; @@ -450,12 +621,14 @@ class UInt32 extends CircuitValue { if (x instanceof UInt32) x = x.value; return new this(this.checkConstant(Field(x))); } + /** * Creates a {@link UInt32} with a value of 4,294,967,295. */ static MAXINT() { return new UInt32(Field((1n << 32n) - 1n)); } + /** * Integer division with remainder. * @@ -483,11 +656,11 @@ class UInt32 extends CircuitValue { () => new Field(x.toBigInt() / y_.toBigInt()) ); - q.rangeCheckHelper(UInt32.NUM_BITS).assertEquals(q); + Gadgets.rangeCheck32(q); // TODO: Could be a bit more efficient let r = x.sub(q.mul(y_)).seal(); - r.rangeCheckHelper(UInt32.NUM_BITS).assertEquals(r); + Gadgets.rangeCheck32(r); let r_ = new UInt32(r); let q_ = new UInt32(q); @@ -520,7 +693,7 @@ class UInt32 extends CircuitValue { */ mul(y: UInt32 | number) { let z = this.value.mul(UInt32.from(y).value); - z.rangeCheckHelper(UInt32.NUM_BITS).assertEquals(z); + Gadgets.rangeCheck32(z); return new UInt32(z); } /** @@ -528,7 +701,7 @@ class UInt32 extends CircuitValue { */ add(y: UInt32 | number) { let z = this.value.add(UInt32.from(y).value); - z.rangeCheckHelper(UInt32.NUM_BITS).assertEquals(z); + Gadgets.rangeCheck32(z); return new UInt32(z); } /** @@ -536,9 +709,171 @@ class UInt32 extends CircuitValue { */ sub(y: UInt32 | number) { let z = this.value.sub(UInt32.from(y).value); - z.rangeCheckHelper(UInt32.NUM_BITS).assertEquals(z); + Gadgets.rangeCheck32(z); return new UInt32(z); } + + /** + * Bitwise XOR gadget on {@link UInt32} elements. Equivalent to the [bitwise XOR `^` operator in JavaScript](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Bitwise_XOR). + * A XOR gate works by comparing two bits and returning `1` if two bits differ, and `0` if two bits are equal. + * + * This gadget builds a chain of XOR gates recursively. + * + * You can find more details about the implementation in the [Mina book](https://o1-labs.github.io/proof-systems/specs/kimchi.html?highlight=gates#xor-1) + * + * @param x {@link UInt32} element to compare. + * + * @example + * ```ts + * let a = UInt32.from(0b0101); + * let b = UInt32.from(0b0011); + * + * let c = a.xor(b); + * c.assertEquals(0b0110); + * ``` + */ + xor(x: UInt32) { + return new UInt32(Gadgets.xor(this.value, x.value, UInt32.NUM_BITS)); + } + + /** + * Bitwise NOT gate on {@link UInt32} elements. Similar to the [bitwise + * NOT `~` operator in JavaScript](https://developer.mozilla.org/en-US/docs/ + * Web/JavaScript/Reference/Operators/Bitwise_NOT). + * + * **Note:** The NOT gate operates over 32 bit for UInt32 types. + * + * A NOT gate works by returning `1` in each bit position if the + * corresponding bit of the operand is `0`, and returning `0` if the + * corresponding bit of the operand is `1`. + * + * NOT is implemented as a subtraction of the input from the all one bitmask. + * + * You can find more details about the implementation in the [Mina book](https://o1-labs.github.io/proof-systems/specs/kimchi.html?highlight=gates#not) + * + * @example + * ```ts + * // NOTing 4 bits with the unchecked version + * let a = UInt32.from(0b0101); + * let b = a.not(); + * + * console.log(b.toBigInt().toString(2)); + * // 11111111111111111111111111111010 + * ``` + * + * @param a - The value to apply NOT to. + */ + not() { + return new UInt32(Gadgets.not(this.value, UInt32.NUM_BITS, false)); + } + + /** + * A (left and right) rotation operates similarly to the shift operation (`<<` for left and `>>` for right) in JavaScript, + * with the distinction that the bits are circulated to the opposite end of a 64-bit representation rather than being discarded. + * For a left rotation, this means that bits shifted off the left end reappear at the right end. + * Conversely, for a right rotation, bits shifted off the right end reappear at the left end. + * + * It’s important to note that these operations are performed considering the big-endian 64-bit representation of the number, + * where the most significant (64th) bit is on the left end and the least significant bit is on the right end. + * The `direction` parameter is a string that accepts either `'left'` or `'right'`, determining the direction of the rotation. + * + * To safely use `rotate()`, you need to make sure that the value passed in is range-checked to 64 bits; + * for example, using {@link Gadgets.rangeCheck64}. + * + * You can find more details about the implementation in the [Mina book](https://o1-labs.github.io/proof-systems/specs/kimchi.html?highlight=gates#rotation) + * + * @param bits amount of bits to rotate this {@link UInt32} element with. + * @param direction left or right rotation direction. + * + * + * @example + * ```ts + * const x = UInt32.from(0b001100); + * const y = x.rotate(2, 'left'); + * const z = x.rotate(2, 'right'); // right rotation by 2 bits + * y.assertEquals(0b110000); + * z.assertEquals(0b000011); + * ``` + */ + rotate(bits: number, direction: 'left' | 'right' = 'left') { + return new UInt32(Gadgets.rotate32(this.value, bits, direction)); + } + + /** + * Performs a left shift operation on the provided {@link UInt32} element. + * This operation is similar to the `<<` shift operation in JavaScript, + * where bits are shifted to the left, and the overflowing bits are discarded. + * + * It’s important to note that these operations are performed considering the big-endian 32-bit representation of the number, + * where the most significant (32th) bit is on the left end and the least significant bit is on the right end. + * + * The operation expects the input to be range checked to 32 bit. + * + * @param bits Amount of bits to shift the {@link UInt32} element to the left. The amount should be between 0 and 32 (or else the shift will fail). + * + * @example + * ```ts + * const x = UInt32.from(0b001100); // 12 in binary + * const y = x.leftShift(2); // left shift by 2 bits + * y.assertEquals(0b110000); // 48 in binary + * ``` + */ + leftShift(bits: number) { + return new UInt32(Gadgets.leftShift32(this.value, bits)); + } + + /** + * Performs a left right operation on the provided {@link UInt32} element. + * This operation is similar to the `>>` shift operation in JavaScript, + * where bits are shifted to the right, and the overflowing bits are discarded. + * + * It’s important to note that these operations are performed considering the big-endian 32-bit representation of the number, + * where the most significant (32th) bit is on the left end and the least significant bit is on the right end. + * + * @param bits Amount of bits to shift the {@link UInt32} element to the right. The amount should be between 0 and 32 (or else the shift will fail). + * + * The operation expects the input to be range checked to 32 bit. + * + * @example + * ```ts + * const x = UInt32.from(0b001100); // 12 in binary + * const y = x.rightShift(2); // left shift by 2 bits + * y.assertEquals(0b000011); // 48 in binary + * ``` + */ + rightShift(bits: number) { + return new UInt32(Gadgets.rightShift64(this.value, bits)); + } + + /** + * Bitwise AND gadget on {@link UInt32} elements. Equivalent to the [bitwise AND `&` operator in JavaScript](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Bitwise_AND). + * The AND gate works by comparing two bits and returning `1` if both bits are `1`, and `0` otherwise. + * + * It can be checked by a double generic gate that verifies the following relationship between the values below. + * + * The generic gate verifies:\ + * `a + b = sum` and the conjunction equation `2 * and = sum - xor`\ + * Where:\ + * `a + b = sum`\ + * `a ^ b = xor`\ + * `a & b = and` + * + * You can find more details about the implementation in the [Mina book](https://o1-labs.github.io/proof-systems/specs/kimchi.html?highlight=gates#and) + * + * + * @example + * ```typescript + * let a = UInt32.from(3); // ... 000011 + * let b = UInt32.from(5); // ... 000101 + * + * let c = a.and(b, 2); // ... 000001 + * c.assertEquals(1); + * ``` + */ + and(x: UInt32) { + return new UInt32(Gadgets.and(this.value, x.value, UInt32.NUM_BITS)); + } + /** * @deprecated Use {@link lessThanOrEqual} instead. * @@ -550,12 +885,8 @@ class UInt32 extends CircuitValue { } else { let xMinusY = this.value.sub(y.value).seal(); let yMinusX = xMinusY.neg(); - let xMinusYFits = xMinusY - .rangeCheckHelper(UInt32.NUM_BITS) - .equals(xMinusY); - let yMinusXFits = yMinusX - .rangeCheckHelper(UInt32.NUM_BITS) - .equals(yMinusX); + let xMinusYFits = Gadgets.isInRangeN(UInt32.NUM_BITS, xMinusY); + let yMinusXFits = Gadgets.isInRangeN(UInt32.NUM_BITS, yMinusX); xMinusYFits.or(yMinusXFits).assertEquals(true); // x <= y if y - x fits in 64 bits return yMinusXFits; @@ -571,12 +902,8 @@ class UInt32 extends CircuitValue { } else { let xMinusY = this.value.sub(y.value).seal(); let yMinusX = xMinusY.neg(); - let xMinusYFits = xMinusY - .rangeCheckHelper(UInt32.NUM_BITS) - .equals(xMinusY); - let yMinusXFits = yMinusX - .rangeCheckHelper(UInt32.NUM_BITS) - .equals(yMinusX); + let xMinusYFits = Gadgets.isInRangeN(UInt32.NUM_BITS, xMinusY); + let yMinusXFits = Gadgets.isInRangeN(UInt32.NUM_BITS, yMinusX); xMinusYFits.or(yMinusXFits).assertEquals(true); // x <= y if y - x fits in 64 bits return yMinusXFits; @@ -606,7 +933,7 @@ class UInt32 extends CircuitValue { return; } let yMinusX = y.value.sub(this.value).seal(); - yMinusX.rangeCheckHelper(UInt32.NUM_BITS).assertEquals(yMinusX, message); + Gadgets.rangeCheckN(UInt32.NUM_BITS, yMinusX, message); } /** @@ -720,8 +1047,8 @@ class Sign extends CircuitValue { // x^2 === 1 <=> x === 1 or x === -1 x.value.square().assertEquals(Field(1)); } - static emptyValue(): Sign { - return Sign.one; + static empty(): InstanceType { + return Sign.one as any; } static toInput(x: Sign): HashInput { return { packed: [[x.isPositive().toField(), 1]] }; @@ -959,3 +1286,387 @@ class Int64 extends CircuitValue implements BalanceChange { return this.sgn.isPositive(); } } + +/** + * A 8 bit unsigned integer with values ranging from 0 to 255. + */ +class UInt8 extends Struct({ + value: Field, +}) { + static NUM_BITS = 8; + + /** + * Create a {@link UInt8} from a bigint or number. + * The max value of a {@link UInt8} is `2^8 - 1 = 255`. + * + * **Warning**: Cannot overflow past 255, an error is thrown if the result is greater than 255. + */ + constructor(x: number | bigint | FieldVar | UInt8) { + if (x instanceof UInt8) x = x.value.value; + super({ value: Field(x) }); + UInt8.checkConstant(this.value); + } + + static Unsafe = { + /** + * Create a {@link UInt8} from a {@link Field} without constraining its range. + * + * **Warning**: This is unsafe, because it does not prove that the input {@link Field} actually fits in 8 bits.\ + * Only use this if you know what you are doing, otherwise use the safe {@link UInt8.from}. + */ + fromField(x: Field) { + return new UInt8(x.value); + }, + }; + + /** + * Add a {@link UInt8} to another {@link UInt8} without allowing overflow. + * + * @example + * ```ts + * const x = UInt8.from(3); + * const sum = x.add(5); + * sum.assertEquals(8); + * ``` + * + * @throws if the result is greater than 255. + */ + add(y: UInt8 | bigint | number) { + let z = this.value.add(UInt8.from(y).value); + Gadgets.rangeCheck8(z); + return UInt8.Unsafe.fromField(z); + } + + /** + * Subtract a {@link UInt8} from another {@link UInt8} without allowing underflow. + * + * @example + * ```ts + * const x = UInt8.from(8); + * const difference = x.sub(5); + * difference.assertEquals(3); + * ``` + * + * @throws if the result is less than 0. + */ + sub(y: UInt8 | bigint | number) { + let z = this.value.sub(UInt8.from(y).value); + Gadgets.rangeCheck8(z); + return UInt8.Unsafe.fromField(z); + } + + /** + * Multiply a {@link UInt8} by another {@link UInt8} without allowing overflow. + * + * @example + * ```ts + * const x = UInt8.from(3); + * const product = x.mul(5); + * product.assertEquals(15); + * ``` + * + * @throws if the result is greater than 255. + */ + mul(y: UInt8 | bigint | number) { + let z = this.value.mul(UInt8.from(y).value); + Gadgets.rangeCheck8(z); + return UInt8.Unsafe.fromField(z); + } + + /** + * Divide a {@link UInt8} by another {@link UInt8}. + * This is integer division that rounds down. + * + * @example + * ```ts + * const x = UInt8.from(7); + * const quotient = x.div(2); + * quotient.assertEquals(3); + * ``` + */ + div(y: UInt8 | bigint | number) { + return this.divMod(y).quotient; + } + + /** + * Get the remainder a {@link UInt8} of division of another {@link UInt8}. + * + * @example + * ```ts + * const x = UInt8.from(50); + * const mod = x.mod(30); + * mod.assertEquals(20); + * ``` + */ + mod(y: UInt8 | bigint | number) { + return this.divMod(y).remainder; + } + + /** + * Get the quotient and remainder of a {@link UInt8} divided by another {@link UInt8}: + * + * `x == y * q + r`, where `0 <= r < y`. + * + * @param y - a {@link UInt8} to get the quotient and remainder of another {@link UInt8}. + * + * @return The quotient `q` and remainder `r`. + */ + divMod(y: UInt8 | bigint | number) { + let x = this.value; + let y_ = UInt8.from(y).value.seal(); + + if (this.value.isConstant() && y_.isConstant()) { + let xn = x.toBigInt(); + let yn = y_.toBigInt(); + let q = xn / yn; + let r = xn - q * yn; + return { quotient: UInt8.from(q), remainder: UInt8.from(r) }; + } + + // prove that x === q * y + r, where 0 <= r < y + let q = Provable.witness(Field, () => Field(x.toBigInt() / y_.toBigInt())); + let r = x.sub(q.mul(y_)).seal(); + + // q, r being 16 bits is enough for them to be 8 bits, + // thanks to the === x check and the r < y check below + Gadgets.rangeCheck16(q); + Gadgets.rangeCheck16(r); + + let remainder = UInt8.Unsafe.fromField(r); + let quotient = UInt8.Unsafe.fromField(q); + + remainder.assertLessThan(y); + return { quotient, remainder }; + } + + /** + * Check if this {@link UInt8} is less than or equal to another {@link UInt8} value. + * Returns a {@link Bool}. + * + * @example + * ```ts + * UInt8.from(3).lessThanOrEqual(UInt8.from(5)); + * ``` + */ + lessThanOrEqual(y: UInt8 | bigint | number): Bool { + let y_ = UInt8.from(y); + if (this.value.isConstant() && y_.value.isConstant()) { + return Bool(this.toBigInt() <= y_.toBigInt()); + } + throw Error('Not implemented'); + } + + /** + * Check if this {@link UInt8} is less than another {@link UInt8} value. + * Returns a {@link Bool}. + * + * @example + * ```ts + * UInt8.from(2).lessThan(UInt8.from(3)); + * ``` + */ + lessThan(y: UInt8 | bigint | number): Bool { + let y_ = UInt8.from(y); + if (this.value.isConstant() && y_.value.isConstant()) { + return Bool(this.toBigInt() < y_.toBigInt()); + } + throw Error('Not implemented'); + } + + /** + * Assert that this {@link UInt8} is less than another {@link UInt8} value. + * + * **Important**: If an assertion fails, the code throws an error. + * + * @param y - the {@link UInt8} value to compare & assert with this {@link UInt8}. + * @param message? - a string error message to print if the assertion fails, optional. + */ + assertLessThan(y: UInt8 | bigint | number, message?: string) { + let y_ = UInt8.from(y); + if (this.value.isConstant() && y_.value.isConstant()) { + let x0 = this.toBigInt(); + let y0 = y_.toBigInt(); + if (x0 >= y0) { + if (message !== undefined) throw Error(message); + throw Error(`UInt8.assertLessThan: expected ${x0} < ${y0}`); + } + return; + } + // x < y <=> x + 1 <= y + let xPlus1 = new UInt8(this.value.add(1).value); + xPlus1.assertLessThanOrEqual(y, message); + } + + /** + * Assert that this {@link UInt8} is less than or equal to another {@link UInt8} value. + * + * **Important**: If an assertion fails, the code throws an error. + * + * @param y - the {@link UInt8} value to compare & assert with this {@link UInt8}. + * @param message? - a string error message to print if the assertion fails, optional. + */ + assertLessThanOrEqual(y: UInt8 | bigint | number, message?: string) { + let y_ = UInt8.from(y); + if (this.value.isConstant() && y_.value.isConstant()) { + let x0 = this.toBigInt(); + let y0 = y_.toBigInt(); + if (x0 > y0) { + if (message !== undefined) throw Error(message); + throw Error(`UInt8.assertLessThanOrEqual: expected ${x0} <= ${y0}`); + } + return; + } + try { + // x <= y <=> y - x >= 0 which is implied by y - x in [0, 2^16) + let yMinusX = y_.value.sub(this.value).seal(); + Gadgets.rangeCheck16(yMinusX); + } catch (err) { + throw withMessage(err, message); + } + } + + /** + * Check if this {@link UInt8} is greater than another {@link UInt8}. + * Returns a {@link Bool}. + * + * @example + * ```ts + * // 5 > 3 + * UInt8.from(5).greaterThan(3); + * ``` + */ + greaterThan(y: UInt8 | bigint | number) { + return UInt8.from(y).lessThan(this); + } + + /** + * Check if this {@link UInt8} is greater than or equal another {@link UInt8} value. + * Returns a {@link Bool}. + * + * @example + * ```ts + * // 3 >= 3 + * UInt8.from(3).greaterThanOrEqual(3); + * ``` + */ + greaterThanOrEqual(y: UInt8 | bigint | number) { + return UInt8.from(y).lessThanOrEqual(this); + } + + /** + * Assert that this {@link UInt8} is greater than another {@link UInt8} value. + * + * **Important**: If an assertion fails, the code throws an error. + * + * @param y - the {@link UInt8} value to compare & assert with this {@link UInt8}. + * @param message? - a string error message to print if the assertion fails, optional. + */ + assertGreaterThan(y: UInt8 | bigint | number, message?: string) { + UInt8.from(y).assertLessThan(this, message); + } + + /** + * Assert that this {@link UInt8} is greater than or equal to another {@link UInt8} value. + * + * **Important**: If an assertion fails, the code throws an error. + * + * @param y - the {@link UInt8} value to compare & assert with this {@link UInt8}. + * @param message? - a string error message to print if the assertion fails, optional. + */ + assertGreaterThanOrEqual(y: UInt8, message?: string) { + UInt8.from(y).assertLessThanOrEqual(this, message); + } + + /** + * Assert that this {@link UInt8} is equal another {@link UInt8} value. + * + * **Important**: If an assertion fails, the code throws an error. + * + * @param y - the {@link UInt8} value to compare & assert with this {@link UInt8}. + * @param message? - a string error message to print if the assertion fails, optional. + */ + assertEquals(y: UInt8 | bigint | number, message?: string) { + let y_ = UInt8.from(y); + this.value.assertEquals(y_.value, message); + } + + /** + * Serialize the {@link UInt8} to a string, e.g. for printing. + * + * **Warning**: This operation is not provable. + */ + toString() { + return this.value.toString(); + } + + /** + * Serialize the {@link UInt8} to a number. + * + * **Warning**: This operation is not provable. + */ + toNumber() { + return Number(this.value.toBigInt()); + } + + /** + * Serialize the {@link UInt8} to a bigint. + * + * **Warning**: This operation is not provable. + */ + toBigInt() { + return this.value.toBigInt(); + } + + /** + * {@link Provable.check} for {@link UInt8}. + * Proves that the input is in the [0, 255] range. + */ + static check(x: { value: Field } | Field) { + if (x instanceof Field) x = { value: x }; + Gadgets.rangeCheck8(x.value); + } + + static toInput(x: { value: Field }): HashInput { + return { packed: [[x.value, 8]] }; + } + + /** + * Turns a {@link UInt8} into a {@link UInt32}. + */ + toUInt32(): UInt32 { + return new UInt32(this.value); + } + + /** + * Turns a {@link UInt8} into a {@link UInt64}. + */ + toUInt64(): UInt64 { + return new UInt64(this.value); + } + + /** + * Creates a {@link UInt8} with a value of 255. + */ + static MAXINT() { + return new UInt8((1n << BigInt(UInt8.NUM_BITS)) - 1n); + } + + /** + * Creates a new {@link UInt8}. + */ + static from(x: UInt8 | UInt64 | UInt32 | Field | number | bigint) { + if (x instanceof UInt8) return x; + if (x instanceof UInt64 || x instanceof UInt32 || x instanceof Field) { + // if the input could be larger than 8 bits, we have to prove that it is not + let xx = x instanceof Field ? { value: x } : x; + UInt8.check(xx); + return new UInt8(xx.value.value); + } + return new UInt8(x); + } + + private static checkConstant(x: Field) { + if (!x.isConstant()) return; + Gadgets.rangeCheck8(x); + } +} diff --git a/src/lib/keccak.ts b/src/lib/keccak.ts new file mode 100644 index 0000000000..ccc8c45548 --- /dev/null +++ b/src/lib/keccak.ts @@ -0,0 +1,550 @@ +import { Field } from './field.js'; +import { Gadgets } from './gadgets/gadgets.js'; +import { assert } from './errors.js'; +import { Provable } from './provable.js'; +import { chunk } from './util/arrays.js'; +import { FlexibleBytes } from './provable-types/bytes.js'; +import { UInt8 } from './int.js'; +import { Bytes } from './provable-types/provable-types.js'; + +export { Keccak }; + +const Keccak = { + /** + * Implementation of [NIST SHA-3](https://csrc.nist.gov/pubs/fips/202/final) Hash Function. + * Supports output lengths of 256, 384, or 512 bits. + * + * Applies the SHA-3 hash function to a list of big-endian byte-sized {@link Field} elements, flexible to handle varying output lengths (256, 384, 512 bits) as specified. + * + * The function accepts {@link Bytes} as the input message, which is a type that represents a static-length list of byte-sized field elements (range-checked using {@link Gadgets.rangeCheck8}). + * Alternatively, you can pass plain `number[]` of `Uint8Array` to perform a hash outside provable code. + * + * Produces an output of {@link Bytes} that conforms to the chosen bit length. + * Both input and output bytes are big-endian. + * + * @param len - Desired output length in bits. Valid options: 256, 384, 512. + * @param message - Big-endian {@link Bytes} representing the message to hash. + * + * ```ts + * let preimage = Bytes.fromString("hello world"); + * let digest256 = Keccak.nistSha3(256, preimage); + * let digest384 = Keccak.nistSha3(384, preimage); + * let digest512 = Keccak.nistSha3(512, preimage); + * ``` + * + */ + nistSha3(len: 256 | 384 | 512, message: FlexibleBytes) { + return nistSha3(len, Bytes.from(message)); + }, + /** + * Ethereum-Compatible Keccak-256 Hash Function. + * This is a specialized variant of {@link Keccak.preNist} configured for a 256-bit output length. + * + * Primarily used in Ethereum for hashing transactions, messages, and other types of payloads. + * + * The function accepts {@link Bytes} as the input message, which is a type that represents a static-length list of byte-sized field elements (range-checked using {@link Gadgets.rangeCheck8}). + * Alternatively, you can pass plain `number[]` of `Uint8Array` to perform a hash outside provable code. + * + * Produces an output of {@link Bytes} of length 32. Both input and output bytes are big-endian. + * + * @param message - Big-endian {@link Bytes} representing the message to hash. + * + * ```ts + * let preimage = Bytes.fromString("hello world"); + * let digest = Keccak.ethereum(preimage); + * ``` + */ + ethereum(message: FlexibleBytes) { + return ethereum(Bytes.from(message)); + }, + /** + * Implementation of [pre-NIST Keccak](https://keccak.team/keccak.html) hash function. + * Supports output lengths of 256, 384, or 512 bits. + * + * Keccak won the SHA-3 competition and was slightly altered before being standardized as SHA-3 by NIST in 2015. + * This variant was used in Ethereum before the NIST standardization, by specifying `len` as 256 bits you can obtain the same hash function as used by Ethereum {@link Keccak.ethereum}. + * + * The function applies the pre-NIST Keccak hash function to a list of byte-sized {@link Field} elements and is flexible to handle varying output lengths (256, 384, 512 bits) as specified. + * + * {@link Keccak.preNist} accepts {@link Bytes} as the input message, which is a type that represents a static-length list of byte-sized field elements (range-checked using {@link Gadgets.rangeCheck8}). + * Alternatively, you can pass plain `number[]` of `Uint8Array` to perform a hash outside provable code. + * + * Produces an output of {@link Bytes} that conforms to the chosen bit length. + * Both input and output bytes are big-endian. + * + * @param len - Desired output length in bits. Valid options: 256, 384, 512. + * @param message - Big-endian {@link Bytes} representing the message to hash. + * + * ```ts + * let preimage = Bytes.fromString("hello world"); + * let digest256 = Keccak.preNist(256, preimage); + * let digest384 = Keccak.preNist(384, preimage); + * let digest512= Keccak.preNist(512, preimage); + * ``` + * + */ + preNist(len: 256 | 384 | 512, message: FlexibleBytes) { + return preNist(len, Bytes.from(message)); + }, +}; + +// KECCAK CONSTANTS + +// Length of the square matrix side of Keccak states +const KECCAK_DIM = 5; + +// Value `l` in Keccak, ranges from 0 to 6 and determines the lane width +const KECCAK_ELL = 6; + +// Width of a lane of the state, meaning the length of each word in bits (64) +const KECCAK_WORD = 2 ** KECCAK_ELL; + +// Number of bytes that fit in a word (8) +const BYTES_PER_WORD = KECCAK_WORD / 8; + +// Length of the state in words, 5x5 = 25 +const KECCAK_STATE_LENGTH_WORDS = KECCAK_DIM ** 2; + +// Length of the state in bits, meaning the 5x5 matrix of words in bits (1600) +const KECCAK_STATE_LENGTH = KECCAK_STATE_LENGTH_WORDS * KECCAK_WORD; + +// Length of the state in bytes, meaning the 5x5 matrix of words in bytes (200) +const KECCAK_STATE_LENGTH_BYTES = KECCAK_STATE_LENGTH / 8; + +// Creates the 5x5 table of rotation offset for Keccak modulo 64 +// | i \ j | 0 | 1 | 2 | 3 | 4 | +// | ----- | -- | -- | -- | -- | -- | +// | 0 | 0 | 36 | 3 | 41 | 18 | +// | 1 | 1 | 44 | 10 | 45 | 2 | +// | 2 | 62 | 6 | 43 | 15 | 61 | +// | 3 | 28 | 55 | 25 | 21 | 56 | +// | 4 | 27 | 20 | 39 | 8 | 14 | +const ROT_TABLE = [ + [0, 36, 3, 41, 18], + [1, 44, 10, 45, 2], + [62, 6, 43, 15, 61], + [28, 55, 25, 21, 56], + [27, 20, 39, 8, 14], +]; + +// Round constants for Keccak +// From https://keccak.team/files/Keccak-reference-3.0.pdf +const ROUND_CONSTANTS = [ + 0x0000000000000001n, + 0x0000000000008082n, + 0x800000000000808an, + 0x8000000080008000n, + 0x000000000000808bn, + 0x0000000080000001n, + 0x8000000080008081n, + 0x8000000000008009n, + 0x000000000000008an, + 0x0000000000000088n, + 0x0000000080008009n, + 0x000000008000000an, + 0x000000008000808bn, + 0x800000000000008bn, + 0x8000000000008089n, + 0x8000000000008003n, + 0x8000000000008002n, + 0x8000000000000080n, + 0x000000000000800an, + 0x800000008000000an, + 0x8000000080008081n, + 0x8000000000008080n, + 0x0000000080000001n, + 0x8000000080008008n, +]; + +// KECCAK HASH FUNCTION + +// Computes the number of required extra bytes to pad a message of length bytes +function bytesToPad(rate: number, length: number): number { + return rate - (length % rate); +} + +// Pads a message M as: +// M || pad[x](|M|) +// The padded message will start with the message argument followed by the padding rule (below) to fulfill a length that is a multiple of rate (in bytes). +// If nist is true, then the padding rule is 0x06 ..0*..1. +// If nist is false, then the padding rule is 10*1. +function pad(message: UInt8[], rate: number, nist: boolean): UInt8[] { + // Find out desired length of the padding in bytes + // If message is already rate bits, need to pad full rate again + const extraBytes = bytesToPad(rate, message.length); + + // 0x06 0x00 ... 0x00 0x80 or 0x86 + const first = nist ? 0x06n : 0x01n; + const last = 0x80n; + + // Create the padding vector + const pad = Array(extraBytes).fill(UInt8.from(0)); + pad[0] = UInt8.from(first); + pad[extraBytes - 1] = pad[extraBytes - 1].add(last); + + // Return the padded message + return [...message, ...pad]; +} + +// ROUND TRANSFORMATION + +// First algorithm in the compression step of Keccak for 64-bit words. +// C[i] = A[i,0] xor A[i,1] xor A[i,2] xor A[i,3] xor A[i,4] +// D[i] = C[i-1] xor ROT(C[i+1], 1) +// E[i,j] = A[i,j] xor D[i] +// In the Keccak reference, it corresponds to the `theta` algorithm. +// We use the first index of the state array as the i coordinate and the second index as the j coordinate. +const theta = (state: Field[][]): Field[][] => { + const stateA = state; + + // XOR the elements of each row together + // for all i in {0..4}: C[i] = A[i,0] xor A[i,1] xor A[i,2] xor A[i,3] xor A[i,4] + const stateC = stateA.map((row) => row.reduce(xor)); + + // for all i in {0..4}: D[i] = C[i-1] xor ROT(C[i+1], 1) + const stateD = Array.from({ length: KECCAK_DIM }, (_, i) => + xor( + stateC[(i + KECCAK_DIM - 1) % KECCAK_DIM], + Gadgets.rotate64(stateC[(i + 1) % KECCAK_DIM], 1, 'left') + ) + ); + + // for all i in {0..4} and j in {0..4}: E[i,j] = A[i,j] xor D[i] + const stateE = stateA.map((row, index) => + row.map((elem) => xor(elem, stateD[index])) + ); + + return stateE; +}; + +// Second and third steps in the compression step of Keccak for 64-bit words. +// pi: A[i,j] = ROT(E[i,j], r[i,j]) +// rho: A[i,j] = A'[j, 2i+3j mod KECCAK_DIM] +// piRho: B[j,2i+3j] = ROT(E[i,j], r[i,j]) +// which is equivalent to the `rho` algorithm followed by the `pi` algorithm in the Keccak reference as follows: +// rho: +// A[0,0] = a[0,0] +// | i | = | 1 | +// | j | = | 0 | +// for t = 0 to 23 do +// A[i,j] = ROT(a[i,j], (t+1)(t+2)/2 mod 64))) +// | i | = | 0 1 | | i | +// | | = | | * | | +// | j | = | 2 3 | | j | +// end for +// pi: +// for i = 0 to 4 do +// for j = 0 to 4 do +// | I | = | 0 1 | | i | +// | | = | | * | | +// | J | = | 2 3 | | j | +// A[I,J] = a[i,j] +// end for +// end for +// We use the first index of the state array as the i coordinate and the second index as the j coordinate. +function piRho(state: Field[][]): Field[][] { + const stateE = state; + const stateB = State.zeros(); + + // for all i in {0..4} and j in {0..4}: B[j,2i+3j] = ROT(E[i,j], r[i,j]) + for (let i = 0; i < KECCAK_DIM; i++) { + for (let j = 0; j < KECCAK_DIM; j++) { + stateB[j][(2 * i + 3 * j) % KECCAK_DIM] = Gadgets.rotate64( + stateE[i][j], + ROT_TABLE[i][j], + 'left' + ); + } + } + + return stateB; +} + +// Fourth step of the compression function of Keccak for 64-bit words. +// F[i,j] = B[i,j] xor ((not B[i+1,j]) and B[i+2,j]) +// It corresponds to the chi algorithm in the Keccak reference. +// for j = 0 to 4 do +// for i = 0 to 4 do +// A[i,j] = a[i,j] xor ((not a[i+1,j]) and a[i+2,j]) +// end for +// end for +function chi(state: Field[][]): Field[][] { + const stateB = state; + const stateF = State.zeros(); + + // for all i in {0..4} and j in {0..4}: F[i,j] = B[i,j] xor ((not B[i+1,j]) and B[i+2,j]) + for (let i = 0; i < KECCAK_DIM; i++) { + for (let j = 0; j < KECCAK_DIM; j++) { + stateF[i][j] = xor( + stateB[i][j], + Gadgets.and( + // We can use unchecked NOT because the length of the input is constrained to be 64 bits thanks to the fact that it is the output of a previous Xor64 + Gadgets.not(stateB[(i + 1) % KECCAK_DIM][j], KECCAK_WORD, false), + stateB[(i + 2) % KECCAK_DIM][j], + KECCAK_WORD + ) + ); + } + } + + return stateF; +} + +// Fifth step of the permutation function of Keccak for 64-bit words. +// It takes the word located at the position (0,0) of the state and XORs it with the round constant. +function iota(state: Field[][], rc: bigint): Field[][] { + const stateG = state; + + stateG[0][0] = xor(stateG[0][0], Field.from(rc)); + + return stateG; +} + +// One round of the Keccak permutation function. +// iota o chi o pi o rho o theta +function round(state: Field[][], rc: bigint): Field[][] { + const stateA = state; + const stateE = theta(stateA); + const stateB = piRho(stateE); + const stateF = chi(stateB); + const stateD = iota(stateF, rc); + return stateD; +} + +// Keccak permutation function with a constant number of rounds +function permutation(state: Field[][], rcs: bigint[]): Field[][] { + return rcs.reduce((state, rc) => round(state, rc), state); +} + +// KECCAK SPONGE + +// Absorb padded message into a keccak state with given rate and capacity +function absorb( + paddedMessage: Field[], + capacity: number, + rate: number, + rc: bigint[] +): State { + assert( + rate + capacity === KECCAK_STATE_LENGTH_WORDS, + `invalid rate or capacity (rate + capacity should be ${KECCAK_STATE_LENGTH_WORDS})` + ); + assert( + paddedMessage.length % rate === 0, + 'invalid padded message length (should be multiple of rate)' + ); + + let state = State.zeros(); + + // array of capacity zero words + const zeros = Array(capacity).fill(Field.from(0)); + + for (let idx = 0; idx < paddedMessage.length; idx += rate) { + // split into blocks of rate words + const block = paddedMessage.slice(idx, idx + rate); + // pad the block with 0s to up to KECCAK_STATE_LENGTH_WORDS words + const paddedBlock = block.concat(zeros); + // convert the padded block to a Keccak state + const blockState = State.fromWords(paddedBlock); + // xor the state with the padded block + const stateXor = State.xor(state, blockState); + // apply the permutation function to the xored state + state = permutation(stateXor, rc); + } + return state; +} + +// Squeeze state until it has a desired length in words +function squeeze(state: State, length: number, rate: number): Field[] { + // number of squeezes + const squeezes = Math.floor(length / rate) + 1; + assert(squeezes === 1, 'squeezes should be 1'); + + // Obtain the hash selecting the first `length` words of the output array + const words = State.toWords(state); + const hashed = words.slice(0, length); + return hashed; +} + +// Keccak sponge function for 200 bytes of state width +function sponge( + paddedMessage: Field[], + length: number, + capacity: number, + rate: number +): Field[] { + // check that the padded message is a multiple of rate + assert(paddedMessage.length % rate === 0, 'Invalid padded message length'); + + // absorb + const state = absorb(paddedMessage, capacity, rate, ROUND_CONSTANTS); + + // squeeze + const hashed = squeeze(state, length, rate); + return hashed; +} + +// Keccak hash function with input message passed as list of Field bytes. +// The message will be parsed as follows: +// - the first byte of the message will be the least significant byte of the first word of the state (A[0][0]) +// - the 10*1 pad will take place after the message, until reaching the bit length rate. +// - then, {0} pad will take place to finish the 200 bytes of the state. +function hash( + message: Bytes, + length: number, + capacity: number, + nistVersion: boolean +): UInt8[] { + // Throw errors if used improperly + assert(capacity > 0, 'capacity must be positive'); + assert( + capacity < KECCAK_STATE_LENGTH_BYTES, + `capacity must be less than ${KECCAK_STATE_LENGTH_BYTES}` + ); + assert(length > 0, 'length must be positive'); + + // convert capacity and length to word units + assert(capacity % BYTES_PER_WORD === 0, 'length must be a multiple of 8'); + capacity /= BYTES_PER_WORD; + assert(length % BYTES_PER_WORD === 0, 'length must be a multiple of 8'); + length /= BYTES_PER_WORD; + + const rate = KECCAK_STATE_LENGTH_WORDS - capacity; + + // apply padding, convert to words, and hash + const paddedBytes = pad(message.bytes, rate * BYTES_PER_WORD, nistVersion); + const padded = bytesToWords(paddedBytes); + + const hash = sponge(padded, length, capacity, rate); + const hashBytes = wordsToBytes(hash); + + return hashBytes; +} + +// Gadget for NIST SHA-3 function for output lengths 256/384/512. +function nistSha3(len: 256 | 384 | 512, message: Bytes): Bytes { + let bytes = hash(message, len / 8, len / 4, true); + return BytesOfBitlength[len].from(bytes); +} + +// Gadget for pre-NIST SHA-3 function for output lengths 256/384/512. +// Note that when calling with output length 256 this is equivalent to the ethereum function +function preNist(len: 256 | 384 | 512, message: Bytes): Bytes { + let bytes = hash(message, len / 8, len / 4, false); + return BytesOfBitlength[len].from(bytes); +} + +// Gadget for Keccak hash function for the parameters used in Ethereum. +function ethereum(message: Bytes): Bytes { + return preNist(256, message); +} + +// FUNCTIONS ON KECCAK STATE + +type State = Field[][]; +const State = { + /** + * Create a state of all zeros + */ + zeros(): State { + return Array.from(Array(KECCAK_DIM), (_) => + Array(KECCAK_DIM).fill(Field.from(0)) + ); + }, + + /** + * Flatten state to words + */ + toWords(state: State): Field[] { + const words = Array(KECCAK_STATE_LENGTH_WORDS); + for (let j = 0; j < KECCAK_DIM; j++) { + for (let i = 0; i < KECCAK_DIM; i++) { + words[KECCAK_DIM * j + i] = state[i][j]; + } + } + return words; + }, + + /** + * Compose words to state + */ + fromWords(words: Field[]): State { + const state = State.zeros(); + for (let j = 0; j < KECCAK_DIM; j++) { + for (let i = 0; i < KECCAK_DIM; i++) { + state[i][j] = words[KECCAK_DIM * j + i]; + } + } + return state; + }, + + /** + * XOR two states together and return the result + */ + xor(a: State, b: State): State { + assert( + a.length === KECCAK_DIM && a[0].length === KECCAK_DIM, + `invalid \`a\` dimensions (should be ${KECCAK_DIM})` + ); + assert( + b.length === KECCAK_DIM && b[0].length === KECCAK_DIM, + `invalid \`b\` dimensions (should be ${KECCAK_DIM})` + ); + + // Calls xor() on each pair (i,j) of the states input1 and input2 and outputs the output Fields as a new matrix + return a.map((row, i) => row.map((x, j) => xor(x, b[i][j]))); + }, +}; + +// AUXILIARY TYPES + +class Bytes32 extends Bytes(32) {} +class Bytes48 extends Bytes(48) {} +class Bytes64 extends Bytes(64) {} + +const BytesOfBitlength = { + 256: Bytes32, + 384: Bytes48, + 512: Bytes64, +}; + +// AUXILARY FUNCTIONS + +// Auxiliary functions to check the composition of 8 byte values (LE) into a 64-bit word and create constraints for it + +function bytesToWord(wordBytes: UInt8[]): Field { + return wordBytes.reduce((acc, byte, idx) => { + const shift = 1n << BigInt(8 * idx); + return acc.add(byte.value.mul(shift)); + }, Field.from(0)); +} + +function wordToBytes(word: Field): UInt8[] { + let bytes = Provable.witness(Provable.Array(UInt8, BYTES_PER_WORD), () => { + let w = word.toBigInt(); + return Array.from({ length: BYTES_PER_WORD }, (_, k) => + UInt8.from((w >> BigInt(8 * k)) & 0xffn) + ); + }); + + // check decomposition + bytesToWord(bytes).assertEquals(word); + + return bytes; +} + +function bytesToWords(bytes: UInt8[]): Field[] { + return chunk(bytes, BYTES_PER_WORD).map(bytesToWord); +} + +function wordsToBytes(words: Field[]): UInt8[] { + return words.flatMap(wordToBytes); +} + +// xor which avoids doing anything on 0 inputs +// (but doesn't range-check the other input in that case) +function xor(x: Field, y: Field): Field { + if (x.isConstant() && x.toBigInt() === 0n) return y; + if (y.isConstant() && y.toBigInt() === 0n) return x; + return Gadgets.xor(x, y, 64); +} diff --git a/src/lib/keccak.unit-test.ts b/src/lib/keccak.unit-test.ts new file mode 100644 index 0000000000..aad02e2342 --- /dev/null +++ b/src/lib/keccak.unit-test.ts @@ -0,0 +1,271 @@ +import { Keccak } from './keccak.js'; +import { ZkProgram } from './proof_system.js'; +import { + equivalentProvable, + equivalent, + equivalentAsync, +} from './testing/equivalent.js'; +import { + keccak_224, + keccak_256, + keccak_384, + keccak_512, + sha3_224, + sha3_256, + sha3_384, + sha3_512, +} from '@noble/hashes/sha3'; +import { Bytes } from './provable-types/provable-types.js'; +import { bytes } from './gadgets/test-utils.js'; +import { UInt8 } from './int.js'; +import { test, Random, sample } from './testing/property.js'; +import { expect } from 'expect'; + +const RUNS = 1; + +const testImplementations = { + sha3: { + 224: sha3_224, + 256: sha3_256, + 384: sha3_384, + 512: sha3_512, + }, + preNist: { + 224: keccak_224, + 256: keccak_256, + 384: keccak_384, + 512: keccak_512, + }, +}; + +const lengths = [256, 384, 512] as const; + +// EQUIVALENCE TESTS AGAINST REF IMPLEMENTATION + +// checks outside circuit +// TODO: fix witness generation slowness + +for (let length of lengths) { + let [preimageLength] = sample(Random.nat(100), 1); + console.log(`Testing ${length} with preimage length ${preimageLength}`); + let inputBytes = bytes(preimageLength); + let outputBytes = bytes(length / 8); + + equivalentProvable({ from: [inputBytes], to: outputBytes, verbose: true })( + testImplementations.sha3[length], + (x) => Keccak.nistSha3(length, x), + `sha3 ${length}` + ); + + equivalentProvable({ from: [inputBytes], to: outputBytes, verbose: true })( + testImplementations.preNist[length], + (x) => Keccak.preNist(length, x), + `keccak ${length}` + ); + + // bytes to hex roundtrip + equivalent({ from: [inputBytes], to: inputBytes })( + (x) => x, + (x) => Bytes.fromHex(x.toHex()), + `Bytes toHex` + ); +} + +// EQUIVALENCE TESTS AGAINST TEST VECTORS (at the bottom) + +for (let { nist, length, message, expected } of testVectors()) { + let Hash = nist ? Keccak.nistSha3 : Keccak.preNist; + let actual = Hash(length, Bytes.fromHex(message)); + expect(actual).toEqual(Bytes.fromHex(expected)); +} + +// MISC QUICK TESTS + +// Test constructor +test(Random.uint8, Random.uint8, (x, y, assert) => { + let z = new UInt8(x); + assert(z instanceof UInt8); + assert(z.toBigInt() === x); + assert(z.toString() === x.toString()); + + assert((z = new UInt8(x)) instanceof UInt8 && z.toBigInt() === x); + assert((z = new UInt8(z)) instanceof UInt8 && z.toBigInt() === x); + assert((z = new UInt8(z.value.value)) instanceof UInt8 && z.toBigInt() === x); + + z = new UInt8(y); + assert(z instanceof UInt8); + assert(z.toString() === y.toString()); +}); + +// handles all numbers up to 2^8 +test(Random.nat(255), (n, assert) => { + assert(UInt8.from(n).toString() === String(n)); +}); + +// throws on negative numbers +test.negative(Random.int(-10, -1), (x) => UInt8.from(x)); + +// throws on numbers >= 2^8 +test.negative(Random.uint8.invalid, (x) => UInt8.from(x)); + +// PROOF TESTS + +// Choose a test length at random +const digestLength = lengths[Math.floor(Math.random() * 3)]; + +// Digest length in bytes +const digestLengthBytes = digestLength / 8; + +const preImageLength = 32; + +// No need to test Ethereum because it's just a special case of preNist +const KeccakProgram = ZkProgram({ + name: `keccak-test-${digestLength}`, + publicInput: Bytes(preImageLength).provable, + publicOutput: Bytes(digestLengthBytes).provable, + methods: { + nistSha3: { + privateInputs: [], + method(preImage: Bytes) { + return Keccak.nistSha3(digestLength, preImage); + }, + }, + preNist: { + privateInputs: [], + method(preImage: Bytes) { + return Keccak.preNist(digestLength, preImage); + }, + }, + }, +}); + +await KeccakProgram.compile(); + +// SHA-3 +await equivalentAsync( + { + from: [bytes(preImageLength)], + to: bytes(digestLengthBytes), + }, + { runs: RUNS } +)(testImplementations.sha3[digestLength], async (x) => { + const proof = await KeccakProgram.nistSha3(x); + await KeccakProgram.verify(proof); + return proof.publicOutput; +}); + +// PreNIST Keccak +await equivalentAsync( + { + from: [bytes(preImageLength)], + to: bytes(digestLengthBytes), + }, + { runs: RUNS } +)(testImplementations.preNist[digestLength], async (x) => { + const proof = await KeccakProgram.preNist(x); + await KeccakProgram.verify(proof); + return proof.publicOutput; +}); + +// TEST VECTORS + +function testVectors(): { + nist: boolean; + length: 256 | 384 | 512; + message: string; + expected: string; +}[] { + return [ + { + nist: false, + length: 256, + message: '30', + expected: + '044852b2a670ade5407e78fb2863c51de9fcb96542a07186fe3aeda6bb8a116d', + }, + { + nist: true, + length: 512, + message: '30', + expected: + '2d44da53f305ab94b6365837b9803627ab098c41a6013694f9b468bccb9c13e95b3900365eb58924de7158a54467e984efcfdabdbcc9af9a940d49c51455b04c', + }, + { + nist: false, + length: 256, + message: + '4920616d20746865206f776e6572206f6620746865204e465420776974682069642058206f6e2074686520457468657265756d20636861696e', + expected: + '63858e0487687c3eeb30796a3e9307680e1b81b860b01c88ff74545c2c314e36', + }, + { + nist: false, + length: 256, + message: + '044852b2a670ade5407e78fb2863c51de9fcb96542a07186fe3aeda6bb8a116df9e2eaaa42d9fe9e558a9b8ef1bf366f190aacaa83bad2641ee106e9041096e42d44da53f305ab94b6365837b9803627ab098c41a6013694f9b468bccb9c13e95b3900365eb58924de7158a54467e984efcfdabdbcc9af9a940d49c51455b04c63858e0487687c3eeb30796a3e9307680e1b81b860b01c88ff74545c2c314e36', + expected: + '560deb1d387f72dba729f0bd0231ad45998dda4b53951645322cf95c7b6261d9', + }, + { + nist: true, + length: 256, + message: + '044852b2a670ade5407e78fb2863c51de9fcb96542a07186fe3aeda6bb8a116df9e2eaaa42d9fe9e558a9b8ef1bf366f190aacaa83bad2641ee106e9041096e42d44da53f305ab94b6365837b9803627ab098c41a6013694f9b468bccb9c13e95b3900365eb58924de7158a54467e984efcfdabdbcc9af9a940d49c51455b04c63858e0487687c3eeb30796a3e9307680e1b81b860b01c88ff74545c2c314e36', + expected: + '1784354c4bbfa5f54e5db23041089e65a807a7b970e3cfdba95e2fbe63b1c0e4', + }, + { + nist: false, + length: 256, + message: + '391ccf9b5de23bb86ec6b2b142adb6e9ba6bee8519e7502fb8be8959fbd2672934cc3e13b7b45bf2b8a5cb48881790a7438b4a326a0c762e31280711e6b64fcc2e3e4e631e501d398861172ea98603618b8f23b91d0208b0b992dfe7fdb298b6465adafbd45e4f88ee9dc94e06bc4232be91587f78572c169d4de4d8b95b714ea62f1fbf3c67a4', + expected: + '7d5655391ede9ca2945f32ad9696f464be8004389151ce444c89f688278f2e1d', + }, + { + nist: false, + length: 256, + message: + 'ff391ccf9b5de23bb86ec6b2b142adb6e9ba6bee8519e7502fb8be8959fbd2672934cc3e13b7b45bf2b8a5cb48881790a7438b4a326a0c762e31280711e6b64fcc2e3e4e631e501d398861172ea98603618b8f23b91d0208b0b992dfe7fdb298b6465adafbd45e4f88ee9dc94e06bc4232be91587f78572c169d4de4d8b95b714ea62f1fbf3c67a4', + expected: + '37694fd4ba137be747eb25a85b259af5563e0a7a3010d42bd15963ac631b9d3f', + }, + { + nist: false, + length: 256, + message: + '80000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001', + expected: + 'bbf1f49a2cc5678aa62196d0c3108d89425b81780e1e90bcec03b4fb5f834714', + }, + { + nist: false, + length: 256, + message: + '80000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001', + expected: + 'bbf1f49a2cc5678aa62196d0c3108d89425b81780e1e90bcec03b4fb5f834714', + }, + { + nist: false, + length: 256, + message: 'a2c0', + expected: + '9856642c690c036527b8274db1b6f58c0429a88d9f3b9298597645991f4f58f0', + }, + { + nist: false, + length: 256, + message: '0a2c', + expected: + '295b48ad49eff61c3abfd399c672232434d89a4ef3ca763b9dbebb60dbb32a8b', + }, + { + nist: false, + length: 256, + message: '00', + expected: + 'bc36789e7a1e281436464229828f817d6612f7b477d66591ff96a9e064bcc98a', + }, + ]; +} diff --git a/src/lib/mina.ts b/src/lib/mina.ts index b1c9309581..e3b5823a9c 100644 --- a/src/lib/mina.ts +++ b/src/lib/mina.ts @@ -482,7 +482,7 @@ function LocalBlockchain({ account, update, commitments, - proofsEnabled + this.proofsEnabled ); } } @@ -587,7 +587,7 @@ function LocalBlockchain({ // and hopefully with upcoming work by Matt we can just run everything in the prover, and nowhere else let tx = createTransaction(sender, f, 0, { isFinalRunOutsideCircuit: false, - proofsEnabled, + proofsEnabled: this.proofsEnabled, fetchMode: 'test', }); let hasProofs = tx.transaction.accountUpdates.some( @@ -595,7 +595,7 @@ function LocalBlockchain({ ); return createTransaction(sender, f, 1, { isFinalRunOutsideCircuit: !hasProofs, - proofsEnabled, + proofsEnabled: this.proofsEnabled, }); }, applyJsonTransaction(json: string) { @@ -666,7 +666,7 @@ function LocalBlockchain({ networkState.totalCurrency = currency; }, setProofsEnabled(newProofsEnabled: boolean) { - proofsEnabled = newProofsEnabled; + this.proofsEnabled = newProofsEnabled; }, }; } @@ -677,24 +677,32 @@ LocalBlockchain satisfies (...args: any) => Mina; * Represents the Mina blockchain running on a real network */ function Network(graphqlEndpoint: string): Mina; -function Network(graphqlEndpoints: { +function Network(endpoints: { mina: string | string[]; - archive: string | string[]; + archive?: string | string[]; + lightnetAccountManager?: string; }): Mina; function Network( - input: { mina: string | string[]; archive: string | string[] } | string + input: + | { + mina: string | string[]; + archive?: string | string[]; + lightnetAccountManager?: string; + } + | string ): Mina { let accountCreationFee = UInt64.from(defaultAccountCreationFee); let minaGraphqlEndpoint: string; let archiveEndpoint: string; + let lightnetAccountManagerEndpoint: string; if (input && typeof input === 'string') { minaGraphqlEndpoint = input; Fetch.setGraphqlEndpoint(minaGraphqlEndpoint); } else if (input && typeof input === 'object') { - if (!input.mina || !input.archive) + if (!input.mina) throw new Error( - "Network: malformed input. Please provide an object with 'mina' and 'archive' endpoints." + "Network: malformed input. Please provide an object with 'mina' endpoint." ); if (Array.isArray(input.mina) && input.mina.length !== 0) { minaGraphqlEndpoint = input.mina[0]; @@ -705,13 +713,23 @@ function Network( Fetch.setGraphqlEndpoint(minaGraphqlEndpoint); } - if (Array.isArray(input.archive) && input.archive.length !== 0) { - archiveEndpoint = input.archive[0]; - Fetch.setArchiveGraphqlEndpoint(archiveEndpoint); - Fetch.setArchiveGraphqlFallbackEndpoints(input.archive.slice(1)); - } else if (typeof input.archive === 'string') { - archiveEndpoint = input.archive; - Fetch.setArchiveGraphqlEndpoint(archiveEndpoint); + if (input.archive !== undefined) { + if (Array.isArray(input.archive) && input.archive.length !== 0) { + archiveEndpoint = input.archive[0]; + Fetch.setArchiveGraphqlEndpoint(archiveEndpoint); + Fetch.setArchiveGraphqlFallbackEndpoints(input.archive.slice(1)); + } else if (typeof input.archive === 'string') { + archiveEndpoint = input.archive; + Fetch.setArchiveGraphqlEndpoint(archiveEndpoint); + } + } + + if ( + input.lightnetAccountManager !== undefined && + typeof input.lightnetAccountManager === 'string' + ) { + lightnetAccountManagerEndpoint = input.lightnetAccountManager; + Fetch.setLightnetAccountManagerEndpoint(lightnetAccountManagerEndpoint); } } else { throw new Error( @@ -1222,7 +1240,7 @@ function getProofsEnabled() { } function dummyAccount(pubkey?: PublicKey): Account { - let dummy = Types.Account.emptyValue(); + let dummy = Types.Account.empty(); if (pubkey) dummy.publicKey = pubkey; return dummy; } diff --git a/src/lib/mina/account.ts b/src/lib/mina/account.ts index c45d38587b..dc31d0d864 100644 --- a/src/lib/mina/account.ts +++ b/src/lib/mina/account.ts @@ -10,6 +10,7 @@ import { TypeMap, } from '../../bindings/mina-transaction/gen/transaction.js'; import { jsLayout } from '../../bindings/mina-transaction/gen/js-layout.js'; +import { ProvableExtended } from '../circuit_value.js'; export { FetchedAccount, Account, PartialAccount }; export { accountQuery, parseFetchedAccount, fillPartialAccount }; @@ -184,19 +185,14 @@ function parseFetchedAccount({ } function fillPartialAccount(account: PartialAccount): Account { - return genericLayoutFold( + return genericLayoutFold>( TypeMap, customTypes, { map(type, value) { // if value exists, use it; otherwise fall back to dummy value if (value !== undefined) return value; - // fall back to dummy value - if (type.emptyValue) return type.emptyValue(); - return type.fromFields( - Array(type.sizeInFields()).fill(Field(0)), - type.toAuxiliary() - ); + return type.empty(); }, reduceArray(array) { return array; diff --git a/src/lib/ml/base.ts b/src/lib/ml/base.ts index cf68579ce0..33cdb00975 100644 --- a/src/lib/ml/base.ts +++ b/src/lib/ml/base.ts @@ -1,21 +1,37 @@ +import { TupleN } from '../util/types.js'; + /** * This module contains basic methods for interacting with OCaml */ -export { MlArray, MlTuple, MlList, MlOption, MlBool, MlBytes }; +export { + MlArray, + MlPair, + MlList, + MlOption, + MlBool, + MlBytes, + MlResult, + MlUnit, + MlString, + MlTuple, +}; // ocaml types -type MlTuple = [0, X, Y]; +type MlPair = [0, X, Y]; type MlArray = [0, ...T[]]; type MlList = [0, T, 0 | MlList]; type MlOption = 0 | [0, T]; type MlBool = 0 | 1; +type MlResult = [0, T] | [1, E]; +type MlUnit = 0; /** * js_of_ocaml representation of a byte array, * see https://github.com/ocsigen/js_of_ocaml/blob/master/runtime/mlBytes.js */ type MlBytes = { t: number; c: string; l: number }; +type MlString = MlBytes; const MlArray = { to(arr: T[]): MlArray { @@ -27,20 +43,26 @@ const MlArray = { map([, ...arr]: MlArray, map: (t: T) => S): MlArray { return [0, ...arr.map(map)]; }, + mapTo(arr: T[], map: (t: T) => S): MlArray { + return [0, ...arr.map(map)]; + }, + mapFrom([, ...arr]: MlArray, map: (t: T) => S): S[] { + return arr.map(map); + }, }; -const MlTuple = Object.assign( - function MlTuple(x: X, y: Y): MlTuple { +const MlPair = Object.assign( + function MlTuple(x: X, y: Y): MlPair { return [0, x, y]; }, { - from([, x, y]: MlTuple): [X, Y] { + from([, x, y]: MlPair): [X, Y] { return [x, y]; }, - first(t: MlTuple): X { + first(t: MlPair): X { return t[1]; }, - second(t: MlTuple): Y { + second(t: MlPair): Y { return t[2]; }, } @@ -77,5 +99,58 @@ const MlOption = Object.assign( if (option === undefined) return 0; return [0, map(option)]; }, + isNone(option: MlOption): option is 0 { + return option === 0; + }, + isSome(option: MlOption): option is [0, T] { + return option !== 0; + }, } ); + +const MlResult = { + ok(t: T): MlResult { + return [0, t]; + }, + unitError(): MlResult { + return [1, 0]; + }, +}; + +/** + * tuple type that has the length as generic parameter + */ +type MlTuple = N extends N + ? number extends N + ? [0, ...T[]] // N is not typed as a constant => fall back to array + : [0, ...TupleRec] + : never; + +type TupleRec = R['length'] extends N + ? R + : TupleRec; + +type Tuple = [T, ...T[]] | []; + +const MlTuple = { + map, B>( + [, ...mlTuple]: [0, ...T], + f: (a: T[number]) => B + ): [0, ...{ [i in keyof T]: B }] { + return [0, ...mlTuple.map(f)] as any; + }, + + mapFrom( + [, ...mlTuple]: MlTuple, + f: (a: T) => B + ): B[] { + return mlTuple.map(f); + }, + + mapTo | TupleN, B>( + tuple: T, + f: (a: T[number]) => B + ): [0, ...{ [i in keyof T]: B }] { + return [0, ...tuple.map(f)] as any; + }, +}; diff --git a/src/lib/ml/conversion.ts b/src/lib/ml/conversion.ts index 646deac480..de65656cce 100644 --- a/src/lib/ml/conversion.ts +++ b/src/lib/ml/conversion.ts @@ -8,7 +8,7 @@ import { Bool, Field } from '../core.js'; import { FieldConst, FieldVar } from '../field.js'; import { Scalar, ScalarConst } from '../scalar.js'; import { PrivateKey, PublicKey } from '../signature.js'; -import { MlTuple, MlBool, MlArray } from './base.js'; +import { MlPair, MlBool, MlArray } from './base.js'; import { MlFieldConstArray } from './fields.js'; export { Ml, MlHashInput }; @@ -35,7 +35,7 @@ const Ml = { type MlHashInput = [ flag: 0, field_elements: MlArray, - packed: MlArray> + packed: MlArray> ]; const MlHashInput = { @@ -86,7 +86,7 @@ function toPrivateKey(sk: ScalarConst) { } function fromPublicKey(pk: PublicKey): MlPublicKey { - return MlTuple(pk.x.toConstant().value[1], MlBool(pk.isOdd.toBoolean())); + return MlPair(pk.x.toConstant().value[1], MlBool(pk.isOdd.toBoolean())); } function toPublicKey([, x, isOdd]: MlPublicKey): PublicKey { return PublicKey.from({ @@ -96,12 +96,8 @@ function toPublicKey([, x, isOdd]: MlPublicKey): PublicKey { } function fromPublicKeyVar(pk: PublicKey): MlPublicKeyVar { - return MlTuple(pk.x.value, pk.isOdd.toField().value); + return MlPair(pk.x.value, pk.isOdd.toField().value); } function toPublicKeyVar([, x, isOdd]: MlPublicKeyVar): PublicKey { - return PublicKey.from({ - x: Field(x), - // TODO - isOdd: Bool.Unsafe.ofField(Field(isOdd)), - }); + return PublicKey.from({ x: Field(x), isOdd: Bool(isOdd) }); } diff --git a/src/lib/precondition.test.ts b/src/lib/precondition.test.ts index d064ddc89f..07d0243d68 100644 --- a/src/lib/precondition.test.ts +++ b/src/lib/precondition.test.ts @@ -76,6 +76,21 @@ describe('preconditions', () => { expect(zkapp.account.nonce.get()).toEqual(nonce.add(1)); }); + it('get + requireEquals should not throw', async () => { + let nonce = zkapp.account.nonce.get(); + let tx = await Mina.transaction(feePayer, () => { + zkapp.requireSignature(); + for (let precondition of implemented) { + let p = precondition().get(); + precondition().requireEquals(p as any); + } + AccountUpdate.attachToTransaction(zkapp.self); + }); + await tx.sign([feePayerKey, zkappKey]).send(); + // check that tx was applied, by checking nonce was incremented + expect(zkapp.account.nonce.get()).toEqual(nonce.add(1)); + }); + it('get + assertEquals should throw for unimplemented fields', async () => { for (let precondition of unimplemented) { await expect( @@ -88,6 +103,18 @@ describe('preconditions', () => { } }); + it('get + requireEquals should throw for unimplemented fields', async () => { + for (let precondition of unimplemented) { + await expect( + Mina.transaction(feePayer, () => { + let p = precondition(); + p.requireEquals(p.get() as any); + AccountUpdate.attachToTransaction(zkapp.self); + }) + ).rejects.toThrow(/not implemented/); + } + }); + it('get + assertBetween should not throw', async () => { let nonce = zkapp.account.nonce.get(); let tx = await Mina.transaction(feePayer, () => { @@ -103,6 +130,21 @@ describe('preconditions', () => { expect(zkapp.account.nonce.get()).toEqual(nonce.add(1)); }); + it('get + requireBetween should not throw', async () => { + let nonce = zkapp.account.nonce.get(); + let tx = await Mina.transaction(feePayer, () => { + for (let precondition of implementedWithRange) { + let p: any = precondition().get(); + precondition().requireBetween(p.constructor.zero, p); + } + zkapp.requireSignature(); + AccountUpdate.attachToTransaction(zkapp.self); + }); + await tx.sign([feePayerKey, zkappKey]).send(); + // check that tx was applied, by checking nonce was incremented + expect(zkapp.account.nonce.get()).toEqual(nonce.add(1)); + }); + it('satisfied currentSlot.assertBetween should not throw', async () => { let nonce = zkapp.account.nonce.get(); let tx = await Mina.transaction(feePayer, () => { @@ -117,6 +159,20 @@ describe('preconditions', () => { expect(zkapp.account.nonce.get()).toEqual(nonce.add(1)); }); + it('satisfied currentSlot.requireBetween should not throw', async () => { + let nonce = zkapp.account.nonce.get(); + let tx = await Mina.transaction(feePayer, () => { + zkapp.currentSlot.requireBetween( + UInt32.from(0), + UInt32.from(UInt32.MAXINT()) + ); + zkapp.requireSignature(); + AccountUpdate.attachToTransaction(zkapp.self); + }); + await tx.sign([feePayerKey, zkappKey]).send(); + expect(zkapp.account.nonce.get()).toEqual(nonce.add(1)); + }); + it('get + assertNothing should not throw', async () => { let nonce = zkapp.account.nonce.get(); let tx = await Mina.transaction(feePayer, () => { @@ -132,6 +188,21 @@ describe('preconditions', () => { expect(zkapp.account.nonce.get()).toEqual(nonce.add(1)); }); + it('get + requireNothing should not throw', async () => { + let nonce = zkapp.account.nonce.get(); + let tx = await Mina.transaction(feePayer, () => { + for (let precondition of implemented) { + precondition().get(); + precondition().requireNothing(); + } + zkapp.requireSignature(); + AccountUpdate.attachToTransaction(zkapp.self); + }); + await tx.sign([feePayerKey, zkappKey]).send(); + // check that tx was applied, by checking nonce was incremented + expect(zkapp.account.nonce.get()).toEqual(nonce.add(1)); + }); + it('get + manual precondition should not throw', async () => { // we only test this for a couple of preconditions let nonce = zkapp.account.nonce.get(); @@ -172,6 +243,19 @@ describe('preconditions', () => { } }); + it('unsatisfied requireEquals should be rejected (numbers)', async () => { + for (let precondition of implementedNumber) { + await expect(async () => { + let tx = await Mina.transaction(feePayer, () => { + let p = precondition().get(); + precondition().requireEquals(p.add(1) as any); + AccountUpdate.attachToTransaction(zkapp.self); + }); + await tx.sign([feePayerKey]).send(); + }).rejects.toThrow(/unsatisfied/); + } + }); + it('unsatisfied assertEquals should be rejected (booleans)', async () => { for (let precondition of implementedBool) { let tx = await Mina.transaction(feePayer, () => { @@ -185,6 +269,19 @@ describe('preconditions', () => { } }); + it('unsatisfied requireEquals should be rejected (booleans)', async () => { + for (let precondition of implementedBool) { + let tx = await Mina.transaction(feePayer, () => { + let p = precondition().get(); + precondition().requireEquals(p.not()); + AccountUpdate.attachToTransaction(zkapp.self); + }); + await expect(tx.sign([feePayerKey]).send()).rejects.toThrow( + /unsatisfied/ + ); + } + }); + it('unsatisfied assertEquals should be rejected (public key)', async () => { let publicKey = PublicKey.from({ x: Field(-1), isOdd: Bool(false) }); let tx = await Mina.transaction(feePayer, () => { @@ -194,6 +291,15 @@ describe('preconditions', () => { await expect(tx.sign([feePayerKey]).send()).rejects.toThrow(/unsatisfied/); }); + it('unsatisfied requireEquals should be rejected (public key)', async () => { + let publicKey = PublicKey.from({ x: Field(-1), isOdd: Bool(false) }); + let tx = await Mina.transaction(feePayer, () => { + zkapp.account.delegate.requireEquals(publicKey); + AccountUpdate.attachToTransaction(zkapp.self); + }); + await expect(tx.sign([feePayerKey]).send()).rejects.toThrow(/unsatisfied/); + }); + it('unsatisfied assertBetween should be rejected', async () => { for (let precondition of implementedWithRange) { let tx = await Mina.transaction(feePayer, () => { @@ -207,6 +313,19 @@ describe('preconditions', () => { } }); + it('unsatisfied requireBetween should be rejected', async () => { + for (let precondition of implementedWithRange) { + let tx = await Mina.transaction(feePayer, () => { + let p: any = precondition().get(); + precondition().requireBetween(p.add(20), p.add(30)); + AccountUpdate.attachToTransaction(zkapp.self); + }); + await expect(tx.sign([feePayerKey]).send()).rejects.toThrow( + /unsatisfied/ + ); + } + }); + it('unsatisfied currentSlot.assertBetween should be rejected', async () => { let tx = await Mina.transaction(feePayer, () => { zkapp.currentSlot.assertBetween(UInt32.from(20), UInt32.from(30)); @@ -215,6 +334,14 @@ describe('preconditions', () => { await expect(tx.sign([feePayerKey]).send()).rejects.toThrow(/unsatisfied/); }); + it('unsatisfied currentSlot.requireBetween should be rejected', async () => { + let tx = await Mina.transaction(feePayer, () => { + zkapp.currentSlot.requireBetween(UInt32.from(20), UInt32.from(30)); + AccountUpdate.attachToTransaction(zkapp.self); + }); + await expect(tx.sign([feePayerKey]).send()).rejects.toThrow(/unsatisfied/); + }); + // TODO: is this a gotcha that should be addressed? // the test below fails, so it seems that nonce is applied successfully with a WRONG precondition.. // however, this is just because `zkapp.sign()` overwrites the nonce precondition with one that is satisfied diff --git a/src/lib/precondition.ts b/src/lib/precondition.ts index 53e044f6ed..25b10b9031 100644 --- a/src/lib/precondition.ts +++ b/src/lib/precondition.ts @@ -49,11 +49,14 @@ function Network(accountUpdate: AccountUpdate): Network { let slot = network.globalSlotSinceGenesis.get(); return globalSlotToTimestamp(slot); }, - getAndAssertEquals() { - let slot = network.globalSlotSinceGenesis.getAndAssertEquals(); + getAndRequireEquals() { + let slot = network.globalSlotSinceGenesis.getAndRequireEquals(); return globalSlotToTimestamp(slot); }, - assertEquals(value: UInt64) { + getAndAssertEquals() { + return this.getAndRequireEquals(); + }, + requireEquals(value: UInt64) { let { genesisTimestamp, slotTime } = Mina.activeInstance.getNetworkConstants(); let slot = timestampToGlobalSlot( @@ -61,14 +64,26 @@ function Network(accountUpdate: AccountUpdate): Network { `Timestamp precondition unsatisfied: the timestamp can only equal numbers of the form ${genesisTimestamp} + k*${slotTime},\n` + `i.e., the genesis timestamp plus an integer number of slots.` ); - return network.globalSlotSinceGenesis.assertEquals(slot); + return network.globalSlotSinceGenesis.requireEquals(slot); }, - assertBetween(lower: UInt64, upper: UInt64) { + assertEquals(value: UInt64) { + return this.requireEquals(value); + }, + requireBetween(lower: UInt64, upper: UInt64) { let [slotLower, slotUpper] = timestampToGlobalSlotRange(lower, upper); - return network.globalSlotSinceGenesis.assertBetween(slotLower, slotUpper); + return network.globalSlotSinceGenesis.requireBetween( + slotLower, + slotUpper + ); + }, + assertBetween(lower: UInt64, upper: UInt64) { + return this.requireBetween(lower, upper); + }, + requireNothing() { + return network.globalSlotSinceGenesis.requireNothing(); }, assertNothing() { - return network.globalSlotSinceGenesis.assertNothing(); + return this.requireNothing(); }, }; return { ...network, timestamp }; @@ -118,7 +133,7 @@ function updateSubclass( function CurrentSlot(accountUpdate: AccountUpdate): CurrentSlot { let context = getPreconditionContextExn(accountUpdate); return { - assertBetween(lower: UInt32, upper: UInt32) { + requireBetween(lower: UInt32, upper: UInt32) { context.constrained.add('validWhile'); let property: RangeCondition = accountUpdate.body.preconditions.validWhile; @@ -126,6 +141,9 @@ function CurrentSlot(accountUpdate: AccountUpdate): CurrentSlot { property.value.lower = lower; property.value.upper = upper; }, + assertBetween(lower: UInt32, upper: UInt32) { + this.requireBetween(lower, upper); + }, }; } @@ -193,7 +211,7 @@ function preconditionSubClassWithRange< ) { return { ...preconditionSubclass(accountUpdate, longKey, fieldType as any, context), - assertBetween(lower: any, upper: any) { + requireBetween(lower: any, upper: any) { context.constrained.add(longKey); let property: RangeCondition = getPath( accountUpdate.body.preconditions, @@ -203,6 +221,9 @@ function preconditionSubClassWithRange< property.value.lower = lower; property.value.upper = upper; }, + assertBetween(lower: any, upper: any) { + this.requireBetween(lower, upper); + }, }; } @@ -232,12 +253,15 @@ function preconditionSubclass< fieldType )) as U; }, - getAndAssertEquals() { + getAndRequireEquals() { let value = obj.get(); - obj.assertEquals(value); + obj.requireEquals(value); return value; }, - assertEquals(value: U) { + getAndAssertEquals() { + return this.getAndRequireEquals(); + }, + requireEquals(value: U) { context.constrained.add(longKey); let property = getPath( accountUpdate.body.preconditions, @@ -255,9 +279,15 @@ function preconditionSubclass< setPath(accountUpdate.body.preconditions, longKey, value); } }, - assertNothing() { + assertEquals(value: U) { + this.requireEquals(value); + }, + requireNothing() { context.constrained.add(longKey); }, + assertNothing() { + this.requireNothing(); + }, }; return obj; } @@ -437,6 +467,10 @@ type Account = PreconditionClassType & Update; type CurrentSlotPrecondition = Preconditions['validWhile']; type CurrentSlot = { + requireBetween(lower: UInt32, upper: UInt32): void; + /** + * @deprecated use `requireBetween(lower: UInt32, upper: UInt32)` which is equivalent + */ assertBetween(lower: UInt32, upper: UInt32): void; }; @@ -452,11 +486,27 @@ type PreconditionBaseTypes = { type PreconditionSubclassType = { get(): U; + getAndRequireEquals(): U; + /** + * @deprecated use `getAndRequireEquals()` which is equivalent + */ getAndAssertEquals(): U; + requireEquals(value: U): void; + /** + * @deprecated use `requireEquals(value: U)` which is equivalent + */ assertEquals(value: U): void; + requireNothing(): void; + /** + * @deprecated use `requireNothing()` which is equivalent + */ assertNothing(): void; }; type PreconditionSubclassRangeType = PreconditionSubclassType & { + requireBetween(lower: U, upper: U): void; + /** + * @deprecated use `requireBetween(lower: U, upper: U)` which is equivalent + */ assertBetween(lower: U, upper: U): void; }; diff --git a/src/lib/proof-system/cache.ts b/src/lib/proof-system/cache.ts new file mode 100644 index 0000000000..42343c88fc --- /dev/null +++ b/src/lib/proof-system/cache.ts @@ -0,0 +1,222 @@ +import { + writeFileSync, + readFileSync, + mkdirSync, + resolve, + cacheDir, +} from '../util/fs.js'; +import { jsEnvironment } from '../../bindings/crypto/bindings/env.js'; + +// external API +export { Cache, CacheHeader }; + +// internal API +export { readCache, writeCache, withVersion, cacheHeaderVersion }; + +/** + * Interface for storing and retrieving values, for caching. + * `read()` and `write()` can just throw errors on failure. + * + * The data that will be passed to the cache for writing is exhaustively described by the {@link CacheHeader} type. + * It represents one of the following: + * - The SRS. This is a deterministic lists of curve points (one per curve) that needs to be generated just once, + * to be used for polynomial commitments. + * - Lagrange basis commitments. Similar to the SRS, this will be created once for every power-of-2 circuit size. + * - Prover and verifier keys for every compiled circuit. + * + * Per smart contract or ZkProgram, several different keys are created: + * - a step prover key (`step-pk`) and verification key (`step-vk`) _for every method_. + * - a wrap prover key (`wrap-pk`) and verification key (`wrap-vk`) for the entire contract. + */ +type Cache = { + /** + * Read a value from the cache. + * + * @param header A small header to identify what is read from the cache. + */ + read(header: CacheHeader): Uint8Array | undefined; + + /** + * Write a value to the cache. + * + * @param header A small header to identify what is written to the cache. This will be used by `read()` to retrieve the data. + * @param value The value to write to the cache, as a byte array. + */ + write(header: CacheHeader, value: Uint8Array): void; + + /** + * Indicates whether the cache is writable. + */ + canWrite: boolean; + + /** + * If `debug` is toggled, `read()` and `write()` errors are logged to the console. + * + * By default, cache errors are silent, because they don't necessarily represent an error condition, + * but could just be a cache miss, or file system permissions incompatible with writing data. + */ + debug?: boolean; +}; + +const cacheHeaderVersion = 1; + +type CommonHeader = { + /** + * Header version to avoid parsing incompatible headers. + */ + version: number; + /** + * An identifier that is persistent even as versions of the data change. Safe to use as a file path. + */ + persistentId: string; + /** + * A unique identifier for the data to be read. Safe to use as a file path. + */ + uniqueId: string; + /** + * Specifies whether the data to be read is a utf8-encoded string or raw binary data. This was added + * because node's `fs.readFileSync` returns garbage when reading string files without specifying the encoding. + */ + dataType: 'string' | 'bytes'; +}; + +type StepKeyHeader = { + kind: Kind; + programName: string; + methodName: string; + methodIndex: number; + hash: string; +}; +type WrapKeyHeader = { kind: Kind; programName: string; hash: string }; +type PlainHeader = { kind: Kind }; + +/** + * A header that is passed to the caching layer, to support rich caching strategies. + * + * Both `uniqueId` and `programId` can safely be used as a file path. + */ +type CacheHeader = ( + | StepKeyHeader<'step-pk'> + | StepKeyHeader<'step-vk'> + | WrapKeyHeader<'wrap-pk'> + | WrapKeyHeader<'wrap-vk'> + | PlainHeader<'srs'> + | PlainHeader<'lagrange-basis'> +) & + CommonHeader; + +function withVersion( + header: Omit, + version = cacheHeaderVersion +): CacheHeader { + let uniqueId = `${header.uniqueId}-${version}`; + return { ...header, version, uniqueId } as CacheHeader; +} + +// default methods to interact with a cache + +function readCache(cache: Cache, header: CacheHeader): Uint8Array | undefined; +function readCache( + cache: Cache, + header: CacheHeader, + transform: (x: Uint8Array) => T +): T | undefined; +function readCache( + cache: Cache, + header: CacheHeader, + transform?: (x: Uint8Array) => T +): T | undefined { + try { + let result = cache.read(header); + if (result === undefined) { + if (cache.debug) console.trace('cache miss'); + return undefined; + } + if (transform === undefined) return result as any as T; + return transform(result); + } catch (e) { + if (cache.debug) console.log('Failed to read cache', e); + return undefined; + } +} + +function writeCache(cache: Cache, header: CacheHeader, value: Uint8Array) { + if (!cache.canWrite) return false; + try { + cache.write(header, value); + return true; + } catch (e) { + if (cache.debug) console.log('Failed to write cache', e); + return false; + } +} + +const None: Cache = { + read() { + throw Error('not available'); + }, + write() { + throw Error('not available'); + }, + canWrite: false, +}; + +const FileSystem = (cacheDirectory: string, debug?: boolean): Cache => ({ + read({ persistentId, uniqueId, dataType }) { + if (jsEnvironment !== 'node') throw Error('file system not available'); + + // read current uniqueId, return data if it matches + let currentId = readFileSync( + resolve(cacheDirectory, `${persistentId}.header`), + 'utf8' + ); + if (currentId !== uniqueId) return undefined; + + if (dataType === 'string') { + let string = readFileSync(resolve(cacheDirectory, persistentId), 'utf8'); + return new TextEncoder().encode(string); + } else { + let buffer = readFileSync(resolve(cacheDirectory, persistentId)); + return new Uint8Array(buffer.buffer); + } + }, + write({ persistentId, uniqueId, dataType }, data) { + if (jsEnvironment !== 'node') throw Error('file system not available'); + mkdirSync(cacheDirectory, { recursive: true }); + writeFileSync(resolve(cacheDirectory, `${persistentId}.header`), uniqueId, { + encoding: 'utf8', + }); + writeFileSync(resolve(cacheDirectory, persistentId), data, { + encoding: dataType === 'string' ? 'utf8' : undefined, + }); + }, + canWrite: jsEnvironment === 'node', + debug, +}); + +const FileSystemDefault = FileSystem(cacheDir('o1js')); + +const Cache = { + /** + * Store data on the file system, in a directory of your choice. + * + * Data will be stored in two files per cache entry: a data file and a `.header` file. + * The header file just contains a unique string which is used to determine whether we can use the cached data. + * + * Note: this {@link Cache} only caches data in Node.js. + */ + FileSystem, + /** + * Store data on the file system, in a standard cache directory depending on the OS. + * + * Data will be stored in two files per cache entry: a data file and a `.header` file. + * The header file just contains a unique string which is used to determine whether we can use the cached data. + * + * Note: this {@link Cache} only caches data in Node.js. + */ + FileSystemDefault, + /** + * Don't store anything. + */ + None, +}; diff --git a/src/lib/proof-system/prover-keys.ts b/src/lib/proof-system/prover-keys.ts new file mode 100644 index 0000000000..18b68f27b9 --- /dev/null +++ b/src/lib/proof-system/prover-keys.ts @@ -0,0 +1,259 @@ +/** + * This file provides helpers to + * - encode and decode all 4 kinds of snark keys to/from bytes + * - create a header which is passed to the `Cache` so that it can figure out where and if to read from cache + * + * The inputs are `SnarkKeyHeader` and `SnarkKey`, which are OCaml tagged enums defined in pickles_bindings.ml + */ +import { + WasmPastaFpPlonkIndex, + WasmPastaFqPlonkIndex, +} from '../../bindings/compiled/node_bindings/plonk_wasm.cjs'; +import { Pickles, getWasm } from '../../snarky.js'; +import { VerifierIndex } from '../../bindings/crypto/bindings/kimchi-types.js'; +import { getRustConversion } from '../../bindings/crypto/bindings.js'; +import { MlString } from '../ml/base.js'; +import { CacheHeader, cacheHeaderVersion } from './cache.js'; +import type { MethodInterface } from '../proof_system.js'; + +export { + parseHeader, + encodeProverKey, + decodeProverKey, + SnarkKeyHeader, + SnarkKey, +}; +export type { MlWrapVerificationKey }; + +// there are 4 types of snark keys in Pickles which we all handle at once +enum KeyType { + StepProvingKey, + StepVerificationKey, + WrapProvingKey, + WrapVerificationKey, +} + +type SnarkKeyHeader = + | [KeyType.StepProvingKey, MlStepProvingKeyHeader] + | [KeyType.StepVerificationKey, MlStepVerificationKeyHeader] + | [KeyType.WrapProvingKey, MlWrapProvingKeyHeader] + | [KeyType.WrapVerificationKey, MlWrapVerificationKeyHeader]; + +type SnarkKey = + | [KeyType.StepProvingKey, MlBackendKeyPair] + | [KeyType.StepVerificationKey, VerifierIndex] + | [KeyType.WrapProvingKey, MlBackendKeyPair] + | [KeyType.WrapVerificationKey, MlWrapVerificationKey]; + +/** + * Create `CacheHeader` from a `SnarkKeyHeader` plus some context available to `compile()` + */ +function parseHeader( + programName: string, + methods: MethodInterface[], + header: SnarkKeyHeader +): CacheHeader { + let hash = Pickles.util.fromMlString(header[1][2][8]); + switch (header[0]) { + case KeyType.StepProvingKey: + case KeyType.StepVerificationKey: { + let kind = snarkKeyStringKind[header[0]]; + let methodIndex = header[1][3]; + let methodName = methods[methodIndex].methodName; + let persistentId = sanitize(`${kind}-${programName}-${methodName}`); + let uniqueId = sanitize( + `${kind}-${programName}-${methodIndex}-${methodName}-${hash}` + ); + return { + version: cacheHeaderVersion, + uniqueId, + kind, + persistentId, + programName, + methodName, + methodIndex, + hash, + dataType: snarkKeySerializationType[header[0]], + }; + } + case KeyType.WrapProvingKey: + case KeyType.WrapVerificationKey: { + let kind = snarkKeyStringKind[header[0]]; + let dataType = snarkKeySerializationType[header[0]]; + let persistentId = sanitize(`${kind}-${programName}`); + let uniqueId = sanitize(`${kind}-${programName}-${hash}`); + return { + version: cacheHeaderVersion, + uniqueId, + kind, + persistentId, + programName, + hash, + dataType, + }; + } + } +} + +/** + * Encode a snark key to bytes + */ +function encodeProverKey(value: SnarkKey): Uint8Array { + let wasm = getWasm(); + switch (value[0]) { + case KeyType.StepProvingKey: { + let index = value[1][1]; + let encoded = wasm.caml_pasta_fp_plonk_index_encode(index); + return encoded; + } + case KeyType.StepVerificationKey: { + let vkMl = value[1]; + const rustConversion = getRustConversion(getWasm()); + let vkWasm = rustConversion.fp.verifierIndexToRust(vkMl); + let string = wasm.caml_pasta_fp_plonk_verifier_index_serialize(vkWasm); + return new TextEncoder().encode(string); + } + case KeyType.WrapProvingKey: { + let index = value[1][1]; + let encoded = wasm.caml_pasta_fq_plonk_index_encode(index); + return encoded; + } + case KeyType.WrapVerificationKey: { + let vk = value[1]; + let string = Pickles.encodeVerificationKey(vk); + return new TextEncoder().encode(string); + } + default: + value satisfies never; + throw Error('todo'); + } +} + +/** + * Decode bytes to a snark key with the help of its header + */ +function decodeProverKey(header: SnarkKeyHeader, bytes: Uint8Array): SnarkKey { + let wasm = getWasm(); + switch (header[0]) { + case KeyType.StepProvingKey: { + let srs = Pickles.loadSrsFp(); + let index = wasm.caml_pasta_fp_plonk_index_decode(bytes, srs); + let cs = header[1][4]; + return [KeyType.StepProvingKey, [0, index, cs]]; + } + case KeyType.StepVerificationKey: { + let srs = Pickles.loadSrsFp(); + let string = new TextDecoder().decode(bytes); + let vkWasm = wasm.caml_pasta_fp_plonk_verifier_index_deserialize( + srs, + string + ); + const rustConversion = getRustConversion(getWasm()); + let vkMl = rustConversion.fp.verifierIndexFromRust(vkWasm); + return [KeyType.StepVerificationKey, vkMl]; + } + case KeyType.WrapProvingKey: { + let srs = Pickles.loadSrsFq(); + let index = wasm.caml_pasta_fq_plonk_index_decode(bytes, srs); + let cs = header[1][3]; + return [KeyType.WrapProvingKey, [0, index, cs]]; + } + case KeyType.WrapVerificationKey: { + let string = new TextDecoder().decode(bytes); + let vk = Pickles.decodeVerificationKey(string); + return [KeyType.WrapVerificationKey, vk]; + } + default: + header satisfies never; + throw Error('todo'); + } +} + +/** + * Sanitize a string so that it can be used as a file name + */ +function sanitize(string: string): string { + return string.toLowerCase().replace(/[^a-z0-9_-]/g, '_'); +} + +const snarkKeyStringKind = { + [KeyType.StepProvingKey]: 'step-pk', + [KeyType.StepVerificationKey]: 'step-vk', + [KeyType.WrapProvingKey]: 'wrap-pk', + [KeyType.WrapVerificationKey]: 'wrap-vk', +} as const; + +const snarkKeySerializationType = { + [KeyType.StepProvingKey]: 'bytes', + [KeyType.StepVerificationKey]: 'string', + [KeyType.WrapProvingKey]: 'bytes', + [KeyType.WrapVerificationKey]: 'string', +} as const; + +// pickles types + +// Plonk_constraint_system.Make()().t + +class MlConstraintSystem { + // opaque type +} + +// Dlog_plonk_based_keypair.Make().t + +type MlBackendKeyPair = [ + _: 0, + index: WasmIndex, + cs: MlConstraintSystem +]; + +// Snarky_keys_header.t + +type MlSnarkKeysHeader = [ + _: 0, + headerVersion: number, + kind: [_: 0, type: MlString, identifier: MlString], + constraintConstants: unknown, + commits: unknown, + length: number, + commitDate: MlString, + constraintSystemHash: MlString, + identifyingHash: MlString +]; + +// Pickles.Cache.{Step,Wrap}.Key.Proving.t + +type MlStepProvingKeyHeader = [ + _: 0, + typeEqual: number, + snarkKeysHeader: MlSnarkKeysHeader, + index: number, + constraintSystem: MlConstraintSystem +]; + +type MlStepVerificationKeyHeader = [ + _: 0, + typeEqual: number, + snarkKeysHeader: MlSnarkKeysHeader, + index: number, + digest: unknown +]; + +type MlWrapProvingKeyHeader = [ + _: 0, + typeEqual: number, + snarkKeysHeader: MlSnarkKeysHeader, + constraintSystem: MlConstraintSystem +]; + +type MlWrapVerificationKeyHeader = [ + _: 0, + typeEqual: number, + snarkKeysHeader: MlSnarkKeysHeader, + digest: unknown +]; + +// Pickles.Verification_key.t + +class MlWrapVerificationKey { + // opaque type +} diff --git a/src/lib/proof_system.ts b/src/lib/proof_system.ts index 36cb0e154f..59ebf57ec4 100644 --- a/src/lib/proof_system.ts +++ b/src/lib/proof_system.ts @@ -4,13 +4,22 @@ import { EmptyVoid, } from '../bindings/lib/generic.js'; import { withThreadPool } from '../bindings/js/wrapper.js'; -import { ProvablePure, Pickles } from '../snarky.js'; +import { + ProvablePure, + Pickles, + FeatureFlags, + MlFeatureFlags, + Gate, + GateType, +} from '../snarky.js'; import { Field, Bool } from './core.js'; import { FlexibleProvable, FlexibleProvablePure, InferProvable, ProvablePureExtended, + Struct, + provable, provablePure, toConstant, } from './circuit_value.js'; @@ -18,9 +27,16 @@ import { Provable } from './provable.js'; import { assert, prettifyStacktracePromise } from './errors.js'; import { snarkContext } from './provable-context.js'; import { hashConstant } from './hash.js'; -import { MlArray, MlTuple } from './ml/base.js'; +import { MlArray, MlBool, MlResult, MlPair } from './ml/base.js'; import { MlFieldArray, MlFieldConstArray } from './ml/fields.js'; import { FieldConst, FieldVar } from './field.js'; +import { Cache, readCache, writeCache } from './proof-system/cache.js'; +import { + decodeProverKey, + encodeProverKey, + parseHeader, +} from './proof-system/prover-keys.js'; +import { setSrsCache, unsetSrsCache } from '../bindings/crypto/bindings/srs.js'; // public API export { @@ -28,10 +44,12 @@ export { SelfProof, JsonProof, ZkProgram, + ExperimentalZkProgram, verify, Empty, Undefined, Void, + VerificationKey, }; // internal API @@ -132,11 +150,47 @@ class Proof { this.proof = proof; // TODO optionally convert from string? this.maxProofsVerified = maxProofsVerified; } + + /** + * Dummy proof. This can be useful for ZkPrograms that handle the base case in the same + * method as the inductive case, using a pattern like this: + * + * ```ts + * method(proof: SelfProof, isRecursive: Bool) { + * proof.verifyIf(isRecursive); + * // ... + * } + * ``` + * + * To use such a method in the base case, you need a dummy proof: + * + * ```ts + * let dummy = await MyProof.dummy(publicInput, publicOutput, 1); + * await myProgram.myMethod(dummy, Bool(false)); + * ``` + * + * **Note**: The types of `publicInput` and `publicOutput`, as well as the `maxProofsVerified` parameter, + * must match your ZkProgram. `maxProofsVerified` is the maximum number of proofs that any of your methods take as arguments. + */ + static async dummy( + publicInput: Input, + publicOutput: OutPut, + maxProofsVerified: 0 | 1 | 2, + domainLog2: number = 14 + ): Promise> { + let dummyRaw = await dummyProof(maxProofsVerified, domainLog2); + return new this({ + publicInput, + publicOutput, + proof: dummyRaw, + maxProofsVerified, + }); + } } async function verify( proof: Proof | JsonProof, - verificationKey: string + verificationKey: string | VerificationKey ) { let picklesProof: Pickles.Proof; let statement: Pickles.Statement; @@ -152,19 +206,21 @@ async function verify( let output = MlFieldConstArray.to( (proof as JsonProof).publicOutput.map(Field) ); - statement = MlTuple(input, output); + statement = MlPair(input, output); } else { // proof class picklesProof = proof.proof; let type = getStatementType(proof.constructor as any); let input = toFieldConsts(type.input, proof.publicInput); let output = toFieldConsts(type.output, proof.publicOutput); - statement = MlTuple(input, output); + statement = MlPair(input, output); } + let vk = + typeof verificationKey === 'string' + ? verificationKey + : verificationKey.data; return prettifyStacktracePromise( - withThreadPool(() => - Pickles.verify(statement, picklesProof, verificationKey) - ) + withThreadPool(() => Pickles.verify(statement, picklesProof, vk)) ); } @@ -197,6 +253,7 @@ function ZkProgram< } >( config: StatementType & { + name: string; methods: { [I in keyof Types]: Method< InferProvableOrUndefined>, @@ -208,7 +265,9 @@ function ZkProgram< } ): { name: string; - compile: () => Promise<{ verificationKey: string }>; + compile: (options?: { cache?: Cache; forceRecompile?: boolean }) => Promise<{ + verificationKey: { data: string; hash: Field }; + }>; verify: ( proof: Proof< InferProvableOrUndefined>, @@ -216,9 +275,25 @@ function ZkProgram< > ) => Promise; digest: () => string; - analyzeMethods: () => ReturnType[]; + analyzeMethods: () => { + [I in keyof Types]: ReturnType; + }; publicInputType: ProvableOrUndefined>; publicOutputType: ProvableOrVoid>; + privateInputTypes: { + [I in keyof Types]: Method< + InferProvableOrUndefined>, + InferProvableOrVoid>, + Types[I] + >['privateInputs']; + }; + rawMethods: { + [I in keyof Types]: Method< + InferProvableOrUndefined>, + InferProvableOrVoid>, + Types[I] + >['method']; + }; } & { [I in keyof Types]: Prover< InferProvableOrUndefined>, @@ -227,10 +302,10 @@ function ZkProgram< >; } { let methods = config.methods; - let publicInputType: ProvablePure = config.publicInput! ?? Undefined; - let publicOutputType: ProvablePure = config.publicOutput! ?? Void; + let publicInputType: ProvablePure = config.publicInput ?? Undefined; + let publicOutputType: ProvablePure = config.publicOutput ?? Void; - let selfTag = { name: `Program${i++}` }; + let selfTag = { name: config.name }; type PublicInput = InferProvableOrUndefined< Get >; @@ -242,13 +317,24 @@ function ZkProgram< static tag = () => selfTag; } - let keys: (keyof Types & string)[] = Object.keys(methods).sort(); // need to have methods in (any) fixed order - let methodIntfs = keys.map((key) => + let methodKeys: (keyof Types & string)[] = Object.keys(methods).sort(); // need to have methods in (any) fixed order + let methodIntfs = methodKeys.map((key) => sortMethodArguments('program', key, methods[key].privateInputs, SelfProof) ); - let methodFunctions = keys.map((key) => methods[key].method); + let methodFunctions = methodKeys.map((key) => methods[key].method); let maxProofsVerified = getMaxProofsVerified(methodIntfs); + function analyzeMethods() { + return Object.fromEntries( + methodIntfs.map((methodEntry, i) => [ + methodEntry.methodName, + analyzeMethod(publicInputType, methodEntry, methodFunctions[i]), + ]) + ) as any as { + [I in keyof Types]: ReturnType; + }; + } + let compileOutput: | { provers: Pickles.Prover[]; @@ -259,17 +345,27 @@ function ZkProgram< } | undefined; - async function compile() { - let { provers, verify, verificationKey } = await compileProgram( + async function compile({ + cache = Cache.FileSystemDefault, + forceRecompile = false, + } = {}) { + let methodsMeta = methodIntfs.map((methodEntry, i) => + analyzeMethod(publicInputType, methodEntry, methodFunctions[i]) + ); + let gates = methodsMeta.map((m) => m.gates); + let { provers, verify, verificationKey } = await compileProgram({ publicInputType, publicOutputType, methodIntfs, - methodFunctions, - selfTag, - config.overrideWrapDomain - ); + methods: methodFunctions, + gates, + proofSystemTag: selfTag, + cache, + forceRecompile, + overrideWrapDomain: config.overrideWrapDomain, + }); compileOutput = { provers, verify }; - return { verificationKey: verificationKey.data }; + return { verificationKey }; } function toProver( @@ -299,7 +395,7 @@ function ZkProgram< } finally { snarkContext.leave(id); } - let [publicOutputFields, proof] = MlTuple.from(result); + let [publicOutputFields, proof] = MlPair.from(result); let publicOutput = fromFieldConsts(publicOutputType, publicOutputFields); class ProgramProof extends Proof { static publicInputType = publicInputType; @@ -325,7 +421,7 @@ function ZkProgram< } return [key, prove]; } - let provers = Object.fromEntries(keys.map(toProver)) as { + let provers = Object.fromEntries(methodKeys.map(toProver)) as { [I in keyof Types]: Prover; }; @@ -335,7 +431,7 @@ function ZkProgram< `Cannot verify proof, verification key not found. Try calling \`await program.compile()\` first.` ); } - let statement = MlTuple( + let statement = MlPair( toFieldConsts(publicInputType, proof.publicInput), toFieldConsts(publicOutputType, proof.publicOutput) ); @@ -352,30 +448,40 @@ function ZkProgram< return hash.toBigInt().toString(16); } - function analyzeMethods() { - return methodIntfs.map((methodEntry, i) => - analyzeMethod(publicInputType, methodEntry, methodFunctions[i]) - ); - } - return Object.assign( selfTag, { compile, verify, digest, + analyzeMethods, publicInputType: publicInputType as ProvableOrUndefined< Get >, publicOutputType: publicOutputType as ProvableOrVoid< Get >, - analyzeMethods, + privateInputTypes: Object.fromEntries( + methodKeys.map((key) => [key, methods[key].privateInputs]) + ) as any, + rawMethods: Object.fromEntries( + methodKeys.map((key) => [key, methods[key].method]) + ) as any, }, provers ); } +type ZkProgram< + S extends { + publicInput?: FlexibleProvablePure; + publicOutput?: FlexibleProvablePure; + }, + T extends { + [I in string]: Tuple; + } +> = ReturnType>; + let i = 0; class SelfProof extends Proof< @@ -383,6 +489,13 @@ class SelfProof extends Proof< PublicOutput > {} +class VerificationKey extends Struct({ + ...provable({ data: String, hash: Field }), + toJSON({ data }: { data: string }) { + return data; + }, +}) {} + function sortMethodArguments( programName: string, methodName: string, @@ -411,6 +524,9 @@ function sortMethodArguments( } else if (isAsFields(privateInput)) { allArgs.push({ type: 'witness', index: witnessArgs.length }); witnessArgs.push(privateInput); + } else if (isAsFields((privateInput as any)?.provable)) { + allArgs.push({ type: 'witness', index: witnessArgs.length }); + witnessArgs.push((privateInput as any).provable); } else if (isGeneric(privateInput)) { allArgs.push({ type: 'generic', index: genericArgs.length }); genericArgs.push(privateInput); @@ -500,39 +616,79 @@ type MethodInterface = { // reasonable default choice for `overrideWrapDomain` const maxProofsToWrapDomain = { 0: 0, 1: 1, 2: 1 } as const; -async function compileProgram( - publicInputType: ProvablePure, - publicOutputType: ProvablePure, - methodIntfs: MethodInterface[], - methods: ((...args: any) => void)[], - proofSystemTag: { name: string }, - overrideWrapDomain?: 0 | 1 | 2 -) { +async function compileProgram({ + publicInputType, + publicOutputType, + methodIntfs, + methods, + gates, + proofSystemTag, + cache, + forceRecompile, + overrideWrapDomain, +}: { + publicInputType: ProvablePure; + publicOutputType: ProvablePure; + methodIntfs: MethodInterface[]; + methods: ((...args: any) => void)[]; + gates: Gate[][]; + proofSystemTag: { name: string }; + cache: Cache; + forceRecompile: boolean; + overrideWrapDomain?: 0 | 1 | 2; +}) { let rules = methodIntfs.map((methodEntry, i) => picklesRuleFromFunction( publicInputType, publicOutputType, methods[i], proofSystemTag, - methodEntry + methodEntry, + gates[i] ) ); let maxProofs = getMaxProofsVerified(methodIntfs); overrideWrapDomain ??= maxProofsToWrapDomain[maxProofs]; + let picklesCache: Pickles.Cache = [ + 0, + function read_(mlHeader) { + if (forceRecompile) return MlResult.unitError(); + let header = parseHeader(proofSystemTag.name, methodIntfs, mlHeader); + let result = readCache(cache, header, (bytes) => + decodeProverKey(mlHeader, bytes) + ); + if (result === undefined) return MlResult.unitError(); + return MlResult.ok(result); + }, + function write_(mlHeader, value) { + if (!cache.canWrite) return MlResult.unitError(); + + let header = parseHeader(proofSystemTag.name, methodIntfs, mlHeader); + let didWrite = writeCache(cache, header, encodeProverKey(value)); + + if (!didWrite) return MlResult.unitError(); + return MlResult.ok(undefined); + }, + MlBool(cache.canWrite), + ]; + let { verificationKey, provers, verify, tag } = await prettifyStacktracePromise( withThreadPool(async () => { let result: ReturnType; let id = snarkContext.enter({ inCompile: true }); + setSrsCache(cache); try { result = Pickles.compile(MlArray.to(rules), { publicInputSize: publicInputType.sizeInFields(), publicOutputSize: publicOutputType.sizeInFields(), + storable: picklesCache, overrideWrapDomain, }); } finally { snarkContext.leave(id); + unsetSrsCache(); } let { getVerificationKey, provers, verify, tag } = result; CompiledTag.store(proofSystemTag, tag); @@ -589,7 +745,8 @@ function picklesRuleFromFunction( publicOutputType: ProvablePure, func: (...args: unknown[]) => any, proofSystemTag: { name: string }, - { methodName, witnessArgs, proofArgs, allArgs }: MethodInterface + { methodName, witnessArgs, proofArgs, allArgs }: MethodInterface, + gates: Gate[] ): Pickles.Rule { function main(publicInput: MlFieldArray): ReturnType { let { witnesses: argsWithoutPublicInput, inProver } = snarkContext.get(); @@ -620,7 +777,7 @@ function picklesRuleFromFunction( proofs.push(proofInstance); let input = toFieldVars(type.input, publicInput); let output = toFieldVars(type.output, publicOutput); - previousStatements.push(MlTuple(input, output)); + previousStatements.push(MlPair(input, output)); } else if (arg.type === 'generic') { finalArgs[i] = argsWithoutPublicInput?.[i] ?? emptyGeneric(); } @@ -664,9 +821,13 @@ function picklesRuleFromFunction( return { isSelf: false, tag: compiledTag }; } }); + + let featureFlags = computeFeatureFlags(gates); + return { identifier: methodName, main, + featureFlags, proofsToVerify: MlArray.to(proofsToVerify), }; } @@ -817,9 +978,57 @@ ZkProgram.Proof = function < static tag = () => program; }; }; +ExperimentalZkProgram.Proof = ZkProgram.Proof; + +function dummyProof(maxProofsVerified: 0 | 1 | 2, domainLog2: number) { + return withThreadPool( + async () => Pickles.dummyProof(maxProofsVerified, domainLog2)[1] + ); +} + +async function dummyBase64Proof() { + let proof = await dummyProof(2, 15); + return Pickles.proofToBase64([2, proof]); +} + +// what feature flags to set to enable certain gate types + +const gateToFlag: Partial> = { + RangeCheck0: 'rangeCheck0', + RangeCheck1: 'rangeCheck1', + ForeignFieldAdd: 'foreignFieldAdd', + ForeignFieldMul: 'foreignFieldMul', + Xor16: 'xor', + Rot64: 'rot', + Lookup: 'lookup', +}; -function dummyBase64Proof() { - return withThreadPool(async () => Pickles.dummyBase64Proof()); +function computeFeatureFlags(gates: Gate[]): MlFeatureFlags { + let flags: FeatureFlags = { + rangeCheck0: false, + rangeCheck1: false, + foreignFieldAdd: false, + foreignFieldMul: false, + xor: false, + rot: false, + lookup: false, + runtimeTables: false, + }; + for (let gate of gates) { + let flag = gateToFlag[gate.type]; + if (flag !== undefined) flags[flag] = true; + } + return [ + 0, + MlBool(flags.rangeCheck0), + MlBool(flags.rangeCheck1), + MlBool(flags.foreignFieldAdd), + MlBool(flags.foreignFieldMul), + MlBool(flags.xor), + MlBool(flags.rot), + MlBool(flags.lookup), + MlBool(flags.runtimeTables), + ]; } // helpers for circuit context @@ -913,3 +1122,30 @@ type UnwrapPromise

= P extends Promise ? T : never; type Get = T extends { [K in Key]: infer Value } ? Value : undefined; + +// deprecated experimental API + +function ExperimentalZkProgram< + StatementType extends { + publicInput?: FlexibleProvablePure; + publicOutput?: FlexibleProvablePure; + }, + Types extends { + [I in string]: Tuple; + } +>( + config: StatementType & { + name?: string; + methods: { + [I in keyof Types]: Method< + InferProvableOrUndefined>, + InferProvableOrVoid>, + Types[I] + >; + }; + overrideWrapDomain?: 0 | 1 | 2; + } +) { + let config_ = { ...config, name: config.name ?? `Program${i++}` }; + return ZkProgram(config_); +} diff --git a/src/lib/proof_system.unit-test.ts b/src/lib/proof_system.unit-test.ts index e7ec592915..3aab1dc9a5 100644 --- a/src/lib/proof_system.unit-test.ts +++ b/src/lib/proof_system.unit-test.ts @@ -1,35 +1,141 @@ -import { Field } from './core.js'; +import { Field, Bool } from './core.js'; import { Struct } from './circuit_value.js'; import { UInt64 } from './int.js'; -import { ZkProgram } from './proof_system.js'; +import { + CompiledTag, + Empty, + Proof, + ZkProgram, + picklesRuleFromFunction, + sortMethodArguments, +} from './proof_system.js'; import { expect } from 'expect'; +import { Pickles, ProvablePure, Snarky } from '../snarky.js'; +import { AnyFunction } from './util/types.js'; +import { snarkContext } from './provable-context.js'; +import { it } from 'node:test'; +import { Provable } from './provable.js'; +import { bool, equivalentAsync, field, record } from './testing/equivalent.js'; +import { FieldConst, FieldVar } from './field.js'; const EmptyProgram = ZkProgram({ + name: 'empty', publicInput: Field, + methods: { run: { privateInputs: [], method: (_) => {} } }, +}); + +class EmptyProof extends ZkProgram.Proof(EmptyProgram) {} + +// unit-test zkprogram creation helpers: +// -) sortMethodArguments +// -) picklesRuleFromFunction + +it('pickles rule creation', async () => { + // a rule that verifies a proof conditionally, and returns the proof's input as output + function main(proof: EmptyProof, shouldVerify: Bool) { + proof.verifyIf(shouldVerify); + return proof.publicInput; + } + let privateInputs = [EmptyProof, Bool]; + + // collect method interface + let methodIntf = sortMethodArguments('mock', 'main', privateInputs, Proof); + + expect(methodIntf).toEqual({ + methodName: 'main', + witnessArgs: [Bool], + proofArgs: [EmptyProof], + allArgs: [ + { type: 'proof', index: 0 }, + { type: 'witness', index: 0 }, + ], + genericArgs: [], + }); + + // store compiled tag + CompiledTag.store(EmptyProgram, 'mock tag'); + + // create pickles rule + let rule: Pickles.Rule = picklesRuleFromFunction( + Empty as ProvablePure, + Field as ProvablePure, + main as AnyFunction, + { name: 'mock' }, + methodIntf, + [] + ); + + await equivalentAsync( + { from: [field, bool], to: record({ field, bool }) }, + { runs: 5 } + )( + (field, bool) => ({ field, bool }), + async (field, bool) => { + let dummy = await EmptyProof.dummy(field, undefined, 0); + let field_: FieldConst = [0, 0n]; + let bool_: FieldConst = [0, 0n]; + + Provable.runAndCheck(() => { + // put witnesses in snark context + snarkContext.get().witnesses = [dummy, bool]; + + // call pickles rule + let { + publicOutput: [, publicOutput], + shouldVerify: [, shouldVerify], + } = rule.main([0]); + + // `publicOutput` and `shouldVerify` are as expected + Snarky.field.assertEqual(publicOutput, dummy.publicInput.value); + Snarky.field.assertEqual(shouldVerify, bool.value); + + Provable.asProver(() => { + field_ = Snarky.field.readVar(publicOutput); + bool_ = Snarky.field.readVar(shouldVerify); + }); + }); + + return { field: Field(field_), bool: Bool(FieldVar.constant(bool_)) }; + } + ); +}); + +// compile works with large inputs + +const N = 100_000; + +const program = ZkProgram({ + name: 'large-array-program', methods: { - run: { - privateInputs: [], - method: (publicInput: Field) => {}, + baseCase: { + privateInputs: [Provable.Array(Field, N)], + method(_: Field[]) {}, }, }, }); +it('can compile program with large input', async () => { + await program.compile(); +}); + +// regression tests for some zkprograms const emptyMethodsMetadata = EmptyProgram.analyzeMethods(); -emptyMethodsMetadata.forEach((methodMetadata) => { - expect(methodMetadata).toEqual({ +expect(emptyMethodsMetadata.run).toEqual( + expect.objectContaining({ rows: 0, digest: '4f5ddea76d29cfcfd8c595f14e31f21b', result: undefined, gates: [], publicInputSize: 0, - }); -}); + }) +); class CounterPublicInput extends Struct({ current: UInt64, updated: UInt64, }) {} const CounterProgram = ZkProgram({ + name: 'counter', publicInput: CounterPublicInput, methods: { increment: { @@ -45,5 +151,5 @@ const CounterProgram = ZkProgram({ }, }); -const incrementMethodMetadata = CounterProgram.analyzeMethods()[0]; +const incrementMethodMetadata = CounterProgram.analyzeMethods().increment; expect(incrementMethodMetadata).toEqual(expect.objectContaining({ rows: 18 })); diff --git a/src/lib/provable-context.ts b/src/lib/provable-context.ts index 076e9f8300..3b79562889 100644 --- a/src/lib/provable-context.ts +++ b/src/lib/provable-context.ts @@ -1,7 +1,8 @@ import { Context } from './global-context.js'; -import { Gate, JsonGate, Snarky } from '../snarky.js'; +import { Gate, GateType, JsonGate, Snarky } from '../snarky.js'; import { parseHexString } from '../bindings/crypto/bigint-helpers.js'; import { prettifyStacktrace } from './errors.js'; +import { Fp } from '../bindings/crypto/finite_field.js'; // internal API export { @@ -17,6 +18,7 @@ export { inCompile, inCompileMode, gatesFromJson, + printGates, }; // global circuit-related context @@ -94,7 +96,25 @@ function constraintSystem(f: () => T) { result = f(); }); let { gates, publicInputSize } = gatesFromJson(json); - return { rows, digest, result: result! as T, gates, publicInputSize }; + return { + rows, + digest, + result: result! as T, + gates, + publicInputSize, + print() { + printGates(gates); + }, + summary() { + let gateTypes: Partial> = {}; + gateTypes['Total rows'] = rows; + for (let gate of gates) { + gateTypes[gate.type] ??= 0; + gateTypes[gate.type]!++; + } + return gateTypes; + }, + }; } catch (error) { throw prettifyStacktrace(error); } finally { @@ -106,8 +126,62 @@ function constraintSystem(f: () => T) { function gatesFromJson(cs: { gates: JsonGate[]; public_input_size: number }) { let gates: Gate[] = cs.gates.map(({ typ, wires, coeffs: hexCoeffs }) => { - let coeffs = hexCoeffs.map(hex => parseHexString(hex).toString()); + let coeffs = hexCoeffs.map((hex) => parseHexString(hex).toString()); return { type: typ, wires, coeffs }; }); return { publicInputSize: cs.public_input_size, gates }; } + +// print a constraint system + +function printGates(gates: Gate[]) { + for (let i = 0, n = gates.length; i < n; i++) { + let { type, wires, coeffs } = gates[i]; + console.log( + i.toString().padEnd(4, ' '), + type.padEnd(15, ' '), + coeffsToPretty(type, coeffs).padEnd(30, ' '), + wiresToPretty(wires, i) + ); + } + console.log(); +} + +let minusRange = Fp.modulus - (1n << 64n); + +function coeffsToPretty(type: Gate['type'], coeffs: Gate['coeffs']): string { + if (coeffs.length === 0) return ''; + if (type === 'Generic' && coeffs.length > 5) { + let first = coeffsToPretty(type, coeffs.slice(0, 5)); + let second = coeffsToPretty(type, coeffs.slice(5)); + return `${first} ${second}`; + } + if (type === 'Poseidon' && coeffs.length > 3) { + return `${coeffsToPretty(type, coeffs.slice(0, 3)).slice(0, -1)} ...]`; + } + let str = coeffs + .map((c) => { + let c0 = BigInt(c); + if (c0 > minusRange) c0 -= Fp.modulus; + let cStr = c0.toString(); + if (cStr.length > 4) return `${cStr.slice(0, 4)}..`; + return cStr; + }) + .join(' '); + return `[${str}]`; +} + +function wiresToPretty(wires: Gate['wires'], row: number) { + let strWires: string[] = []; + let n = wires.length; + for (let col = 0; col < n; col++) { + let wire = wires[col]; + if (wire.row === row && wire.col === col) continue; + if (wire.row === row) { + strWires.push(`${col}->${wire.col}`); + } else { + strWires.push(`${col}->(${wire.row},${wire.col})`); + } + } + return strWires.join(', '); +} diff --git a/src/lib/provable-types/bytes.ts b/src/lib/provable-types/bytes.ts new file mode 100644 index 0000000000..9bde301e69 --- /dev/null +++ b/src/lib/provable-types/bytes.ts @@ -0,0 +1,130 @@ +import { provableFromClass } from '../../bindings/lib/provable-snarky.js'; +import { ProvablePureExtended } from '../circuit_value.js'; +import { assert } from '../gadgets/common.js'; +import { chunkString } from '../util/arrays.js'; +import { Provable } from '../provable.js'; +import { UInt8 } from '../int.js'; +import { randomBytes } from '../../bindings/crypto/random.js'; + +export { Bytes, createBytes, FlexibleBytes }; + +type FlexibleBytes = Bytes | (UInt8 | bigint | number)[] | Uint8Array; + +/** + * A provable type representing an array of bytes. + */ +class Bytes { + bytes: UInt8[]; + + constructor(bytes: UInt8[]) { + let size = (this.constructor as typeof Bytes).size; + + // assert that data is not too long + assert( + bytes.length <= size, + `Expected at most ${size} bytes, got ${bytes.length}` + ); + + // pad the data with zeros + let padding = Array.from( + { length: size - bytes.length }, + () => new UInt8(0) + ); + this.bytes = bytes.concat(padding); + } + + /** + * Coerce the input to {@link Bytes}. + * + * Inputs smaller than `this.size` are padded with zero bytes. + */ + static from(data: (UInt8 | bigint | number)[] | Uint8Array | Bytes): Bytes { + if (data instanceof Bytes) return data; + if (this._size === undefined) { + let Bytes_ = createBytes(data.length); + return Bytes_.from(data); + } + return new this([...data].map(UInt8.from)); + } + + toBytes(): Uint8Array { + return Uint8Array.from(this.bytes.map((x) => x.toNumber())); + } + + toFields() { + return this.bytes.map((x) => x.value); + } + + /** + * Create {@link Bytes} from a string. + * + * Inputs smaller than `this.size` are padded with zero bytes. + */ + static fromString(s: string) { + let bytes = new TextEncoder().encode(s); + return this.from(bytes); + } + + /** + * Create random {@link Bytes} using secure builtin randomness. + */ + static random() { + let bytes = randomBytes(this.size); + return this.from(bytes); + } + + /** + * Create {@link Bytes} from a hex string. + * + * Inputs smaller than `this.size` are padded with zero bytes. + */ + static fromHex(xs: string): Bytes { + let bytes = chunkString(xs, 2).map((s) => parseInt(s, 16)); + return this.from(bytes); + } + + /** + * Convert {@link Bytes} to a hex string. + */ + toHex(): string { + return this.bytes + .map((x) => x.toBigInt().toString(16).padStart(2, '0')) + .join(''); + } + + // dynamic subclassing infra + static _size?: number; + static _provable?: ProvablePureExtended< + Bytes, + { bytes: { value: string }[] } + >; + + /** + * The size of the {@link Bytes}. + */ + static get size() { + assert(this._size !== undefined, 'Bytes not initialized'); + return this._size; + } + + get length() { + return this.bytes.length; + } + + /** + * `Provable` + */ + static get provable() { + assert(this._provable !== undefined, 'Bytes not initialized'); + return this._provable; + } +} + +function createBytes(size: number): typeof Bytes { + return class Bytes_ extends Bytes { + static _size = size; + static _provable = provableFromClass(Bytes_, { + bytes: Provable.Array(UInt8, size), + }); + }; +} diff --git a/src/lib/provable-types/provable-types.ts b/src/lib/provable-types/provable-types.ts new file mode 100644 index 0000000000..ad11e446b0 --- /dev/null +++ b/src/lib/provable-types/provable-types.ts @@ -0,0 +1,21 @@ +import { Bytes as InternalBytes, createBytes } from './bytes.js'; + +export { Bytes }; + +type Bytes = InternalBytes; + +/** + * A provable type representing an array of bytes. + * + * ```ts + * class Bytes32 extends Bytes(32) {} + * + * let bytes = Bytes32.fromHex('deadbeef'); + * ``` + */ +function Bytes(size: number) { + return createBytes(size); +} +Bytes.from = InternalBytes.from; +Bytes.fromHex = InternalBytes.fromHex; +Bytes.fromString = InternalBytes.fromString; diff --git a/src/lib/provable.ts b/src/lib/provable.ts index 0b1a5e00aa..8094bcf89e 100644 --- a/src/lib/provable.ts +++ b/src/lib/provable.ts @@ -13,7 +13,6 @@ import { InferProvable, InferredProvable, } from '../bindings/lib/provable-snarky.js'; -import { isField } from './field.js'; import { inCheckedComputation, inProver, @@ -23,7 +22,6 @@ import { runUnchecked, constraintSystem, } from './provable-context.js'; -import { isBool } from './bool.js'; // external API export { Provable }; @@ -119,6 +117,17 @@ const Provable = { * ``` */ Array: provableArray, + /** + * Check whether a value is constant. + * See {@link FieldVar} for more information about constants and variables. + * + * @example + * ```ts + * let x = Field(42); + * Provable.isConstant(Field, x); // true + * ``` + */ + isConstant, /** * Interface to log elements within a circuit. Similar to `console.log()`. * @example @@ -334,11 +343,7 @@ function ifImplicit(condition: Bool, x: T, y: T): T { ); // TODO remove second condition once we have consolidated field class back into one // if (type !== y.constructor) { - if ( - type !== y.constructor && - !(isField(x) && isField(y)) && - !(isBool(x) && isBool(y)) - ) { + if (type !== y.constructor) { throw Error( 'Provable.if: Mismatched argument types. Try using an explicit type argument:\n' + `Provable.if(bool, MyType, x, y)` @@ -392,6 +397,10 @@ function switch_>( return (type as Provable).fromFields(fields, aux); } +function isConstant(type: Provable, x: T): boolean { + return type.toFields(x).every((x) => x.isConstant()); +} + // logging in provable code function log(...args: any) { @@ -566,5 +575,12 @@ function provableArray>( HashInput.empty ); }, + + empty() { + if (!('empty' in type)) { + throw Error('circuitArray.empty: element type has no empty() method'); + } + return Array.from({ length }, () => type.empty()); + }, } satisfies ProvableExtended as any; } diff --git a/src/lib/provable.unit-test.ts b/src/lib/provable.unit-test.ts new file mode 100644 index 0000000000..9c14a0f02c --- /dev/null +++ b/src/lib/provable.unit-test.ts @@ -0,0 +1,23 @@ +import { it } from 'node:test'; +import { Provable } from './provable.js'; +import { exists } from './gadgets/common.js'; +import { Field } from './field.js'; +import { expect } from 'expect'; + +it('can witness large field array', () => { + let N = 100_000; + let arr = Array(N).fill(0n); + + Provable.runAndCheck(() => { + // with exists + let fields = exists(N, () => arr); + + // with Provable.witness + let fields2 = Provable.witness(Provable.Array(Field, N), () => + arr.map(Field.from) + ); + + expect(fields.length).toEqual(N); + expect(fields2.length).toEqual(N); + }); +}); diff --git a/src/lib/scalar.ts b/src/lib/scalar.ts index 176478947f..d54f20c209 100644 --- a/src/lib/scalar.ts +++ b/src/lib/scalar.ts @@ -88,7 +88,7 @@ class Scalar { * Convert this {@link Scalar} into a bigint */ toBigInt() { - return this.#assertConstant('toBigInt'); + return assertConstant(this, 'toBigInt'); } // TODO: fix this API. we should represent "shifted status" internally and use @@ -112,17 +112,13 @@ class Scalar { // operations on constant scalars - #assertConstant(name: string) { - return constantScalarToBigint(this, `Scalar.${name}`); - } - /** * Negate a scalar field element. * * **Warning**: This method is not available for provable code. */ neg() { - let x = this.#assertConstant('neg'); + let x = assertConstant(this, 'neg'); let z = Fq.negate(x); return Scalar.from(z); } @@ -133,8 +129,8 @@ class Scalar { * **Warning**: This method is not available for provable code. */ add(y: Scalar) { - let x = this.#assertConstant('add'); - let y0 = y.#assertConstant('add'); + let x = assertConstant(this, 'add'); + let y0 = assertConstant(y, 'add'); let z = Fq.add(x, y0); return Scalar.from(z); } @@ -145,8 +141,8 @@ class Scalar { * **Warning**: This method is not available for provable code. */ sub(y: Scalar) { - let x = this.#assertConstant('sub'); - let y0 = y.#assertConstant('sub'); + let x = assertConstant(this, 'sub'); + let y0 = assertConstant(y, 'sub'); let z = Fq.sub(x, y0); return Scalar.from(z); } @@ -157,8 +153,8 @@ class Scalar { * **Warning**: This method is not available for provable code. */ mul(y: Scalar) { - let x = this.#assertConstant('mul'); - let y0 = y.#assertConstant('mul'); + let x = assertConstant(this, 'mul'); + let y0 = assertConstant(y, 'mul'); let z = Fq.mul(x, y0); return Scalar.from(z); } @@ -170,8 +166,8 @@ class Scalar { * **Warning**: This method is not available for provable code. */ div(y: Scalar) { - let x = this.#assertConstant('div'); - let y0 = y.#assertConstant('div'); + let x = assertConstant(this, 'div'); + let y0 = assertConstant(y, 'div'); let z = Fq.div(x, y0); if (z === undefined) throw Error('Scalar.div(): Division by zero'); return Scalar.from(z); @@ -179,11 +175,11 @@ class Scalar { // TODO don't leak 'shifting' to the user and remove these methods shift() { - let x = this.#assertConstant('shift'); + let x = assertConstant(this, 'shift'); return Scalar.from(shift(x)); } unshift() { - let x = this.#assertConstant('unshift'); + let x = assertConstant(this, 'unshift'); return Scalar.from(unshift(x)); } @@ -196,7 +192,7 @@ class Scalar { * is needed to represent all Scalars. However, for a random Scalar, the high bit will be `false` with overwhelming probability. */ toFieldsCompressed(): { field: Field; highBit: Bool } { - let s = this.#assertConstant('toFieldsCompressed'); + let s = assertConstant(this, 'toFieldsCompressed'); let lowBitSize = BigInt(Fq.sizeInBits - 1); let lowBitMask = (1n << lowBitSize) - 1n; return { @@ -292,7 +288,7 @@ class Scalar { * This operation does _not_ affect the circuit and can't be used to prove anything about the string representation of the Scalar. */ static toJSON(x: Scalar) { - let s = x.#assertConstant('toJSON'); + let s = assertConstant(x, 'toJSON'); return s.toString(); } @@ -312,6 +308,12 @@ class Scalar { } } +// internal helpers + +function assertConstant(x: Scalar, name: string) { + return constantScalarToBigint(x, `Scalar.${name}`); +} + function toConstantScalar([, ...bits]: MlArray): Fq | undefined { if (bits.length !== Fq.sizeInBits) throw Error( diff --git a/src/lib/signature.ts b/src/lib/signature.ts index e26e68ca2b..58d68ccef0 100644 --- a/src/lib/signature.ts +++ b/src/lib/signature.ts @@ -165,8 +165,8 @@ class PublicKey extends CircuitValue { * Creates an empty {@link PublicKey}. * @returns an empty {@link PublicKey} */ - static empty() { - return PublicKey.from({ x: Field(0), isOdd: Bool(false) }); + static empty(): InstanceType { + return PublicKey.from({ x: Field(0), isOdd: Bool(false) }) as any; } /** diff --git a/src/lib/state.ts b/src/lib/state.ts index 5be755d94d..dc848a1996 100644 --- a/src/lib/state.ts +++ b/src/lib/state.ts @@ -22,17 +22,17 @@ type State = { * Get the current on-chain state. * * Caution: If you use this method alone inside a smart contract, it does not prove that your contract uses the current on-chain state. - * To successfully prove that your contract uses the current on-chain state, you must add an additional `.assertEquals()` statement or use `.getAndAssertEquals()`: + * To successfully prove that your contract uses the current on-chain state, you must add an additional `.requireEquals()` statement or use `.getAndRequireEquals()`: * * ```ts * let x = this.x.get(); - * this.x.assertEquals(x); + * this.x.requireEquals(x); * ``` * * OR * * ```ts - * let x = this.x.getAndAssertEquals(); + * let x = this.x.getAndRequireEquals(); * ``` */ get(): A; @@ -40,6 +40,10 @@ type State = { * Get the current on-chain state and prove it really has to equal the on-chain state, * by adding a precondition which the verifying Mina node will check before accepting this transaction. */ + getAndRequireEquals(): A; + /** + * @deprecated use `this.state.getAndRequireEquals()` which is equivalent + */ getAndAssertEquals(): A; /** * Set the on-chain state to a new value. @@ -53,10 +57,18 @@ type State = { * Prove that the on-chain state has to equal the given state, * by adding a precondition which the verifying Mina node will check before accepting this transaction. */ + requireEquals(a: A): void; + /** + * @deprecated use `this.state.requireEquals()` which is equivalent + */ assertEquals(a: A): void; /** * **DANGER ZONE**: Override the error message that warns you when you use `.get()` without adding a precondition. */ + requireNothing(): void; + /** + * @deprecated use `this.state.requireNothing()` which is equivalent + */ assertNothing(): void; /** * Get the state from the raw list of field elements on a zkApp account, for example: @@ -203,10 +215,10 @@ function createState(): InternalStateType { }); }, - assertEquals(state: T) { + requireEquals(state: T) { if (this._contract === undefined) throw Error( - 'assertEquals can only be called when the State is assigned to a SmartContract @state.' + 'requireEquals can only be called when the State is assigned to a SmartContract @state.' ); let layout = getLayoutPosition(this._contract); let stateAsFields = this._contract.stateType.toFields(state); @@ -220,14 +232,22 @@ function createState(): InternalStateType { this._contract.wasConstrained = true; }, - assertNothing() { + assertEquals(state: T) { + this.requireEquals(state); + }, + + requireNothing() { if (this._contract === undefined) throw Error( - 'assertNothing can only be called when the State is assigned to a SmartContract @state.' + 'requireNothing can only be called when the State is assigned to a SmartContract @state.' ); this._contract.wasConstrained = true; }, + assertNothing() { + this.requireNothing(); + }, + get() { if (this._contract === undefined) throw Error( @@ -294,12 +314,16 @@ function createState(): InternalStateType { return state; }, - getAndAssertEquals() { + getAndRequireEquals() { let state = this.get(); - this.assertEquals(state); + this.requireEquals(state); return state; }, + getAndAssertEquals() { + return this.getAndRequireEquals(); + }, + async fetch() { if (this._contract === undefined) throw Error( diff --git a/src/lib/string.ts b/src/lib/string.ts index 623aa4a72e..8045dd3318 100644 --- a/src/lib/string.ts +++ b/src/lib/string.ts @@ -2,6 +2,7 @@ import { Bool, Field } from '../lib/core.js'; import { arrayProp, CircuitValue, prop } from './circuit_value.js'; import { Provable } from './provable.js'; import { Poseidon } from './hash.js'; +import { Gadgets } from './gadgets/gadgets.js'; export { Character, CircuitString }; @@ -31,7 +32,7 @@ class Character extends CircuitValue { // TODO: Add support for more character sets // right now it's 16 bits because 8 not supported :/ static check(c: Character) { - c.value.rangeCheckHelper(16).assertEquals(c.value); + Gadgets.rangeCheckN(16, c.value); } } diff --git a/src/lib/testing/constraint-system.ts b/src/lib/testing/constraint-system.ts new file mode 100644 index 0000000000..1f8ad9ba52 --- /dev/null +++ b/src/lib/testing/constraint-system.ts @@ -0,0 +1,452 @@ +/** + * DSL for testing that a gadget generates the expected constraint system. + * + * An essential feature is that `constraintSystem()` automatically generates a + * variety of fieldvar types for the inputs: constants, variables, and combinators. + */ +import { Gate, GateType } from '../../snarky.js'; +import { randomBytes } from '../../bindings/crypto/random.js'; +import { Field, FieldType, FieldVar } from '../field.js'; +import { Provable } from '../provable.js'; +import { Tuple } from '../util/types.js'; +import { Random } from './random.js'; +import { test } from './property.js'; +import { Undefined, ZkProgram } from '../proof_system.js'; +import { printGates } from '../provable-context.js'; + +export { + constraintSystem, + not, + and, + or, + satisfies, + equals, + contains, + allConstant, + ifNotAllConstant, + isEmpty, + withoutGenerics, + print, + repeat, + ConstraintSystemTest, +}; + +/** + * `constraintSystem()` is a test runner to check properties of constraint systems. + * You give it a description of inputs and a circuit, as well as a `ConstraintSystemTest` to assert + * properties on the generated constraint system. + * + * As input variables, we generate random combinations of constants, variables, add & scale combinators, + * to poke for the common problem of gate chains broken by unexpected Generic gates. + * + * The `constraintSystemTest` is written using a DSL of property assertions, such as {@link equals} and {@link contains}. + * To run multiple assertions, use the {@link and} / {@link or} combinators. + * To debug the constraint system, use the {@link print} test or `and(print, ...otherTests)`. + * + * @param label description of the constraint system + * @param inputs input spec in form `{ from: [...provables] }` + * @param main circuit to test + * @param constraintSystemTest property test to run on the constraint system + */ +function constraintSystem>>( + label: string, + inputs: { from: Input }, + main: (...args: CsParams) => void, + constraintSystemTest: ConstraintSystemTest +) { + // create random generators + let types = inputs.from.map(provable); + let rngs = types.map(layout); + + test(...rngs, (...args) => { + let layouts = args.slice(0, -1); + + // compute the constraint system + let { gates } = Provable.constraintSystem(() => { + // each random input "layout" has to be instantiated into vars in this circuit + let values = types.map((type, i) => + instantiate(type, layouts[i]) + ) as CsParams; + main(...values); + }); + + // run tests + let typesAndValues = types.map((type, i) => ({ type, value: layouts[i] })); + + let { ok, failures } = run(constraintSystemTest, gates, typesAndValues); + + if (!ok) { + console.log('Constraint system:'); + printGates(gates); + + throw Error( + `Constraint system test: ${label}\n\n${failures + .map((f) => `FAIL: ${f}`) + .join('\n')}\n` + ); + } + }); +} + +/** + * Convenience function to run {@link constraintSystem} on the method of a {@link ZkProgram}. + * + * @example + * ```ts + * const program = ZkProgram({ methods: { myMethod: ... }, ... }); + * + * constraintSystem.fromZkProgram(program, 'myMethod', contains('Rot64')); + * ``` + */ +constraintSystem.fromZkProgram = function fromZkProgram< + T, + K extends keyof T & string +>( + program: { privateInputTypes: T }, + methodName: K, + test: ConstraintSystemTest +) { + let program_: ZkProgram = program as any; + let from: any = [...program_.privateInputTypes[methodName]]; + if (program_.publicInputType !== Undefined) { + from.unshift(program_.publicInputType); + } + return constraintSystem( + `${program_.name} / ${methodName}()`, + { from }, + program_.rawMethods[methodName], + test + ); +}; + +// DSL for writing tests + +type ConstraintSystemTestBase = { + run: (cs: Gate[], inputs: TypeAndValue[]) => boolean; + label: string; +}; +type Base = { kind?: undefined } & ConstraintSystemTestBase; +type Not = { kind: 'not' } & ConstraintSystemTestBase; +type And = { kind: 'and'; tests: ConstraintSystemTest[]; label: string }; +type Or = { kind: 'or'; tests: ConstraintSystemTest[]; label: string }; +type ConstraintSystemTest = Base | Not | And | Or; + +type Result = { ok: boolean; failures: string[] }; + +function run( + test: ConstraintSystemTest, + cs: Gate[], + inputs: TypeAndValue[] +): Result { + switch (test.kind) { + case undefined: { + let ok = test.run(cs, inputs); + let failures = ok ? [] : [test.label]; + return { ok, failures }; + } + case 'not': { + let ok = test.run(cs, inputs); + let failures = ok ? [`not(${test.label})`] : []; + return { ok: !ok, failures }; + } + case 'and': { + let results = test.tests.map((t) => run(t, cs, inputs)); + let ok = results.every((r) => r.ok); + let failures = ok ? [] : results.flatMap((r) => r.failures); + return { ok, failures }; + } + case 'or': { + let results = test.tests.map((t) => run(t, cs, inputs)); + let ok = results.some((r) => r.ok); + let failures = ok ? [] : results.flatMap((r) => r.failures); + return { ok, failures }; + } + } +} + +/** + * Negate a test. + */ +function not(test: ConstraintSystemTest): ConstraintSystemTest { + return { kind: 'not', ...test }; +} +/** + * Check that all input tests pass. + */ +function and(...tests: ConstraintSystemTest[]): ConstraintSystemTest { + return { kind: 'and', tests, label: `and(${tests.map((t) => t.label)})` }; +} +/** + * Check that at least one input test passes. + */ +function or(...tests: ConstraintSystemTest[]): ConstraintSystemTest { + return { kind: 'or', tests, label: `or(${tests.map((t) => t.label)})` }; +} + +/** + * General test + */ +function satisfies( + label: string, + run: (cs: Gate[], inputs: TypeAndValue[]) => boolean +): ConstraintSystemTest { + return { run, label }; +} + +/** + * Test for precise equality of the constraint system with a given list of gates. + */ +function equals(gates: readonly GateType[]): ConstraintSystemTest { + return { + run(cs) { + if (cs.length !== gates.length) return false; + return cs.every((g, i) => g.type === gates[i]); + }, + label: `equals ${JSON.stringify(gates)}`, + }; +} + +/** + * Test that constraint system contains each of a list of gates consecutively. + * + * You can also pass a list of lists. In that case, the constraint system has to contain + * each of the lists of gates in the given order, but not necessarily consecutively. + * + * @example + * ```ts + * // constraint system contains a Rot64 gate + * contains('Rot64') + * + * // constraint system contains a Rot64 gate, followed directly by a RangeCheck0 gate + * contains(['Rot64', 'RangeCheck0']) + * + * // constraint system contains two instances of the combination [Rot64, RangeCheck0] + * contains([['Rot64', 'RangeCheck0'], ['Rot64', 'RangeCheck0']]]) + * ``` + */ +function contains( + gates: GateType | readonly GateType[] | readonly GateType[][] +): ConstraintSystemTest { + let expectedGatess = toGatess(gates); + return { + run(cs) { + let gates = cs.map((g) => g.type); + let i = 0; + let j = 0; + for (let gate of gates) { + if (gate === expectedGatess[i][j]) { + j++; + if (j === expectedGatess[i].length) { + i++; + j = 0; + if (i === expectedGatess.length) return true; + } + } else if (gate === expectedGatess[i][0]) { + j = 1; + } else { + j = 0; + } + } + return false; + }, + label: `contains ${JSON.stringify(expectedGatess)}`, + }; +} + +/** + * Test whether all inputs are constant. + */ +const allConstant: ConstraintSystemTest = { + run(cs, inputs) { + return inputs.every(({ type, value }) => + type.toFields(value).every((x) => x.isConstant()) + ); + }, + label: 'all inputs constant', +}; + +/** + * Modifies a test so that it doesn't fail if all inputs are constant, and instead + * checks that the constraint system is empty in that case. + */ +function ifNotAllConstant(test: ConstraintSystemTest): ConstraintSystemTest { + return or(test, and(allConstant, isEmpty)); +} + +/** + * Test whether constraint system is empty. + */ +const isEmpty = satisfies( + 'constraint system is empty', + (cs) => cs.length === 0 +); + +/** + * Modifies a test so that it runs on the constraint system with generic gates filtered out. + */ +function withoutGenerics(test: ConstraintSystemTest): ConstraintSystemTest { + return { + run(cs, inputs) { + return run( + test, + cs.filter((g) => g.type !== 'Generic'), + inputs + ).ok; + }, + label: `withoutGenerics(${test.label})`, + }; +} + +/** + * "Test" that just pretty-prints the constraint system. + */ +const print: ConstraintSystemTest = { + run(cs) { + console.log('Constraint system:'); + printGates(cs); + return true; + }, + label: '', +}; + +// Do other useful things with constraint systems + +/** + * Get constraint system as a list of gates. + */ +constraintSystem.gates = function gates>>( + inputs: { from: Input }, + main: (...args: CsParams) => void +) { + let types = inputs.from.map(provable); + let { gates } = Provable.constraintSystem(() => { + let values = types.map((type) => + Provable.witness(type, (): unknown => { + throw Error('not needed'); + }) + ) as CsParams; + main(...values); + }); + return gates; +}; + +function map(transform: (gates: Gate[]) => T) { + return >>( + inputs: { from: Input }, + main: (...args: CsParams) => void + ) => transform(constraintSystem.gates(inputs, main)); +} + +/** + * Get size of constraint system. + */ +constraintSystem.size = map((gates) => gates.length); + +/** + * Print constraint system. + */ +constraintSystem.print = map(printGates); + +function repeat( + n: number, + gates: GateType | readonly GateType[] +): readonly GateType[] { + gates = Array.isArray(gates) ? gates : [gates]; + return Array(n).fill(gates).flat(); +} + +function toGatess( + gateTypes: GateType | readonly GateType[] | readonly GateType[][] +): GateType[][] { + if (typeof gateTypes === 'string') return [[gateTypes]]; + if (Array.isArray(gateTypes[0])) return gateTypes as GateType[][]; + return [gateTypes as GateType[]]; +} + +// Random generator for arbitrary provable types + +function provable(spec: CsVarSpec): Provable { + return 'provable' in spec ? spec.provable : spec; +} + +function layout(type: Provable): Random { + let length = type.sizeInFields(); + + return Random(() => { + let fields = Array.from({ length }, () => new Field(drawFieldVar())); + return type.fromFields(fields, type.toAuxiliary()); + }); +} + +function instantiate(type: Provable, value: T) { + let fields = type.toFields(value).map((x) => instantiateFieldVar(x.value)); + return type.fromFields(fields, type.toAuxiliary()); +} + +// Random generator for fieldvars that exercises constants, variables and combinators + +function drawFieldVar(): FieldVar { + let fieldType = drawFieldType(); + switch (fieldType) { + case FieldType.Constant: { + return FieldVar.constant(1n); + } + case FieldType.Var: { + return [FieldType.Var, 0]; + } + case FieldType.Add: { + let x = drawFieldVar(); + let y = drawFieldVar(); + // prevent blow-up of constant size + if (x[0] === FieldType.Constant && y[0] === FieldType.Constant) return x; + return FieldVar.add(x, y); + } + case FieldType.Scale: { + let x = drawFieldVar(); + // prevent blow-up of constant size + if (x[0] === FieldType.Constant) return x; + return FieldVar.scale(3n, x); + } + } +} + +function instantiateFieldVar(x: FieldVar): Field { + switch (x[0]) { + case FieldType.Constant: { + return new Field(x); + } + case FieldType.Var: { + return Provable.witness(Field, () => Field.from(0n)); + } + case FieldType.Add: { + let a = instantiateFieldVar(x[1]); + let b = instantiateFieldVar(x[2]); + return a.add(b); + } + case FieldType.Scale: { + let a = instantiateFieldVar(x[2]); + return a.mul(x[1][1]); + } + } +} + +function drawFieldType(): FieldType { + let oneOf8 = randomBytes(1)[0] & 0b111; + if (oneOf8 < 4) return FieldType.Var; + if (oneOf8 < 6) return FieldType.Constant; + if (oneOf8 === 6) return FieldType.Scale; + return FieldType.Add; +} + +// types + +type CsVarSpec = Provable | { provable: Provable }; +type InferCsVar = T extends { provable: Provable } + ? U + : T extends Provable + ? U + : never; +type CsParams>> = { + [k in keyof In]: InferCsVar; +}; +type TypeAndValue = { type: Provable; value: T }; diff --git a/src/lib/testing/equivalent.ts b/src/lib/testing/equivalent.ts index c50c662a0a..7a0f20fd7d 100644 --- a/src/lib/testing/equivalent.ts +++ b/src/lib/testing/equivalent.ts @@ -4,134 +4,408 @@ import { test, Random } from '../testing/property.js'; import { Provable } from '../provable.js'; import { deepEqual } from 'node:assert/strict'; +import { Bool, Field } from '../core.js'; +import { AnyFunction, Tuple } from '../util/types.js'; +import { provable } from '../circuit_value.js'; +import { assert } from '../gadgets/common.js'; -export { createEquivalenceTesters, throwError, handleErrors }; +export { + equivalent, + equivalentProvable, + equivalentAsync, + oneOf, + throwError, + handleErrors, + deepEqual as defaultAssertEqual, + id, +}; +export { + spec, + field, + fieldWithRng, + bigintField, + bool, + boolean, + unit, + array, + record, + map, + onlyIf, + fromRandom, + first, + second, +}; +export { + Spec, + ToSpec, + FromSpec, + SpecFromFunctions, + ProvableSpec, + First, + Second, +}; -function createEquivalenceTesters( - Field: Provable, - newField: (x: bigint) => Field -) { - function equivalent1( - op1: (x: Field) => Field, - op2: (x: bigint) => bigint, - rng: Random = Random.field +// a `Spec` tells us how to compare two functions + +type FromSpec = { + // `rng` creates random inputs to the first function + rng: Random; + + // `there` converts to inputs to the second function + there: (x: In1) => In2; + + // `provable` tells us how to create witnesses, to test provable code + // note: we only allow the second function to be provable; + // the second because it's more natural to have non-provable types as random generator output + provable?: Provable; +}; + +type ToSpec = { + // `back` converts outputs of the second function back to match the first function + back: (x: Out2) => Out1; + + // `assertEqual` to compare outputs against each other; defaults to `deepEqual` + assertEqual?: (x: Out1, y: Out1, message: string) => void; +}; + +type Spec = FromSpec & ToSpec; + +type ProvableSpec = Spec & { provable: Provable }; + +type FuncSpec, Out1, In2 extends Tuple, Out2> = { + from: { + [k in keyof In1]: k extends keyof In2 ? FromSpec : never; + }; + to: ToSpec; +}; + +type SpecFromFunctions< + F1 extends AnyFunction, + F2 extends AnyFunction +> = FuncSpec, ReturnType, Parameters, ReturnType>; + +function id(x: T) { + return x; +} + +// unions of specs, to cleanly model function parameters that are unions of types + +type FromSpecUnion = { + _isUnion: true; + specs: Tuple>; + rng: Random<[number, T1]>; +}; + +type OrUnion = FromSpec | FromSpecUnion; + +type Union = T[keyof T & number]; + +function oneOf>>( + ...specs: In +): FromSpecUnion>, Union>> { + // the randomly generated value from a union keeps track of which spec it came from + let rng = Random.oneOf( + ...specs.map((spec, i) => + Random.map(spec.rng, (x) => [i, x] as [number, any]) + ) + ); + return { _isUnion: true, specs, rng }; +} + +function toUnion(spec: OrUnion): FromSpecUnion { + let specAny = spec as any; + return specAny._isUnion ? specAny : oneOf(specAny); +} + +// equivalence tester + +function equivalent< + In extends Tuple>, + Out extends ToSpec +>({ from, to, verbose }: { from: In; to: Out; verbose?: boolean }) { + return function run( + f1: (...args: Params1) => First, + f2: (...args: Params2) => Second, + label = 'expect equal results' ) { - test(rng, (x0, assert) => { - let x = newField(x0); - // outside provable code + let generators = from.map((spec) => spec.rng); + let assertEqual = to.assertEqual ?? deepEqual; + let start = performance.now(); + let nRuns = test(...(generators as any[]), (...args) => { + args.pop(); + let inputs = args as Params1; handleErrors( - () => op1(x), - () => op2(x0), - (a, b) => assert(a.toBigInt() === b, 'equal results') + () => f1(...inputs), + () => + to.back( + f2(...(inputs.map((x, i) => from[i].there(x)) as Params2)) + ), + (x, y) => assertEqual(x, y, label), + label ); - // inside provable code - Provable.runAndCheck(() => { - x = Provable.witness(Field, () => x); - handleErrors( - () => op1(x), - () => op2(x0), - (a, b) => - Provable.asProver(() => assert(a.toBigInt() === b, 'equal results')) - ); - }); }); - } - function equivalent2( - op1: (x: Field, y: Field | bigint) => Field, - op2: (x: bigint, y: bigint) => bigint, - rng: Random = Random.field - ) { - test(rng, rng, (x0, y0, assert) => { - let x = newField(x0); - let y = newField(y0); - // outside provable code - handleErrors( - () => op1(x, y), - () => op2(x0, y0), - (a, b) => assert(a.toBigInt() === b, 'equal results') - ); - handleErrors( - () => op1(x, y0), - () => op2(x0, y0), - (a, b) => assert(a.toBigInt() === b, 'equal results') + + if (verbose) { + let ms = (performance.now() - start).toFixed(1); + let runs = nRuns.toString().padStart(2, ' '); + console.log( + `${label.padEnd(20, ' ')} success on ${runs} runs in ${ms}ms.` ); - // inside provable code - Provable.runAndCheck(() => { - x = Provable.witness(Field, () => x); - y = Provable.witness(Field, () => y); - handleErrors( - () => op1(x, y), - () => op2(x0, y0), - (a, b) => - Provable.asProver(() => assert(a.toBigInt() === b, 'equal results')) - ); - handleErrors( - () => op1(x, y0), - () => op2(x0, y0), - (a, b) => - Provable.asProver(() => assert(a.toBigInt() === b, 'equal results')) - ); - }); - }); - } - function equivalentVoid1( - op1: (x: Field) => void, - op2: (x: bigint) => void, - rng: Random = Random.field + } + }; +} + +// async equivalence + +function equivalentAsync< + In extends Tuple>, + Out extends ToSpec +>({ from, to }: { from: In; to: Out }, { runs = 1 } = {}) { + return async function run( + f1: (...args: Params1) => Promise> | First, + f2: (...args: Params2) => Promise> | Second, + label = 'expect equal results' ) { - test(rng, (x0) => { - let x = newField(x0); - // outside provable code - handleErrors( - () => op1(x), - () => op2(x0) - ); - // inside provable code - Provable.runAndCheck(() => { - x = Provable.witness(Field, () => x); - handleErrors( - () => op1(x), - () => op2(x0) + let generators = from.map((spec) => spec.rng); + let assertEqual = to.assertEqual ?? deepEqual; + + let nexts = generators.map((g) => g.create()); + + for (let i = 0; i < runs; i++) { + let args = nexts.map((next) => next()); + let inputs = args as Params1; + try { + await handleErrorsAsync( + () => f1(...inputs), + async () => + to.back( + await f2( + ...(inputs.map((x, i) => from[i].there(x)) as Params2) + ) + ), + (x, y) => assertEqual(x, y, label), + label ); - }); - }); - } - function equivalentVoid2( - op1: (x: Field, y: Field | bigint) => void, - op2: (x: bigint, y: bigint) => void, - rng: Random = Random.field + } catch (err) { + console.log(...inputs); + throw err; + } + } + }; +} + +// equivalence tester for provable code + +function isProvable(spec: FromSpecUnion) { + return spec.specs.some((spec) => spec.provable); +} + +function equivalentProvable< + In extends Tuple>, + Out extends ToSpec +>({ from: fromRaw, to, verbose }: { from: In; to: Out; verbose?: boolean }) { + let fromUnions = fromRaw.map(toUnion); + assert(fromUnions.some(isProvable), 'equivalentProvable: no provable input'); + + return function run( + f1: (...args: Params1) => First, + f2: (...args: Params2) => Second, + label = 'expect equal results' ) { - test(rng, rng, (x0, y0) => { - let x = newField(x0); - let y = newField(y0); - // outside provable code - handleErrors( - () => op1(x, y), - () => op2(x0, y0) + let generators = fromUnions.map((spec) => spec.rng); + let assertEqual = to.assertEqual ?? deepEqual; + + let start = performance.now(); + let nRuns = test.custom({ minRuns: 5 })(...generators, (...args) => { + args.pop(); + + // figure out which spec to use for each argument + let from = (args as [number, unknown][]).map( + ([j], i) => fromUnions[i].specs[j] ); + let inputs = (args as [number, unknown][]).map( + ([, x]) => x + ) as Params1; + let inputs2 = inputs.map((x, i) => from[i].there(x)) as Params2; + + // outside provable code handleErrors( - () => op1(x, y0), - () => op2(x0, y0) + () => f1(...inputs), + () => f2(...inputs2), + (x, y) => assertEqual(x, to.back(y), label), + label ); + // inside provable code Provable.runAndCheck(() => { - x = Provable.witness(Field, () => x); - y = Provable.witness(Field, () => y); + let inputWitnesses = inputs2.map((x, i) => { + let provable = from[i].provable; + return provable !== undefined + ? Provable.witness(provable, () => x) + : x; + }) as Params2; handleErrors( - () => op1(x, y), - () => op2(x0, y0) - ); - handleErrors( - () => op1(x, y0), - () => op2(x0, y0) + () => f1(...inputs), + () => f2(...inputWitnesses), + (x, y) => Provable.asProver(() => assertEqual(x, to.back(y), label)), + label ); }); }); - } + if (verbose) { + let ms = (performance.now() - start).toFixed(1); + let runs = nRuns.toString().padStart(2, ' '); + console.log( + `${label.padEnd(20, ' ')} success on ${runs} runs in ${ms}ms.` + ); + } + }; +} + +// creating specs - return { equivalent1, equivalent2, equivalentVoid1, equivalentVoid2 }; +function spec(spec: { + rng: Random; + there: (x: T) => S; + back: (x: S) => T; + assertEqual?: (x: T, y: T, message: string) => void; + provable: Provable; +}): ProvableSpec; +function spec(spec: { + rng: Random; + there: (x: T) => S; + back: (x: S) => T; + assertEqual?: (x: T, y: T, message: string) => void; +}): Spec; +function spec(spec: { + rng: Random; + assertEqual?: (x: T, y: T, message: string) => void; +}): Spec; +function spec(spec: { + rng: Random; + there?: (x: T) => S; + back?: (x: S) => T; + assertEqual?: (x: T, y: T, message: string) => void; + provable?: Provable; +}): Spec { + return { + rng: spec.rng, + there: spec.there ?? (id as any), + back: spec.back ?? (id as any), + assertEqual: spec.assertEqual, + provable: spec.provable, + }; } +// some useful specs + +let unit: ToSpec = { back: id, assertEqual() {} }; + +let field: ProvableSpec = { + rng: Random.field, + there: Field, + back: (x) => x.toBigInt(), + provable: Field, +}; + +let bigintField: Spec = { + rng: Random.field, + there: id, + back: id, +}; + +let bool: ProvableSpec = { + rng: Random.boolean, + there: Bool, + back: (x) => x.toBoolean(), + provable: Bool, +}; +let boolean: Spec = fromRandom(Random.boolean); + +function fieldWithRng(rng: Random): Spec { + return { ...field, rng }; +} + +// spec combinators + +function array( + spec: ProvableSpec, + n: number +): ProvableSpec; +function array( + spec: Spec, + n: Random | number +): Spec; +function array( + spec: Spec, + n: Random | number +): Spec { + return { + rng: Random.array(spec.rng, n), + there: (x) => x.map(spec.there), + back: (x) => x.map(spec.back), + provable: + typeof n === 'number' && spec.provable + ? Provable.Array(spec.provable, n) + : undefined, + }; +} + +function record }>( + specs: Specs +): Spec< + { [k in keyof Specs]: First }, + { [k in keyof Specs]: Second } +> { + let isProvable = Object.values(specs).every((spec) => spec.provable); + return { + rng: Random.record(mapObject(specs, (spec) => spec.rng)) as any, + there: (x) => mapObject(specs, (spec, k) => spec.there(x[k])) as any, + back: (x) => mapObject(specs, (spec, k) => spec.back(x[k])) as any, + provable: isProvable + ? provable(mapObject(specs, (spec) => spec.provable) as any) + : undefined, + }; +} + +function map( + { from, to }: { from: FromSpec; to: Spec }, + there: (t: T1) => S1 +): Spec { + return { ...to, rng: Random.map(from.rng, there) }; +} + +function onlyIf(spec: Spec, onlyIf: (t: T) => boolean): Spec { + return { ...spec, rng: Random.reject(spec.rng, (x) => !onlyIf(x)) }; +} + +function mapObject( + t: { [k in K]: T }, + map: (t: T, k: K) => S +): { [k in K]: S } { + return Object.fromEntries( + Object.entries(t).map(([k, v]) => [k, map(v, k as K)]) + ) as any; +} + +function fromRandom(rng: Random): Spec { + return { rng, there: id, back: id }; +} + +function first(spec: Spec): Spec { + return { rng: spec.rng, there: id, back: id }; +} +function second(spec: Spec): Spec { + return { + rng: Random.map(spec.rng, spec.there), + there: id, + back: id, + provable: spec.provable, + }; +} + +// helper to ensure two functions throw equivalent errors + function handleErrors( op1: () => T, op2: () => S, @@ -162,6 +436,67 @@ function handleErrors( } } +async function handleErrorsAsync( + op1: () => T, + op2: () => S, + useResults?: (a: Awaited, b: Awaited) => R, + label?: string +): Promise { + let result1: Awaited, result2: Awaited; + let error1: Error | undefined; + let error2: Error | undefined; + try { + result1 = await op1(); + } catch (err) { + error1 = err as Error; + } + try { + result2 = await op2(); + } catch (err) { + error2 = err as Error; + } + if (!!error1 !== !!error2) { + error1 && console.log(error1); + error2 && console.log(error2); + } + let message = `${(label && `${label}: `) || ''}equivalent errors`; + deepEqual(!!error1, !!error2, message); + if (!(error1 || error2) && useResults !== undefined) { + return useResults(result1!, result2!); + } +} + function throwError(message?: string): any { throw Error(message); } + +// infer input types from specs + +type Param1> = In extends { + there: (x: infer In) => any; +} + ? In + : In extends FromSpecUnion + ? T1 + : never; +type Param2> = In extends { + there: (x: any) => infer In; +} + ? In + : In extends FromSpecUnion + ? T2 + : never; + +type Params1>> = { + [k in keyof Ins]: Param1; +}; +type Params2>> = { + [k in keyof Ins]: Param2; +}; + +type First> = Out extends ToSpec + ? Out1 + : never; +type Second> = Out extends ToSpec + ? Out2 + : never; diff --git a/src/lib/testing/random.ts b/src/lib/testing/random.ts index 3324908e45..8dd41ac38f 100644 --- a/src/lib/testing/random.ts +++ b/src/lib/testing/random.ts @@ -5,7 +5,7 @@ import { Json, AccountUpdate, ZkappCommand, - emptyValue, + empty, } from '../../bindings/mina-transaction/gen/transaction-bigint.js'; import { AuthRequired, @@ -26,7 +26,7 @@ import { import { genericLayoutFold } from '../../bindings/lib/from-layout.js'; import { jsLayout } from '../../bindings/mina-transaction/gen/js-layout.js'; import { - GenericProvable, + PrimitiveTypeMap, primitiveTypeMap, } from '../../bindings/lib/generic.js'; import { Scalar, PrivateKey, Group } from '../../provable/curve-bigint.js'; @@ -35,10 +35,11 @@ import { randomBytes } from '../../bindings/crypto/random.js'; import { alphabet } from '../base58.js'; import { bytesToBigInt } from '../../bindings/crypto/bigint-helpers.js'; import { Memo } from '../../mina-signer/src/memo.js'; -import { ProvableExtended } from '../../bindings/lib/provable-bigint.js'; +import { Signable } from '../../bindings/lib/provable-bigint.js'; import { tokenSymbolLength } from '../../bindings/mina-transaction/derived-leaves.js'; import { stringLengthInBytes } from '../../bindings/lib/binable.js'; import { mocks } from '../../bindings/crypto/constants.js'; +import type { FiniteField } from '../../bindings/crypto/finite_field.js'; export { Random, sample, withHardCoded }; @@ -65,6 +66,7 @@ function sample(rng: Random, size: number) { const boolean = Random_(() => drawOneOf8() < 4); const bool = map(boolean, Bool); +const uint8 = biguintWithInvalid(8); const uint32 = biguintWithInvalid(32); const uint64 = biguintWithInvalid(64); const byte = Random_(drawRandomByte); @@ -80,7 +82,7 @@ const keypair = map(privateKey, (privatekey) => ({ publicKey: PrivateKey.toPublicKey(privatekey), })); -const tokenId = oneOf(TokenId.emptyValue(), field); +const tokenId = oneOf(TokenId.empty(), field); const stateHash = field; const authRequired = map( oneOf( @@ -105,16 +107,16 @@ const actions = mapWithInvalid( array(array(field, int(1, 5)), nat(2)), Actions.fromList ); -const actionState = oneOf(ActionState.emptyValue(), field); -const verificationKeyHash = oneOf(VerificationKeyHash.emptyValue(), field); -const receiptChainHash = oneOf(ReceiptChainHash.emptyValue(), field); +const actionState = oneOf(ActionState.empty(), field); +const verificationKeyHash = oneOf(VerificationKeyHash.empty(), field); +const receiptChainHash = oneOf(ReceiptChainHash.empty(), field); const zkappUri = map(string(nat(50)), ZkappUri.fromJSON); -const PrimitiveMap = primitiveTypeMap(); -type Types = typeof TypeMap & typeof customTypes & typeof PrimitiveMap; -type Provable = GenericProvable; +type Types = typeof TypeMap & typeof customTypes & PrimitiveTypeMap; type Generators = { - [K in keyof Types]: Types[K] extends Provable ? Random : never; + [K in keyof Types]: Types[K] extends Signable + ? Random + : never; }; const Generators: Generators = { Field: field, @@ -137,8 +139,8 @@ const Generators: Generators = { string: base58(nat(50)), // TODO replace various strings, like signature, with parsed types number: nat(3), }; -let typeToBigintGenerator = new Map, Random>( - [TypeMap, PrimitiveMap, customTypes] +let typeToBigintGenerator = new Map, Random>( + [TypeMap, primitiveTypeMap, customTypes] .map(Object.entries) .flat() .map(([key, value]) => [value, Generators[key as keyof Generators]]) @@ -186,17 +188,20 @@ const nonNumericString = reject( string(nat(20)), (str: any) => !isNaN(str) && !isNaN(parseFloat(str)) ); -const invalidUint64Json = toString( - oneOf(uint64.invalid, nonInteger, nonNumericString) +const invalidUint8Json = toString( + oneOf(uint8.invalid, nonInteger, nonNumericString) ); const invalidUint32Json = toString( oneOf(uint32.invalid, nonInteger, nonNumericString) ); +const invalidUint64Json = toString( + oneOf(uint64.invalid, nonInteger, nonNumericString) +); // some json versions of those types let json_ = { - uint64: { ...toString(uint64), invalid: invalidUint64Json }, uint32: { ...toString(uint32), invalid: invalidUint32Json }, + uint64: { ...toString(uint64), invalid: invalidUint64Json }, publicKey: withInvalidBase58(mapWithInvalid(publicKey, PublicKey.toBase58)), privateKey: withInvalidBase58(map(privateKey, PrivateKey.toBase58)), keypair: map(keypair, ({ privatekey, publicKey }) => ({ @@ -213,7 +218,7 @@ function withInvalidRandomString(rng: Random) { } type JsonGenerators = { - [K in keyof Types]: Types[K] extends ProvableExtended + [K in keyof Types]: Types[K] extends Signable ? Random : never; }; @@ -240,8 +245,8 @@ const JsonGenerators: JsonGenerators = { string: base58(nat(50)), number: nat(3), }; -let typeToJsonGenerator = new Map, Random>( - [TypeMap, PrimitiveMap, customTypes] +let typeToJsonGenerator = new Map, Random>( + [TypeMap, primitiveTypeMap, customTypes] .map(Object.entries) .flat() .map(([key, value]) => [value, JsonGenerators[key as keyof JsonGenerators]]) @@ -307,10 +312,13 @@ const Random = Object.assign(Random_, { reject, dice: Object.assign(dice, { ofSize: diceOfSize() }), field, + otherField: fieldWithInvalid, bool, + uint8, uint32, uint64, biguint: biguintWithInvalid, + bignat: bignatWithInvalid, privateKey, publicKey, scalar, @@ -326,7 +334,13 @@ function generatorFromLayout( { isJson }: { isJson: boolean } ): Random { let typeToGenerator = isJson ? typeToJsonGenerator : typeToBigintGenerator; - return genericLayoutFold, TypeMap, Json.TypeMap>( + return genericLayoutFold< + Signable, + undefined, + Random, + TypeMap, + Json.TypeMap + >( TypeMap, customTypes, { @@ -356,7 +370,7 @@ function generatorFromLayout( } else { return mapWithInvalid(isSome, value, (isSome, value) => { let isSomeBoolean = TypeMap.Bool.toJSON(isSome); - if (!isSomeBoolean) return emptyValue(typeData); + if (!isSomeBoolean) return empty(typeData); return { isSome, value }; }); } @@ -658,14 +672,14 @@ function int(min: number, max: number): Random { * log-uniform distribution over range [0, max] * with bias towards 0, 1, 2 */ -function nat(max: number): Random { - if (max < 0) throw Error('max < 0'); - if (max === 0) return constant(0); +function bignat(max: bigint): Random { + if (max < 0n) throw Error('max < 0'); + if (max === 0n) return constant(0n); let bits = max.toString(2).length; let bitBits = bits.toString(2).length; // set of special numbers that will appear more often in tests - let special = [0, 0, 1]; - if (max > 1) special.push(2); + let special = [0n, 0n, 1n]; + if (max > 1n) special.push(2n); let nSpecial = special.length; return { create: () => () => { @@ -681,13 +695,21 @@ function nat(max: number): Random { let bitLength = 1 + drawUniformUintBits(bitBits); if (bitLength > bits) continue; // draw number from [0, 2**bitLength); reject if > max - let n = drawUniformUintBits(bitLength); + let n = drawUniformBigUintBits(bitLength); if (n <= max) return n; } }, }; } +/** + * log-uniform distribution over range [0, max] + * with bias towards 0, 1, 2 + */ +function nat(max: number): Random { + return map(bignat(BigInt(max)), (n) => Number(n)); +} + function fraction(fixedPrecision = 3) { let denom = 10 ** fixedPrecision; if (fixedPrecision < 1) throw Error('precision must be > 1'); @@ -825,12 +847,21 @@ function biguintWithInvalid(bits: number): RandomWithInvalid { return Object.assign(valid, { invalid }); } -function fieldWithInvalid( - F: typeof Field | typeof Scalar -): RandomWithInvalid { +function bignatWithInvalid(max: bigint): RandomWithInvalid { + let valid = bignat(max); + let double = bignat(2n * max); + let negative = map(double, (uint) => -uint - 1n); + let tooLarge = map(valid, (uint) => uint + max); + let invalid = oneOf(negative, tooLarge); + return Object.assign(valid, { invalid }); +} + +function fieldWithInvalid(F: FiniteField): RandomWithInvalid { let randomField = Random_(F.random); - let specialField = oneOf(0n, 1n, F(-1)); - let field = oneOf(randomField, randomField, uint64, specialField); + let specialField = oneOf(0n, 1n, F.negate(1n)); + let roughLogSize = 1 << Math.ceil(Math.log2(F.sizeInBits) - 1); + let uint = biguint(roughLogSize); + let field = oneOf(randomField, randomField, uint, specialField); let tooLarge = map(field, (x) => x + F.modulus); let negative = map(field, (x) => -x - 1n); let invalid = oneOf(tooLarge, negative); diff --git a/src/lib/testing/testing.unit-test.ts b/src/lib/testing/testing.unit-test.ts index 4a0abe91fb..6041b155b4 100644 --- a/src/lib/testing/testing.unit-test.ts +++ b/src/lib/testing/testing.unit-test.ts @@ -6,11 +6,11 @@ import { PublicKey, UInt32, UInt64, - provableFromLayout, + signableFromLayout, ZkappCommand, Json, } from '../../bindings/mina-transaction/gen/transaction-bigint.js'; -import { test, Random, sample } from './property.js'; +import { test, Random } from './property.js'; // some trivial roundtrip tests test(Random.accountUpdate, (accountUpdate, assert) => { @@ -20,10 +20,11 @@ test(Random.accountUpdate, (accountUpdate, assert) => { jsonString === JSON.stringify(AccountUpdate.toJSON(AccountUpdate.fromJSON(json))) ); - let fields = AccountUpdate.toFields(accountUpdate); - let auxiliary = AccountUpdate.toAuxiliary(accountUpdate); - let recovered = AccountUpdate.fromFields(fields, auxiliary); - assert(jsonString === JSON.stringify(AccountUpdate.toJSON(recovered))); + // TODO add back using `fromValue` + // let fields = AccountUpdate.toFields(accountUpdate); + // let auxiliary = AccountUpdate.toAuxiliary(accountUpdate); + // let recovered = AccountUpdate.fromFields(fields, auxiliary); + // assert(jsonString === JSON.stringify(AccountUpdate.toJSON(recovered))); }); test(Random.json.accountUpdate, (json) => { let jsonString = JSON.stringify(json); @@ -52,7 +53,7 @@ test.custom({ negative: true, timeBudget: 1000 })( AccountUpdate.fromJSON ); -const FeePayer = provableFromLayout< +const FeePayer = signableFromLayout< ZkappCommand['feePayer'], Json.ZkappCommand['feePayer'] >(jsLayout.ZkappCommand.entries.feePayer as any); diff --git a/src/lib/util/arrays.ts b/src/lib/util/arrays.ts new file mode 100644 index 0000000000..2a1a913eef --- /dev/null +++ b/src/lib/util/arrays.ts @@ -0,0 +1,14 @@ +import { assert } from '../gadgets/common.js'; + +export { chunk, chunkString }; + +function chunk(array: T[], size: number): T[][] { + assert(array.length % size === 0, 'invalid input length'); + return Array.from({ length: array.length / size }, (_, i) => + array.slice(size * i, size * (i + 1)) + ); +} + +function chunkString(str: string, size: number): string[] { + return chunk([...str], size).map((c) => c.join('')); +} diff --git a/src/lib/util/fs.ts b/src/lib/util/fs.ts new file mode 100644 index 0000000000..4d08832363 --- /dev/null +++ b/src/lib/util/fs.ts @@ -0,0 +1,5 @@ +import cachedir from 'cachedir'; + +export { writeFileSync, readFileSync, mkdirSync } from 'node:fs'; +export { resolve } from 'node:path'; +export { cachedir as cacheDir }; diff --git a/src/lib/util/fs.web.ts b/src/lib/util/fs.web.ts new file mode 100644 index 0000000000..6810aff2fd --- /dev/null +++ b/src/lib/util/fs.web.ts @@ -0,0 +1,15 @@ +export { + dummy as writeFileSync, + dummy as readFileSync, + dummy as resolve, + dummy as mkdirSync, + cacheDir, +}; + +function dummy() { + throw Error('not implemented'); +} + +function cacheDir() { + return ''; +} diff --git a/src/lib/util/types.ts b/src/lib/util/types.ts new file mode 100644 index 0000000000..3256e60476 --- /dev/null +++ b/src/lib/util/types.ts @@ -0,0 +1,57 @@ +import { assert } from '../errors.js'; + +export { AnyFunction, Tuple, TupleN, AnyTuple, TupleMap }; + +type AnyFunction = (...args: any) => any; + +type Tuple = [T, ...T[]] | []; +type AnyTuple = Tuple; + +type TupleMap, B> = [ + ...{ + [i in keyof T]: B; + } +]; + +const Tuple = { + map, B>( + tuple: T, + f: (a: T[number]) => B + ): TupleMap { + return tuple.map(f) as any; + }, +}; + +/** + * tuple type that has the length as generic parameter + */ +type TupleN = N extends N + ? number extends N + ? [...T[]] // N is not typed as a constant => fall back to array + : [...TupleRec] + : never; + +const TupleN = { + map, B>( + tuple: T, + f: (a: T[number]) => B + ): TupleMap { + return tuple.map(f) as any; + }, + + fromArray(n: N, arr: T[]): TupleN { + assert( + arr.length === n, + `Expected array of length ${n}, got ${arr.length}` + ); + return arr as any; + }, + + hasLength(n: N, tuple: T[]): tuple is TupleN { + return tuple.length === n; + }, +}; + +type TupleRec = R['length'] extends N + ? R + : TupleRec; diff --git a/src/lib/zkapp.ts b/src/lib/zkapp.ts index 50b1265441..9346973f72 100644 --- a/src/lib/zkapp.ts +++ b/src/lib/zkapp.ts @@ -22,7 +22,6 @@ import { FlexibleProvablePure, InferProvable, provable, - Struct, toConstant, } from './circuit_value.js'; import { Provable, getBlindingValue, memoizationContext } from './provable.js'; @@ -56,6 +55,7 @@ import { inProver, snarkContext, } from './provable-context.js'; +import { Cache } from './proof-system/cache.js'; // external API export { @@ -65,7 +65,6 @@ export { declareMethods, Callback, Account, - VerificationKey, Reducer, }; @@ -196,7 +195,8 @@ function wrapMethod( let id = memoizationContext.enter({ ...context, blindingValue }); let result: unknown; try { - result = method.apply(this, actualArgs.map(cloneCircuitValue)); + let clonedArgs = actualArgs.map(cloneCircuitValue); + result = method.apply(this, clonedArgs); } finally { memoizationContext.leave(id); } @@ -661,7 +661,10 @@ class SmartContract { * it so that proofs end up in the original finite field). These are fairly expensive operations, so **expect compiling to take at least 20 seconds**, * up to several minutes if your circuit is large or your hardware is not optimal for these operations. */ - static async compile() { + static async compile({ + cache = Cache.FileSystemDefault, + forceRecompile = false, + } = {}) { let methodIntfs = this._methods ?? []; let methods = methodIntfs.map(({ methodName }) => { return ( @@ -675,22 +678,18 @@ class SmartContract { }; }); // run methods once to get information that we need already at compile time - this.analyzeMethods(); - let { - verificationKey: verificationKey_, - provers, - verify, - } = await compileProgram( - ZkappPublicInput, - Empty, + let methodsMeta = this.analyzeMethods(); + let gates = methodIntfs.map((intf) => methodsMeta[intf.methodName].gates); + let { verificationKey, provers, verify } = await compileProgram({ + publicInputType: ZkappPublicInput, + publicOutputType: Empty, methodIntfs, methods, - this - ); - let verificationKey = { - data: verificationKey_.data, - hash: Field(verificationKey_.hash), - } satisfies VerificationKey; + gates, + proofSystemTag: this, + cache, + forceRecompile, + }); this._provers = provers; this._verificationKey = verificationKey; // TODO: instead of returning provers, return an artifact from which provers can be recovered @@ -730,7 +729,7 @@ class SmartContract { verificationKey?: { data: string; hash: Field | string }; zkappKey?: PrivateKey; } = {}) { - let accountUpdate = this.newSelf(); + let accountUpdate = this.newSelf('deploy'); verificationKey ??= (this.constructor as typeof SmartContract) ._verificationKey; if (verificationKey === undefined) { @@ -874,10 +873,10 @@ super.init(); /** * Same as `SmartContract.self` but explicitly creates a new {@link AccountUpdate}. */ - newSelf(): AccountUpdate { + newSelf(methodName?: string): AccountUpdate { let inTransaction = Mina.currentTransaction.has(); let transactionId = inTransaction ? Mina.currentTransaction.id() : NaN; - let accountUpdate = selfAccountUpdate(this); + let accountUpdate = selfAccountUpdate(this, methodName); this.#executionState = { transactionId, accountUpdate }; return accountUpdate; } @@ -1480,13 +1479,6 @@ Use the optional \`maxTransactionsWithActions\` argument to increase this number }; } -class VerificationKey extends Struct({ - ...provable({ data: String, hash: Field }), - toJSON({ data }: { data: string }) { - return data; - }, -}) {} - function selfAccountUpdate(zkapp: SmartContract, methodName?: string) { let body = Body.keepAll(zkapp.address, zkapp.tokenId); let update = new (AccountUpdate as any)(body, {}, true) as AccountUpdate; diff --git a/src/mina b/src/mina index a681b130eb..5a4e6cf36c 160000 --- a/src/mina +++ b/src/mina @@ -1 +1 @@ -Subproject commit a681b130eb1c11817fcea241c0c003ce36950c2b +Subproject commit 5a4e6cf36c153bc292fc6f6ed7710ae19b6d2e4d diff --git a/src/mina-signer/MinaSigner.ts b/src/mina-signer/MinaSigner.ts index 36c94e3acb..d4dce0e2df 100644 --- a/src/mina-signer/MinaSigner.ts +++ b/src/mina-signer/MinaSigner.ts @@ -85,9 +85,9 @@ class Client { ) { throw Error('Public key not derivable from private key'); } - let dummy = ZkappCommand.toJSON(ZkappCommand.emptyValue()); + let dummy = ZkappCommand.toJSON(ZkappCommand.empty()); dummy.feePayer.body.publicKey = publicKey; - dummy.memo = Memo.toBase58(Memo.emptyValue()); + dummy.memo = Memo.toBase58(Memo.empty()); let signed = signZkappCommand(dummy, privateKey, this.network); let ok = verifyZkappCommandSignature(signed, publicKey, this.network); if (!ok) throw Error('Could not sign a transaction with private key'); diff --git a/src/mina-signer/src/memo.ts b/src/mina-signer/src/memo.ts index 976c822ca6..04538c3965 100644 --- a/src/mina-signer/src/memo.ts +++ b/src/mina-signer/src/memo.ts @@ -62,10 +62,8 @@ const Memo = { hash, ...withBits(Binable, SIZE * 8), ...base58(Binable, versionBytes.userCommandMemo), - sizeInBytes() { - return SIZE; - }, - emptyValue() { + sizeInBytes: SIZE, + empty() { return Memo.fromString(''); }, toValidString(memo = '') { diff --git a/src/mina-signer/src/sign-zkapp-command.ts b/src/mina-signer/src/sign-zkapp-command.ts index 69578ed193..07d8a37c62 100644 --- a/src/mina-signer/src/sign-zkapp-command.ts +++ b/src/mina-signer/src/sign-zkapp-command.ts @@ -180,7 +180,7 @@ function accountUpdateFromFeePayer({ body: { fee, nonce, publicKey, validUntil }, authorization: signature, }: FeePayer): AccountUpdate { - let { body } = AccountUpdate.emptyValue(); + let { body } = AccountUpdate.empty(); body.publicKey = publicKey; body.balanceChange = { magnitude: fee, sgn: Sign(-1) }; body.incrementNonce = Bool(true); diff --git a/src/mina-signer/src/sign-zkapp-command.unit-test.ts b/src/mina-signer/src/sign-zkapp-command.unit-test.ts index 7538dbd9ff..e400f9c8d4 100644 --- a/src/mina-signer/src/sign-zkapp-command.unit-test.ts +++ b/src/mina-signer/src/sign-zkapp-command.unit-test.ts @@ -62,7 +62,7 @@ test(Random.json.publicKey, (publicKeyBase58) => { }); // empty account update -let dummy = AccountUpdate.emptyValue(); +let dummy = AccountUpdate.empty(); let dummySnarky = AccountUpdateSnarky.dummy(); expect(AccountUpdate.toJSON(dummy)).toEqual( AccountUpdateSnarky.toJSON(dummySnarky) diff --git a/src/mina-signer/src/signature.unit-test.ts b/src/mina-signer/src/signature.unit-test.ts index 2fb520f02c..9743bda23b 100644 --- a/src/mina-signer/src/signature.unit-test.ts +++ b/src/mina-signer/src/signature.unit-test.ts @@ -122,7 +122,7 @@ for (let i = 0; i < 10; i++) { [0xffff_ffff_ffff_ffffn, 64], ], }, - AccountUpdate.toInput(AccountUpdate.emptyValue()), + AccountUpdate.toInput(AccountUpdate.empty()), ]; for (let msg of messages) { checkCanVerify(msg, key, publicKey); diff --git a/src/mina-signer/tests/verify-in-snark.unit-test.ts b/src/mina-signer/tests/verify-in-snark.unit-test.ts index 104fabad6c..c32180cff0 100644 --- a/src/mina-signer/tests/verify-in-snark.unit-test.ts +++ b/src/mina-signer/tests/verify-in-snark.unit-test.ts @@ -32,6 +32,7 @@ signature.verify(publicKey, fieldsSnarky).assertTrue(); const Message = Provable.Array(Field, fields.length); const MyProgram = ZkProgram({ + name: 'verify-signature', methods: { verifySignature: { privateInputs: [Signature, Message], diff --git a/src/mina-signer/tests/zkapp.unit-test.ts b/src/mina-signer/tests/zkapp.unit-test.ts index 64d83c2bca..5c2aaf6a3f 100644 --- a/src/mina-signer/tests/zkapp.unit-test.ts +++ b/src/mina-signer/tests/zkapp.unit-test.ts @@ -11,7 +11,7 @@ import { mocks } from '../../bindings/crypto/constants.js'; const client = new Client({ network: 'testnet' }); let { publicKey, privateKey } = client.genKeys(); -let dummy = ZkappCommand.toJSON(ZkappCommand.emptyValue()); +let dummy = ZkappCommand.toJSON(ZkappCommand.empty()); let dummySignature = Signature.toBase58(Signature.dummy()); // we construct a transaction which needs signing of the fee payer and another account update diff --git a/src/provable/curve-bigint.ts b/src/provable/curve-bigint.ts index 78e1e37a45..f4d18dc4a9 100644 --- a/src/provable/curve-bigint.ts +++ b/src/provable/curve-bigint.ts @@ -11,7 +11,7 @@ import { Bool, checkRange, Field, pseudoClass } from './field-bigint.js'; import { BinableBigint, ProvableBigint, - provable, + signable, } from '../bindings/lib/provable-bigint.js'; import { HashInputLegacy } from './poseidon-bigint.js'; @@ -79,7 +79,7 @@ let BinablePublicKey = withVersionNumber( * A public key, represented by a non-zero point on the Pallas curve, in compressed form { x, isOdd } */ const PublicKey = { - ...provable({ x: Field, isOdd: Bool }), + ...signable({ x: Field, isOdd: Bool }), ...withBase58(BinablePublicKey, versionBytes.publicKey), toJSON(publicKey: PublicKey) { @@ -137,7 +137,7 @@ let Base58PrivateKey = base58(BinablePrivateKey, versionBytes.privateKey); */ const PrivateKey = { ...Scalar, - ...provable(Scalar), + ...signable(Scalar), ...Base58PrivateKey, ...BinablePrivateKey, toPublicKey(key: PrivateKey) { diff --git a/src/provable/field-bigint.ts b/src/provable/field-bigint.ts index ea2d797c83..c23e0e0bc4 100644 --- a/src/provable/field-bigint.ts +++ b/src/provable/field-bigint.ts @@ -64,9 +64,7 @@ const Bool = pseudoClass( checkBool(x); return x; }, - sizeInBytes() { - return 1; - }, + sizeInBytes: 1, fromField(x: Field) { checkBool(x); return x as 0n | 1n; @@ -111,7 +109,7 @@ const Sign = pseudoClass( { ...ProvableBigint(checkSign), ...BinableBigint(1, checkSign), - emptyValue() { + empty() { return 1n; }, toInput(x: Sign): HashInput { diff --git a/src/provable/poseidon-bigint.ts b/src/provable/poseidon-bigint.ts index 2ef684d585..906ab2a2d5 100644 --- a/src/provable/poseidon-bigint.ts +++ b/src/provable/poseidon-bigint.ts @@ -7,7 +7,7 @@ import { createHashHelpers } from '../lib/hash-generic.js'; export { Poseidon, - Hash, + HashHelpers, HashInput, prefixes, packToFields, @@ -20,8 +20,8 @@ export { type HashInput = GenericHashInput; const HashInput = createHashInput(); -const Hash = createHashHelpers(Field, Poseidon); -let { hashWithPrefix } = Hash; +const HashHelpers = createHashHelpers(Field, Poseidon); +let { hashWithPrefix } = HashHelpers; const HashLegacy = createHashHelpers(Field, PoseidonLegacy); diff --git a/src/snarky.d.ts b/src/snarky.d.ts index 1843df88bc..2c47b9dc63 100644 --- a/src/snarky.d.ts +++ b/src/snarky.d.ts @@ -1,21 +1,44 @@ import type { Account as JsonAccount } from './bindings/mina-transaction/gen/transaction-json.js'; -import type { Field, FieldConst, FieldVar } from './lib/field.js'; +import type { Field, FieldConst, FieldVar, VarFieldVar } from './lib/field.js'; import type { BoolVar, Bool } from './lib/bool.js'; import type { ScalarConst } from './lib/scalar.js'; import type { MlArray, - MlTuple, + MlPair, MlList, MlOption, MlBool, MlBytes, + MlResult, + MlUnit, + MlString, + MlTuple, } from './lib/ml/base.js'; import type { MlHashInput } from './lib/ml/conversion.js'; +import type { + SnarkKey, + SnarkKeyHeader, + MlWrapVerificationKey, +} from './lib/proof-system/prover-keys.js'; +import { getWasm } from './bindings/js/wrapper.js'; +import type { + WasmFpSrs, + WasmFqSrs, +} from './bindings/compiled/node_bindings/plonk_wasm.cjs'; +import type { KimchiGateType } from './lib/gates.ts'; -export { ProvablePure, Provable, Ledger, Pickles, Gate }; +export { ProvablePure, Provable, Ledger, Pickles, Gate, GateType, getWasm }; // internal -export { Snarky, Test, JsonGate, MlPublicKey, MlPublicKeyVar }; +export { + Snarky, + Test, + JsonGate, + MlPublicKey, + MlPublicKeyVar, + FeatureFlags, + MlFeatureFlags, +}; /** * `Provable` is the general circuit type interface in o1js. `Provable` interface describes how a type `T` is made up of {@link Field} elements and "auxiliary" (non-provable) data. @@ -134,7 +157,7 @@ declare interface ProvablePure extends Provable { check: (value: T) => void; } -type MlGroup = MlTuple; +type MlGroup = MlPair; declare namespace Snarky { type Main = (publicInput: MlArray) => void; @@ -158,11 +181,11 @@ declare const Snarky: { exists( sizeInFields: number, compute: () => MlArray - ): MlArray; + ): MlArray; /** * witness a single field element variable */ - existsVar(compute: () => FieldConst): FieldVar; + existsVar(compute: () => FieldConst): VarFieldVar; /** * APIs that have to do with running provable code @@ -258,7 +281,7 @@ declare const Snarky: { * returns a new witness from an AST * (implemented with toConstantAndTerms) */ - seal(x: FieldVar): FieldVar; + seal(x: FieldVar): VarFieldVar; /** * Unfolds AST to get `x = c + c0*Var(i0) + ... + cn*Var(in)`, * returns `(c, [(c0, i0), ..., (cn, in)])`; @@ -269,27 +292,30 @@ declare const Snarky: { ): [ _: 0, constant: MlOption, - terms: MlList> + terms: MlList> ]; }; - bool: { - not(x: BoolVar): BoolVar; - - and(x: BoolVar, y: BoolVar): BoolVar; + gates: { + zero(in1: FieldVar, in2: FieldVar, out: FieldVar): void; - or(x: BoolVar, y: BoolVar): BoolVar; + generic( + sl: FieldConst, + l: FieldVar, + sr: FieldConst, + r: FieldVar, + so: FieldConst, + o: FieldVar, + sm: FieldConst, + sc: FieldConst + ): void; - equals(x: BoolVar, y: BoolVar): BoolVar; - - assertEqual(x: BoolVar, y: BoolVar): void; - }; + poseidon(state: MlArray>): void; - group: { /** * Low-level Elliptic Curve Addition gate. */ - ecadd( + ecAdd( p1: MlGroup, p2: MlGroup, p3: MlGroup, @@ -300,6 +326,164 @@ declare const Snarky: { x21_inv: FieldVar ): MlGroup; + ecScale( + state: MlArray< + [ + _: 0, + accs: MlArray>, + bits: MlArray, + ss: MlArray, + base: MlGroup, + nPrev: Field, + nNext: Field + ] + > + ): void; + + ecEndoscale( + state: MlArray< + [ + _: 0, + xt: FieldVar, + yt: FieldVar, + xp: FieldVar, + yp: FieldVar, + nAcc: FieldVar, + xr: FieldVar, + yr: FieldVar, + s1: FieldVar, + s3: FieldVar, + b1: FieldVar, + b2: FieldVar, + b3: FieldVar, + b4: FieldVar + ] + >, + xs: FieldVar, + ys: FieldVar, + nAcc: FieldVar + ): void; + + ecEndoscalar( + state: MlArray< + [ + _: 0, + n0: FieldVar, + n8: FieldVar, + a0: FieldVar, + b0: FieldVar, + a8: FieldVar, + b8: FieldVar, + x0: FieldVar, + x1: FieldVar, + x2: FieldVar, + x3: FieldVar, + x4: FieldVar, + x5: FieldVar, + x6: FieldVar, + x7: FieldVar + ] + > + ): void; + + lookup(input: MlTuple): void; + + /** + * Range check gate + * + * @param v0 field var to be range checked + * @param v0p bits 16 to 88 as 6 12-bit limbs + * @param v0c bits 0 to 16 as 8 2-bit limbs + * @param compact boolean field elements -- whether to use "compact mode" + */ + rangeCheck0( + v0: FieldVar, + v0p: MlTuple, + v0c: MlTuple, + compact: FieldConst + ): void; + + rangeCheck1( + v2: FieldVar, + v12: FieldVar, + vCurr: MlTuple, + vNext: MlTuple + ): void; + + xor( + in1: FieldVar, + in2: FieldVar, + out: FieldVar, + in1_0: FieldVar, + in1_1: FieldVar, + in1_2: FieldVar, + in1_3: FieldVar, + in2_0: FieldVar, + in2_1: FieldVar, + in2_2: FieldVar, + in2_3: FieldVar, + out_0: FieldVar, + out_1: FieldVar, + out_2: FieldVar, + out_3: FieldVar + ): void; + + foreignFieldAdd( + left: MlTuple, + right: MlTuple, + fieldOverflow: FieldVar, + carry: FieldVar, + foreignFieldModulus: MlTuple, + sign: FieldConst + ): void; + + foreignFieldMul( + left: MlTuple, + right: MlTuple, + remainder: MlTuple, + quotient: MlTuple, + quotientHiBound: FieldVar, + product1: MlTuple, + carry0: FieldVar, + carry1p: MlTuple, + carry1c: MlTuple, + foreignFieldModulus2: FieldConst, + negForeignFieldModulus: MlTuple + ): void; + + rotate( + field: FieldVar, + rotated: FieldVar, + excess: FieldVar, + limbs: MlArray, + crumbs: MlArray, + two_to_rot: FieldConst + ): void; + + addFixedLookupTable(id: number, data: MlArray>): void; + + addRuntimeTableConfig(id: number, firstColumn: MlArray): void; + + raw( + kind: KimchiGateType, + values: MlArray, + coefficients: MlArray + ): void; + }; + + bool: { + not(x: BoolVar): BoolVar; + + and(x: BoolVar, y: BoolVar): BoolVar; + + or(x: BoolVar, y: BoolVar): BoolVar; + + equals(x: BoolVar, y: BoolVar): BoolVar; + + assertEqual(x: BoolVar, y: BoolVar): void; + }; + + group: { scale(p: MlGroup, s: MlArray): MlGroup; }; @@ -341,13 +525,14 @@ declare const Snarky: { }; }; + // TODO: implement in TS poseidon: { update( state: MlArray, input: MlArray ): [0, FieldVar, FieldVar, FieldVar]; - hashToGroup(input: MlArray): MlTuple; + hashToGroup(input: MlArray): MlPair; sponge: { create(isChecked: boolean): unknown; @@ -357,15 +542,31 @@ declare const Snarky: { }; }; +type GateType = + | 'Zero' + | 'Generic' + | 'Poseidon' + | 'CompleteAdd' + | 'VarbaseMul' + | 'EndoMul' + | 'EndoMulScalar' + | 'Lookup' + | 'RangeCheck0' + | 'RangeCheck1' + | 'ForeignFieldAdd' + | 'ForeignFieldMul' + | 'Xor16' + | 'Rot64'; + type JsonGate = { - typ: string; + typ: GateType; wires: { row: number; col: number }[]; coeffs: string[]; }; type JsonConstraintSystem = { gates: JsonGate[]; public_input_size: number }; type Gate = { - type: string; + type: GateType; wires: { row: number; col: number }[]; coeffs: string[]; }; @@ -436,7 +637,7 @@ declare const Test: { }; poseidon: { - hashToGroup(input: MlArray): MlTuple; + hashToGroup(input: MlArray): MlPair; }; signature: { @@ -495,18 +696,70 @@ declare const Test: { }; }; +type FeatureFlags = { + rangeCheck0: boolean; + rangeCheck1: boolean; + foreignFieldAdd: boolean; + foreignFieldMul: boolean; + xor: boolean; + rot: boolean; + lookup: boolean; + runtimeTables: boolean; +}; + +type MlFeatureFlags = [ + _: 0, + rangeCheck0: MlBool, + rangeCheck1: MlBool, + foreignFieldAdd: MlBool, + foreignFieldMul: MlBool, + xor: MlBool, + rot: MlBool, + lookup: MlBool, + runtimeTables: MlBool +]; + declare namespace Pickles { type Proof = unknown; // opaque to js type Statement = [_: 0, publicInput: MlArray, publicOutput: MlArray]; + + /** + * A "rule" is a circuit plus some metadata for `Pickles.compile` + */ type Rule = { identifier: string; + /** + * The main circuit functions + */ main: (publicInput: MlArray) => { publicOutput: MlArray; previousStatements: MlArray>; shouldVerify: MlArray; }; + /** + * Feature flags which enable certain custom gates + */ + featureFlags: MlFeatureFlags; + /** + * Description of previous proofs to verify in this rule + */ proofsToVerify: MlArray<{ isSelf: true } | { isSelf: false; tag: unknown }>; }; + + /** + * Type to configure how Pickles should cache prover keys + */ + type Cache = [ + _: 0, + read: (header: SnarkKeyHeader, path: string) => MlResult, + write: ( + header: SnarkKeyHeader, + value: SnarkKey, + path: string + ) => MlResult, + canWrite: MlBool + ]; + type Prover = ( publicInput: MlArray, previousProofs: MlArray @@ -537,9 +790,10 @@ declare const Pickles: { */ compile: ( rules: MlArray, - signature: { + config: { publicInputSize: number; publicOutputSize: number; + storable?: Pickles.Cache; overrideWrapDomain?: 0 | 1 | 2; } ) => { @@ -561,17 +815,32 @@ declare const Pickles: { verificationKey: string ): Promise; - dummyBase64Proof: () => string; + loadSrsFp(): WasmFpSrs; + loadSrsFq(): WasmFqSrs; + + dummyProof: ( + maxProofsVerified: N, + domainLog2: number + ) => [N, Pickles.Proof]; + /** * @returns (base64 vk, hash) */ dummyVerificationKey: () => [_: 0, data: string, hash: FieldConst]; + encodeVerificationKey: (vk: MlWrapVerificationKey) => string; + decodeVerificationKey: (vk: string) => MlWrapVerificationKey; + proofToBase64: (proof: [0 | 1 | 2, Pickles.Proof]) => string; - proofOfBase64: ( + proofOfBase64: ( base64: string, - maxProofsVerified: 0 | 1 | 2 - ) => [0 | 1 | 2, Pickles.Proof]; + maxProofsVerified: N + ) => [N, Pickles.Proof]; proofToBase64Transaction: (proof: Pickles.Proof) => string; + + util: { + toMlString(s: string): MlString; + fromMlString(s: MlString): string; + }; }; diff --git a/src/snarky.js b/src/snarky.js index 698a4a17b6..b80fa8e6bd 100644 --- a/src/snarky.js +++ b/src/snarky.js @@ -1,9 +1,9 @@ import './bindings/crypto/bindings.js'; -import { getSnarky, withThreadPool } from './bindings/js/wrapper.js'; +import { getSnarky, getWasm, withThreadPool } from './bindings/js/wrapper.js'; import snarkySpec from './bindings/js/snarky-class-spec.js'; import { proxyClasses } from './bindings/js/proxy.js'; -export { Snarky, Ledger, Pickles, Test, withThreadPool }; +export { Snarky, Ledger, Pickles, Test, withThreadPool, getWasm }; let isReadyBoolean = true; let isItReady = () => isReadyBoolean; diff --git a/src/tests/fake-proof.ts b/src/tests/fake-proof.ts new file mode 100644 index 0000000000..3607494be1 --- /dev/null +++ b/src/tests/fake-proof.ts @@ -0,0 +1,96 @@ +import { + Mina, + PrivateKey, + SmartContract, + UInt64, + method, + ZkProgram, + verify, +} from 'o1js'; +import assert from 'assert'; + +const RealProgram = ZkProgram({ + name: 'real', + methods: { + make: { + privateInputs: [UInt64], + method(value: UInt64) { + let expected = UInt64.from(34); + value.assertEquals(expected); + }, + }, + }, +}); + +const FakeProgram = ZkProgram({ + name: 'fake', + methods: { + make: { privateInputs: [UInt64], method(_: UInt64) {} }, + }, +}); + +class RealProof extends ZkProgram.Proof(RealProgram) {} + +const RecursiveProgram = ZkProgram({ + name: 'broken', + methods: { + verifyReal: { + privateInputs: [RealProof], + method(proof: RealProof) { + proof.verify(); + }, + }, + }, +}); + +class RecursiveContract extends SmartContract { + @method verifyReal(proof: RealProof) { + proof.verify(); + } +} + +Mina.setActiveInstance(Mina.LocalBlockchain()); +let publicKey = PrivateKey.random().toPublicKey(); +let zkApp = new RecursiveContract(publicKey); + +await RealProgram.compile(); +await FakeProgram.compile(); +let { verificationKey: contractVk } = await RecursiveContract.compile(); +let { verificationKey: programVk } = await RecursiveProgram.compile(); + +// proof that should be rejected +const fakeProof = await FakeProgram.make(UInt64.from(99999)); +const dummyProof = await RealProof.dummy(undefined, undefined, 0); + +for (let proof of [fakeProof, dummyProof]) { + // zkprogram rejects proof + await assert.rejects(async () => { + await RecursiveProgram.verifyReal(proof); + }, 'recursive program rejects fake proof'); + + // contract rejects proof + await assert.rejects(async () => { + let tx = await Mina.transaction(() => zkApp.verifyReal(proof)); + await tx.prove(); + }, 'recursive contract rejects fake proof'); +} + +// proof that should be accepted +const realProof = await RealProgram.make(UInt64.from(34)); + +// zkprogram accepts proof +const brokenProof = await RecursiveProgram.verifyReal(realProof); +assert( + await verify(brokenProof, programVk.data), + 'recursive program accepts real proof' +); + +// contract accepts proof +let tx = await Mina.transaction(() => zkApp.verifyReal(realProof)); +let [contractProof] = await tx.prove(); +assert( + await verify(contractProof!, contractVk.data), + 'recursive contract accepts real proof' +); + +console.log('fake proof test passed 🎉'); diff --git a/src/tests/inductive-proofs-small.ts b/src/tests/inductive-proofs-small.ts index 271a2bb6cc..acde6a86f6 100644 --- a/src/tests/inductive-proofs-small.ts +++ b/src/tests/inductive-proofs-small.ts @@ -1,16 +1,8 @@ -import { - SelfProof, - Field, - Experimental, - isReady, - shutdown, - Proof, -} from '../index.js'; -import { tic, toc } from '../examples/zkapps/tictoc.js'; +import { SelfProof, Field, ZkProgram, Proof } from 'o1js'; +import { tic, toc } from '../examples/utils/tic-toc.node.js'; -await isReady; - -let MaxProofsVerifiedOne = Experimental.ZkProgram({ +let MaxProofsVerifiedOne = ZkProgram({ + name: 'recursive-1', publicInput: Field, methods: { @@ -45,7 +37,7 @@ async function testRecursion( ) { console.log(`testing maxProofsVerified = ${maxProofsVerified}`); - let ProofClass = Experimental.ZkProgram.Proof(Program); + let ProofClass = ZkProgram.Proof(Program); tic('executing base case'); let initialProof = await Program.baseCase(Field(0)); @@ -84,5 +76,3 @@ function testJsonRoundtrip(ProofClass: any, proof: Proof) { ); return ProofClass.fromJSON(jsonProof); } - -shutdown(); diff --git a/src/tests/inductive-proofs.ts b/src/tests/inductive-proofs.ts index 61c1c9dc1d..101487dae5 100644 --- a/src/tests/inductive-proofs.ts +++ b/src/tests/inductive-proofs.ts @@ -1,16 +1,8 @@ -import { - SelfProof, - Field, - Experimental, - isReady, - shutdown, - Proof, -} from '../index.js'; -import { tic, toc } from '../examples/zkapps/tictoc.js'; - -await isReady; - -let MaxProofsVerifiedZero = Experimental.ZkProgram({ +import { SelfProof, Field, ZkProgram, Proof } from 'o1js'; +import { tic, toc } from '../examples/utils/tic-toc.node.js'; + +let MaxProofsVerifiedZero = ZkProgram({ + name: 'no-recursion', publicInput: Field, methods: { @@ -24,7 +16,8 @@ let MaxProofsVerifiedZero = Experimental.ZkProgram({ }, }); -let MaxProofsVerifiedOne = Experimental.ZkProgram({ +let MaxProofsVerifiedOne = ZkProgram({ + name: 'recursive-1', publicInput: Field, methods: { @@ -47,7 +40,8 @@ let MaxProofsVerifiedOne = Experimental.ZkProgram({ }, }); -let MaxProofsVerifiedTwo = Experimental.ZkProgram({ +let MaxProofsVerifiedTwo = ZkProgram({ + name: 'recursive-2', publicInput: Field, methods: { @@ -100,7 +94,7 @@ async function testRecursion( ) { console.log(`testing maxProofsVerified = ${maxProofsVerified}`); - let ProofClass = Experimental.ZkProgram.Proof(Program); + let ProofClass = ZkProgram.Proof(Program); tic('executing base case'); let initialProof = await Program.baseCase(Field(0)); @@ -152,5 +146,3 @@ function testJsonRoundtrip(ProofClass: any, proof: Proof) { ); return ProofClass.fromJSON(jsonProof); } - -shutdown(); diff --git a/tests/integration/inductive-proofs.js b/tests/integration/inductive-proofs.js index 5fe77f4ced..d8ff85cfd0 100644 --- a/tests/integration/inductive-proofs.js +++ b/tests/integration/inductive-proofs.js @@ -1,7 +1,7 @@ import { SelfProof, Field, - Experimental, + ZkProgram, isReady, shutdown, } from '../../dist/node/index.js'; @@ -9,7 +9,8 @@ import { tic, toc } from './tictoc.js'; await isReady; -let MaxProofsVerifiedZero = Experimental.ZkProgram({ +let MaxProofsVerifiedZero = ZkProgram({ + name: 'no-recursion', publicInput: Field, methods: { @@ -23,7 +24,8 @@ let MaxProofsVerifiedZero = Experimental.ZkProgram({ }, }); -let MaxProofsVerifiedOne = Experimental.ZkProgram({ +let MaxProofsVerifiedOne = ZkProgram({ + name: 'recursive-1', publicInput: Field, methods: { @@ -46,7 +48,8 @@ let MaxProofsVerifiedOne = Experimental.ZkProgram({ }, }); -let MaxProofsVerifiedTwo = Experimental.ZkProgram({ +let MaxProofsVerifiedTwo = ZkProgram({ + name: 'recursive-2', publicInput: Field, methods: { @@ -92,7 +95,7 @@ await testRecursion(MaxProofsVerifiedTwo, 2); async function testRecursion(Program, maxProofsVerified) { console.log(`testing maxProofsVerified = ${maxProofsVerified}`); - let ProofClass = Experimental.ZkProgram.Proof(Program); + let ProofClass = ZkProgram.Proof(Program); tic('executing base case..'); let initialProof = await Program.baseCase(Field(0)); diff --git a/tests/vk-regression/plain-constraint-system.ts b/tests/vk-regression/plain-constraint-system.ts new file mode 100644 index 0000000000..7165b751c1 --- /dev/null +++ b/tests/vk-regression/plain-constraint-system.ts @@ -0,0 +1,160 @@ +import { Field, Group, Gadgets, Provable, Scalar, Hash, Bytes } from 'o1js'; + +export { GroupCS, BitwiseCS, HashCS }; + +const GroupCS = constraintSystem('Group Primitive', { + add() { + let g1 = Provable.witness(Group, () => Group.generator); + let g2 = Provable.witness(Group, () => Group.generator); + g1.add(g2); + }, + sub() { + let g1 = Provable.witness(Group, () => Group.generator); + let g2 = Provable.witness(Group, () => Group.generator); + g1.sub(g2); + }, + scale() { + let g1 = Provable.witness(Group, () => Group.generator); + let s = Provable.witness(Scalar, () => Scalar.from(5n)); + g1.scale(s); + }, + equals() { + let g1 = Provable.witness(Group, () => Group.generator); + let g2 = Provable.witness(Group, () => Group.generator); + g1.equals(g2).assertTrue(); + g1.equals(g2).assertFalse(); + g1.equals(g2).assertEquals(true); + g1.equals(g2).assertEquals(false); + }, + assertions() { + let g1 = Provable.witness(Group, () => Group.generator); + let g2 = Provable.witness(Group, () => Group.generator); + g1.assertEquals(g2); + }, +}); + +const BitwiseCS = constraintSystem('Bitwise Primitive', { + rot32() { + let a = Provable.witness(Field, () => new Field(12)); + Gadgets.rotate32(a, 2, 'left'); + Gadgets.rotate32(a, 2, 'right'); + Gadgets.rotate32(a, 4, 'left'); + Gadgets.rotate32(a, 4, 'right'); + }, + rot64() { + let a = Provable.witness(Field, () => new Field(12)); + Gadgets.rangeCheck64(a); // `rotate()` doesn't do this + Gadgets.rotate64(a, 2, 'left'); + Gadgets.rotate64(a, 2, 'right'); + Gadgets.rotate64(a, 4, 'left'); + Gadgets.rotate64(a, 4, 'right'); + }, + xor() { + let a = Provable.witness(Field, () => new Field(5n)); + let b = Provable.witness(Field, () => new Field(5n)); + Gadgets.xor(a, b, 16); + Gadgets.xor(a, b, 32); + Gadgets.xor(a, b, 48); + Gadgets.xor(a, b, 64); + }, + notUnchecked() { + let a = Provable.witness(Field, () => new Field(5n)); + Gadgets.not(a, 16, false); + Gadgets.not(a, 32, false); + Gadgets.not(a, 48, false); + Gadgets.not(a, 64, false); + }, + notChecked() { + let a = Provable.witness(Field, () => new Field(5n)); + Gadgets.not(a, 16, true); + Gadgets.not(a, 32, true); + Gadgets.not(a, 48, true); + Gadgets.not(a, 64, true); + }, + leftShift() { + let a = Provable.witness(Field, () => new Field(12)); + Gadgets.leftShift64(a, 2); + Gadgets.leftShift64(a, 4); + }, + rightShift() { + let a = Provable.witness(Field, () => new Field(12)); + Gadgets.rightShift64(a, 2); + Gadgets.rightShift64(a, 4); + }, + and() { + let a = Provable.witness(Field, () => new Field(5n)); + let b = Provable.witness(Field, () => new Field(5n)); + Gadgets.and(a, b, 16); + Gadgets.and(a, b, 32); + Gadgets.and(a, b, 48); + Gadgets.and(a, b, 64); + }, +}); + +const Bytes32 = Bytes(32); +const bytes32 = Bytes32.from([]); + +const HashCS = constraintSystem('Hashes', { + SHA256() { + let xs = Provable.witness(Bytes32.provable, () => bytes32); + Hash.SHA3_256.hash(xs); + }, + + SHA384() { + let xs = Provable.witness(Bytes32.provable, () => bytes32); + Hash.SHA3_384.hash(xs); + }, + + SHA512() { + let xs = Provable.witness(Bytes32.provable, () => bytes32); + Hash.SHA3_512.hash(xs); + }, + + Keccak256() { + let xs = Provable.witness(Bytes32.provable, () => bytes32); + Hash.Keccak256.hash(xs); + }, + + Poseidon() { + let xs = Array.from({ length: 32 }, (_, i) => i).map((x) => + Provable.witness(Field, () => Field(x)) + ); + Hash.Poseidon.hash(xs); + }, +}); + +// mock ZkProgram API for testing + +function constraintSystem( + name: string, + obj: { [K: string]: (...args: any) => void } +) { + let methodKeys = Object.keys(obj); + + return { + analyzeMethods() { + let cs: Record< + string, + { + rows: number; + digest: string; + } + > = {}; + for (let key of methodKeys) { + let { rows, digest } = Provable.constraintSystem(obj[key]); + cs[key] = { + digest, + rows, + }; + } + return cs; + }, + async compile() { + return { + verificationKey: { data: '', hash: '' }, + }; + }, + name, + digest: () => name, + }; +} diff --git a/tests/vk-regression/tsconfig.json b/tests/vk-regression/tsconfig.json new file mode 100644 index 0000000000..702d13e4a4 --- /dev/null +++ b/tests/vk-regression/tsconfig.json @@ -0,0 +1,12 @@ +{ + "extends": "../../tsconfig.json", + "include": ["."], + "exclude": [], + "compilerOptions": { + "rootDir": "../..", + "baseUrl": "../..", + "paths": { + "o1js": ["."] + } + } +} diff --git a/src/examples/regression_test.json b/tests/vk-regression/vk-regression.json similarity index 50% rename from src/examples/regression_test.json rename to tests/vk-regression/vk-regression.json index c8916cc0c4..9a183bc35e 100644 --- a/src/examples/regression_test.json +++ b/tests/vk-regression/vk-regression.json @@ -1,168 +1,268 @@ { "Voting_": { - "digest": "2f0ca1dca22d7af925d31d23f3d4e37686ec1755a9f069063ae3b6300f6b3920", + "digest": "3f56ff09ceba13daf64b20cd48419395a04aa0007cac20e6e9c5f9106f251c3a", "methods": { "voterRegistration": { - "rows": 1260, - "digest": "5d6fa3b1f0f781fba8894da098827088" + "rows": 1258, + "digest": "5572b0d59feea6b199f3f45af7498d92" }, "candidateRegistration": { - "rows": 1260, - "digest": "abf93b4d926a8743bf622da4ee0f88b2" + "rows": 1258, + "digest": "07c8451f1c1ea4e9653548d411d5728c" }, "approveRegistrations": { "rows": 1146, - "digest": "197ce95e1895f940278034d20ab24274" + "digest": "ec68c1d8ab22e779ccbd2659dd6b46cd" }, "vote": { - "rows": 1674, - "digest": "a0dd0be93734dc39d52fc7d60d0f0bab" + "rows": 1672, + "digest": "fa5671190ca2cc46084cae922a62288e" }, "countVotes": { - "rows": 5797, - "digest": "9c5503e03dcbb6b04c907eb1da43e26e" + "rows": 5796, + "digest": "775f327a408b3f3d7bae4e3ff18aeb54" } }, "verificationKey": { - "data": "AACd9tWcrEA7+0z2zM4uOSwj5GdeIBIROoVsS/yRuSRjKmnpZwY33yiryBLa9HQWpeZDSJI5y91gKJ9g5atltQApAhMdOuU5+NrHN3RCJtswX+WPvwaHJnihtSy2FcJPyghvBVTi2i7dtWIPQLVDIzC5ARu8f8H9JWjzjVVYE/rQLruuq2qUsCrqdVsdRaw+6OjIFeAXS6mzvrVv5iYGslg5CV5mgLBg3xC408jZJ0pe8ua2mcIEDMGEdSR/+VuhPQaqxZTJPBVhazVc1P9gRyS26SdOohL85UmEc4duqlJOOlXOFuwOT6dvoiUcdQtzuPp1pzA/LHueqm9yQG9mlT0Df8uY/A+rwM4l/ypTP/o0+5GCM9jJf9bl/z0DpGWheCJY+LZbIGeBUOpg0Gx1+KZsD9ivWJ0vxNz8zKcAS1i3FgntjqyfY+62jfTR8PW1Y4wdaFan6jSxaaH6WYnvccAo2QHxEAFL91CfnZB1pwF8NAT395N/rXr5XhMHFPoCkSHd2+5u+b62pkvFqqZZ9r24SMQOe9Bl2ZfMew2DyFLMPzwTowHw8onMEXcVKabFs9zQVp66AMf/wlipirNztdguAMlwrr5wrHUaGeTWFlLisFAPw2kdtXHUaWFMr8rlLZAH95JF1AU+hzhCIMmrPGuqtWSZ3PevCs61j8Mg1hZc7yYc4d5ts+btlepIrTet7yJK5rlsFQfJGzaeTz9BN+g+C2ZK8B+2a2Qrz386FvB+elJAkJ2/Agn35oBHB2HobDkF6sRfrXOdH5l+QV7vR2v385RKRtfnmcJeUQcpq5/JTgVwagDJ/FarTN5jFsrBBRTeW3yZ5/CfVNA7NNWxoKhjBaHVIhn/fLT5sFLYzYdCx/uTsusyZmE2d6iqnLS+j1IXNJX/zR0ZD3aGuoUc4MaFZQnN5om4dfpbloe4Roob3BuDhBHTKoYC+nVsyEvDRyiYLEOjJ45/bSwTCfwngYKtNmo3sVTvQ9mqBf0cLdBCn8skp3S/gz324TFm8iJ+t8EWsSEg+BHXhfDdeVIH8kIDSRHLhfHm4ekJzyDdkhSrpx2vUjEr3DsCxEebGoDTQn3asUnHGhnGWBYwF2POo4QuBvqS2B9pOUgfgpcmcvpUe0yTZ3WrOkHl1RwJL07ktygdm/SxKUslsfL3Ds6RrXEDr65EJ2ArVceibKJPp8cvhwYMqH5O4Df/c6DNekL1d6QYnjO0/3LMvY/f/y1+b7nPHI8+1Wqp5jZH8UsuN63SSMdfBEe6x46AG/R+YS/wH78GKekabWu9QQnUJdjXyXiqF4qRebvfcmpQz91anvVz3ggBqCv4sYqCIvP0ysDtMdi36zFErV+8SdUu+NsPDGvdPSCGdLuC25izxb21up2HORmlM5R7yuIW3rCiq8DeLD0OHjqOBZ+IEv9zEkb5fHTJvxoxnZlArtZSBpD6iIDPVDymuK+BsOggZav3K+TytjeD2Gcld5NfyRISFWUIMkZNFQRL8AQpET6RJnG1HSW0CaRfNeomtjCBWIr85wFCrp06j/D1J8B3EyhloZLJJ6ywxt41smXVugxA8LRTO+6lVBOBF14jHQCCUl6u7uiWCe1z4/bC5wQXPwWSljp8NVU8Erp1U9ModNK7W63Pkh0efvgSD5d0nLzbfa0jTdxZ1JkfKsnvYk43Ed+vmXooHZhUeZAIX8ZCizhb1Gfvm02JFwxYXmiYAOp5wkGzweU2I5zo8r5yZFI1r4XibNQs7eAfKGRv3gh8/EuLkX/bdettgPvNsI8ndpQ3kL/V8W2PQN4/hjC9AKCYBeXQG42bRncYZdLe++R2KA1ZdPDxQPF3sxUIKhzmRWqbozrtv310Maorwv6eZJjldlCJwICR9QgcDwDuNj+UFJnX3RWsdIWsUbI1T4wO0sE2sBiMX/OqmiGJEAnBegioistlFyfRvm54h+duNOl/ol1Fva7NoXvsL/wThAWUly7bnc7/Al2bBQlUrmEX46UnKXzYntkZDee7Lx1u1BBkJAj/5BH1YZOPmMCh498rBUiHmc+4uQqebqNSHdOSgC39ESss4u7GNhWj3fi9XXta6UT9wapEMGq0WTg2Kry6xNP2YZ5X8eaapRQc/KzYgz9XjQL6TKpqNuGEbRlmfYvIuoFbnOkZI7RYoGp3YheMs1pQErwOxLzZa9W3Okwx16TSDwPLR0xMdAyogMrOdKN4JSMyNnmOaoVf6PkN+K9fz7RuHtvgjKpuz4vsK5Z2wRneqPrnfu6PkgHcRQrd0SxqCbN23Z/yp8qOcN6XU49iCNEBjztT00tolQ9hCPMSE/eTZ+ioez7m3pJFVks3T5Rk/e+6MeowJWIOv20x6CPS9mhpr1JPwdNFrWdgs19VsobntCpF/rWxksdrYyk=", - "hash": "11565671297372915311669581079391063231397733208021234592662736001743584177724" + "data": "AACd9tWcrEA7+0z2zM4uOSwj5GdeIBIROoVsS/yRuSRjKmnpZwY33yiryBLa9HQWpeZDSJI5y91gKJ9g5atltQApAhMdOuU5+NrHN3RCJtswX+WPvwaHJnihtSy2FcJPyghvBVTi2i7dtWIPQLVDIzC5ARu8f8H9JWjzjVVYE/rQLruuq2qUsCrqdVsdRaw+6OjIFeAXS6mzvrVv5iYGslg5CV5mgLBg3xC408jZJ0pe8ua2mcIEDMGEdSR/+VuhPQaqxZTJPBVhazVc1P9gRyS26SdOohL85UmEc4duqlJOOlXOFuwOT6dvoiUcdQtzuPp1pzA/LHueqm9yQG9mlT0Df8uY/A+rwM4l/ypTP/o0+5GCM9jJf9bl/z0DpGWheCJY+LZbIGeBUOpg0Gx1+KZsD9ivWJ0vxNz8zKcAS1i3FgntjqyfY+62jfTR8PW1Y4wdaFan6jSxaaH6WYnvccAo2QHxEAFL91CfnZB1pwF8NAT395N/rXr5XhMHFPoCkSHd2+5u+b62pkvFqqZZ9r24SMQOe9Bl2ZfMew2DyFLMPzwTowHw8onMEXcVKabFs9zQVp66AMf/wlipirNztdguAEgTiVyzydYxNTKRpau/O5JaThaBCqePJzujSXLd5uIguUQkKMjVpfnHKOeoOtlMY8PYYFASPZjP4K1Y1XpE5DIc4d5ts+btlepIrTet7yJK5rlsFQfJGzaeTz9BN+g+C2ZK8B+2a2Qrz386FvB+elJAkJ2/Agn35oBHB2HobDkF6sRfrXOdH5l+QV7vR2v385RKRtfnmcJeUQcpq5/JTgVwagDJ/FarTN5jFsrBBRTeW3yZ5/CfVNA7NNWxoKhjBaHVIhn/fLT5sFLYzYdCx/uTsusyZmE2d6iqnLS+j1IXNJX/zR0ZD3aGuoUc4MaFZQnN5om4dfpbloe4Roob3BuDhBHTKoYC+nVsyEvDRyiYLEOjJ45/bSwTCfwngYKtNmo3sVTvQ9mqBf0cLdBCn8skp3S/gz324TFm8iJ+t8EWVKjlhM+1lrOQC7OfL98Sy0lD9j349LjxKcpiLTM7xxR/fSS4Yv9QXnEZxDigYQO7N+8yMm6PfgqtNLa4gTlCOq1tWaRtaZtq24x+SyOo5P8EXWYuvsV/qMMPNmhoTq85lDI+iwlA1xDTYyFHBdUe/zfoe5Znk7Ej3dQt+wVKtRgMqH5O4Df/c6DNekL1d6QYnjO0/3LMvY/f/y1+b7nPHI8+1Wqp5jZH8UsuN63SSMdfBEe6x46AG/R+YS/wH78GKekabWu9QQnUJdjXyXiqF4qRebvfcmpQz91anvVz3ggBqCv4sYqCIvP0ysDtMdi36zFErV+8SdUu+NsPDGvdPSCGdLuC25izxb21up2HORmlM5R7yuIW3rCiq8DeLD0OHjqOBZ+IEv9zEkb5fHTJvxoxnZlArtZSBpD6iIDPVDymuK+BsOggZav3K+TytjeD2Gcld5NfyRISFWUIMkZNFQRL8AQpET6RJnG1HSW0CaRfNeomtjCBWIr85wFCrp06j/D1J8B3EyhloZLJJ6ywxt41smXVugxA8LRTO+6lVBOBF14jHQCCUl6u7uiWCe1z4/bC5wQXPwWSljp8NVU8Erp1U9ModNK7W63Pkh0efvgSD5d0nLzbfa0jTdxZ1JkfKsnvYk43Ed+vmXooHZhUeZAIX8ZCizhb1Gfvm02JFwxYXmiYAOp5wkGzweU2I5zo8r5yZFI1r4XibNQs7eAfKGRv3gh8/EuLkX/bdettgPvNsI8ndpQ3kL/V8W2PQN4/hjC9AKCYBeXQG42bRncYZdLe++R2KA1ZdPDxQPF3sxUIKhzmRWqbozrtv310Maorwv6eZJjldlCJwICR9QgcDwDuNj+UFJnX3RWsdIWsUbI1T4wO0sE2sBiMX/OqmiGJEAnBegioistlFyfRvm54h+duNOl/ol1Fva7NoXvsL/wThAWUly7bnc7/Al2bBQlUrmEX46UnKXzYntkZDee7Lx1u1BBkJAj/5BH1YZOPmMCh498rBUiHmc+4uQqebqNSHdOSgC39ESss4u7GNhWj3fi9XXta6UT9wapEMGq0WTg2Kry6xNP2YZ5X8eaapRQc/KzYgz9XjQL6TKpqNuGEbRlmfYvIuoFbnOkZI7RYoGp3YheMs1pQErwOxLzZa9W3Okwx16TSDwPLR0xMdAyogMrOdKN4JSMyNnmOaoVf6PkN+K9fz7RuHtvgjKpuz4vsK5Z2wRneqPrnfu6PkgHcRQrd0SxqCbN23Z/yp8qOcN6XU49iCNEBjztT00tolQ9hCPMSE/eTZ+ioez7m3pJFVks3T5Rk/e+6MeowJWIOv20x6CPS9mhpr1JPwdNFrWdgs19VsobntCpF/rWxksdrYyk=", + "hash": "1740450553572902301764143810281331039416167348454304895395553400061364101079" } }, "Membership_": { - "digest": "fb87402442d6984de28d7bab62deb87ab8a7f46d5c2d59da4980ae6927dd1ec", + "digest": "255745fb9365ff4f970b96ed630c01c9c8f63e21744f83fbe833396731d096e2", "methods": { "addEntry": { - "rows": 1354, - "digest": "bdb5dd653383ec699e25e418135aeec0" + "rows": 1353, + "digest": "fa32e32384aef8ce6a7d019142149d95" }, "isMember": { - "rows": 470, - "digest": "f1b63907b48c40cd01b1d34166fa86eb" + "rows": 469, + "digest": "16dae12385ed7e5aca9161030a426335" }, "publish": { - "rows": 695, - "digest": "c6be510154c0e624a41756ad349df9de" + "rows": 694, + "digest": "c7f77b05b8ec477338849af8dcb34a11" } }, "verificationKey": { - "data": "AACwuS3vTWCwpRIX/QlJQqJcmPO9nPm4+sCfcrqiY1NUMiV9k6Pc8kFkMsbGLst78T8uAnYwc1Ql49kq0I2GizwshS9xkBcfxRTAAMBHXhf8KDkK39AalVocKIrfWMV0MSShinj0bCxPCc10K0cya4Voy8fud4+hktDOuwjaAstpEJSbKRHMIki77xHmJWlFUYdkgPg30MU4Ta3ev/h+mcMWmofyhLSQqUbaV6hM95n3Y0Wcn2LRNxJP8TRwHndIcylleqPsGMh3P+A+N9c32N4kl29nreMJJdcUrCXK90GLPAFOB9mHIjKk9+9o3eZc3cGQ+jppXoN3zwO91DeT/GYvXqCZTAudLxIwuJU11UBThG5CKKABa9ulQ1bYGXj9Eydy0vPxfojDeFrnKMi9GKSjiSMzmOLbIw7Dt+g9ggjsHM5rPrT7dY1VV4ZT9shjlcX3029xnk3Bjz4Q9PiK+A8o6f7L6aVB07I+QY2iDtwSQWuXYPohrk85I1UbPfY+giWqFXBtHaN45PMWCyBx0TKaozETCmv0kA5KGTzesYQCECPQ8F2DM+oXz8xly+z9/Ypt/Zx9NvF7wute/1s6Q/QuAK8auJnAo+Ayoz9QWJYOyPCcdSOOu06auILgZc5OvCIQ3JDLaRLCOVxcDqSyBjhkiGid2eXAIb9unaj/ZtIH0zm2vXswFt90jphf6jgLtFJULrvKVg+YCMNM/04QLTGcMmjjzv4LciQ6IVXth7zhVKxfL1/2peC0r/ZrP8k+Ox4LEBXWMCQE5kfK476bQgrLeKJfQ45PZfgB688DGwaYAxWbcxBV822/aAsA55ijFY1Xf7S+DiytY4a/u0bellKMDUQqTOq9VwmbDv868zXscUwKpNVR3wy2En/q9M/HJJc4BZyuuQvlQSR59m0gL4hKHf5Dci/YVvM6ACHmg+5SxCr1pUNKbyy2lsIa5Ma40ZmsTpT4/lQczmGENQSQXA9bFibT0Q+Vj885p9heLOCCXyAujC4DhAdYmT1MQ7v4IxckU6470FP1JqRV4SOd0KOhihaeAUFeAXycuok7/nt3dhZW8juNm39lwVhQ6wyTRXGZT9+y5BBglIqA2LehBYoNJGTPe56ukPLNlOisxnYe/Bk8CxsWtY1GhKPPY0qGRZIhsrgZdgfkxPT19xI0t2Th5Uz9IdzapUDwAGgSy0E0zDD6l24QrqQAp0ebGEbpXqv21bhlr6dYBsculE2VU9SuGJ2g6yuuKf4+lfJ2V5TkIxFvlgw5cxTXNQ010JYug38++ZDV+MibXPzg+cODE5wfZ3jon5wVNkAiG642DzXzNj67x80zBWLdt3UKnFZs9dpa1fYpTjlJg8T+dnJJiKf2IvmvF8xyi1HAwAFyhDL2dn/w/pDE2Kl9QdpZpQYDEBQgCCkegsZszQ+2mjxU9pLXzz5GSoqz8jABW5Qo3abBAhvYKKaAs6NoRgeAD6SadFDbQmXaftE+Y1MVOtjnaZDUBdwahWiJMlkfZpxW1aubEc/GSX8WzCZ8h9HeakcRc7kcN0CR8kmfER3eiZ2JMbt5cQl/afNjwGGAmeXzTaR34AgFjiw/RlZJkhYm9jyf18M8yP94QGBMxd6Y6wrNvOmJHzEnp8aitJsDlZklm8LKbjumlSbLcbBokpIDhFBBKfwP2qsQX7eHLCZ/3mztoFKoIiYXgrHWG8m2SzIJ/ljn6Rg7AxIsPjzZyEw1eXAOC7A1FCT/757ygMsnk+rLlpDTBYLmhJtQdt61MQFDi5BuCmQ/PY9C/74/k4APl5htiNcCZty/1JElFwjuCQFjvAiMPUMyqp7/ALFapsTZqhSs1g6jd8uhuJoTNEqLDvKUUbs0kMvGy8BOG0YXNxmNccabGwBzxmijv6LF/Xinecl4aD8FCh6opY98TJnOHd3XSYL1DbLqmmc6CXEM+g5iDGnXr/CkI2Jy37OkF8X03jz4AH0Yj0+J63yH4IS+PrNpKZEXKh7PvXNaLGGKsFcKEi63/xKPKH0G4RzvFKbkp+IWqtIYjMiwIJMwzmfS1NLLXqqpFiD364eFcXINR2rrDKcoTUp1JkVZVfXfKwaRUPWSGFYIYMtwPh2w8ZfubAmXZFpyzstORhFyg9rtVAAy0lcDhQwWVlhFFkR2qbdoy0EFLBrfKqUIkd1N6vDQQYL1RGaTAv/ybregrJsFo+VP3ZatlR6LnKYWp1m7vPkGm3I6Pus/mvp1k10QGk8nhFuR31DjsG3lzZ4gXSs1oSv0qbxD2S6g5+Y6cPbITEGX3uQjsunXnQ9PHd22Mk+fqbDakTiCJh6aFqqPNShiAXkGSuC1oXJHX3zqnbn75dWO0UVhBNAbjYkSnQeyka1wnZb12sR+PlRMvWQVcd93t5L/FiE0ORo=", - "hash": "11513384784058506063139870141763328684986961462198948028100954667309086537868" + "data": "AACwuS3vTWCwpRIX/QlJQqJcmPO9nPm4+sCfcrqiY1NUMiV9k6Pc8kFkMsbGLst78T8uAnYwc1Ql49kq0I2GizwshS9xkBcfxRTAAMBHXhf8KDkK39AalVocKIrfWMV0MSShinj0bCxPCc10K0cya4Voy8fud4+hktDOuwjaAstpEJSbKRHMIki77xHmJWlFUYdkgPg30MU4Ta3ev/h+mcMWmofyhLSQqUbaV6hM95n3Y0Wcn2LRNxJP8TRwHndIcylleqPsGMh3P+A+N9c32N4kl29nreMJJdcUrCXK90GLPAFOB9mHIjKk9+9o3eZc3cGQ+jppXoN3zwO91DeT/GYvXqCZTAudLxIwuJU11UBThG5CKKABa9ulQ1bYGXj9Eydy0vPxfojDeFrnKMi9GKSjiSMzmOLbIw7Dt+g9ggjsHM5rPrT7dY1VV4ZT9shjlcX3029xnk3Bjz4Q9PiK+A8o6f7L6aVB07I+QY2iDtwSQWuXYPohrk85I1UbPfY+giWqFXBtHaN45PMWCyBx0TKaozETCmv0kA5KGTzesYQCECPQ8F2DM+oXz8xly+z9/Ypt/Zx9NvF7wute/1s6Q/QuAHHgQqvSF2AEzSEy6kDop6fnFtVTxzp0MgW0M9X0uVcRTRJTkcVZSz1JzihGEjzkEZnZW6tVr6CEkmzXh/t3DSq2vXswFt90jphf6jgLtFJULrvKVg+YCMNM/04QLTGcMmjjzv4LciQ6IVXth7zhVKxfL1/2peC0r/ZrP8k+Ox4LEBXWMCQE5kfK476bQgrLeKJfQ45PZfgB688DGwaYAxWbcxBV822/aAsA55ijFY1Xf7S+DiytY4a/u0bellKMDUQqTOq9VwmbDv868zXscUwKpNVR3wy2En/q9M/HJJc4BZyuuQvlQSR59m0gL4hKHf5Dci/YVvM6ACHmg+5SxCr1pUNKbyy2lsIa5Ma40ZmsTpT4/lQczmGENQSQXA9bFibT0Q+Vj885p9heLOCCXyAujC4DhAdYmT1MQ7v4IxcktsWwr3mRVBRM4iPa87OEKZOxq0wWPrGcTnmqV/ihFAcp38VS2KUNwsiWjprCq1MFDCf1dT4c1U6/mdLP6AI/AJi7REoCfvJfwxSZYr2obhnskD1VjqelMdksHemFbsQDczNhNcSg1TTD5ZsuG71wj9rSJPEisRCRRd733MLARwv6l24QrqQAp0ebGEbpXqv21bhlr6dYBsculE2VU9SuGJ2g6yuuKf4+lfJ2V5TkIxFvlgw5cxTXNQ010JYug38++ZDV+MibXPzg+cODE5wfZ3jon5wVNkAiG642DzXzNj67x80zBWLdt3UKnFZs9dpa1fYpTjlJg8T+dnJJiKf2IvmvF8xyi1HAwAFyhDL2dn/w/pDE2Kl9QdpZpQYDEBQgCCkegsZszQ+2mjxU9pLXzz5GSoqz8jABW5Qo3abBAhvYKKaAs6NoRgeAD6SadFDbQmXaftE+Y1MVOtjnaZDUBdwahWiJMlkfZpxW1aubEc/GSX8WzCZ8h9HeakcRc7kcN0CR8kmfER3eiZ2JMbt5cQl/afNjwGGAmeXzTaR34AgFjiw/RlZJkhYm9jyf18M8yP94QGBMxd6Y6wrNvOmJHzEnp8aitJsDlZklm8LKbjumlSbLcbBokpIDhFBBKfwP2qsQX7eHLCZ/3mztoFKoIiYXgrHWG8m2SzIJ/ljn6Rg7AxIsPjzZyEw1eXAOC7A1FCT/757ygMsnk+rLlpDTBYLmhJtQdt61MQFDi5BuCmQ/PY9C/74/k4APl5htiNcCZty/1JElFwjuCQFjvAiMPUMyqp7/ALFapsTZqhSs1g6jd8uhuJoTNEqLDvKUUbs0kMvGy8BOG0YXNxmNccabGwBzxmijv6LF/Xinecl4aD8FCh6opY98TJnOHd3XSYL1DbLqmmc6CXEM+g5iDGnXr/CkI2Jy37OkF8X03jz4AH0Yj0+J63yH4IS+PrNpKZEXKh7PvXNaLGGKsFcKEi63/xKPKH0G4RzvFKbkp+IWqtIYjMiwIJMwzmfS1NLLXqqpFiD364eFcXINR2rrDKcoTUp1JkVZVfXfKwaRUPWSGFYIYMtwPh2w8ZfubAmXZFpyzstORhFyg9rtVAAy0lcDhQwWVlhFFkR2qbdoy0EFLBrfKqUIkd1N6vDQQYL1RGaTAv/ybregrJsFo+VP3ZatlR6LnKYWp1m7vPkGm3I6Pus/mvp1k10QGk8nhFuR31DjsG3lzZ4gXSs1oSv0qbxD2S6g5+Y6cPbITEGX3uQjsunXnQ9PHd22Mk+fqbDakTiCJh6aFqqPNShiAXkGSuC1oXJHX3zqnbn75dWO0UVhBNAbjYkSnQeyka1wnZb12sR+PlRMvWQVcd93t5L/FiE0ORo=", + "hash": "16610506589527352533348678289715227768202510979537802187565243095524972136674" } }, "HelloWorld": { - "digest": "5efddf140c592bb0bfd51d9c77252bcc0c0fab1fbd7fbaf85bd3bb051a96596", + "digest": "20cadc6f44ecd546700e9fac15aa2740d4357d46ee03c2627c36be49b02e8227", "methods": { "update": { - "rows": 2352, - "digest": "217224955830a2c0db65a6dcc91cae29" + "rows": 2351, + "digest": "f5b77fd12fee155fd3a40946dd453962" } }, "verificationKey": { - "data": "AAAxHIvaXF+vRj2/+pyAfE6U29d1K5GmGbhiKR9lTC6LJ2o1ygGxXERl1oQh6DBxf/hDUD0HOeg/JajCp3V6b5wytil2mfx8v2DB5RuNQ7VxJWkha0TSnJJsOl0FxhjldBbOY3tUZzZxHpPhHOKHz/ZAXRYFIsf2x+7boXC0iPurETHN7j5IevHIgf2fSW8WgHZYn83hpVI33LBdN1pIbUc7oWAUQVmmgp04jRqTCYK1oNg+Y9DeIuT4EVbp/yN7eS7Ay8ahic2sSAZvtn08MdRyk/jm2cLlJbeAAad6Xyz/H9l7JrkbVwDMMPxvHVHs27tNoJCzIlrRzB7pg3ju9aQOu4h3thDr+WSgFQWKvcRPeL7f3TFjIr8WZ2457RgMcTwXwORKbqJCcyKVNOE+FlNwVkOKER+WIpC0OlgGuayPFwQQkbb91jaRlJvahfwkbF2+AJmDnavmNpop9T+/Xak1adXIrsRPeOjC+qIKxIbGimoMOoYzYlevKA80LnJ7HC0IxR+yNLvoSYxDDPNRD+OCCxk5lM2h8IDUiCNWH4FZNJ+doiigKjyZlu/xZ7jHcX7qibu/32KFTX85DPSkQM8dALqTYvt53Y2qtBATwNK97/SJ/NcMvhcXE4RGfofcGhoEK8I7PkcmU5pc3qaZ6a1jailHXKV47zJNSF/4HyjpQAQKR89XcqLS/NP7lwCEej/L8q8R7sKGMCXmgFYluWH4JBSPDgvMxScfjFS33oBNb7po8cLnAORzohXoYTSgztklD0mKn6EegLbkLtwwr9ObsLz3m7fp/3wkNWFRkY5xzSZN1VybbQbmpyQNCpxd/kdDsvlszqlowkyC8HnKbhnvE0Mrz3ZIk4vSs/UGBSXAoESFCFCPcTq11TCOhE5rumMJErv5LusDHJgrBtQUMibLU9A1YbF7SPDAR2QZd0yx3wbCC54QZ2t+mZ4s6RQndfRpndXoIZJgari62jHRccBnGpRmURHG20jukwW6RYDDED7OlvEzEhFlsXyViehlSn4EwBn97R43eHZQvzawB0Xo8qVfOHstjgWKF0vSwenrWxwq8p1y9IQeEWVpLG8IU6dzZfX2/X71b5I74QwxlrLRM0exIlapLarEGI7ZVvg5jAqXDlXxVS3HRo/skxgt2LYm8wLIKLHX0ClznArLVLXkSX18cSoSsVMG3QCSsmH1Oh8xOGUbSHzawovjubcH7qWjIZoghZJ16QB1c0ryiAfHB48OHhs2p/JZWz8Dp7kfcPkeg2Of2NbupJlNVMLIH4IGWaPAscBRkZ+F4oLqOhJ5as7fAzzU8PQdeZi0YgssGDJVmNEHP61I16KZNcxQqR0EUVwhyMmYmpVjvtfhHi/6I3mfPS+FDxTuf4yaqVF0xg2V3ep/WYnnKPJIegxoTFY8pChjyow3PMfhAP5HOnXjHQ2Va9BFo4mfEQXvRzPmIRRVmlVsP8zA+xuHylyiww/Lercce7cq0YA5PtYS3ge9IDYwXckBUXb5ikD3alrrv5mvMu6itB7ix2f8lbiF9Fkmc4Bk2ycIWXJDCuBN+2sTFqzUeoT6xY8XWaOcnDvqOgSm/CCSv38umiOE2jEpsKYxhRc6W70UJkrzd3hr2DiSF1I2B+krpUVK1GeOdCLC5sl7YPzk+pF8183uI9wse6UTlqIiroKqsggzLBy/IjAfxS0BxFy5zywXqp+NogFkoTEJmR5MaqOkPfap+OsD1lGScY6+X4WW/HqCWrmA3ZTqDGngQMTGXLCtl6IS/cQpihS1NRbNqOtKTaCB9COQu0oz6RivBlywuaj3MKUdmbQ2gVDj+SGQItCNaXawyPSBjB9VT+68SoJVySQsYPCuEZCb0V/40n/a7RAbyrnNjP+2HwD7p27Pl1RSzqq35xiPdnycD1UeEPLpx/ON65mYCkn+KLQZmkqPio+vA2KmJngWTx+ol4rVFimGm76VT0xCFDsu2K0YX0yoLNH4u2XfmT9NR8gGfkVRCnnNjlbgHQmEwC75+GmEJ5DjD3d+s6IXTQ60MHvxbTHHlnfmPbgKn2SAI0uVoewKC9GyK6dSaboLw3C48jl0E2kyc+7umhCk3kEeWmt//GSjRNhoq+B+mynXiOtgFs/Am2v1TBjSb+6tcijsf5tFJmeGxlCjJnTdNWBkSHpMoo6OFkkpA6/FBAUHLSM7Yv8oYyd0GtwF5cCwQ6aRTbl9oG/mUn5Q92OnDMQcUjpgEho0Dcp2OqZyyxqQSPrbIIZZQrS2HkxBgjcfcSTuSHo7ONqlRjLUpO5yS95VLGXBLLHuCiIMGT+DW6DoJRtRIS+JieVWBoX0YsWgYInXrVlWUv6gDng5AyVFkUIFwZk7/3mVAgvXO83ArVKA4S747jT60w5bgV4Jy55slDM=", - "hash": "16374078687847242751733960472737775252305759960836159362409166419576402399087" + "data": "AAAxHIvaXF+vRj2/+pyAfE6U29d1K5GmGbhiKR9lTC6LJ2o1ygGxXERl1oQh6DBxf/hDUD0HOeg/JajCp3V6b5wytil2mfx8v2DB5RuNQ7VxJWkha0TSnJJsOl0FxhjldBbOY3tUZzZxHpPhHOKHz/ZAXRYFIsf2x+7boXC0iPurETHN7j5IevHIgf2fSW8WgHZYn83hpVI33LBdN1pIbUc7oWAUQVmmgp04jRqTCYK1oNg+Y9DeIuT4EVbp/yN7eS7Ay8ahic2sSAZvtn08MdRyk/jm2cLlJbeAAad6Xyz/H9l7JrkbVwDMMPxvHVHs27tNoJCzIlrRzB7pg3ju9aQOu4h3thDr+WSgFQWKvcRPeL7f3TFjIr8WZ2457RgMcTwXwORKbqJCcyKVNOE+FlNwVkOKER+WIpC0OlgGuayPFwQQkbb91jaRlJvahfwkbF2+AJmDnavmNpop9T+/Xak1adXIrsRPeOjC+qIKxIbGimoMOoYzYlevKA80LnJ7HC0IxR+yNLvoSYxDDPNRD+OCCxk5lM2h8IDUiCNWH4FZNJ+doiigKjyZlu/xZ7jHcX7qibu/32KFTX85DPSkQM8dADbWQUmeiyX6c8BmLNrtM9m7dAj8BktFxGV9DpdhbakQltUbxbGrb3EcZ+43YFE/yWa3/WAQL81kbrXD0yjFthEKR89XcqLS/NP7lwCEej/L8q8R7sKGMCXmgFYluWH4JBSPDgvMxScfjFS33oBNb7po8cLnAORzohXoYTSgztklD0mKn6EegLbkLtwwr9ObsLz3m7fp/3wkNWFRkY5xzSZN1VybbQbmpyQNCpxd/kdDsvlszqlowkyC8HnKbhnvE0Mrz3ZIk4vSs/UGBSXAoESFCFCPcTq11TCOhE5rumMJErv5LusDHJgrBtQUMibLU9A1YbF7SPDAR2QZd0yx3wbCC54QZ2t+mZ4s6RQndfRpndXoIZJgari62jHRccBnGpRmURHG20jukwW6RYDDED7OlvEzEhFlsXyViehlSn4Evb44z+VGilheD0D6v1paoVTv2A4m5ZVGEQOeoCQELABdoFrIZRrd4+glnXPz8Gy4nOI/rmGgnPa9fSK0N1zMKEexIlapLarEGI7ZVvg5jAqXDlXxVS3HRo/skxgt2LYm8wLIKLHX0ClznArLVLXkSX18cSoSsVMG3QCSsmH1Oh8xOGUbSHzawovjubcH7qWjIZoghZJ16QB1c0ryiAfHB48OHhs2p/JZWz8Dp7kfcPkeg2Of2NbupJlNVMLIH4IGWaPAscBRkZ+F4oLqOhJ5as7fAzzU8PQdeZi0YgssGDJVmNEHP61I16KZNcxQqR0EUVwhyMmYmpVjvtfhHi/6I3mfPS+FDxTuf4yaqVF0xg2V3ep/WYnnKPJIegxoTFY8pChjyow3PMfhAP5HOnXjHQ2Va9BFo4mfEQXvRzPmIRRVmlVsP8zA+xuHylyiww/Lercce7cq0YA5PtYS3ge9IDYwXckBUXb5ikD3alrrv5mvMu6itB7ix2f8lbiF9Fkmc4Bk2ycIWXJDCuBN+2sTFqzUeoT6xY8XWaOcnDvqOgSm/CCSv38umiOE2jEpsKYxhRc6W70UJkrzd3hr2DiSF1I2B+krpUVK1GeOdCLC5sl7YPzk+pF8183uI9wse6UTlqIiroKqsggzLBy/IjAfxS0BxFy5zywXqp+NogFkoTEJmR5MaqOkPfap+OsD1lGScY6+X4WW/HqCWrmA3ZTqDGngQMTGXLCtl6IS/cQpihS1NRbNqOtKTaCB9COQu0oz6RivBlywuaj3MKUdmbQ2gVDj+SGQItCNaXawyPSBjB9VT+68SoJVySQsYPCuEZCb0V/40n/a7RAbyrnNjP+2HwD7p27Pl1RSzqq35xiPdnycD1UeEPLpx/ON65mYCkn+KLQZmkqPio+vA2KmJngWTx+ol4rVFimGm76VT0xCFDsu2K0YX0yoLNH4u2XfmT9NR8gGfkVRCnnNjlbgHQmEwC75+GmEJ5DjD3d+s6IXTQ60MHvxbTHHlnfmPbgKn2SAI0uVoewKC9GyK6dSaboLw3C48jl0E2kyc+7umhCk3kEeWmt//GSjRNhoq+B+mynXiOtgFs/Am2v1TBjSb+6tcijsf5tFJmeGxlCjJnTdNWBkSHpMoo6OFkkpA6/FBAUHLSM7Yv8oYyd0GtwF5cCwQ6aRTbl9oG/mUn5Q92OnDMQcUjpgEho0Dcp2OqZyyxqQSPrbIIZZQrS2HkxBgjcfcSTuSHo7ONqlRjLUpO5yS95VLGXBLLHuCiIMGT+DW6DoJRtRIS+JieVWBoX0YsWgYInXrVlWUv6gDng5AyVFkUIFwZk7/3mVAgvXO83ArVKA4S747jT60w5bgV4Jy55slDM=", + "hash": "28560680247074990771744165492810964987846406526367865642032954725768850073454" } }, "TokenContract": { - "digest": "10ccc8e9c4a5788084a73993036c15ea25a5b2643ad06b76614de7e2910b2cc", + "digest": "346c5ce0416c2479d962f0868825b4bcbf68f5beac5e7a93632013a6c57d1be8", "methods": { "init": { - "rows": 656, - "digest": "b6debf22e710ad6bfb67177517ed66ea" + "rows": 655, + "digest": "3941ac88f0b92eec098dfcf46faa4e60" }, "init2": { - "rows": 653, - "digest": "b5ac64e93cd25de68a96f1a7b8cee9aa" + "rows": 652, + "digest": "1ebb84a10bafd30accfd3e8046d2e20d" }, "deployZkapp": { - "rows": 703, - "digest": "9b45cc038648bab83ea14ed64b08aa79" + "rows": 702, + "digest": "e5ac2667a79f44f1e7a65b12d8ac006c" }, "approveUpdate": { - "rows": 1929, - "digest": "5ededd9323d9347dc5c91250c08b5206" + "rows": 1928, + "digest": "f8bd1807567dc405f841283227dfb158" }, "approveAny": { "rows": 1928, - "digest": "b34bc95ca48bfc7fc09f8d1b84215389" + "digest": "240aada76b79de1ca67ecbe455621378" }, "approveUpdateAndSend": { - "rows": 2322, - "digest": "7359c73a15841a7c958b97cc86da58e6" + "rows": 2321, + "digest": "b1cff49cdc3cc751f802b4b5aee53383" }, "transferToAddress": { - "rows": 1045, - "digest": "79bdb6579a9f42dc4ec2d3e9ceedbe1c" + "rows": 1044, + "digest": "212879ca2441ccc20f5e58940833cf35" }, "transferToUpdate": { - "rows": 2327, - "digest": "f7297e43a8082e4677855bca9a206a88" + "rows": 2326, + "digest": "a7241cbc2946a3c468e600003a5d9a16" }, "getBalance": { - "rows": 687, - "digest": "738dbad00d454b34d06dd87a346aff11" + "rows": 686, + "digest": "44a90b65d1d7ee553811759b115d12cc" } }, "verificationKey": { - "data": "AAAVRdJJF0DehjdPSA0kYGZTkzSfoEaHqDprP5lbtp+BLeGqblAzBabKYB+hRBo7ijFWFnIHV4LwvOlCtrAhNtk/Ae0EY5Tlufvf2snnstKNDXVgcRc/zNAaS5iW43PYqQnEYsaesXs/y5DeeEaFxwdyujsHSK/UaltNLsCc34RKG71O/TGRVVX/eYb8saPPV9W5YjPHLQdhqcHRU6Qq7hMEI1ejTXMokQcurz7jtYU/P56OYekAREejgrEV38U82BbgJigOmh5NhgGTBSAhJ35c9XCsJldUMd5xZiua9cWxGOHm0r7TkcCrV9CEPm5sT7sP7IYQ5dnSdPoi/sy7moUPRitxw7iGvewRVXro6rIemmbxNSzKXWprnl6ewrB2HTppMUEZRp7zYkFIaNDHpvdw4dvjX6K/i527/jwX0JL4BideRc+z3FNhj1VBSHhhvMzwFW6aUwSmWC4UCuwDBokkkBtUE0YYH8kwFnMoWWAlDzHekrxaVmxWRS0lvkr8IDlsR5kyq8SMXFLgKJjoFr6HZWE4tkO/abEgrsK1A3c9F5r/G2yUdMQZu8JMwxUY5qw7D09IPsUQ63c5/CJpea8PAHcbRTYdRCpbscSmIstbX8jWBrg9LufKaJLfVQsGuyIvSHmj9IbGTLpbPr6uz/+gTuce5EOv1uHkF3g8HxyomAtU3betWNXGJbS4dC4hTNfWM956bK+fwkIlwhM3BC+wOai+M0+y9/y/RSI8qJkSU3MqOF9+nrifKRyNQ3KILqIyR7LjE0/Z/4NzH7eF3uZTBlqfLdf8WhXdwvOPoP1dCx1shF6g4Hh9V4myikRZBtkix1cO5FLUNLNAFw+glg1PB1eA+4ATFuFcfMjxDpDjxqCFCyuQ5TaLuNfYMA7fiO0vB6yqtWgSmCOlD/MQqAhHYRMq4PXk3TUQSle8XBZ67T0+gENjIJleTRgZFG6PgIEwHXcsKIvfFAPklTlnY+5sNVw8yBisVaFgw36DrHWNavWvsZM5HwD0h1Wk0hkavjEI5BbtUDG+pn3lbowHxfFJ/wf5VCLNQfn/di8uGbG0egJyahU9gHfw4MWCMHi8MI5Ofj/cqCekGDjDfnfymsSzPrFW8S2prrtw7qFiIZITFLHFe+jZN2a5iTEoIhyUOvYP+zJbfD1mrqoY7g0Iizhh610wdBy6TiGgfJpcDf3kUxuav/WbabGDMJhbugO4TNu1/i5omH8pbsjGGHQXk1UYPoP1SnMVPZ9RXPoWHJn/kePU9QqGxETHF4T7b2Ov7CcZDLuz147VCknmGiziHzbmYJleu4tzSlFsxHPkp2d9JiDUbO7X66Dh/+84gc5KWpMnEIAF9gITi3cXUglZTjWaASaXcpgHXXGZHZJcrG2VfPNjgTKJ1+CbvyXlvuhvX+0E2oaPB+BoP0i2iTXQHPNhOY/Gg2h6uKvE5fSSiYC7Rws2TGF1aEM54wX3Ti1qA1cAiNG5y8yk1YMGCk3TPqs9MRp0qjgjJbbvFlbgPkkqz5o6c7g8gfhIa4VEJyyI2joqJeIc7vMZFWhquSFHNs0TZKvKLiSAsyNDrpWZb/1PHxziswKvisk296AJi7hmlM1pKx6S4LlbT2OKLXbgq5HUKfe8QhxG4aOsPSSiVGwvnCrIPdSxLq77M27UWXnXHC8mmJmOsGUFj+bdX/u6AgrBhw/w74dDbuNEpC80PbJTuglF/TeDryYsFWCrBnF/WPstgzy3zDDTZ3DXHVYVxOEvErIynlQEY9Cv9QSxRI3dA+hLtob/L78ZeJSU4Al+Qv0QGZTOxQORosVshOP2eFQ1VMKGWOpCVvyi8QE4fa+gOgYT0JRm4rkQBZ5WDlYGkamD3euC92Kd7Z39G89h/AqeFACahkAW1a78SzLW69mZ+CDLfKp/xQsi2TWgJqGh7QNOEtMnn/2owLzLWd071mvUtT0484Eqx6hUqLJMH70p8oUjQIMsh0mvp1BWSU8XC6z+UZIpVm2CERrV8BMLmTLOgTNJlEIJQR7zzpJCDFNNOI+Y2ZtdcuU8XHgcsQhQ3PgCACFAWN3rO+goXoTWdYR/LcqszKzPnMArmPIHWkRM6Mkm13OsHXCVudUbqQjC/pNQZH1VW+RMXnre1vQVb3fnCy5h28Dce3Q2WzjBSZFhe3iADZpo7gWHM/sqe+Mbnbn8A+RRWVNbtjss9376jN73zV4xPH3un3VjTxrzCluqR8MbH8t7mhPBqV5CslmSIbDNruVXtwCf4VS1nssw63PfLzeOSvzhTTsg82rna/+TKl1RIwhD8VFnCDq/Rk8fdy/+K5qP6GcSTbh6J8ERx4jOOukL9TUCpJkhvo/3ED8GOewmWAwzL8avXuf9AFvhwH3ENp5v4IIGBljuDJ77vckGmTI=", - "hash": "19471120960548999816351394077166332230185600464930675317083534065611063009411" + "data": "AAAVRdJJF0DehjdPSA0kYGZTkzSfoEaHqDprP5lbtp+BLeGqblAzBabKYB+hRBo7ijFWFnIHV4LwvOlCtrAhNtk/Ae0EY5Tlufvf2snnstKNDXVgcRc/zNAaS5iW43PYqQnEYsaesXs/y5DeeEaFxwdyujsHSK/UaltNLsCc34RKG71O/TGRVVX/eYb8saPPV9W5YjPHLQdhqcHRU6Qq7hMEI1ejTXMokQcurz7jtYU/P56OYekAREejgrEV38U82BbgJigOmh5NhgGTBSAhJ35c9XCsJldUMd5xZiua9cWxGOHm0r7TkcCrV9CEPm5sT7sP7IYQ5dnSdPoi/sy7moUPRitxw7iGvewRVXro6rIemmbxNSzKXWprnl6ewrB2HTppMUEZRp7zYkFIaNDHpvdw4dvjX6K/i527/jwX0JL4BideRc+z3FNhj1VBSHhhvMzwFW6aUwSmWC4UCuwDBokkkBtUE0YYH8kwFnMoWWAlDzHekrxaVmxWRS0lvkr8IDlsR5kyq8SMXFLgKJjoFr6HZWE4tkO/abEgrsK1A3c9F5r/G2yUdMQZu8JMwxUY5qw7D09IPsUQ63c5/CJpea8PAHbUlzRl2KhAhm58JzY0th81wwK0uXhv2e0aXMoEpM0YViAu+c/32zmBe6xl97uBNmNWwlWOLEpHakq46OzONidU3betWNXGJbS4dC4hTNfWM956bK+fwkIlwhM3BC+wOai+M0+y9/y/RSI8qJkSU3MqOF9+nrifKRyNQ3KILqIyR7LjE0/Z/4NzH7eF3uZTBlqfLdf8WhXdwvOPoP1dCx1shF6g4Hh9V4myikRZBtkix1cO5FLUNLNAFw+glg1PB1eA+4ATFuFcfMjxDpDjxqCFCyuQ5TaLuNfYMA7fiO0vB6yqtWgSmCOlD/MQqAhHYRMq4PXk3TUQSle8XBZ67T0+gENjIJleTRgZFG6PgIEwHXcsKIvfFAPklTlnY+5sNVw8yBisVaFgw36DrHWNavWvsZM5HwD0h1Wk0hkavjEIz9nTxQU+nsZsR+70ALZ69HljR0fUjNU7qpVmpYBlRiFxA/BWf8qie2wfhSfy6Q1v5Ee4+3vN/mYuS3uF47LkM1dRTanQ73mLIz80yky+lCNkLWHmZtyWjtMsDFNgupc+yc+FvFNjJM/ea6u3PROtSyU3rAlmchkKvxO4qfrd0iqav/WbabGDMJhbugO4TNu1/i5omH8pbsjGGHQXk1UYPoP1SnMVPZ9RXPoWHJn/kePU9QqGxETHF4T7b2Ov7CcZDLuz147VCknmGiziHzbmYJleu4tzSlFsxHPkp2d9JiDUbO7X66Dh/+84gc5KWpMnEIAF9gITi3cXUglZTjWaASaXcpgHXXGZHZJcrG2VfPNjgTKJ1+CbvyXlvuhvX+0E2oaPB+BoP0i2iTXQHPNhOY/Gg2h6uKvE5fSSiYC7Rws2TGF1aEM54wX3Ti1qA1cAiNG5y8yk1YMGCk3TPqs9MRp0qjgjJbbvFlbgPkkqz5o6c7g8gfhIa4VEJyyI2joqJeIc7vMZFWhquSFHNs0TZKvKLiSAsyNDrpWZb/1PHxziswKvisk296AJi7hmlM1pKx6S4LlbT2OKLXbgq5HUKfe8QhxG4aOsPSSiVGwvnCrIPdSxLq77M27UWXnXHC8mmJmOsGUFj+bdX/u6AgrBhw/w74dDbuNEpC80PbJTuglF/TeDryYsFWCrBnF/WPstgzy3zDDTZ3DXHVYVxOEvErIynlQEY9Cv9QSxRI3dA+hLtob/L78ZeJSU4Al+Qv0QGZTOxQORosVshOP2eFQ1VMKGWOpCVvyi8QE4fa+gOgYT0JRm4rkQBZ5WDlYGkamD3euC92Kd7Z39G89h/AqeFACahkAW1a78SzLW69mZ+CDLfKp/xQsi2TWgJqGh7QNOEtMnn/2owLzLWd071mvUtT0484Eqx6hUqLJMH70p8oUjQIMsh0mvp1BWSU8XC6z+UZIpVm2CERrV8BMLmTLOgTNJlEIJQR7zzpJCDFNNOI+Y2ZtdcuU8XHgcsQhQ3PgCACFAWN3rO+goXoTWdYR/LcqszKzPnMArmPIHWkRM6Mkm13OsHXCVudUbqQjC/pNQZH1VW+RMXnre1vQVb3fnCy5h28Dce3Q2WzjBSZFhe3iADZpo7gWHM/sqe+Mbnbn8A+RRWVNbtjss9376jN73zV4xPH3un3VjTxrzCluqR8MbH8t7mhPBqV5CslmSIbDNruVXtwCf4VS1nssw63PfLzeOSvzhTTsg82rna/+TKl1RIwhD8VFnCDq/Rk8fdy/+K5qP6GcSTbh6J8ERx4jOOukL9TUCpJkhvo/3ED8GOewmWAwzL8avXuf9AFvhwH3ENp5v4IIGBljuDJ77vckGmTI=", + "hash": "13796172868423455932596117465273580383420853883879480382066094121613342871544" } }, "Dex": { - "digest": "2039439604f1b4a88563bd34178bb380c0d998cc662b8fe9b9ea1164eefe70c7", + "digest": "14f902411526156cdf7de9a822a3f6467f7608a135504038993cbc8efeaf720a", "methods": { "supplyLiquidityBase": { - "rows": 3752, - "digest": "8d00a233c53340e6ef8898bb40ad919c" + "rows": 3749, + "digest": "08830f49d9e8a4bf683db63c1c19bd28" }, "swapX": { "rows": 1986, - "digest": "23a2ddbc46117681d58c03a130cfee3f" + "digest": "e1c79fee9c8f94815daa5d6fee7c5181" }, "swapY": { "rows": 1986, - "digest": "c218b1e81ac390ba81678a5be89bc7a7" + "digest": "4cf07c1491e7fc167edcf3a26d636f3d" }, "burnLiquidity": { - "rows": 719, - "digest": "63d13fc14775e43e4f6dd6245b74d516" + "rows": 718, + "digest": "99fb50066d2aa2f3f7afe801009cb503" }, "transfer": { - "rows": 1045, - "digest": "dd6f98961085734ff5ec578746db3a53" + "rows": 1044, + "digest": "7c188ff9cf8e7db108e2d24f8e097622" } }, "verificationKey": { - "data": "AADgDFCYyznG8hH/Z695+WW86B544SmJFzz5ObrizTJ4KMqy+pfsOR2Mt2yGViXSJPpAR76RNHNga83UB8/9OPQIB+uHOnxXH7vN8sUeDQi50gWdXzRlzSS1jsT9t+XsQwHNWgMQp04pKmF+0clYz1zwOO95BwHGcQ/olrSYW4tbJN6KW0hN2eESQfUJcwfB6uUzwvGtkFs+aiUykn7KUgUgXQkKgdHHdyFioNHNPmkpiAre/Ts8BKwwvf5hCa1MtBF6ax6ymlATB4YBL0ETiEPTE/Qk1zGWUSL2UB6aY45/LlfTLCKlyLq7cR3HOucFfBncVfzI7D8j5n4wVqY+vAI4cf+Yv7iVRLbeFcycXtsuPQntgBzKa/mcqcWuVM7p2SYRrtKdX8EKvOO6NhfLx4x0atAi8pKf+vZR76LSP4iOA8hwXvk6MNvPt1fxCS96ZAKuAzZnAcK+MH1OcKeLj+EHtZmf40WRb3AEG5TWRKuD6DT5noDclZsE8ROZKUSOKAUGIBvt7MpzOWPPchmnromWEevmXo3GoPUZCKnWX6ZLAtJwAszLUgiVS8rx3JnLXuXrtcVFto5FFQhwSHZyzuYZACz8aW+oEDHXv3riERCsWdAt/zvOg0mMRDp1/RqJ/18vkTKQH2mfv2wQlLNUma0senYjXxHGjRexXfLGK88bpCVK3k+3L+ZskBbfKiZXd+gbKIy6+Hv2EhkBjtG9h6OrEggQwRgP4mWuoJ91bSnCFiMLJyH5dV4yry6WsGsfiUIFJ5M/KjfmCc2/EsnV7Mhax350ZtrXdzh/HWIWzEZKKxcbERFbRtf+fkMOOLNpNov1FEFvKOU612vDOIbrVHeBN9mwuepUrJctcfgLc0Mi3Sxs3+NA0I74qm5ktjmplDwgUtKzIs3IrVFv6b1pg/J32HmwNzJZw2fYzpFE1LDjBSK/SX3axwMy5yEd8+jl4uAdQZpa9UQQIHu1Y1ZMgJSDDicXz6D1bZMA1Q2/lU+8AYbldgQVmlLq/lzr63krX+AM0ziaXap3vDs8CDbm/+mAPE51oDFs5zs57N0ue8N/OhsUv6w9D2axBwF8udR96c+MZkLi29Fu/ZC3Op4qCZK+PUpnwM1uzqAGDEbkGfUh1UR/PColxru25K5GS10vC0oFC+TdXVnVj3kTdztGDJk0udozRbpvb+S6A1YwO6+OOhH59l19FcR35ItoigIxtMfkv3rdlCOeBVI93oVl5esiH8AvYGHhulWIvrNfKol3Viir41zv4qMBOcQg8+ygqjwqREU5+qiYeJlQ2AtT0/PVeZWg4mHC39uz1Lld3N2hyyxRo+Z0nC/8220uuf9gAnQ+JFixgyYW0NowUtuFj+uYAV9Dh/Zpe4LyAOkU0kBW4CEuOxNr+gz+9h0BoPfBHlMuuQAUc5L8uMunJC7uBKZiL+/tT1ZGfyIuqU47fEP9Hghxmip8v7gpf+4wB0MVUUwav9QRe9g88ER1HcJPqYb4EIOc2kbYSX75bT0mAFqR8lwZrj6lbQtNS0QQboG5fzoyYGi8YnSXhC2T5fFDpGJ319GHUsna58o5wk8LMwKWNTxq+FN6XiRgu0BFOrtG6MtT1OxYE9Dti6WatGDsWv+KMLDHjxUK1bhiSRnvkWYNcnuDJ0Ry+PRGHNUijVU0SbchntC2JHdhwKbwIofwKHE8HhvlK8FgQ1VOLDioA26UFzr23LpCTqwSJ7/sAqttNGcPR8MSeeR9TQvXNYQPKrA7Gh720X+7LD6BuHdy4vkcr9EKBU0ccUJ2ABBiyPdji+AgEbUCL/wrp6/GX8pui5YJGWx3XmIFj/RnYS2Je5FZ7w74JclD3XhLUo5Dhpq5RznHplpLB9mNdZdm5269US/XCgC/ZKyUxW3+0ajdBY1cLzF6qglitaYTp3MVUENVOkACM2RyKw6jIK2Leq3qLp6AUz21VXj4WznZcdI8MXqT9v8HxjXbAI9dtbhLRZRpJmu/129vrVmwSTHvsVoA7vXyYh/iO3ZMcy+D1x+HZU6Q/oDYCicqOPHxpSc9QGehmNyeGzI//524Gz3RudkU7s6MPdLWqZrieRTnWsTIrCDieu4ValfP8BFz7asYUv0t9jMWpv3yjbY7c5h8N/m7IUXwTQCzFpjPV7HC72BjVwPaYqh5/oAQsSNcv5I3c2GsCGj5C4hFFoT7eWfVtu/6ibQl0COhRDsegnOBtZ7NGfybI8IIO/4yrgel92bypb3eSxeMvdE5wzURluGDkBVVIACD8C5W1MzqrejUiiTfc3mkLhQ0xKRRhT0qqkmYWlbGN5hmMOA9YaYx8OFTgMys1WbzdidWgEkyvvdkWctGlges6eg/lJE61tJ8wGxvJfKtpyDW/2MRvsnO1+2EXIQ2eV3hkxg=", - "hash": "14012766312159048636010037107657830276597168503405582242210403680501924916774" + "data": "AADgDFCYyznG8hH/Z695+WW86B544SmJFzz5ObrizTJ4KMqy+pfsOR2Mt2yGViXSJPpAR76RNHNga83UB8/9OPQIB+uHOnxXH7vN8sUeDQi50gWdXzRlzSS1jsT9t+XsQwHNWgMQp04pKmF+0clYz1zwOO95BwHGcQ/olrSYW4tbJN6KW0hN2eESQfUJcwfB6uUzwvGtkFs+aiUykn7KUgUgXQkKgdHHdyFioNHNPmkpiAre/Ts8BKwwvf5hCa1MtBF6ax6ymlATB4YBL0ETiEPTE/Qk1zGWUSL2UB6aY45/LlfTLCKlyLq7cR3HOucFfBncVfzI7D8j5n4wVqY+vAI4cf+Yv7iVRLbeFcycXtsuPQntgBzKa/mcqcWuVM7p2SYRrtKdX8EKvOO6NhfLx4x0atAi8pKf+vZR76LSP4iOA8hwXvk6MNvPt1fxCS96ZAKuAzZnAcK+MH1OcKeLj+EHtZmf40WRb3AEG5TWRKuD6DT5noDclZsE8ROZKUSOKAUGIBvt7MpzOWPPchmnromWEevmXo3GoPUZCKnWX6ZLAtJwAszLUgiVS8rx3JnLXuXrtcVFto5FFQhwSHZyzuYZAOLg+O5JsHz6EFkkWxwdCSmy1K1LFaS6A78wbTtc9uIslLAntKxTApVE2lxPzH+TwHBFMkSweXxdP3dGxtecxxpbbLKvz9Clh3WpX0ia/8PSErjEfdpClkDrgo8DG2MpEgFaBcgfyFNTEhXLnxCiGlwjJ+DdBAfnonMPIkkY6p0SJ5M/KjfmCc2/EsnV7Mhax350ZtrXdzh/HWIWzEZKKxcbERFbRtf+fkMOOLNpNov1FEFvKOU612vDOIbrVHeBN9mwuepUrJctcfgLc0Mi3Sxs3+NA0I74qm5ktjmplDwgUtKzIs3IrVFv6b1pg/J32HmwNzJZw2fYzpFE1LDjBSK/SX3axwMy5yEd8+jl4uAdQZpa9UQQIHu1Y1ZMgJSDDicXz6D1bZMA1Q2/lU+8AYbldgQVmlLq/lzr63krX+AM84dcwR1Ur7O0YSVR5TXXJhMigCPYsF0/fmLijOWNAga8rtMJvF0oZ02aoQv4KpGu9yq72CsoXSpWqu/6C+GSP52zL9QV0VkohE1njGsSrC/EMtWuNxk6avge+WIxnbAbrFVGoWKdAN3uuZBKQW6ehhi1watI+S5lkpbpTnrK3R/59l19FcR35ItoigIxtMfkv3rdlCOeBVI93oVl5esiH8AvYGHhulWIvrNfKol3Viir41zv4qMBOcQg8+ygqjwqREU5+qiYeJlQ2AtT0/PVeZWg4mHC39uz1Lld3N2hyyxRo+Z0nC/8220uuf9gAnQ+JFixgyYW0NowUtuFj+uYAV9Dh/Zpe4LyAOkU0kBW4CEuOxNr+gz+9h0BoPfBHlMuuQAUc5L8uMunJC7uBKZiL+/tT1ZGfyIuqU47fEP9Hghxmip8v7gpf+4wB0MVUUwav9QRe9g88ER1HcJPqYb4EIOc2kbYSX75bT0mAFqR8lwZrj6lbQtNS0QQboG5fzoyYGi8YnSXhC2T5fFDpGJ319GHUsna58o5wk8LMwKWNTxq+FN6XiRgu0BFOrtG6MtT1OxYE9Dti6WatGDsWv+KMLDHjxUK1bhiSRnvkWYNcnuDJ0Ry+PRGHNUijVU0SbchntC2JHdhwKbwIofwKHE8HhvlK8FgQ1VOLDioA26UFzr23LpCTqwSJ7/sAqttNGcPR8MSeeR9TQvXNYQPKrA7Gh720X+7LD6BuHdy4vkcr9EKBU0ccUJ2ABBiyPdji+AgEbUCL/wrp6/GX8pui5YJGWx3XmIFj/RnYS2Je5FZ7w74JclD3XhLUo5Dhpq5RznHplpLB9mNdZdm5269US/XCgC/ZKyUxW3+0ajdBY1cLzF6qglitaYTp3MVUENVOkACM2RyKw6jIK2Leq3qLp6AUz21VXj4WznZcdI8MXqT9v8HxjXbAI9dtbhLRZRpJmu/129vrVmwSTHvsVoA7vXyYh/iO3ZMcy+D1x+HZU6Q/oDYCicqOPHxpSc9QGehmNyeGzI//524Gz3RudkU7s6MPdLWqZrieRTnWsTIrCDieu4ValfP8BFz7asYUv0t9jMWpv3yjbY7c5h8N/m7IUXwTQCzFpjPV7HC72BjVwPaYqh5/oAQsSNcv5I3c2GsCGj5C4hFFoT7eWfVtu/6ibQl0COhRDsegnOBtZ7NGfybI8IIO/4yrgel92bypb3eSxeMvdE5wzURluGDkBVVIACD8C5W1MzqrejUiiTfc3mkLhQ0xKRRhT0qqkmYWlbGN5hmMOA9YaYx8OFTgMys1WbzdidWgEkyvvdkWctGlges6eg/lJE61tJ8wGxvJfKtpyDW/2MRvsnO1+2EXIQ2eV3hkxg=", + "hash": "6361961148584909856756402479432671413765163664396823312454174383287651676472" } }, "Group Primitive": { "digest": "Group Primitive", "methods": { "add": { - "rows": 33, - "digest": "4e687eaee46e4c03860f46ceb2e50bd3" + "rows": 30, + "digest": "8179f9497cc9b6624912033324c27b6d" }, "sub": { - "rows": 34, - "digest": "dc048f79ddfd96938d439b8960d42d57" + "rows": 30, + "digest": "ddb709883792aa08b3bdfb69206a9f69" }, "scale": { - "rows": 114, - "digest": "198a5c73fb635db3d7ca134c02687aba" + "rows": 113, + "digest": "b912611500f01c57177285f538438abc" }, "equals": { - "rows": 42, - "digest": "bca7af6eb9762989838d484c8e29a205" + "rows": 37, + "digest": "59cd8f24e1e0f3ba721f9c5380801335" }, "assertions": { - "rows": 20, - "digest": "d4525ed2ab7b897ab5d9e8309a2fdba2" + "rows": 19, + "digest": "7d87f453433117a306b19e50a5061443" } }, "verificationKey": { "data": "", "hash": "" } + }, + "Bitwise Primitive": { + "digest": "Bitwise Primitive", + "methods": { + "rot32": { + "rows": 31, + "digest": "3e39e3f7bfdef1c546700c0294407fc2" + }, + "rot64": { + "rows": 10, + "digest": "c38703de755b10edf77bf24269089274" + }, + "xor": { + "rows": 15, + "digest": "b3595a9cc9562d4f4a3a397b6de44971" + }, + "notUnchecked": { + "rows": 2, + "digest": "bc6dd8d1aa3f7fe3718289fe4a07f81d" + }, + "notChecked": { + "rows": 17, + "digest": "b12ad7e8a3fd28b765e059357dbe9e44" + }, + "leftShift": { + "rows": 5, + "digest": "451f550bf73fecf53c9be82367572cb8" + }, + "rightShift": { + "rows": 5, + "digest": "d0793d4a326d480eaa015902dc34bc39" + }, + "and": { + "rows": 19, + "digest": "647e6fd1852873d1c326ba1cd269cff2" + } + }, + "verificationKey": { + "data": "", + "hash": "" + } + }, + "Hashes": { + "digest": "Hashes", + "methods": { + "SHA256": { + "rows": 14494, + "digest": "949539824d56622702d9ac048e8111e9" + }, + "SHA384": { + "rows": 14541, + "digest": "93dedf5824cab797d48e7a98c53c6bf3" + }, + "SHA512": { + "rows": 14588, + "digest": "3756008585b30a3951ed6455a7fbcdb0" + }, + "Keccak256": { + "rows": 14493, + "digest": "1ab08bd64002a0dd0a82f74df445de05" + }, + "Poseidon": { + "rows": 208, + "digest": "afa1f9920f1f657ab015c02f9b2f6c52" + } + }, + "verificationKey": { + "data": "", + "hash": "" + } + }, + "ecdsa-only": { + "digest": "1e6bef24a2e02b573363f3512822d401d53ec7220c8d5837cd49691c19723028", + "methods": { + "verifySignedHash": { + "rows": 30680, + "digest": "5fe00efee7ecfb82b3ca69c8dd23d07f" + } + }, + "verificationKey": { + "data": "AAAdmtvKeZvyx7UyPW6rIhb96GnTZEEywf8pGpbkt+QXNIm7oWxIDWYa4EWTiadEqzk8WXg3wiZmbXWcqBQU+uIoTiBnYTRcd7RsaAjbdbIbQJ9EuopFRFewZRx9qeQeEibNeMRcRMP4LdfS3AQRxhFZzN4HFa4MbtGs+Aja820cI9VFULH2/7BvD6JjpVWjVLvvo6zhO3S5axqfDh7QqtkPo3TLpand9OVvMHhTVlz/AV7rus5E/+0cv50MaEJ/wBfUh5XNLAlGgVi7FfWR6p9P72AAymyD3lUdecJyZmCREiVgPrTdFppkp45TefJWNTySkV9c5YzpNxQoXedZDvYP/5s4KBkfIeK+zB2yJC9eZ1ZDYfM88shGDYxmBtur9AkQ49QGquR+kYUI0lpXtuNMG+ZRy0FRJ8ci/TE+PIPIFnSiGcSOA3YM2G171LYf89abU2QUoQRHSP3PmmAOy/8CoRLVro7Nl6z/Ou0oZzX7RjOEo//LBqcSWa2S9X8TQz0R3uivbovTdq0rrba56SbEnK6LWItmBc6CubYWL7UzDbD3RZM6iRz1hqTHDzDz7UIWOzHgLqW9rjnZllQCyfsSAEce2RV0gBkOlsJXf/A50Yo1Y+0y0ZMB/g8wkRIs0p8RIff5piGXJPfSak+7+oCoV/CQoa0RkkegIKmjsjOyMAAgKzcNZhhPW5VfbcSYDpx5nVaU5pTEFl+2+RlcuhBpG1ksAWbD64AUKDjdyTWIC5Wn68AagPtG65V13eFS5LgkSfVtNXxGodg7SdP4AJmXpBgZfzMg4RW6Qje5ZFfrwRzoHPo0y7nO1hkaNLGV3Wvd3/pYiebXvyo+DdTZmaMbJpJaGSCysnovOrVUIpcn4h1hvA12jztQFQcbNHoVeZgslPxA54y9ynjhN7VZfT8lNXXIrRCpmPaxZW6Bw6Op/g6FGIs8TlruzZJRhz1lOLvl2FPvrUsFtz4yWTPjbT+VGsKuJFPvMuYybxq8pGyWVQN023uObDel47krlcQoH4MAggd39s50O0IaSbMgpJUWQVx+sxoXepe26SF5LQjWRDf7usrBVYTYoI9gDkVXMxLRmNnjQsKjg65fnQdhdLHyE/DR5DLlenSa0wQ3PXdv/C9LpDvkzJOLZs+/ZePd4YMI0+WuP2+6Xas4aNM+4JkNuHF5uMDcxgWID4TUy7Vdlzm3CVbhX15uBoKhuYWQgLr2rnVJ5SOZoDvlwJtcK2izLMYVAasejw4fvsehYGb88wvDbFxS6sM9gDSgTlavZRs95Qf+c1KpYf/jb8BxYNrwrqy8F++c1APDzfzQ/IbVLiaL28wkEy412qmXSjM+9hErKXFy8JIT/WBOIWMMg/7mMjvxngHnci+aYJZ6J+Lszh5zgo708vzO7fwaxC0wgd8anH3gFrbFnOg1hkmmoUEIgIwXh+ynuoZPOaoKNXNm1jOl8HpdFOG7vpQavC600YgzS2YGtY7K2WQ5GtN5ZTZBHPsUSir2yKSo9Le9CWXbDtn3SBDepWypwDa3YWKtNog+y10VmpL1N+RG3u1DXSuY7y9WZgkQ7tdvyx/Gjr91kjF0s3bt7vHIAZCtzNlRlWDBz3og0cSnEucCEuKR6dL2Mz+RuF1GmLoXZXapUjVG/82BjdAMAOxPlE67lEs+JWgnrVrA5NLJoL4DZ6+fhQKpNfk0uOrEfZIWR9Sau0IBwBxu6IYVm5/XAB19dt8MAuVcRdN/JGGzo0Hr3WVJuKzbAhuFwJZzcd1J1n4xO09ECT5NQdFSFXGsy8kIFjRNEOkLl+bAExePtGCt0w6cYqB0uCeX3lTI7ugIEgdStMtHFiWngJ218l8CuVrkwTJ7ZqHLtuJDiNqlLptkHWChDfw+IgDwz85dZrfBBzQrMRWranxQmisM+wx3vC+pLURRQHZJEasGCAElj0lTColrqQ/cXS7cBaqs1tBsQDGzKYMCMwsqL53fyxGCljVvljBa99+FpYfoUK+Fi0z6uEbem+luXRScr2yPB5I08lnBY23RmBb/pfSyBfbcmnmF5BkRlJTJKY7fQL/t9bFfywoquQe9e7OQvIjppA/FO7HmZS6hoOU+eS8+W94fEF2gvrowpTeqQHM6hLN9Qzl8niwZWUIyRCfyuzQnuSz/VP1K2sMFBKnZZNDcuBh1/xSFymOH6LfNKostvc6qHTIxrTjlH6952bo1bQl+mVvBUaJuRkYh12QbcyIyzcBFUYwaFazzkHXMof0O30oL3Q6wegTvJxTSZD5VCr5D26Myzoa0JBpqL0st9/MNGZe5a/+HW1qan/VtGA5nYkJcUzwKVqqlmZeuOZekFLGxlfp0lv9IQUQWtiU5uvd5HVoolEc/teUnx/IxYe01IDxX9cbmPMJnLYXJGSY=", + "hash": "13090090603196708539583054794074329820941412942119534377592583560995629520780" + } + }, + "ecdsa": { + "digest": "334c2efc6a82e87319cadb7f3e6f9edb55f8f2eab71e0bcf2451f3d5536de5fe", + "methods": { + "sha3": { + "rows": 14494, + "digest": "949539824d56622702d9ac048e8111e9" + }, + "verifyEcdsa": { + "rows": 45178, + "digest": "0b6ce4cc658bcca79b1b1373569aa9b9" + } + }, + "verificationKey": { + "data": "AAB/ZjMH6HxnifKVGWGw41mrmDWq4R7eG+tJRvszclriFqNFKZXi3LD9Z+2F/VSHlQD8mq3JCoD6tm3rlEQXheATALIHoglupcYB4X43h14qFKloG2kuKwLeDl6mRpeVwSmwE/Tv+P3K8KkyB9zmoT1eEt/ayPRm4EzrJb0EHzyrE0VhKfn6Xh48tBUpGk3JahLhsK+yOTjDBvwcZ/vsiNkGCtFbQv9rM7YZlpBk6NmFYzcnsfJOHyYyr6Xab+6UYjVt6aJGVLMgCv+qLekdsCsmVBwOWhEWwASVZ5WW5qWyOL613+zKXyQvqBcWWvg68CZk2rEjZvH64dPlVXV0hxYLb7p43WNgY+e8MR+RHA0/aGnvB4cwCKQw23n2B9bg+wxDwS7fK9/va/0HspgUvwGv8fNpdsYW1OgLnY1deUHhJc1+UaUFwHJVdB7Nx/WmQ0eq0s2wtLQGy0tpkDAkL5I9yd+qOSIbRoqSxBA7l/HWht0xGvVgNUOarOAeoqwfuBre8d3L5s5RR0HSmbjjP26/82rqxhQxiO7/O9lkCJ3GEeRqFbufY02mX+khOFw0uNU9XC6PXVmf01pMU7saHI4MAH9Akhy847NB7DCD8FCNcRyDJyDFJLlhdVhiVMAh55EYy513dTTwhKN9XKico081U+T2n7No0PiKZ32DBpDoLAPXVmyUfcILoFX2PQCQeSvOfAFM9mmARZ+OTRI8YpJuOzL9U2eIRqy34iz23PcKBmKQP0fGuuRJQOMEeRZp6sAjKJR/Temid2hEFxMIGnnwJGg/lEq5zXgo+e6Sdz4QeQEIT9PKi46gYqWxzyVsBJhtXX90uGNqo+ExnU3EGCvsB0/X9CQMBMKkHWk9zwkpQ3aQeVtNrzaLporX2ZQlPb03NgWHdeSk7V1VWa8KlCdsxVxj5tEOaWEX21jc6fMfUzegESsz7szcSEmbKYdzglTPAQ7DxXl0uhwGhAI9yPO3OtcqArq9TmnU/fkGG513bbwNYfAwr6P3u0QUs7GKZu8tRl04WobviAvYFoag3SDW1q6Vw5hze027jtn/cNmKGjLtwXvyz9YIeYEHNon9r2cWxrQOTvP/FhG/5+TsFMPsA5fH3hU0Fz1gmXw3qss0RmfBxLk+EdQC6m5yA/2B/8EPdrq5oHQPUyb6IHvNvUWn/Q/dMbZOKrMX2W+Htjjqtx4RTk4kxBypgD+8d4XRa8b3nN1iah85f+Qq6HcFzRB6E/Lye/74I01SUmwebrztjSxsz2qvHGoWtj+Ael25p2U3273WHAQtqv/koetaNRg7a17YCbEcLSCZTwI71dYDMiEFCaeijJL/ZJjRkev/LKK+dgBI8hi2ifiGzUsmNsJ1JUbNOjHS0et1ZljCsxnJ8GtPIcYU3+Az4UgJG6hcgZIYDmbwt/ElByu2obpX6gO2AD0g95NMIhOvlXXrfkRrygPlDiRIUUyO9h7z5mXB+A1iDUoi+m3hfbgLyjzXEDtJLZnX5k0gVVLjwGkk1Tgw0pJgWj1H+8sFAjKLB5T9yIEgYpLcXXbtV5zfaBXzPUFeLUAonv4S/gUhMA1QgSwXyCffgLRBrIXgwwiYepCmu1RYWXkLkTBwvSuTtKj9z4QGNdp5tBHrXKCOrYYN0chiDKIhFAa+PtylRK2fGeVnOZQSMcy04VIC5T+e0KR3pIt9PdJ72SHEYjmdNL+W9JL/fjz+co5Vxpz6p6RCwE1pL+kw4B0v1/BhJJB+NIv+GXTxOYeGx8K/P/3B9KUsbBykBQOvjcM8kJFIx5yry9idrtEGh5Ejw7UZ1W2sWIpFdKIAGUm54ir07a5Txkhy7PBIiyp5PRacp6V6S9aI8liE5LRC1qguXcKnbBg3QudnY/wUFwB6rJf7EBA2otA8PgEG8ICOg86XltPUqpVKUMBiu30jIa0VFS3O8BtuJvzdmVa8xK4K3ZP3/T4s4SAMOtyi6L4pxTJB9NHI0WnvQQeVAxlizlIu6VMwDdZuVtRJa0wbKTIDmc7lF+d2pD5uYOSLakc5h5jKRz386Dt9pYA577NKP+ioED2m+iv9bLPDIhDo6X589HxFZX4oxrzDI7m85rUvWBgNWqjZ9MpQSuui9yr2F5P8xtBsHDEIRi+wqX7Sui56cRuLZYaWvMVOIwkplHDIz0afncgI5BcA8KM4mJfpKosF42cWAMhijdDVAG234RIP6ogJCAcQu3q+q8uG2sYf655WE3+70zh0aiLvgQa1odo9FdEXV5HWs6Gh4hUDhA0ULlZVbJ+Mxg/XacBb+AnBxBpuSrIjfHaEtCkFKlDdF48Ny+HFVojxE4ZYuoNlXDDVkBi58aj2oMFDfaPB+bEJjxF7wpqjbts703mEckhLhQWpnnhNVbEyBGED8N4gIiA=", + "hash": "18381574995221799963215366500837755447342811019610547066832598459350935665488" + } } } \ No newline at end of file diff --git a/src/examples/vk_regression.ts b/tests/vk-regression/vk-regression.ts similarity index 81% rename from src/examples/vk_regression.ts rename to tests/vk-regression/vk-regression.ts index 6dbcd31373..9c63c7e38e 100644 --- a/src/examples/vk_regression.ts +++ b/tests/vk-regression/vk-regression.ts @@ -1,14 +1,18 @@ import fs from 'fs'; -import { Voting_ } from './zkapps/voting/voting.js'; -import { Membership_ } from './zkapps/voting/membership.js'; -import { HelloWorld } from './zkapps/hello_world/hello_world.js'; -import { TokenContract, createDex } from './zkapps/dex/dex.js'; -import { GroupCS } from './primitive_constraint_system.js'; +import { Voting_ } from '../../src/examples/zkapps/voting/voting.js'; +import { Membership_ } from '../../src/examples/zkapps/voting/membership.js'; +import { HelloWorld } from '../../src/examples/zkapps/hello_world/hello_world.js'; +import { TokenContract, createDex } from '../../src/examples/zkapps/dex/dex.js'; +import { + ecdsa, + keccakAndEcdsa, +} from '../../src/examples/crypto/ecdsa/ecdsa.js'; +import { GroupCS, BitwiseCS, HashCS } from './plain-constraint-system.js'; // toggle this for quick iteration when debugging vk regressions const skipVerificationKeys = false; -// usage ./run ./src/examples/vk_regression.ts --bundle --dump ./src/examples/regression_test.json +// usage ./run ./tests/regression/vk-regression.ts --bundle --dump ./tests/vk-regression/vk-regression.json let dump = process.argv[4] === '--dump'; let jsonPath = process.argv[dump ? 5 : 4]; @@ -37,9 +41,13 @@ const ConstraintSystems: MinimumConstraintSystem[] = [ TokenContract, createDex().Dex, GroupCS, + BitwiseCS, + HashCS, + ecdsa, + keccakAndEcdsa, ]; -let filePath = jsonPath ? jsonPath : './src/examples/regression_test.json'; +let filePath = jsonPath ? jsonPath : './tests/vk-regression/vk-regression.json'; let RegressionJson: { [contractName: string]: { digest: string; @@ -56,7 +64,7 @@ try { } catch (error) { if (!dump) { throw Error( - `The requested file ${filePath} does not yet exist, try dumping the verification keys first. ./run ./src/examples/vk_regression.ts [--bundle] --dump ` + `The requested file ${filePath} does not yet exist, try dumping the verification keys first. npm run dump-vks` ); } } diff --git a/tsconfig.json b/tsconfig.json index 7eb2e0ba4d..1884914e9c 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -6,7 +6,7 @@ "outDir": "dist", "baseUrl": ".", // affects where output files end up "target": "es2021", // goal: ship *the most modern syntax* that is supported by *all* browsers that support our Wasm - "module": "es2022", // allow top-level await + "module": "nodenext", // allow top-level await "moduleResolution": "nodenext", // comply with node + "type": "module" "esModuleInterop": true, // to silence jest diff --git a/update-changelog.sh b/update-changelog.sh new file mode 100755 index 0000000000..701a1bc491 --- /dev/null +++ b/update-changelog.sh @@ -0,0 +1,34 @@ +#!/bin/bash + +# CHANGELOG Update Script for New Releases +# +# This script automates the process of updating the CHANGELOG.md in response to new releases. +# It performs the following actions: +# +# 1. Identifies the latest version number in the CHANGELOG (following semantic versioning). +# 2. Increments the patch segment of the version number for the new release. +# 3. Retrieves the current Git commit hash and truncates it for brevity. +# 4. Updates the CHANGELOG.md file: +# - Adds a new entry for the upcoming release using the incremented version number. +# - Updates the link for the [Unreleased] section to point from the current commit to HEAD. +# +# Usage: +# It should be run in the root directory of the repository where the CHANGELOG.md is located. +# Ensure that you have the necessary permissions to commit and push changes to the repository. + +# Step 1: Capture the latest version +latest_version=$(grep -oP '\[\K[0-9]+\.[0-9]+\.[0-9]+(?=\])' CHANGELOG.md | head -1) +echo "Latest version: $latest_version" + +# Step 2: Bump the patch version +IFS='.' read -r -a version_parts <<< "$latest_version" +let version_parts[2]+=1 +new_version="${version_parts[0]}.${version_parts[1]}.${version_parts[2]}" +echo "New version: $new_version" + +# Step 3: Capture the current git commit and truncate it to the first 9 characters +current_commit=$(git rev-parse HEAD | cut -c 1-9) +echo "Current commit: $current_commit" + +# Step 4: Update the CHANGELOG +sed -i "s/\[Unreleased\](.*\.\.\.HEAD)/\[Unreleased\](https:\/\/github.com\/o1-labs\/o1js\/compare\/$current_commit...HEAD)\n\n## \[$new_version\](https:\/\/github.com\/o1-labs\/o1js\/compare\/1ad7333e9e...$current_commit)/" CHANGELOG.md