diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md index cd9ba03d30..d08a150e51 100644 --- a/.github/pull_request_template.md +++ b/.github/pull_request_template.md @@ -2,8 +2,6 @@ Closes # . ### Pre-review checklist -- [ ] All code has been formatted using our config (`make format` for Purescript, `nixpkgs-fmt` for Nix) -- [ ] All Purescript imports are explicit, including constructors -- [ ] **All** existing examples have been run locally against a fully-synced testnet node +- [ ] All code has been formatted using our config (`make format`) - [ ] The integration and unit tests have been run (`npm run test`) locally -- [ ] The changelog has been updated under the `## Unreleased` header, using the appropriate sub-headings (`### Added`, `### Removed`, `### Fixed`) +- [ ] The changelog has been updated under the `## Unreleased` header, using the appropriate sub-headings (`### Added`, `### Removed`, `### Fixed`), and the links to the appropriate issues/PRs have been included diff --git a/.github/workflows/pages.yml b/.github/workflows/pages.yml new file mode 100644 index 0000000000..c69b3068dd --- /dev/null +++ b/.github/workflows/pages.yml @@ -0,0 +1,25 @@ +name: Build and Deploy docs to Github Pages +on: + push: + branches: + - develop +permissions: + contents: write +jobs: + build-and-deploy: + concurrency: ci-${{ github.ref }} # Recommended if you intend to make multiple deployments in quick succession. + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v3 + + - name: Install and Build + run: | + npm install -g spago + npm install -g purescript@0.14.5 + spago docs + - name: Deploy + uses: JamesIves/github-pages-deploy-action@v4.3.3 + with: + branch: gh-pages + folder: generated-docs/html diff --git a/.gitignore b/.gitignore index a6cdd1f915..10e2e6cc1b 100644 --- a/.gitignore +++ b/.gitignore @@ -22,3 +22,5 @@ result-* output.js .DS_Store .idea/ +test-data/chrome-user-data/ +tmp diff --git a/CHANGELOG.md b/CHANGELOG.md index 7ff97065a8..467a405fab 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -25,23 +25,70 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) ### Added +- Plutip integration to run `Contract`s in local, private testnets ([#470](https://github.com/Plutonomicon/cardano-transaction-lib/pull/470)) +- Ability to run `Contract`s in Plutip environment in parallel - `Contract.Test.Plutip.withPlutipContractEnv` ([#800](https://github.com/Plutonomicon/cardano-transaction-lib/issues/800)) +- `withKeyWallet` utility that allows to simulate multiple actors in Plutip environment ([#663](https://github.com/Plutonomicon/cardano-transaction-lib/issues/663)) - `Alt` and `Plus` instances for `Contract`. - `Contract.Utxos.getUtxo` call to get a single utxo at a given output reference -- `purescriptProject`'s `shell` parameter now accepts `packageLockOnly`, which if set to true will stop npm from generating node\_modules. This is enabled for CTL developers +- `Contract.Monad.withContractEnv` function that constructs and finalizes a contract environment that is usable inside a bracket callback. **This is the intended way to run multiple contracts**. ([#731](https://github.com/Plutonomicon/cardano-transaction-lib/pull/731)) +- `Contract.Monad.stopContractEnv` function to finalize a contract environment (close the `WebSockets`). It should be used together with `mkContractEnv`, and is not needed with `withContractEnv`. ([#731](https://github.com/Plutonomicon/cardano-transaction-lib/pull/731)) +- `Contract.Config` module that contains everything needed to create and manipulate `ConfigParams`, as well as a number of `ConfigParams` fixtures for common use cases. ([#731](https://github.com/Plutonomicon/cardano-transaction-lib/pull/731)) +- `Contract.Monad.askConfig` and `Contract.Monad.asksConfig` functions to access user-defined configurations. ([#731](https://github.com/Plutonomicon/cardano-transaction-lib/pull/731)) +- `Contract.Config.WalletSpec` type that allows to define wallet parameters declaratively in `ConfigParams`, instead of initializing wallet and setting it to a `ContractConfig` ([#731](https://github.com/Plutonomicon/cardano-transaction-lib/pull/731)) +- Faster initialization of `Contract` runtime due to parallelism. ([#731](https://github.com/Plutonomicon/cardano-transaction-lib/pull/731)) +- `purescriptProject`'s `shell` parameter now accepts `packageLockOnly`, which if set to true will stop npm from generating `node_modules`. This is enabled for CTL developers - `Contract.Transaction.awaitTxConfirmed` and `Contract.Transaction.awaitTxConfirmedWithTimeout` - `Contract.TextEnvelope.textEnvelopeBytes` and family to decode the `TextEnvelope` format, a common format output by tools like `cardano-cli` to serialize values such as cryptographical keys and on-chain scripts - `Contract.Wallet.isNamiAvailable` and `Contract.Wallet.isGeroAvailable` functions ([#558](https://github.com/Plutonomicon/cardano-transaction-lib/issues/558)]) +- `Contract.Transaction.balanceTxWithOwnAddress` and `Contract.Transaction.balanceTxsWithOwnAddress` to override an `Address` used in `balanceTx` internally ([#775](https://github.com/Plutonomicon/cardano-transaction-lib/pull/775)) +- `Contract.Transaction.awaitTxConfirmedWithTimeoutSlots` waits a specified number of slots for a transaction to succeed. ([#790](https://github.com/Plutonomicon/cardano-transaction-lib/pull/790)) +- `Contract.Transaction.submitE` like submit but uses an `Either (Array Aeson) TransactionHash` to handle a SubmitFail response from ogmios +- `Contract.Chain.waitNSlots`, `Contract.Chain.currentSlot` and `Contract.Chain.currentTime` a function to wait at least `N` number of slots and functions to get the current time in `Slot` or `POSIXTime`. ([#740](https://github.com/Plutonomicon/cardano-transaction-lib/issues/740)) +- `Contract.Transaction.getTxByHash` to retrieve contents of an on-chain transaction. +- `project.launchSearchablePursDocs` to create an `apps` output for serving Pursuit documentation locally ([#816](https://github.com/Plutonomicon/cardano-transaction-lib/issues/816)) +- `Contract.PlutusData.IsData` type class (`ToData` + `FromData`) ([#809](https://github.com/Plutonomicon/cardano-transaction-lib/pull/809)) +- A check for port availability before Plutip runtime initialization attempt ([#837](https://github.com/Plutonomicon/cardano-transaction-lib/issues/837)) +- `Contract.Address.addressToBech32` and `Contract.Address.addressWithNetworkTagToBech32` ([#846](https://github.com/Plutonomicon/cardano-transaction-lib/issues/846)) +- `doc/e2e-testing.md` describes the process of E2E testing. ([#814](https://github.com/Plutonomicon/cardano-transaction-lib/pull/814)) +- Added unzip to the `devShell`. New `purescriptProject.shell` flag `withChromium` also optionally adds Chromium to the `devShell` ([#799](https://github.com/Plutonomicon/cardano-transaction-lib/pull/799)) ### Changed +- `runContract` now accepts `ConfigParams` instead of `ContractConfig` ([#731](https://github.com/Plutonomicon/cardano-transaction-lib/pull/731)) +- `mkContractConfig` has been renamed to `mkContractEnv`. Users are advised to use `withContractEnv` instead to ensure proper finalization of WebSocket connections. ([#731](https://github.com/Plutonomicon/cardano-transaction-lib/pull/731)) - CTL's `overlay` no longer requires an explicitly passed `system` - Switched to CSL for utxo min ada value calculation ([#715](https://github.com/Plutonomicon/cardano-transaction-lib/pull/715)) -- ServerConfig accepts a url `path` field +- `ConfigParams` is now a type synonym instead of a newtype. `ContractConfig` has been renamed to `ContractEnv`. +- Moved logging functions to `Contract.Log` from `Contract.Monad` ([#727](https://github.com/Plutonomicon/cardano-transaction-lib/issues/727) +- Renamed `Contract.Wallet.mkKeyWalletFromPrivateKey` to `Contract.Wallet.mkKeyWalletFromPrivateKeys`. +- ServerConfig accepts a url `path` field ([#728](https://github.com/Plutonomicon/cardano-transaction-lib/issues/728)). +- Examples now wait for transactions to be confirmed and log success ([#739](https://github.com/Plutonomicon/cardano-transaction-lib/issues/739)). +- Updated CSL version to v11.0.0 ([#801](https://github.com/Plutonomicon/cardano-transaction-lib/issues/801)) +- Better error message when attempting to initialize a wallet in NodeJS environment ([#778](https://github.com/Plutonomicon/cardano-transaction-lib/issues/778)) +- The [`ctl-scaffold`](https://github.com/mlabs-haskell/ctl-scaffold) repository has been archived and deprecated and its contents moved to `templates.ctl-scaffold` in the CTL flake ([#760](https://github.com/Plutonomicon/cardano-transaction-lib/issues/760)). +- The CTL `overlay` output has been deprecated and replaced by `overlays.purescript` and `overlays.runtime` ([#796](https://github.com/Plutonomicon/cardano-transaction-lib/issues/796)). +- `buildCtlRuntime` and `launchCtlRuntime` now take an `extraServices` argument to add `docker-compose` services to the resulting Arion expression ([#769](https://github.com/Plutonomicon/cardano-transaction-lib/issues/769)). -### Fixed +### Removed + +- `Contract.Monad.traceTestnetContractConfig` - use `Contract.Config.testnetNamiConfig` instead. +- `runContract_` - use `void <<< runContract`. + +### Fixed +- Endless `awaitTxConfirmed` calls ([#804](https://github.com/Plutonomicon/cardano-transaction-lib/issues/804)) - Bug with collateral selection: only the first UTxO provided by wallet was included as collateral [(#723)](https://github.com/Plutonomicon/cardano-transaction-lib/issues/723) - Bug with collateral selection for `KeyWallet` when signing multiple transactions ([#709](https://github.com/Plutonomicon/cardano-transaction-lib/pull/709)) +- Properly implemented CIP-25 V2 metadata. Now there's no need to split arbitrary-length strings manually to fit them in 64 PlutusData bytes (CTL handles that). A new `Cip25String` type has been introduced (a smart constructor ensures that byte representation fits 64 bytes, as required by the spec). Additionally, a new `Metadata.Cip25.Common.Cip25TokenName` wrapper over `TokenName` is added to ensure proper encoding of `asset_name`s. There are still some minor differences from the spec: + +-- We do not split strings in pieces when encoding to JSON +-- We require a `"version": 2` tag. +-- `policy_id` must be 28 bytes +-- `asset_name` is up to 32 bytes. + +See https://github.com/cardano-foundation/CIPs/issues/303 for motivation +- `ogmios-datum-cache` now works on `x86_64-darwin` +- `TypedValidator` interface ([#808](https://github.com/Plutonomicon/cardano-transaction-lib/issues/808)) ## [2.0.0-alpha] - 2022-07-05 @@ -49,9 +96,9 @@ This release adds support for running CTL contracts against Babbage-era nodes. * ### Added +- Support for using a `PrivateKey` as a `Wallet`. - `mkKeyWalletFromFile` helper to use `cardano-cli`-style `skey`s - Single `Plutus.Conversion` module exposing all `(Type <-> Plutus Type)` conversion functions ([#464](https://github.com/Plutonomicon/cardano-transaction-lib/pull/464)) -- Support for using a `PrivateKey` as a `Wallet` - `logAeson` family of functions to be able to log JSON representations - `EncodeAeson` instances for most types under `Cardano.Types.*` as well as other useful types (`Value`, `Coin`, etc.) - `getProtocolParameters` call to retrieve current protocol parameters from Ogmios ([#541](https://github.com/Plutonomicon/cardano-transaction-lib/issues/541)) diff --git a/Makefile b/Makefile index 5c4f037b92..ea1628d977 100644 --- a/Makefile +++ b/Makefile @@ -1,17 +1,48 @@ SHELL := bash .ONESHELL: .PHONY: run-dev run-build check-format format run-datum-cache-postgres-console - query-testnet-tip clean check-explicit-exports + query-testnet-tip clean check-explicit-exports e2e-test .SHELLFLAGS := -eu -o pipefail -c -ps-sources := $(shell fd -epurs) -nix-sources := $(shell fd -enix --exclude='spago*') -hs-sources := $(shell fd . './server/src' './server/exe' -ehs) -js-sources := $(shell fd -ejs) -ps-entrypoint := Examples.AlwaysSucceeds -ps-bundle = spago bundle-module -m ${ps-entrypoint} --to output.js +# find a suitable browser for e2e-tests +define e2e-browser + $(shell which chromium google-chrome | head -n1) +endef + +# find a suitable temp directory for e2e-tests. snaps don't work +# in $TMPDIR, because of lacking read access! +define e2e-temp-base + $(if $(findstring snap,$(call e2e-browser)),./tmp,$TMPDIR) +endef +ps-sources := $(shell fd -epurs -Etmp) +nix-sources := $(shell fd -enix --exclude='spago*' -Etmp) +hs-sources := $(shell fd . './server/src' './server/exe' -ehs -Etmp) +js-sources := $(shell fd -ejs -Etmp) +ps-entrypoint := Examples.ByUrl +ps-bundle = spago bundle-module -m ${ps-entrypoint} --to output.js node-ipc = $(shell docker volume inspect cardano-transaction-lib_node-ipc | jq -r '.[0].Mountpoint') +e2e-temp-dir := $(strip $(call e2e-temp-base))/$(shell mktemp -du e2e.XXXXXXX) +e2e-test-chrome-dir := test-data/chrome-user-data + +# bump version here +e2e-test-nami := test-data/chrome-extensions/nami_3.2.5_1.crx +e2e-test-nami-settings := test-data/nami_settings.tar.gz + +# bump version here +e2e-test-gero := test-data/chrome-extensions/gero_testnet_1.10.0_0.crx +e2e-test-gero-settings := test-data/gero_settings.tar.gz + +# https://stackoverflow.com/questions/2214575/passing-arguments-to-make-run +# example: make e2e-test -- --no-headless +# +# If the first argument is "e2e-test,"... +ifeq (e2e-test,$(firstword $(MAKECMDGOALS))) + # use the rest as arguments for "e2e-test" + TEST_ARGS := $(wordlist 2,$(words $(MAKECMDGOALS)),$(MAKECMDGOALS)) + # ...and turn them into do-nothing targets + $(eval $(TEST_ARGS):;@:) +endif run-dev: @${ps-bundle} && BROWSER_RUNTIME=1 webpack-dev-server --progress @@ -24,13 +55,49 @@ check-explicit-exports: (echo "Use explicit exports:" && \ grep -rn '(\.\.)' ./src ./test ./examples) -check-format: +check-format: check-explicit-exports @purs-tidy check ${ps-sources} nixpkgs-fmt --check ${nix-sources} fourmolu -m check -o -XTypeApplications -o -XImportQualifiedPost ${hs-sources} prettier -c ${js-sources} eslint ${js-sources} +e2e-test: + @mkdir -p ${e2e-temp-dir} + @unzip ${e2e-test-nami} -d ${e2e-temp-dir}/nami > /dev/zero \ + || echo "ignore warnings" # or make stops + @tar xzf ${e2e-test-nami-settings} + @unzip ${e2e-test-gero} -d ${e2e-temp-dir}/gero > /dev/zero \ + || echo "ignore warnings" # or make stops + @tar xzf ${e2e-test-gero-settings} + @rm -f ${e2e-test-chrome-dir}/SingletonLock + @spago test --main Test.E2E -a "E2ETest --nami-dir ${e2e-temp-dir}/nami --gero-dir ${e2e-temp-dir}/gero $(TEST_ARGS) --chrome-exe $(call e2e-browser)" || rm -Rf ${e2e-temp-dir} + +e2e-run-browser-nami: + @mkdir -p ${e2e-temp-dir} + @unzip ${e2e-test-nami} -d ${e2e-temp-dir}/nami > /dev/zero \ + || echo "ignore warnings" # or make stops + @tar xzf ${e2e-test-nami-settings} + @$(call e2e-browser) --load-extension=${e2e-temp-dir}/nami --user-data-dir=${e2e-test-chrome-dir} || rm -Rf ${e2e-temp-dir} + +e2e-run-browser-gero: + @mkdir -p ${e2e-temp-dir} + @unzip ${e2e-test-gero} -d ${e2e-temp-dir}/gero > /dev/zero \ + || echo "ignore warnings" # or make stops + @tar xzf ${e2e-test-gero-settings} + echo $(call e2e-browser) --load-extension=${e2e-temp-dir}/gero --user-data-dir=${e2e-test-chrome-dir} || rm -Rf ${e2e-temp-dir} + $(call e2e-browser) --load-extension=${e2e-temp-dir}/gero --user-data-dir=${e2e-test-chrome-dir} || rm -Rf ${e2e-temp-dir} + +# extract current nami settings from e2e-test-chrome-dir and store them for git +nami-settings: + tar czf ${e2e-test-nami-settings} ${e2e-test-chrome-dir}/Default/Local\ Extension\ Settings/lpfcbjknijpeeillifnkikgncikgfhdo/* + +# extract current gero settings from e2e-test-chrome-dir and store them for git +gero-settings: + tar czf ${e2e-test-gero-settings} \ + ${e2e-test-chrome-dir}/Default/IndexedDB/chrome-extension_iifeegfcfhlhhnilhfoeihllenamcfgc_0.indexeddb.leveldb \ + ${e2e-test-chrome-dir}/Default/Extension\ State + format: @purs-tidy format-in-place ${ps-sources} nixpkgs-fmt ${nix-sources} @@ -52,3 +119,6 @@ clean: @ rm -rf .spago2nix || true @ rm -rf node_modules || true @ rm -rf output || true + +tgt: + @echo $(call e2e-temp-base,$(call e2e-browser)) diff --git a/README.md b/README.md index 690683ca55..a8fd792de7 100644 --- a/README.md +++ b/README.md @@ -25,7 +25,7 @@ - [x] **Stage 3** Once we have a simple working transaction, we will seek to build a Plutus smart contract transaction with datum from scratch - [x] **Stage 4** Once we can construct Plutus smart contract transactions, we will seek to build a library/DSL/interface such that transactions can be built using constraints and lookups - as close as possible to a cut-and-paste solution from Plutus' `Contract` monad code in Haskell (but with no guarantee that code changes are not necessary) - [ ] **Stage 4.1** Investigate supporting compatibility with the Vasil hardfork and improvements to our initial `Contract` API (**In progress**) -- [ ] **Stage 5** Once we have a basic `Contract`-style API, we will further refine its public interface, expand wallet support (see [below](#light-wallet-support)), expose a test interface, provide a more ergonomic JS/TS API, support stake validators, and support CIP workflows on the public testnet +- [ ] **Stage 5** Once we have a basic `Contract`-style API, we will further refine its public interface, expand wallet support (see [below](#light-wallet-support)), expose a test interface (see [here](doc/plutip-testing.md)), provide a more ergonomic JS/TS API, support stake validators, and support CIP workflows on the public testnet - [ ] **Stage 6** Once CTL's `Contract` interface has been stabilized, we will add support for even more wallets and attempt to deprecate CTL's currently required Haskell server ### Light wallet support @@ -44,13 +44,15 @@ Support is planned for the following light wallets: Please explore our documentation to discover how to use CTL, how to set up its runtime, and how it compares to Plutus/PAB: +- [Super quick start](./doc/getting-started.md#setting-up-a-new-project) - [Migrating from Plutus to CTL](./doc/plutus-comparison.md) - [Adding CTL as a dependency](./doc/ctl-as-dependency.md) - [Getting started writing CTL contracts](./doc/getting-started.md) - [CTL's runtime dependencies](./doc/runtime.md) - [Developing on CTL](./doc/development.md) +- [Testing contracts with Plutip](./doc/plutip-testing.md) -You can also [generate Purescript documentation for CTL and its dependencies](./doc/development.md#generating-ps-documentation). +You can also access [PureScript documentation for CTL and its dependencies](https://plutonomicon.github.io/cardano-transaction-lib/) for the most recent `develop` version, or [generate it yourself](./doc/development.md#generating-ps-documentation). ## Architecture @@ -82,5 +84,6 @@ CTL is directly inspired by the Plutus Application Backend (PAB). Unlike PAB, ho ## Available support channels info You can find help, more information and ongoing discusion about the project here: + - Plutonomicon Discord: https://discord.gg/c8kZWxzJ - #ctl channel at MLabs' Slack diff --git a/doc/ctl-as-dependency.md b/doc/ctl-as-dependency.md index 20a52bb0aa..0180b8bb35 100644 --- a/doc/ctl-as-dependency.md +++ b/doc/ctl-as-dependency.md @@ -5,226 +5,72 @@ CTL can be imported as an additional dependency into a Purescript project built **Table of Contents** - [Caveats](#caveats) -- [Using the CTL overlay](#using-the-ctl-overlay) +- [Using CTL's overlays](#using-ctls-overlays) - [Upgrading CTL](#upgrading-ctl) ## Caveats The following caveats alway applies when using CTL from your project: -1. Only bundling with Webpack is supported (this is due to our internal dependency on `cardano-serialization-lib` which uses WASM; only Webpack is reliably capable of bundling this properly) +1. Only bundling with Webpack is supported (this is due to our internal dependency on `cardano-serialization-lib` which uses WASM with top-level `await`; only Webpack is reliably capable of bundling this properly) 2. The environment variable `BROWSER_RUNTIME` determines which version of `cardano-serialization-lib` is loaded by CTL, so you must use it as well (i.e. set it to `1` for the browser; leave it unset for NodeJS) -## Using the CTL overlay - -CTL exposes an `overlay` from its flake. You can use this in the Nix setup of your own project to use the same setup as we do, e.g. the same packages and PS builders. Here is an example `flake.nix` that takes CTL as a dependency: - -```nix -{ - inputs = { - cardano-transaction-lib = { - type = "github"; - owner = "Plutonomicon"; - repo = "cardano-transaction-lib"; - # NOTE - # This should match the same revision as the one in your `packages.dhall` to ensure - # the greatest compatibility - rev = "a5539ab89853393b11f6893485a89251f51d17ef"; - }; - - # To use the same version of `nixpkgs` as we do - nixpkgs.follows = "cardano-transaction-lib/nixpkgs"; - }; - - outputs = { self, cardano-transaction-lib, ... }: - # some boilerplate - let - perSystem = nixpkgs.lib.genAttrs [ - "x86_64-linux" - "x86_64-darwin" - "aarch64-linux" - "aarch64-darwin" - ]; - # generate `pkgs` with the CTL overlay applied. This gives you access to - # various additional packages, using the same versions of CTL, including: - # - all of `easy-purescript-nix` - # - Ogmios and `ogmios-datum-cache` - # - `cardano-cli` - nixpkgsFor = system: import nixpkgs { - inherit system; - overlays = [ cardano-transaction-lib.overlay ]; - }; - - # The overlay also include several tools for generating a PS project - # using the same approach as CTL, under `purescriptProject` - psProjectFor = system: - let - # Will be used to generate derivation names - projectName = "your-project"; - - pkgs = nixpkgsFor system; - # This is the root of the project. Typically, this would be `self` - # for flakes-based projects - # - # You may also want to filter this to avoid bloat or unecessary - # rebuilds when copying the source into derivations. Make sure to - # use `builtins.path` with an explicit `name`, as `builtins.filterSource` - # will still trigger rebuilds when using flakes, even when only a - # filtered path is modified - src = builtins.path { - path = self; - name = "${projectName}-src"; - filter = path: ftype: - # filter out certain files, e.g. markdown - !(pkgs.lib.hasSuffix ".md" path) - # or entire directories - && !(ftype == "directory" && builtins.elem - (baseNameOf path) [ "doc" ] - ); - }; - in - # `purescriptProject` takes the `src` argument and compiles all of the - # Purescipt source files contained within. There is no way to define - # individual components using spago, so all of the `sources` defined - # in your `spago.dhall` will always be compiled. Then this compiled - # project is used in `bundlePursProject` and `runPursTest`, examples - # of which are shown below - pkgs.purescriptProject { - inherit pkgs src projectName; - - # These paths can usually be easily derived from the `src` argument - # above. However, it is *strongly* recommended to pass them in - # explicitly, as it helps avoid rebuilding the generated `node_modules` - # for your project - packageJson = ./package.json; - packageLock = ./package-lock.json; - - # Optional arg to override the version of `nodejs` used, defaulting to - # the version used by CTL itself. This will be used throughout - # `purescriptProject` - # - # Note that the version of `purs` is not configurable, as CTL - # will currently break with any other version (it uses 0.14.5 - # internally) - nodejs = pkgs.nodejs-14_x; - - # Also optional; the path to the packages generated by `spago2nix`, - # defaulting to: - spagoPackages = "${src}/spago-packages.nix"; - - # If warnings generated from compilation fo project source files will - # trigger a build error - strictComp = true; - - # Warnings from `purs` to silence during compilation - censorCodes = [ "UserDefinedWarning" ]; - - # The optional `shell` lets you configure the `devShell` that is - # generated by `purescriptProject` - # - # All of the attrs below are entirely optional (shown here with - # their default values) - shell = { - # Extra packages to include in the shell environment. By default - # a common version of `nodejs`, `purs`, `spago`, and more are - # included - packages = [ ]; - - # This will be appended to the `shellHook` that runs. By default, - # the `shellHook` loads generated `node_modules` and exports a - # modified `NODE_PATH` and `PATH` - shellHook = ""; - - # The same as `pkgs.mkShell.inputsFrom` - inputsFrom = [ ]; - - # Which formatter to be made available, `purty` is another option - formatter = "purs-tidy"; - - # If `purescript-language-server` should be included in the shell - pursls = true; - - # If the `node_modules` folder should not be built when `npm` is - # used inside the shell - packageLockOnly = false; - }; - }; - - in - { - packages = perSystem (system: { - # `bundlePursProject` creates a JS bundle with webpack - your-project-bundle = (psProjectFor system).bundlePursProject { - # All of the following are optional and show with default values: - # - # The main Purscript module entrypoint - main = "Main"; - # The JS entrypoint (must correspond to the one listed in the - # webpack config), relative to the `src` - entrypoint = "index.js"; - # The HTML template to render the bundle to (must correspond to - # the template listed in the webpack config) - htmlTemplate = "index.html"; - # If this should be bundled for the browser - browserRuntime = true; - # The path to the webpack config to use - webpackConfig = "webpack.config.js"; - # The module that `spago bundle-module` should write to (must - # match the one that is imported in your JS entrypoint). Is - # relative to the `src` argument provided to `purescriptProject` - bundledModuleName = "output.js"; - }; - }); - - checks = perSystem (system: { - # Build and run a test, also useful for CI - your-project = (psProjectFor system).runPursTest { - # Optional arg, the default value is: - testMain = "Test.Main"; - }; - }); - - # This corresponds to the `shell` argument given above - devShell = perSystem (system: (psProjectFor system).devShell); - }; -} -``` - -We have recenly set up a small scaffolding repository for projects wishing to adopt CTL: https://github.com/mlabs-haskell/ctl-scaffold. More documentation and resources will be added soon to the repo +## Using CTL's overlays + +CTL exposes two `overlay`s from its flake. You can use these in the Nix setup of your own project to use the same setup as we do, e.g. the same packages and PS builders: + +- `overlays.purescript` contains Purescript builders to compile Purescript sources, build bundles with Webpack (`bundlePursProject`), run unit tests using NodeJS (`runPursTest`), run CTL contracts on a private testnet using Plutip (`runPlutipTest`), or build Pursuit documentation (`buildSearchablePursDocs` and `launchSearchablePursDocs`) +- `overlays.runtime` contains various packages and other tools used in CTL's runtime, including `ogmios`, `ogmios-datum-cache`, `plutip-server`, and our own `ctl-server`. It also defines `buildCtlRuntime` and `launchCtlRuntime` to help you quickly launch all runtime services (see the [runtime docs](./runtime.md)) + +We've split the overlays into two components to allow users to more easily choose which parts of CTL's Nix infrastructure they would like to directly consume. For example, some users do not require a pre-packaged runtime and would prefer to build it themselves with more control over its components (e.g. by directly using `ogmios` from their own `inputs`). Such users might still like to use our `purescript` overlay -- splitting the `overlays` allows us to support this. `overlays.runtime` also contains several haskell.nix packages which may cause issues with `hackage.nix` versions in your own project. + +Do note that `runPlutipTest` in `overlays.purescript` requires the presence of all of our runtime components. If you choose not to consume `overlays.runtime`, please ensure that your package set contains these (e.g. by adding them to your own `overlays` when instantiating `nixpkgs`). You can find a complete list of the required runtime services [here](./plutip-testing.md#architecture). + +To see an example project that uses both `overlays`, please refer to our [scaffolding template](../templates/ctl-scaffold/flake.nix). You can also use this template to conveniently initialize a new CTL-based project (`nix flake init -t github:Plutonomicon/cardano-transaction-lib` in a new directory). ## Upgrading CTL -Unfortunately, the process of upgrading CTL is fairly involved. This is in part due to the complexity of the project and in part due to features inherent to Spago's approach to dependency management. The following assumes that you are using a project based on Nix flakes and using our overlay as outlined above. +Unfortunately, the process of upgrading CTL is fairly involved. This is in part due to the complexity of the project and in part due to features inherent to Spago's approach to dependency management. The following assumes that you are using a project based on Nix flakes and using our overlays as outlined above. Make sure to perform **all** of the following steps, otherwise you **will** encounter difficult-to-debug issues: 1. **Update your flake input** - - Update the `rev` you're using for CTL in your flake `inputs` - - **Note**: Nix might throw an error about CTL following a "non-existent input" after doing this. The best way to solve this is to upgrade the version of Nix that you're using. Otherwise, `nix flake lock --update-input `, where `NAME` is the one you're using for CTL in your `inputs`, should solve this + +- Update the `rev` you're using for CTL in your flake `inputs` + - **Note**: Nix might throw an error about CTL following a "non-existent input" after doing this. The best way to solve this is to upgrade the version of Nix that you're using. Otherwise, `nix flake lock --update-input `, where `NAME` corresponds to CTL in your flake's `inputs`, should solve this + 2. **Update your Purescript dependencies** - - Update the CTL `version` in your `packages.dhall`. Make sure that this is the exact same revision as in your flake inputs - - Possibly update the `dependencies` section for CTL in your `packages.dhall` - - You can find a list of CTL's dependencies in our own `spago.dhall` (but make sure to check at the correct revision) - - You might also need to add new transitive git dependencies if CTL has added any to its own direct dependencies (i.e. you need to copy the matching stanzas from CTL's `packages.dhall` to your own; these are contained in the `additions` record in CTL's `packages.dhall`) - - For example, if the following package `foo` is added to CTL's `additions` (in `packages.dhall`) between your old revision and the one you're upgrading to: - ```dhall - - let additions = - { foo = - { dependencies = - [ "bar" - , "baz" - ] - } - , repo = "https://github.com/quux/foo.git" - , version = "0000000000000000000000000000000000000000" - -- ... + +- Update the CTL `version` in your `packages.dhall`. Make sure that this is the exact same revision as in your flake inputs +- Possibly update the `dependencies` section for CTL in your `packages.dhall` + + - You can find a list of CTL's dependencies in our own `spago.dhall` (but make sure to check at the correct revision) + - You might also need to add new transitive git dependencies if CTL has added any to its own direct dependencies (i.e. you need to copy the matching stanzas from CTL's `packages.dhall` to your own; these are contained in the `additions` record in CTL's `packages.dhall`) + + - For example, if the following package `foo` is added to CTL's `additions` (in `packages.dhall`) between your old revision and the one you're upgrading to: + + ```dhall + + let additions = + { foo = + { dependencies = + [ "bar" + , "baz" + ] } - ``` - You also need to add the same package, identically, to your own `packages.dhall`, otherwise the compiler will not be able to find it - - Run `spago2nix generate` and make sure to stage and commit the resulting `spago-packages.nix` if it has changed + , repo = "https://github.com/quux/foo.git" + , version = "0000000000000000000000000000000000000000" + -- ... + } + ``` + + You also need to add the same package, identically, to your own `packages.dhall`, otherwise the compiler will not be able to find it + +- Run `spago2nix generate` and make sure to stage and commit the resulting `spago-packages.nix` if it has changed + 3. **Update your JS dependencies** - - If CTL has added any JS dependencies, these will also need to be added to your own `package.json` - - Similarly, if any of CTL's JS dependencies have changed versions, you will need to use the **exact** same version in your own `package.json` - - That is, avoid using the `~` or `^` prefixes (e.g use versions like `"1.6.51"` instead of `"^1.6.51"`) - - If you're using a `package-lock.json` (which is _highly_ recommended), you can update the lockfile with `npm i --package-lock-only` + +- If CTL has added any JS dependencies, these will also need to be added to your own `package.json` +- Similarly, if any of CTL's JS dependencies have changed versions, you will need to use the **exact** same version in your own `package.json` +- That is, avoid using the `~` or `^` prefixes (e.g use versions like `"1.6.51"` instead of `"^1.6.51"`) +- If you're using a `package-lock.json` (which is _highly_ recommended), you can update the lockfile with `npm i --package-lock-only` diff --git a/doc/development.md b/doc/development.md index 19865e68a7..b914bd2d57 100644 --- a/doc/development.md +++ b/doc/development.md @@ -51,7 +51,10 @@ To develop locally, you can use one the CTL flake to launch all required service - `nix build` _or_ - `spago build` - To test the project, currently only supported when running in a NodeJS environment: - - `npm run unit-test` for unit tests, `npm run integration-test` for integration tests, or `npm run test` for both. + - Use `npm run test`, or, if you need to test some specific functionality: + - `npm run unit-test` for unit tests + - `npm run integration-test` for integration tests (requires ctl-runtime running) + - `npm run plutip-test` for Plutip integration tests (does not require ctl-runtime) - `nix build .#checks..ctl-unit-test` will build and run the unit tests (useful for CI) - To run or build/bundle the project for the browser: - `make run-dev` _or_ `npm run dev` will start a Webpack development server at `localhost:4008` @@ -64,6 +67,8 @@ By default, Webpack will build a [small Purescript example](examples/Pkh2Pkh.pur ## Generating PS documentation +CTL PureScript docs are publicly deployed [here](https://plutonomicon.github.io/cardano-transaction-lib/). + - To build the documentation as HTML: - `spago docs` - To build and open the documentation in your browser: @@ -71,7 +76,7 @@ By default, Webpack will build a [small Purescript example](examples/Pkh2Pkh.pur - To build the documentation as Markdown: - `spago docs --format markdown` -The documentation will be generated in the `./generated_docs` folder, which contains an `index.html` which lists all modules by default. At this index is a checkbox to toggle viewing by package, and all the modules defined in our package will be available under `cardano-transaction-lib`. +The documentation will be generated in the `./generated_docs/html` directory, which contains an `index.html` which lists all modules by default. At this index is a checkbox to toggle viewing by package, and all the modules defined in our package will be available under `cardano-transaction-lib`. Alternatively, you can view the documentation with `nix run -L .#docs` and opening `localhost:8080` in your browser. `nix build -L .#docs` will produce a `result` folder containing the documentation. diff --git a/doc/e2e-testing.md b/doc/e2e-testing.md new file mode 100644 index 0000000000..65f34ad72d --- /dev/null +++ b/doc/e2e-testing.md @@ -0,0 +1,133 @@ +# E2E Testing in the Browser + +CTL has basic machinery for E2E testing in the browser. This can be used to either run the included examples (in `examples`) or create a custom test suite for E2E testing. + +- [E2E Testing in the Browser](#e2e-testing-in-the-browser) +- [Parts Involved](#parts-involved) +- [How to Run the Included Examples](#how-to-run-the-included-examples) +- [Accepted Command Line Options](#accepted-command-line-options) +- [How Wallets are Used](#how-wallets-are-used) + - [How to Use a Different Version of a Wallet](#how-to-use-a-different-version-of-a-wallet) + - [Where to Find the Installed Extensions](#where-to-find-the-installed-extensions) + - [Re-Package an Extension as a CRX File](#re-package-an-extension-as-a-crx-file) + - [Use a CRX File](#use-a-crx-file) + - [How to Use a Different User Wallet](#how-to-use-a-different-user-wallet) +- [How to Create Your Own Test Suite](#how-to-create-your-own-test-suite) +- [Using a reproducible `chromium` version](#using-a-reproducible-chromium-version) + +## Parts Involved + +[Puppeteer](https://github.com/puppeteer/puppeteer) (driven by [Toppokki](https://github.com/justinwoo/purescript-toppokki)) +is used to drive the tests. Supported browsers are [Chromium](https://www.chromium.org/) and Google Chrome. +The browser can be run headless (default) or headful (useful during test development). + +Any programs that should be tested must be deployed and running on some testserver (e.g. with `make run-dev` for the included examples). + +An executable for concrete tests is also needed. For a working example see `test/E2E.purs`. + +## How to Run the Included Examples + +The process is as follows: + +1. Set `ps-entrypoint` in Makefile to `Examples.ByURL`. +2. run `make run-dev`. +3. In another shell, run `make e2e-test`. +4. Examples will be run headless by default. In case of errors, the browser console will be printed to the console. + +## Accepted Command Line Options + +The provided test suite accepts some options. These can be passed via `make` after an additional double dash `--`, e.g. `make e2e-test -- --no-headless`. For usage examples, see the invocations in the `Makefile`, for a complete explanation, see `src/Contract/Test/E2E/Browser.purs`. + +## How Wallets are Used + +For purposes of testing, there are two parts to using a wallet: providing the right software version and importing a wallet with enough assets and a known password. + +- The software just needs to be unpacked to some directory. This can either be the location where the browser unpacks it, or the result of unpacking a CRX file (see below). +- We provide the wallet data as tarballs which will be unpacked into the chrome profile before a test run. + +### How to Use a Different Version of a Wallet + +Chrome extensions are unpacked to some directory by the browser. From there, they can either be used directly by the tests (which gives no control over upgrades and instead uses always the current version), or they can be repackaged as CRX files. The default setup provides CRX versions which `make e2e-test` automatically unpacks on each test run. + +The default test suite accepts the arguments `--nami-dir` and `--gero-dir` to point to the directories from which the extensions are loaded. (see the Makefile) In order to use the "live" version of an extension, just pass the arguments accordingly, e.g.: + +``` +@spago test --main Test.E2E -a "E2ETest --nami-dir ~/.config/google-chrome/Default/Extensions/lpfcbjknijpeeillifnkikgncikgfhdo/ --gero-dir ~/.config/google-chrome/Default/Extensions/iifeegfcfhlhhnilhfoeihllenamcfgc --chrome-exe google-chome +``` + +### Where to Find the Installed Extensions + +1. Locate your browser profile directory. Commonly used locations include: `~/.config/{google-chrome,chromium}/Default` (where `Default` is the profile name), `~/snap/chromium/common/chromium/Default`. +2. Make sure that inside the profile, your desired extension is unpacked. Nami should be in `Extensions/lpfcbjknijpeeillifnkikgncikgfhdo`, Gero (testnet version) in `Extensions/iifeegfcfhlhhnilhfoeihllenamcfgc`. +3. Add the version as a subdirectory, too. The final path may look like `/home/user/.config/google-chrome/Default/Extensions/iifeegfcfhlhhnilhfoeihllenamcfgc/1.10.9_0` + +### Re-Package an Extension as a CRX File + +1. Make sure your browser is using the desired extension version. +2. Navigate to chrome://extensions/ +3. Click the extension. +4. Switch on "Developer mode" (upper right corner). +5. Click "Pack extension". +6. Paste the extension's directory (see above) into "Extension root directory". You can leave "Private key file" empty. +7. Click "Pack extension". +8. The path of the CRX file is displayed in the browser. + +(See [puppeteer-crx](https://www.npmjs.com/package/puppeteer-crx) for an effort to automate this process.) + +### Use a CRX File + +We use `unzip` to unpack it. However, `unzip` will issue a warning because of extra bytes at the beginning, and will exit with a non-zero code, so the exit code needs to be ignored. (we use `|| echo to achieve that`). + +See the `Makefile` for an example: + +``` +e2e-test-nami := test-data/chrome-extensions/nami_3.2.5_1.crx +unzip ${e2e-test-nami} -d ${e2e-temp-dir}/nami > /dev/zero || echo "ignore warnings" +``` + +`${e2e-temp-dir}/nami` can then be passed to the test suite as nami directory. + +### How to Use a Different User Wallet + +In the test suite, the wallet settings are just unpacked using `tar xzf ${e2e-test-nami-settings}` (see `Makefile`). + +A new settings tarball can be easily created, for example using the `Makefile`: + +1. Adjust `${e2e-test-nami-settings}`, `${e2e-test-gero-settings}` and `${e2e-test-chrome-dir}` to point to where you want to store the settings and to chromes user-profile directory +2. Run `make e2e-run-browser-gero` or `make e2e-run-browser-nami` to fire up the test browser with one of the wallets loaded. Configure your wallet as usual. +3. Run `make nami-settings` or `make gero-settings` to store the settings to a tarball. + +## How to Create Your Own Test Suite + +If you are using CTL as a library, you can and should create your own test suite to test your own contracts. + +1. Take `test/E2E.purs` as inspiration and create your own binary. You will find the necessary machinery in `Contract.Test.E2E`. Notable components: + - `withBrowser`: bracket to launch the browser with a specific extension, run something and clos the browser. + - `parseOptions`: Parses command line options, in case you want to use the same as our example suite. + - `publishTestFeedback`, `resetTestFeedback`, `retrieveTestFeedback`: Can be used to communicate success or failure from a contract to the tests. + - `geroConfirmAccess`, `geroSign`, `namiConfirmAccess`, `namiSign`: Confirm a transaction in the browser (i.e. enter the password, click "Sign") + - `withExample`: navigate to a URL, detect the wallet and get ready to run a contract. +2. Fire up your own contracts. +3. Take the `Makefile` as an inspiration, prepare your wallets and run the tests. + +## Using a reproducible `chromium` version + +Although most users will have some version of Chromium or Google Chrome installed system-wide, it can be a good idea to use the same version for all e2e testing. When creating your project's `devShell` using `purescriptProject`, you can set the `shell.withChromium` flag to `true` to include it in the shell's packages. This will be the version of `chromium` present in the `nixpkgs` you pass to create your project: + +```nix +{ + projectFor = system: + let + pkgs = nixpkgsFor system; + in + pkgs.purescriptProject { + inherit pkgs; + projectName = "my-project"; + shell = { + withChromium = true; + # ... + }; + # ... + }; +} +``` diff --git a/doc/faq.md b/doc/faq.md new file mode 100644 index 0000000000..09be5f421a --- /dev/null +++ b/doc/faq.md @@ -0,0 +1,24 @@ +This document lists common problems encountered by CTL users and developers. + +# Bundling-related + +## lib.Something is not a function + +This is probably because npm is used directly. This is something users have reported when using `npm install` instead of having Nix manage the node dependencies (done automatically with `nix develop`, but if you have `node_modules` present in the working directory it will shadow the ones from the Nix store). + +# Time-related + +## Q: Time-related functions behave strangely, what's the reason? + +Local `cardano-node` lags behind the global network time, so when using time conversion functions (`slotToPosixTime`, `posixTimeToSlot`, etc.) users should be aware that the node sees time differently from the OS. +During normal runs, the lag can be somewhere between 0 and 200 seconds. + +To do anything time-related, it's best to rely on local node chain tip time, instead of using `Date.now()` as a source of truth. This is often a requirement when using `mustValidateIn`, because the node will reject the transaction if it appears too early. + +# Q: I am getting `Error: (AtKey "coinsPerUtxoByte" MissingValue)` + +This is because the node hasn't fully synched. The protocol parameter name changed from `coinsPerUtxoWord` to `coinsPerUtxoByte` in Babbage. CTL only supports the latest era, but Ogmios returns different protocol parameters format depending on current era of a local node. + +# Q: Time/slot conversion functions return `Nothing`. Why is that? + +Time/slot conversion functions depend on `eraSummaries` [Ogmios local state query](https://ogmios.dev/mini-protocols/local-state-query/), that returns era bounds and slotting parameters details, required for proper slot arithmetic. The most common source of the problem is that Ogmios does not return enough epochs into the future. diff --git a/doc/getting-started.md b/doc/getting-started.md index 128f6167e1..b9266c5eb3 100644 --- a/doc/getting-started.md +++ b/doc/getting-started.md @@ -5,11 +5,13 @@ This guide will help you get started writing contracts with CTL. Please also see **Table of Contents** - [Prerequisites](#prerequisites) + - [Setting up a new project](#setting-up-a-new-project) + - [Other prerequisites](#other-prerequisites) - [Importing CTL modules](#importing-ctl-modules) - [The `Contract` interface](#the-contract-interface) - [Our `Prelude`](#our-prelude) -- [Executing contracts and the `ContractConfig`](#executing-contracts-and-the-contractconfig) - - [Making the `ContractConfig`](#making-the-contractconfig) +- [Executing contracts and the `ContractEnv`](#executing-contracts-and-the-contractenv) + - [Making the `ContractEnv`](#making-the-contractenv) - [Building and submitting transactions](#building-and-submitting-transactions) - [Awaiting tx confirmation](#awaiting-tx-confirmation) - [Using compiled scripts](#using-compiled-scripts) @@ -19,7 +21,26 @@ This guide will help you get started writing contracts with CTL. Please also see ## Prerequisites -You need to have set up a Purescript project using CTL as a dependency ([more details here](./ctl-as-dependency.md)). You will also need to become familiar with [CTL's runtime](./runtime.md) as its runtime services are required for executing virtually all contracts. +### Setting up a new project + +The easiest way to create a new CTL project is to use our `ctl-scaffold` flake template. This lives in the CTL repo -- you can have a look [here](../templates/ctl-scaffold). It contains a simple, yet complete, flakes-based scaffolding project with example `outputs` for a CTL project, including its runtime. + +A new project can be initialized as follows: + +``` +$ mkdir new-project && cd new-project +$ nix flake init -t github:Plutonomicon/cardano-transaction-lib +$ git init +$ git commit -a -m 'Initial commit' +``` + +**Note**: Nix flakes are just source trees with a `flake.nix` file in them, so initializing a `git` repo as illustrated above is necessary to have a working project. Source files not tracked by a VCS are invisible to Nix when using flakes, so do not skip that (or a similar) step! + +You can learn more about using CTL as a dependency [here](./ctl-as-dependency.md). + +### Other prerequisites + +You will also need to become familiar with [CTL's runtime](./runtime.md) as its runtime services are required for executing virtually all contracts. ## Importing CTL modules @@ -52,7 +73,7 @@ import Contract.Scripts (MintingPolicy) Unlike Haskell, Purescript's `Prelude` is not imported implicitly in every module and is much smaller in scope (for example, common non-primitive types like `Maybe` are contained in their own packages, rather than in the `Prelude`). Rather than require users to import Purescript's `Prelude` and other common modules directly, we offer a `Contract.Prelude` that re-exports Purescript's `Prelude`, several common modules (e.g. `Data.Maybe`, `Data.Either`, etc...), and CTL-specific functionality. **We recommend using `Contract.Prelude` as a replacement for `Prelude` in projects using CTL**, particularly for developers who are less familiar with Purescript and its divergences from Haskell. -## Executing contracts and the `ContractConfig` +## Executing contracts and the `ContractEnv` Unlike [Plutus/PAB](plutus-comparison.md#the-contract-type), CTL is structued internally around a familiar `mtl`-style monad transformer stack. As such, contracts written in CTL are called from other Purescript code (i.e. CTL has no concept of "endpoints" or "activation"). The top-level of your program written in CTL will look like the following: @@ -67,65 +88,59 @@ Internally, CTL uses Purescript's `Aff` monad, which represents asynchronous eff ```purescript main :: Effect Unit -main = Contract.Monad.launchAff_ $ do -- we re-export this for you +main = Contract.Monad.launchAff_ do -- we re-export this for you ... ``` -Within this `Aff` action, you should create a wallet type and initialize your `ContractConfig`: +Then use the eliminator `Contract.Monad.runContract` with a config specifying network and wallet: ```purescript main :: Effect Unit -main = Contract.Monad.launchAff_ $ do - wallet <- Contract.Wallet.mkNamiWalletAff - cfg <- undefined -- see the next section for more details on this part - ... +main = Contract.Monad.launchAff_ do + runContract Contract.Config.testnetNamiConfig do + ... ``` -Then use the eliminator `Contract.Monad.runContract` (or `runContract_` to discard the return value): +### Making the `ContractEnv` -```purescript -main :: Effect Unit -main = Contract.Monad.launchAff_ $ do - wallet <- Contract.Wallet.mkNamiWalletAff - cfg <- undefined - runContract_ cfg $ do - ... -``` +The `ContractEnv` type contains configuration values and websocket connections that are required to execute contracts written in CTL. The users should not construct it directly - `Contract.Config.ConfigParams` should be used instead. -### Making the `ContractConfig` +For local development and testing, we provide `Contract.Config.testnetConfig` where all service hosts are set to `localhost` and the `logLevel` is set to `Trace`. -The `ContractConfig` contains the configuration values and websocket connections that are required to execute contracts written in CTL. For local development and testing, we provide `Contract.Monad.traceContractConfig` where all service hosts are set to `localhost` and the `logLevel` is set to `Trace`. Needless to say, this is not viable for production or staging environments. +It is **not recommended to directly construct or manipulate a `ContractEnv` yourself** as the process of making a new config initializes websockets. Instead, use `Contract.Monad.ConfigParams` with `runContract`. -It is **not recommended to directly construct or manipulate a `ContractConfig` yourself** as the process of making a new config initializes websockets. Instead, use `Contract.Monad.ConfigParams` with `Contract.Monad.mkContractConfig`. +As explained in the [Plutus/PAB comparison](plutus-comparison.md#the-contract-type), the `ContractEnv` environment using Purescript's extensible records. This can also be done via `ConfigParams`, which holds an `extraConfig` field corresponding to the `Row Type` argument to `ContractEnv` (and by extension, `Contract`). -As explained in the [Plutus/PAB comparison](plutus-comparison.md#the-contract-type), the `ContractConfig` environment using Purescript's extensible records. This can also be done via `ConfigParams`, which holds an `extraConfig` field corresponding to the `Row Type` argument to `ContractConfig` (and by extension, `Contract`). +A special `Contract.Config.WalletSpec` type is used to specify which wallet to use during the `Contract` lifetime. -An example of building a `ContractConfig` via `ConfigParams` is as follows: +An example of building a `Contract` via `ConfigParams` is as follows: ```purescript main :: Effect Unit -main = Contract.Monad.launchAff_ $ do -- we re-export this for you - wallet <- Contract.Wallet.mkNamiWalletAff -- for the Nami backend - cfg <- mkContractConfig $ ConfigParams - -- The server defaults below are also exported from - -- `Contract.Monad` - { ogmiosConfig: defaultOgmiosWsConfig - , datumCacheConfig: defaultDatumCacheWsConfig - , ctlServerConfig: defaultServerConfig - , networkId: TestnetId - , logLevel: Trace - , extraConfig: { apiKey: "foo" } - , wallet - } - runContract_ cfg someContractWithApiKeyInEnv +main = Contract.Monad.launchAff_ do -- we re-export this for you + let + (config :: ConfigParams (apiKey :: String)) = + { ogmiosConfig: defaultOgmiosWsConfig + , datumCacheConfig: defaultDatumCacheWsConfig + , ctlServerConfig: defaultServerConfig + , networkId: TestnetId + , logLevel: Trace + , extraConfig: { apiKey: "foo" } + , walletSpec: Just ConnectToNami + , customLogger: Nothing + } + runContract config someContractWithApiKeyInEnv -- As we provided `(apiKey :: String)` to the `extraConfig` above, we can now -- access it in the reader environment of any `Contract` actions call using --- the `ContractConfig` we created above. We can also retain polymorphism --- by adding `| r` to the row type +-- `askConfig`. someContractWithApiKeyInEnv - :: forall (r :: Row Type). Contract (apiKey :: String | r) Unit -someContractWithApiKeyInEnv = ... + :: forall. Contract (apiKey :: String) Unit + -- We can also retain polymorphism by adding `| r` to the row type: + -- :: forall (r :: Row Type). Contract (apiKey :: String | r) Unit +someContractWithApiKeyInEnv = do + { apiKey } <- askConfig + ... ``` When using custom environments (e.g. in production), services can be configured to point to the same port with different paths (a webserver is needed to set that up): @@ -181,7 +196,8 @@ Unlike PAB, CTL obscures less of the build-balance-sign-submit pipeline for tran ... ``` -- Submit using `Contract.Transaction.submit` (note that due to current infelicities in CTL's internal transaction builder, we must currently use the CBOR of the balanced and signed transaction rather than the transaction itself; this will be resolved in an upcoming CTL version): +- Submit using `Contract.Transaction.submit`: + ```purescript contract = do ... @@ -212,7 +228,7 @@ module.exports = { Scripts: path.resolve(__dirname, "fixtures/scripts"), }, }, -} +}; ``` You must also add the following to `module.exports.module.rules`: @@ -229,7 +245,7 @@ module.exports = { // ... ], }, -} +}; ``` This enables inlining your serialized scripts in `.js` files, to then be loaded in Purescript via the FFI: @@ -255,6 +271,25 @@ myContract cfg = runContract_ cfg $ do This way you avoid hardcoding your scripts directly to .purs files which could lead to synchronization issues should your scripts change. +**Note**: The `alias` method above will only work in the browser when bundling with Webpack. In order to load the scripts for both browser and NodeJS environments, you can use the `BROWSER_RUNTIME` environment variable like so: + +```javascript +let script; +if (typeof BROWSER_RUNTIME != "undefined" && BROWSER_RUNTIME) { + script = require("Scripts/my-script.plutus"); +} else { + const fs = require("fs"); + const path = require("path"); + script = fs.readFileSync( + path.resolve(__dirname, "../../fixtures/scripts/my-script.plutus"), + "utf8" + ); +} +exports.myScript = script; +``` + +Note that the relative path passed to `path.resolve` for the NodeJS case starts from the `output` directory that the Purescript compiler produces. + ## Testing ### Without a light wallet @@ -265,7 +300,7 @@ We provide `KeyWallet` to enable testing outside of the browser, or in-browser w $ cardano-cli address key-gen --normal-key --signing-key-file payment.skey --verification-key-file payment.vkey ``` -The signing key can be loaded to CTL using `Contract.Wallet.KeyFile.mkKeyWalletFromFile` See also `examples/Pkh2PkhKeyWallet.purs`. +The signing key can be loaded to CTL using `WalletSpec`'s `UseKeys` constructor. See `examples/Pkh2PkhKeyWallet.purs`. From here you can submit transactions that will be signed with your private key, or perhaps export transactions to be tested with external tools such as [`plutip` testing tool](https://github.com/mlabs-haskell/plutip). We are currently working on integration with the plutip. These will be included in an upcoming release of CTL. diff --git a/doc/plutip-testing.md b/doc/plutip-testing.md new file mode 100644 index 0000000000..64048a8ade --- /dev/null +++ b/doc/plutip-testing.md @@ -0,0 +1,68 @@ +# CTL integration with Plutip + +[Plutip](https://github.com/mlabs-haskell/plutip) is a tool to run private Cardano testnets. CTL provides integration with Plutip via a [`plutip-server` binary](https://github.com/mlabs-haskell/plutip/pull/79) that exposes an HTTP interface to control local Cardano clusters. + +**Table of Contents** + +- [Architecture](#architecture) +- [Testing contracts](#testing-contracts) +- [Limitations](#limitations) + +## Architecture + +CTL depends on a number of binaries in the `$PATH` to execute Plutip tests: + +- `plutip-server` to launch a local `cardano-node` cluster +- [`ogmios`](https://ogmios.dev/) +- [`ogmios-datum-cache`](https://github.com/mlabs-haskell/ogmios-datum-cache) +- PostgreSQL: `initdb`, `createdb` and `psql` for `ogmios-datum-cache` storage +- `ctl-server`: a server-side part of CTL itself. + +All of these are provided by CTL's `overlays.runtime` (and are provided in CTL's own `devShell`). You **must** use the `runtime` overlay or otherwise make the services available in your package set (e.g. by defining them within your own `overlays` when instantiating `nixpkgs`) as `purescriptProject.runPlutipTest` expects all of them. + +The services are NOT run by `docker-compose` as is the case with `launchCtlRuntime`: they are started and stopped on each CTL `Contract` execution by CTL. + +## Testing contracts + +The main entry point to the testing interface is `Contract.Test.Plutip.runPlutipContract` function: + +```purescript +runPlutipContract + :: forall (distr :: Type) (wallets :: Type) (a :: Type) + . UtxoDistribution distr wallets + => PlutipConfig + -> distr + -> (wallets -> Contract () a) + -> Aff a +``` + +`distr` is a specification of how many wallets and with how much funds should be created. It should either be a `unit` (for no wallets) or nested tuples containing `Array BigInt` - each element of the array specifies an UTxO amount in Lovelaces (0.000001 Ada). + +The `wallets` argument is either a `Unit` or a tuple of `KeyWallet`s (with the same nesting level as in `distr`, which is guaranteed by `UtxoDistribution`). + +`wallets` should be pattern-matched on, and its components should be passed to `withKeyWallet`: + +An example `Contract` with two actors: + +```purescript +let + distribution :: Array BigInt /\ Array BigInt + distribution = + [ BigInt.fromInt 1_000_000_000 + , BigInt.fromInt 2_000_000_000 + ] /\ + [ BigInt.fromInt 2_000_000_000 ] +runPlutipContract config distribution \(alice /\ bob) -> do + withKeyWallet alice do + pure unit -- sign, balance, submit, etc. + withKeyWallet bob do + pure unit -- sign, balance, submit, etc. +``` + +In most cases at least two UTxOs per wallet are needed (one of which will be used as collateral, so it should exceed `5_000_000` Lovelace). + +Note that during execution WebSocket connection errors may occur. However, payloads are re-sent after these errors, so you can ignore them. [These errors will be suppressed in the future.](https://github.com/Plutonomicon/cardano-transaction-lib/issues/670). + +## Limitations + +- Plutip does not currently provide staking keys. However, arbitrary staking keys can be used if the application does not depend on staking (because payment keys and stake keys don't have to be connected in any way). It's also possible to omit staking keys in many cases by using `mustPayToPubKey` instead of `mustPayToPubKeyAddress`. diff --git a/doc/plutus-comparison.md b/doc/plutus-comparison.md index ae78651632..7ecc5b1f9c 100644 --- a/doc/plutus-comparison.md +++ b/doc/plutus-comparison.md @@ -91,23 +91,25 @@ class ValidatorTypes (a :: Type) where Purescript lacks most of Haskell's more advanced type-level faculties, including type/data families. Purescript does, however, support functional dependencies, allowing us to encode `ValidatorTypes` as follows: ```purescript +class ValidatorTypes :: Type -> Type -> Type -> Constraint class - ( DatumType a b - , RedeemerType a b + ( DatumType validator datum + , RedeemerType validator redeemer ) <= - ValidatorTypes (a :: Type) (b :: Type) - | a -> b + ValidatorTypes validator datum redeemer -class DatumType (a :: Type) (b :: Type) | a -> b +class DatumType :: Type -> Type -> Constraint +class DatumType validator datum | validator -> datum -class RedeemerType (a :: Type) (b :: Type) | a -> b +class RedeemerType :: Type -> Type -> Constraint +class RedeemerType validator redeemer | validator -> redeemer ``` ### Working with scripts #### Using scripts from the frontend -As noted above, all scripts and various script newtypes (`Validator`, `MintingPolicy`, etc...) must be explicitly passed to CTL. Unlike Plutus, where on- and off-chain code can freely share Haskell values, scripts must be provided to CTL in a serialized format. The easiest way to do this is using `Contract.TextEnvelope.textEnvelope` along with the JS FFI. See the [getting started guide](doc/getting-started#using-compiled-scripts) for more details. +As noted above, all scripts and various script newtypes (`Validator`, `MintingPolicy`, etc...) must be explicitly passed to CTL. Unlike Plutus, where on- and off-chain code can freely share Haskell values, scripts must be provided to CTL in a serialized format. The easiest way to do this is using `Contract.TextEnvelope.textEnvelope` along with the JS FFI. See the [getting started guide](getting-started.md#using-compiled-scripts) for more details. #### Applying arguments to parameterized scripts diff --git a/doc/runtime.md b/doc/runtime.md index 714f0e8b11..ef23b49f5e 100644 --- a/doc/runtime.md +++ b/doc/runtime.md @@ -1,13 +1,15 @@ # CTL's Runtime Dependencies -In order to run CTL's `Contract` effects, several services are required. These can be configured through a `ContractConfig` that holds websocket connections, information about server hosts/ports, and other requisite information. +In order to run CTL's `Contract` effects, several services are required. These can be configured through a `ContractEnv` that holds websocket connections, information about server hosts/ports, and other requisite information. **Table of Contents** - [Current services](#current-services) -- [Using the CTL overlay](#using-the-ctl-overlay) +- [Using CTL's `runtime` overlay](#using-ctls-runtime-overlay) - [Changing network configurations](#changing-network-configurations) - [Other requirements](#other-requirements) + - [With Nami:](#with-nami) + - [With Gero:](#with-gero) ### Current services @@ -24,11 +26,11 @@ The services that are currently required are: - We hope to deprecate this in the future, but we use it at the moment for certain Cardano libraries that have no Purescript analogue - To build the server project, run the following from the repository root: `nix build -L .#ctl-server:exe:ctl-server` -### Using the CTL overlay +### Using CTL's `runtime` overlay -CTL's overlay (contained in its flake `outputs`) provides some mechanisms for conveniently launching all runtime services using [Arion](https://docs.hercules-ci.com/arion)(itself a wrapper around `docker-compose`). To use this, you must have a setup based on Nix flakes (recommended as well for [using CTL as a dependency for Purescript projects](./ctl-as-dependency.md)). +CTL's `overlays.runtime` (contained in its flake `outputs`) provides some mechanisms for conveniently launching all runtime services using [Arion](https://docs.hercules-ci.com/arion) (itself a wrapper around `docker-compose`). To use this, you must have a setup based on Nix flakes (recommended as well for [using CTL as a dependency for Purescript projects](./ctl-as-dependency.md)). -Here is an example that uses the overlay to launch runtime services: +Here is an example that uses the `runtime` overlay to launch all of the required services: ```nix { @@ -45,14 +47,24 @@ Here is an example that uses the overlay to launch runtime services: outputs = { self, cardano-transaction-lib, nixpkgs, ... }: # some boilerplate let - defaultSystems = [ "x86_64-linux" "x86_64-darwin" ]; + defaultSystems = [ + "x86_64-linux" + "x86_64-darwin" + "aarch64-linux" + "aarch64-darwin" + ]; perSystem = nixpkgs.lib.genAttrs defaultSystems; - # generate `pkgs` with the CTL overlay applied. This gives you access to - # various additional packages, using the same versions of CTL, including: + # generate `pkgs` with CTL's overlays applied. This gives you access to + # various additional packages, using the same versions of CTL nixpkgsFor = system: import nixpkgs { inherit system; - overlays = [ cardano-transaction-lib.overlay ]; + overlays = [ + cardano-transaction-lib.overlays.runtime + # you probably want this one too, although it's not demonstrated + # in this example + cardano-transaction-lib.overlays.purescript + ]; }; # The configuration for the CTL runtime, which will be passed to the @@ -63,6 +75,36 @@ Here is an example that uses the overlay to launch runtime services: # that takes a single arugment. Alternatively, you can pass an attrset # directly runtimeConfig = final: with final; { + # You can add new services to the runtime. These should correspond to + # Arion's `service` definition. The default is the empty attribute set + extraServices = { + # an image from dockerhub + foo = { + service = { + image = "bar:foo"; + command = [ + "baz" + "--quux" + ]; + }; + + # Or a Nix-based image + foo2 = { + service = { + useHostStore = true; + command = [ + "${(nixpkgsFor system).baz}/bin/baz" + "--quux" + ]; + }; + }; + }; + }; + # This corresponds to `docker-compose.raw` from Arion. You can add new + # volumes, etc... using this + extraDockerCompose = { volumes = { someVol = { }; }; }; + # This is the default. You can override this to run using different + # configurations: see ./runtime.md#changing-network-configurations network = { name = "testnet"; magic = 1097911063; @@ -71,7 +113,12 @@ Here is an example that uses the overlay to launch runtime services: # values. If you need even more customization, you can use `overideAttrs` # to change the values after calling `buildCtlRuntime` (e.g. a secrets # volume for the `postgres` service) - node = { port = 3001; }; + node = { + port = 3001; + # the version of the node to use, corresponds to the image version tag, + # # i.e. `"inputoutput/cardano-node:${tag}"` + tag = "1.35.2"; + }; ogmios = { port = 1337; }; ctlServer = { port = 8081; }; postgres = { @@ -156,7 +203,7 @@ When changing networks, make sure that `network.magic` is correctly synchronized ### Other requirements -In order to run most `Contract` actions, **you must use Nami wallet** (or Gero, depending on how `ContractConfig` is initialized). The following steps must be taken to ensure that you can run CTL contracts: +In order to run most `Contract` actions in the browser, **you must use Nami or Gero wallet**. The following steps must be taken to ensure that you can run CTL contracts: #### With Nami: diff --git a/doc/side-by-side-ctl-plutus-comparison.md b/doc/side-by-side-ctl-plutus-comparison.md index 144643e17c..99fd4861c3 100644 --- a/doc/side-by-side-ctl-plutus-comparison.md +++ b/doc/side-by-side-ctl-plutus-comparison.md @@ -1,47 +1,51 @@ # Side by side comparison of CTL and Plutus off-chain contracts -We are going to compare two different off-chain contracts -both in Plutus and CTL. +We are going to compare two different off-chain contracts +both in Plutus and CTL. -For this discussion, we need to go through an overview of +For this discussion, we need to go through an overview of both of them. ## About `Contract` in CTL and Plutus -The purpose of `Contract` in CTL is to bring the capabilities of -`QueryMExtended` to the public API. - The current definition of `Contract` in CTL is : ```PureScript -type QueryConfig (r :: Row Type) = - { some_internal_parameters - | r +type QueryConfig = + { -- configuration options used on initialization to construct `QueryRuntime` + } + +type QueryRuntime = + { -- Part of `QueryEnv` that is reusable between contracts (internal type) } -type DefaultQueryConfig = QueryConfig () +-- | `QueryEnv` contains everything needed for `QueryM` to run. +type QueryEnv (r :: Row Type) = + { config :: QueryConfig + , runtime :: QueryRuntime + , extraConfig :: { | r } + } + +type DefaultQueryEnv = QueryEnv () -type QueryM (a :: Type) = ReaderT DefaultQueryConfig (LoggerT Aff) a +type QueryM (a :: Type) = ReaderT DefaultQueryEnv (LoggerT Aff) a -type QueryMExtended (r :: Row Type) (a :: Type) = - ReaderT - (QueryConfig r) - (LoggerT Aff) - a +type QueryMExtended (r :: Row Type) (a :: Type) = ReaderT (QueryEnv r) + (LoggerT Aff) + a newtype Contract (r :: Row Type) (a :: Type) = Contract (QueryMExtended r a) ``` - -In CTL we have a general environment type +In CTL we have a general environment type ```PureScript -forall (r :: Row Type) . QueryConfig r` +newtype ContractEnv (r :: Row Type) = ContractEnv (QueryEnv r) ``` -it stores the needed parameters to connect to an `Ogmios` server, -wallets and more things, this configuration uses the PureScript native +it stores the needed parameters to connect to an `Ogmios` server, +wallets and more things, this configuration uses the PureScript native [row polymorphism](https://en.wikipedia.org/wiki/Row_polymorphism) to make it extensible for both CTL developers and users. You can find a little discussion about row polymorphism [here](https://hgiasac.github.io/posts/2018-11-18-Record-Row-Type-and-Row-Polymorphism.html). diff --git a/examples/AlwaysMints.js b/examples/AlwaysMints.js index 13913b2b88..3ab8b58f30 100644 --- a/examples/AlwaysMints.js +++ b/examples/AlwaysMints.js @@ -1 +1,14 @@ -exports.alwaysMints = require("Scripts/always-mints.plutus"); +/* global BROWSER_RUNTIME */ + +let script; +if (typeof BROWSER_RUNTIME != "undefined" && BROWSER_RUNTIME) { + script = require("Scripts/always-mints.plutus"); +} else { + const fs = require("fs"); + const path = require("path"); + script = fs.readFileSync( + path.resolve(__dirname, "../../fixtures/scripts/always-mints.plutus"), + "utf8" + ); +} +exports.alwaysMints = script; diff --git a/examples/AlwaysMints.purs b/examples/AlwaysMints.purs index 586a7871f8..e71312f203 100644 --- a/examples/AlwaysMints.purs +++ b/examples/AlwaysMints.purs @@ -1,10 +1,12 @@ -- | This module demonstrates how the `Contract` interface can be used to build, -- | balance, and submit a smart-contract transaction. It creates a transaction -- | that mints a value using the `AlwaysMints` policy -module Examples.AlwaysMints (main) where +module Examples.AlwaysMints (main, alwaysMintsPolicy) where import Contract.Prelude +import Contract.Config (testnetNamiConfig) +import Contract.Log (logInfo') import Contract.Monad ( Contract , launchAff_ @@ -12,9 +14,7 @@ import Contract.Monad , liftContractM , liftedE , liftedM - , logInfo' - , runContract_ - , traceTestnetContractConfig + , runContract ) import Contract.Prim.ByteArray (byteArrayFromAscii) import Contract.ScriptLookups as Lookups @@ -23,15 +23,19 @@ import Contract.TextEnvelope ( TextEnvelopeType(PlutusScriptV1) , textEnvelopeBytes ) -import Contract.Transaction (balanceAndSignTxM, submit) +import Contract.Transaction + ( awaitTxConfirmed + , balanceAndSignTxM + , submit + ) import Contract.TxConstraints as Constraints import Contract.Value as Value import Data.BigInt as BigInt +import Contract.Test.E2E (publishTestFeedback) main :: Effect Unit main = launchAff_ $ do - cfg <- traceTestnetContractConfig - runContract_ cfg $ do + runContract testnetNamiConfig $ do logInfo' "Running Examples.AlwaysMints" mp <- alwaysMintsPolicy cs <- liftContractAffM "Cannot get cs" $ Value.scriptCurrencySymbol mp @@ -54,6 +58,11 @@ main = launchAff_ $ do txId <- submit bsTx logInfo' $ "Tx ID: " <> show txId + awaitTxConfirmed txId + logInfo' $ "Tx submitted successfully!" + + publishTestFeedback true + foreign import alwaysMints :: String alwaysMintsPolicy :: Contract () MintingPolicy diff --git a/examples/AlwaysSucceeds.js b/examples/AlwaysSucceeds.js index b63ce0a1b4..4ba6671b78 100644 --- a/examples/AlwaysSucceeds.js +++ b/examples/AlwaysSucceeds.js @@ -1 +1,15 @@ -exports.alwaysSucceeds = require("Scripts/always-succeeds.plutus"); +/* global BROWSER_RUNTIME */ + +let script; +if (typeof BROWSER_RUNTIME != "undefined" && BROWSER_RUNTIME) { + script = require("Scripts/always-succeeds.plutus"); +} else { + const fs = require("fs"); + const path = require("path"); + script = fs.readFileSync( + path.resolve(__dirname, "../../fixtures/scripts/always-succeeds.plutus"), + "utf8" + ); +} + +exports.alwaysSucceeds = script; diff --git a/examples/AlwaysSucceeds.purs b/examples/AlwaysSucceeds.purs index 1adb09e204..f19ef64477 100644 --- a/examples/AlwaysSucceeds.purs +++ b/examples/AlwaysSucceeds.purs @@ -1,19 +1,24 @@ -- | This module demonstrates how the `Contract` interface can be used to build, -- | balance, and submit a smart-contract transaction. It creates a transaction -- | that pays two Ada to the `AlwaysSucceeds` script address -module Examples.AlwaysSucceeds (main) where +module Examples.AlwaysSucceeds + ( main + , alwaysSucceedsScript + , payToAlwaysSucceeds + , spendFromAlwaysSucceeds + ) where import Contract.Prelude import Contract.Address (scriptHashAddress) +import Contract.Config (testnetNamiConfig) +import Contract.Log (logInfo') import Contract.Monad ( Contract , launchAff_ , liftContractAffM , liftedE - , logInfo' - , runContract_ - , traceTestnetContractConfig + , runContract ) import Contract.PlutusData (PlutusData, unitDatum, unitRedeemer) import Contract.ScriptLookups as Lookups @@ -25,6 +30,7 @@ import Contract.TextEnvelope import Contract.Transaction ( TransactionHash , TransactionInput(TransactionInput) + , awaitTxConfirmed , balanceAndSignTxE , submit ) @@ -34,12 +40,11 @@ import Contract.Utxos (UtxoM(UtxoM), utxosAt) import Contract.Value as Value import Data.BigInt as BigInt import Data.Map as Map -import Effect.Aff (delay) +import Contract.Test.E2E (publishTestFeedback) main :: Effect Unit -main = launchAff_ $ do - cfg <- traceTestnetContractConfig - runContract_ cfg $ do +main = launchAff_ do + runContract testnetNamiConfig do logInfo' "Running Examples.AlwaysSucceeds" validator <- alwaysSucceedsScript vhash <- liftContractAffM "Couldn't hash validator" @@ -47,16 +52,10 @@ main = launchAff_ $ do logInfo' "Attempt to lock value" txId <- payToAlwaysSucceeds vhash -- If the wallet is cold, you need a high parameter here. - countToZero 60 - logInfo' "Try to spend locked values" + awaitTxConfirmed txId + logInfo' "Tx submitted successfully, Try to spend locked values" spendFromAlwaysSucceeds vhash validator txId - -countToZero :: Int -> Contract () Unit -countToZero n = - unless (n <= 0) do - logInfo' $ "Waiting before we try to unlock: " <> show n - (liftAff <<< delay <<< wrap) 1000.0 - countToZero (n - 1) + publishTestFeedback true payToAlwaysSucceeds :: ValidatorHash -> Contract () TransactionHash payToAlwaysSucceeds vhash = do @@ -90,7 +89,11 @@ spendFromAlwaysSucceeds vhash validator txId = do constraints = Constraints.mustSpendScriptOutput txInput unitRedeemer in - void $ buildBalanceSignAndSubmitTx lookups constraints + do + spendTxId <- buildBalanceSignAndSubmitTx lookups constraints + awaitTxConfirmed spendTxId + logInfo' "Successfully spent locked values." + _ -> logInfo' $ "The id " <> show txId diff --git a/examples/ByUrl.js b/examples/ByUrl.js new file mode 100644 index 0000000000..48c063f653 --- /dev/null +++ b/examples/ByUrl.js @@ -0,0 +1 @@ +exports._queryString = () => window.location.search; diff --git a/examples/ByUrl.purs b/examples/ByUrl.purs new file mode 100644 index 0000000000..0467dc2449 --- /dev/null +++ b/examples/ByUrl.purs @@ -0,0 +1,35 @@ +module Examples.ByUrl (main) where + +import Prelude +import Data.String.Common (split) +import Data.String.Pattern (Pattern(Pattern)) +import Data.Array (last) +import Data.Maybe (Maybe(Just)) +import Effect (Effect) +import Effect.Class (liftEffect) +import Effect.Console (error) +import Examples.AlwaysMints as AlwaysMints +import Examples.AlwaysSucceeds as AlwaysSucceeds +import Examples.Datums as Datums +import Examples.Nami as Nami +import Examples.Gero as Gero +import Examples.Pkh2Pkh as Pkh2Pkh +import Examples.Pkh2PkhGero as Pkh2PkhGero +import Examples.SignMultiple as SignMultiple +import Examples.MintsMultipleTokens as MintsMultipleTokens + +foreign import _queryString :: Effect String + +main :: Effect Unit +main = last <<< split (Pattern "?") <$> _queryString >>= + case _ of + Just "AlwaysMints" -> AlwaysMints.main + Just "AlwaysSucceeds" -> AlwaysSucceeds.main + Just "Datums" -> Datums.main + Just "Nami" -> Nami.main + Just "Gero" -> Gero.main + Just "Pkh2Pkh" -> Pkh2Pkh.main + Just "Pkh2PkhGero" -> Pkh2PkhGero.main + Just "SignMultiple" -> SignMultiple.main + Just "MintsMultipleTokens" -> MintsMultipleTokens.main + _ -> liftEffect $ error "Error parsing query string" diff --git a/examples/Datums.purs b/examples/Datums.purs index 1befc44dba..dc794017bc 100644 --- a/examples/Datums.purs +++ b/examples/Datums.purs @@ -19,19 +19,16 @@ module Examples.Datums (main) where import Contract.Prelude -import Contract.Monad - ( runContract_ - , launchAff_ - , logInfo' - , traceTestnetContractConfig - ) +import Contract.Config (testnetConfig) +import Contract.Log (logInfo') +import Contract.Monad (runContract, launchAff_) import Contract.PlutusData (DataHash, getDatumByHash, getDatumsByHashes) import Contract.Prim.ByteArray (hexToByteArrayUnsafe) +import Contract.Test.E2E (publishTestFeedback) main :: Effect Unit main = launchAff_ $ do - cfg <- traceTestnetContractConfig - runContract_ cfg $ do + runContract testnetConfig $ do logInfo' "Running Examples.Datums" logInfo' <<< show =<< getDatumByHash ( mkDatumHash @@ -43,6 +40,7 @@ main = launchAff_ $ do , mkDatumHash "e8cb7d18e81b0be160c114c563c020dcc7bf148a1994b73912db3ea1318d488b" ] + publishTestFeedback true where mkDatumHash :: String -> DataHash mkDatumHash = wrap <<< hexToByteArrayUnsafe diff --git a/examples/Gero.purs b/examples/Gero.purs index 388341b87f..d723d313c1 100644 --- a/examples/Gero.purs +++ b/examples/Gero.purs @@ -8,17 +8,15 @@ module Examples.Gero (main) where import Contract.Prelude import Contract.Address (getWalletAddress, getWalletCollateral) -import Contract.Monad (configWithLogLevel, launchAff_, runContract_) +import Contract.Config (testnetGeroConfig) +import Contract.Monad (launchAff_, runContract) import Contract.Utxos (getWalletBalance) -import Contract.Wallet (mkGeroWalletAff) -import Data.Log.Level (LogLevel(Trace)) -import Serialization.Address (NetworkId(TestnetId)) +import Contract.Test.E2E (publishTestFeedback) main :: Effect Unit main = launchAff_ $ do - wallet <- mkGeroWalletAff - cfg <- configWithLogLevel TestnetId wallet Trace - runContract_ cfg $ do + runContract testnetGeroConfig $ do log <<< show =<< getWalletAddress log <<< show =<< getWalletCollateral log <<< show =<< getWalletBalance + liftAff $ publishTestFeedback true diff --git a/examples/KeyWallet/Internal/Pkh2PkhContract.purs b/examples/KeyWallet/Internal/Pkh2PkhContract.purs index cfb1354f34..aeb06d3676 100644 --- a/examples/KeyWallet/Internal/Pkh2PkhContract.purs +++ b/examples/KeyWallet/Internal/Pkh2PkhContract.purs @@ -5,19 +5,19 @@ module Examples.KeyWallet.Internal.Pkh2PkhContract import Contract.Prelude import Contract.Address (NetworkId(TestnetId), PaymentPubKeyHash) +import Contract.Config + ( PrivatePaymentKeySource(PrivatePaymentKeyValue) + , WalletSpec(UseKeys) + ) import Contract.Monad - ( ConfigParams(ConfigParams) - , Contract - , ContractConfig(ContractConfig) + ( Contract , defaultDatumCacheWsConfig , defaultOgmiosWsConfig , defaultServerConfig , launchAff_ - , mkContractConfig + , runContract ) import Control.Monad.Error.Class (class MonadError, catchError, liftMaybe) -import Control.Monad.Logger.Trans (runLoggerT) -import Control.Monad.Reader (runReaderT) import Data.BigInt (BigInt) import Data.BigInt (fromString) as BigInt import Data.Log.Formatter.Pretty (prettyFormatter) @@ -35,7 +35,6 @@ import Examples.KeyWallet.Internal.Pkh2PkhHtmlForm import Serialization (privateKeyFromBytes) import Serialization.Hash (ed25519KeyHashFromBech32) import Types.RawBytes (hexToRawBytes) -import Wallet (mkKeyWallet) runKeyWalletContract_ :: (PaymentPubKeyHash -> BigInt -> Unlock -> Contract () Unit) -> Effect Unit @@ -50,32 +49,27 @@ runKeyWalletContract_ contract = $ ed25519KeyHashFromBech32 input.toPkh lovelace <- liftMaybe (error "Failed to parse lovelace amount") $ BigInt.fromString input.lovelace - let wallet = mkKeyWallet (wrap privateKey) Nothing - cfg <- mkContractConfig $ ConfigParams - { ogmiosConfig: defaultOgmiosWsConfig - , datumCacheConfig: defaultDatumCacheWsConfig - , ctlServerConfig: defaultServerConfig - , networkId: TestnetId - , logLevel: Trace - , extraConfig: {} - , wallet: Just wallet - } - runContract' log' cfg (contract pkh lovelace unlock) + let + cfg = + { ogmiosConfig: defaultOgmiosWsConfig + , datumCacheConfig: defaultDatumCacheWsConfig + , ctlServerConfig: defaultServerConfig + , networkId: TestnetId + , logLevel: Trace + , extraConfig: {} + , walletSpec: Just $ UseKeys + (PrivatePaymentKeyValue $ wrap privateKey) + Nothing + , customLogger: Just printLog + } + + printLog :: Message -> Aff Unit + printLog m = liftEffect $ when (m.level >= Trace) $ do + prettyFormatter m >>= log + log' (HtmlForm.levelColor m.level) + ("[" <> HtmlForm.levelName m.level <> "] " <> m.message) + runContract cfg (contract pkh lovelace unlock) where - runContract' - :: forall (r :: Row Type) - . Log - -> ContractConfig r - -> Contract r Unit - -> Aff Unit - runContract' log' (ContractConfig config) = - flip runLoggerT printLog <<< flip runReaderT config <<< unwrap - where - printLog :: Message -> Aff Unit - printLog m = liftEffect $ when (m.level >= config.logLevel) $ do - prettyFormatter m >>= log - log' (HtmlForm.levelColor m.level) - ("[" <> HtmlForm.levelName m.level <> "] " <> m.message) errorHandler :: forall (m :: Type -> Type) diff --git a/examples/KeyWallet/Pkh2Pkh.purs b/examples/KeyWallet/Pkh2Pkh.purs index d827341583..b8eda03d7c 100644 --- a/examples/KeyWallet/Pkh2Pkh.purs +++ b/examples/KeyWallet/Pkh2Pkh.purs @@ -6,9 +6,14 @@ module Examples.KeyWallet.Pkh2Pkh (main) where import Contract.Prelude -import Contract.Monad (liftedE, liftedM, logInfo') +import Contract.Monad (liftedE, liftedM) +import Contract.Log (logInfo') import Contract.ScriptLookups as Lookups -import Contract.Transaction (balanceAndSignTxM, submit) +import Contract.Transaction + ( awaitTxConfirmed + , balanceAndSignTxM + , submit + ) import Contract.TxConstraints as Constraints import Contract.Value (lovelaceValueOf) as Value import Examples.KeyWallet.Internal.Pkh2PkhContract (runKeyWalletContract_) @@ -31,3 +36,5 @@ main = runKeyWalletContract_ \pkh lovelace unlock -> do txId <- submit bsTx logInfo' $ "Tx ID: " <> show txId liftEffect unlock + awaitTxConfirmed txId + logInfo' $ "Tx submitted successfully!" diff --git a/examples/KeyWallet/SignMultiple.purs b/examples/KeyWallet/SignMultiple.purs index 6cedec6b5f..51d290279e 100644 --- a/examples/KeyWallet/SignMultiple.purs +++ b/examples/KeyWallet/SignMultiple.purs @@ -2,23 +2,27 @@ module Examples.KeyWallet.SignMultiple where import Contract.Prelude -import Contract.Monad (Contract, liftedE, logInfo') +import Contract.Monad (Contract, liftedE, throwContractError) +import Contract.Log (logInfo') import Control.Monad.Reader (asks) import Contract.ScriptLookups as Lookups import Contract.Transaction ( BalancedSignedTransaction + , TransactionHash + , awaitTxConfirmed , submit , withBalancedAndSignedTxs ) import Contract.TxConstraints as Constraints import Contract.Value (lovelaceValueOf) as Value +import Data.Newtype (unwrap) import Effect.Ref (read) as Ref import Examples.KeyWallet.Internal.Pkh2PkhContract (runKeyWalletContract_) import Types.UsedTxOuts (TxOutRefCache) getLockedInputs :: forall (r :: Row Type). Contract r TxOutRefCache getLockedInputs = do - cache <- asks (_.usedTxOuts <<< unwrap) + cache <- asks (_.usedTxOuts <<< _.runtime <<< unwrap) liftEffect $ Ref.read $ unwrap cache main :: Effect Unit @@ -36,19 +40,30 @@ main = runKeyWalletContract_ \pkh lovelace unlock -> do ubTx1 <- liftedE $ Lookups.mkUnbalancedTx lookups constraints ubTx2 <- liftedE $ Lookups.mkUnbalancedTx lookups constraints - withBalancedAndSignedTxs [ ubTx1, ubTx2 ] $ \txs -> do + txIds <- withBalancedAndSignedTxs [ ubTx1, ubTx2 ] $ \txs -> do locked <- getLockedInputs logInfo' $ "Locked inputs inside bracket (should be nonempty): " <> show locked - traverse_ submitAndLog txs + traverse submitAndLog txs locked <- getLockedInputs logInfo' $ "Locked inputs after bracket (should be empty): " <> show locked + case txIds of + [ txId1, txId2 ] -> do + awaitTxConfirmed txId1 + logInfo' $ "Tx 1 submitted successfully!" + awaitTxConfirmed txId2 + logInfo' $ "Tx 2 submitted successfully!" + _ -> throwContractError "Unexpected error - no transaction IDs" + liftEffect unlock where submitAndLog - :: forall (r :: Row Type). BalancedSignedTransaction -> Contract r Unit + :: forall (r :: Row Type) + . BalancedSignedTransaction + -> Contract r TransactionHash submitAndLog bsTx = do txId <- submit bsTx logInfo' $ "Tx ID: " <> show txId + pure txId diff --git a/examples/MintsMultipleTokens.js b/examples/MintsMultipleTokens.js index aa2bc24acf..74ad17f73a 100644 --- a/examples/MintsMultipleTokens.js +++ b/examples/MintsMultipleTokens.js @@ -1,3 +1,24 @@ -exports.redeemerInt1 = require("Scripts/redeemer1.plutus"); -exports.redeemerInt2 = require("Scripts/redeemer2.plutus"); -exports.redeemerInt3 = require("Scripts/redeemer3.plutus"); +/* global BROWSER_RUNTIME */ + +let redeemerInt1, redeemerInt2, redeemerInt3; + +if (typeof BROWSER_RUNTIME != "undefined" && BROWSER_RUNTIME) { + redeemerInt1 = require("Scripts/redeemer1.plutus"); + redeemerInt2 = require("Scripts/redeemer2.plutus"); + redeemerInt3 = require("Scripts/redeemer3.plutus"); +} else { + const fs = require("fs"); + const path = require("path"); + const readScript = name => + fs.readFileSync( + path.resolve(__dirname, `../../fixtures/scripts/${name}.plutus`), + "utf8" + ); + redeemerInt1 = readScript("redeemer1"); + redeemerInt2 = readScript("redeemer2"); + redeemerInt3 = readScript("redeemer3"); +} + +exports.redeemerInt1 = redeemerInt1; +exports.redeemerInt2 = redeemerInt2; +exports.redeemerInt3 = redeemerInt3; diff --git a/examples/MintsMultipleTokens.purs b/examples/MintsMultipleTokens.purs index 615881b7e0..7e9acb0e27 100644 --- a/examples/MintsMultipleTokens.purs +++ b/examples/MintsMultipleTokens.purs @@ -1,10 +1,17 @@ -- | This module demonstrates how the `Contract` interface can be used to build, -- | balance, and submit a smart-contract transaction. It creates a transaction -- | that mints a value using three minting policies with different redeemers. -module Examples.MintsMultipleTokens (main) where +module Examples.MintsMultipleTokens + ( main + , mintingPolicyRdmrInt1 + , mintingPolicyRdmrInt2 + , mintingPolicyRdmrInt3 + ) where import Contract.Prelude +import Contract.Config (testnetNamiConfig) +import Contract.Log (logInfo') import Contract.Monad ( Contract , launchAff_ @@ -12,9 +19,7 @@ import Contract.Monad , liftContractM , liftedE , liftedM - , logInfo' - , runContract_ - , traceTestnetContractConfig + , runContract ) import Contract.PlutusData (PlutusData(Integer), Redeemer(Redeemer)) import Contract.Prim.ByteArray (byteArrayFromAscii) @@ -24,16 +29,20 @@ import Contract.TextEnvelope ( TextEnvelopeType(PlutusScriptV1) , textEnvelopeBytes ) -import Contract.Transaction (balanceAndSignTx, submit) +import Contract.Transaction + ( awaitTxConfirmed + , balanceAndSignTx + , submit + ) import Contract.TxConstraints as Constraints import Contract.Value (CurrencySymbol, TokenName) import Contract.Value as Value import Data.BigInt (fromInt) as BigInt +import Contract.Test.E2E (publishTestFeedback) main :: Effect Unit -main = launchAff_ $ do - cfg <- traceTestnetContractConfig - runContract_ cfg $ do +main = launchAff_ do + runContract testnetNamiConfig do logInfo' "Running Examples.MintsMultipleTokens" tn1 <- mkTokenName "Token with a long name" tn2 <- mkTokenName "Token" @@ -66,6 +75,11 @@ main = launchAff_ $ do txId <- submit bsTx logInfo' $ "Tx ID: " <> show txId + awaitTxConfirmed txId + logInfo' $ "Tx submitted successfully!" + + publishTestFeedback true + mkTokenName :: String -> Contract () TokenName mkTokenName = liftContractM "Cannot make token name" diff --git a/examples/Nami.purs b/examples/Nami.purs index fdb1fbaea1..9d683413ec 100644 --- a/examples/Nami.purs +++ b/examples/Nami.purs @@ -11,13 +11,15 @@ module Examples.Nami (main) where import Contract.Prelude import Contract.Address (getWalletAddress, getWalletCollateral) -import Contract.Monad (defaultTestnetContractConfig, launchAff_, runContract_) +import Contract.Config (testnetNamiConfig) +import Contract.Monad (launchAff_, runContract) import Contract.Utxos (getWalletBalance) +import Contract.Test.E2E (publishTestFeedback) main :: Effect Unit -main = launchAff_ $ do - cfg <- defaultTestnetContractConfig - runContract_ cfg $ do +main = launchAff_ do + runContract testnetNamiConfig do log <<< show =<< getWalletAddress log <<< show =<< getWalletCollateral log <<< show =<< getWalletBalance + publishTestFeedback true diff --git a/examples/Pkh2Pkh.purs b/examples/Pkh2Pkh.purs index 5691810bd6..3adb9575ae 100644 --- a/examples/Pkh2Pkh.purs +++ b/examples/Pkh2Pkh.purs @@ -5,49 +5,24 @@ module Examples.Pkh2Pkh (main) where import Contract.Prelude -import Contract.Address - ( NetworkId(TestnetId) - , ownPaymentPubKeyHash - , ownStakePubKeyHash - ) -import Contract.Monad - ( ConfigParams(ConfigParams) - , LogLevel(Trace) - , defaultDatumCacheWsConfig - , defaultOgmiosWsConfig - , defaultServerConfig - , launchAff_ - , liftedE - , liftedM - , logInfo' - , mkContractConfig - , runContract_ - ) +import Contract.Address (ownPaymentPubKeyHash, ownStakePubKeyHash) +import Contract.Config (testnetNamiConfig) +import Contract.Log (logInfo') +import Contract.Monad (launchAff_, liftedE, liftedM, runContract) import Contract.ScriptLookups as Lookups import Contract.Transaction - ( awaitTxConfirmed - , balanceAndSignTxM + ( balanceAndSignTxM + , awaitTxConfirmedWithTimeout , submit ) import Contract.TxConstraints as Constraints import Contract.Value as Value -import Contract.Wallet (mkNamiWalletAff) import Data.BigInt as BigInt +import Contract.Test.E2E (publishTestFeedback) main :: Effect Unit -main = launchAff_ $ do - wallet <- Just <$> mkNamiWalletAff - cfg <- mkContractConfig $ ConfigParams - { ogmiosConfig: defaultOgmiosWsConfig - , datumCacheConfig: defaultDatumCacheWsConfig - , ctlServerConfig: defaultServerConfig - , networkId: TestnetId - , logLevel: Trace - , extraConfig: {} - , wallet - } - - runContract_ cfg $ do +main = launchAff_ do + runContract testnetNamiConfig do logInfo' "Running Examples.Pkh2Pkh" pkh <- liftedM "Failed to get own PKH" ownPaymentPubKeyHash skh <- liftedM "Failed to get own SKH" ownStakePubKeyHash @@ -66,5 +41,6 @@ main = launchAff_ $ do liftedM "Failed to balance/sign tx" $ balanceAndSignTxM ubTx txId <- submit bsTx logInfo' $ "Tx ID: " <> show txId - awaitTxConfirmed txId + awaitTxConfirmedWithTimeout (wrap 100.0) txId logInfo' $ "Tx submitted successfully!" + liftAff $ publishTestFeedback true diff --git a/examples/Pkh2PkhGero.purs b/examples/Pkh2PkhGero.purs new file mode 100644 index 0000000000..f39727b1e1 --- /dev/null +++ b/examples/Pkh2PkhGero.purs @@ -0,0 +1,41 @@ +-- | This module demonstrates how the `Contract` interface can be used to build, +-- | balance, and submit a transaction. It creates a simple transaction that gets +-- | UTxOs from the user's wallet and sends two Ada back to the same wallet address +module Examples.Pkh2PkhGero (main) where + +import Contract.Prelude + +import Contract.Address (ownPaymentPubKeyHash, ownStakePubKeyHash) +import Contract.Config (testnetGeroConfig) +import Contract.Log (logInfo') +import Contract.Monad (launchAff_, liftedE, liftedM, runContract) +import Contract.ScriptLookups as Lookups +import Contract.Test.E2E (publishTestFeedback) +import Contract.Transaction (balanceAndSignTxE, submit) +import Contract.TxConstraints as Constraints +import Contract.Value as Value +import Data.BigInt as BigInt + +main :: Effect Unit +main = launchAff_ do + + runContract testnetGeroConfig do + logInfo' "Running Examples.Pkh2PkhGero" + pkh <- liftedM "Failed to get own PKH" ownPaymentPubKeyHash + skh <- liftedM "Failed to get own SKH" ownStakePubKeyHash + + let + constraints :: Constraints.TxConstraints Void Void + constraints = Constraints.mustPayToPubKeyAddress pkh skh + $ Value.lovelaceValueOf + $ BigInt.fromInt 2_000_000 + + lookups :: Lookups.ScriptLookups Void + lookups = mempty + + ubTx <- liftedE $ Lookups.mkUnbalancedTx lookups constraints + bsTx <- liftedE $ balanceAndSignTxE ubTx + txId <- submit bsTx + logInfo' $ "Tx ID: " <> show txId + + liftAff $ publishTestFeedback true diff --git a/examples/SignMultiple.purs b/examples/SignMultiple.purs index f670440518..5583218b54 100644 --- a/examples/SignMultiple.purs +++ b/examples/SignMultiple.purs @@ -5,59 +5,42 @@ module Examples.SignMultiple (main) where import Contract.Prelude -import Contract.Address - ( NetworkId(TestnetId) - , ownPaymentPubKeyHash - , ownStakePubKeyHash - ) +import Contract.Address (ownPaymentPubKeyHash, ownStakePubKeyHash) +import Contract.Config (testnetNamiConfig) +import Contract.Log (logInfo') import Contract.Monad ( Contract - , ConfigParams(ConfigParams) - , LogLevel(Trace) - , defaultDatumCacheWsConfig - , defaultOgmiosWsConfig - , defaultServerConfig , launchAff_ , liftedE , liftedM - , logInfo' - , mkContractConfig - , runContract_ + , runContract + , throwContractError ) -import Control.Monad.Reader (asks) -import Effect.Ref as Ref import Contract.ScriptLookups as Lookups +import Contract.Test.E2E (publishTestFeedback) import Contract.Transaction ( BalancedSignedTransaction + , TransactionHash + , awaitTxConfirmed , submit , withBalancedAndSignedTxs ) import Contract.TxConstraints as Constraints import Contract.Value as Value -import Contract.Wallet (mkNamiWalletAff) +import Control.Monad.Reader (asks) import Data.BigInt as BigInt +import Effect.Ref as Ref import Types.UsedTxOuts (TxOutRefCache) getLockedInputs :: forall (r :: Row Type). Contract r TxOutRefCache getLockedInputs = do - cache <- asks (_.usedTxOuts <<< unwrap) + cache <- asks (_.usedTxOuts <<< _.runtime <<< unwrap) liftEffect $ Ref.read $ unwrap cache main :: Effect Unit -main = launchAff_ $ do - wallet <- Just <$> mkNamiWalletAff - cfg <- mkContractConfig $ ConfigParams - { ogmiosConfig: defaultOgmiosWsConfig - , datumCacheConfig: defaultDatumCacheWsConfig - , ctlServerConfig: defaultServerConfig - , networkId: TestnetId - , logLevel: Trace - , extraConfig: {} - , wallet - } - - runContract_ cfg $ do - logInfo' "Running Examples.Pkh2Pkh" +main = launchAff_ do + runContract testnetNamiConfig do + logInfo' "Running Examples.SignMultiple" pkh <- liftedM "Failed to get own PKH" ownPaymentPubKeyHash skh <- liftedM "Failed to get own SKH" ownStakePubKeyHash @@ -73,19 +56,31 @@ main = launchAff_ $ do ubTx1 <- liftedE $ Lookups.mkUnbalancedTx lookups constraints ubTx2 <- liftedE $ Lookups.mkUnbalancedTx lookups constraints - withBalancedAndSignedTxs [ ubTx1, ubTx2 ] $ \txs -> do + txIds <- withBalancedAndSignedTxs [ ubTx1, ubTx2 ] $ \txs -> do locked <- getLockedInputs logInfo' $ "Locked inputs inside bracket (should be nonempty): " <> show locked - traverse_ submitAndLog txs + traverse submitAndLog txs locked <- getLockedInputs logInfo' $ "Locked inputs after bracket (should be empty): " <> show locked + case txIds of + [ txId1, txId2 ] -> do + awaitTxConfirmed txId1 + logInfo' $ "Tx 1 submitted successfully!" + awaitTxConfirmed txId2 + logInfo' $ "Tx 2 submitted successfully!" + _ -> throwContractError "Unexpected error - no transaction IDs" + + publishTestFeedback true + where submitAndLog - :: forall (r :: Row Type). BalancedSignedTransaction -> Contract r Unit + :: forall (r :: Row Type) + . BalancedSignedTransaction + -> Contract r TransactionHash submitAndLog bsTx = do txId <- submit bsTx logInfo' $ "Tx ID: " <> show txId - + pure txId diff --git a/flake.lock b/flake.lock index 3ac5a0da72..0380cfb48f 100644 --- a/flake.lock +++ b/flake.lock @@ -16,23 +16,24 @@ "type": "github" } }, - "HTTP_2": { + "Win32-network": { "flake": false, "locked": { - "lastModified": 1451647621, - "narHash": "sha256-oHIyw3x0iKBexEo49YeUDV1k74ZtyYKGR2gNJXXRxts=", - "owner": "phadej", - "repo": "HTTP", - "rev": "9bc0996d412fef1787449d841277ef663ad9a915", + "lastModified": 1627315969, + "narHash": "sha256-Hesb5GXSx0IwKSIi42ofisVELcQNX6lwHcoZcbaDiqc=", + "owner": "input-output-hk", + "repo": "Win32-network", + "rev": "3825d3abf75f83f406c1f7161883c438dac7277d", "type": "github" }, "original": { - "owner": "phadej", - "repo": "HTTP", + "owner": "input-output-hk", + "repo": "Win32-network", + "rev": "3825d3abf75f83f406c1f7161883c438dac7277d", "type": "github" } }, - "Win32-network": { + "Win32-network_2": { "flake": false, "locked": { "lastModified": 1627315969, @@ -49,7 +50,7 @@ "type": "github" } }, - "Win32-network_2": { + "Win32-network_3": { "flake": false, "locked": { "lastModified": 1627315969, @@ -66,24 +67,60 @@ "type": "github" } }, - "cabal-32": { - "flake": false, + "bot-plutus-interface": { + "inputs": { + "Win32-network": "Win32-network_3", + "cardano-addresses": "cardano-addresses_2", + "cardano-base": "cardano-base_3", + "cardano-config": "cardano-config_2", + "cardano-crypto": "cardano-crypto_3", + "cardano-ledger": "cardano-ledger_3", + "cardano-node": "cardano-node_3", + "cardano-prelude": "cardano-prelude_3", + "cardano-wallet": "cardano-wallet_2", + "ekg-forward": "ekg-forward_2", + "ekg-json": "ekg-json_2", + "flake-compat": "flake-compat_4", + "flat": "flat_3", + "goblins": "goblins_3", + "haskell-nix": "haskell-nix", + "hedgehog-extras": "hedgehog-extras_2", + "hw-aeson": "hw-aeson", + "hysterical-screams": "hysterical-screams", + "io-sim": "io-sim_2", + "iohk-monitoring-framework": "iohk-monitoring-framework_3", + "iohk-nix": "iohk-nix_3", + "nixpkgs": [ + "plutip", + "bot-plutus-interface", + "haskell-nix", + "nixpkgs-unstable" + ], + "optparse-applicative": "optparse-applicative_3", + "ouroboros-network": "ouroboros-network_3", + "plutus": "plutus_2", + "plutus-apps": "plutus-apps", + "purescript-bridge": "purescript-bridge", + "quickcheck-dynamic": "quickcheck-dynamic", + "servant-purescript": "servant-purescript", + "typed-protocols": "typed-protocols_2" + }, "locked": { - "lastModified": 1603716527, - "narHash": "sha256-X0TFfdD4KZpwl0Zr6x+PLxUt/VyKQfX7ylXHdmZIL+w=", - "owner": "haskell", - "repo": "cabal", - "rev": "48bf10787e27364730dd37a42b603cee8d6af7ee", + "lastModified": 1658231641, + "narHash": "sha256-WlyKzl+MvzKLSC21XZLNb5ZJdxJdLhLmFZZqwzTstzQ=", + "owner": "mlabs-haskell", + "repo": "bot-plutus-interface", + "rev": "e6e0c4aa81c7e5e6c109c18675759457d5fbbce2", "type": "github" }, "original": { - "owner": "haskell", - "ref": "3.2", - "repo": "cabal", + "owner": "mlabs-haskell", + "repo": "bot-plutus-interface", + "rev": "e6e0c4aa81c7e5e6c109c18675759457d5fbbce2", "type": "github" } }, - "cabal-32_2": { + "cabal-32": { "flake": false, "locked": { "lastModified": 1603716527, @@ -117,23 +154,6 @@ "type": "github" } }, - "cabal-34_2": { - "flake": false, - "locked": { - "lastModified": 1640353650, - "narHash": "sha256-N1t6M3/wqj90AEdRkeC8i923gQYUpzSr8b40qVOZ1Rk=", - "owner": "haskell", - "repo": "cabal", - "rev": "942639c18c0cd8ec53e0a6f8d120091af35312cd", - "type": "github" - }, - "original": { - "owner": "haskell", - "ref": "3.4", - "repo": "cabal", - "type": "github" - } - }, "cabal-36": { "flake": false, "locked": { @@ -151,37 +171,37 @@ "type": "github" } }, - "cabal-36_2": { + "cardano-addresses": { "flake": false, "locked": { - "lastModified": 1641652457, - "narHash": "sha256-BlFPKP4C4HRUJeAbdembX1Rms1LD380q9s0qVDeoAak=", - "owner": "haskell", - "repo": "cabal", - "rev": "f27667f8ec360c475027dcaee0138c937477b070", + "lastModified": 1631515399, + "narHash": "sha256-XgXQKJHRKAFwIjONh19D/gKE0ARlhMXXcV74eZpd0lw=", + "owner": "input-output-hk", + "repo": "cardano-addresses", + "rev": "d2f86caa085402a953920c6714a0de6a50b655ec", "type": "github" }, "original": { - "owner": "haskell", - "ref": "3.6", - "repo": "cabal", + "owner": "input-output-hk", + "repo": "cardano-addresses", + "rev": "d2f86caa085402a953920c6714a0de6a50b655ec", "type": "github" } }, - "cardano-addresses": { + "cardano-addresses_2": { "flake": false, "locked": { - "lastModified": 1631515399, - "narHash": "sha256-XgXQKJHRKAFwIjONh19D/gKE0ARlhMXXcV74eZpd0lw=", + "lastModified": 1655809189, + "narHash": "sha256-hYAvI7KlFnFRjMG8/JvDl733YnQUE1O26VMcr94h0oM=", "owner": "input-output-hk", "repo": "cardano-addresses", - "rev": "d2f86caa085402a953920c6714a0de6a50b655ec", + "rev": "b6f2f3cef01a399376064194fd96711a5bdba4a7", "type": "github" }, "original": { "owner": "input-output-hk", "repo": "cardano-addresses", - "rev": "d2f86caa085402a953920c6714a0de6a50b655ec", + "rev": "b6f2f3cef01a399376064194fd96711a5bdba4a7", "type": "github" } }, @@ -219,6 +239,23 @@ "type": "github" } }, + "cardano-base_3": { + "flake": false, + "locked": { + "lastModified": 1654537609, + "narHash": "sha256-4b0keLjRaVSdEwfBXB1iT3QPlsutdxSltGfBufT4Clw=", + "owner": "input-output-hk", + "repo": "cardano-base", + "rev": "0f3a867493059e650cda69e20a5cbf1ace289a57", + "type": "github" + }, + "original": { + "owner": "input-output-hk", + "repo": "cardano-base", + "rev": "0f3a867493059e650cda69e20a5cbf1ace289a57", + "type": "github" + } + }, "cardano-config": { "flake": false, "locked": { @@ -236,6 +273,23 @@ "type": "github" } }, + "cardano-config_2": { + "flake": false, + "locked": { + "lastModified": 1642737642, + "narHash": "sha256-TNbpnR7llUgBN2WY7CryMxNVupBIUH01h1hRNHoxboY=", + "owner": "input-output-hk", + "repo": "cardano-config", + "rev": "1646e9167fab36c0bff82317743b96efa2d3adaa", + "type": "github" + }, + "original": { + "owner": "input-output-hk", + "repo": "cardano-config", + "rev": "1646e9167fab36c0bff82317743b96efa2d3adaa", + "type": "github" + } + }, "cardano-configurations": { "flake": false, "locked": { @@ -286,6 +340,23 @@ "type": "github" } }, + "cardano-crypto_3": { + "flake": false, + "locked": { + "lastModified": 1604244485, + "narHash": "sha256-2Fipex/WjIRMrvx6F3hjJoAeMtFd2wGnZECT0kuIB9k=", + "owner": "input-output-hk", + "repo": "cardano-crypto", + "rev": "f73079303f663e028288f9f4a9e08bcca39a923e", + "type": "github" + }, + "original": { + "owner": "input-output-hk", + "repo": "cardano-crypto", + "rev": "f73079303f663e028288f9f4a9e08bcca39a923e", + "type": "github" + } + }, "cardano-ledger": { "flake": false, "locked": { @@ -320,6 +391,23 @@ "type": "github" } }, + "cardano-ledger_3": { + "flake": false, + "locked": { + "lastModified": 1657127204, + "narHash": "sha256-4wcSA61TwoDTvJ6rx7tjEAJjQLO/cs8WGTHcOghNdTc=", + "owner": "input-output-hk", + "repo": "cardano-ledger", + "rev": "3be8a19083fc13d9261b1640e27dd389b51bb08e", + "type": "github" + }, + "original": { + "owner": "input-output-hk", + "repo": "cardano-ledger", + "rev": "3be8a19083fc13d9261b1640e27dd389b51bb08e", + "type": "github" + } + }, "cardano-node": { "flake": false, "locked": { @@ -354,6 +442,23 @@ "type": "github" } }, + "cardano-node_3": { + "flake": false, + "locked": { + "lastModified": 1657227628, + "narHash": "sha256-CP58qcHZJGYq1FzXCj8ll085TvnJoYMeXnVGVGLYH/w=", + "owner": "input-output-hk", + "repo": "cardano-node", + "rev": "c75451f0ffd7a60b5ad6c4263891e6c8acac105a", + "type": "github" + }, + "original": { + "owner": "input-output-hk", + "repo": "cardano-node", + "rev": "c75451f0ffd7a60b5ad6c4263891e6c8acac105a", + "type": "github" + } + }, "cardano-prelude": { "flake": false, "locked": { @@ -388,23 +493,24 @@ "type": "github" } }, - "cardano-shell": { + "cardano-prelude_3": { "flake": false, "locked": { - "lastModified": 1608537748, - "narHash": "sha256-PulY1GfiMgKVnBci3ex4ptk2UNYMXqGjJOxcPy2KYT4=", + "lastModified": 1617089317, + "narHash": "sha256-kgX3DKyfjBb8/XcDEd+/adlETsFlp5sCSurHWgsFAQI=", "owner": "input-output-hk", - "repo": "cardano-shell", - "rev": "9392c75087cb9a3d453998f4230930dea3a95725", + "repo": "cardano-prelude", + "rev": "bb4ed71ba8e587f672d06edf9d2e376f4b055555", "type": "github" }, "original": { "owner": "input-output-hk", - "repo": "cardano-shell", + "repo": "cardano-prelude", + "rev": "bb4ed71ba8e587f672d06edf9d2e376f4b055555", "type": "github" } }, - "cardano-shell_2": { + "cardano-shell": { "flake": false, "locked": { "lastModified": 1608537748, @@ -437,6 +543,23 @@ "type": "github" } }, + "cardano-wallet_2": { + "flake": false, + "locked": { + "lastModified": 1657745277, + "narHash": "sha256-+PrfQH6m7ROpHKNyo54MzLrL31tIvSZUQYnbBT70ekc=", + "owner": "input-output-hk", + "repo": "cardano-wallet", + "rev": "2ac308b00d9d4a3435f6b9594ded9495e2b217eb", + "type": "github" + }, + "original": { + "owner": "input-output-hk", + "repo": "cardano-wallet", + "rev": "2ac308b00d9d4a3435f6b9594ded9495e2b217eb", + "type": "github" + } + }, "easy-purescript-nix": { "flake": false, "locked": { @@ -450,6 +573,7 @@ "original": { "owner": "justinwoo", "repo": "easy-purescript-nix", + "rev": "d56c436a66ec2a8a93b309c83693cef1507dca7a", "type": "github" } }, @@ -470,6 +594,23 @@ "type": "github" } }, + "ekg-forward_2": { + "flake": false, + "locked": { + "lastModified": 1642052814, + "narHash": "sha256-jwj/gh/A/PXhO6yVESV27k4yx9I8Id8fTa3m4ofPnP0=", + "owner": "input-output-hk", + "repo": "ekg-forward", + "rev": "297cd9db5074339a2fb2e5ae7d0780debb670c63", + "type": "github" + }, + "original": { + "owner": "input-output-hk", + "repo": "ekg-forward", + "rev": "297cd9db5074339a2fb2e5ae7d0780debb670c63", + "type": "github" + } + }, "ekg-json": { "flake": false, "locked": { @@ -487,6 +628,23 @@ "type": "github" } }, + "ekg-json_2": { + "flake": false, + "locked": { + "lastModified": 1642583945, + "narHash": "sha256-VT8Ur585TCn03P2TVi6t92v2Z6tl8vKijICjse6ocv8=", + "owner": "vshabanov", + "repo": "ekg-json", + "rev": "00ebe7211c981686e65730b7144fbf5350462608", + "type": "github" + }, + "original": { + "owner": "vshabanov", + "repo": "ekg-json", + "rev": "00ebe7211c981686e65730b7144fbf5350462608", + "type": "github" + } + }, "flake-compat": { "flake": false, "locked": { @@ -535,22 +693,39 @@ "type": "github" } }, - "flake-utils": { + "flake-compat_4": { + "flake": false, "locked": { - "lastModified": 1644229661, - "narHash": "sha256-1YdnJAsNy69bpcjuoKdOYQX0YxZBiCYZo4Twxerqv7k=", - "owner": "numtide", - "repo": "flake-utils", - "rev": "3cecb5b042f7f209c56ffd8371b2711a290ec797", + "lastModified": 1650374568, + "narHash": "sha256-Z+s0J8/r907g149rllvwhb4pKi8Wam5ij0st8PwAh+E=", + "owner": "edolstra", + "repo": "flake-compat", + "rev": "b4a34015c698c7793d592d66adbab377907a2be8", "type": "github" }, "original": { - "owner": "numtide", - "repo": "flake-utils", + "owner": "edolstra", + "repo": "flake-compat", + "type": "github" + } + }, + "flake-compat_5": { + "flake": false, + "locked": { + "lastModified": 1650374568, + "narHash": "sha256-Z+s0J8/r907g149rllvwhb4pKi8Wam5ij0st8PwAh+E=", + "owner": "edolstra", + "repo": "flake-compat", + "rev": "b4a34015c698c7793d592d66adbab377907a2be8", + "type": "github" + }, + "original": { + "owner": "edolstra", + "repo": "flake-compat", "type": "github" } }, - "flake-utils_2": { + "flake-utils": { "locked": { "lastModified": 1644229661, "narHash": "sha256-1YdnJAsNy69bpcjuoKdOYQX0YxZBiCYZo4Twxerqv7k=", @@ -599,24 +774,24 @@ "type": "github" } }, - "ghc-8.6.5-iohk": { + "flat_3": { "flake": false, "locked": { - "lastModified": 1600920045, - "narHash": "sha256-DO6kxJz248djebZLpSzTGD6s8WRpNI9BTwUeOf5RwY8=", - "owner": "input-output-hk", - "repo": "ghc", - "rev": "95713a6ecce4551240da7c96b6176f980af75cae", + "lastModified": 1628771504, + "narHash": "sha256-lRFND+ZnZvAph6ZYkr9wl9VAx41pb3uSFP8Wc7idP9M=", + "owner": "Quid2", + "repo": "flat", + "rev": "ee59880f47ab835dbd73bea0847dab7869fc20d8", "type": "github" }, "original": { - "owner": "input-output-hk", - "ref": "release/8.6.5-iohk", - "repo": "ghc", + "owner": "Quid2", + "repo": "flat", + "rev": "ee59880f47ab835dbd73bea0847dab7869fc20d8", "type": "github" } }, - "ghc-8.6.5-iohk_2": { + "ghc-8.6.5-iohk": { "flake": false, "locked": { "lastModified": 1600920045, @@ -667,30 +842,31 @@ "type": "github" } }, - "hackage": { + "goblins_3": { "flake": false, "locked": { - "lastModified": 1650157984, - "narHash": "sha256-hitutrIIn+qINGi6oef53f87we+cp3QNmXSBiCzVU90=", + "lastModified": 1598362523, + "narHash": "sha256-z9ut0y6umDIjJIRjz9KSvKgotuw06/S8QDwOtVdGiJ0=", "owner": "input-output-hk", - "repo": "hackage.nix", - "rev": "2290fdc4d135407896f41ba518a0eae8efaae9c5", + "repo": "goblins", + "rev": "cde90a2b27f79187ca8310b6549331e59595e7ba", "type": "github" }, "original": { "owner": "input-output-hk", - "repo": "hackage.nix", + "repo": "goblins", + "rev": "cde90a2b27f79187ca8310b6549331e59595e7ba", "type": "github" } }, - "hackage_2": { + "hackage": { "flake": false, "locked": { - "lastModified": 1650157984, - "narHash": "sha256-hitutrIIn+qINGi6oef53f87we+cp3QNmXSBiCzVU90=", + "lastModified": 1653441966, + "narHash": "sha256-aJFK0wDzoOrtb7ucZzKh5J+S2pThpwNCofl74s1olXU=", "owner": "input-output-hk", "repo": "hackage.nix", - "rev": "2290fdc4d135407896f41ba518a0eae8efaae9c5", + "rev": "f7fe6ef8de52c43a9efa6fd4ac4902e5957dc573", "type": "github" }, "original": { @@ -713,6 +889,8 @@ "hydra": "hydra", "nix-tools": "nix-tools", "nixpkgs": [ + "plutip", + "bot-plutus-interface", "haskell-nix", "nixpkgs-unstable" ], @@ -724,61 +902,37 @@ "stackage": "stackage" }, "locked": { - "lastModified": 1650194184, - "narHash": "sha256-wwRdO075Gh+NbyTH4Gce/hxn7hKJjbNs4/YrKpOguAA=", + "lastModified": 1653486569, + "narHash": "sha256-342b0LPX6kaBuEX8KZV40FwCCFre1lCtjdTQIDEt9kw=", "owner": "mlabs-haskell", "repo": "haskell.nix", - "rev": "cf1f0460b65efadac6dc96169ef1e497410fa4f4", + "rev": "220f8a9cd166e726aea62843bdafa7ecded3375c", "type": "github" }, "original": { "owner": "mlabs-haskell", - "ref": "master", "repo": "haskell.nix", "type": "github" } }, - "haskell-nix_2": { - "inputs": { - "HTTP": "HTTP_2", - "cabal-32": "cabal-32_2", - "cabal-34": "cabal-34_2", - "cabal-36": "cabal-36_2", - "cardano-shell": "cardano-shell_2", - "flake-utils": "flake-utils_2", - "ghc-8.6.5-iohk": "ghc-8.6.5-iohk_2", - "hackage": "hackage_2", - "hpc-coveralls": "hpc-coveralls_2", - "hydra": "hydra_2", - "nix-tools": "nix-tools_2", - "nixpkgs": [ - "ogmios", - "haskell-nix", - "nixpkgs-unstable" - ], - "nixpkgs-2003": "nixpkgs-2003_2", - "nixpkgs-2105": "nixpkgs-2105_2", - "nixpkgs-2111": "nixpkgs-2111_2", - "nixpkgs-unstable": "nixpkgs-unstable_2", - "old-ghc-nix": "old-ghc-nix_2", - "stackage": "stackage_2" - }, + "hedgehog-extras": { + "flake": false, "locked": { - "lastModified": 1650194184, - "narHash": "sha256-wwRdO075Gh+NbyTH4Gce/hxn7hKJjbNs4/YrKpOguAA=", - "owner": "mlabs-haskell", - "repo": "haskell.nix", - "rev": "cf1f0460b65efadac6dc96169ef1e497410fa4f4", + "lastModified": 1647260073, + "narHash": "sha256-TR9i1J3HUYz3QnFQbfJPr/kGDahxZPojDsorYtRZeGU=", + "owner": "input-output-hk", + "repo": "hedgehog-extras", + "rev": "967d79533c21e33387d0227a5f6cc185203fe658", "type": "github" }, "original": { - "owner": "mlabs-haskell", - "repo": "haskell.nix", - "rev": "cf1f0460b65efadac6dc96169ef1e497410fa4f4", + "owner": "input-output-hk", + "repo": "hedgehog-extras", + "rev": "967d79533c21e33387d0227a5f6cc185203fe658", "type": "github" } }, - "hedgehog-extras": { + "hedgehog-extras_2": { "flake": false, "locked": { "lastModified": 1647260073, @@ -845,19 +999,20 @@ "type": "github" } }, - "hpc-coveralls_2": { + "hw-aeson": { "flake": false, "locked": { - "lastModified": 1607498076, - "narHash": "sha256-8uqsEtivphgZWYeUo5RDUhp6bO9j2vaaProQxHBltQk=", - "owner": "sevanspowell", - "repo": "hpc-coveralls", - "rev": "14df0f7d229f4cd2e79f8eabb1a740097fdfa430", + "lastModified": 1649341404, + "narHash": "sha256-xO4/zPMBmZtBXFwHF8p3nw4TilrJHxH54mfg9CRnuO8=", + "owner": "haskell-works", + "repo": "hw-aeson", + "rev": "d99d2f3e39a287607418ae605b132a3deb2b753f", "type": "github" }, "original": { - "owner": "sevanspowell", - "repo": "hpc-coveralls", + "owner": "haskell-works", + "repo": "hw-aeson", + "rev": "d99d2f3e39a287607418ae605b132a3deb2b753f", "type": "github" } }, @@ -865,6 +1020,8 @@ "inputs": { "nix": "nix", "nixpkgs": [ + "plutip", + "bot-plutus-interface", "haskell-nix", "hydra", "nix", @@ -884,28 +1041,21 @@ "type": "indirect" } }, - "hydra_2": { - "inputs": { - "nix": "nix_2", - "nixpkgs": [ - "ogmios", - "haskell-nix", - "hydra", - "nix", - "nixpkgs" - ] - }, + "hysterical-screams": { + "flake": false, "locked": { - "lastModified": 1646878427, - "narHash": "sha256-KtbrofMtN8GlM7D+n90kixr7QpSlVmdN+vK5CA/aRzc=", - "owner": "NixOS", - "repo": "hydra", - "rev": "28b682b85b7efc5cf7974065792a1f22203a5927", + "lastModified": 1654007733, + "narHash": "sha256-d4N3rUzg45BUs5Lx/kK7vXYsLMNoO15dlzo7t8lGIXA=", + "owner": "raduom", + "repo": "hysterical-screams", + "rev": "4c523469e9efd3f0d10d17da3304923b7b0e0674", "type": "github" }, "original": { - "id": "hydra", - "type": "indirect" + "owner": "raduom", + "repo": "hysterical-screams", + "rev": "4c523469e9efd3f0d10d17da3304923b7b0e0674", + "type": "github" } }, "io-sim": { @@ -925,6 +1075,23 @@ "type": "github" } }, + "io-sim_2": { + "flake": false, + "locked": { + "lastModified": 1654253725, + "narHash": "sha256-TviSvCBEYtlKEo9qJmE8pCE25nMjDi8HeIAFniunaM8=", + "owner": "input-output-hk", + "repo": "io-sim", + "rev": "57e888b1894829056cb00b7b5785fdf6a74c3271", + "type": "github" + }, + "original": { + "owner": "input-output-hk", + "repo": "io-sim", + "rev": "57e888b1894829056cb00b7b5785fdf6a74c3271", + "type": "github" + } + }, "iohk-monitoring-framework": { "flake": false, "locked": { @@ -959,16 +1126,33 @@ "type": "github" } }, + "iohk-monitoring-framework_3": { + "flake": false, + "locked": { + "lastModified": 1653619339, + "narHash": "sha256-0ia5UflYEmBYepj2gkJy9msknklI0UPtUavMEGwk3Wg=", + "owner": "input-output-hk", + "repo": "iohk-monitoring-framework", + "rev": "066f7002aac5a0efc20e49643fea45454f226caa", + "type": "github" + }, + "original": { + "owner": "input-output-hk", + "repo": "iohk-monitoring-framework", + "rev": "066f7002aac5a0efc20e49643fea45454f226caa", + "type": "github" + } + }, "iohk-nix": { "inputs": { - "nixpkgs": "nixpkgs_2" + "nixpkgs": "nixpkgs" }, "locked": { - "lastModified": 1649070135, - "narHash": "sha256-UFKqcOSdPWk3TYUCPHF22p1zf7aXQpCmmgf7UMg7fWA=", + "lastModified": 1658222743, + "narHash": "sha256-yFH01psqx30y5Ws4dBElLkxYpIxxqZx4G+jCVhsXpnA=", "owner": "input-output-hk", "repo": "iohk-nix", - "rev": "cecab9c71d1064f05f1615eead56ac0b9196bc20", + "rev": "9a604d01bd4420ab7f396f14d1947fbe2ce7db8b", "type": "github" }, "original": { @@ -979,7 +1163,7 @@ }, "iohk-nix_2": { "inputs": { - "nixpkgs": "nixpkgs_4" + "nixpkgs": "nixpkgs_2" }, "locked": { "lastModified": 1649070135, @@ -996,23 +1180,23 @@ "type": "github" } }, - "lowdown-src": { + "iohk-nix_3": { "flake": false, "locked": { - "lastModified": 1633514407, - "narHash": "sha256-Dw32tiMjdK9t3ETl5fzGrutQTzh2rufgZV4A/BbxuD4=", - "owner": "kristapsdz", - "repo": "lowdown", - "rev": "d2c2b44ff6c27b936ec27358a2653caaef8f73b8", + "lastModified": 1653579289, + "narHash": "sha256-wveDdPsgB/3nAGAdFaxrcgLEpdi0aJ5kEVNtI+YqVfo=", + "owner": "input-output-hk", + "repo": "iohk-nix", + "rev": "edb2d2df2ebe42bbdf03a0711115cf6213c9d366", "type": "github" }, "original": { - "owner": "kristapsdz", - "repo": "lowdown", + "owner": "input-output-hk", + "repo": "iohk-nix", "type": "github" } }, - "lowdown-src_2": { + "lowdown-src": { "flake": false, "locked": { "lastModified": 1633514407, @@ -1031,7 +1215,7 @@ "nix": { "inputs": { "lowdown-src": "lowdown-src", - "nixpkgs": "nixpkgs", + "nixpkgs": "nixpkgs_4", "nixpkgs-regression": "nixpkgs-regression" }, "locked": { @@ -1065,55 +1249,15 @@ "type": "github" } }, - "nix-tools_2": { - "flake": false, - "locked": { - "lastModified": 1649424170, - "narHash": "sha256-XgKXWispvv5RCvZzPb+p7e6Hy3LMuRjafKMl7kXzxGw=", - "owner": "input-output-hk", - "repo": "nix-tools", - "rev": "e109c94016e3b6e0db7ed413c793e2d4bdb24aa7", - "type": "github" - }, - "original": { - "owner": "input-output-hk", - "repo": "nix-tools", - "type": "github" - } - }, - "nix_2": { - "inputs": { - "lowdown-src": "lowdown-src_2", - "nixpkgs": "nixpkgs_3", - "nixpkgs-regression": "nixpkgs-regression_2" - }, - "locked": { - "lastModified": 1643066034, - "narHash": "sha256-xEPeMcNJVOeZtoN+d+aRwolpW8mFSEQx76HTRdlhPhg=", - "owner": "NixOS", - "repo": "nix", - "rev": "a1cd7e58606a41fcf62bf8637804cf8306f17f62", - "type": "github" - }, - "original": { - "owner": "NixOS", - "ref": "2.6.0", - "repo": "nix", - "type": "github" - } - }, "nixpkgs": { "locked": { - "lastModified": 1632864508, - "narHash": "sha256-d127FIvGR41XbVRDPVvozUPQ/uRHbHwvfyKHwEt5xFM=", - "owner": "NixOS", - "repo": "nixpkgs", - "rev": "82891b5e2c2359d7e58d08849e4c89511ab94234", - "type": "github" + "lastModified": 0, + "narHash": "sha256-cowVkScfUPlbBXUp08MeVk/wgm9E1zp1uC+9no2hZYw=", + "path": "/nix/store/0ki4clglxkgjw6znhpc0yg89pmy54xql-source", + "type": "path" }, "original": { "id": "nixpkgs", - "ref": "nixos-21.05-small", "type": "indirect" } }, @@ -1133,22 +1277,6 @@ "type": "github" } }, - "nixpkgs-2003_2": { - "locked": { - "lastModified": 1620055814, - "narHash": "sha256-8LEHoYSJiL901bTMVatq+rf8y7QtWuZhwwpKE2fyaRY=", - "owner": "NixOS", - "repo": "nixpkgs", - "rev": "1db42b7fe3878f3f5f7a4f2dc210772fd080e205", - "type": "github" - }, - "original": { - "owner": "NixOS", - "ref": "nixpkgs-20.03-darwin", - "repo": "nixpkgs", - "type": "github" - } - }, "nixpkgs-2105": { "locked": { "lastModified": 1645296114, @@ -1165,22 +1293,6 @@ "type": "github" } }, - "nixpkgs-2105_2": { - "locked": { - "lastModified": 1645296114, - "narHash": "sha256-y53N7TyIkXsjMpOG7RhvqJFGDacLs9HlyHeSTBioqYU=", - "owner": "NixOS", - "repo": "nixpkgs", - "rev": "530a53dcbc9437363471167a5e4762c5fcfa34a1", - "type": "github" - }, - "original": { - "owner": "NixOS", - "ref": "nixpkgs-21.05-darwin", - "repo": "nixpkgs", - "type": "github" - } - }, "nixpkgs-2111": { "locked": { "lastModified": 1648744337, @@ -1197,22 +1309,6 @@ "type": "github" } }, - "nixpkgs-2111_2": { - "locked": { - "lastModified": 1648744337, - "narHash": "sha256-bYe1dFJAXovjqiaPKrmAbSBEK5KUkgwVaZcTbSoJ7hg=", - "owner": "NixOS", - "repo": "nixpkgs", - "rev": "0a58eebd8ec65ffdef2ce9562784123a73922052", - "type": "github" - }, - "original": { - "owner": "NixOS", - "ref": "nixpkgs-21.11-darwin", - "repo": "nixpkgs", - "type": "github" - } - }, "nixpkgs-regression": { "locked": { "lastModified": 1643052045, @@ -1228,21 +1324,6 @@ "type": "indirect" } }, - "nixpkgs-regression_2": { - "locked": { - "lastModified": 1643052045, - "narHash": "sha256-uGJ0VXIhWKGXxkeNnq4TvV3CIOkUJ3PAoLZ3HMzNVMw=", - "owner": "NixOS", - "repo": "nixpkgs", - "rev": "215d4d0fd80ca5163643b03a33fde804a29cc1e2", - "type": "github" - }, - "original": { - "id": "nixpkgs", - "rev": "215d4d0fd80ca5163643b03a33fde804a29cc1e2", - "type": "indirect" - } - }, "nixpkgs-unstable": { "locked": { "lastModified": 1648219316, @@ -1259,22 +1340,6 @@ "type": "github" } }, - "nixpkgs-unstable_2": { - "locked": { - "lastModified": 1648219316, - "narHash": "sha256-Ctij+dOi0ZZIfX5eMhgwugfvB+WZSrvVNAyAuANOsnQ=", - "owner": "NixOS", - "repo": "nixpkgs", - "rev": "30d3d79b7d3607d56546dd2a6b49e156ba0ec634", - "type": "github" - }, - "original": { - "owner": "NixOS", - "ref": "nixpkgs-unstable", - "repo": "nixpkgs", - "type": "github" - } - }, "nixpkgs_2": { "locked": { "lastModified": 1647122627, @@ -1290,46 +1355,33 @@ }, "nixpkgs_3": { "locked": { - "lastModified": 1632864508, - "narHash": "sha256-d127FIvGR41XbVRDPVvozUPQ/uRHbHwvfyKHwEt5xFM=", + "lastModified": 1634172192, + "narHash": "sha256-FBF4U/T+bMg4sEyT/zkgasvVquGzgdAf4y8uCosKMmo=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "82891b5e2c2359d7e58d08849e4c89511ab94234", + "rev": "2cf9db0e3d45b9d00f16f2836cb1297bcadc475e", "type": "github" }, "original": { - "id": "nixpkgs", - "ref": "nixos-21.05-small", - "type": "indirect" + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "2cf9db0e3d45b9d00f16f2836cb1297bcadc475e", + "type": "github" } }, "nixpkgs_4": { "locked": { - "lastModified": 1647122627, - "narHash": "sha256-w4hGsXYyMgJAQRSBxh7O6AAsawJSbudCxfQXhDRhwPQ=", - "path": "/nix/store/s6wigis38dnikj5y92jrrj7ywc38b78g-source", - "rev": "0f85665118d850aae5164d385d24783d0b16cf1b", - "type": "path" - }, - "original": { - "id": "nixpkgs", - "type": "indirect" - } - }, - "nixpkgs_5": { - "locked": { - "lastModified": 1634172192, - "narHash": "sha256-FBF4U/T+bMg4sEyT/zkgasvVquGzgdAf4y8uCosKMmo=", + "lastModified": 1632864508, + "narHash": "sha256-d127FIvGR41XbVRDPVvozUPQ/uRHbHwvfyKHwEt5xFM=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "2cf9db0e3d45b9d00f16f2836cb1297bcadc475e", + "rev": "82891b5e2c2359d7e58d08849e4c89511ab94234", "type": "github" }, "original": { - "owner": "NixOS", - "repo": "nixpkgs", - "rev": "2cf9db0e3d45b9d00f16f2836cb1297bcadc475e", - "type": "github" + "id": "nixpkgs", + "ref": "nixos-21.05-small", + "type": "indirect" } }, "ogmios": { @@ -1344,7 +1396,9 @@ "flake-compat": "flake-compat_2", "flat": "flat_2", "goblins": "goblins_2", - "haskell-nix": "haskell-nix_2", + "haskell-nix": [ + "haskell-nix" + ], "hedgehog-extras": "hedgehog-extras", "hjsonpointer": "hjsonpointer", "hjsonschema": "hjsonschema", @@ -1352,9 +1406,7 @@ "iohk-monitoring-framework": "iohk-monitoring-framework_2", "iohk-nix": "iohk-nix_2", "nixpkgs": [ - "ogmios", - "haskell-nix", - "nixpkgs-unstable" + "nixpkgs" ], "optparse-applicative": "optparse-applicative", "ouroboros-network": "ouroboros-network", @@ -1380,21 +1432,21 @@ "ogmios-datum-cache": { "inputs": { "flake-compat": "flake-compat_3", - "nixpkgs": "nixpkgs_5", + "nixpkgs": "nixpkgs_3", "unstable_nixpkgs": "unstable_nixpkgs" }, "locked": { - "lastModified": 1658143527, - "narHash": "sha256-odoAINZlYoygNti9Lo6x8sqNa6oCyAJxM9hCurF0bPs=", + "lastModified": 1659358988, + "narHash": "sha256-YKabPu9FDvUNmSR7+MNwLwiURv4lWQr13r1CuoS3qhM=", "owner": "mlabs-haskell", "repo": "ogmios-datum-cache", - "rev": "bf76a74fa9e94d97310087dcda7b3aca259f96dd", + "rev": "47f01a1d9f7dc5cc5246c0c228e5cf5f5ba44399", "type": "github" }, "original": { "owner": "mlabs-haskell", "repo": "ogmios-datum-cache", - "rev": "bf76a74fa9e94d97310087dcda7b3aca259f96dd", + "rev": "47f01a1d9f7dc5cc5246c0c228e5cf5f5ba44399", "type": "github" } }, @@ -1415,24 +1467,24 @@ "type": "github" } }, - "old-ghc-nix_2": { + "optparse-applicative": { "flake": false, "locked": { - "lastModified": 1631092763, - "narHash": "sha256-sIKgO+z7tj4lw3u6oBZxqIhDrzSkvpHtv0Kki+lh9Fg=", - "owner": "angerman", - "repo": "old-ghc-nix", - "rev": "af48a7a7353e418119b6dfe3cd1463a657f342b8", + "lastModified": 1628901899, + "narHash": "sha256-uQx+SEYsCH7JcG3xAT0eJck9yq3y0cvx49bvItLLer8=", + "owner": "input-output-hk", + "repo": "optparse-applicative", + "rev": "7497a29cb998721a9068d5725d49461f2bba0e7a", "type": "github" }, "original": { - "owner": "angerman", - "ref": "master", - "repo": "old-ghc-nix", + "owner": "input-output-hk", + "repo": "optparse-applicative", + "rev": "7497a29cb998721a9068d5725d49461f2bba0e7a", "type": "github" } }, - "optparse-applicative": { + "optparse-applicative_2": { "flake": false, "locked": { "lastModified": 1628901899, @@ -1449,7 +1501,7 @@ "type": "github" } }, - "optparse-applicative_2": { + "optparse-applicative_3": { "flake": false, "locked": { "lastModified": 1628901899, @@ -1500,6 +1552,59 @@ "type": "github" } }, + "ouroboros-network_3": { + "flake": false, + "locked": { + "lastModified": 1654820431, + "narHash": "sha256-bmLD5sFsiny/eRv6MHrqGvo6I4QG9pO0psiHWGFZqro=", + "owner": "input-output-hk", + "repo": "ouroboros-network", + "rev": "a65c29b6a85e90d430c7f58d362b7eb097fd4949", + "type": "github" + }, + "original": { + "owner": "input-output-hk", + "repo": "ouroboros-network", + "rev": "a65c29b6a85e90d430c7f58d362b7eb097fd4949", + "type": "github" + } + }, + "plutip": { + "inputs": { + "bot-plutus-interface": "bot-plutus-interface", + "flake-compat": "flake-compat_5", + "haskell-nix": [ + "plutip", + "bot-plutus-interface", + "haskell-nix" + ], + "iohk-nix": [ + "plutip", + "bot-plutus-interface", + "iohk-nix" + ], + "nixpkgs": [ + "plutip", + "bot-plutus-interface", + "haskell-nix", + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1658235706, + "narHash": "sha256-T5E4Qz/6ZlxVVJer6j5xFKvY4XGJPMbZsVxac8RbZ9w=", + "owner": "mlabs-haskell", + "repo": "plutip", + "rev": "d24b98162bcbcbfb4ca403ee62fdb890f2059f47", + "type": "github" + }, + "original": { + "owner": "mlabs-haskell", + "repo": "plutip", + "rev": "d24b98162bcbcbfb4ca403ee62fdb890f2059f47", + "type": "github" + } + }, "plutus": { "flake": false, "locked": { @@ -1517,7 +1622,41 @@ "type": "github" } }, + "plutus-apps": { + "flake": false, + "locked": { + "lastModified": 1658170029, + "narHash": "sha256-/G3CrE2aXrhytDGOqhifmIT31gJcvI3FjuntztOi8DY=", + "owner": "gege251", + "repo": "plutus-apps", + "rev": "62342808fa7422ebea3233a7e031d3aa00c04672", + "type": "github" + }, + "original": { + "owner": "gege251", + "repo": "plutus-apps", + "rev": "62342808fa7422ebea3233a7e031d3aa00c04672", + "type": "github" + } + }, "plutus_2": { + "flake": false, + "locked": { + "lastModified": 1656585904, + "narHash": "sha256-ATwDR5LX2RN9YfoPhTxV7REvFoJnM4x/CN9XZVZlalg=", + "owner": "input-output-hk", + "repo": "plutus", + "rev": "69ab98c384703172f898eb5bcad1078ded521426", + "type": "github" + }, + "original": { + "owner": "input-output-hk", + "repo": "plutus", + "rev": "69ab98c384703172f898eb5bcad1078ded521426", + "type": "github" + } + }, + "plutus_3": { "flake": false, "locked": { "lastModified": 1632818067, @@ -1535,6 +1674,23 @@ } }, "purescript-bridge": { + "flake": false, + "locked": { + "lastModified": 1642802224, + "narHash": "sha256-/SbnmXrB9Y2rrPd6E79Iu5RDaKAKozIl685HQ4XdQTU=", + "owner": "input-output-hk", + "repo": "purescript-bridge", + "rev": "47a1f11825a0f9445e0f98792f79172efef66c00", + "type": "github" + }, + "original": { + "owner": "input-output-hk", + "repo": "purescript-bridge", + "rev": "47a1f11825a0f9445e0f98792f79172efef66c00", + "type": "github" + } + }, + "purescript-bridge_2": { "flake": false, "locked": { "lastModified": 1612544328, @@ -1551,6 +1707,23 @@ "type": "github" } }, + "quickcheck-dynamic": { + "flake": false, + "locked": { + "lastModified": 1656927450, + "narHash": "sha256-TioJQASNrQX6B3n2Cv43X2olyT67//CFQqcpvNW7N60=", + "owner": "input-output-hk", + "repo": "quickcheck-dynamic", + "rev": "c272906361471d684440f76c297e29ab760f6a1e", + "type": "github" + }, + "original": { + "owner": "input-output-hk", + "repo": "quickcheck-dynamic", + "rev": "c272906361471d684440f76c297e29ab760f6a1e", + "type": "github" + } + }, "root": { "inputs": { "Win32-network": "Win32-network", @@ -1568,23 +1741,44 @@ "flake-compat": "flake-compat", "flat": "flat", "goblins": "goblins", - "haskell-nix": "haskell-nix", + "haskell-nix": [ + "plutip", + "haskell-nix" + ], "iohk-monitoring-framework": "iohk-monitoring-framework", "iohk-nix": "iohk-nix", "nixpkgs": [ - "haskell-nix", - "nixpkgs-unstable" + "plutip", + "nixpkgs" ], "ogmios": "ogmios", "ogmios-datum-cache": "ogmios-datum-cache", "optparse-applicative": "optparse-applicative_2", "ouroboros-network": "ouroboros-network_2", - "plutus": "plutus_2", - "purescript-bridge": "purescript-bridge", - "servant-purescript": "servant-purescript" + "plutip": "plutip", + "plutus": "plutus_3", + "purescript-bridge": "purescript-bridge_2", + "servant-purescript": "servant-purescript_2" } }, "servant-purescript": { + "flake": false, + "locked": { + "lastModified": 1642798070, + "narHash": "sha256-DH9ISydu5gxvN4xBuoXVv1OhYCaqGOtzWlACdJ0H64I=", + "owner": "input-output-hk", + "repo": "servant-purescript", + "rev": "44e7cacf109f84984cd99cd3faf185d161826963", + "type": "github" + }, + "original": { + "owner": "input-output-hk", + "repo": "servant-purescript", + "rev": "44e7cacf109f84984cd99cd3faf185d161826963", + "type": "github" + } + }, + "servant-purescript_2": { "flake": false, "locked": { "lastModified": 1612956215, @@ -1604,11 +1798,11 @@ "stackage": { "flake": false, "locked": { - "lastModified": 1650158092, - "narHash": "sha256-uQ/TEFcce0bKmYcoBziDhYYzCDmhPsjC5WgsJjpd9wA=", + "lastModified": 1653355076, + "narHash": "sha256-mQdOgAyFkLUJBPrVDZmZQ2JRtgHKOQkil//SDdcjP1U=", "owner": "input-output-hk", "repo": "stackage.nix", - "rev": "adc7f942e756b382a7a833520ebef6dfc859af8e", + "rev": "71b16ca68d6acd639121db43238896357fe53f54", "type": "github" }, "original": { @@ -1617,23 +1811,24 @@ "type": "github" } }, - "stackage_2": { + "typed-protocols": { "flake": false, "locked": { - "lastModified": 1650158092, - "narHash": "sha256-uQ/TEFcce0bKmYcoBziDhYYzCDmhPsjC5WgsJjpd9wA=", + "lastModified": 1653046676, + "narHash": "sha256-5Wof5yTKb12EPY6B8LfapX18xNZZpF+rvhnQ88U6KdM=", "owner": "input-output-hk", - "repo": "stackage.nix", - "rev": "adc7f942e756b382a7a833520ebef6dfc859af8e", + "repo": "typed-protocols", + "rev": "181601bc3d9e9d21a671ce01e0b481348b3ca104", "type": "github" }, "original": { "owner": "input-output-hk", - "repo": "stackage.nix", + "repo": "typed-protocols", + "rev": "181601bc3d9e9d21a671ce01e0b481348b3ca104", "type": "github" } }, - "typed-protocols": { + "typed-protocols_2": { "flake": false, "locked": { "lastModified": 1653046676, diff --git a/flake.nix b/flake.nix index 3865ccecd1..6d3cb36176 100644 --- a/flake.nix +++ b/flake.nix @@ -8,9 +8,16 @@ }; # for the purescript project - ogmios.url = "github:mlabs-haskell/ogmios/e406801eaeb32b28cd84357596ca1512bff27741"; - ogmios-datum-cache.url = "github:mlabs-haskell/ogmios-datum-cache/bf76a74fa9e94d97310087dcda7b3aca259f96dd"; + ogmios = { + url = "github:mlabs-haskell/ogmios/e406801eaeb32b28cd84357596ca1512bff27741"; + inputs = { + haskell-nix.follows = "haskell-nix"; + nixpkgs.follows = "nixpkgs"; + }; + }; + plutip.url = "github:mlabs-haskell/plutip/d24b98162bcbcbfb4ca403ee62fdb890f2059f47"; + ogmios-datum-cache.url = "github:mlabs-haskell/ogmios-datum-cache/47f01a1d9f7dc5cc5246c0c228e5cf5f5ba44399"; # Repository with network parameters cardano-configurations = { # Override with "path:/path/to/cardano-configurations"; @@ -18,14 +25,14 @@ flake = false; }; easy-purescript-nix = { - url = "github:justinwoo/easy-purescript-nix"; + url = "github:justinwoo/easy-purescript-nix/d56c436a66ec2a8a93b309c83693cef1507dca7a"; flake = false; }; # for the haskell server iohk-nix.url = "github:input-output-hk/iohk-nix"; - haskell-nix.url = "github:mlabs-haskell/haskell.nix?ref=master"; - nixpkgs.follows = "haskell-nix/nixpkgs-unstable"; + haskell-nix.follows = "plutip/haskell-nix"; + nixpkgs.follows = "plutip/nixpkgs"; cardano-addresses = { url = "github:input-output-hk/cardano-addresses/d2f86caa085402a953920c6714a0de6a50b655ec"; @@ -131,75 +138,35 @@ , ... }@inputs: let - defaultSystems = [ + supportedSystems = [ "x86_64-linux" "x86_64-darwin" "aarch64-linux" "aarch64-darwin" ]; - perSystem = nixpkgs.lib.genAttrs defaultSystems; - overlay = with inputs; (final: prev: - let - inherit (prev) system; - in - { - easy-ps = - import inputs.easy-purescript-nix { pkgs = final; }; - ogmios-datum-cache = - inputs.ogmios-datum-cache.defaultPackage.${system}; - ogmios = ogmios.packages.${system}."ogmios:exe:ogmios"; - ogmios-fixtures = ogmios; - purescriptProject = import ./nix { pkgs = final; inherit system; }; - buildCtlRuntime = buildCtlRuntime final; - launchCtlRuntime = launchCtlRuntime final; - inherit cardano-configurations; - }); + + perSystem = nixpkgs.lib.genAttrs supportedSystems; mkNixpkgsFor = system: import nixpkgs { overlays = [ haskell-nix.overlay iohk-nix.overlays.crypto - overlay + self.overlays.purescript + self.overlays.runtime + (_: _: { + ogmios-fixtures = inputs.ogmios; + }) ]; inherit (haskell-nix) config; inherit system; }; + + inherit (import ./nix/runtime.nix { inherit inputs; }) + buildCtlRuntime launchCtlRuntime; + allNixpkgs = perSystem mkNixpkgsFor; - nixpkgsFor = system: allNixpkgs.${system}; - defaultConfig = final: with final; { - inherit (inputs) cardano-configurations; - network = { - name = "testnet"; - magic = 1097911063; # use `null` for mainnet - }; - node = { port = 3001; }; - ogmios = { port = 1337; }; - ctlServer = { port = 8081; }; - postgres = { - # User-facing port on host machine. - # Can be set to null in order to not bind postgres port to host. - # Postgres will always be accessible via `postgres:5432` from - # containers. - port = 5432; - user = "ctxlib"; - password = "ctxlib"; - db = "ctxlib"; - }; - datumCache = { - port = 9999; - controlApiToken = ""; - blockFetcher = { - firstBlock = { - slot = 61625527; - id = "3afd8895c7b270f8250b744ec8d2b3c53ee2859c9d5711d906c47fe51b800988"; - }; - autoStart = true; - startFromLast = false; - filter = builtins.toJSON { const = true; }; - }; - }; - }; + nixpkgsFor = system: allNixpkgs.${system}; buildOgmiosFixtures = pkgs: pkgs.runCommand "ogmios-fixtures" { @@ -228,162 +195,6 @@ cp -rT ogmios $out ''; - buildCtlRuntime = pkgs: extraConfig: { ... }: - let - inherit (builtins) toString; - config = with pkgs.lib; - fix (final: recursiveUpdate - (defaultConfig final) - (if isFunction extraConfig then extraConfig final else extraConfig)); - nodeDbVol = "node-${config.network.name}-db"; - nodeIpcVol = "node-${config.network.name}-ipc"; - nodeSocketPath = "/ipc/node.socket"; - serverName = "ctl-server:exe:ctl-server"; - server = self.packages.${pkgs.system}."${serverName}"; - bindPort = port: "${toString port}:${toString port}"; - in - with config; - { - docker-compose.raw = { - volumes = { - "${nodeDbVol}" = { }; - "${nodeIpcVol}" = { }; - }; - }; - services = { - cardano-node = { - service = { - image = "inputoutput/cardano-node:1.35.1"; - ports = [ (bindPort node.port) ]; - volumes = [ - "${config.cardano-configurations}/network/${config.network.name}/cardano-node:/config" - "${config.cardano-configurations}/network/${config.network.name}/genesis:/genesis" - "${nodeDbVol}:/data" - "${nodeIpcVol}:/ipc" - ]; - command = [ - "run" - "--config" - "/config/config.json" - "--database-path" - "/data/db" - "--socket-path" - "${nodeSocketPath}" - "--topology" - "/config/topology.json" - ]; - }; - }; - ogmios = { - service = { - useHostStore = true; - ports = [ (bindPort ogmios.port) ]; - volumes = [ - "${config.cardano-configurations}/network/${config.network.name}:/config" - "${nodeIpcVol}:/ipc" - ]; - command = [ - "${pkgs.bash}/bin/sh" - "-c" - '' - ${pkgs.ogmios}/bin/ogmios \ - --host ogmios \ - --port ${toString ogmios.port} \ - --node-socket /ipc/node.socket \ - --node-config /config/cardano-node/config.json - '' - ]; - }; - }; - ctl-server = { - service = { - useHostStore = true; - ports = [ (bindPort ctlServer.port) ]; - depends_on = [ "ogmios" ]; - volumes = [ "${nodeIpcVol}:/ipc" ]; - command = [ - "${pkgs.bash}/bin/sh" - "-c" - '' - ${server}/bin/ctl-server --port ${toString ctlServer.port} - '' - ]; - }; - }; - postgres = { - service = { - image = "postgres:13"; - ports = - if postgres.port == null - then [ ] - else [ "${toString postgres.port}:5432" ]; - environment = { - POSTGRES_USER = "${postgres.user}"; - POSTGRES_PASSWORD = "${postgres.password}"; - POSTGRES_DB = "${postgres.db}"; - }; - }; - }; - ogmios-datum-cache = - let - filter = nixpkgs.lib.strings.replaceStrings - [ "\"" "\\" ] [ "\\\"" "\\\\" ] - datumCache.blockFetcher.filter; - in - { - service = { - useHostStore = true; - ports = [ (bindPort datumCache.port) ]; - restart = "on-failure"; - depends_on = [ "postgres" "ogmios" ]; - command = [ - "${pkgs.bash}/bin/sh" - "-c" - '' - ${pkgs.ogmios-datum-cache}/bin/ogmios-datum-cache \ - --log-level warn \ - --use-latest \ - --server-api "${toString datumCache.controlApiToken}" \ - --server-port ${toString datumCache.port} \ - --ogmios-address ogmios \ - --ogmios-port ${toString ogmios.port} \ - --db-port 5432 \ - --db-host postgres \ - --db-user "${postgres.user}" \ - --db-name "${postgres.db}" \ - --db-password "${postgres.password}" \ - --block-slot ${toString datumCache.blockFetcher.firstBlock.slot} \ - --block-hash "${datumCache.blockFetcher.firstBlock.id}" \ - --block-filter "${filter}" - '' - ]; - }; - }; - }; - }; - - # Makes a set compatible with flake `apps` to launch all runtime services - launchCtlRuntime = pkgs: config: - let - binPath = "ctl-runtime"; - prebuilt = (pkgs.arion.build { - inherit pkgs; - modules = [ (buildCtlRuntime pkgs config) ]; - }).outPath; - script = pkgs.writeShellApplication { - name = binPath; - runtimeInputs = [ pkgs.arion pkgs.docker ]; - text = - '' - ${pkgs.arion}/bin/arion --prebuilt-file ${prebuilt} up - ''; - }; - in - { - type = "app"; - program = "${script}/bin/${binPath}"; - }; - psProjectFor = pkgs: let projectName = "cardano-transaction-lib"; @@ -408,14 +219,17 @@ shellHook = exportOgmiosFixtures; packageLockOnly = true; packages = with pkgs; [ - ogmios - ogmios-datum-cache - nixpkgs-fmt - fd arion + ctl-server + fd haskellPackages.fourmolu - nodePackages.prettier + nixpkgs-fmt nodePackages.eslint + nodePackages.prettier + ogmios + ogmios-datum-cache + plutip-server + postgresql ]; }; }; @@ -425,13 +239,10 @@ ''; in rec { - defaultPackage = packages.ctl-example-bundle-web; - packages = { ctl-example-bundle-web = project.bundlePursProject { main = "Examples.Pkh2Pkh"; entrypoint = "examples/index.js"; - htmlTemplate = "examples/index.html"; }; ctl-runtime = pkgs.arion.build { @@ -445,9 +256,14 @@ }; checks = { + ctl-plutip-test = project.runPlutipTest { + name = "ctl-plutip-test"; + testMain = "Test.Plutip"; + env = { OGMIOS_FIXTURES = "${ogmiosFixtures}"; }; + }; ctl-unit-test = project.runPursTest { name = "ctl-unit-test"; - testMain = "Test.Unit"; + testMain = "Ctl.Test.Unit"; env = { OGMIOS_FIXTURES = "${ogmiosFixtures}"; }; }; }; @@ -455,27 +271,9 @@ devShell = project.devShell; apps = { - docs = - let - binPath = "docs-server"; - builtDocs = packages.docs; - script = pkgs.writeShellApplication { - name = binPath; - runtimeInputs = [ - pkgs.nodejs-14_x - pkgs.nodePackages.http-server - ]; - text = - '' - ${pkgs.nodePackages.http-server}/bin/http-server \ - ${builtDocs}/generated-docs/html - ''; - }; - in - { - type = "app"; - program = "${script}/bin/${binPath}"; - }; + docs = project.launchSearchablePursDocs { + builtDocs = packages.docs; + }; }; }; @@ -486,17 +284,48 @@ }; in { - inherit overlay; + overlay = builtins.trace + ( + "warning: `cardano-transaction-lib.overlay` is deprecated and will be" + + " removed in the next release. Please use" + + " `cardano-transaction-lib.overlays.{runtime, purescript}`" + + " directly instead" + ) + (final: prev: + (self.overlays.purescript final prev) + // (self.overlays.runtime final prev) + ); + + overlays = with inputs; { + purescript = final: prev: { + easy-ps = import inputs.easy-purescript-nix { pkgs = final; }; + purescriptProject = import ./nix { pkgs = final; }; + }; + runtime = final: prev: + let + inherit (prev) system; + in + { + plutip-server = + inputs.plutip.packages.${system}."plutip:exe:plutip-server"; + ctl-server = + (hsProjectFor final).hsPkgs.ctl-server.components.exes.ctl-server; + ogmios-datum-cache = + inputs.ogmios-datum-cache.defaultPackage.${system}; + ogmios = ogmios.packages.${system}."ogmios:exe:ogmios"; + buildCtlRuntime = buildCtlRuntime final; + launchCtlRuntime = launchCtlRuntime final; + inherit cardano-configurations; + }; + }; # flake from haskell.nix project hsFlake = perSystem (system: (hsProjectFor (nixpkgsFor system)).flake { }); - devShell = perSystem (system: self.devShells.${system}.ctl); - devShells = perSystem (system: { # This is the default `devShell` and can be run without specifying # it (i.e. `nix develop`) - ctl = (psProjectFor (nixpkgsFor system)).devShell; + default = (psProjectFor (nixpkgsFor system)).devShell; # It might be a good idea to keep this as a separate shell; if you're # working on the PS frontend, it doesn't make a lot of sense to pull # in all of the Haskell dependencies @@ -517,8 +346,13 @@ (psProjectFor pkgs).apps // { inherit (self.hsFlake.${system}.apps) "ctl-server:exe:ctl-server"; ctl-runtime = pkgs.launchCtlRuntime { }; + default = self.apps.${system}.ctl-runtime; }); + # TODO + # Add a check that attempts to verify if the scaffolding template is + # reasonably up-to-date. See: + # https://github.com/Plutonomicon/cardano-transaction-lib/issues/839 checks = perSystem (system: let pkgs = nixpkgsFor system; @@ -572,9 +406,28 @@ '' ); - defaultPackage = perSystem (system: - (psProjectFor (nixpkgsFor system)).defaultPackage - ); + templates = { + default = self.templates.ctl-scaffold; + ctl-scaffold = { + path = ./templates/ctl-scaffold; + description = "A minimal CTL-based scaffold project"; + welcomeText = '' + Welcome to your new CTL project! + + To enter the Nix environment and start working on it, run `nix develop` + + Please also see + - Our documentation at https://github.com/Plutonomicon/cardano-transaction-lib/tree/develop/doc + - Our Pursuit docs at https://plutonomicon.github.io/cardano-transaction-lib/ + - Our Discord server https://discord.gg/c8kZWxzJ + + If you encounter problems and/or want to report a bug, you can open + an issue at https://github.com/Plutonomicon/cardano-transaction-lib/issues. + Please search for existing issues before! + + ''; + }; + }; hydraJobs = perSystem (system: self.checks.${system} diff --git a/nix/default.nix b/nix/default.nix index 3789c3c4d9..99c4107c82 100644 --- a/nix/default.nix +++ b/nix/default.nix @@ -1,4 +1,4 @@ -{ pkgs, system }: +{ pkgs }: { src # The name of the project, used to generate derivation names , projectName @@ -23,6 +23,8 @@ , ... }: let + inherit (pkgs) system; + purs = pkgs.easy-ps.purs-0_14_5; spagoPkgs = import spagoPackages { inherit pkgs; }; @@ -57,14 +59,30 @@ let projectNodeModules = mkNodeModules { }; + # Constructs a development environment containing various tools to work on + # Purescript projects. The resulting derivation can be used as a `devShell` in + # your flake outputs + # + # All arguments are optional shellFor = - { packages ? [ ] + { + # Extra packages to include in the shell environment + packages ? [ ] + # Passed through to `pkgs.mkShell.inputsFrom` , inputsFrom ? [ ] + # Passed through to `pkgs.mkShell.shellHook` , shellHook ? "" + # One of `purs-tidy` or `purty` to format Purescript sources , formatter ? "purs-tidy" + # Whether or not to include `purescript-language-server` , pursls ? true + # Generated `node_modules` in the Nix store. Can be passed to have better + # control over individual project components , nodeModules ? projectNodeModules + # If `true`, `npm i` will only write to your `package-lock.json` instead + # of installing to a local `node_modules` , packageLockOnly ? false + , withChromium ? false }: assert pkgs.lib.assertOneOf "formatter" formatter [ "purs-tidy" "purty" ]; pkgs.mkShell { @@ -78,9 +96,13 @@ let pkgs.easy-ps.psa pkgs.easy-ps.spago2nix pkgs.nodePackages.node2nix + pkgs.unzip ] ++ pkgs.lib.lists.optional pursls - pkgs.easy-ps.purescript-language-server; + pkgs.easy-ps.purescript-language-server + ++ pkgs.lib.lists.optional + withChromium + pkgs.chromium; inherit packages inputsFrom; shellHook = '' export NODE_PATH="${nodeModules}/lib/node_modules" @@ -90,8 +112,16 @@ let + shellHook; }; + # Compiles your Purescript project and copies the `output` directory into the + # Nix store. Also copies the local sources to be made available later as `purs` + # does not include any external files to its `output` (if we attempted to refer + # to absolute paths from the project-wide `src` argument, they would be wrong) buildPursProject = - { name ? projectName + { + # Can be used to override the name given to the resulting derivation + name ? projectName + # Generated `node_modules` in the Nix store. Can be passed to have better + # control over individual project components , nodeModules ? projectNodeModules , ... }: @@ -126,77 +156,120 @@ let --censor-lib --is-lib=.spago ${spagoGlobs} \ --censor-codes=${builtins.concatStringsSep "," censorCodes} "./**/*.purs" ''; + # We also need to copy all of `src` here, since compiled modules in `output` + # might refer to paths that will point to nothing if we use `src` directly + # in other derivations (e.g. when using `fs.readFileSync` inside an FFI + # module) installPhase = '' mkdir $out mv output $out/ + cp -r $src/* $out/ ''; }; project = buildPursProject { }; + # Runs a test written in Purescript using NodeJS. runPursTest = - { testMain ? "Test.Main" + { + # The name of the main Purescript module + testMain ? "Test.Main" + # Can be used to override the name of the resulting derivation , name ? "${projectName}-check" + # Generated `node_modules` in the Nix store. Can be passed to have better + # control over individual project components , nodeModules ? projectNodeModules + # Additional variables to pass to the test environment , env ? { } + # Passed through to the `buildInputs` of the derivation. Use this to add + # additional packages to the test environment + , buildInputs ? [ ] , ... }: pkgs.runCommand "${name}" - ({ - buildInputs = [ project nodeModules ]; - NODE_PATH = "${nodeModules}/lib/node_modules"; - } // env) + ( + { + buildInputs = [ project nodeModules ] ++ buildInputs; + NODE_PATH = "${nodeModules}/lib/node_modules"; + } // env + ) # spago will attempt to download things, which will fail in the # sandbox, so we can just use node instead # (idea taken from `plutus-playground-client`) '' - cd ${src} - ${nodejs}/bin/node -e 'require("${project}/output/${testMain}").main()' + cd ${project} + ${nodejs}/bin/node -e 'require("./output/${testMain}").main()' touch $out ''; + # Runs a test using Plutip. Takes the same arguments as `runPursTest` + # + # NOTE: You *must* either use CTL's `overlays.runtime` or otherwise make the + # the following required `buildInputs` available in your own package set: + # + # - `ogmios` + # - `ogmios-datum-cache` + # - `plutip-server` + # - `ctl-server` + # + runPlutipTest = args: runPursTest ( + { + buildInputs = with pkgs; [ + postgresql + ogmios + ogmios-datum-cache + plutip-server + ctl-server + ]; + } // args + ); + + # Bundles a Purescript project using Webpack, typically for the browser bundlePursProject = - { name ? "${projectName}-bundle-" + + { + # Can be used to override the name given to the resulting derivation + name ? "${projectName}-bundle-" + (if browserRuntime then "web" else "nodejs") + # The Webpack `entrypoint` , entrypoint ? "index.js" - , htmlTemplate ? "index.html" + # The main Purescript module , main ? "Main" + # If this bundle is being produced for a browser environment or not , browserRuntime ? true + # Path to the Webpack config to use , webpackConfig ? "webpack.config.js" + # The name of the bundled JS module that `spago bundle-module` will produce , bundledModuleName ? "output.js" + # Generated `node_modules` in the Nix store. Can be passed to have better + # control over individual project components , nodeModules ? projectNodeModules , ... - }: pkgs.stdenv.mkDerivation { - inherit name src; - buildInputs = [ - nodejs - nodeModules - project - ]; - nativeBuildInputs = [ - purs - pkgs.easy-ps.spago - ]; - buildPhase = '' + }: pkgs.runCommand "${name}" + { + buildInputs = [ + nodejs + nodeModules + project + ]; + nativeBuildInputs = [ + purs + pkgs.easy-ps.spago + ]; + } + '' export HOME="$TMP" export NODE_PATH="${nodeModules}/lib/node_modules" export PATH="${nodeModules}/bin:$PATH" ${pkgs.lib.optionalString browserRuntime "export BROWSER_RUNTIME=1"} - cp -r ${project}/output . + cp -r ${project}/* . chmod -R +rwx . spago bundle-module --no-install --no-build -m "${main}" \ --to ${bundledModuleName} - cp $src/${entrypoint} . - cp $src/${htmlTemplate} . - cp $src/${webpackConfig} . mkdir ./dist webpack --mode=production -c ${webpackConfig} -o ./dist \ --entry ./${entrypoint} - ''; - installPhase = '' mkdir $out mv dist $out ''; - }; pursDocsSearchNpm = let @@ -253,28 +326,79 @@ let ''; }); - buildSearchablePursDocs = { packageName, ... }: - pkgs.stdenv.mkDerivation { - name = "${projectName}-searchable-docs"; - dontUnpack = true; - buildInputs = [ spagoPkgs.installSpagoStyle ]; - buildPhase = '' + # Builds all of the documentation for your Purescript project (including deps) + # and creates a searchable index for them + buildSearchablePursDocs = + { + # Passed to the `--package-name` argument of `purescript-docs-search` + packageName ? projectName + , ... + }: + pkgs.runCommand "${projectName}-searchable-docs" + { + buildInputs = [ spagoPkgs.installSpagoStyle ]; + } + '' export NODE_PATH="${pursDocsSearchNpm.nodeDependencies}/lib/node_modules" export PATH="${pursDocsSearchNpm.nodeDependencies}/bin:$PATH" cp -r ${buildPursDocs { }}/{generated-docs,output} . install-spago-style chmod -R +rwx . purescript-docs-search build-index --package-name ${packageName} - ''; - installPhase = '' mkdir $out cp -r generated-docs $out ''; + + # Creates a flakes-compatible `apps` output to serve a searchable index of all + # project docs (including dependencies) locally. For example + # + # ``` + # apps = perSystem (system: { + # docs = (psProjectFor system).launchSearchablePursDocs { port = 9090; }; + # }); + # ``` + # + # You can then invoke `nix run .#docs` to serve the documentation index locally + # and visit `localhost:9090` to browse them + launchSearchablePursDocs = + { + # If you are already building your docs (e.g. as part of your flake + # `packages`), you can pass them here. Otherwise, `buildSearchablePursDocs` + # will be invoked + builtDocs ? null + # The port to run the local HTTP server on + , port ? 8080 + , ... + }: + let + binPath = "docs-server"; + docs = + if builtDocs == null + then buildSearchablePursDocs { } + else builtDocs; + script = pkgs.writeShellApplication { + name = binPath; + runtimeInputs = [ + pkgs.nodejs-14_x + pkgs.nodePackages.http-server + ]; + text = + '' + ${pkgs.nodePackages.http-server}/bin/http-server \ + --port ${builtins.toString port} ${docs}/generated-docs/html + ''; + }; + in + { + type = "app"; + program = "${script}/bin/${binPath}"; }; in { - inherit buildPursProject runPursTest buildPursDocs bundlePursProject - buildSearchablePursDocs purs nodejs mkNodeModules; + inherit + buildPursProject runPursTest runPlutipTest bundlePursProject + buildPursDocs buildSearchablePursDocs launchSearchablePursDocs + purs nodejs mkNodeModules; devShell = shellFor shell; } diff --git a/nix/runtime.nix b/nix/runtime.nix new file mode 100644 index 0000000000..cc0a4e96c6 --- /dev/null +++ b/nix/runtime.nix @@ -0,0 +1,241 @@ +{ inputs, ... }: +rec { + defaultConfig = final: with final; { + inherit (inputs) cardano-configurations; + network = { + name = "testnet"; + magic = 1097911063; # use `null` for mainnet + }; + node = { + port = 3001; + # the version of the node to use, corresponds to the image version tag, + # i.e. `"inputoutput/cardano-node:${tag}"` + tag = "1.35.2"; + }; + ogmios = { port = 1337; }; + ctlServer = { port = 8081; }; + postgres = { + # User-facing port on host machine. + # Can be set to null in order to not bind postgres port to host. + # Postgres will always be accessible via `postgres:5432` from + # containers. + port = 5432; + user = "ctxlib"; + password = "ctxlib"; + db = "ctxlib"; + }; + datumCache = { + port = 9999; + controlApiToken = ""; + blockFetcher = { + firstBlock = { + slot = 61625527; + id = "3afd8895c7b270f8250b744ec8d2b3c53ee2859c9d5711d906c47fe51b800988"; + }; + autoStart = true; + startFromLast = false; + filter = builtins.toJSON { const = true; }; + }; + }; + # Additional config that will be included in Arion's `docker-compose.raw`. This + # corresponds directly to YAML that would be written in a `docker-compose` file, + # e.g. volumes + extraDockerCompose = { }; + # Additional services to include in the `docker-compose` config that Arion + # produces. + # + # For a docker image: + # + # ``` + # foo = { + # service = { + # image = "bar:foo"; + # command = [ + # "baz" + # "--quux" + # ]; + # }; + # }; + # ``` + # + # For a Nix package: + # + # ``` + # foo = { + # service = { + # useHostStore = true; + # command = [ + # "${pkgs.baz}/bin/baz" + # "--quux" + # ]; + # }; + # }; + # + # ``` + extraServices = { }; + }; + + buildCtlRuntime = pkgs: extraConfig: { ... }: + let + inherit (builtins) toString; + config = with pkgs.lib; + fix (final: + recursiveUpdate + (defaultConfig final) + ( + if isFunction extraConfig + then extraConfig final + else extraConfig + ) + ); + nodeDbVol = "node-${config.network.name}-db"; + nodeIpcVol = "node-${config.network.name}-ipc"; + nodeSocketPath = "/ipc/node.socket"; + server = pkgs.ctl-server; + bindPort = port: "${toString port}:${toString port}"; + defaultServices = with config; { + cardano-node = { + service = { + image = "inputoutput/cardano-node:${node.tag}"; + ports = [ (bindPort node.port) ]; + volumes = [ + "${config.cardano-configurations}/network/${config.network.name}/cardano-node:/config" + "${config.cardano-configurations}/network/${config.network.name}/genesis:/genesis" + "${nodeDbVol}:/data" + "${nodeIpcVol}:/ipc" + ]; + command = [ + "run" + "--config" + "/config/config.json" + "--database-path" + "/data/db" + "--socket-path" + "${nodeSocketPath}" + "--topology" + "/config/topology.json" + ]; + }; + }; + ogmios = { + service = { + useHostStore = true; + ports = [ (bindPort ogmios.port) ]; + volumes = [ + "${config.cardano-configurations}/network/${config.network.name}:/config" + "${nodeIpcVol}:/ipc" + ]; + command = [ + "${pkgs.bash}/bin/sh" + "-c" + '' + ${pkgs.ogmios}/bin/ogmios \ + --host ogmios \ + --port ${toString ogmios.port} \ + --node-socket /ipc/node.socket \ + --node-config /config/cardano-node/config.json + '' + ]; + }; + }; + ctl-server = { + service = { + useHostStore = true; + ports = [ (bindPort ctlServer.port) ]; + depends_on = [ "ogmios" ]; + volumes = [ "${nodeIpcVol}:/ipc" ]; + command = [ + "${pkgs.bash}/bin/sh" + "-c" + '' + ${server}/bin/ctl-server --port ${toString ctlServer.port} + '' + ]; + }; + }; + postgres = { + service = { + image = "postgres:13"; + ports = + if postgres.port == null + then [ ] + else [ "${toString postgres.port}:5432" ]; + environment = { + POSTGRES_USER = "${postgres.user}"; + POSTGRES_PASSWORD = "${postgres.password}"; + POSTGRES_DB = "${postgres.db}"; + }; + }; + }; + ogmios-datum-cache = + let + filter = inputs.nixpkgs.lib.strings.replaceStrings + [ "\"" "\\" ] [ "\\\"" "\\\\" ] + datumCache.blockFetcher.filter; + in + { + service = { + useHostStore = true; + ports = [ (bindPort datumCache.port) ]; + restart = "on-failure"; + depends_on = [ "postgres" "ogmios" ]; + command = [ + "${pkgs.bash}/bin/sh" + "-c" + '' + ${pkgs.ogmios-datum-cache}/bin/ogmios-datum-cache \ + --log-level warn \ + --use-latest \ + --server-api "${toString datumCache.controlApiToken}" \ + --server-port ${toString datumCache.port} \ + --ogmios-address ogmios \ + --ogmios-port ${toString ogmios.port} \ + --db-port 5432 \ + --db-host postgres \ + --db-user "${postgres.user}" \ + --db-name "${postgres.db}" \ + --db-password "${postgres.password}" \ + --block-slot ${toString datumCache.blockFetcher.firstBlock.slot} \ + --block-hash "${datumCache.blockFetcher.firstBlock.id}" \ + --block-filter "${filter}" + '' + ]; + }; + }; + }; + in + { + docker-compose.raw = pkgs.lib.recursiveUpdate + { + volumes = { + "${nodeDbVol}" = { }; + "${nodeIpcVol}" = { }; + }; + } + config.extraDockerCompose; + services = pkgs.lib.recursiveUpdate defaultServices config.extraServices; + }; + + # Makes a set compatible with flake `apps` to launch all runtime services + launchCtlRuntime = pkgs: config: + let + binPath = "ctl-runtime"; + prebuilt = (pkgs.arion.build { + inherit pkgs; + modules = [ (buildCtlRuntime pkgs config) ]; + }).outPath; + script = pkgs.writeShellApplication { + name = binPath; + runtimeInputs = [ pkgs.arion pkgs.docker ]; + text = + '' + ${pkgs.arion}/bin/arion --prebuilt-file ${prebuilt} up + ''; + }; + in + { + type = "app"; + program = "${script}/bin/${binPath}"; + }; + +} diff --git a/package-lock.json b/package-lock.json index 1065cfea16..3c549b6a2f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -11,14 +11,63 @@ "dev": true }, "@emurgo/cardano-serialization-lib-browser": { - "version": "11.0.0-beta.1", - "resolved": "https://registry.npmjs.org/@emurgo/cardano-serialization-lib-browser/-/cardano-serialization-lib-browser-11.0.0-beta.1.tgz", - "integrity": "sha512-y2jxCPQBZIG1WTcNxPT8AVb1KjCUQV5nq0em32m4l4siHCQjA4STVVds1r61sSCyviP6dzrAJo4n5i/rs+zeKQ==" + "version": "11.0.0", + "resolved": "https://registry.npmjs.org/@emurgo/cardano-serialization-lib-browser/-/cardano-serialization-lib-browser-11.0.0.tgz", + "integrity": "sha512-aJHJSUdlXGu1wfr7sETdoE6mv43jdtflW03Jwro3kFQwchLGAOUiA4LfTDOKwgrtxT6lq5fQW46YoHh14PZDyg==" }, "@emurgo/cardano-serialization-lib-nodejs": { - "version": "11.0.0-beta.1", - "resolved": "https://registry.npmjs.org/@emurgo/cardano-serialization-lib-nodejs/-/cardano-serialization-lib-nodejs-11.0.0-beta.1.tgz", - "integrity": "sha512-YCsfZCXDDFGeX8dp0CFOksPEsTYbnJ8YbmwKc+Vm6e6Vv6yNpXRRhcwE0Zm29lGqLQ+gUbMmLZSclKiXAb52fA==" + "version": "11.0.0", + "resolved": "https://registry.npmjs.org/@emurgo/cardano-serialization-lib-nodejs/-/cardano-serialization-lib-nodejs-11.0.0.tgz", + "integrity": "sha512-thm7g+NT9W4Iwor0S7oTewpLoKwGTGVdVWiZsUj3GjNNp+XJQHXffsOy9JyauMHjqaQeIpDLR1QckY5VnCWzhg==" + }, + "@jridgewell/gen-mapping": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.1.tgz", + "integrity": "sha512-GcHwniMlA2z+WFPWuY8lp3fsza0I8xPFMWL5+n8LYyP6PSvPrXf4+n8stDHZY2DM0zy9sVkRDy1jDI4XGzYVqg==", + "dev": true, + "requires": { + "@jridgewell/set-array": "^1.0.0", + "@jridgewell/sourcemap-codec": "^1.4.10", + "@jridgewell/trace-mapping": "^0.3.9" + } + }, + "@jridgewell/resolve-uri": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.0.7.tgz", + "integrity": "sha512-8cXDaBBHOr2pQ7j77Y6Vp5VDT2sIqWyWQ56TjEq4ih/a4iST3dItRe8Q9fp0rrIl9DoKhWQtUQz/YpOxLkXbNA==", + "dev": true + }, + "@jridgewell/set-array": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.1.1.tgz", + "integrity": "sha512-Ct5MqZkLGEXTVmQYbGtx9SVqD2fqwvdubdps5D3djjAkgkKwT918VNOz65pEHFaYTeWcukmJmH5SwsA9Tn2ObQ==", + "dev": true + }, + "@jridgewell/source-map": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.2.tgz", + "integrity": "sha512-m7O9o2uR8k2ObDysZYzdfhb08VuEml5oWGiosa1VdaPZ/A6QyPkAJuwN0Q1lhULOf6B7MtQmHENS743hWtCrgw==", + "dev": true, + "requires": { + "@jridgewell/gen-mapping": "^0.3.0", + "@jridgewell/trace-mapping": "^0.3.9" + } + }, + "@jridgewell/sourcemap-codec": { + "version": "1.4.13", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.13.tgz", + "integrity": "sha512-GryiOJmNcWbovBxTfZSF71V/mXbgcV3MewDe3kIMCLyIh5e7SKAeUZs+rMnJ8jkMolZ/4/VsdBmMrw3l+VdZ3w==", + "dev": true + }, + "@jridgewell/trace-mapping": { + "version": "0.3.13", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.13.tgz", + "integrity": "sha512-o1xbKhp9qnIAoHJSWd6KlCZfqslL4valSF81H8ImioOAxluWYWOpWkpyktY2vnt4tbrX9XYaxovq6cgowaJp2w==", + "dev": true, + "requires": { + "@jridgewell/resolve-uri": "^3.0.3", + "@jridgewell/sourcemap-codec": "^1.4.10" + } }, "@nodelib/fs.scandir": { "version": "2.1.5", @@ -85,9 +134,9 @@ } }, "@types/eslint": { - "version": "8.4.2", - "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-8.4.2.tgz", - "integrity": "sha512-Z1nseZON+GEnFjJc04sv4NSALGjhFwy6K0HXt7qsn5ArfAKtb63dXNJHf+1YW6IpOIYRBGUbu3GwJdj8DGnCjA==", + "version": "8.4.3", + "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-8.4.3.tgz", + "integrity": "sha512-YP1S7YJRMPs+7KZKDb9G63n8YejIwW9BALq7a5j2+H4yl6iOv9CB29edho+cuFRrvmJbbaH2yiVChKLJVysDGw==", "dev": true, "requires": { "@types/estree": "*", @@ -123,9 +172,9 @@ } }, "@types/express-serve-static-core": { - "version": "4.17.28", - "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.17.28.tgz", - "integrity": "sha512-P1BJAEAW3E2DJUlkgq4tOL3RyMunoWXqbSCygWo5ZIWTjUgN1YnaXWW4VWl/oc8vs/XoYibEGBKP0uZyF4AHig==", + "version": "4.17.29", + "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.17.29.tgz", + "integrity": "sha512-uMd++6dMKS32EOuw1Uli3e3BPgdLIXmezcfHv7N4c1s3gkhikBplORPpMq3fuWkxncZN1reb16d5n8yhQ80x7Q==", "dev": true, "requires": { "@types/node": "*", @@ -163,8 +212,7 @@ "@types/node": { "version": "17.0.35", "resolved": "https://registry.npmjs.org/@types/node/-/node-17.0.35.tgz", - "integrity": "sha512-vu1SrqBjbbZ3J6vwY17jBs8Sr/BKA+/a/WtjRG+whKg1iuLFOosq872EXS0eXWILdO36DHQQeku/ZcL6hz2fpg==", - "dev": true + "integrity": "sha512-vu1SrqBjbbZ3J6vwY17jBs8Sr/BKA+/a/WtjRG+whKg1iuLFOosq872EXS0eXWILdO36DHQQeku/ZcL6hz2fpg==" }, "@types/qs": { "version": "6.9.7", @@ -221,6 +269,15 @@ "@types/node": "*" } }, + "@types/yauzl": { + "version": "2.10.0", + "resolved": "https://registry.npmjs.org/@types/yauzl/-/yauzl-2.10.0.tgz", + "integrity": "sha512-Cn6WYCm0tXv8p6k+A8PvbDG763EDpBoTzHdA+Q/MF6H3sapGjCm9NzoaJncJS9tUKSuCoDs9XHxYYsQDgxR6kw==", + "optional": true, + "requires": { + "@types/node": "*" + } + }, "@webassemblyjs/ast": { "version": "1.11.1", "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.11.1.tgz", @@ -368,24 +425,24 @@ } }, "@webpack-cli/configtest": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@webpack-cli/configtest/-/configtest-1.1.1.tgz", - "integrity": "sha512-1FBc1f9G4P/AxMqIgfZgeOTuRnwZMten8E7zap5zgpPInnCrP8D4Q81+4CWIch8i/Nf7nXjP0v6CjjbHOrXhKg==", + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@webpack-cli/configtest/-/configtest-1.2.0.tgz", + "integrity": "sha512-4FB8Tj6xyVkyqjj1OaTqCjXYULB9FMkqQ8yGrZjRDrYh0nOE+7Lhs45WioWQQMV+ceFlE368Ukhe6xdvJM9Egg==", "dev": true }, "@webpack-cli/info": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/@webpack-cli/info/-/info-1.4.1.tgz", - "integrity": "sha512-PKVGmazEq3oAo46Q63tpMr4HipI3OPfP7LiNOEJg963RMgT0rqheag28NCML0o3GIzA3DmxP1ZIAv9oTX1CUIA==", + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@webpack-cli/info/-/info-1.5.0.tgz", + "integrity": "sha512-e8tSXZpw2hPl2uMJY6fsMswaok5FdlGNRTktvFk2sD8RjH0hE2+XistawJx1vmKteh4NmGmNUrp+Tb2w+udPcQ==", "dev": true, "requires": { "envinfo": "^7.7.3" } }, "@webpack-cli/serve": { - "version": "1.6.1", - "resolved": "https://registry.npmjs.org/@webpack-cli/serve/-/serve-1.6.1.tgz", - "integrity": "sha512-gNGTiTrjEVQ0OcVnzsRSqTxaBSr+dmTfm+qJsCDluky8uhdLWep7Gcr62QsAKHTMxjCS/8nEITsmFAhfIx+QSw==", + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/@webpack-cli/serve/-/serve-1.7.0.tgz", + "integrity": "sha512-oxnCNGj88fL+xzV+dacXs44HcDwf1ovs3AuEzvP7mqXw7fQntqIhQ1BRmynh4qEKQSSSRSWVyXRjmTbZIX9V2Q==", "dev": true }, "@xtuc/ieee754": { @@ -422,6 +479,29 @@ "integrity": "sha512-m7VZ3jwz4eK6A4Vtt8Ew1/mNbP24u0FhdyfA7fSvnJR6LMdfOYnmuIrrJAgrYfYJ10F/otaHTtrtrtmHdMNzEw==", "dev": true }, + "agent-base": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", + "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", + "requires": { + "debug": "4" + }, + "dependencies": { + "debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "requires": { + "ms": "2.1.2" + } + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + } + } + }, "aggregate-error": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-3.1.0.tgz", @@ -564,8 +644,7 @@ "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 + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" }, "base64-js": { "version": "1.5.1", @@ -589,6 +668,27 @@ "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==", "dev": true }, + "bl": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz", + "integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==", + "requires": { + "buffer": "^5.5.0", + "inherits": "^2.0.4", + "readable-stream": "^3.4.0" + }, + "dependencies": { + "buffer": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", + "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", + "requires": { + "base64-js": "^1.3.1", + "ieee754": "^1.1.13" + } + } + } + }, "blake2b-wasm": { "version": "2.4.0", "resolved": "https://registry.npmjs.org/blake2b-wasm/-/blake2b-wasm-2.4.0.tgz", @@ -655,7 +755,6 @@ "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" @@ -743,16 +842,15 @@ } }, "browserslist": { - "version": "4.20.3", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.20.3.tgz", - "integrity": "sha512-NBhymBQl1zM0Y5dQT/O+xiLP9/rzOIQdKM/eMJBAq7yBgaB6krIYLGejrwVYnSHZdqjscB1SPuAjHwxjvN6Wdg==", + "version": "4.21.0", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.21.0.tgz", + "integrity": "sha512-UQxE0DIhRB5z/zDz9iA03BOfxaN2+GQdBYH/2WrSIWEUrnpzTPJbhqt+umq6r3acaPRTW1FNTkrcp0PXgtFkvA==", "dev": true, "requires": { - "caniuse-lite": "^1.0.30001332", - "electron-to-chromium": "^1.4.118", - "escalade": "^3.1.1", - "node-releases": "^2.0.3", - "picocolors": "^1.0.0" + "caniuse-lite": "^1.0.30001358", + "electron-to-chromium": "^1.4.164", + "node-releases": "^2.0.5", + "update-browserslist-db": "^1.0.0" } }, "buffer": { @@ -764,6 +862,11 @@ "ieee754": "^1.2.1" } }, + "buffer-crc32": { + "version": "0.2.13", + "resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.13.tgz", + "integrity": "sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ==" + }, "buffer-from": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", @@ -820,9 +923,9 @@ } }, "caniuse-lite": { - "version": "1.0.30001343", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001343.tgz", - "integrity": "sha512-8KeCrAtPMabo/XW14B+R9sZYoClx1n0b+WYgwDKZPtWR3TcdvWzdSy7mPyFEmR5WU1St9v1PW6sdO5dkFOEzfA==", + "version": "1.0.30001358", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001358.tgz", + "integrity": "sha512-hvp8PSRymk85R20bsDra7ZTCpSVGN/PAz9pSAjPSjKC+rNmnUk5vCRgJwiTT/O4feQ/yu/drvZYpKxxhbFuChw==", "dev": true }, "chokidar": { @@ -841,6 +944,11 @@ "readdirp": "~3.6.0" } }, + "chownr": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz", + "integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==" + }, "chrome-trace-event": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/chrome-trace-event/-/chrome-trace-event-1.0.3.tgz", @@ -883,9 +991,9 @@ } }, "colorette": { - "version": "2.0.16", - "resolved": "https://registry.npmjs.org/colorette/-/colorette-2.0.16.tgz", - "integrity": "sha512-hUewv7oMjCp+wkBv5Rm0v87eJhq4woh5rSR+42YSQJKecCqgIqNkZ6lAlQms/BwHPJA5NKMRlpxPRv0n8HQW6g==", + "version": "2.0.19", + "resolved": "https://registry.npmjs.org/colorette/-/colorette-2.0.19.tgz", + "integrity": "sha512-3tlv/dIP7FWvj3BsbHrGLJ6l/oKh1O3TcgBqMn+yyCagOxc23fyzDS6HypQbgxWbkpDnf52p1LuR4eWDQ/K9WQ==", "dev": true }, "commander": { @@ -929,8 +1037,7 @@ "concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", - "dev": true + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==" }, "connect-history-api-fallback": { "version": "1.6.0", @@ -1022,6 +1129,14 @@ "sha.js": "^2.4.8" } }, + "cross-fetch": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/cross-fetch/-/cross-fetch-3.1.5.tgz", + "integrity": "sha512-lvb1SBsI0Z7GDwmuid+mU3kWVBwTVUbe7S0H52yaaAdQOXq2YktTCZdlAcNKFzE6QtRz0snpw9bNiPeOIkkQvw==", + "requires": { + "node-fetch": "2.6.7" + } + }, "cross-spawn": { "version": "7.0.3", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", @@ -1160,6 +1275,11 @@ "integrity": "sha512-T0NIuQpnTvFDATNuHN5roPwSBG83rFsuO+MXXH9/3N1eFbn4wcPjttvjMLEPWJ0RGUYgQE7cGgS3tNxbqCGM7g==", "dev": true }, + "devtools-protocol": { + "version": "0.0.1011705", + "resolved": "https://registry.npmjs.org/devtools-protocol/-/devtools-protocol-0.0.1011705.tgz", + "integrity": "sha512-OKvTvu9n3swmgYshvsyVHYX0+aPzCoYUnyXUacfQMmFtBtBKewV/gT4I9jkAbpTqtTi2E4S9MXLlvzBDUlqg0Q==" + }, "diffie-hellman": { "version": "5.0.3", "resolved": "https://registry.npmjs.org/diffie-hellman/-/diffie-hellman-5.0.3.tgz", @@ -1189,7 +1309,7 @@ "dns-equal": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/dns-equal/-/dns-equal-1.0.0.tgz", - "integrity": "sha1-s55/HabrCnW6nBcySzR1PEfgZU0=", + "integrity": "sha512-z+paD6YUQsk+AbGCEM4PrOXSss5gd66QfcVBFTKR/HpFL9jCqikS94HYwKww6fQyO7IxrIIyUu+g0Ka9tUS2Cg==", "dev": true }, "dns-packet": { @@ -1205,7 +1325,7 @@ "dns-txt": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/dns-txt/-/dns-txt-2.0.2.tgz", - "integrity": "sha1-uR2Ab10nGI5Ks+fRB9iBocxGQrY=", + "integrity": "sha512-Ix5PrWjphuSoUXV/Zv5gaFHjnaJtb02F2+Si3Ht9dyJ87+Z/lMmy+dpNHtTGraNK958ndXq2i+GLkWsWHcKaBQ==", "dev": true, "requires": { "buffer-indexof": "^1.0.0" @@ -1275,13 +1395,13 @@ "ee-first": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", - "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=", + "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==", "dev": true }, "electron-to-chromium": { - "version": "1.4.139", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.139.tgz", - "integrity": "sha512-lYxzcUCjWxxVug+A7UxBCUiVr13TCjfZFYJS9Lq1VpU/ErwV4a6zUQo9dfojuGpw/L/x9REGuBl6ICQPGgbs3g==", + "version": "1.4.167", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.167.tgz", + "integrity": "sha512-lPHuHXBwpkr4RcfaZBKm6TKOWG/1N9mVggUpP4fY3l1JIUU2x4fkM8928smYdZ5lF+6KCTAxo1aK9JmqT+X71Q==", "dev": true }, "elliptic": { @@ -1308,9 +1428,17 @@ "encodeurl": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", - "integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=", + "integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==", "dev": true }, + "end-of-stream": { + "version": "1.4.4", + "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", + "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==", + "requires": { + "once": "^1.4.0" + } + }, "enhanced-resolve": { "version": "5.9.3", "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.9.3.tgz", @@ -1382,7 +1510,7 @@ "es6-object-assign": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/es6-object-assign/-/es6-object-assign-1.1.0.tgz", - "integrity": "sha1-wsNYJlYkfDnqEHyx5mUrb58kUjw=" + "integrity": "sha512-MEl9uirslVwqQU369iHNWZXsI8yaZYGg/D65aOgZkeyFJwHYSxilf7rQzXKI7DdDuBPrBXbfk3sl9hJhmd5AUw==" }, "escalade": { "version": "3.1.1", @@ -1393,7 +1521,7 @@ "escape-html": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", - "integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg=", + "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==", "dev": true }, "eslint-scope": { @@ -1432,7 +1560,7 @@ "etag": { "version": "1.8.1", "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", - "integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=", + "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", "dev": true }, "eventemitter3": { @@ -1519,6 +1647,40 @@ } } }, + "extract-zip": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extract-zip/-/extract-zip-2.0.1.tgz", + "integrity": "sha512-GDhU9ntwuKyGXdZBUgTIe+vXnWj0fppUEtMDL0+idd5Sta8TGpHssn/eusA9mrPr9qNDym6SxAYZjNvCn/9RBg==", + "requires": { + "@types/yauzl": "^2.9.1", + "debug": "^4.1.1", + "get-stream": "^5.1.0", + "yauzl": "^2.10.0" + }, + "dependencies": { + "debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "requires": { + "ms": "2.1.2" + } + }, + "get-stream": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz", + "integrity": "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==", + "requires": { + "pump": "^3.0.0" + } + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + } + } + }, "fast-deep-equal": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", @@ -1545,9 +1707,9 @@ "dev": true }, "fastest-levenshtein": { - "version": "1.0.12", - "resolved": "https://registry.npmjs.org/fastest-levenshtein/-/fastest-levenshtein-1.0.12.tgz", - "integrity": "sha512-On2N+BpYJ15xIC974QNVuYGMOlEVt4s0EOI3wwMqOmK1fdDY+FN/zltPV8vosq4ad4c/gJ1KHScUn/6AWIgiow==", + "version": "1.0.14", + "resolved": "https://registry.npmjs.org/fastest-levenshtein/-/fastest-levenshtein-1.0.14.tgz", + "integrity": "sha512-tFfWHjnuUfKE186Tfgr+jtaFc0mZTApEgKDOeyN+FwOqRkO/zK/3h1AiRd8u8CY53owL3CUmGr/oI9p/RdyLTA==", "dev": true }, "fastq": { @@ -1568,6 +1730,14 @@ "websocket-driver": ">=0.5.1" } }, + "fd-slicer": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/fd-slicer/-/fd-slicer-1.1.0.tgz", + "integrity": "sha512-cE1qsB/VwyQozZ+q1dGxR8LBYNZeofhEdUNGSMbQD3Gw2lAzX9Zb3uIU6Ebc/Fmyjo9AWWfnn0AUCHqtevs/8g==", + "requires": { + "pend": "~1.2.0" + } + }, "fill-range": { "version": "7.0.1", "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", @@ -1601,7 +1771,6 @@ "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" @@ -1630,9 +1799,14 @@ "fresh": { "version": "0.5.2", "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", - "integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac=", + "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==", "dev": true }, + "fs-constants": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz", + "integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==" + }, "fs-monkey": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/fs-monkey/-/fs-monkey-1.0.3.tgz", @@ -1642,8 +1816,7 @@ "fs.realpath": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", - "dev": true + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==" }, "fsevents": { "version": "2.3.2", @@ -1702,7 +1875,6 @@ "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", @@ -1821,7 +1993,7 @@ "hmac-drbg": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/hmac-drbg/-/hmac-drbg-1.0.1.tgz", - "integrity": "sha1-0nRXAQJabHdabFRXk+1QL8DGSaE=", + "integrity": "sha512-Tti3gMqLdZfhOQY1Mzf/AanLiqh1WTiJgEj26ZuYQ9fbkLomzGchCws4FyrSd4VkpBfiNhaE1On+lOz894jvXg==", "requires": { "hash.js": "^1.0.3", "minimalistic-assert": "^1.0.0", @@ -1831,7 +2003,7 @@ "hpack.js": { "version": "2.1.6", "resolved": "https://registry.npmjs.org/hpack.js/-/hpack.js-2.1.6.tgz", - "integrity": "sha1-h3dMCUnlE/QuhFdbPEVoH63ioLI=", + "integrity": "sha512-zJxVehUdMGIKsRaNt7apO2Gqp0BdqW5yaiGHXXmbpvxgBYVZnAql+BJb4RO5ad2MgpbZKn5G6nMnegrH1FcNYQ==", "dev": true, "requires": { "inherits": "^2.0.1", @@ -1921,7 +2093,7 @@ "http-deceiver": { "version": "1.2.7", "resolved": "https://registry.npmjs.org/http-deceiver/-/http-deceiver-1.2.7.tgz", - "integrity": "sha1-+nFolEq5pRnTN8sL7HKE3D5yPYc=", + "integrity": "sha512-LmpOGxTfbpgtGVxJrj5k7asXHCgNZp5nLfp+hWc8QQRqtb7fUy6kRY3BO1h9ddF6yIPYUARgxGOwB42DnxIaNw==", "dev": true }, "http-errors": { @@ -1970,7 +2142,31 @@ "https-browserify": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/https-browserify/-/https-browserify-1.0.0.tgz", - "integrity": "sha1-7AbBDgo0wPL68Zn3/X/Hj//QPHM=" + "integrity": "sha512-J+FkSdyD+0mA0N+81tMotaRMfSL9SGi+xpD3T6YApKsc3bGSXJlfXri3VyFOeYkfLRQisDk1W+jIFFKBeUBbBg==" + }, + "https-proxy-agent": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz", + "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==", + "requires": { + "agent-base": "6", + "debug": "4" + }, + "dependencies": { + "debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "requires": { + "ms": "2.1.2" + } + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + } + } }, "human-signals": { "version": "2.1.0", @@ -2017,8 +2213,7 @@ "inflight": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", - "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", - "dev": true, + "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", "requires": { "once": "^1.3.0", "wrappy": "1" @@ -2123,7 +2318,7 @@ "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 }, "is-generator-function": { @@ -2269,19 +2464,19 @@ "isarray": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", + "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", "dev": true }, "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 }, "isobject": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", - "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", + "integrity": "sha512-WhB9zCku7EGTj/HQQRz5aUQEUeoQZH2bWcltRErOpymJ4boYE6wL9Tbr23krRPSZ+C5zqNSrSw+Cc7sZZ4b7vg==", "dev": true }, "jest-worker": { @@ -2328,7 +2523,6 @@ "version": "5.0.0", "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", - "dev": true, "requires": { "p-locate": "^4.1.0" } @@ -2339,12 +2533,6 @@ "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", "dev": true }, - "lodash.sortby": { - "version": "4.7.0", - "resolved": "https://registry.npmjs.org/lodash.sortby/-/lodash.sortby-4.7.0.tgz", - "integrity": "sha1-7dFMgk4sycHgsKG0K7UhBRakJDg=", - "dev": true - }, "lower-case": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/lower-case/-/lower-case-2.0.2.tgz", @@ -2367,22 +2555,22 @@ "media-typer": { "version": "0.3.0", "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", - "integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=", + "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==", "dev": true }, "memfs": { - "version": "3.4.3", - "resolved": "https://registry.npmjs.org/memfs/-/memfs-3.4.3.tgz", - "integrity": "sha512-eivjfi7Ahr6eQTn44nvTnR60e4a1Fs1Via2kCR5lHo/kyNoiMWaXCNJ/GpSd0ilXas2JSOl9B5FTIhflXu0hlg==", + "version": "3.4.6", + "resolved": "https://registry.npmjs.org/memfs/-/memfs-3.4.6.tgz", + "integrity": "sha512-rH9mjopto6Wkr7RFuH9l9dk3qb2XGOcYKr7xMhaYqfzuJqOqhRrcFvfD7JMuPj6SLmPreh5+6eAuv36NFAU+Mw==", "dev": true, "requires": { - "fs-monkey": "1.0.3" + "fs-monkey": "^1.0.3" } }, "merge-descriptors": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", - "integrity": "sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E=", + "integrity": "sha512-cCi6g3/Zr1iqQi6ySbseM1Xvooa98N0w31jzUYrXPX2xqObmFGHJ0tQ5u74H3mVh7wLouTseZyYIq39g8cNp1w==", "dev": true }, "merge-stream": { @@ -2400,7 +2588,7 @@ "methods": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", - "integrity": "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4=", + "integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==", "dev": true }, "micromatch": { @@ -2464,13 +2652,12 @@ "minimalistic-crypto-utils": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz", - "integrity": "sha1-9sAMHAsIIkblxNmd+4x8CDsrWCo=" + "integrity": "sha512-JIYlbt6g8i5jKfJ3xz7rF0LXmv2TkDxBLUkiBeZ7bAx4GnnNMr8xFpGnOxn6GhTEHx3SjRrZEoU+j04prX1ktg==" }, "minimatch": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dev": true, "requires": { "brace-expansion": "^1.1.7" } @@ -2490,10 +2677,15 @@ "minimist": "^1.2.6" } }, + "mkdirp-classic": { + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz", + "integrity": "sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==" + }, "ms": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", "dev": true }, "multicast-dns": { @@ -2509,7 +2701,7 @@ "multicast-dns-service-types": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/multicast-dns-service-types/-/multicast-dns-service-types-1.1.0.tgz", - "integrity": "sha1-iZ8R2WhuXgXLkbNdXw5jt3PPyQE=", + "integrity": "sha512-cnAsSVxIDsYt0v7HmC0hWZFwwXSh+E6PgCrREDuN/EsjgLwA5XRmlMHhSiDPrt6HxY1gTivEa/Zh7GtODoLevQ==", "dev": true }, "nanoassert": { @@ -2539,6 +2731,35 @@ "tslib": "^2.0.3" } }, + "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" + }, + "dependencies": { + "tr46": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", + "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==" + }, + "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==" + }, + "whatwg-url": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", + "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", + "requires": { + "tr46": "~0.0.3", + "webidl-conversions": "^3.0.0" + } + } + } + }, "node-forge": { "version": "1.3.1", "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-1.3.1.tgz", @@ -2665,8 +2886,7 @@ "once": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", - "dev": true, + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", "requires": { "wrappy": "1" } @@ -2694,13 +2914,12 @@ "os-browserify": { "version": "0.3.0", "resolved": "https://registry.npmjs.org/os-browserify/-/os-browserify-0.3.0.tgz", - "integrity": "sha1-hUNzx/XCMVkU/Jv8a9gjj92h7Cc=" + "integrity": "sha512-gjcpUc3clBf9+210TRaDWbf+rZZZEshZ+DlXMRCeAjp0xhTrnQsKHypIy1J3d5hKdUzj69t708EHtU8P6bUn0A==" }, "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" } @@ -2709,7 +2928,6 @@ "version": "4.1.0", "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", - "dev": true, "requires": { "p-limit": "^2.2.0" } @@ -2736,8 +2954,7 @@ "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 + "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==" }, "pako": { "version": "1.0.11", @@ -2790,14 +3007,12 @@ "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 + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==" }, "path-is-absolute": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", - "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", - "dev": true + "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==" }, "path-key": { "version": "3.1.1", @@ -2814,7 +3029,7 @@ "path-to-regexp": { "version": "0.1.7", "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", - "integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w=", + "integrity": "sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ==", "dev": true }, "path-type": { @@ -2835,6 +3050,11 @@ "sha.js": "^2.4.8" } }, + "pend": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz", + "integrity": "sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg==" + }, "picocolors": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", @@ -2851,7 +3071,6 @@ "version": "4.2.0", "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", - "dev": true, "requires": { "find-up": "^4.0.0" } @@ -2897,7 +3116,7 @@ "process": { "version": "0.11.10", "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz", - "integrity": "sha1-czIwDoQBYb2j5podHZGn1LwW8YI=" + "integrity": "sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==" }, "process-nextick-args": { "version": "2.0.1", @@ -2905,6 +3124,11 @@ "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", "dev": true }, + "progress": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz", + "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==" + }, "proxy-addr": { "version": "2.0.7", "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", @@ -2923,6 +3147,11 @@ } } }, + "proxy-from-env": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", + "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==" + }, "public-encrypt": { "version": "4.0.3", "resolved": "https://registry.npmjs.org/public-encrypt/-/public-encrypt-4.0.3.tgz", @@ -2943,11 +3172,59 @@ } } }, + "pump": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", + "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", + "requires": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } + }, "punycode": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==" }, + "puppeteer-core": { + "version": "15.3.2", + "resolved": "https://registry.npmjs.org/puppeteer-core/-/puppeteer-core-15.3.2.tgz", + "integrity": "sha512-Fmca9UzXmJkRrvGBgUmrffGD2BlulUTfsVefV1+vqfNm4PnlZ/U1bfD6X8XQ0nftyyg520tmSKd81yH3Z2tszg==", + "requires": { + "cross-fetch": "3.1.5", + "debug": "4.3.4", + "devtools-protocol": "0.0.1011705", + "extract-zip": "2.0.1", + "https-proxy-agent": "5.0.1", + "pkg-dir": "4.2.0", + "progress": "2.0.3", + "proxy-from-env": "1.1.0", + "rimraf": "3.0.2", + "tar-fs": "2.1.1", + "unbzip2-stream": "1.4.3", + "ws": "8.8.0" + }, + "dependencies": { + "debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "requires": { + "ms": "2.1.2" + } + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + }, + "ws": { + "version": "8.8.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.8.0.tgz", + "integrity": "sha512-JDAgSYQ1ksuwqfChJusw1LSJ8BizJ2e/vVu5Lxjq3YvNJNlROv1ui4i+c/kUUrPheBvQl4c5UbERhTwKa6QBJQ==" + } + } + }, "qs": { "version": "6.10.3", "resolved": "https://registry.npmjs.org/qs/-/qs-6.10.3.tgz", @@ -2960,12 +3237,12 @@ "querystring": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/querystring/-/querystring-0.2.0.tgz", - "integrity": "sha1-sgmEkgO7Jd+CDadW50cAWHhSFiA=" + "integrity": "sha512-X/xY82scca2tau62i9mDyU9K+I+djTMUsvwf7xnUX5GLvVzgJybOJf4Y6o9Zx3oJK/LSXg5tTZBjwzqVPaPO2g==" }, "querystring-es3": { "version": "0.2.1", "resolved": "https://registry.npmjs.org/querystring-es3/-/querystring-es3-0.2.1.tgz", - "integrity": "sha1-nsYfeQSYdXB9aUFFlv2Qek1xHnM=" + "integrity": "sha512-773xhDQnZBMFobEiztv8LIl70ch5MSF/jUQVlhwFyBILqq96anmoctVIYz+ZRp0qbCKATTn6ev02M3r7Ga5vqA==" }, "queue-microtask": { "version": "1.2.3", @@ -3062,7 +3339,7 @@ "relateurl": { "version": "0.2.7", "resolved": "https://registry.npmjs.org/relateurl/-/relateurl-0.2.7.tgz", - "integrity": "sha1-VNvzd+UUQKypCkzSdGANP/LYiKk=", + "integrity": "sha512-G08Dxvm4iDN3MLM0EsP62EDV9IuhXPR6blNz6Utcp7zyV3tr4HVNINt6MpaRWbxoOHT3Q7YN2P+jaHX8vUbgog==", "dev": true }, "renderkid": { @@ -3087,16 +3364,16 @@ "requires-port": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz", - "integrity": "sha1-kl0mAdOaxIXgkc8NpcbmlNw9yv8=", + "integrity": "sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==", "dev": true }, "resolve": { - "version": "1.22.0", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.0.tgz", - "integrity": "sha512-Hhtrw0nLeSrFQ7phPp4OOcVjLPIeMnRlr5mcnVuMe7M/7eBn98A3hmFRLoFo3DLZkivSYwhRUJTyPyWAk56WLw==", + "version": "1.22.1", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.1.tgz", + "integrity": "sha512-nBpuuYuY5jFsli/JIs1oldw6fOQCBioohqWZg/2hiaOybXOft4lonv85uDOKXdf8rhyK159cxU5cDcK/NKk8zw==", "dev": true, "requires": { - "is-core-module": "^2.8.1", + "is-core-module": "^2.9.0", "path-parse": "^1.0.7", "supports-preserve-symlinks-flag": "^1.0.0" } @@ -3132,7 +3409,6 @@ "version": "3.0.2", "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", - "dev": true, "requires": { "glob": "^7.1.3" } @@ -3179,7 +3455,7 @@ "select-hose": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/select-hose/-/select-hose-2.0.0.tgz", - "integrity": "sha1-Yl2GWPhlr0Psliv8N2o3NZpJlMo=", + "integrity": "sha512-mEugaLK+YfkijB4fx0e6kImuJdCIt2LxCRcbEYPqRGCs4F2ogyfZU5IAZRdjCP8JPq2AtdNoC/Dux63d9Kiryg==", "dev": true }, "selfsigned": { @@ -3232,7 +3508,7 @@ "serve-index": { "version": "1.9.1", "resolved": "https://registry.npmjs.org/serve-index/-/serve-index-1.9.1.tgz", - "integrity": "sha1-03aNabHn2C5c4FD/9bRTvqEqkjk=", + "integrity": "sha512-pXHfKNP4qujrtteMrSBb0rc8HJ9Ms/GrXwcUtUtD5s4ewDJI8bT3Cz2zTVRMKtri49pLx2e0Ya8ziP5Ya2pZZw==", "dev": true, "requires": { "accepts": "~1.3.4", @@ -3247,13 +3523,13 @@ "depd": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", - "integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak=", + "integrity": "sha512-7emPTl6Dpo6JRXOXjLRxck+FlLRX5847cLKEn00PLAgc3g2hTZZgr+e4c2v6QpSmLeFP3n5yUo7ft6avBK/5jQ==", "dev": true }, "http-errors": { "version": "1.6.3", "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.6.3.tgz", - "integrity": "sha1-i1VoC7S+KDoLW/TqLjhYC+HZMg0=", + "integrity": "sha512-lks+lVC8dgGyh97jxvxeYTWQFvh4uw4yC12gVl63Cg30sjPX4wuGcdkICVXDAESr6OJGjqGA8Iz5mkeN6zlD7A==", "dev": true, "requires": { "depd": "~1.1.2", @@ -3265,7 +3541,7 @@ "inherits": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", - "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=", + "integrity": "sha512-x00IRNXNy63jwGkJmzPigoySHbaqpNuzKbBOmzK+g2OdZpQ9w+sxCN+VSB3ja7IAge2OP2qpfxTjeNcyjmW1uw==", "dev": true }, "setprototypeof": { @@ -3277,7 +3553,7 @@ "statuses": { "version": "1.5.0", "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", - "integrity": "sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow=", + "integrity": "sha512-OpZ3zP+jT1PI7I8nemJX4AKmAX070ZkYPVWV/AaKTJl+tXCTGyVdC1a4SL8RUQYEwk/f34ZX8UTykN68FwrqAA==", "dev": true } } @@ -3297,7 +3573,7 @@ "setimmediate": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz", - "integrity": "sha1-KQy7Iy4waULX1+qbg3Mqt4VvgoU=" + "integrity": "sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA==" }, "setprototypeof": { "version": "1.2.0", @@ -3538,15 +3814,38 @@ "integrity": "sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==", "dev": true }, + "tar-fs": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-2.1.1.tgz", + "integrity": "sha512-V0r2Y9scmbDRLCNex/+hYzvp/zyYjvFbHPNgVTKfQvVrb6guiE/fxP+XblDNR011utopbkex2nM4dHNV6GDsng==", + "requires": { + "chownr": "^1.1.1", + "mkdirp-classic": "^0.5.2", + "pump": "^3.0.0", + "tar-stream": "^2.1.4" + } + }, + "tar-stream": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-2.2.0.tgz", + "integrity": "sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==", + "requires": { + "bl": "^4.0.3", + "end-of-stream": "^1.4.1", + "fs-constants": "^1.0.0", + "inherits": "^2.0.3", + "readable-stream": "^3.1.1" + } + }, "terser": { - "version": "5.13.1", - "resolved": "https://registry.npmjs.org/terser/-/terser-5.13.1.tgz", - "integrity": "sha512-hn4WKOfwnwbYfe48NgrQjqNOH9jzLqRcIfbYytOXCOv46LBfWr9bDS17MQqOi+BWGD0sJK3Sj5NC/gJjiojaoA==", + "version": "5.14.1", + "resolved": "https://registry.npmjs.org/terser/-/terser-5.14.1.tgz", + "integrity": "sha512-+ahUAE+iheqBTDxXhTisdA8hgvbEG1hHOQ9xmNjeUJSoi6DU/gMrKNcfZjHkyY6Alnuyc+ikYJaxxfHkT3+WuQ==", "dev": true, "requires": { + "@jridgewell/source-map": "^0.3.2", "acorn": "^8.5.0", "commander": "^2.20.0", - "source-map": "~0.8.0-beta.0", "source-map-support": "~0.5.20" }, "dependencies": { @@ -3559,27 +3858,28 @@ "source-map": { "version": "0.8.0-beta.0", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.8.0-beta.0.tgz", - "integrity": "sha512-2ymg6oRBpebeZi9UUNsgQ89bhx01TcTkmNTGnNO88imTmbSgy4nfujrgVEFKWpMTEGA11EDkTt7mqObTPdigIA==", - "dev": true, - "requires": { - "whatwg-url": "^7.0.0" - } + "integrity": "sha512-2ymg6oRBpebeZi9UUNsgQ89bhx01TcTkmNTGnNO88imTmbSgy4nfujrgVEFKWpMTEGA11EDkTt7mqObTPdigIA==" } } }, "terser-webpack-plugin": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-5.3.1.tgz", - "integrity": "sha512-GvlZdT6wPQKbDNW/GDQzZFg/j4vKU96yl2q6mcUkzKOgW4gwf1Z8cZToUCrz31XHlPWH8MVb1r2tFtdDtTGJ7g==", + "version": "5.3.3", + "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-5.3.3.tgz", + "integrity": "sha512-Fx60G5HNYknNTNQnzQ1VePRuu89ZVYWfjRAeT5rITuCY/1b08s49e5kSQwHDirKZWuoKOBRFS98EUUoZ9kLEwQ==", "dev": true, "requires": { + "@jridgewell/trace-mapping": "^0.3.7", "jest-worker": "^27.4.5", "schema-utils": "^3.1.1", "serialize-javascript": "^6.0.0", - "source-map": "^0.6.1", "terser": "^5.7.2" } }, + "through": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", + "integrity": "sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==" + }, "thunky": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/thunky/-/thunky-1.1.0.tgz", @@ -3609,15 +3909,6 @@ "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", "dev": true }, - "tr46": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/tr46/-/tr46-1.0.1.tgz", - "integrity": "sha1-qLE/1r/SSJUZZ0zN5VujaTtwbQk=", - "dev": true, - "requires": { - "punycode": "^2.1.0" - } - }, "tslib": { "version": "2.4.0", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz", @@ -3650,6 +3941,26 @@ "which-boxed-primitive": "^1.0.2" } }, + "unbzip2-stream": { + "version": "1.4.3", + "resolved": "https://registry.npmjs.org/unbzip2-stream/-/unbzip2-stream-1.4.3.tgz", + "integrity": "sha512-mlExGW4w71ebDJviH16lQLtZS32VKqsSfk80GCfUlwT/4/hNRFsoscrF/c++9xinkMzECL1uL9DDwXqFWkruPg==", + "requires": { + "buffer": "^5.2.1", + "through": "^2.3.8" + }, + "dependencies": { + "buffer": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", + "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", + "requires": { + "base64-js": "^1.3.1", + "ieee754": "^1.1.13" + } + } + } + }, "uniqid": { "version": "5.4.0", "resolved": "https://registry.npmjs.org/uniqid/-/uniqid-5.4.0.tgz", @@ -3658,9 +3969,19 @@ "unpipe": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", - "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=", + "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==", "dev": true }, + "update-browserslist-db": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.3.tgz", + "integrity": "sha512-ufSazemeh9Gty0qiWtoRpJ9F5Q5W3xdIPm1UZQqYQv/q0Nyb9EMHUB2lu+O9x1re9WsorpMAUu4Y6Lxcs5n+XQ==", + "dev": true, + "requires": { + "escalade": "^3.1.1", + "picocolors": "^1.0.0" + } + }, "uri-js": { "version": "4.4.1", "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", @@ -3673,7 +3994,7 @@ "url": { "version": "0.11.0", "resolved": "https://registry.npmjs.org/url/-/url-0.11.0.tgz", - "integrity": "sha1-ODjpfPxgUh63PFJajlW/3Z4uKPE=", + "integrity": "sha512-kbailJa29QrtXnxgq+DdCEGlbTeYM2eJUxsz6vjZavrCYPMIFHMKQmSKYAIuUK2i7hgPm28a8piX5NTUtM/LKQ==", "requires": { "punycode": "1.3.2", "querystring": "0.2.0" @@ -3682,7 +4003,7 @@ "punycode": { "version": "1.3.2", "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.3.2.tgz", - "integrity": "sha1-llOgNvt8HuQjQvIyXM7v6jkmxI0=" + "integrity": "sha512-RofWgt/7fL5wP1Y7fxE7/EmTLzQVnB0ycyibJ0OOHIlJqTNzglYFxVwETOcIoJqJmpDXJ9xImDv+Fq34F/d4Dw==" } } }, @@ -3702,18 +4023,18 @@ "util-deprecate": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", - "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=" + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==" }, "utila": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/utila/-/utila-0.4.0.tgz", - "integrity": "sha1-ihagXURWV6Oupe7MWxKk+lN5dyw=", + "integrity": "sha512-Z0DbgELS9/L/75wZbro8xAnT50pBVFQZ+hUEueGDU5FN51YSCYM+jdxsfCiHjwNP/4LCDD0i/graKpeBnOXKRA==", "dev": true }, "utils-merge": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", - "integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM=", + "integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==", "dev": true }, "uuid": { @@ -3725,7 +4046,7 @@ "vary": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", - "integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw=", + "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==", "dev": true }, "vm-browserify": { @@ -3752,12 +4073,6 @@ "minimalistic-assert": "^1.0.0" } }, - "webidl-conversions": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-4.0.2.tgz", - "integrity": "sha512-YQ+BmxuTgd6UXZW3+ICGfyqRyHXVlD5GtQr5+qjiNW7bF0cqrzX500HVXPBOvgXb5YnzDd+h0zqyv61KUD7+Sg==", - "dev": true - }, "webpack": { "version": "5.67.0", "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.67.0.tgz", @@ -3791,18 +4106,18 @@ } }, "webpack-cli": { - "version": "4.9.2", - "resolved": "https://registry.npmjs.org/webpack-cli/-/webpack-cli-4.9.2.tgz", - "integrity": "sha512-m3/AACnBBzK/kMTcxWHcZFPrw/eQuY4Df1TxvIWfWM2x7mRqBQCqKEd96oCUa9jkapLBaFfRce33eGDb4Pr7YQ==", + "version": "4.10.0", + "resolved": "https://registry.npmjs.org/webpack-cli/-/webpack-cli-4.10.0.tgz", + "integrity": "sha512-NLhDfH/h4O6UOy+0LSso42xvYypClINuMNBVVzX4vX98TmTaTUxwRbXdhucbFMd2qLaCTcLq/PdYrvi8onw90w==", "dev": true, "requires": { "@discoveryjs/json-ext": "^0.5.0", - "@webpack-cli/configtest": "^1.1.1", - "@webpack-cli/info": "^1.4.1", - "@webpack-cli/serve": "^1.6.1", + "@webpack-cli/configtest": "^1.2.0", + "@webpack-cli/info": "^1.5.0", + "@webpack-cli/serve": "^1.7.0", "colorette": "^2.0.14", "commander": "^7.0.0", - "execa": "^5.0.0", + "cross-spawn": "^7.0.3", "fastest-levenshtein": "^1.0.12", "import-local": "^3.0.2", "interpret": "^2.2.0", @@ -4005,17 +4320,6 @@ "integrity": "sha512-OqedPIGOfsDlo31UNwYbCFMSaO9m9G/0faIHj5/dZFDMFqPTcx6UwqyOy3COEaEOg/9VsGIpdqn62W5KhoKSpg==", "dev": true }, - "whatwg-url": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-7.1.0.tgz", - "integrity": "sha512-WUu7Rg1DroM7oQvGWfOiAK21n74Gg+T4elXEQYkOhtyLeWiJFoOGLXPKI/9gzIie9CtwVLm8wtw6YJdKyxSjeg==", - "dev": true, - "requires": { - "lodash.sortby": "^4.7.0", - "tr46": "^1.0.1", - "webidl-conversions": "^4.0.2" - } - }, "which": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", @@ -4059,8 +4363,7 @@ "wrappy": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", - "dev": true + "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" }, "ws": { "version": "8.4.0", @@ -4076,6 +4379,15 @@ "version": "4.0.2", "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==" + }, + "yauzl": { + "version": "2.10.0", + "resolved": "https://registry.npmjs.org/yauzl/-/yauzl-2.10.0.tgz", + "integrity": "sha512-p4a9I6X6nu6IhoGmBqAcbJy1mlC4j27vEPZX9F4L4/vZT3Lyq1VkFHw/V/PUcB9Buo+DG3iHkT0x3Qya58zc3g==", + "requires": { + "buffer-crc32": "~0.2.3", + "fd-slicer": "~1.1.0" + } } } } diff --git a/package.json b/package.json index 55441a5ba7..8661e4e458 100755 --- a/package.json +++ b/package.json @@ -10,22 +10,25 @@ "test": "test" }, "scripts": { - "test": "npm run unit-test && npm run integration-test", + "test": "npm run unit-test && npm run integration-test && npm run plutip-test", "integration-test": "spago run --main Test.Integration", - "unit-test": "spago run --main Test.Unit", + "unit-test": "spago run --main Ctl.Test.Unit", + "plutip-test": "spago run --main Test.Plutip", "dev": "make run-dev", "build": "make run-build" }, "author": "", "license": "MIT", "dependencies": { - "@emurgo/cardano-serialization-lib-browser": "11.0.0-beta.1", - "@emurgo/cardano-serialization-lib-nodejs": "11.0.0-beta.1", + "@emurgo/cardano-serialization-lib-browser": "11.0.0", + "@emurgo/cardano-serialization-lib-nodejs": "11.0.0", + "base64-js": "^1.5.1", "big-integer": "1.6.51", "blake2b-wasm": "2.4.0", "bufferutil": "4.0.5", "jssha": "3.2.0", "node-polyfill-webpack-plugin": "1.1.4", + "puppeteer-core": "^15.3.2", "reconnecting-websocket": "4.4.0", "uniqid": "5.4.0", "ws": "8.4.0", @@ -35,7 +38,7 @@ "buffer": "6.0.3", "html-webpack-plugin": "5.5.0", "webpack": "5.67.0", - "webpack-cli": "4.9.2", + "webpack-cli": "4.10", "webpack-dev-server": "4.7.4" }, "prettier": { diff --git a/packages.dhall b/packages.dhall index 42e5f9ca82..ddca21fb98 100644 --- a/packages.dhall +++ b/packages.dhall @@ -267,6 +267,20 @@ let additions = , repo = "https://github.com/juspay/medea-ps.git" , version = "8b215851959aa8bbf33e6708df6bd683c89d1a5a" } + , purescript-toppokki = + { dependencies = + [ "prelude" + , "record" + , "functions" + , "node-http" + , "aff-promise" + , "node-buffer" + , "node-fs-aff" + ] + , repo = "https://github.com/firefrorefiddle/purescript-toppokki" + , version = "6983e07bf0aa55ab779bcef12df3df339a2b5bd9" + } } in upstream // additions + diff --git a/server/src/Ogmios/Query.hs b/server/src/Ogmios/Query.hs index ca6fa8679d..4d3dfa9cf4 100644 --- a/server/src/Ogmios/Query.hs +++ b/server/src/Ogmios/Query.hs @@ -82,7 +82,10 @@ tryQueryUntilZero query remainAttempts | remainAttempts <= 0 = pure $ Left "Error trying to connect to Ogmios" | otherwise = try @SomeException query >>= \case - Right msg -> pure $ Right msg + Right msg -> do + putStrLn "Successfully connected to Ogmios" + hFlush stdout + pure $ Right msg Left e -> do putStrLn $ "Error : " <> show e putStrLn "Waiting for ogmios conection attempt" diff --git a/spago-packages.nix b/spago-packages.nix index 7c4b677db3..4c2ae3497f 100644 --- a/spago-packages.nix +++ b/spago-packages.nix @@ -53,6 +53,18 @@ let installPhase = "ln -s $src $out"; }; + "aff-retry" = pkgs.stdenv.mkDerivation { + name = "aff-retry"; + version = "v1.2.1"; + src = pkgs.fetchgit { + url = "https://github.com/Unisay/purescript-aff-retry.git"; + rev = "936fad803e3610f149df724ead288657a905cb84"; + sha256 = "08651ly153ywzviab0ipd0zrhwdr8nz4xfym45dlpbgabgrh8pra"; + }; + phases = "installPhase"; + installPhase = "ln -s $src $out"; + }; + "affjax" = pkgs.stdenv.mkDerivation { name = "affjax"; version = "v12.0.0"; @@ -377,6 +389,18 @@ let installPhase = "ln -s $src $out"; }; + "exitcodes" = pkgs.stdenv.mkDerivation { + name = "exitcodes"; + version = "v4.0.0"; + src = pkgs.fetchgit { + url = "https://github.com/Risto-Stevcev/purescript-exitcodes.git"; + rev = "8a9a93fd1776aba4a14cdc9a31094c9e05b05348"; + sha256 = "16861bn1h6jz47i20sd2a0d3qdj52akkqpx43yllmsdggcawmjxc"; + }; + phases = "installPhase"; + installPhase = "ln -s $src $out"; + }; + "foldable-traversable" = pkgs.stdenv.mkDerivation { name = "foldable-traversable"; version = "v5.0.1"; @@ -557,6 +581,18 @@ let installPhase = "ln -s $src $out"; }; + "js-timers" = pkgs.stdenv.mkDerivation { + name = "js-timers"; + version = "v5.0.1"; + src = pkgs.fetchgit { + url = "https://github.com/purescript-contrib/purescript-js-timers.git"; + rev = "86afef13f457b9506acdfe88559ee18f1cd2c2d8"; + sha256 = "0008paz0qkz5n1pfrzagkkac6jny9z2rd1ij10ww2k1pkb9cy59z"; + }; + phases = "installPhase"; + installPhase = "ln -s $src $out"; + }; + "js-uri" = pkgs.stdenv.mkDerivation { name = "js-uri"; version = "v2.0.0"; @@ -749,6 +785,18 @@ let installPhase = "ln -s $src $out"; }; + "node-child-process" = pkgs.stdenv.mkDerivation { + name = "node-child-process"; + version = "v7.1.0"; + src = pkgs.fetchgit { + url = "https://github.com/purescript-node/purescript-node-child-process.git"; + rev = "5c4e560eceead04efc1d5a3ec1f6de91bb1d512e"; + sha256 = "18va367xims00hmjwiasiifdfak3cbs0sp4sr52ihb20n19n6h5b"; + }; + phases = "installPhase"; + installPhase = "ln -s $src $out"; + }; + "node-fs" = pkgs.stdenv.mkDerivation { name = "node-fs"; version = "v6.1.0"; @@ -773,6 +821,30 @@ let installPhase = "ln -s $src $out"; }; + "node-http" = pkgs.stdenv.mkDerivation { + name = "node-http"; + version = "v6.0.0"; + src = pkgs.fetchgit { + url = "https://github.com/purescript-node/purescript-node-http.git"; + rev = "48a4da07051f0cc9a9d08fbfe8179ebf55aff39a"; + sha256 = "1521ab70jx7a9d7kk4gn1sk4w6knfi13pai1kanhrvwp5lfys5wl"; + }; + phases = "installPhase"; + installPhase = "ln -s $src $out"; + }; + + "node-net" = pkgs.stdenv.mkDerivation { + name = "node-net"; + version = "v2.0.1"; + src = pkgs.fetchgit { + url = "https://github.com/purescript-node/purescript-node-net.git"; + rev = "e25a2c538dfa524cd9b75bf12fd7a393efe2f7e9"; + sha256 = "17sx9r74kdjq85dafm5kisbvgdb0wn11lq9gaazpdirhshpm2wl5"; + }; + phases = "installPhase"; + installPhase = "ln -s $src $out"; + }; + "node-path" = pkgs.stdenv.mkDerivation { name = "node-path"; version = "v4.0.0"; @@ -809,6 +881,18 @@ let installPhase = "ln -s $src $out"; }; + "node-url" = pkgs.stdenv.mkDerivation { + name = "node-url"; + version = "v5.0.0"; + src = pkgs.fetchgit { + url = "https://github.com/purescript-node/purescript-node-url.git"; + rev = "d5671f5e38051f4fa7acacd9ec157ed9dc6ded46"; + sha256 = "0w78q23vxa2nldy0dfj4nb5kv0pcrc1yq7dp1mysz7cdi9f72zp9"; + }; + phases = "installPhase"; + installPhase = "ln -s $src $out"; + }; + "nonempty" = pkgs.stdenv.mkDerivation { name = "nonempty"; version = "v6.1.0"; @@ -857,6 +941,42 @@ let installPhase = "ln -s $src $out"; }; + "open-memoize" = pkgs.stdenv.mkDerivation { + name = "open-memoize"; + version = "v6.1.0"; + src = pkgs.fetchgit { + url = "https://github.com/purescript-open-community/purescript-open-memoize.git"; + rev = "20d5c14d3033d19044e2d49c11d02278bda72a54"; + sha256 = "10xaylggw22s41bdvxvy7jg16idwa7npwjnns4d65mjynh2ia6kv"; + }; + phases = "installPhase"; + installPhase = "ln -s $src $out"; + }; + + "options" = pkgs.stdenv.mkDerivation { + name = "options"; + version = "v6.0.0"; + src = pkgs.fetchgit { + url = "https://github.com/purescript-contrib/purescript-options.git"; + rev = "0309a42692251ce5e3d1d0be57d4f63f7143f858"; + sha256 = "04f70wfik1pi6nzfq2cn3la9z735akkadpx5cxbs4mx8xg032sjd"; + }; + phases = "installPhase"; + installPhase = "ln -s $src $out"; + }; + + "optparse" = pkgs.stdenv.mkDerivation { + name = "optparse"; + version = "v4.1.0"; + src = pkgs.fetchgit { + url = "https://github.com/f-o-a-m/purescript-optparse.git"; + rev = "04f2ed818f32390a9feff04b892f23c96ccb84cb"; + sha256 = "0b05wczcjnann0xw6vdaq2c1a2n9rcgvq9l29wa5461b5mvjyb80"; + }; + phases = "installPhase"; + installPhase = "ln -s $src $out"; + }; + "ordered-collections" = pkgs.stdenv.mkDerivation { name = "ordered-collections"; version = "v2.0.2"; @@ -1001,6 +1121,18 @@ let installPhase = "ln -s $src $out"; }; + "purescript-toppokki" = pkgs.stdenv.mkDerivation { + name = "purescript-toppokki"; + version = "6983e07bf0aa55ab779bcef12df3df339a2b5bd9"; + src = pkgs.fetchgit { + url = "https://github.com/firefrorefiddle/purescript-toppokki"; + rev = "6983e07bf0aa55ab779bcef12df3df339a2b5bd9"; + sha256 = "01arx2sp2k287cr4y96frnn6jlghcias9hwdr27yr28k4xa5bhfv"; + }; + phases = "installPhase"; + installPhase = "ln -s $src $out"; + }; + "quickcheck" = pkgs.stdenv.mkDerivation { name = "quickcheck"; version = "v7.1.0"; @@ -1181,6 +1313,18 @@ let installPhase = "ln -s $src $out"; }; + "test-unit" = pkgs.stdenv.mkDerivation { + name = "test-unit"; + version = "v16.0.0"; + src = pkgs.fetchgit { + url = "https://github.com/bodil/purescript-test-unit.git"; + rev = "56d06897b621df5d2f619433d19ababb5bb8ebd1"; + sha256 = "0qz903phxkgrn7qdz1xi49bydkf5cbxssyb4xk029zi4lshb35mw"; + }; + phases = "installPhase"; + installPhase = "ln -s $src $out"; + }; + "text-encoding" = pkgs.stdenv.mkDerivation { name = "text-encoding"; version = "v1.0.0"; diff --git a/spago.dhall b/spago.dhall index efa6a1c2c1..7e4e50a4ae 100644 --- a/spago.dhall +++ b/spago.dhall @@ -8,6 +8,7 @@ You can edit this file as you like. , "aeson-helpers" , "aff" , "aff-promise" + , "aff-retry" , "affjax" , "arraybuffer-types" , "arrays" @@ -26,6 +27,7 @@ You can edit this file as you like. , "enums" , "exceptions" , "foldable-traversable" + , "foreign" , "foreign-object" , "http-methods" , "identity" @@ -40,20 +42,25 @@ You can edit this file as you like. , "mote" , "newtype" , "node-buffer" + , "node-child-process" , "node-fs" , "node-fs-aff" , "node-path" , "node-process" + , "node-streams" , "nonempty" + , "optparse" , "now" , "numbers" , "ordered-collections" , "orders" , "parallel" , "partial" + , "posix-types" , "prelude" , "profunctor" , "profunctor-lenses" + , "purescript-toppokki" , "quickcheck" , "quickcheck-combinators" , "quickcheck-laws" diff --git a/src/Address.purs b/src/Address.purs index 3304003e30..01560c9638 100644 --- a/src/Address.purs +++ b/src/Address.purs @@ -73,4 +73,4 @@ enterpriseAddressStakeValidatorHash = -- NetworkId -------------------------------------------------------------------------------- getNetworkId :: QueryM NetworkId -getNetworkId = asks _.networkId +getNetworkId = asks $ _.config >>> _.networkId diff --git a/src/BalanceTx/BalanceTx.purs b/src/BalanceTx/BalanceTx.purs index ea555e5924..77488da47f 100644 --- a/src/BalanceTx/BalanceTx.purs +++ b/src/BalanceTx/BalanceTx.purs @@ -35,7 +35,11 @@ module BalanceTx , FinalizedTransaction(FinalizedTransaction) , GetPublicKeyTransactionInputError(CannotConvertScriptOutputToTxInput) , GetWalletAddressError(CouldNotGetWalletAddress) - , GetWalletCollateralError(CouldNotGetCollateral) + , GetWalletCollateralError + ( CannotRequestCollateralForWallet + , CouldNotGetCollateral + , WalletNotSpecified + ) , TxInputLockedError(TxInputLockedError) , ImpossibleError(Impossible) , ReturnAdaChangeError @@ -46,6 +50,7 @@ module BalanceTx , UtxosAtError(CouldNotGetUtxos) , UtxoMinAdaValueCalcError(UtxoMinAdaValueCalcError) , balanceTx + , balanceTxWithAddress ) where import Prelude @@ -163,7 +168,10 @@ derive instance Generic GetWalletAddressError _ instance Show GetWalletAddressError where show = genericShow -data GetWalletCollateralError = CouldNotGetCollateral +data GetWalletCollateralError + = CannotRequestCollateralForWallet ImpossibleError + | CouldNotGetCollateral + | WalletNotSpecified derive instance Generic GetWalletCollateralError _ @@ -352,7 +360,7 @@ finalizeTransaction reindexedUnattachedTxWithExUnits = datums = wrap <$> fromMaybe mempty ws.plutusData in do - costModels <- asks _.pparams <#> unwrap >>> _.costModels + costModels <- asks (_.runtime >>> _.pparams >>> unwrap >>> _.costModels) liftEffect $ FinalizedTransaction <$> setScriptDataHash costModels redeemers datums attachedTxWithExUnits @@ -435,36 +443,33 @@ _redeemersTxIns = lens' \(UnattachedUnbalancedTx rec@{ redeemersTxIns }) -> \rdmrs -> UnattachedUnbalancedTx rec { redeemersTxIns = rdmrs } -------------------------------------------------------------------------------- --- Setting collateral, collateral return, total collateral +-- Setting collateral -------------------------------------------------------------------------------- setCollateral - :: Transaction -> Utxos -> QueryM (Either BalanceTxError Transaction) -setCollateral transaction utxos = - runExceptT do - wallet <- asks _.wallet - mCollateral <- ExceptT $ selectCollateral wallet - pure $ case mCollateral /\ wallet of - Nothing /\ _ -> - transaction - Just collateral /\ Just (KeyWallet _) -> - -- TODO: https://github.com/Plutonomicon/cardano-transaction-lib/pull/707 - addTxCollateral collateral transaction - Just collateral /\ _ -> do - addTxCollateral collateral transaction + :: Transaction + -> Utxos + -> QueryM (Either GetWalletCollateralError Transaction) +setCollateral transaction utxos = runExceptT do + wallet <- + ExceptT $ asks (_.runtime >>> _.wallet) <#> note WalletNotSpecified + collateral <- ExceptT $ selectCollateral wallet + -- TODO: https://github.com/Plutonomicon/cardano-transaction-lib/pull/707 + pure $ addTxCollateral collateral transaction where selectCollateral - :: Maybe Wallet - -> QueryM (Either BalanceTxError (Maybe (Array TransactionUnspentOutput))) - selectCollateral (Just w) | isJust (cip30Wallet w) = - map Just <$> QueryM.getWalletCollateral <#> - note (GetWalletCollateralError' CouldNotGetCollateral) - selectCollateral (Just (KeyWallet kw)) = + :: Wallet + -> QueryM (Either GetWalletCollateralError (Array TransactionUnspentOutput)) + selectCollateral (KeyWallet keyWallet) = -- TODO: Combine with getWalletCollateral and supply with fee estimate -- https://github.com/Plutonomicon/cardano-transaction-lib/issues/510 - Right <<< map pure <$> kw.selectCollateral <$> filterLockedUtxos utxos - selectCollateral _ = - pure (Right Nothing) + (unwrap keyWallet).selectCollateral <$> filterLockedUtxos utxos + <#> note CouldNotGetCollateral <<< map Array.singleton + selectCollateral wallet + | isJust (cip30Wallet wallet) = + QueryM.getWalletCollateral <#> note CouldNotGetCollateral + | otherwise = + pure $ Left $ CannotRequestCollateralForWallet Impossible addTxCollateral :: Array TransactionUnspentOutput -> Transaction -> Transaction addTxCollateral utxos transaction = @@ -477,30 +482,36 @@ addTxCollateral utxos transaction = -- FIX ME: UnbalancedTx contains requiredSignatories which would be a part of -- multisig but we don't have such functionality ATM. --- | Balances an unbalanced transaction. For submitting a tx via Nami, the --- | utxo set shouldn't include the collateral which is vital for balancing. --- | In particular, the transaction inputs must not include the collateral. -balanceTx - :: UnattachedUnbalancedTx +-- | Like `balanceTx`, but allows to provide an address that is treated like +-- | user's own (while `balanceTx` gets it from the wallet). +balanceTxWithAddress + :: Address + -> UnattachedUnbalancedTx -> QueryM (Either BalanceTxError FinalizedTransaction) -balanceTx unattachedTx@(UnattachedUnbalancedTx { unbalancedTx: t }) = do +balanceTxWithAddress + ownAddr + unattachedTx@(UnattachedUnbalancedTx { unbalancedTx: t }) = do let (UnbalancedTx { transaction: unbalancedTx, utxoIndex }) = t networkId <- (unbalancedTx ^. _body <<< _networkId) # - maybe (asks _.networkId) pure + maybe (asks $ _.config >>> _.networkId) pure let unbalancedTx' = unbalancedTx # _body <<< _networkId ?~ networkId utxoMinVal <- adaOnlyUtxoMinAdaValue runExceptT do -- Get own wallet address, collateral and utxo set: - ownAddr <- ExceptT $ QueryM.getWalletAddress <#> - note (GetWalletAddressError' CouldNotGetWalletAddress) utxos <- ExceptT $ utxosAt ownAddr <#> (note (UtxosAtError' CouldNotGetUtxos) >>> map unwrap) + -- After adding collateral, we need to balance the inputs and -- non-Ada outputs before looping, i.e. we need to add input fees -- for the Ada only collateral. No MinUtxos required. Perhaps -- for some wallets this step can be skipped and we can go straight -- to prebalancer. - unbalancedCollTx <- ExceptT $ setCollateral unbalancedTx' utxos + unbalancedCollTx <- + if Array.null (unattachedTx ^. _redeemersTxIns) + -- Don't set collateral if tx doesn't contain phase-2 scripts: + then pure unbalancedTx' + else ExceptT $ setCollateral unbalancedTx' utxos + <#> lmap GetWalletCollateralError' let -- Combines utxos at the user address and those from any scripts @@ -515,13 +526,12 @@ balanceTx unattachedTx@(UnattachedUnbalancedTx { unbalancedTx: t }) = do -- Prebalance collaterised tx without fees: ubcTx <- except $ - prebalanceCollateral zero availableUtxos ownAddr utxoMinVal - unbalancedCollTx + prebalanceCollateral zero availableUtxos utxoMinVal unbalancedCollTx -- Prebalance collaterised tx with fees: let unattachedTx' = unattachedTx # _transaction' .~ ubcTx _ /\ fees <- ExceptT $ evalExUnitsAndMinFee unattachedTx' ubcTx' <- except $ - prebalanceCollateral (fees + feeBuffer) availableUtxos ownAddr utxoMinVal + prebalanceCollateral (fees + feeBuffer) availableUtxos utxoMinVal ubcTx -- Loop to balance non-Ada assets nonAdaBalancedCollTx <- ExceptT $ loop availableUtxos ownAddr [] $ @@ -541,11 +551,10 @@ balanceTx unattachedTx@(UnattachedUnbalancedTx { unbalancedTx: t }) = do prebalanceCollateral :: BigInt -> Utxos - -> Address -> BigInt -> Transaction -> Either BalanceTxError Transaction - prebalanceCollateral fees utxos ownAddr adaOnlyUtxoMinValue tx = + prebalanceCollateral fees utxos adaOnlyUtxoMinValue tx = balanceTxIns utxos fees adaOnlyUtxoMinValue (tx ^. _body) >>= balanceNonAdaOuts ownAddr utxos <#> flip (set _body) tx @@ -619,6 +628,17 @@ balanceTx unattachedTx@(UnattachedUnbalancedTx { unbalancedTx: t }) = do feeBuffer :: BigInt feeBuffer = fromInt 500000 +-- | Balances an unbalanced transaction. For submitting a tx via Nami, the +-- | utxo set shouldn't include the collateral which is vital for balancing. +-- | In particular, the transaction inputs must not include the collateral. +balanceTx + :: UnattachedUnbalancedTx + -> QueryM (Either BalanceTxError FinalizedTransaction) +balanceTx tx = do + QueryM.getWalletAddress >>= case _ of + Nothing -> pure $ Left $ GetWalletAddressError' CouldNotGetWalletAddress + Just address -> balanceTxWithAddress address tx + -- Logging for Transaction type without returning Transaction logTx :: forall (m :: Type -> Type) diff --git a/src/BalanceTx/UtxoMinAda.purs b/src/BalanceTx/UtxoMinAda.purs index a99791360b..3f403222e3 100644 --- a/src/BalanceTx/UtxoMinAda.purs +++ b/src/BalanceTx/UtxoMinAda.purs @@ -26,7 +26,8 @@ foreign import minAdaForOutput utxoMinAdaValue :: TransactionOutput -> QueryM (Maybe BigInt) utxoMinAdaValue txOutput = do - coinsPerUtxoByte <- asks _.pparams <#> unwrap >>> _.coinsPerUtxoByte + coinsPerUtxoByte <- asks (_.runtime >>> _.pparams) <#> unwrap >>> + _.coinsPerUtxoByte cslTxOutput <- liftEffect $ convertTxOutput txOutput pure $ BigNum.fromBigInt (unwrap coinsPerUtxoByte) >>= minAdaForOutput maybeFfiHelper cslTxOutput diff --git a/src/Base64.js b/src/Base64.js new file mode 100644 index 0000000000..5bd33d96af --- /dev/null +++ b/src/Base64.js @@ -0,0 +1,13 @@ +const base64 = require("base64-js"); + +exports.fromByteArray = base64.fromByteArray; + +exports.toByteArray = base64.toByteArray; + +exports._decodeBase64 = maybe => str => { + try { + return maybe.just(base64.toByteArray(str)); + } catch (_) { + return maybe.nothing; + } +}; diff --git a/src/Base64.purs b/src/Base64.purs new file mode 100644 index 0000000000..a37c092abb --- /dev/null +++ b/src/Base64.purs @@ -0,0 +1,57 @@ +module Base64 + ( Base64String + , mkBase64String + , unBase64String + , toByteArray + , fromByteArray + , decodeBase64 + ) where + +import Prelude + +import Aeson + ( class DecodeAeson + , class EncodeAeson + , JsonDecodeError(TypeMismatch) + , decodeAeson + ) +import Data.Either (Either(Left)) +import Data.Maybe (Maybe, maybe) +import FfiHelpers (MaybeFfiHelper, maybeFfiHelper) +import Test.QuickCheck (class Arbitrary, arbitrary) +import Types.ByteArray (ByteArray) + +newtype Base64String = Base64String String + +derive newtype instance Eq Base64String +derive newtype instance Ord Base64String + +instance Show Base64String where + show str = "(fromByteArray " <> show (toByteArray str) <> ")" + +instance DecodeAeson Base64String where + decodeAeson json = do + decodeAeson json >>= mkBase64String >>> + maybe (Left $ TypeMismatch "Base64String") pure + +derive newtype instance EncodeAeson Base64String + +-- | Wraps a `String` into `Base64String` if it is valid. +mkBase64String :: String -> Maybe Base64String +mkBase64String str = decodeBase64 str $> Base64String str + +unBase64String :: Base64String -> String +unBase64String (Base64String str) = str + +foreign import toByteArray :: Base64String -> ByteArray + +foreign import fromByteArray :: ByteArray -> Base64String + +foreign import _decodeBase64 :: MaybeFfiHelper -> String -> Maybe ByteArray + +decodeBase64 :: String -> Maybe ByteArray +decodeBase64 = _decodeBase64 maybeFfiHelper + +instance Arbitrary Base64String where + arbitrary = do + fromByteArray <$> arbitrary diff --git a/src/Cardano/Types/Transaction.purs b/src/Cardano/Types/Transaction.purs index 6dd1beb9c0..a784fd58fe 100644 --- a/src/Cardano/Types/Transaction.purs +++ b/src/Cardano/Types/Transaction.purs @@ -355,7 +355,7 @@ type ProtocolParamUpdate = , treasuryGrowthRate :: Maybe UnitInterval , d :: Maybe UnitInterval , extraEntropy :: Maybe Nonce - , protocolVersion :: Maybe (Array ProtocolVersion) + , protocolVersion :: Maybe ProtocolVersion , minPoolCost :: Maybe BigNum , adaPerUtxoByte :: Maybe BigNum , costModels :: Maybe Costmdls diff --git a/src/Contract/Address.purs b/src/Contract/Address.purs index 26cb46e000..ef87c12e0e 100644 --- a/src/Contract/Address.purs +++ b/src/Contract/Address.purs @@ -4,6 +4,8 @@ module Contract.Address , enterpriseAddressStakeValidatorHash , enterpriseAddressValidatorHash , getNetworkId + , addressWithNetworkTagToBech32 + , addressToBech32 , getWalletAddress , getWalletCollateral , module ByteArray @@ -12,6 +14,7 @@ module Contract.Address , module ExportUnbalancedTransaction , module Hash , module SerializationAddress + , module TypeAliases , ownPaymentPubKeyHash , ownPubKeyHash , ownStakePubKeyHash @@ -44,7 +47,11 @@ import Plutus.Conversion , toPlutusAddress , toPlutusTxUnspentOutput ) -import Plutus.Types.Address (Address) +import Plutus.Conversion.Address (fromPlutusAddressWithNetworkTag) +import Plutus.Types.Address + ( Address + , AddressWithNetworkTag(AddressWithNetworkTag) + ) import Plutus.Types.Address ( Address , AddressWithNetworkTag(AddressWithNetworkTag) @@ -68,7 +75,7 @@ import Scripts , validatorHashBaseAddress , validatorHashEnterpriseAddress ) as Scripts -import Serialization.Address (NetworkId(MainnetId)) +import Serialization.Address (NetworkId(MainnetId), addressBech32) import Serialization.Address ( Slot(Slot) , BlockId(BlockId) @@ -80,6 +87,8 @@ import Serialization.Address ) as SerializationAddress import Serialization.Hash (Ed25519KeyHash) as Hash import Serialization.Hash (ScriptHash) +import Types.Aliases (Bech32String) +import Types.Aliases (Bech32String) as TypeAliases import Types.ByteArray (ByteArray) as ByteArray import Types.PubKeyHash ( PaymentPubKeyHash(PaymentPubKeyHash) @@ -154,6 +163,20 @@ getNetworkId = wrapContract Address.getNetworkId -- `module Address` -------------------------------------------------------------------------------- +-- | Convert `Address` to `Bech32String`, using given `NetworkId` to determine +-- | Bech32 prefix. +addressWithNetworkTagToBech32 :: AddressWithNetworkTag -> Bech32String +addressWithNetworkTagToBech32 = fromPlutusAddressWithNetworkTag >>> + addressBech32 + +-- | Convert `Address` to `Bech32String`, using current `NetworkId` provided by +-- | `Contract` configuration to determine the network tag. +addressToBech32 :: forall (r :: Row Type). Address -> Contract r Bech32String +addressToBech32 address = do + networkId <- getNetworkId + pure $ addressWithNetworkTagToBech32 + (AddressWithNetworkTag { address, networkId }) + -- | Get the `ValidatorHash` with an Plutus `Address` enterpriseAddressValidatorHash :: Address -> Maybe ValidatorHash enterpriseAddressValidatorHash = diff --git a/src/Contract/Chain.purs b/src/Contract/Chain.purs index edf2baa581..2c2afa2ad1 100644 --- a/src/Contract/Chain.purs +++ b/src/Contract/Chain.purs @@ -2,6 +2,9 @@ module Contract.Chain ( getTip , waitUntilSlot + , waitNSlots + , currentTime + , currentSlot , module Chain ) where @@ -9,16 +12,32 @@ import Prelude import Contract.Monad (Contract, wrapContract) import QueryM (getChainTip) as QueryM -import QueryM.WaitUntilSlot (waitUntilSlot) as QueryM +import QueryM.WaitUntilSlot + ( currentSlot + , currentTime + , waitNSlots + , waitUntilSlot + ) as QueryM import Serialization.Address (Slot) import Types.Chain ( BlockHeaderHash(BlockHeaderHash) , ChainTip(ChainTip) , Tip(Tip, TipAtGenesis) ) as Chain +import Types.Natural (Natural) +import Types.Interval (POSIXTime) getTip :: forall (r :: Row Type). Contract r Chain.Tip getTip = wrapContract QueryM.getChainTip waitUntilSlot :: forall (r :: Row Type). Slot -> Contract r Chain.Tip waitUntilSlot = wrapContract <<< QueryM.waitUntilSlot + +waitNSlots :: forall (r :: Row Type). Natural -> Contract r Chain.Tip +waitNSlots = wrapContract <<< QueryM.waitNSlots + +currentTime :: forall (r :: Row Type). Contract r POSIXTime +currentTime = wrapContract QueryM.currentTime + +currentSlot :: forall (r :: Row Type). Contract r Slot +currentSlot = wrapContract QueryM.currentSlot diff --git a/src/Contract/Config.purs b/src/Contract/Config.purs new file mode 100644 index 0000000000..f6dd9353a5 --- /dev/null +++ b/src/Contract/Config.purs @@ -0,0 +1,67 @@ +-- | Exposes some pre-defined Contract configurations. Re-exports all modules needed to modify `ConfigParams`. +module Contract.Config + ( testnetConfig + , testnetNamiConfig + , testnetGeroConfig + , mainnetConfig + , mainnetNamiConfig + , mainnetGeroConfig + , module Contract.Address + , module Contract.Monad + , module Data.Log.Level + , module Data.Log.Message + , module Serialization + , module QueryM.ServerConfig + , module Wallet.Spec + , module Wallet.Key + ) where + +import Contract.Address (NetworkId(MainnetId, TestnetId)) +import Serialization (privateKeyFromBytes) +import Contract.Monad (ConfigParams) +import Data.Log.Level (LogLevel(Trace, Debug, Info, Warn, Error)) +import Data.Maybe (Maybe(Just, Nothing)) +import Wallet.Spec + ( WalletSpec(UseKeys, ConnectToNami, ConnectToGero) + , PrivateStakeKeySource(PrivateStakeKeyFile, PrivateStakeKeyValue) + , PrivatePaymentKeySource(PrivatePaymentKeyFile, PrivatePaymentKeyValue) + ) +import QueryM.ServerConfig + ( Host + , ServerConfig + , defaultDatumCacheWsConfig + , defaultOgmiosWsConfig + , defaultServerConfig + ) +import Wallet.Key + ( PrivatePaymentKey(PrivatePaymentKey) + , PrivateStakeKey(PrivateStakeKey) + ) +import Data.Log.Message (Message) + +testnetConfig :: ConfigParams () +testnetConfig = + { ogmiosConfig: defaultOgmiosWsConfig + , datumCacheConfig: defaultDatumCacheWsConfig + , ctlServerConfig: defaultServerConfig + , networkId: TestnetId + , logLevel: Trace + , extraConfig: {} + , walletSpec: Nothing + , customLogger: Nothing + } + +testnetNamiConfig :: ConfigParams () +testnetNamiConfig = testnetConfig { walletSpec = Just ConnectToNami } + +testnetGeroConfig :: ConfigParams () +testnetGeroConfig = testnetConfig { walletSpec = Just ConnectToGero } + +mainnetConfig :: ConfigParams () +mainnetConfig = testnetConfig { networkId = MainnetId } + +mainnetNamiConfig :: ConfigParams () +mainnetNamiConfig = mainnetConfig { walletSpec = Just ConnectToNami } + +mainnetGeroConfig :: ConfigParams () +mainnetGeroConfig = mainnetConfig { walletSpec = Just ConnectToGero } diff --git a/src/Contract/Log.purs b/src/Contract/Log.purs new file mode 100644 index 0000000000..b0720d46bb --- /dev/null +++ b/src/Contract/Log.purs @@ -0,0 +1,123 @@ +module Contract.Log + ( logTrace + , logTrace' + , logDebug + , logDebug' + , logInfo + , logInfo' + , logWarn + , logWarn' + , logError + , logError' + , logAeson + , logAesonTrace + , logAesonDebug + , logAesonInfo + , logAesonWarn + , logAesonError + ) where + +import Prelude + +import Aeson (class EncodeAeson, encodeAeson, stringifyAeson) +import Control.Monad.Logger.Class (class MonadLogger) +import Control.Monad.Logger.Class as Logger +import Data.Log.Tag (TagSet) +import Data.Map as Map + +logTrace + :: forall (m :: Type -> Type). MonadLogger m => TagSet -> String -> m Unit +logTrace = Logger.trace + +logDebug + :: forall (m :: Type -> Type). MonadLogger m => TagSet -> String -> m Unit +logDebug = Logger.debug + +logInfo + :: forall (m :: Type -> Type). MonadLogger m => TagSet -> String -> m Unit +logInfo = Logger.info + +logWarn + :: forall (m :: Type -> Type). MonadLogger m => TagSet -> String -> m Unit +logWarn = Logger.warn + +logError + :: forall (m :: Type -> Type). MonadLogger m => TagSet -> String -> m Unit +logError = Logger.error + +logTrace' + :: forall (m :: Type -> Type). MonadLogger m => String -> m Unit +logTrace' = Logger.trace Map.empty + +logDebug' + :: forall (m :: Type -> Type). MonadLogger m => String -> m Unit +logDebug' = Logger.debug Map.empty + +logInfo' + :: forall (m :: Type -> Type). MonadLogger m => String -> m Unit +logInfo' = Logger.info Map.empty + +logWarn' + :: forall (m :: Type -> Type). MonadLogger m => String -> m Unit +logWarn' = Logger.warn Map.empty + +logError' + :: forall (m :: Type -> Type). MonadLogger m => String -> m Unit +logError' = Logger.error Map.empty + +-- | Log JSON representation of a data structure +logAeson + :: forall (m :: Type -> Type) (a :: Type) + . MonadLogger m + => EncodeAeson a + => (String -> m Unit) + -- ^ Logging function to use + -> a + -- ^ Data structure to output + -> m Unit +logAeson logger = logger <<< stringifyAeson <<< encodeAeson + +logAesonTrace + :: forall (m :: Type -> Type) (a :: Type) + . MonadLogger m + => EncodeAeson a + => a + -- ^ Data structure to output + -> m Unit +logAesonTrace = logAeson logTrace' + +logAesonDebug + :: forall (m :: Type -> Type) (a :: Type) + . MonadLogger m + => EncodeAeson a + => a + -- ^ Data structure to output + -> m Unit +logAesonDebug = logAeson logDebug' + +logAesonInfo + :: forall (m :: Type -> Type) (a :: Type) + . MonadLogger m + => EncodeAeson a + => a + -- ^ Data structure to output + -> m Unit +logAesonInfo = logAeson logInfo' + +logAesonWarn + :: forall (m :: Type -> Type) (a :: Type) + . MonadLogger m + => EncodeAeson a + => a + -- ^ Data structure to output + -> m Unit +logAesonWarn = logAeson logWarn' + +logAesonError + :: forall (m :: Type -> Type) (a :: Type) + . MonadLogger m + => EncodeAeson a + => a + -- ^ Data structure to output + -> m Unit +logAesonError = logAeson logError' diff --git a/src/Contract/Monad.purs b/src/Contract/Monad.purs index dd750c110f..d6b6d98c40 100644 --- a/src/Contract/Monad.purs +++ b/src/Contract/Monad.purs @@ -1,15 +1,14 @@ -- | A module defining the `Contract` monad. module Contract.Monad ( Contract(Contract) - , ContractConfig(ContractConfig) - , ConfigParams(ConfigParams) - , DefaultContractConfig + , ContractEnv(ContractEnv) + , ConfigParams + , DefaultContractEnv , module Aff , module QueryM - , module Log.Level , module Log.Tag - , configWithLogLevel - , defaultTestnetContractConfig + , askConfig + , asksConfig , liftContractAffM , liftContractE , liftContractE' @@ -17,33 +16,17 @@ module Contract.Monad , liftedE , liftedE' , liftedM - , logTrace - , logTrace' - , logDebug - , logDebug' - , logInfo - , logInfo' - , logWarn - , logWarn' - , logError - , logError' - , logAeson - , logAesonTrace - , logAesonDebug - , logAesonInfo - , logAesonWarn - , logAesonError - , mkContractConfig + , mkContractEnv , runContract - , runContract_ + , runContractInEnv + , stopContractEnv , throwContractError - , traceTestnetContractConfig + , withContractEnv , wrapContract ) where import Prelude -import Aeson (class EncodeAeson, encodeAeson, stringifyAeson) import Control.Alt (class Alt) import Control.Monad.Error.Class ( class MonadError @@ -51,22 +34,19 @@ import Control.Monad.Error.Class , catchError ) import Control.Monad.Logger.Class (class MonadLogger) -import Control.Monad.Logger.Class as Logger -import Control.Monad.Logger.Trans (runLoggerT) import Control.Monad.Reader.Class ( class MonadAsk , class MonadReader , ask + , asks , local ) import Control.Monad.Reader.Trans (runReaderT) import Control.Monad.Rec.Class (class MonadRec) import Control.Plus (class Plus, empty) import Data.Either (Either, either, hush) -import Data.Log.Level (LogLevel(Error, Trace)) -import Data.Log.Level (LogLevel(Trace, Debug, Info, Warn, Error)) as Log.Level +import Data.Log.Level (LogLevel) import Data.Log.Message (Message) -import Data.Log.Tag (TagSet) import Data.Log.Tag ( TagSet , tag @@ -76,16 +56,16 @@ import Data.Log.Tag , jsDateTag , tagSetTag ) as Log.Tag -import Data.Map as Map -import Data.Maybe (Maybe(Just), maybe) +import Data.Maybe (Maybe, maybe) import Data.Newtype (class Newtype, unwrap, wrap) import Data.Profunctor (dimap) +import Effect (Effect) import Effect.Aff (Aff) import Effect.Aff (Aff, launchAff_) as Aff import Effect.Aff.Class (class MonadAff, liftAff) import Effect.Class (class MonadEffect, liftEffect) import Effect.Exception (Error, throw) -import Helpers (logWithLevel) +import Prim.TypeError (class Warn, Text) import QueryM ( DatumCacheListeners , DatumCacheWebSocket @@ -105,12 +85,18 @@ import QueryM , mkOgmiosWebSocketAff , mkWsUrl ) as QueryM -import QueryM (QueryM, QueryMExtended, QueryConfig) -import QueryM.ProtocolParameters (getProtocolParametersAff) as Ogmios -import Record as Record -import Serialization.Address (NetworkId(TestnetId)) -import Types.UsedTxOuts (newUsedTxOuts) -import Wallet (Wallet, mkNamiWalletAff) +import QueryM + ( QueryConfig + , QueryEnv + , QueryM + , QueryMExtended + , QueryRuntime + , mkQueryRuntime + , stopQueryRuntime + , withQueryRuntime + ) +import Serialization.Address (NetworkId) +import Wallet.Spec (WalletSpec) -- | The `Contract` monad is a newtype wrapper over `QueryM` which is `ReaderT` -- | on `QueryConfig` over asynchronous effects, `Aff`. Throwing and catching @@ -126,7 +112,7 @@ import Wallet (Wallet, mkNamiWalletAff) -- | find the type in the `QueryM` module. -- | -- | The configuration for `Contract` is also a newtype wrapper over the --- | underlying `QueryConfig`, see `ContractConfig`. +-- | underlying `QueryConfig`, see `ContractEnv`. -- | -- | All useful functions written in `QueryM` should be lifted into the -- | `Contract` monad and available in the same namespace. If anything is @@ -151,14 +137,17 @@ derive newtype instance MonadError Error (Contract r) derive newtype instance MonadRec (Contract r) derive newtype instance MonadLogger (Contract r) -instance MonadAsk (ContractConfig r) (Contract r) where +instance MonadAsk (ContractEnv r) (Contract r) where -- Use the underlying `ask`: - ask = Contract $ ContractConfig <$> ask + ask = Contract $ ContractEnv <$> ask -instance MonadReader (ContractConfig r) (Contract r) where +instance MonadReader (ContractEnv r) (Contract r) where -- Use the underlying `local` after dimapping and unwrapping: local f contract = Contract $ local (dimap wrap unwrap f) (unwrap contract) +-- | `ContractEnv` is a trivial wrapper over `QueryEnv`. +newtype ContractEnv (r :: Row Type) = ContractEnv (QueryEnv r) + -- | Contract's `Alt` instance piggie-backs on the underlying `Aff`'s `Alt` -- | instance, which uses `MonadError` capabilities. -- | You can use `alt` operator to provide an alternative contract in case @@ -170,61 +159,168 @@ instance Alt (Contract r) where instance Plus (Contract r) where empty = liftAff empty --- | The config for `Contract` is just a newtype wrapper over the underlying --- | `QueryM` config. To use a configuration with default values, see --- | `defaultContractConfig`. To specify non-default configuration values, it --- | is recommended to construct a `ContractConfig` indirectly with from --- | `ConfigParams` using `mkContractConfig` -newtype ContractConfig (r :: Row Type) = ContractConfig (QueryConfig r) - -type DefaultContractConfig = ContractConfig () +type DefaultContractEnv = ContractEnv () -derive instance Newtype (ContractConfig r) _ +derive instance Newtype (ContractEnv r) _ wrapContract :: forall (r :: Row Type) (a :: Type). QueryM a -> Contract r a wrapContract = wrap <<< QueryM.liftQueryM --- | Options to construct a `ContractConfig` indirectly. Use `mkContractConfig` --- | to create a `ContractConfig` which will call the necessary effects for --- | websocket initializations according to the provided options. `extraConfig` --- | holds additional options that will extend the resulting `ContractConfig` --- | directly -newtype ConfigParams (r :: Row Type) = ConfigParams +-- | Same as `ask`, but points to the user config record. +askConfig + :: forall (r :: Row Type) + . Warn + ( Text + "User-defined configs are deprecated - https://github.com/Plutonomicon/cardano-transaction-lib/issues/734" + ) + => Contract r { | r } +askConfig = do + asks $ unwrap >>> _.extraConfig + +-- | Same as `asks`, but allows to apply a function to the user config record. +asksConfig + :: forall (r :: Row Type) (a :: Type) + . Warn + ( Text + "User-defined configs are deprecated - https://github.com/Plutonomicon/cardano-transaction-lib/issues/734" + ) + => ({ | r } -> a) + -> Contract r a +asksConfig f = do + asks $ unwrap >>> _.extraConfig >>> f + +-- | Options to construct a `ContractEnv` indirectly. `extraConfig` +-- | holds additional options that will extend the resulting `ContractEnv`. +-- | +-- | Use `runContract` to run a `Contract` within an implicity constructed +-- | `ContractEnv` environment, or use `withContractEnv` if your application +-- | contains multiple contracts that can be run in parallel, reusing the same +-- | environment (see `withContractEnv`) +type ConfigParams (r :: Row Type) = { ogmiosConfig :: QueryM.ServerConfig , datumCacheConfig :: QueryM.ServerConfig , ctlServerConfig :: QueryM.ServerConfig , networkId :: NetworkId , logLevel :: LogLevel - , wallet :: Maybe Wallet - -- | Additional config options to extend the `ContractConfig` + , walletSpec :: Maybe WalletSpec + , customLogger :: Maybe (Message -> Aff Unit) + -- | Additional config options to extend the `ContractEnv` , extraConfig :: { | r } } -derive instance Newtype (ConfigParams r) _ +-- | Interprets a contract into an `Aff` context. +-- | Implicitly initializes and finalizes a new `ContractEnv` runtime. +-- | +-- | Use `withContractEnv` if your application contains multiple contracts that +-- | can be run in parallel, reusing the same environment (see +-- | `withContractEnv`) +runContract + :: forall (r :: Row Type) (a :: Type) + . ConfigParams r + -> Contract r a + -> Aff a +runContract params contract = do + withContractEnv params \config -> + runContractInEnv config contract --- | Create a `ContractConfig` from the provided params. This will call the --- | necessary initialization code for the websocket connections -mkContractConfig - :: forall (r :: Row Type). ConfigParams r -> Aff (ContractConfig r) -mkContractConfig - (ConfigParams params@{ logLevel, networkId, wallet }) = do - ogmiosWs <- QueryM.mkOgmiosWebSocketAff logLevel params.ogmiosConfig - datumCacheWs <- QueryM.mkDatumCacheWebSocketAff logLevel - params.datumCacheConfig - pparams <- Ogmios.getProtocolParametersAff ogmiosWs logLevel - usedTxOuts <- newUsedTxOuts +-- | Runs a contract in existing environment. Does not destroy the environment +-- | when contract execution ends. +runContractInEnv + :: forall (r :: Row Type) (a :: Type) + . ContractEnv r + -> Contract r a + -> Aff a +runContractInEnv config = + flip runReaderT (unwrap config) + <<< unwrap + <<< unwrap + +-- | Initializes a `Contract` environment. Does not ensure finalization. +-- | Consider using `withContractEnv` if possible - otherwise use +-- | `stopContractEnv` to properly finalize. +mkContractEnv + :: forall (r :: Row Type) (a :: Type) + . Warn + ( Text + "Using `mkContractEnv` is not recommended: it does not ensure `ContractEnv` finalization. Consider using `withContractEnv`" + ) + => ConfigParams r + -> Aff (ContractEnv r) +mkContractEnv + params@ + { ctlServerConfig + , ogmiosConfig + , datumCacheConfig + , networkId + , logLevel + , walletSpec + , customLogger + } = do let - queryConfig = - { logLevel + config = + { ctlServerConfig + , ogmiosConfig + , datumCacheConfig , networkId - , ogmiosWs - , usedTxOuts - , wallet - , datumCacheWs - , serverConfig: params.ctlServerConfig - , pparams + , logLevel + , walletSpec + , customLogger } - pure $ wrap $ queryConfig `Record.union` params.extraConfig + runtime <- mkQueryRuntime + config + let + contractEnv = wrap + { runtime, config, extraConfig: params.extraConfig } + pure contractEnv + +-- | Finalizes a `Contract` environment. +stopContractEnv + :: forall (r :: Row Type) + . Warn + ( Text + "Using `stopContractEnv` is not recommended: users should rely on `withContractEnv` to finalize the runtime environment instead" + ) + => ContractEnv r + -> Effect Unit +stopContractEnv env = stopQueryRuntime (unwrap env).runtime + +-- | Constructs and finalizes a contract environment that is usable inside a +-- | bracket callback. +-- | One environment can be used by multiple `Contract`s in parallel (see +-- | `runContractInEnv`). +-- | Make sure that `Aff` action does not end before all contracts that use the +-- | runtime terminate. Otherwise `WebSocket`s will be closed too early. +withContractEnv + :: forall (r :: Row Type) (a :: Type) + . ConfigParams r + -> (ContractEnv r -> Aff a) + -> Aff a +withContractEnv + params@ + { ctlServerConfig + , ogmiosConfig + , datumCacheConfig + , networkId + , logLevel + , walletSpec + , customLogger + } + action = do + let + config = + { ctlServerConfig + , ogmiosConfig + , datumCacheConfig + , networkId + , logLevel + , walletSpec + , customLogger + } + withQueryRuntime config \runtime -> do + let + contractEnv = wrap + { runtime, config, extraConfig: params.extraConfig } + action contractEnv -- | Throws an `Error` for any showable error using `Effect.Exception.throw` -- | and lifting into the `Contract` monad. @@ -297,157 +393,3 @@ liftedE' -> Contract r (Either e a) -> Contract r a liftedE' str em = em >>= liftContractE' str - --- | Runs the contract, essentially `runReaderT` but with arguments flipped. -runContract - :: forall (r :: Row Type) (a :: Type) - . ContractConfig r - -> Contract r a - -> Aff a -runContract config = flip runLoggerT printLog <<< flip runReaderT cfg <<< unwrap - where - printLog :: Message -> Aff Unit - printLog = logWithLevel cfg.logLevel - - cfg :: QueryConfig r - cfg = unwrap config - --- | Same as `runContract` discarding output. -runContract_ - :: forall (r :: Row Type) (a :: Type) - . ContractConfig r - -> Contract r a - -> Aff Unit -runContract_ config = void <<< runContract config - --- | Creates a default `ContractConfig` with a Nami wallet inside `Aff` as --- | required by the websockets. -defaultTestnetContractConfig :: Aff DefaultContractConfig -defaultTestnetContractConfig = do - wallet <- mkNamiWalletAff - configWithLogLevel TestnetId wallet Error - --- | Same as `defaultTestnetContractConfig` but with `Trace` config level. --- | Should be used for testing. -traceTestnetContractConfig :: Aff DefaultContractConfig -traceTestnetContractConfig = do - wallet <- mkNamiWalletAff - configWithLogLevel TestnetId wallet Trace - -configWithLogLevel - :: NetworkId -> Wallet -> LogLevel -> Aff DefaultContractConfig -configWithLogLevel networkId wallet logLevel = do - ogmiosWs <- QueryM.mkOgmiosWebSocketAff logLevel QueryM.defaultOgmiosWsConfig - datumCacheWs <- - QueryM.mkDatumCacheWebSocketAff logLevel QueryM.defaultDatumCacheWsConfig - pparams <- Ogmios.getProtocolParametersAff ogmiosWs logLevel - usedTxOuts <- newUsedTxOuts - pure $ ContractConfig - { ogmiosWs - , datumCacheWs - , wallet: Just wallet - , usedTxOuts - , serverConfig: QueryM.defaultServerConfig - , networkId - , logLevel - , pparams - } - --- Logging effects - -logTrace - :: forall (m :: Type -> Type). MonadLogger m => TagSet -> String -> m Unit -logTrace = Logger.trace - -logDebug - :: forall (m :: Type -> Type). MonadLogger m => TagSet -> String -> m Unit -logDebug = Logger.debug - -logInfo - :: forall (m :: Type -> Type). MonadLogger m => TagSet -> String -> m Unit -logInfo = Logger.info - -logWarn - :: forall (m :: Type -> Type). MonadLogger m => TagSet -> String -> m Unit -logWarn = Logger.warn - -logError - :: forall (m :: Type -> Type). MonadLogger m => TagSet -> String -> m Unit -logError = Logger.error - -logTrace' - :: forall (m :: Type -> Type). MonadLogger m => String -> m Unit -logTrace' = Logger.trace Map.empty - -logDebug' - :: forall (m :: Type -> Type). MonadLogger m => String -> m Unit -logDebug' = Logger.debug Map.empty - -logInfo' - :: forall (m :: Type -> Type). MonadLogger m => String -> m Unit -logInfo' = Logger.info Map.empty - -logWarn' - :: forall (m :: Type -> Type). MonadLogger m => String -> m Unit -logWarn' = Logger.warn Map.empty - -logError' - :: forall (m :: Type -> Type). MonadLogger m => String -> m Unit -logError' = Logger.error Map.empty - --- | Log JSON representation of a data structure -logAeson - :: forall (m :: Type -> Type) (a :: Type) - . MonadLogger m - => EncodeAeson a - => (String -> m Unit) - -- ^ Logging function to use - -> a - -- ^ Data structure to output - -> m Unit -logAeson logger = logger <<< stringifyAeson <<< encodeAeson - -logAesonTrace - :: forall (m :: Type -> Type) (a :: Type) - . MonadLogger m - => EncodeAeson a - => a - -- ^ Data structure to output - -> m Unit -logAesonTrace = logAeson logTrace' - -logAesonDebug - :: forall (m :: Type -> Type) (a :: Type) - . MonadLogger m - => EncodeAeson a - => a - -- ^ Data structure to output - -> m Unit -logAesonDebug = logAeson logDebug' - -logAesonInfo - :: forall (m :: Type -> Type) (a :: Type) - . MonadLogger m - => EncodeAeson a - => a - -- ^ Data structure to output - -> m Unit -logAesonInfo = logAeson logInfo' - -logAesonWarn - :: forall (m :: Type -> Type) (a :: Type) - . MonadLogger m - => EncodeAeson a - => a - -- ^ Data structure to output - -> m Unit -logAesonWarn = logAeson logWarn' - -logAesonError - :: forall (m :: Type -> Type) (a :: Type) - . MonadLogger m - => EncodeAeson a - => a - -- ^ Data structure to output - -> m Unit -logAesonError = logAeson logError' diff --git a/src/Contract/PlutusData.purs b/src/Contract/PlutusData.purs index 0b473519b5..3a1c4de723 100644 --- a/src/Contract/PlutusData.purs +++ b/src/Contract/PlutusData.purs @@ -9,6 +9,7 @@ module Contract.PlutusData , module Datum , module ExportQueryM , module Hashing + , module IsData , module PlutusData , module Redeemer , module FromData @@ -95,6 +96,7 @@ import Types.Redeemer , redeemerHash , unitRedeemer ) as Redeemer +import IsData (class IsData) as IsData -- | Get a `PlutusData` given a `DatumHash`. getDatumByHash diff --git a/src/Contract/Prelude.purs b/src/Contract/Prelude.purs index 3d1f1fa954..fe9cbe7523 100644 --- a/src/Contract/Prelude.purs +++ b/src/Contract/Prelude.purs @@ -9,6 +9,7 @@ module Contract.Prelude , module Enum , module Foldable , module Generic + , module Log.Level , module Maybe , module Newtype , module PurescriptPrelude @@ -204,6 +205,7 @@ import Data.Foldable , surroundMap ) as Foldable import Data.Generic.Rep (class Generic) as Generic +import Data.Log.Level (LogLevel(Trace, Debug, Info, Warn, Error)) as Log.Level import Data.Maybe ( Maybe(Just, Nothing) , fromJust diff --git a/src/Contract/ScriptLookups.purs b/src/Contract/ScriptLookups.purs index a8fe2459c7..f5fa0c8d00 100644 --- a/src/Contract/ScriptLookups.purs +++ b/src/Contract/ScriptLookups.purs @@ -18,8 +18,7 @@ import Prelude import Contract.Monad (Contract, wrapContract) import Data.Either (Either, hush) import Data.Maybe (Maybe) -import FromData (class FromData) -import ToData (class ToData) +import IsData (class IsData) import Types.ScriptLookups ( MkUnbalancedTxError ( TypeCheckFailed @@ -67,10 +66,7 @@ import Types.ScriptLookups ) as ScriptLookups import Types.ScriptLookups (mkUnbalancedTx) as SL import Types.TxConstraints (TxConstraints) -import Types.TypedValidator - ( class DatumType - , class RedeemerType - ) +import Types.TypedValidator (class ValidatorTypes) -- | Create an `UnattachedUnbalancedTx` given `ScriptLookups` and -- | `TxConstraints`. You will probably want to use this version as it returns @@ -78,13 +74,13 @@ import Types.TypedValidator -- | a separate call. In particular, this should be called in conjuction with -- | `balanceAndSignTx`. mkUnbalancedTx - :: forall (r :: Row Type) (a :: Type) (b :: Type) - . DatumType a b - => RedeemerType a b - => FromData b - => ToData b - => ScriptLookups.ScriptLookups a - -> TxConstraints b b + :: forall (r :: Row Type) (validator :: Type) (datum :: Type) + (redeemer :: Type) + . ValidatorTypes validator datum redeemer + => IsData datum + => IsData redeemer + => ScriptLookups.ScriptLookups validator + -> TxConstraints redeemer datum -> Contract r ( Either ScriptLookups.MkUnbalancedTxError @@ -94,12 +90,12 @@ mkUnbalancedTx lookups = wrapContract <<< SL.mkUnbalancedTx lookups -- | Same as `mkUnbalancedTx` but hushes the error. mkUnbalancedTxM - :: forall (r :: Row Type) (a :: Type) (b :: Type) - . DatumType a b - => RedeemerType a b - => FromData b - => ToData b - => ScriptLookups.ScriptLookups a - -> TxConstraints b b + :: forall (r :: Row Type) (validator :: Type) (datum :: Type) + (redeemer :: Type) + . ValidatorTypes validator datum redeemer + => IsData datum + => IsData redeemer + => ScriptLookups.ScriptLookups validator + -> TxConstraints redeemer datum -> Contract r (Maybe ScriptLookups.UnattachedUnbalancedTx) mkUnbalancedTxM lookups = map hush <<< mkUnbalancedTx lookups diff --git a/src/Contract/Test/E2E.purs b/src/Contract/Test/E2E.purs new file mode 100644 index 0000000000..8d1c388660 --- /dev/null +++ b/src/Contract/Test/E2E.purs @@ -0,0 +1,33 @@ +module Contract.Test.E2E + ( module Browser + , module Feedback + , module Helpers + ) where + +import Contract.Test.E2E.Browser + ( Mode(Headless, Visible) + , TestOptions(TestOptions) + , WalletExt(GeroExt, NamiExt) + , withBrowser + , parseOptions + ) as Browser + +import Contract.Test.E2E.Feedback + ( publishTestFeedback + , retrieveTestFeedback + , resetTestFeedback + , testFeedbackIsTrue + ) as Feedback + +import Contract.Test.E2E.Helpers + ( E2EOutput + , RunningExample(RunningExample) + , WalletPassword(WalletPassword) + , checkSuccess + , delaySec + , geroConfirmAccess + , geroSign + , namiConfirmAccess + , namiSign + , withExample + ) as Helpers diff --git a/src/Contract/Test/E2E/Browser.purs b/src/Contract/Test/E2E/Browser.purs new file mode 100644 index 0000000000..4e3bee26bd --- /dev/null +++ b/src/Contract/Test/E2E/Browser.purs @@ -0,0 +1,124 @@ +module Contract.Test.E2E.Browser + ( Mode(Headless, Visible) + , TestOptions(TestOptions) + , WalletExt(GeroExt, NamiExt) + , withBrowser + , parseOptions + ) where + +import Prelude + +import Data.Foldable (fold) +import Data.Maybe (Maybe(Just, Nothing), fromMaybe) +import Effect (Effect) +import Effect.Aff (Aff, bracket) +import Node.Path (FilePath) +import Options.Applicative + ( Parser + , execParser + , help + , long + , metavar + , option + , showDefault + , str + , strOption + , switch + , value + , info + , fullDesc + ) +import Toppokki as Toppokki + +data WalletExt = NamiExt | GeroExt + +-- | Parameters for E2E tests +-- | 'chromeExe' should point to a chromium or google-chrome binary. +-- | 'namiDir' should point to a directory where Nami is unpacked. +-- | 'geroDir' should point to a directory where Gero is unpacked. +-- | both Nami and Gero must be provided to run the full suite of tests. +-- | 'chromeUserDataDir' should point to a chromium profile directory. If +-- | not provided, test-data/chrome-user-data is used. +-- | 'make e2e-test' then takes care to unpack Nami and Gero settings to there, +-- | so that wallet data is available. +-- | 'noHeadless' is a flag to display the browser during the tests. +data TestOptions = TestOptions + { chromeExe :: Maybe FilePath + , namiDir :: FilePath + , geroDir :: FilePath + , chromeUserDataDir :: FilePath + , noHeadless :: Boolean + } + +data Mode = Headless | Visible + +derive instance Eq Mode + +optParser :: Parser TestOptions +optParser = ado + chromeExe <- option (Just <$> str) $ fold + [ long "chrome-exe" + , metavar "FILE" + , help "Chrome/-ium exe (search in env if not set)" + , value Nothing + ] + namiDir <- strOption $ fold + [ long "nami-dir" + , metavar "DIR" + , help "Directory where nami is unpacked" + ] + geroDir <- strOption $ fold + [ long "gero-dir" + , metavar "DIR" + , help "Directory where gero is unpacked" + ] + chromeUserDataDir <- strOption $ fold + [ long "chrome-user-data" + , help "Chrome/-ium user data dir" + , value "test-data/chrome-user-data" + , showDefault + , metavar "DIR" + ] + noHeadless <- switch $ fold + [ long "no-headless" + , help "Show visible browser window" + ] + in + TestOptions + { chromeExe, namiDir, geroDir, chromeUserDataDir, noHeadless } + +parseOptions :: Effect TestOptions +parseOptions = execParser $ info optParser fullDesc + +withBrowser + :: forall (a :: Type) + . TestOptions + -> WalletExt + -> (Toppokki.Browser -> Aff a) + -> Aff a +withBrowser opts ext = bracket (launchWithExtension ext opts) Toppokki.close + +launchWithExtension + :: WalletExt -> TestOptions -> Aff Toppokki.Browser +launchWithExtension + walletExt + (TestOptions { chromeExe, chromeUserDataDir, namiDir, geroDir, noHeadless }) = + Toppokki.launch + { args: + [ "--disable-extensions-except=" <> extDir + , "--load-extension=" <> extDir + ] <> if mode == Headless then [ "--headless=chrome" ] else [] + , headless: mode == Headless + , userDataDir: chromeUserDataDir + , executablePath: fromMaybe "" chromeExe + } + where + mode :: Mode + mode + | noHeadless = Visible + | otherwise = Headless + + extDir :: FilePath + extDir = case walletExt of + GeroExt -> geroDir + NamiExt -> namiDir diff --git a/src/Contract/Test/E2E/Feedback.js b/src/Contract/Test/E2E/Feedback.js new file mode 100644 index 0000000000..a78681ce10 --- /dev/null +++ b/src/Contract/Test/E2E/Feedback.js @@ -0,0 +1 @@ +exports._publishTestFeedback = value => () => (window.ctlTestFeedback = value); diff --git a/src/Contract/Test/E2E/Feedback.purs b/src/Contract/Test/E2E/Feedback.purs new file mode 100644 index 0000000000..a62906b73e --- /dev/null +++ b/src/Contract/Test/E2E/Feedback.purs @@ -0,0 +1,36 @@ +-- | Store and retrieve values in simple JS variable (window.ctlTestFeedback), +-- | in order to establish a communication between the Examples and E2E tests. +-- | Retrieval must be called from Puppeteer, while publishing happens in the browser. +module Contract.Test.E2E.Feedback + ( publishTestFeedback + , retrieveTestFeedback + , resetTestFeedback + , testFeedbackIsTrue + ) where + +import Prelude + +import Effect (Effect) +import Effect.Aff (Aff) +import Effect.Class (liftEffect) +import Foreign (Foreign, unsafeFromForeign) +import Toppokki as Toppokki + +foreign import _publishTestFeedback :: forall (a :: Type). a -> Effect Unit + +-- | Store an arbitrary value as feedback +publishTestFeedback :: forall (a :: Type). a -> Aff Unit +publishTestFeedback = liftEffect <<< _publishTestFeedback + +resetTestFeedback :: Toppokki.Page -> Aff Unit +resetTestFeedback = void <<< Toppokki.unsafeEvaluateStringFunction + "window.ctlTestFeedback = false;" + +-- | Retrieve the feedback value +retrieveTestFeedback :: Toppokki.Page -> Aff Foreign +retrieveTestFeedback = Toppokki.unsafeEvaluateStringFunction + "window.ctlTestFeedback" + +-- | Convenience function for boolean feedback +testFeedbackIsTrue :: Toppokki.Page -> Aff Boolean +testFeedbackIsTrue page = unsafeFromForeign <$> retrieveTestFeedback page diff --git a/src/Contract/Test/E2E/Helpers.js b/src/Contract/Test/E2E/Helpers.js new file mode 100644 index 0000000000..a4a5347930 --- /dev/null +++ b/src/Contract/Test/E2E/Helpers.js @@ -0,0 +1,11 @@ +exports._retrieveJQuery = page => () => + page.evaluate(() => + window + .fetch( + "https://cdnjs.cloudflare.com/ajax/libs/jquery/3.6.0/jquery.min.js" + ) + .then(res => res.text()) + ); + +exports._typeInto = selector => text => page => () => + page.focus(selector).then(() => page.keyboard.type(text)); diff --git a/src/Contract/Test/E2E/Helpers.purs b/src/Contract/Test/E2E/Helpers.purs new file mode 100644 index 0000000000..0495b60fc4 --- /dev/null +++ b/src/Contract/Test/E2E/Helpers.purs @@ -0,0 +1,335 @@ +{- | This module is intended to be used for running custom E2E-tests -} +module Contract.Test.E2E.Helpers + ( E2EOutput + , RunningExample(RunningExample) + , WalletPassword(WalletPassword) + , checkSuccess + , delaySec + , geroConfirmAccess + , geroSign + , namiConfirmAccess + , namiSign + , withExample + ) where + +import Prelude + +import Contract.Test.E2E.Feedback (testFeedbackIsTrue) +import Control.Alternative ((<|>)) +import Control.Promise (Promise, toAffE) +import Data.Array (head, filterA, elem, any) +import Data.Foldable (intercalate) +import Data.Maybe (Maybe(Just, Nothing)) +import Data.Newtype (class Newtype, wrap, unwrap) +import Data.Traversable (for, fold) +import Effect (Effect) +import Effect.Aff (Aff, bracket, launchAff_, delay) +import Effect.Class (liftEffect) +import Effect.Console (log) +import Effect.Exception (throw) +import Effect.Ref (Ref) +import Effect.Ref as Ref +import Effect.Uncurried (mkEffectFn1, EffectFn1) +import Foreign (Foreign, unsafeFromForeign) +import Toppokki as Toppokki + +data OutputType = PageError | Console | RequestFailed + +derive instance Eq OutputType + +newtype E2EOutput = E2EOutput + { outputType :: OutputType + , output :: String + } + +newtype RunningExample = RunningExample + { browser :: Toppokki.Browser + , jQuery :: String + , main :: Toppokki.Page + , errors :: Ref (Array E2EOutput) + } + +derive instance Newtype RunningExample _ + +newtype WalletPassword = WalletPassword String + +derive instance Newtype WalletPassword _ + +-- | Download jQuery +retrieveJQuery :: Toppokki.Page -> Aff String +retrieveJQuery = toAffE <<< _retrieveJQuery + +-- | Simulate physical typing into a form element. +typeInto :: Selector -> String -> Toppokki.Page -> Aff Unit +typeInto selector text page = toAffE $ _typeInto selector text page + +-- | Count how many items match a selector +jQueryCount :: Selector -> Toppokki.Page -> Aff Int +jQueryCount selector page = unsafeFromForeign <$> doJQ selector (wrap "length") + page + +-- | Check if a selector is matched on a page +hasSelector :: Selector -> Toppokki.Page -> Aff Boolean +hasSelector selector page = (_ > 0) <$> jQueryCount selector page + +-- | Find the popup page of the wallet. This works for both Nami and Gero +-- | by looking for a page with a button. If there is a button on the main page +-- | this needs to be modified. +findWalletPage :: String -> Toppokki.Browser -> Aff (Maybe Toppokki.Page) +findWalletPage jQuery browser = do + pages <- injectJQueryAll jQuery browser + pages' <- filterA (hasSelector button) pages + pure $ head $ pages' + +-- | Wait until the wallet page pops up. Timout should be at least a few seconds. +-- | The 'String' param is the text of jQuery, which will be injected. +waitForWalletPage :: String -> Number -> Toppokki.Browser -> Aff Toppokki.Page +waitForWalletPage jQuery timeout browser = + findWalletPage jQuery browser >>= case _ of + Nothing -> do + if timeout > 0.0 then do + delaySec 0.1 + waitForWalletPage jQuery (timeout - 0.1) browser + else liftEffect $ throw "Wallet did not open" + Just page -> pure page + +showOutput :: Ref (Array E2EOutput) -> Effect String +showOutput ref = + Ref.read ref >>= map show' >>> intercalate "\n" >>> pure + where + show' :: E2EOutput -> String + show' (E2EOutput { outputType, output }) = showType outputType <> " " <> + output + + showType :: OutputType -> String + showType PageError = "ERR" + showType Console = "..." + showType RequestFailed = "REQ" + +-- | Navigate to an example's page, inject jQuery and set up error handlers +startExample :: Toppokki.URL -> Toppokki.Browser -> Aff RunningExample +startExample url browser = do + page <- Toppokki.newPage browser + jQuery <- retrieveJQuery page + errorRef <- liftEffect $ Ref.new [] + liftEffect do + Toppokki.onPageError (handler errorRef PageError $ pure <<< show) page + Toppokki.onConsole (handler errorRef Console Toppokki.consoleMessageText) + page + Toppokki.onRequestFailed (handler errorRef RequestFailed $ pure <<< show) + page + Toppokki.goto url page + pure $ wrap + { browser: browser + , jQuery: jQuery + , main: page + , errors: errorRef + } + where + + -- Setup a handler for an output type. + handler + :: forall (a :: Type) + . Ref (Array (E2EOutput)) + -> OutputType + -> (a -> Aff String) + -> EffectFn1 a Unit + handler errorRef outputType f = mkEffectFn1 $ \e -> launchAff_ do + output <- f e + liftEffect $ Ref.modify_ + ( _ <> + [ E2EOutput + { outputType: outputType + , output: output + } + ] + ) + errorRef + +-- | Run an example in the browser and close the browser afterwards +-- | A Wallet page will be detected. +-- | Sample usage: +-- | withBrowser options NamiExt $ \browser -> do +-- | withExample +-- | (wrap "http://myserver:1234/docontract") +-- | browser $ do +-- | namiSign $ wrap "mypassword" +withExample + :: forall (a :: Type) + . Toppokki.URL + -> Toppokki.Browser + -> (RunningExample -> Aff a) + -> Aff a +withExample url browser = bracket (startExample url browser) + (const $ pure unit) + +waitForTestFeedback :: RunningExample -> Number -> Aff Unit +waitForTestFeedback ex@(RunningExample { main, errors }) timeout + | timeout <= 0.0 = pure unit + | otherwise = + do + done <- + testFeedbackIsTrue main <|> + any isError <$> liftEffect (Ref.read errors) + delaySec 1.0 + when (not done) $ waitForTestFeedback ex (timeout - 1.0) + where + isError :: E2EOutput -> Boolean + isError (E2EOutput { outputType }) = outputType `elem` + [ PageError, RequestFailed ] + +checkSuccess :: RunningExample -> Aff Boolean +checkSuccess ex@(RunningExample { main, errors }) = do + waitForTestFeedback ex 50.0 + feedback <- testFeedbackIsTrue main + unless feedback $ liftEffect $ showOutput errors >>= log + pure feedback + +inWalletPage + :: forall (a :: Type). RunningExample -> (Toppokki.Page -> Aff a) -> Aff a +inWalletPage (RunningExample { browser, jQuery }) = + (waitForWalletPage jQuery 10.0 browser >>= _) + +namiConfirmAccess :: RunningExample -> Aff Unit +namiConfirmAccess = flip inWalletPage (clickButton "Access") + +namiSign :: WalletPassword -> RunningExample -> Aff Unit +namiSign wpassword = flip inWalletPage \nami -> do + clickButton "Sign" nami + reactSetValue password (unwrap wpassword) nami + clickButton "Confirm" nami + +geroConfirmAccess :: RunningExample -> Aff Unit +geroConfirmAccess = + flip inWalletPage $ \page -> do + delaySec 0.1 + void $ doJQ (inputType "radio") click page + void $ doJQ (buttonWithText "Continue") click page + delaySec 0.1 + void $ doJQ (inputType "checkbox") click page + void $ doJQ (buttonWithText "Connect") click page + +geroSign :: WalletPassword -> RunningExample -> Aff Unit +geroSign gpassword = + flip inWalletPage $ \gero -> do + void $ doJQ (byId "confirm-swap") click gero + typeInto (byId "wallet-password") (unwrap gpassword) gero + clickButton "Next" gero + +-- | A String representing a jQuery selector, e.g. "#my-id" or ".my-class" +newtype Selector = Selector String + +derive instance Newtype Selector _ + +-- | A String representing a jQuery action, e.g. "click" or "enable". +newtype Action = Action String + +derive instance Newtype Action _ + +-- | Build a primitive jQuery expression like '$("button").click()' and +-- | out of a selector and action and evaluate it in Toppokki +doJQ :: Selector -> Action -> Toppokki.Page -> Aff Foreign +doJQ selector action page = do + Toppokki.unsafeEvaluateStringFunction jq page + where + jq :: String + jq = "$('" <> unwrap selector <> "')." <> unwrap action + +-- | select any button +button :: Selector +button = wrap "button" + +-- | select a button with a specific text inside +buttonWithText :: String -> Selector +buttonWithText text = wrap $ "button:contains(" <> text <> ")" + +-- | select an input element of a specific type +inputType :: String -> Selector +inputType typ = wrap $ "input[type=\"" <> typ <> "\"]" + +-- | select an element by Id +byId :: String -> Selector +byId = wrap <<< ("#" <> _) + +-- | select any password field +password :: Selector +password = wrap ":password" + +click :: Action +click = wrap "click()" + +clickButton :: String -> Toppokki.Page -> Aff Unit +clickButton buttonText = void <$> doJQ (buttonWithText buttonText) click + +-- | inject jQuery into all pages in the browser which don't have it yet. +injectJQueryAll :: String -> Toppokki.Browser -> Aff (Array Toppokki.Page) +injectJQueryAll jQuery browser = do + pages <- Toppokki.pages browser + void $ for pages $ \page -> do + (alreadyInjected :: Boolean) <- + unsafeFromForeign <$> + Toppokki.unsafeEvaluateStringFunction "typeof(jQuery) !== 'undefined'" + page + unless alreadyInjected $ void $ Toppokki.unsafeEvaluateStringFunction jQuery + page + pure pages + +-- | Set the value of an item with the browser's native value setter. +-- | This is necessary for react items so that react reacts. +-- | (sometimes 'typeInto' is an alternative). +-- | React is used in Nami. +-- | https://stackoverflow.com/questions/23892547/what-is-the-best-way-to-trigger-onchange-event-in-react-js +reactSetValue :: Selector -> String -> Toppokki.Page -> Aff Unit +reactSetValue selector value page = void + $ flip Toppokki.unsafeEvaluateStringFunction page + $ fold + [ "let input = $('" <> unwrap selector <> "').get(0);" + , "var nativeInputValueSetter = " + <> + " Object.getOwnPropertyDescriptor(window.HTMLInputElement.prototype, 'value').set;" + , "nativeInputValueSetter.call(input, '" <> value <> "');" + , "var ev2 = new Event('input', { bubbles: true});" + , "input.dispatchEvent(ev2);" + ] + +foreign import _retrieveJQuery :: Toppokki.Page -> Effect (Promise String) + +foreign import _typeInto + :: Selector -> String -> Toppokki.Page -> Effect (Promise Unit) + +delaySec :: Number -> Aff Unit +delaySec seconds = delay $ wrap $ seconds * 1000.0 + +{- +-- This can initialize gero and import a walllet without storing any config data +-- anywhere. However, there doesn't seem to be a straightforward way to set the collateral, +-- which makes it rather useless, as this would involve opening the extension popup window, +-- and that is currently not supported in Puppeteer. + +geroInit :: Toppokki.Browser -> Aff Unit +geroInit browser = do + page <- Toppokki.newPage browser + jQuery <- retrieveJQuery page + Toppokki.goto + (wrap "chrome-extension://iifeegfcfhlhhnilhfoeihllenamcfgc/index.html") + page + void $ Toppokki.unsafeEvaluateStringFunction jQuery page + delay $ wrap 1000.0 + void $ doJQ (buttonWithText "Get Started") click page + delay $ wrap 1000.0 + void $ doJQ (wrap "a[href=\"#/wallet-options/import-wallet\"]") click page + delay $ wrap 100.0 + reactSetValue (byId "wallet-name") "ctltest" page + typeInto textarea geroSeed page + void $ doJQ (buttonWithText "Import") click page + delay $ wrap 100.0 + typeInto (byId "wallet-password") testPasswordGero page + typeInto (byId "wallet-password-confirm") testPasswordGero page + void $ doJQ (byId "user-agree") click page + void $ doJQ (byId "password-agree") click page + void $ doJQ (byId "set-up-password") click page + delay $ wrap 100.0 + void $ doJQ (buttonWithText "Continue") click page + delay $ wrap 100.0 + void $ doJQ (byId "done") click page +-} diff --git a/src/Contract/Test/Plutip.purs b/src/Contract/Test/Plutip.purs new file mode 100644 index 0000000000..71d87799b5 --- /dev/null +++ b/src/Contract/Test/Plutip.purs @@ -0,0 +1,20 @@ +-- | This module contains everything needed for `Contract` testing in Plutip +-- | environment. +module Contract.Test.Plutip + ( module X + ) where + +import Plutip.Server + ( runPlutipContract + , withPlutipContractEnv + ) as X +import Contract.Monad (runContractInEnv) as X +import Plutip.Types + ( PlutipConfig + , PostgresConfig + , UtxoAmount + , InitialUTxO + , InitialUTxODistribution + , class UtxoDistribution + ) as X +import Contract.Wallet (withKeyWallet) as X diff --git a/src/Contract/Transaction.purs b/src/Contract/Transaction.purs index 6f793dcac1..16d3f6be0f 100644 --- a/src/Contract/Transaction.purs +++ b/src/Contract/Transaction.purs @@ -4,14 +4,17 @@ module Contract.Transaction ( BalancedSignedTransaction(BalancedSignedTransaction) , awaitTxConfirmed , awaitTxConfirmedWithTimeout + , awaitTxConfirmedWithTimeoutSlots , balanceAndSignTx , balanceAndSignTxs , balanceAndSignTxE , balanceAndSignTxM , balanceTx + , balanceTxWithAddress , balanceTxM , calculateMinFee , calculateMinFeeM + , getTxByHash , module BalanceTxError , module ExportQueryM , module PTransaction @@ -24,18 +27,20 @@ module Contract.Transaction , scriptOutputToTransactionOutput , signTransaction , submit + , submitE , withBalancedTxs , withBalancedTx , withBalancedAndSignedTxs , withBalancedAndSignedTx + , balanceTxsWithAddress ) where import Prelude -import Aeson (class EncodeAeson) +import Aeson (class EncodeAeson, Aeson) import BalanceTx (BalanceTxError) as BalanceTxError import BalanceTx (FinalizedTransaction) -import BalanceTx (balanceTx) as BalanceTx +import BalanceTx (balanceTx, balanceTxWithAddress) as BalanceTx import Cardano.Types.Transaction ( AuxiliaryData(AuxiliaryData) , AuxiliaryDataHash(AuxiliaryDataHash) @@ -107,13 +112,14 @@ import Cardano.Types.Transaction , _witnessSet ) as Transaction import Cardano.Types.Transaction (Transaction) +import Contract.Address (getWalletAddress) import Contract.Monad (Contract, liftedE, liftedM, wrapContract) import Control.Monad.Error.Class (try, catchError, throwError) import Control.Monad.Reader (asks, runReaderT, ReaderT) import Data.Array.NonEmpty as NonEmptyArray -import Data.Either (Either, hush) +import Data.Either (Either(Left, Right), hush) import Data.Generic.Rep (class Generic) -import Data.Maybe (Maybe) +import Data.Maybe (Maybe(Just, Nothing)) import Data.Newtype (class Newtype, unwrap, wrap) import Data.Show.Generic (genericShow) import Data.Time.Duration (Seconds) @@ -122,6 +128,8 @@ import Data.Tuple.Nested (type (/\)) import Effect.Class (liftEffect) import Effect.Exception (Error, throw) import Plutus.Conversion (toPlutusCoin, toPlutusTxOutput) +import Plutus.Conversion.Address (fromPlutusAddress) +import Plutus.Types.Address (Address) import Plutus.Types.Transaction (TransactionOutput(TransactionOutput)) as PTransaction import Plutus.Types.Value (Coin) import QueryM @@ -134,13 +142,14 @@ import QueryM , ClientOtherError ) ) as ExportQueryM -import QueryM +import QueryM (calculateMinFee, signTransaction, submitTxOgmios) as QueryM +import QueryM.AwaitTxConfirmed ( awaitTxConfirmed , awaitTxConfirmedWithTimeout - , calculateMinFee - , signTransaction - , submitTxOgmios - ) as QueryM + , awaitTxConfirmedWithTimeoutSlots + ) as AwaitTx +import QueryM.GetTxByHash (getTxByHash) as QueryM +import QueryM.Ogmios (SubmitTxR(SubmitTxSuccess, SubmitFail)) import ReindexRedeemers (ReindexErrors(CannotGetTxOutRefIndexForRedeemer)) as ReindexRedeemersExport import ReindexRedeemers (reindexSpentScriptRedeemers) as ReindexRedeemers import Serialization (convertTransaction, toBytes) as Serialization @@ -222,11 +231,28 @@ submit :: forall (r :: Row Type) . BalancedSignedTransaction -> Contract r TransactionHash -submit tx = wrapContract <<< map (wrap <<< unwrap) <<< QueryM.submitTxOgmios =<< - liftEffect - ( wrap <<< Serialization.toBytes <<< asOneOf <$> - Serialization.convertTransaction (unwrap tx) - ) +submit tx = do + result <- submitE tx + case result of + Right th -> pure th + Left json -> liftEffect $ throw $ + "`submit` call failed. Error from Ogmios: " <> show json + +-- | Like submit except when ogmios sends a SubmitFail +-- | the error is returned as an Array of Aesons +submitE + :: forall (r :: Row Type) + . BalancedSignedTransaction + -> Contract r (Either (Array Aeson) TransactionHash) +submitE tx = do + result <- wrapContract <<< QueryM.submitTxOgmios =<< + liftEffect + ( wrap <<< Serialization.toBytes <<< asOneOf <$> + Serialization.convertTransaction (unwrap tx) + ) + pure $ case result of + SubmitTxSuccess th -> Right $ wrap th + SubmitFail json -> Left json -- | Query the Haskell server for the minimum transaction fee calculateMinFee @@ -247,7 +273,7 @@ withUsedTxouts :: forall (r :: Row Type) (a :: Type) . ReaderT UsedTxOuts (Contract r) a -> Contract r a -withUsedTxouts f = asks (_.usedTxOuts <<< unwrap) >>= runReaderT f +withUsedTxouts f = asks (_.usedTxOuts <<< _.runtime <<< unwrap) >>= runReaderT f -- | Attempts to balance an `UnattachedUnbalancedTx`. balanceTx @@ -256,6 +282,18 @@ balanceTx -> Contract r (Either BalanceTxError.BalanceTxError FinalizedTransaction) balanceTx = wrapContract <<< BalanceTx.balanceTx +-- | Attempts to balance an `UnattachedUnbalancedTx`. +balanceTxWithAddress + :: forall (r :: Row Type) + . Address + -> UnattachedUnbalancedTx + -> Contract r (Either BalanceTxError.BalanceTxError FinalizedTransaction) +balanceTxWithAddress addr tx = do + networkId <- asks $ unwrap >>> _.config >>> _.networkId + wrapContract $ BalanceTx.balanceTxWithAddress + (fromPlutusAddress networkId addr) + tx + -- Helper to avoid repetition withTransactions :: forall (a :: Type) @@ -343,16 +381,17 @@ withBalancedAndSignedTx = withSingleTransaction (liftedE <<< balanceAndSignTxE) unwrap --- | Balances each transaction and locks the used inputs --- | so that they cannot be reused by subsequent transactions. -balanceTxs +-- | Like `balanceTxs`, but uses `balanceTxWithAddress` instead of `balanceTx` +-- | internally. +balanceTxsWithAddress :: forall (t :: Type -> Type) (r :: Row Type) . Traversable t - => t UnattachedUnbalancedTx + => Address + -> t UnattachedUnbalancedTx -> Contract r (t FinalizedTransaction) -balanceTxs unbalancedTxs = +balanceTxsWithAddress ownAddress unbalancedTxs = unlockAllOnError $ traverse balanceAndLock unbalancedTxs where unlockAllOnError :: forall (a :: Type). Contract r a -> Contract r a @@ -366,10 +405,30 @@ balanceTxs unbalancedTxs = balanceAndLock :: UnattachedUnbalancedTx -> Contract r FinalizedTransaction balanceAndLock unbalancedTx = do - balancedTx <- liftedE $ balanceTx unbalancedTx + networkId <- asks $ unwrap >>> _.config >>> _.networkId + balancedTx <- liftedE $ wrapContract $ BalanceTx.balanceTxWithAddress + (fromPlutusAddress networkId ownAddress) + unbalancedTx void $ withUsedTxouts $ lockTransactionInputs (unwrap balancedTx) pure balancedTx +-- | Balances each transaction and locks the used inputs +-- | so that they cannot be reused by subsequent transactions. +balanceTxs + :: forall + (t :: Type -> Type) + (r :: Row Type) + . Traversable t + => t UnattachedUnbalancedTx + -> Contract r (t FinalizedTransaction) +balanceTxs unbalancedTxs = do + mbOwnAddress <- getWalletAddress + case mbOwnAddress of + Nothing -> liftEffect $ throw $ + "Failed to get own Address" + Just ownAddress -> + balanceTxsWithAddress ownAddress unbalancedTxs + -- | Attempts to balance an `UnattachedUnbalancedTx` hushing the error. balanceTxM :: forall (r :: Row Type) @@ -459,15 +518,22 @@ scriptOutputToTransactionOutput networkId = toPlutusTxOutput <<< TxOutput.scriptOutputToTransactionOutput networkId +-- | Get `Transaction` contents by hash +getTxByHash + :: forall (r :: Row Type) + . TransactionHash + -> Contract r (Maybe Transaction) +getTxByHash = wrapContract <<< QueryM.getTxByHash <<< unwrap + -- | Wait until a transaction with given hash is confirmed. -- | Use `awaitTxConfirmedWithTimeout` if you want to limit the time of waiting. awaitTxConfirmed :: forall (r :: Row Type) . TransactionHash -> Contract r Unit -awaitTxConfirmed = wrapContract <<< QueryM.awaitTxConfirmed <<< unwrap +awaitTxConfirmed = wrapContract <<< AwaitTx.awaitTxConfirmed <<< unwrap --- | Same as `awaitTxConfirmed`, but allows to specify a timeout for waiting. +-- | Same as `awaitTxConfirmed`, but allows to specify a timeout in seconds for waiting. -- | Throws an exception on timeout. awaitTxConfirmedWithTimeout :: forall (r :: Row Type) @@ -475,5 +541,16 @@ awaitTxConfirmedWithTimeout -> TransactionHash -> Contract r Unit awaitTxConfirmedWithTimeout timeout = wrapContract - <<< QueryM.awaitTxConfirmedWithTimeout timeout + <<< AwaitTx.awaitTxConfirmedWithTimeout timeout + <<< unwrap + +-- | Same as `awaitTxConfirmed`, but allows to specify a timeout in slots for waiting. +-- | Throws an exception on timeout. +awaitTxConfirmedWithTimeoutSlots + :: forall (r :: Row Type) + . Int + -> TransactionHash + -> Contract r Unit +awaitTxConfirmedWithTimeoutSlots timeout = wrapContract + <<< AwaitTx.awaitTxConfirmedWithTimeoutSlots timeout <<< unwrap diff --git a/src/Contract/Utxos.purs b/src/Contract/Utxos.purs index 8e567c3a50..d0e4c33c11 100644 --- a/src/Contract/Utxos.purs +++ b/src/Contract/Utxos.purs @@ -41,7 +41,7 @@ utxosAt => Address -> Contract r (Maybe Transaction.UtxoM) utxosAt address = do - networkId <- asks (_.networkId <<< unwrap) + networkId <- asks (_.networkId <<< _.config <<< unwrap) let cardanoAddr = fromPlutusAddress networkId address -- Don't error if we get `Nothing` as the Cardano utxos mCardanoUtxos <- wrapContract $ Utxos.utxosAt cardanoAddr diff --git a/src/Contract/Wallet.purs b/src/Contract/Wallet.purs index d7d0026a36..7e405b67eb 100644 --- a/src/Contract/Wallet.purs +++ b/src/Contract/Wallet.purs @@ -1,27 +1,57 @@ -- | A module with Wallet-related functionality. module Contract.Wallet - ( mkKeyWalletFromPrivateKey - , module ContractAddress - , module Wallet + ( mkKeyWalletFromPrivateKeys + , withKeyWallet + , module Contract.Address , module Serialization + , module Wallet.Spec + , module Wallet.Key + , module Wallet ) where -import Contract.Address (getWalletAddress, getWalletCollateral) as ContractAddress -import Data.Maybe (Maybe) +import Prelude + +import Contract.Address (getWalletAddress, getWalletCollateral) +import Contract.Monad (Contract, ContractEnv) +import Control.Monad.Reader (local) +import Data.Lens (Lens, (.~)) +import Data.Lens.Common (simple) +import Data.Lens.Iso.Newtype (_Newtype) +import Data.Lens.Record (prop) +import Data.Maybe (Maybe(Just)) import Serialization (privateKeyFromBytes) as Serialization -import Wallet - ( Cip30Connection - , Cip30Wallet - , Wallet(Gero, Nami) - , isGeroAvailable - , isNamiAvailable - , mkGeroWalletAff - , mkNamiWalletAff - ) as Wallet -import Wallet (mkKeyWallet) +import Type.Proxy (Proxy(Proxy)) +import Wallet (isGeroAvailable, isNamiAvailable) as Wallet +import Wallet (mkKeyWallet, Wallet(KeyWallet)) +import Wallet.Spec + ( WalletSpec(UseKeys, ConnectToNami, ConnectToGero) + , PrivateStakeKeySource(PrivateStakeKeyFile, PrivateStakeKeyValue) + , PrivatePaymentKeySource(PrivatePaymentKeyFile, PrivatePaymentKeyValue) + ) import Wallet.Key (KeyWallet, privateKeysToKeyWallet) as Wallet import Wallet.Key (PrivatePaymentKey, PrivateStakeKey) -mkKeyWalletFromPrivateKey - :: PrivatePaymentKey -> Maybe PrivateStakeKey -> Wallet.Wallet -mkKeyWalletFromPrivateKey = mkKeyWallet +withKeyWallet + :: forall (r :: Row Type) (a :: Type) + . Wallet.KeyWallet + -> Contract r a + -> Contract r a +withKeyWallet wallet action = do + let + setUpdatedWallet :: ContractEnv r -> ContractEnv r + setUpdatedWallet = + simple _Newtype <<< _runtime <<< _wallet .~ + (Just (KeyWallet wallet)) + local setUpdatedWallet action + where + _wallet + :: forall x rest. Lens { wallet :: x | rest } { wallet :: x | rest } x x + _wallet = prop (Proxy :: Proxy "wallet") + + _runtime + :: forall x rest. Lens { runtime :: x | rest } { runtime :: x | rest } x x + _runtime = prop (Proxy :: Proxy "runtime") + +mkKeyWalletFromPrivateKeys + :: PrivatePaymentKey -> Maybe PrivateStakeKey -> Wallet +mkKeyWalletFromPrivateKeys = mkKeyWallet diff --git a/src/Deserialization/Transaction.js b/src/Deserialization/Transaction.js index 3bc9f07161..9ef44e4fcb 100644 --- a/src/Deserialization/Transaction.js +++ b/src/Deserialization/Transaction.js @@ -275,14 +275,10 @@ exports._unpackUnitInterval = ui => { }; }; -exports._unpackProtocolVersions = containerhelper => cslPV => { - const pvs = containerhelper.unpack(cslPV); - const res = []; - for (let i = 0; i < pvs.length; i++) { - res.push({ major: pvs[i].major(), minor: pvs[i].minor() }); - } - return res; -}; +exports._unpackProtocolVersion = cslPV => ({ + major: cslPV.major(), + minor: cslPV.minor(), +}); exports._unpackExUnitsPrices = cslEup => { return { diff --git a/src/Deserialization/Transaction.purs b/src/Deserialization/Transaction.purs index 42179418d3..b36d6d5c08 100644 --- a/src/Deserialization/Transaction.purs +++ b/src/Deserialization/Transaction.purs @@ -36,7 +36,7 @@ module Deserialization.Transaction , _unpackMint , _unpackMintAssets , _unpackProtocolParamUpdate - , _unpackProtocolVersions + , _unpackProtocolVersion , _unpackUnitInterval , _unpackUpdate , _unpackWithdrawals @@ -54,7 +54,7 @@ module Deserialization.Transaction , convertMint , convertNonce , convertProtocolParamUpdate - , convertProtocolVersions + , convertProtocolVersion , convertTransaction , convertTxBody , convertUpdate @@ -186,7 +186,7 @@ import Serialization.Types , PoolMetadata , PoolParams , ProtocolParamUpdate - , ProtocolVersions + , ProtocolVersion , Relay , ScriptDataHash , SingleHostAddr @@ -472,7 +472,7 @@ convertProtocolParamUpdate cslPpu = do maxEpoch <- traverse (map T.Epoch <<< cslNumberToUInt (lbl "maxEpoch")) ppu.maxEpoch nOpt <- traverse (cslNumberToUInt (lbl "nOpt")) ppu.nOpt - protocolVersion <- traverse (convertProtocolVersions (lbl "protocolVersion")) + protocolVersion <- traverse (convertProtocolVersion (lbl "protocolVersion")) ppu.protocolVersion costModels <- traverse (convertCostModels (lbl "costModels")) ppu.costModels maxTxExUnits <- traverse (convertExUnits (lbl "maxTxExUnits")) @@ -664,13 +664,13 @@ convertExUnits nm cslExunits = convertScriptDataHash :: Csl.ScriptDataHash -> T.ScriptDataHash convertScriptDataHash = asOneOf >>> toBytes >>> T.ScriptDataHash -convertProtocolVersions +convertProtocolVersion :: forall (r :: Row Type) . String - -> Csl.ProtocolVersions - -> E (FromCslRepError + r) (Array T.ProtocolVersion) -convertProtocolVersions nm cslPV = - for (_unpackProtocolVersions containerHelper cslPV) + -> Csl.ProtocolVersion + -> E (FromCslRepError + r) T.ProtocolVersion +convertProtocolVersion nm cslPV = + _unpackProtocolVersion cslPV # ( \{ major, minor } -> { major: _, minor: _ } <$> cslNumberToUInt (nm <> " major") major @@ -703,7 +703,7 @@ foreign import _unpackProtocolParamUpdate Maybe Csl.UnitInterval , d :: Maybe Csl.UnitInterval , extraEntropy :: Maybe Csl.Nonce - , protocolVersion :: Maybe Csl.ProtocolVersions + , protocolVersion :: Maybe Csl.ProtocolVersion , minPoolCost :: Maybe Csl.BigNum , adaPerUtxoByte :: Maybe Csl.BigNum , costModels :: Maybe Csl.Costmdls @@ -735,10 +735,9 @@ type MetadatumHelper (r :: Row Type) = , error :: String -> Err r TransactionMetadatum } -foreign import _unpackProtocolVersions - :: ContainerHelper - -> Csl.ProtocolVersions - -> Array { major :: Number, minor :: Number } +foreign import _unpackProtocolVersion + :: Csl.ProtocolVersion + -> { major :: Number, minor :: Number } foreign import _unpackExUnits :: Csl.ExUnits -> { mem :: Csl.BigNum, steps :: Csl.BigNum } diff --git a/src/IsData.purs b/src/IsData.purs new file mode 100644 index 0000000000..e0dfda96cd --- /dev/null +++ b/src/IsData.purs @@ -0,0 +1,9 @@ +module IsData (class IsData) where + +import FromData (class FromData) +import ToData (class ToData) + +class IsData :: Type -> Constraint +class (FromData a, ToData a) <= IsData a + +instance (FromData a, ToData a) => IsData a diff --git a/src/Metadata/Cip25.purs b/src/Metadata/Cip25.purs deleted file mode 100644 index eedf2c87c1..0000000000 --- a/src/Metadata/Cip25.purs +++ /dev/null @@ -1,332 +0,0 @@ -module Metadata.Cip25 - ( Cip25Metadata(Cip25Metadata) - , Cip25MetadataEntry(Cip25MetadataEntry) - , Cip25MetadataFile(Cip25MetadataFile) - ) where - -import Prelude - -import Aeson - ( class DecodeAeson - , Aeson - , JsonDecodeError - ( TypeMismatch - ) - , caseAesonArray - , caseAesonObject - , decodeAeson - , encodeAeson - , isString - , toString - , (.:) - , (.:?) - ) -import Aeson as Aeson -import Data.Array (concat, groupBy, uncons) -import Data.Array.NonEmpty (NonEmptyArray, toArray) -import Data.Array.NonEmpty (head) as NonEmpty -import Data.BigInt (fromInt) as BigInt -import Data.Either (Either(Left), note) -import Data.Generic.Rep (class Generic) -import Data.Map (toUnfoldable) as Map -import Data.Maybe (Maybe(Just, Nothing), maybe) -import Data.Newtype (class Newtype, wrap, unwrap) -import Data.NonEmpty (NonEmpty, (:|)) -import Data.Show.Generic (genericShow) -import Data.TextEncoder (encodeUtf8) -import Data.Traversable (for, traverse, sequence) -import Data.Tuple (Tuple) -import Data.Tuple.Nested ((/\)) -import Foreign.Object (Object, toUnfoldable) as FO -import Plutus.Types.AssocMap (Map(Map), singleton) as AssocMap -import FromData (class FromData, fromData) -import ToData (class ToData, toData) -import Metadata.Helpers (lookupKey, lookupMetadata) -import Metadata.FromMetadata (class FromMetadata, fromMetadata) -import Metadata.ToMetadata (class ToMetadata, toMetadata, anyToMetadata) -import Metadata.MetadataType (class MetadataType) -import Serialization.Hash (scriptHashFromBytes) -import Types.Scripts (MintingPolicyHash) -import Types.RawBytes (hexToRawBytes) -import Types.PlutusData (PlutusData(Map)) -import Types.TokenName (TokenName, mkTokenName) -import Types.TransactionMetadata (TransactionMetadatum(MetadataMap)) - -nftMetadataLabel :: String -nftMetadataLabel = "721" - --------------------------------------------------------------------------------- --- Cip25MetadataFile --------------------------------------------------------------------------------- - -newtype Cip25MetadataFile = Cip25MetadataFile - { name :: String - , mediaType :: String - , uris :: NonEmpty Array String - } - -derive instance Generic Cip25MetadataFile _ -derive instance Newtype Cip25MetadataFile _ -derive instance Eq Cip25MetadataFile - -instance Show Cip25MetadataFile where - show = genericShow - -instance ToMetadata Cip25MetadataFile where - toMetadata (Cip25MetadataFile file) = toMetadata - [ "name" /\ toMetadata file.name - , "mediaType" /\ toMetadata file.mediaType - , "src" /\ toMetadata file.uris - ] - -instance FromMetadata Cip25MetadataFile where - fromMetadata contents = do - name <- lookupMetadata "name" contents >>= fromMetadata - mediaType <- lookupMetadata "mediaType" contents >>= fromMetadata - uris <- lookupMetadata "src" contents >>= fromMetadata - pure $ wrap { name, mediaType, uris } - -instance ToData Cip25MetadataFile where - toData (Cip25MetadataFile file) = toData $ AssocMap.Map $ - [ "name" /\ toData file.name - , "mediaType" /\ toData file.mediaType - , "src" /\ toData file.uris - ] - -instance FromData Cip25MetadataFile where - fromData contents = do - name <- lookupKey "name" contents >>= fromData - mediaType <- lookupKey "mediaType" contents >>= fromData - uris <- lookupKey "src" contents >>= fromData - pure $ wrap { name, mediaType, uris } - -instance DecodeAeson Cip25MetadataFile where - decodeAeson = - caseAesonObject errExpectedObject $ \obj -> do - name <- obj .: "name" - mediaType <- obj .: "mediaType" - uris <- decodeNonEmptyStringArray =<< obj .: "src" - pure $ wrap { name, mediaType, uris } - --------------------------------------------------------------------------------- --- Cip25MetadataEntry --------------------------------------------------------------------------------- - -newtype Cip25MetadataEntry = Cip25MetadataEntry - { policyId :: MintingPolicyHash - , assetName :: TokenName - , imageUris :: NonEmpty Array String - , mediaType :: Maybe String - , description :: Array String - , files :: Array Cip25MetadataFile - } - -derive instance Generic Cip25MetadataEntry _ -derive instance Newtype Cip25MetadataEntry _ -derive instance Eq Cip25MetadataEntry - -instance Show Cip25MetadataEntry where - show = genericShow - -metadataEntryToMetadata :: Cip25MetadataEntry -> TransactionMetadatum -metadataEntryToMetadata (Cip25MetadataEntry entry) = toMetadata - [ "name" /\ anyToMetadata entry.assetName - , "image" /\ anyToMetadata entry.imageUris - , "mediaType" /\ anyToMetadata entry.mediaType - , "description" /\ anyToMetadata entry.description - , "files" /\ anyToMetadata entry.files - ] - -metadataEntryFromMetadata - :: MintingPolicyHash - -> TokenName - -> TransactionMetadatum - -> Maybe Cip25MetadataEntry -metadataEntryFromMetadata policyId assetName contents = do - imageUris <- lookupMetadata "image" contents >>= fromMetadata - let mediaType = lookupMetadata "mediaType" contents >>= fromMetadata - description <- lookupMetadata "description" contents >>= fromMetadata - files <- lookupMetadata "files" contents >>= fromMetadata - pure $ - wrap { policyId, assetName, imageUris, mediaType, description, files } - -metadataEntryToData :: Cip25MetadataEntry -> PlutusData -metadataEntryToData (Cip25MetadataEntry entry) = toData $ AssocMap.Map $ - [ "name" /\ toData entry.assetName - , "image" /\ toData entry.imageUris - , "mediaType" /\ toData entry.mediaType - , "description" /\ toData entry.description - , "files" /\ toData entry.files - ] - -metadataEntryFromData - :: MintingPolicyHash - -> TokenName - -> PlutusData - -> Maybe Cip25MetadataEntry -metadataEntryFromData policyId assetName contents = do - imageUris <- lookupKey "image" contents >>= fromData - mediaType <- lookupKey "mediaType" contents >>= fromData - description <- lookupKey "description" contents >>= fromData - files <- lookupKey "files" contents >>= fromData - pure $ - wrap { policyId, assetName, imageUris, mediaType, description, files } - -metadataEntryDecodeAeson - :: MintingPolicyHash - -> TokenName - -> Aeson - -> Either JsonDecodeError Cip25MetadataEntry -metadataEntryDecodeAeson policyId assetName = - caseAesonObject errExpectedObject $ \obj -> do - imageUris <- obj .: "image" >>= - decodeNonEmptyStringArray - mediaType <- obj .:? "mediaType" - description <- obj .:? "description" >>= - maybe (pure mempty) decodeStringArray - files <- obj .:? "files" >>= \arr -> - maybe (pure mempty) (traverse decodeAeson) (Aeson.toArray =<< arr) - pure $ - wrap { policyId, assetName, imageUris, mediaType, description, files } - --------------------------------------------------------------------------------- --- Cip25Metadata --------------------------------------------------------------------------------- - -newtype Cip25Metadata = Cip25Metadata (Array Cip25MetadataEntry) - -derive instance Generic Cip25Metadata _ -derive instance Newtype Cip25Metadata _ -derive instance Eq Cip25Metadata - -instance Show Cip25Metadata where - show = genericShow - -instance MetadataType Cip25Metadata where - metadataLabel _ = wrap (BigInt.fromInt 721) - -groupEntries - :: Array Cip25MetadataEntry -> Array (NonEmptyArray Cip25MetadataEntry) -groupEntries = - groupBy \(Cip25MetadataEntry a) (Cip25MetadataEntry b) -> - a.policyId == b.policyId - -instance ToMetadata Cip25Metadata where - toMetadata (Cip25Metadata entries) = toMetadata $ - groupEntries entries <#> - \group -> - (_.policyId <<< unwrap $ NonEmpty.head group) /\ - (toArray <<< flip map group) \entry -> - (unwrap entry).assetName /\ metadataEntryToMetadata entry - -instance FromMetadata Cip25Metadata where - fromMetadata (MetadataMap mp1) = do - entries <- map concat - $ for (Map.toUnfoldable mp1) - $ \(policyId /\ assets) -> - case assets of - MetadataMap mp2 -> - for (Map.toUnfoldable mp2) $ \(assetName /\ contents) -> - metadataEntryFromMetadata <$> fromMetadata policyId - <*> fromMetadata assetName - <*> pure contents - _ -> Nothing - wrap <$> sequence entries - fromMetadata _ = Nothing - -instance ToData Cip25Metadata where - toData (Cip25Metadata entries) = toData - $ AssocMap.singleton (toData nftMetadataLabel) - $ AssocMap.Map - $ groupEntries entries <#> - \group -> - toData (_.policyId <<< unwrap $ NonEmpty.head group) /\ - (AssocMap.Map <<< toArray <<< flip map group) \entry -> - toData ((unwrap entry).assetName) /\ metadataEntryToData entry - -instance FromData Cip25Metadata where - fromData meta = do - entries <- lookupKey nftMetadataLabel meta >>= case _ of - Map mp1 -> map concat - $ for mp1 - $ \(policyId /\ assets) -> - case assets of - Map mp2 -> - for mp2 $ \(assetName /\ contents) -> - metadataEntryFromData <$> fromData policyId - <*> fromData assetName - <*> pure contents - _ -> Nothing - _ -> Nothing - wrap <$> sequence entries - -instance DecodeAeson Cip25Metadata where - decodeAeson = - caseAesonObject errExpectedObject $ \obj -> do - policies <- obj .: nftMetadataLabel - withJsonObject policies $ \objPolicies -> - map (wrap <<< concat) - $ for (objToArray objPolicies) - $ \(policyId /\ assets) -> - withJsonObject assets $ \objAssets -> - for (objToArray objAssets) $ \(assetName /\ contents) -> do - policyId_ <- decodePolicyId policyId - assetName_ <- decodeAssetName assetName - metadataEntryDecodeAeson policyId_ assetName_ contents - where - objToArray :: forall a. FO.Object a -> Array (Tuple String a) - objToArray = FO.toUnfoldable - - withJsonObject - :: forall a - . Aeson - -> (FO.Object Aeson -> Either JsonDecodeError a) - -> Either JsonDecodeError a - withJsonObject = flip (caseAesonObject errExpectedObject) - - decodePolicyId :: String -> Either JsonDecodeError MintingPolicyHash - decodePolicyId = - note (TypeMismatch "Expected hex-encoded policy id") - <<< map wrap - <<< (scriptHashFromBytes <=< hexToRawBytes) - - decodeAssetName :: String -> Either JsonDecodeError TokenName - decodeAssetName = - note (TypeMismatch "Expected UTF-8 encoded asset name") - <<< mkTokenName - <<< wrap - <<< encodeUtf8 - --------------------------------------------------------------------------------- --- Helpers --------------------------------------------------------------------------------- - -errExpectedObject :: forall a. Either JsonDecodeError a -errExpectedObject = - Left (TypeMismatch "Expected object") - -errExpectedArray :: forall a. Either JsonDecodeError a -errExpectedArray = - Left (TypeMismatch "Expected array") - -errExpectedNonEmptyArray :: forall a. Either JsonDecodeError a -errExpectedNonEmptyArray = - Left (TypeMismatch "Expected non-empty array") - -decodeStringArray - :: Aeson -> Either JsonDecodeError (Array String) -decodeStringArray aeson - | isString aeson = - decodeStringArray (encodeAeson [ aeson ]) - | otherwise = - flip (caseAesonArray errExpectedArray) aeson $ - note (TypeMismatch "Expected UTF-8 encoded string") - <<< traverse toString - -decodeNonEmptyStringArray - :: Aeson -> Either JsonDecodeError (NonEmpty Array String) -decodeNonEmptyStringArray json = - decodeStringArray json >>= \arr -> - case uncons arr of - Just { head, tail } -> pure (head :| tail) - Nothing -> errExpectedNonEmptyArray diff --git a/src/Metadata/Cip25/Cip25String.purs b/src/Metadata/Cip25/Cip25String.purs new file mode 100644 index 0000000000..02c009d73e --- /dev/null +++ b/src/Metadata/Cip25/Cip25String.purs @@ -0,0 +1,146 @@ +-- | This module includes a string type that is used in CIP-25 standard. +module Metadata.Cip25.Cip25String + ( Cip25String + , mkCip25String + , unCip25String + , fromMetadataString + , toMetadataString + , toCip25Strings + , fromCip25Strings + , fromDataString + , toDataString + ) where + +import Prelude + +import Aeson + ( class DecodeAeson + , class EncodeAeson + , JsonDecodeError(TypeMismatch) + , decodeAeson + ) +import Control.Alt ((<|>)) +import Data.Array ((:)) +import Data.Array as Array +import Data.Either (hush, note) +import Data.Foldable (fold, foldMap) +import Data.Maybe (Maybe(Nothing, Just), isJust) +import Data.Newtype (unwrap, wrap) +import Data.String.CodePoints as String +import Data.TextDecoder (decodeUtf8) +import Data.TextEncoder (encodeUtf8) +import Data.Tuple.Nested (type (/\), (/\)) +import FromData (class FromData, fromData) +import Metadata.FromMetadata (class FromMetadata, fromMetadata) +import Metadata.ToMetadata (class ToMetadata, toMetadata) +import ToData (class ToData, toData) +import Types.ByteArray (ByteArray, byteLength) +import Types.PlutusData (PlutusData) +import Types.TransactionMetadata (TransactionMetadatum) + +-- | A string type that is used in CIP-25 standard. +-- | String length in bytes (in UTF-8) is limited by 64, because PlutusData +-- | bytes have this length limit. +newtype Cip25String = Cip25String String + +derive newtype instance Eq Cip25String +derive newtype instance Ord Cip25String +derive newtype instance ToMetadata Cip25String +derive newtype instance FromMetadata Cip25String +derive newtype instance ToData Cip25String +derive newtype instance FromData Cip25String +derive newtype instance EncodeAeson Cip25String + +instance Show Cip25String where + show (Cip25String str) = "(unsafePartial (fromJust (mkCip25String " + <> show str + <> ")))" + +instance DecodeAeson Cip25String where + decodeAeson = decodeAeson >=> mkCip25String >>> note + (TypeMismatch "Cip25String") + +unCip25String :: Cip25String -> String +unCip25String (Cip25String str) = str + +-- | A smart constructor for `Cip25String` +mkCip25String :: String -> Maybe Cip25String +mkCip25String str + | byteLength (wrap (encodeUtf8 str)) <= 64 = Just $ Cip25String str + | otherwise = Nothing + +takeCip25String :: String -> Maybe { init :: Cip25String, rest :: Maybe String } +takeCip25String str = + -- > https://www.rfc-editor.org/rfc/rfc3629 + -- + -- In UTF-8, characters from the U+0000..U+10FFFF range (the UTF-16 + -- accessible range) are encoded using sequences of 1 to 4 octets + -- + -- Hence we start at 64/4 = 16 (worst case, all code points take 4 bytes), + -- with a step equal to (64 - 16) / 2 = 24. + case + forwardSearch + { minBound: 16 + , maxBound: 64 + , step: 24 + , takeN: \ix -> mkCip25String (String.take ix str) + } + of + Nothing /\ _ -> Nothing + Just cip25String /\ ix -> Just + { init: cip25String + , rest: + let + rest = String.drop ix str + in + if rest == "" then Nothing else Just rest + } + +forwardSearch + :: forall (a :: Type) + . { step :: Int, minBound :: Int, maxBound :: Int, takeN :: Int -> Maybe a } + -> Maybe a /\ Int +forwardSearch { minBound, maxBound, takeN, step } + | isJust (takeN $ minBound + step) = + if minBound + step <= maxBound then forwardSearch + { minBound: minBound + step, maxBound, takeN, step } + else takeN maxBound /\ maxBound + | otherwise = + if step == 1 then + takeN minBound /\ minBound + else + forwardSearch + { minBound: minBound, maxBound, takeN, step: step `div` 2 } + +toCip25Strings :: String -> Array Cip25String +toCip25Strings str = case takeCip25String str of + Nothing -> [] + Just { init: cip25String, rest } -> + case rest of + Just restString -> cip25String : toCip25Strings restString + Nothing -> [ cip25String ] + +fromCip25Strings :: Array Cip25String -> String +fromCip25Strings = foldMap unCip25String + +toDataString :: String -> PlutusData +toDataString str = case toCip25Strings str of + [ singleStr ] -> toData singleStr + strings -> toData $ toData <$> strings + +fromDataString :: PlutusData -> Maybe String +fromDataString datum = do + (fromCip25Strings <$> (Array.singleton <$> fromData datum)) <|> do + bytes :: Array ByteArray <- fromData datum + hush $ decodeUtf8 $ unwrap $ fold bytes + +toMetadataString :: String -> TransactionMetadatum +toMetadataString str = case toCip25Strings str of + [ singleStr ] -> toMetadata singleStr + strings -> toMetadata $ toMetadata <$> strings + +fromMetadataString :: TransactionMetadatum -> Maybe String +fromMetadataString datum = do + fromCip25Strings <$> (Array.singleton <$> fromMetadata datum) <|> do + bytes :: Array ByteArray <- fromMetadata datum + hush $ decodeUtf8 $ unwrap $ fold bytes diff --git a/src/Metadata/Cip25/Common.purs b/src/Metadata/Cip25/Common.purs new file mode 100644 index 0000000000..4f7cbcaf28 --- /dev/null +++ b/src/Metadata/Cip25/Common.purs @@ -0,0 +1,133 @@ +-- | This module contains definitions common for CIP-25 V1 and V2 standards +-- | (and probably future versions). +-- | https://cips.cardano.org/cips/cip25/ +module Metadata.Cip25.Common + ( nftMetadataLabel + , Cip25TokenName(Cip25TokenName) + , Cip25MetadataFile(Cip25MetadataFile) + ) where + +import Prelude + +import Aeson + ( class DecodeAeson + , class EncodeAeson + , JsonDecodeError(TypeMismatch) + , caseAesonObject + , decodeAeson + , encodeAeson' + , (.:) + ) +import Data.BigInt (BigInt) +import Data.BigInt as BigInt +import Data.Either (note) +import Data.Generic.Rep (class Generic) +import Data.Newtype (class Newtype, unwrap, wrap) +import Data.Show.Generic (genericShow) +import Data.Tuple.Nested ((/\)) +import FromData (class FromData, fromData) +import Metadata.Cip25.Cip25String + ( Cip25String + , fromDataString + , fromMetadataString + , toDataString + , toMetadataString + ) +import Metadata.FromMetadata (class FromMetadata, fromMetadata) +import Metadata.Helpers (errExpectedObject, lookupKey, lookupMetadata) +import Metadata.ToMetadata (class ToMetadata, toMetadata) +import Plutus.Types.AssocMap as AssocMap +import ToData (class ToData, toData) +import Types.TokenName (TokenName, getTokenName, mkTokenName) + +nftMetadataLabel :: BigInt +nftMetadataLabel = BigInt.fromInt 721 + +-- | A newtype over `TokenName` that uses correct Json encoding (without `0x` prefix) +newtype Cip25TokenName = Cip25TokenName TokenName + +derive newtype instance Eq Cip25TokenName +derive newtype instance Ord Cip25TokenName +derive newtype instance ToData Cip25TokenName +derive newtype instance FromData Cip25TokenName +derive newtype instance ToMetadata Cip25TokenName +derive newtype instance FromMetadata Cip25TokenName +derive instance Newtype Cip25TokenName _ + +instance Show Cip25TokenName where + show (Cip25TokenName tn) = "(Cip25TokenName " <> show tn <> ")" + +instance DecodeAeson Cip25TokenName where + decodeAeson = (note (TypeMismatch "TokenName") <<< map wrap <<< mkTokenName) + <=< decodeAeson + +instance EncodeAeson Cip25TokenName where + encodeAeson' = encodeAeson' <<< getTokenName <<< unwrap + +-- | `files_details` in CDDL +-- | +-- | Same for V1 and V2. +-- | +-- | ``` +-- | files_details = +-- | { +-- | name : string, +-- | mediaType : string, +-- | src : string / [* string] +-- | } +-- | ``` +newtype Cip25MetadataFile = Cip25MetadataFile + { name :: Cip25String + , mediaType :: Cip25String + , src :: String + } + +derive instance Generic Cip25MetadataFile _ +derive instance Newtype Cip25MetadataFile _ +derive instance Eq Cip25MetadataFile + +instance Show Cip25MetadataFile where + show = genericShow + +instance ToMetadata Cip25MetadataFile where + toMetadata (Cip25MetadataFile file) = toMetadata + [ "name" /\ toMetadata file.name + , "mediaType" /\ toMetadata file.mediaType + , "src" /\ toMetadataString file.src + ] + +instance FromMetadata Cip25MetadataFile where + fromMetadata contents = do + name <- lookupMetadata "name" contents >>= fromMetadata + mediaType <- lookupMetadata "mediaType" contents >>= fromMetadata + src <- lookupMetadata "src" contents >>= fromMetadataString + pure $ wrap { name, mediaType, src } + +instance ToData Cip25MetadataFile where + toData (Cip25MetadataFile file) = toData $ AssocMap.Map $ + [ "name" /\ toData file.name + , "mediaType" /\ toData file.mediaType + , "src" /\ toDataString file.src + ] + +instance FromData Cip25MetadataFile where + fromData contents = do + name <- lookupKey "name" contents >>= fromData + mediaType <- lookupKey "mediaType" contents >>= fromData + src <- lookupKey "src" contents >>= fromDataString + pure $ wrap { name, mediaType, src } + +instance DecodeAeson Cip25MetadataFile where + decodeAeson = + caseAesonObject errExpectedObject \obj -> do + name <- obj .: "name" + mediaType <- obj .: "mediaType" + src <- obj .: "src" + pure $ wrap { name, mediaType, src } + +instance EncodeAeson Cip25MetadataFile where + encodeAeson' (Cip25MetadataFile { name, mediaType, src }) = encodeAeson' + { name + , mediaType + , src + } diff --git a/src/Metadata/Cip25/V2.purs b/src/Metadata/Cip25/V2.purs new file mode 100644 index 0000000000..9c71b65471 --- /dev/null +++ b/src/Metadata/Cip25/V2.purs @@ -0,0 +1,347 @@ +-- Implementation of CIP 25 - NFT Metadata Standard V2. +-- https://cips.cardano.org/cips/cip25/ +-- https://cips.cardano.org/cips/cip25/cddl/version_2.cddl +-- +-- Differences from the spec: +-- - We do not split strings in pieces when encoding to JSON +-- - We require a "version": 2 tag. +-- - `policy_id` is 28 bytes +-- - `asset_name` is up to 32 bytes. +-- +-- Motivation: https://github.com/cardano-foundation/CIPs/issues/303 +module Metadata.Cip25.V2 + ( Cip25Metadata(Cip25Metadata) + , Cip25MetadataEntry(Cip25MetadataEntry) + , module Metadata.Cip25.Common + ) where + +import Prelude + +import Aeson + ( class DecodeAeson + , Aeson + , JsonDecodeError(TypeMismatch) + , caseAesonObject + , decodeAeson + , (.:) + , (.:?) + ) +import Aeson as Aeson +import Control.Alt ((<|>)) +import Data.Array (catMaybes, concat, groupBy) +import Data.Array.NonEmpty (NonEmptyArray, toArray) +import Data.Array.NonEmpty (head) as NonEmpty +import Data.BigInt (fromInt, toString) as BigInt +import Data.Either (Either(Left), note) +import Data.Function (on) +import Data.Generic.Rep (class Generic) +import Data.Map (toUnfoldable) as Map +import Data.Maybe (Maybe(Just, Nothing), fromJust, fromMaybe) +import Data.Newtype (class Newtype, wrap, unwrap) +import Data.Show.Generic (genericShow) +import Data.TextEncoder (encodeUtf8) +import Data.Traversable (fold, for, sequence, traverse) +import Data.Tuple (Tuple) +import Data.Tuple.Nested ((/\)) +import Foreign.Object (Object, toUnfoldable) as FO +import FromData (class FromData, fromData) +import Metadata.Cip25.Cip25String + ( Cip25String + , fromDataString + , fromMetadataString + ) +import Metadata.Cip25.Common + ( nftMetadataLabel + , Cip25TokenName(Cip25TokenName) + , Cip25MetadataFile(Cip25MetadataFile) + ) +import Metadata.FromMetadata (class FromMetadata, fromMetadata) +import Metadata.Helpers (errExpectedObject, lookupKey, lookupMetadata) +import Metadata.MetadataType (class MetadataType) +import Metadata.ToMetadata (class ToMetadata, toMetadata, anyToMetadata) +import Partial.Unsafe (unsafePartial) +import Plutus.Types.AssocMap (Map(Map), singleton) as AssocMap +import Serialization.Hash (scriptHashFromBytes) +import ToData (class ToData, toData) +import Types.Int as Int +import Types.PlutusData (PlutusData(Map, Integer)) +import Types.RawBytes (hexToRawBytes) +import Types.Scripts (MintingPolicyHash) +import Types.TokenName (mkTokenName) +import Types.TransactionMetadata (TransactionMetadatum(Int, MetadataMap)) + +-- | ``` +-- | metadata_details = +-- | { +-- | name : string, +-- | image : string / [* string], +-- | ? mediaType : string, +-- | ? description : string / [* string], +-- | ? files : [* files_details] +-- | } +-- | ``` +newtype Cip25MetadataEntry = Cip25MetadataEntry + { policyId :: MintingPolicyHash + , assetName :: Cip25TokenName + -- metadata_details: + , name :: Cip25String + , image :: String + , mediaType :: Maybe Cip25String + , description :: Maybe String + , files :: Array Cip25MetadataFile + } + +derive instance Generic Cip25MetadataEntry _ +derive instance Newtype Cip25MetadataEntry _ +derive instance Eq Cip25MetadataEntry + +instance Show Cip25MetadataEntry where + show = genericShow + +-- | A version tag (not exported, internal) +data Cip25V2 = Cip25V2 + +derive instance Eq Cip25V2 +derive instance Ord Cip25V2 +derive instance Generic Cip25V2 _ + +instance Show Cip25V2 where + show = genericShow + +instance FromData Cip25V2 where + fromData (Integer int) + | int == BigInt.fromInt 2 = pure Cip25V2 + fromData _ = Nothing + +instance ToData Cip25V2 where + toData _ = Integer $ BigInt.fromInt 2 + +instance FromMetadata Cip25V2 where + fromMetadata (Int int) + | Int.toBigInt int == BigInt.fromInt 2 = pure Cip25V2 + fromMetadata _ = Nothing + +instance ToMetadata Cip25V2 where + toMetadata _ = Int $ unsafePartial $ fromJust $ Int.fromBigInt $ + BigInt.fromInt 2 + +instance DecodeAeson Cip25V2 where + decodeAeson aeson = do + n :: Int <- decodeAeson aeson + case n of + 2 -> pure Cip25V2 + _ -> Left $ TypeMismatch "Cip25V2" + +metadataEntryToMetadata :: Cip25MetadataEntry -> TransactionMetadatum +metadataEntryToMetadata (Cip25MetadataEntry entry) = toMetadata $ + [ "name" /\ anyToMetadata entry.name + , "image" /\ anyToMetadata entry.image + ] + <> mbMediaType + <> mbDescription + <> mbFiles + where + mbFiles = case entry.files of + [] -> [] + files -> [ "files" /\ anyToMetadata files ] + mbMediaType = fold $ entry.mediaType <#> \mediaType -> + [ "mediaType" /\ anyToMetadata mediaType ] + mbDescription = fold $ entry.description <#> \description -> + [ "description" /\ anyToMetadata description ] + +metadataEntryFromMetadata + :: MintingPolicyHash + -> Cip25TokenName + -> TransactionMetadatum + -> Maybe Cip25MetadataEntry +metadataEntryFromMetadata policyId assetName contents = do + name <- lookupMetadata "name" contents >>= fromMetadata + image <- lookupMetadata "image" contents >>= fromMetadataString + mediaType <- for (lookupMetadata "mediaType" contents) fromMetadata + description <- for (lookupMetadata "description" contents) fromMetadataString + files <- for (lookupMetadata "files" contents) fromMetadata <#> fromMaybe [] + pure $ + wrap { policyId, assetName, name, image, mediaType, description, files } + +metadataEntryToData :: Cip25MetadataEntry -> PlutusData +metadataEntryToData (Cip25MetadataEntry entry) = toData $ AssocMap.Map $ + [ "name" /\ toData entry.name + , "image" /\ toData entry.image + ] + <> mbMediaType + <> mbDescription + <> mbFiles + where + mbFiles = case entry.files of + [] -> [] + files -> [ "files" /\ toData files ] + mbMediaType = fold $ entry.mediaType <#> \mediaType -> + [ "mediaType" /\ toData mediaType ] + mbDescription = fold $ entry.description <#> \description -> + [ "description" /\ toData description ] + +metadataEntryFromData + :: MintingPolicyHash + -> Cip25TokenName + -> PlutusData + -> Maybe Cip25MetadataEntry +metadataEntryFromData policyId assetName contents = do + name <- lookupKey "name" contents >>= fromData + image <- lookupKey "image" contents >>= fromDataString + mediaType <- for (lookupKey "mediaType" contents) fromData + description <- for (lookupKey "description" contents) fromDataString + files <- for (lookupKey "files" contents) fromData <#> fromMaybe [] + pure $ + wrap { policyId, assetName, name, image, mediaType, description, files } + +metadataEntryDecodeAeson + :: MintingPolicyHash + -> Cip25TokenName + -> Aeson + -> Either JsonDecodeError Cip25MetadataEntry +metadataEntryDecodeAeson policyId assetName = + caseAesonObject errExpectedObject \obj -> do + name <- obj .: "name" + image <- obj .: "image" + mediaType <- obj .:? "mediaType" + description <- obj .:? "description" <#> fromMaybe mempty + files <- obj .:? "files" >>= \mbFiles -> + fromMaybe [] <$> for mbFiles \files -> + traverse decodeAeson =<< note (TypeMismatch "files") + (Aeson.toArray files) + pure $ + wrap { policyId, assetName, name, image, mediaType, description, files } + +newtype Cip25Metadata = Cip25Metadata (Array Cip25MetadataEntry) + +derive instance Generic Cip25Metadata _ +derive instance Newtype Cip25Metadata _ +derive instance Eq Cip25Metadata + +instance Show Cip25Metadata where + show = genericShow + +instance MetadataType Cip25Metadata where + metadataLabel _ = wrap nftMetadataLabel + +groupEntries + :: Array Cip25MetadataEntry -> Array (NonEmptyArray Cip25MetadataEntry) +groupEntries = groupBy (eq `on` (unwrap >>> _.policyId)) + +instance ToMetadata Cip25Metadata where + toMetadata (Cip25Metadata entries) = toMetadata $ + let + dataEntries = + groupEntries entries <#> + \group -> + (toMetadata <<< _.policyId <<< unwrap $ NonEmpty.head group) /\ + (toMetadata <<< toArray <<< flip map group) \entry -> + (unwrap entry).assetName /\ metadataEntryToMetadata entry + versionEntry = [ toMetadata "version" /\ toMetadata Cip25V2 ] + in + dataEntries <> versionEntry + +instance FromMetadata Cip25Metadata where + fromMetadata (MetadataMap mp1) = do + entries <- map concat + $ map catMaybes + $ for (Map.toUnfoldable mp1) + \(key /\ assets) -> + if key == toMetadata "version" then + -- top-level version tag + ( if assets == toMetadata Cip25V2 then pure Nothing + else Nothing -- fail if version does not match + ) + else + -- key is policyId + Just case assets of + MetadataMap mp2 -> + for (Map.toUnfoldable mp2) \(assetName /\ contents) -> + metadataEntryFromMetadata <$> fromMetadata key + <*> fromMetadata assetName + <*> pure contents + _ -> Nothing + wrap <$> sequence entries + fromMetadata _ = Nothing + +instance ToData Cip25Metadata where + toData (Cip25Metadata entries) = toData + $ AssocMap.singleton (toData $ BigInt.toString nftMetadataLabel) + $ AssocMap.Map + let + dataEntries = + groupEntries entries <#> + \group -> + toData (_.policyId <<< unwrap $ NonEmpty.head group) /\ toData + ( (AssocMap.Map <<< toArray <<< flip map group) \entry -> + toData ((unwrap entry).assetName) /\ metadataEntryToData + entry + ) + versionEntry = [ toData "version" /\ toData Cip25V2 ] + in + dataEntries <> versionEntry + +instance FromData Cip25Metadata where + fromData meta = do + entries <- lookupKey (BigInt.toString nftMetadataLabel) meta >>= case _ of + Map mp1 -> map concat + $ for mp1 + \(policyId /\ assets) -> + let + fromDataAssets = + case assets of + Map mp2 -> + for mp2 \(assetName /\ contents) -> + metadataEntryFromData <$> fromData policyId + <*> fromData assetName + <*> pure contents + _ -> Nothing + fromDataVersion = + if + policyId == toData "version" + && assets == toData Cip25V2 then + pure [] + else + Nothing + in + fromDataAssets <|> fromDataVersion + _ -> Nothing + wrap <$> sequence entries + +instance DecodeAeson Cip25Metadata where + decodeAeson = + caseAesonObject errExpectedObject \obj -> do + policies <- obj .: BigInt.toString nftMetadataLabel + withJsonObject policies \objPolicies -> + map (wrap <<< concat) + $ for (objToArray objPolicies) + \(policyId /\ assets) -> + withJsonObject assets \objAssets -> + for (objToArray objAssets) \(assetName /\ contents) -> do + policyId_ <- decodePolicyId policyId + assetName_ <- decodeAssetName assetName + metadataEntryDecodeAeson policyId_ assetName_ contents + where + objToArray :: forall (a :: Type). FO.Object a -> Array (Tuple String a) + objToArray = FO.toUnfoldable + + withJsonObject + :: forall (a :: Type) + . Aeson + -> (FO.Object Aeson -> Either JsonDecodeError a) + -> Either JsonDecodeError a + withJsonObject = flip (caseAesonObject errExpectedObject) + + decodePolicyId :: String -> Either JsonDecodeError MintingPolicyHash + decodePolicyId = + note (TypeMismatch "Expected hex-encoded policy id") + <<< map wrap + <<< (scriptHashFromBytes <=< hexToRawBytes) + + decodeAssetName :: String -> Either JsonDecodeError Cip25TokenName + decodeAssetName = + note (TypeMismatch "Expected UTF-8 encoded asset name") + <<< map wrap + <<< mkTokenName + <<< wrap + <<< encodeUtf8 diff --git a/src/Metadata/Helpers.purs b/src/Metadata/Helpers.purs index 4b372c48c0..c2948f2d9b 100644 --- a/src/Metadata/Helpers.purs +++ b/src/Metadata/Helpers.purs @@ -3,13 +3,16 @@ module Metadata.Helpers , unsafeMkKey , lookupKey , lookupMetadata + , errExpectedObject ) where import Prelude +import Aeson (JsonDecodeError(TypeMismatch)) +import Data.Either (Either(Left)) +import Data.Foldable (lookup) import Data.Map (lookup) as Map import Data.Maybe (Maybe(Nothing), fromJust) -import Data.Foldable (lookup) import Types.ByteArray (byteArrayFromAscii) import Types.PlutusData (PlutusData(Map, Bytes)) import Types.TransactionMetadata (TransactionMetadatum(MetadataMap, Text)) @@ -27,3 +30,7 @@ lookupKey _ _ = Nothing lookupMetadata :: String -> TransactionMetadatum -> Maybe TransactionMetadatum lookupMetadata keyStr (MetadataMap mp) = Map.lookup (Text keyStr) mp lookupMetadata _ _ = Nothing + +errExpectedObject :: forall (a :: Type). Either JsonDecodeError a +errExpectedObject = + Left (TypeMismatch "Expected object") diff --git a/src/Metadata/ToMetadata.purs b/src/Metadata/ToMetadata.purs index 3c957475f7..11f9bbd363 100644 --- a/src/Metadata/ToMetadata.purs +++ b/src/Metadata/ToMetadata.purs @@ -16,7 +16,7 @@ import Data.Map (catMaybes, fromFoldable, toUnfoldable) as Map import Data.Maybe (Maybe(Just), fromJust) import Data.NonEmpty (NonEmpty) import Data.Profunctor.Strong ((***)) -import Data.Tuple.Nested (type (/\)) +import Data.Tuple (Tuple) import Partial.Unsafe (unsafePartial) import Types.ByteArray (ByteArray) import Types.Int (Int, fromBigInt) as Int @@ -40,14 +40,19 @@ instance (Ord k, ToMetadata k) => ToMetadata (Map k AnyToMetadata) where else instance (ToMetadata k, ToMetadata v) => ToMetadata (Map k v) where toMetadata mp = let - entries = Map.toUnfoldable mp :: Array (k /\ v) + entries = Map.toUnfoldable mp :: Array (Tuple k v) in MetadataMap <<< Map.fromFoldable $ map (toMetadata *** toMetadata) entries -instance (Ord k, ToMetadata k) => ToMetadata (Array (k /\ AnyToMetadata)) where +instance (Ord k, ToMetadata k) => ToMetadata (Array (Tuple k AnyToMetadata)) where toMetadata = toMetadata <<< Map.fromFoldable -else instance (Ord k, ToMetadata k, ToMetadata v) => ToMetadata (Array (k /\ v)) where +else instance + ( Ord k + , ToMetadata k + , ToMetadata v + ) => + ToMetadata (Array (Tuple k v)) where toMetadata = toMetadata <<< Map.fromFoldable else instance ToMetadata a => ToMetadata (Array a) where toMetadata = MetadataList <<< map toMetadata diff --git a/src/Plutip/PortCheck.js b/src/Plutip/PortCheck.js new file mode 100644 index 0000000000..b9440791b7 --- /dev/null +++ b/src/Plutip/PortCheck.js @@ -0,0 +1,24 @@ +const net = require("net"); + +exports._isPortAvailable = port => () => + new Promise((resolve, reject) => { + const server = net + .createServer() + .once("error", function (err) { + if (err.code == "EADDRINUSE") { + resolve(false); + } else { + reject( + "Failed check for port availability (port: " + + port + + ", error: " + + err.code + + ")" + ); + } + }) + .once("listening", () => { + server.once("close", () => resolve(true)).close(); + }) + .listen(port); + }); diff --git a/src/Plutip/PortCheck.purs b/src/Plutip/PortCheck.purs new file mode 100644 index 0000000000..3ffe642c87 --- /dev/null +++ b/src/Plutip/PortCheck.purs @@ -0,0 +1,15 @@ +module Plutip.PortCheck + ( isPortAvailable + ) where + +import Prelude + +import Control.Promise (Promise, toAffE) +import Data.UInt (UInt, toInt) +import Effect (Effect) +import Effect.Aff (Aff) + +foreign import _isPortAvailable :: Int -> Effect (Promise Boolean) + +isPortAvailable :: UInt -> Aff Boolean +isPortAvailable = toAffE <<< _isPortAvailable <<< toInt diff --git a/src/Plutip/Server.purs b/src/Plutip/Server.purs new file mode 100644 index 0000000000..062bb816a7 --- /dev/null +++ b/src/Plutip/Server.purs @@ -0,0 +1,440 @@ +module Plutip.Server + ( runPlutipContract + , withPlutipContractEnv + , startPlutipCluster + , stopPlutipCluster + , startPlutipServer + , stopChildProcessWithPort + ) where + +import Prelude + +import Aeson (decodeAeson, encodeAeson, parseJsonStringToAeson, stringifyAeson) +import Affjax as Affjax +import Affjax.RequestBody as RequestBody +import Affjax.RequestHeader as Header +import Affjax.ResponseFormat as Affjax.ResponseFormat +import Contract.Address (NetworkId(MainnetId)) +import Contract.Monad (Contract, ContractEnv(ContractEnv), runContractInEnv) +import Control.Monad.Error.Class (withResource) +import Data.Array as Array +import Data.Bifunctor (lmap) +import Data.Either (Either(Left), either) +import Data.HTTP.Method as Method +import Data.Maybe (Maybe(Just, Nothing), maybe) +import Data.Newtype (unwrap, wrap) +import Data.Posix.Signal (Signal(SIGINT)) +import Data.String.CodeUnits as String +import Data.String.Pattern (Pattern(Pattern)) +import Data.Traversable (for, foldMap) +import Data.Tuple.Nested ((/\)) +import Data.UInt (UInt) +import Data.UInt as UInt +import Effect (Effect) +import Effect.Aff (Aff, Milliseconds(Milliseconds)) +import Effect.Aff.Class (liftAff) +import Effect.Aff.Retry + ( RetryPolicy + , constantDelay + , limitRetriesByCumulativeDelay + , recovering + ) +import Effect.Class (liftEffect) +import Effect.Exception (throw) +import Node.ChildProcess + ( ChildProcess + , defaultExecSyncOptions + , defaultSpawnOptions + , execSync + , kill + , spawn + ) +import Plutip.PortCheck (isPortAvailable) +import Plutip.Spawn + ( NewOutputAction(Success, NoOp) + , killOnExit + , spawnAndWaitForOutput + ) +import Plutip.Types + ( class UtxoDistribution + , ClusterStartupParameters + , ClusterStartupRequest(ClusterStartupRequest) + , PlutipConfig + , PostgresConfig + , StartClusterResponse(ClusterStartupSuccess, ClusterStartupFailure) + , StopClusterRequest(StopClusterRequest) + , StopClusterResponse + , decodeWallets + , encodeDistribution + ) +import Plutip.Utils (tmpdir) +import QueryM + ( ClientError(ClientDecodeJsonError, ClientHttpError) + , stopQueryRuntime + ) +import QueryM as QueryM +import QueryM.ProtocolParameters as Ogmios +import QueryM.UniqueId (uniqueId) +import Types.UsedTxOuts (newUsedTxOuts) + +-- | Run a single `Contract` in Plutip environment. +runPlutipContract + :: forall (distr :: Type) (wallets :: Type) (a :: Type) + . UtxoDistribution distr wallets + => PlutipConfig + -> distr + -> (wallets -> Contract () a) + -> Aff a +runPlutipContract cfg distr cont = withPlutipContractEnv cfg distr + \env wallets -> + runContractInEnv env (cont wallets) + +-- | Provide a `ContractEnv` connected to Plutip. +-- | can be used to run multiple `Contract`s using `runContractInEnv`. +withPlutipContractEnv + :: forall (distr :: Type) (wallets :: Type) (a :: Type) + . UtxoDistribution distr wallets + => PlutipConfig + -> distr + -> (ContractEnv () -> wallets -> Aff a) + -> Aff a +withPlutipContractEnv plutipCfg distr cont = do + configCheck plutipCfg + withPlutipServer $ + withPlutipCluster \response -> + withWallets response \wallets -> + withPostgres response + $ withOgmios response + $ withOgmiosDatumCache response + $ withCtlServer + $ withContractEnv (flip cont wallets) + where + withPlutipServer :: Aff a -> Aff a + withPlutipServer = + withResource (startPlutipServer plutipCfg) + (stopChildProcessWithPort plutipCfg.port) <<< const + + withPlutipCluster :: (ClusterStartupParameters -> Aff a) -> Aff a + withPlutipCluster cc = withResource (startPlutipCluster plutipCfg distr) + (const $ void $ stopPlutipCluster plutipCfg) + case _ of + ClusterStartupFailure _ -> do + liftEffect $ throw "Failed to start up cluster" + ClusterStartupSuccess response -> do + cc response + + withPostgres :: ClusterStartupParameters -> Aff a -> Aff a + withPostgres response = + withResource (startPostgresServer plutipCfg.postgresConfig response) + (stopChildProcessWithPort plutipCfg.postgresConfig.port) <<< const + + withOgmios :: ClusterStartupParameters -> Aff a -> Aff a + withOgmios response = + withResource (startOgmios plutipCfg response) + (stopChildProcessWithPort plutipCfg.ogmiosConfig.port) <<< const + + withOgmiosDatumCache :: ClusterStartupParameters -> Aff a -> Aff a + withOgmiosDatumCache response = + withResource (startOgmiosDatumCache plutipCfg response) + (stopChildProcessWithPort plutipCfg.ogmiosDatumCacheConfig.port) <<< const + + withCtlServer :: Aff a -> Aff a + withCtlServer = + withResource (startCtlServer plutipCfg) + (stopChildProcessWithPort plutipCfg.ctlServerConfig.port) <<< const + + withWallets :: ClusterStartupParameters -> (wallets -> Aff a) -> Aff a + withWallets response cc = case decodeWallets response.privateKeys of + Nothing -> + liftEffect $ throw $ "Impossible happened: unable to decode" <> + " wallets from private keys. Please report as bug." + Just wallets -> cc wallets + + withContractEnv :: (ContractEnv () -> Aff a) -> Aff a + withContractEnv = withResource (mkClusterContractEnv plutipCfg) + (liftEffect <<< stopContractEnv) + + -- a version of Contract.Monad.stopContractEnv without a compile-time warning + stopContractEnv :: ContractEnv () -> Effect Unit + stopContractEnv env = stopQueryRuntime (unwrap env).runtime + +-- | Throw an exception if `PlutipConfig` contains ports that are occupied. +configCheck :: PlutipConfig -> Aff Unit +configCheck cfg = do + let + services = + [ cfg.port /\ "plutip-server" + , cfg.ogmiosConfig.port /\ "ogmios" + , cfg.ogmiosDatumCacheConfig.port /\ "ogmios-datum-cache" + , cfg.ctlServerConfig.port /\ "ctl-server" + , cfg.postgresConfig.port /\ "postgres" + ] + occupiedServices <- Array.catMaybes <$> for services \(port /\ service) -> do + isPortAvailable port <#> if _ then Nothing else Just (port /\ service) + unless (Array.null occupiedServices) do + liftEffect $ throw $ + "Unable to run the following services, because the ports are occupied:\ + \\n" <> foldMap printServiceEntry occupiedServices + where + printServiceEntry (port /\ service) = + "- " <> service <> " (port: " <> show (UInt.toInt port) <> ")\n" + +startPlutipCluster + :: forall (distr :: Type) (wallets :: Type) + . UtxoDistribution distr wallets + => PlutipConfig + -> distr + -> Aff StartClusterResponse +startPlutipCluster cfg utxoDistribution = do + let url = mkServerEndpointUrl cfg "start" + res <- do + response <- liftAff + ( Affjax.request + Affjax.defaultRequest + { content = Just + $ RequestBody.String + $ stringifyAeson + $ encodeAeson + $ ClusterStartupRequest + { keysToGenerate: encodeDistribution utxoDistribution } + , responseFormat = Affjax.ResponseFormat.string + , headers = [ Header.ContentType (wrap "application/json") ] + , url = url + , method = Left Method.POST + } + ) + pure $ response # either + (Left <<< ClientHttpError) + ( lmap ClientDecodeJsonError + <<< (decodeAeson <=< parseJsonStringToAeson) + <<< _.body + ) + either (liftEffect <<< throw <<< show) pure res + +stopPlutipCluster :: PlutipConfig -> Aff StopClusterResponse +stopPlutipCluster cfg = do + let url = mkServerEndpointUrl cfg "stop" + res <- do + response <- liftAff + ( Affjax.request + Affjax.defaultRequest + { content = Just + $ RequestBody.String + $ stringifyAeson + $ encodeAeson + $ StopClusterRequest + , responseFormat = Affjax.ResponseFormat.string + , headers = [ Header.ContentType (wrap "application/json") ] + , url = url + , method = Left Method.POST + } + ) + pure $ response # either + (Left <<< ClientHttpError) + ( lmap ClientDecodeJsonError + <<< (decodeAeson <=< parseJsonStringToAeson) + <<< _.body + ) + either (liftEffect <<< throw <<< show) pure res + +startOgmios :: PlutipConfig -> ClusterStartupParameters -> Aff ChildProcess +startOgmios cfg params = do + -- We wait for any output, because CTL-server tries to connect to Ogmios + -- repeatedly, and we can just wait for CTL-server to connect, instead of + -- waiting for Ogmios first. + child <- spawnAndWaitForOutput "ogmios" ogmiosArgs defaultSpawnOptions + $ pure Success + liftEffect $ killOnExit child + pure child + where + ogmiosArgs :: Array String + ogmiosArgs = + [ "--host" + , cfg.ogmiosConfig.host + , "--port" + , UInt.toString cfg.ogmiosConfig.port + , "--node-socket" + , params.nodeSocketPath + , "--node-config" + , params.nodeConfigPath + ] + +stopChildProcess :: ChildProcess -> Aff Unit +stopChildProcess = liftEffect <<< kill SIGINT + +startPlutipServer :: PlutipConfig -> Aff ChildProcess +startPlutipServer cfg = do + p <- liftEffect $ spawn "plutip-server" [ "-p", UInt.toString cfg.port ] + defaultSpawnOptions + liftEffect $ killOnExit p + -- We are trying to call stopPlutipCluster endpoint to ensure that + -- `plutip-server` has started. + void + $ recovering defaultRetryPolicy + ([ \_ _ -> pure true ]) + $ const + $ stopPlutipCluster cfg + pure p + +startPostgresServer + :: PostgresConfig -> ClusterStartupParameters -> Aff ChildProcess +startPostgresServer pgConfig _ = do + tmpDir <- liftEffect tmpdir + randomStr <- liftEffect $ uniqueId "" + let + workingDir = tmpDir <> "/" <> randomStr + databaseDir = workingDir <> "/postgres/data" + postgresSocket = workingDir <> "/postgres" + liftEffect $ void $ execSync ("initdb " <> databaseDir) defaultExecSyncOptions + pgChildProcess <- liftEffect $ spawn "postgres" + [ "-D" + , databaseDir + , "-p" + , UInt.toString pgConfig.port + , "-h" + , pgConfig.host + , "-k" + , postgresSocket + ] + defaultSpawnOptions + liftEffect $ killOnExit pgChildProcess + void $ recovering defaultRetryPolicy ([ \_ _ -> pure true ]) + $ const + $ liftEffect + $ execSync + ( "psql -h " <> pgConfig.host <> " -p " <> UInt.toString pgConfig.port + <> " -d postgres" + ) + defaultExecSyncOptions + liftEffect $ void $ execSync + ( "psql -h " <> pgConfig.host <> " -p " <> UInt.toString pgConfig.port + <> " -d postgres" + <> " -c \"CREATE ROLE " + <> pgConfig.user + <> " WITH LOGIN SUPERUSER CREATEDB PASSWORD '" + <> pgConfig.password + <> "';\"" + ) + defaultExecSyncOptions + liftEffect $ void $ execSync + ( "createdb -h " <> pgConfig.host <> " -p " <> UInt.toString pgConfig.port + <> " -U " + <> pgConfig.user + <> " -O " + <> pgConfig.user + <> " " + <> pgConfig.dbname + ) + defaultExecSyncOptions + pure pgChildProcess + +-- | Kill a process and wait for it to stop listening on a specific port. +stopChildProcessWithPort :: UInt -> ChildProcess -> Aff Unit +stopChildProcessWithPort port childProcess = do + stopChildProcess childProcess + void $ recovering defaultRetryPolicy ([ \_ _ -> pure true ]) + \_ -> do + isAvailable <- isPortAvailable port + unless isAvailable do + liftEffect $ throw "retry" + +startOgmiosDatumCache + :: PlutipConfig + -> ClusterStartupParameters + -> Aff ChildProcess +startOgmiosDatumCache cfg _params = do + apiKey <- liftEffect $ uniqueId "token" + let + arguments :: Array String + arguments = + [ "--server-api" + , apiKey + , "--server-port" + , UInt.toString cfg.ogmiosDatumCacheConfig.port + , "--ogmios-address" + , cfg.ogmiosDatumCacheConfig.host + , "--ogmios-port" + , UInt.toString cfg.ogmiosConfig.port + , "--db-port" + , UInt.toString cfg.postgresConfig.port + , "--db-host" + , cfg.postgresConfig.host + , "--db-user" + , cfg.postgresConfig.user + , "--db-name" + , cfg.postgresConfig.dbname + , "--db-password" + , cfg.postgresConfig.password + , "--use-latest" + , "--from-origin" + ] + child <- + spawnAndWaitForOutput "ogmios-datum-cache" arguments defaultSpawnOptions + -- Wait for "Intersection found" string in the output + $ String.indexOf (Pattern "Intersection found") + >>> maybe NoOp (const Success) + liftEffect $ killOnExit child + pure child + +mkClusterContractEnv + :: PlutipConfig + -> Aff (ContractEnv ()) +mkClusterContractEnv plutipCfg = do + ogmiosWs <- QueryM.mkOgmiosWebSocketAff plutipCfg.logLevel + QueryM.defaultOgmiosWsConfig + { port = plutipCfg.ogmiosConfig.port + , host = plutipCfg.ogmiosConfig.host + } + datumCacheWs <- + QueryM.mkDatumCacheWebSocketAff plutipCfg.logLevel + QueryM.defaultDatumCacheWsConfig + { port = plutipCfg.ogmiosDatumCacheConfig.port + , host = plutipCfg.ogmiosDatumCacheConfig.host + } + usedTxOuts <- newUsedTxOuts + pparams <- Ogmios.getProtocolParametersAff ogmiosWs plutipCfg.logLevel + pure $ ContractEnv + { config: + { ctlServerConfig: plutipCfg.ctlServerConfig + , ogmiosConfig: plutipCfg.ogmiosConfig + , datumCacheConfig: plutipCfg.ogmiosDatumCacheConfig + , networkId: MainnetId + , logLevel: plutipCfg.logLevel + , walletSpec: Nothing + , customLogger: Nothing + } + , runtime: + { ogmiosWs + , datumCacheWs + , wallet: Nothing + , usedTxOuts + , pparams + } + , extraConfig: {} + } + +startCtlServer :: PlutipConfig -> Aff ChildProcess +startCtlServer cfg = do + let + ctlServerArgs = + [ "--port" + , UInt.toString cfg.ctlServerConfig.port + , "--ogmios-host" + , cfg.ogmiosConfig.host + , "--ogmios-port" + , UInt.toString cfg.ogmiosConfig.port + ] + child <- spawnAndWaitForOutput "ctl-server" ctlServerArgs defaultSpawnOptions + -- Wait for "Successfully connected to Ogmios" string in the output + $ String.indexOf (Pattern "Successfully connected to Ogmios") + >>> maybe NoOp (const Success) + liftEffect $ killOnExit child + pure child + +defaultRetryPolicy :: RetryPolicy +defaultRetryPolicy = limitRetriesByCumulativeDelay (Milliseconds 3000.00) $ + constantDelay (Milliseconds 100.0) + +mkServerEndpointUrl :: PlutipConfig -> String -> String +mkServerEndpointUrl cfg path = do + "http://" <> cfg.host <> ":" <> UInt.toString cfg.port <> "/" <> path diff --git a/src/Plutip/Spawn.purs b/src/Plutip/Spawn.purs new file mode 100644 index 0000000000..fc3b59e3e7 --- /dev/null +++ b/src/Plutip/Spawn.purs @@ -0,0 +1,90 @@ +-- | This module provides ability to spawn a program using `spawn` and wait +-- | for some specific output that indicates that the program has started +-- | successfully or failed, in which case an exception is thrown. +module Plutip.Spawn + ( NewOutputAction(NoOp, Success, Cancel) + , spawnAndWaitForOutput + , killOnExit + ) where + +import Prelude + +import Data.Either (Either(Left)) +import Data.Foldable (fold) +import Data.Maybe (Maybe(Just, Nothing)) +import Data.Posix.Signal (Signal(SIGINT)) +import Effect (Effect) +import Effect.Aff (Aff, Canceler(Canceler), makeAff) +import Effect.Class (liftEffect) +import Effect.Exception (Error, error) +import Effect.Ref as Ref +import Node.ChildProcess + ( ChildProcess + , SpawnOptions + , kill + , spawn + , stdout + ) +import Node.ChildProcess as ChildProcess +import Node.Encoding as Encoding +import Node.Process as Process +import Node.Stream (onDataString) + +-- | Provides a way to react on update of a program output. +-- | Do nothing, indicate startup success, or thrown an exception to the Aff +-- | action, killing the spawned program. +data NewOutputAction = NoOp | Success | Cancel + +-- | `spawn`, but with ability to wait for program startup, using a callback +-- | returning a `NewOutputAction`, or to kill the program depending on its +-- | output. +spawnAndWaitForOutput + :: String + -> Array String + -> SpawnOptions + -> (String -> NewOutputAction) + -> Aff ChildProcess +spawnAndWaitForOutput cmd args opts filter = + makeAff (spawnAndWaitForOutput' cmd args opts filter) + +spawnAndWaitForOutput' + :: String + -> Array String + -> SpawnOptions + -> (String -> NewOutputAction) + -> (Either Error ChildProcess -> Effect Unit) + -> Effect Canceler +spawnAndWaitForOutput' cmd args opts filter cont = do + child <- spawn cmd args opts + ref <- Ref.new (Just "") + ChildProcess.onExit child $ const do + output <- Ref.read ref + cont $ Left $ error $ + "Process " <> cmd <> " exited. Output:\n" <> fold output + onDataString (stdout child) Encoding.UTF8 + \str -> do + output <- Ref.modify (map (_ <> str)) ref + case filter <$> output of + Just Success -> do + -- Set to Nothing to prevent future updates + Ref.write Nothing ref + cont (pure child) + Just Cancel -> do + Ref.write Nothing ref + kill SIGINT child + cont $ Left $ error + $ "Process cancelled because output received: " <> str + _ -> pure unit + pure $ Canceler $ const $ liftEffect $ kill SIGINT child + +-- | Kill child process when current process exits. Assumes that given process +-- | is still running. +killOnExit :: ChildProcess -> Effect Unit +killOnExit child = do + aliveRef <- Ref.new true + ChildProcess.onExit child \_ -> do + Ref.write false aliveRef + Process.onExit \_ -> do + alive <- Ref.read aliveRef + when alive do + kill SIGINT child diff --git a/src/Plutip/Types.purs b/src/Plutip/Types.purs new file mode 100644 index 0000000000..a4add90bc7 --- /dev/null +++ b/src/Plutip/Types.purs @@ -0,0 +1,223 @@ +module Plutip.Types + ( PlutipConfig + , PostgresConfig + , FilePath + , ErrorMessage + , UtxoAmount + , InitialUTxODistribution + , ClusterStartupRequest(ClusterStartupRequest) + , PrivateKeyResponse(PrivateKeyResponse) + , ClusterStartupParameters + , ClusterStartupFailureReason + ( ClusterIsRunningAlready + , NegativeLovelaces + , NodeConfigNotFound + ) + , StartClusterResponse + ( ClusterStartupFailure + , ClusterStartupSuccess + ) + , StopClusterRequest(StopClusterRequest) + , StopClusterResponse(StopClusterSuccess, StopClusterFailure) + , InitialUTxO + , class UtxoDistribution + , encodeDistribution + , decodeWallets + ) where + +import Prelude + +import Aeson + ( class DecodeAeson + , class EncodeAeson + , JsonDecodeError(TypeMismatch, UnexpectedValue) + , decodeAeson + , encodeAeson' + , toStringifiedNumbersJson + , (.:) + ) +import Data.Array as Array +import Data.BigInt (BigInt) +import Data.Either (Either(Left), note) +import Data.Generic.Rep (class Generic) +import Data.Log.Level (LogLevel) +import Data.Maybe (Maybe(Just, Nothing)) +import Data.Newtype (class Newtype) +import Data.Show.Generic (genericShow) +import Data.String as String +import Data.Tuple (Tuple(Tuple)) +import Data.Tuple.Nested (type (/\), (/\)) +import Data.UInt (UInt) +import QueryM.ServerConfig (ServerConfig) +import Serialization (privateKeyFromBytes) +import Serialization.Types (PrivateKey) +import Types.ByteArray (hexToByteArray) +import Types.RawBytes (RawBytes(RawBytes)) +import Wallet.Key + ( KeyWallet + , PrivatePaymentKey(PrivatePaymentKey) + , privateKeysToKeyWallet + ) + +type PlutipConfig = + { host :: String + , port :: UInt + , logLevel :: LogLevel + -- Server configs are used to deploy the corresponding services: + , ogmiosConfig :: ServerConfig + , ogmiosDatumCacheConfig :: ServerConfig + , ctlServerConfig :: ServerConfig + -- Should be synchronized with `defaultConfig.postgres` in `flake.nix` + , postgresConfig :: PostgresConfig + } + +type PostgresConfig = + { host :: String + , port :: UInt + , user :: String + , password :: String + , dbname :: String + } + +type FilePath = String + +type ErrorMessage = String + +-- | UTxO amount in Lovelaces +type UtxoAmount = BigInt + +type InitialUTxO = Array UtxoAmount + +type InitialUTxODistribution = Array InitialUTxO + +newtype ClusterStartupRequest = ClusterStartupRequest + { keysToGenerate :: InitialUTxODistribution } + +derive newtype instance EncodeAeson ClusterStartupRequest + +newtype PrivateKeyResponse = PrivateKeyResponse PrivateKey + +derive instance Newtype PrivateKeyResponse _ +derive instance Generic PrivateKeyResponse _ + +instance Show PrivateKeyResponse where + show _ = "(PrivateKeyResponse \"\")" + +instance DecodeAeson PrivateKeyResponse where + decodeAeson json = do + cborStr <- decodeAeson json + let splitted = String.splitAt 4 cborStr + -- 5820 prefix comes from Cbor + if splitted.before == "5820" then do + cborBytes <- note err $ hexToByteArray splitted.after + PrivateKeyResponse <$> note err (privateKeyFromBytes (RawBytes cborBytes)) + else Left err + where + err :: JsonDecodeError + err = TypeMismatch "PrivateKey" + +type ClusterStartupParameters = + { privateKeys :: Array PrivateKeyResponse + , nodeSocketPath :: FilePath + , nodeConfigPath :: FilePath + , keysDirectory :: FilePath + } + +data ClusterStartupFailureReason + = ClusterIsRunningAlready + | NegativeLovelaces + | NodeConfigNotFound + +derive instance Generic ClusterStartupFailureReason _ + +instance Show ClusterStartupFailureReason where + show = genericShow + +instance DecodeAeson ClusterStartupFailureReason where + decodeAeson aeson = do + decodeAeson aeson >>= case _ of + "ClusterIsRunningAlready" -> do + pure ClusterIsRunningAlready + "NegativeLovelaces" -> pure NegativeLovelaces + "NodeConfigNotFound" -> pure NodeConfigNotFound + _ -> do + Left (UnexpectedValue (toStringifiedNumbersJson aeson)) + +data StartClusterResponse + = ClusterStartupFailure ClusterStartupFailureReason + | ClusterStartupSuccess ClusterStartupParameters + +derive instance Generic StartClusterResponse _ + +instance Show StartClusterResponse where + show = genericShow + +instance DecodeAeson StartClusterResponse where + decodeAeson aeson = do + obj <- decodeAeson aeson + obj .: "tag" >>= case _ of + "ClusterStartupSuccess" -> do + contents <- obj .: "contents" + ClusterStartupSuccess <$> decodeAeson contents + "ClusterStartupFailure" -> do + failure <- obj .: "contents" + ClusterStartupFailure <$> decodeAeson failure + _ -> do + Left (UnexpectedValue (toStringifiedNumbersJson aeson)) + +data StopClusterRequest = StopClusterRequest + +derive instance Generic StopClusterRequest _ + +instance Show StopClusterRequest where + show = genericShow + +instance EncodeAeson StopClusterRequest where + encodeAeson' _ = encodeAeson' ([] :: Array Int) + +data StopClusterResponse = StopClusterSuccess | StopClusterFailure ErrorMessage + +derive instance Generic StopClusterResponse _ + +instance Show StopClusterResponse where + show = genericShow + +instance DecodeAeson StopClusterResponse where + decodeAeson aeson = do + obj <- decodeAeson aeson + obj .: "tag" >>= case _ of + "StopClusterSuccess" -> pure StopClusterSuccess + "StopClusterFailure" -> do + failure <- obj .: "contents" + StopClusterFailure <$> decodeAeson failure + _ -> do + Left (UnexpectedValue (toStringifiedNumbersJson aeson)) + +-- | A type class that implements a type-safe interface for specifying UTXO +-- | distribution for wallets. +-- | Number of wallets in distribution specification matches the number of +-- | wallets provided to the user. +class UtxoDistribution distr wallets | distr -> wallets, wallets -> distr where + encodeDistribution :: distr -> Array (Array UtxoAmount) + decodeWallets :: Array PrivateKeyResponse -> Maybe wallets + +instance UtxoDistribution Unit Unit where + encodeDistribution _ = [] + decodeWallets _ = Just unit + +instance UtxoDistribution InitialUTxO KeyWallet where + encodeDistribution amounts = [ amounts ] + decodeWallets [ (PrivateKeyResponse key) ] = + pure $ privateKeysToKeyWallet (PrivatePaymentKey key) Nothing + decodeWallets _ = Nothing + +instance + UtxoDistribution restSpec restWallets => + UtxoDistribution (InitialUTxO /\ restSpec) (KeyWallet /\ restWallets) where + encodeDistribution (amounts /\ rest) = + encodeDistribution amounts <> encodeDistribution rest + decodeWallets = Array.uncons >>> case _ of + Nothing -> Nothing + Just { head: PrivateKeyResponse key, tail } -> + Tuple (privateKeysToKeyWallet (PrivatePaymentKey key) Nothing) <$> + decodeWallets tail diff --git a/src/Plutip/Utils.js b/src/Plutip/Utils.js new file mode 100644 index 0000000000..279fd085a1 --- /dev/null +++ b/src/Plutip/Utils.js @@ -0,0 +1,3 @@ +const os = require("os"); + +exports.tmpdir = () => os.tmpdir(); diff --git a/src/Plutip/Utils.purs b/src/Plutip/Utils.purs new file mode 100644 index 0000000000..3109c2c66a --- /dev/null +++ b/src/Plutip/Utils.purs @@ -0,0 +1,9 @@ +module Plutip.Utils + ( tmpdir + ) where + +import Effect (Effect) + +-- TODO: remove this function when PS bindings for os.tmpdir are available. +-- https://github.com/Plutonomicon/cardano-transaction-lib/issues/726 +foreign import tmpdir :: Effect String diff --git a/src/QueryM.purs b/src/QueryM.purs index 731fb8d074..7c35a2ce26 100644 --- a/src/QueryM.purs +++ b/src/QueryM.purs @@ -9,7 +9,7 @@ module QueryM ) , DatumCacheListeners , DatumCacheWebSocket - , DefaultQueryConfig + , DefaultQueryEnv , DispatchError(JsError, JsonError, FaultError, ListenerCancelled) , DispatchIdMap , FeeEstimate(FeeEstimate) @@ -19,13 +19,13 @@ module QueryM , PendingRequests , QueryConfig , QueryM - , QueryMExtended + , QueryMExtended(QueryMExtended) + , QueryEnv + , QueryRuntime , RequestBody , WebSocket(WebSocket) , allowError , applyArgs - , awaitTxConfirmed - , awaitTxConfirmedWithTimeout , calculateMinFee , evaluateTxOgmios , getChainTip @@ -38,12 +38,14 @@ module QueryM , listeners , postAeson , mkDatumCacheWebSocketAff + , mkDatumCacheRequest , queryDispatch , defaultMessageListener , mkListenerSet , mkOgmiosRequest , mkOgmiosRequestAff , mkOgmiosWebSocketAff + , mkQueryRuntime , mkRequest , mkRequestAff , module ServerConfig @@ -51,11 +53,14 @@ module QueryM , ownPubKeyHash , ownStakePubKeyHash , runQueryM + , runQueryMWithSettings + , runQueryMInRuntime , signTransaction , scriptToAeson + , stopQueryRuntime , submitTxOgmios - , traceQueryConfig , underlyingWebSocket + , withQueryRuntime ) where import Prelude @@ -79,36 +84,48 @@ import Cardano.Types.Transaction (Transaction(Transaction)) import Cardano.Types.Transaction as Transaction import Cardano.Types.TransactionUnspentOutput (TransactionUnspentOutput) import Cardano.Types.Value (Coin) -import Control.Monad.Error.Class (throwError) -import Control.Monad.Logger.Trans (LoggerT, runLoggerT) -import Control.Monad.Reader.Trans (ReaderT, runReaderT, withReaderT, ask, asks) +import Control.Monad.Error.Class + ( class MonadError + , class MonadThrow + , throwError + ) +import Control.Monad.Logger.Class (class MonadLogger) +import Control.Monad.Reader.Class (class MonadAsk, class MonadReader) +import Control.Monad.Reader.Trans (ReaderT, asks, runReaderT, withReaderT) +import Control.Monad.Rec.Class (class MonadRec) +import Control.Parallel (parallel, sequential) import Data.Array (length) import Data.Array as Array import Data.Bifunctor (lmap) import Data.BigInt (BigInt) import Data.BigInt as BigInt -import Data.DateTime.Instant (unInstant) import Data.Either (Either(Left, Right), either, isRight, note) import Data.Foldable (foldl) import Data.HTTP.Method (Method(POST)) -import Data.Log.Level (LogLevel(Trace, Debug, Error)) +import Data.Log.Level (LogLevel(Error, Debug)) +import Data.Log.Message (Message) import Data.Map (Map) import Data.Map as Map import Data.Maybe (Maybe(Just, Nothing), fromMaybe, maybe) import Data.MediaType.Common (applicationJSON) import Data.Newtype (class Newtype, unwrap, wrap) -import Data.Number (infinity) -import Data.Time.Duration (Seconds(Seconds)) -import Data.Traversable (for_, traverse, traverse_) -import Data.Tuple.Nested ((/\)) +import Data.Traversable (for, for_, traverse, traverse_) +import Data.Tuple.Nested ((/\), type (/\)) import Data.UInt (UInt) import Data.UInt as UInt import Effect (Effect) -import Effect.Aff (Aff, Canceler(Canceler), delay, launchAff_, makeAff) -import Effect.Aff.Class (liftAff) -import Effect.Class (liftEffect) +import Effect.Aff + ( Aff + , Canceler(Canceler) + , delay + , finally + , launchAff_ + , makeAff + , supervise + ) +import Effect.Aff.Class (class MonadAff, liftAff) +import Effect.Class (class MonadEffect, liftEffect) import Effect.Exception (Error, error, message, throw) -import Effect.Now (now) import Effect.Ref (Ref) import Effect.Ref as Ref import Foreign.Object as Object @@ -145,9 +162,7 @@ import QueryM.ServerConfig ) as ServerConfig import QueryM.ServerConfig ( ServerConfig - , defaultDatumCacheWsConfig , defaultOgmiosWsConfig - , defaultServerConfig , mkHttpUrl , mkOgmiosDatumCacheWsUrl , mkWsUrl @@ -156,10 +171,10 @@ import QueryM.UniqueId (ListenerId) import Serialization (convertTransaction, toBytes) as Serialization import Serialization.Address ( Address - , NetworkId(TestnetId) + , NetworkId + , addressPaymentCred , baseAddressDelegationCred , baseAddressFromAddress - , addressPaymentCred , stakeCredentialToKeyHash ) import Serialization.PlutusData (convertPlutusData) as Serialization @@ -174,77 +189,189 @@ import Types.PubKeyHash (PaymentPubKeyHash, PubKeyHash, StakePubKeyHash) import Types.Scripts (PlutusScript) import Types.UsedTxOuts (newUsedTxOuts, UsedTxOuts) import Untagged.Union (asOneOf) -import Wallet (Wallet(Gero, Nami, KeyWallet), Cip30Connection, Cip30Wallet) +import Wallet + ( Cip30Connection + , Cip30Wallet + , Wallet(Gero, Nami, KeyWallet) + , mkGeroWalletAff + , mkKeyWallet + , mkNamiWalletAff + ) +import Wallet.KeyFile (privatePaymentKeyFromFile, privateStakeKeyFromFile) +import Wallet.Spec + ( WalletSpec(UseKeys, ConnectToGero, ConnectToNami) + , PrivateStakeKeySource(PrivateStakeKeyFile, PrivateStakeKeyValue) + , PrivatePaymentKeySource(PrivatePaymentKeyFile, PrivatePaymentKeyValue) + ) -- This module defines an Aff interface for Ogmios Websocket Queries -- Since WebSockets do not define a mechanism for linking request/response -- Or for verifying that the connection is live, those concerns are addressed -- here ------------------------- +-- | `QueryConfig` contains a complete specification on how to initialize a +-- | `QueryM` environment. +-- | It includes: +-- | - server parameters for all the services +-- | - network ID +-- | - logging level +-- | - wallet setup instructions +-- | - optional custom logger +type QueryConfig = + { ctlServerConfig :: ServerConfig + , ogmiosConfig :: ServerConfig + , datumCacheConfig :: ServerConfig + , networkId :: NetworkId + , logLevel :: LogLevel + , walletSpec :: Maybe WalletSpec + , customLogger :: Maybe (Message -> Aff Unit) + } --- when we add multiple query backends or wallets, --- we just need to extend this type -type QueryConfig (r :: Row Type) = +-- | Reusable part of `QueryRuntime` that can be shared between many `QueryM` +-- | instances running in parallel. +-- | +-- | Includes: +-- | - WebSocket connections +-- | - A wallet connection +-- | - A data structure to keep UTxOs that has already been spent +-- | - Current protocol parameters +type QueryRuntime = { ogmiosWs :: OgmiosWebSocket , datumCacheWs :: DatumCacheWebSocket - , serverConfig :: ServerConfig , wallet :: Maybe Wallet - -- should probably be more tightly coupled with a wallet , usedTxOuts :: UsedTxOuts - , networkId :: NetworkId - , logLevel :: LogLevel , pparams :: Ogmios.ProtocolParameters - | r } -type DefaultQueryConfig = QueryConfig () - -type QueryM (a :: Type) = ReaderT DefaultQueryConfig (LoggerT Aff) a +-- | `QueryEnv` contains everything needed for `QueryM` to run. +type QueryEnv (r :: Row Type) = + { config :: QueryConfig + , runtime :: QueryRuntime + , extraConfig :: { | r } + } -type QueryMExtended (r :: Row Type) (a :: Type) = ReaderT (QueryConfig r) - (LoggerT Aff) - a +type DefaultQueryEnv = QueryEnv () + +type QueryM (a :: Type) = QueryMExtended () a + +newtype QueryMExtended (r :: Row Type) (a :: Type) = QueryMExtended + (ReaderT (QueryEnv r) Aff a) + +derive instance Newtype (QueryMExtended r a) _ +derive newtype instance Functor (QueryMExtended r) +derive newtype instance Apply (QueryMExtended r) +derive newtype instance Applicative (QueryMExtended r) +derive newtype instance Bind (QueryMExtended r) +derive newtype instance Monad (QueryMExtended r) +derive newtype instance MonadEffect (QueryMExtended r) +derive newtype instance MonadAff (QueryMExtended r) +derive newtype instance Semigroup a => Semigroup (QueryMExtended r a) +derive newtype instance Monoid a => Monoid (QueryMExtended r a) +derive newtype instance MonadThrow Error (QueryMExtended r) +derive newtype instance MonadError Error (QueryMExtended r) +derive newtype instance MonadRec (QueryMExtended r) +derive newtype instance MonadAsk (QueryEnv r) (QueryMExtended r) +derive newtype instance MonadReader (QueryEnv r) (QueryMExtended r) + +instance MonadLogger (QueryMExtended r) where + log msg = do + config <- asks $ _.config + let + logFunction = + config # _.customLogger >>> fromMaybe (logWithLevel config.logLevel) + liftAff $ logFunction msg liftQueryM :: forall (r :: Row Type) (a :: Type). QueryM a -> QueryMExtended r a -liftQueryM = withReaderT toDefaultQueryConfig +liftQueryM = unwrap >>> withReaderT toDefaultQueryEnv >>> wrap where - toDefaultQueryConfig :: QueryConfig r -> DefaultQueryConfig - toDefaultQueryConfig c = - { ogmiosWs: c.ogmiosWs - , datumCacheWs: c.datumCacheWs - , serverConfig: c.serverConfig - , wallet: c.wallet - , usedTxOuts: c.usedTxOuts - , networkId: c.networkId - , logLevel: c.logLevel - , pparams: c.pparams - } - -runQueryM :: forall (a :: Type). DefaultQueryConfig -> QueryM a -> Aff a -runQueryM cfg = - flip runLoggerT (logWithLevel cfg.logLevel) <<< flip runReaderT cfg - --- A `DefaultQueryConfig` useful for testing, with `logLevel` set to `Trace` -traceQueryConfig :: Aff DefaultQueryConfig -traceQueryConfig = do - ogmiosWs <- mkOgmiosWebSocketAff logLevel defaultOgmiosWsConfig - datumCacheWs <- mkDatumCacheWebSocketAff logLevel defaultDatumCacheWsConfig + toDefaultQueryEnv :: QueryEnv r -> DefaultQueryEnv + toDefaultQueryEnv c = c { extraConfig = {} } + +-- | Constructs and finalizes a contract environment that is usable inside a +-- | bracket callback. +-- | Make sure that `Aff` action does not end before all contracts that use the +-- | runtime terminate. Otherwise `WebSocket`s will be closed too early. +withQueryRuntime + :: forall a + . QueryConfig + -> (QueryRuntime -> Aff a) + -> Aff a +withQueryRuntime config action = do + runtime <- mkQueryRuntime config + supervise (action runtime) `flip finally` do + liftEffect $ stopQueryRuntime runtime + +-- | Close the websockets in `QueryRuntime`, effectively making it unusable +stopQueryRuntime + :: QueryRuntime + -> Effect Unit +stopQueryRuntime runtime = do + _wsClose $ underlyingWebSocket runtime.ogmiosWs + _wsClose $ underlyingWebSocket runtime.datumCacheWs + +-- | Used in `mkQueryRuntime` only +data QueryRuntimeModel = QueryRuntimeModel + (OgmiosWebSocket /\ Ogmios.ProtocolParameters) + DatumCacheWebSocket + (Maybe Wallet) + +mkQueryRuntime + :: QueryConfig + -> Aff QueryRuntime +mkQueryRuntime config = do usedTxOuts <- newUsedTxOuts - pparams <- getProtocolParametersAff ogmiosWs logLevel + QueryRuntimeModel (ogmiosWs /\ pparams) datumCacheWs wallet <- sequential $ + QueryRuntimeModel + <$> parallel do + ogmiosWs <- mkOgmiosWebSocketAff config.logLevel defaultOgmiosWsConfig + pparams <- getProtocolParametersAff ogmiosWs config.logLevel + pure $ ogmiosWs /\ pparams + <*> parallel + (mkDatumCacheWebSocketAff config.logLevel config.datumCacheConfig) + <*> parallel (for config.walletSpec mkWalletBySpec) pure { ogmiosWs , datumCacheWs - , serverConfig: defaultServerConfig - , wallet: Nothing + , wallet , usedTxOuts - , networkId: TestnetId - , logLevel , pparams } - where - logLevel :: LogLevel - logLevel = Trace + +mkWalletBySpec :: WalletSpec -> Aff Wallet +mkWalletBySpec = case _ of + UseKeys paymentKeySpec mbStakeKeySpec -> do + privatePaymentKey <- case paymentKeySpec of + PrivatePaymentKeyFile filePath -> + privatePaymentKeyFromFile filePath + PrivatePaymentKeyValue key -> pure key + mbPrivateStakeKey <- for mbStakeKeySpec case _ of + PrivateStakeKeyFile filePath -> privateStakeKeyFromFile filePath + PrivateStakeKeyValue key -> pure key + pure $ mkKeyWallet privatePaymentKey mbPrivateStakeKey + ConnectToNami -> mkNamiWalletAff + ConnectToGero -> mkGeroWalletAff + +runQueryM :: forall (a :: Type). QueryConfig -> QueryM a -> Aff a +runQueryM config action = do + withQueryRuntime config \runtime -> + runQueryMInRuntime config runtime action + +runQueryMWithSettings + :: forall (r :: Row Type) (a :: Type) + . QueryEnv r + -> QueryM a + -> Aff a +runQueryMWithSettings settings action = do + runQueryMInRuntime settings.config settings.runtime action + +runQueryMInRuntime + :: forall (a :: Type) + . QueryConfig + -> QueryRuntime + -> QueryM a + -> Aff a +runQueryMInRuntime config runtime = do + flip runReaderT { config, runtime, extraConfig: {} } <<< unwrap getProtocolParametersAff :: OgmiosWebSocket -> LogLevel -> Aff Ogmios.ProtocolParameters @@ -297,30 +424,6 @@ getDatumsByHashes :: Array DataHash -> QueryM (Map DataHash Datum) getDatumsByHashes hashes = unwrap <$> do mkDatumCacheRequest DcWsp.getDatumsByHashesCall _.getDatumsByHashes hashes -awaitTxConfirmed :: TxHash -> QueryM Unit -awaitTxConfirmed = awaitTxConfirmedWithTimeout (Seconds infinity) - -awaitTxConfirmedWithTimeout :: Seconds -> TxHash -> QueryM Unit -awaitTxConfirmedWithTimeout timeoutSeconds txHash = do - nowMs <- getNowMs - let timeoutTime = nowMs + unwrap timeoutSeconds * 1000.0 - go timeoutTime - where - getNowMs :: QueryM Number - getNowMs = unwrap <<< unInstant <$> liftEffect now - - go :: Number -> QueryM Unit - go timeoutTime = do - isTxFound <- unwrap <$> mkDatumCacheRequest DcWsp.getTxByHash _.getTxByHash - txHash - nowMs <- getNowMs - when (nowMs >= timeoutTime) do - liftEffect $ throw $ - "awaitTxConfirmedWithTimeout: timeout exceeded, Transaction not \ - \confirmed" - liftAff $ delay $ wrap 1000.0 - if isTxFound then pure unit else go timeoutTime - allowError :: forall (a :: Type). (Either Error a -> Effect Unit) -> a -> Effect Unit allowError func = func <<< Right @@ -331,11 +434,11 @@ allowError func = func <<< Right getWalletAddress :: QueryM (Maybe Address) getWalletAddress = do - networkId <- asks _.networkId + networkId <- asks $ _.config >>> _.networkId withMWalletAff case _ of Nami nami -> callCip30Wallet nami _.getWalletAddress Gero gero -> callCip30Wallet gero _.getWalletAddress - KeyWallet kw -> Just <$> kw.address networkId + KeyWallet kw -> Just <$> (unwrap kw).address networkId getWalletCollateral :: QueryM (Maybe (Array TransactionUnspentOutput)) getWalletCollateral = do @@ -344,7 +447,7 @@ getWalletCollateral = do Gero gero -> callCip30Wallet gero _.getCollateral KeyWallet _ -> liftEffect $ throw "Not implemented" for_ mbCollateralUTxOs \collateralUTxOs -> do - pparams <- asks _.pparams + pparams <- asks $ _.runtime >>> _.pparams let tooManyCollateralUTxOs = fromMaybe false do @@ -364,7 +467,7 @@ signTransaction signTransaction tx = withMWalletAff case _ of Nami nami -> callCip30Wallet nami \nw -> flip nw.signTx tx Gero gero -> callCip30Wallet gero \nw -> flip nw.signTx tx - KeyWallet kw -> Just <$> kw.signTx tx + KeyWallet kw -> Just <$> (unwrap kw).signTx tx ownPubKeyHash :: QueryM (Maybe PubKeyHash) ownPubKeyHash = do @@ -385,7 +488,8 @@ ownStakePubKeyHash = do withMWalletAff :: forall (a :: Type). (Wallet -> Aff (Maybe a)) -> QueryM (Maybe a) -withMWalletAff act = asks _.wallet >>= maybe (pure Nothing) (liftAff <<< act) +withMWalletAff act = asks (_.runtime >>> _.wallet) >>= maybe (pure Nothing) + (liftAff <<< act) callCip30Wallet :: forall (a :: Type) @@ -545,7 +649,8 @@ scriptToAeson = encodeAeson <<< byteArrayToHex <<< unwrap mkServerEndpointUrl :: String -> QueryM Url mkServerEndpointUrl path = asks $ (_ <> "/" <> path) <<< mkHttpUrl - <<< _.serverConfig + <<< _.ctlServerConfig + <<< _.config -------------------------------------------------------------------------------- -- OgmiosWebSocket Setup and PrimOps @@ -810,8 +915,8 @@ mkOgmiosRequest -> request -> QueryM response mkOgmiosRequest = mkRequest - (listeners <<< _.ogmiosWs <$> ask) - (underlyingWebSocket <<< _.ogmiosWs <$> ask) + (asks $ listeners <<< _.ogmiosWs <<< _.runtime) + (asks $ underlyingWebSocket <<< _.ogmiosWs <<< _.runtime) -- | Builds an Ogmios request action using `Aff` mkOgmiosRequestAff @@ -833,8 +938,8 @@ mkDatumCacheRequest -> request -> QueryM response mkDatumCacheRequest = mkRequest - (listeners <<< _.datumCacheWs <$> ask) - (underlyingWebSocket <<< _.datumCacheWs <$> ask) + (asks $ listeners <<< _.datumCacheWs <<< _.runtime) + (asks $ underlyingWebSocket <<< _.datumCacheWs <<< _.runtime) -- | Builds an Ogmios request action using `QueryM` mkRequest @@ -848,7 +953,7 @@ mkRequest mkRequest getListeners getWebSocket jsonWspCall getLs inp = do ws <- getWebSocket listeners' <- getListeners - logLevel <- asks _.logLevel + logLevel <- asks $ _.config >>> _.logLevel liftAff $ mkRequestAff listeners' ws logLevel jsonWspCall getLs inp -- | Builds an Ogmios request action using `Aff` diff --git a/src/QueryM/AwaitTxConfirmed.purs b/src/QueryM/AwaitTxConfirmed.purs new file mode 100644 index 0000000000..ad35d10a36 --- /dev/null +++ b/src/QueryM/AwaitTxConfirmed.purs @@ -0,0 +1,79 @@ +module QueryM.AwaitTxConfirmed + ( awaitTxConfirmed + , awaitTxConfirmedWithTimeout + , awaitTxConfirmedWithTimeoutSlots + ) where + +import Prelude + +import Data.DateTime.Instant (unInstant) +import Data.Maybe (isJust, maybe) +import Data.Newtype (wrap, unwrap) +import Data.Number (infinity) +import Data.Time.Duration (Seconds(Seconds)) +import Effect.Aff (delay) +import Effect.Aff.Class (liftAff) +import Effect.Class (liftEffect) +import Effect.Exception (throw) +import Effect.Now (now) +import QueryM (QueryM, getChainTip, mkDatumCacheRequest) +import QueryM.DatumCacheWsp (getTxByHash) +import QueryM.Ogmios (TxHash) +import QueryM.WaitUntilSlot (waitUntilSlot) +import Serialization.Address (Slot) +import Types.BigNum as BigNum +import Types.Chain as Chain + +awaitTxConfirmed :: TxHash -> QueryM Unit +awaitTxConfirmed = awaitTxConfirmedWithTimeout (Seconds infinity) + +awaitTxConfirmedWithTimeout :: Seconds -> TxHash -> QueryM Unit +awaitTxConfirmedWithTimeout timeoutSeconds txHash = do + nowMs <- getNowMs + let timeoutTime = nowMs + unwrap timeoutSeconds * 1000.0 + go timeoutTime + where + getNowMs :: QueryM Number + getNowMs = unwrap <<< unInstant <$> liftEffect now + + go :: Number -> QueryM Unit + go timeoutTime = do + isTxFound <- isJust <<< unwrap <$> mkDatumCacheRequest getTxByHash + _.getTxByHash + txHash + nowMs <- getNowMs + when (nowMs >= timeoutTime) do + liftEffect $ throw $ + "awaitTxConfirmedWithTimeout: timeout exceeded, Transaction not \ + \confirmed" + liftAff $ delay $ wrap 1000.0 + if isTxFound then pure unit else go timeoutTime + +awaitTxConfirmedWithTimeoutSlots :: Int -> TxHash -> QueryM Unit +awaitTxConfirmedWithTimeoutSlots timeoutSlots txHash = do + getCurrentSlot >>= addSlots timeoutSlots >>= go + + where + getCurrentSlot :: QueryM Slot + getCurrentSlot = getChainTip >>= case _ of + Chain.TipAtGenesis -> do + liftAff $ delay $ wrap 1000.0 + getCurrentSlot + Chain.Tip (Chain.ChainTip { slot }) -> pure slot + + addSlots :: Int -> Slot -> QueryM Slot + addSlots n slot = + maybe (liftEffect $ throw "Cannot determine next slot") (pure <<< wrap) $ + unwrap slot `BigNum.add` BigNum.fromInt n + + go :: Slot -> QueryM Unit + go timeout = + mkDatumCacheRequest getTxByHash _.getTxByHash txHash >>= \found -> + unless (isJust $ unwrap found) do + slot <- getCurrentSlot + when (slot >= timeout) do + liftEffect $ throw $ + "awaitTxConfirmedWithTimeoutSlots: \ + \ timeout exceeded, Transaction not confirmed" + void $ addSlots 1 slot >>= waitUntilSlot + go timeout diff --git a/src/QueryM/Config.purs b/src/QueryM/Config.purs new file mode 100644 index 0000000000..8f54059b96 --- /dev/null +++ b/src/QueryM/Config.purs @@ -0,0 +1,28 @@ +module QueryM.Config + ( testnetTraceQueryConfig + , testnetQueryConfig + ) where + +import Data.Log.Level (LogLevel(Error, Trace)) +import Data.Maybe (Maybe(Nothing)) +import QueryM (QueryConfig) +import QueryM.ServerConfig + ( defaultDatumCacheWsConfig + , defaultOgmiosWsConfig + , defaultServerConfig + ) +import Serialization.Address (NetworkId(TestnetId)) + +testnetTraceQueryConfig :: QueryConfig +testnetTraceQueryConfig = + { ctlServerConfig: defaultServerConfig + , ogmiosConfig: defaultOgmiosWsConfig + , datumCacheConfig: defaultDatumCacheWsConfig + , networkId: TestnetId + , logLevel: Trace + , walletSpec: Nothing + , customLogger: Nothing + } + +testnetQueryConfig :: QueryConfig +testnetQueryConfig = testnetTraceQueryConfig { logLevel = Error } diff --git a/src/QueryM/DatumCacheWsp.purs b/src/QueryM/DatumCacheWsp.purs index 9d4bc4a9ba..2d88168c19 100644 --- a/src/QueryM/DatumCacheWsp.purs +++ b/src/QueryM/DatumCacheWsp.purs @@ -30,6 +30,7 @@ import Aeson , stringifyAeson , (.:) ) +import Base64 (Base64String) import Control.Alt ((<|>)) import Data.Either (Either(Left)) import Data.Generic.Rep (class Generic) @@ -113,16 +114,20 @@ instance DecodeAeson GetDatumsByHashesR where decodeDatum = caseAesonObject (Left $ TypeMismatch "expected object") $ \o -> (/\) <$> map wrap (o .: "hash") <*> (decodeAeson =<< o .: "value") + datumsFound = + map GetDatumsByHashesR <<< decodeDatumArray =<< getNestedAeson + r + [ "DatumsFound", "value" ] + datumsNotFound = + getNestedAeson r [ "DatumsNotFound" ] $> GetDatumsByHashesR Map.empty in - map GetDatumsByHashesR <<< decodeDatumArray =<< getNestedAeson - r - [ "DatumsFound", "value" ] + datumsFound <|> datumsNotFound -- TODO -- This should be changed to `GetTxByHashR Transaction` once we support `getTxById` -- -- See https://github.com/Plutonomicon/cardano-transaction-lib/issues/30 -newtype GetTxByHashR = GetTxByHashR Boolean +newtype GetTxByHashR = GetTxByHashR (Maybe Base64String) derive instance Newtype GetTxByHashR _ derive instance Generic GetTxByHashR _ @@ -133,13 +138,13 @@ instance Show GetTxByHashR where instance DecodeAeson GetTxByHashR where decodeAeson r = GetTxByHashR <$> let - txFound :: Either JsonDecodeError Boolean + txFound :: Either JsonDecodeError (Maybe Base64String) txFound = - true <$ getNestedAeson r [ "TxFound", "value" ] + getNestedAeson r [ "TxFound", "value", "raw" ] >>= decodeAeson - txNotFound :: Either JsonDecodeError Boolean + txNotFound :: Either JsonDecodeError (Maybe Base64String) txNotFound = - false <$ getNestedAeson r [ "TxNotFound" ] + Nothing <$ getNestedAeson r [ "TxNotFound" ] in txFound <|> txNotFound diff --git a/src/QueryM/GetTxByHash.purs b/src/QueryM/GetTxByHash.purs new file mode 100644 index 0000000000..d0376858ae --- /dev/null +++ b/src/QueryM/GetTxByHash.purs @@ -0,0 +1,29 @@ +module QueryM.GetTxByHash where + +import Prelude + +import Base64 (toByteArray) +import Cardano.Types.Transaction (Transaction) +import Data.Either (hush) +import Data.Newtype (unwrap) +import Data.Maybe (Maybe(Just, Nothing), maybe) +import Deserialization.Transaction (deserializeTransaction) +import Effect.Class (liftEffect) +import Effect.Exception (throw) +import QueryM (QueryM, mkDatumCacheRequest) +import QueryM.DatumCacheWsp as QueryM +import QueryM.Ogmios (TxHash) +import Types.CborBytes (CborBytes(CborBytes)) + +getTxByHash :: TxHash -> QueryM (Maybe Transaction) +getTxByHash txHash = do + unwrap <$> mkDatumCacheRequest QueryM.getTxByHash _.getTxByHash txHash >>= + maybe + (pure Nothing) + \txBase64 -> do + let + txBytes = CborBytes $ toByteArray txBase64 + maybe (liftEffect $ throw "Unable to decode transaction") + (pure <<< Just) + $ hush + $ deserializeTransaction txBytes diff --git a/src/QueryM/Ogmios.purs b/src/QueryM/Ogmios.purs index a9d82475c2..45b818ae7f 100644 --- a/src/QueryM/Ogmios.purs +++ b/src/QueryM/Ogmios.purs @@ -37,7 +37,7 @@ module QueryM.Ogmios , OgmiosTxIn , OgmiosTxId , SlotLength(SlotLength) - , SubmitTxR(SubmitTxR) + , SubmitTxR(SubmitTxSuccess, SubmitFail) , SystemStart(SystemStart) , TxEvaluationFailure(UnparsedError, ScriptFailures) , TxEvaluationResult(TxEvaluationResult) @@ -255,10 +255,11 @@ mkOgmiosCallType = mkCallType ---------------- TX SUBMISSION QUERY RESPONSE & PARSING -newtype SubmitTxR = SubmitTxR TxHash +data SubmitTxR + = SubmitTxSuccess TxHash + | SubmitFail (Array Aeson) derive instance Generic SubmitTxR _ -derive instance Newtype SubmitTxR _ instance Show SubmitTxR where show = genericShow @@ -267,8 +268,11 @@ type TxHash = ByteArray instance DecodeAeson SubmitTxR where decodeAeson = aesonObject $ - \o -> getField o "SubmitSuccess" >>= flip getField "txId" >>= hexToByteArray - >>> maybe (Left (TypeMismatch "Expected hexstring")) (pure <<< wrap) + \o -> + ( getField o "SubmitSuccess" >>= flip getField "txId" >>= hexToByteArray + >>> maybe (Left (TypeMismatch "Expected hexstring")) + (pure <<< SubmitTxSuccess) + ) <|> (SubmitFail <$> getField o "SubmitFail") ---------------- SYSTEM START QUERY RESPONSE & PARSING newtype SystemStart = SystemStart String diff --git a/src/QueryM/Utxos.purs b/src/QueryM/Utxos.purs index 28b1491dd4..9e458b9440 100644 --- a/src/QueryM/Utxos.purs +++ b/src/QueryM/Utxos.purs @@ -11,7 +11,6 @@ import Prelude import Address (addressToOgmiosAddress) import Cardano.Types.Transaction (TransactionOutput, UtxoM(UtxoM), Utxos) import Cardano.Types.Value (Value) -import Control.Monad.Logger.Trans (LoggerT) import Control.Monad.Reader (withReaderT) import Control.Monad.Reader.Trans (ReaderT, asks) import Data.Bifunctor (bimap) @@ -27,7 +26,6 @@ import Effect.Aff.Class (liftAff) import Effect.Class (liftEffect) import Effect.Exception (throw) import Helpers as Helpers -import Prim.TypeError (class Warn, Text) import QueryM (QueryM, getWalletAddress, getWalletCollateral, mkOgmiosRequest) import QueryM.Ogmios as Ogmios import Serialization.Address (Address) @@ -44,11 +42,7 @@ import Wallet (Wallet(Gero, Nami, KeyWallet)) -- | Gets utxos at an (internal) `Address` in terms of (internal) `Cardano.Transaction.Types`. -- | Results may vary depending on `Wallet` type. utxosAt - :: Warn - ( Text - "`utxosAt`: Querying for UTxOs by address is deprecated. See https://github.com/Plutonomicon/cardano-transaction-lib/issues/536." - ) - => Address + :: Address -> QueryM (Maybe UtxoM) utxosAt = mkUtxoQuery <<< mkOgmiosRequest Ogmios.queryUtxosAtCall _.utxo @@ -63,7 +57,8 @@ getUtxo ref = (_ >>= unwrap >>> Map.lookup ref) mkUtxoQuery :: QueryM Ogmios.UtxoQR -> QueryM (Maybe UtxoM) -mkUtxoQuery query = asks _.wallet >>= maybe allUtxosAt utxosAtByWallet +mkUtxoQuery query = asks (_.runtime >>> _.wallet) >>= maybe allUtxosAt + utxosAtByWallet where -- Add more wallet types here: utxosAtByWallet :: Wallet -> QueryM (Maybe UtxoM) @@ -125,14 +120,14 @@ filterLockedUtxos utxos = withTxRefsCache :: forall (m :: Type -> Type) (a :: Type) - . ReaderT UsedTxOuts (LoggerT Aff) a + . ReaderT UsedTxOuts Aff a -> QueryM a -withTxRefsCache f = withReaderT (_.usedTxOuts) f +withTxRefsCache = wrap <<< withReaderT (_.runtime >>> _.usedTxOuts) getWalletBalance :: QueryM (Maybe Value) getWalletBalance = do - asks _.wallet >>= map join <<< traverse case _ of + asks (_.runtime >>> _.wallet) >>= map join <<< traverse case _ of Nami wallet -> liftAff $ wallet.getBalance wallet.connection Gero wallet -> liftAff $ wallet.getBalance wallet.connection KeyWallet _ -> do diff --git a/src/QueryM/WaitUntilSlot.purs b/src/QueryM/WaitUntilSlot.purs index 11a9924b07..3f32a979c4 100644 --- a/src/QueryM/WaitUntilSlot.purs +++ b/src/QueryM/WaitUntilSlot.purs @@ -1,5 +1,8 @@ module QueryM.WaitUntilSlot ( waitUntilSlot + , waitNSlots + , currentSlot + , currentTime ) where import Prelude @@ -24,7 +27,7 @@ import QueryM (QueryM, getChainTip) import QueryM.EraSummaries (getEraSummaries) import QueryM.Ogmios (EraSummaries, SystemStart) import QueryM.SystemStart (getSystemStart) -import Serialization.Address (Slot) +import Serialization.Address (Slot(Slot)) import Types.BigNum as BigNum import Types.Chain as Chain import Types.Interval @@ -33,6 +36,8 @@ import Types.Interval , getSlotLength , slotToPosixTime ) +import Types.Natural (Natural) +import Types.Natural as Natural -- | The returned slot will be no less than the slot provided as argument. waitUntilSlot :: Slot -> QueryM Chain.Tip @@ -61,12 +66,12 @@ waitUntilSlot futureSlot = fetchRepeatedly :: QueryM Chain.Tip fetchRepeatedly = getChainTip >>= case _ of - currentTip@(Chain.Tip (Chain.ChainTip { slot: currentSlot })) - | currentSlot >= futureSlot -> pure currentTip + currentTip@(Chain.Tip (Chain.ChainTip { slot: currentSlot_ })) + | currentSlot_ >= futureSlot -> pure currentTip | otherwise -> do liftAff $ delay $ Milliseconds $ BigInt.toNumber slotLengthMs - getLag eraSummaries sysStart currentSlot >>= logLag + getLag eraSummaries sysStart currentSlot_ >>= logLag slotLengthMs fetchRepeatedly Chain.TipAtGenesis -> do @@ -83,7 +88,7 @@ waitUntilSlot futureSlot = logLag :: BigInt -> Milliseconds -> QueryM Unit logLag slotLengthMs (Milliseconds lag) = do - logLevel <- asks _.logLevel + logLevel <- asks $ _.config >>> _.logLevel liftEffect $ logString logLevel Trace $ "waitUntilSlot: current lag: " <> show lag <> " ms, " <> show (lag / BigInt.toNumber slotLengthMs) @@ -93,7 +98,7 @@ waitUntilSlot futureSlot = -- | and current time. getLag :: EraSummaries -> SystemStart -> Slot -> QueryM Milliseconds getLag eraSummaries sysStart nowSlot = do - logLevel <- asks _.logLevel + logLevel <- asks $ _.config >>> _.logLevel nowPosixTime <- liftEffect (slotToPosixTime eraSummaries sysStart nowSlot) >>= hush >>> liftM (error "Unable to convert Slot to POSIXTime") nowMs <- unwrap <<< unInstant <$> liftEffect now @@ -113,7 +118,7 @@ estimateDelayUntil :: POSIXTime -> QueryM Milliseconds estimateDelayUntil futureTimePosix = do futureTimeSec <- posixTimeToSeconds futureTimePosix nowMs <- unwrap <<< unInstant <$> liftEffect now - logLevel <- asks _.logLevel + logLevel <- asks $ _.config >>> _.logLevel let result = wrap $ mul 1000.0 $ nonNegative $ unwrap futureTimeSec - nowMs / 1000.0 @@ -137,3 +142,40 @@ posixTimeToSeconds (POSIXTime futureTimeBigInt) = do $ map (wrap <<< Int.toNumber) $ BigInt.toInt $ futureTimeBigInt / BigInt.fromInt 1000 + +-- | Wait at least `offset` number of slots. +waitNSlots :: Natural -> QueryM Chain.Tip +waitNSlots offset = do + offsetBigNum <- liftM (error "Unable to convert BigInt to BigNum") + $ (BigNum.fromBigInt <<< Natural.toBigInt) offset + if offsetBigNum == BigNum.fromInt 0 then getChainTip + else do + slot <- currentSlot + newSlot <- liftM (error "Unable to advance slot") + $ wrap <$> BigNum.add (unwrap slot) offsetBigNum + waitUntilSlot newSlot + +currentSlot :: QueryM Slot +currentSlot = getChainTip <#> case _ of + Chain.Tip (Chain.ChainTip { slot }) -> slot + Chain.TipAtGenesis -> (Slot <<< BigNum.fromInt) 0 + +-- | Get the latest POSIXTime of the current slot. +-- The plutus implementation relies on `slotToEndPOSIXTime` +-- https://github.com/input-output-hk/plutus-apps/blob/fb8a39645e532841b6e38d42ecb957f1945833a5/plutus-contract/src/Plutus/Contract/Trace.hs +currentTime :: QueryM POSIXTime +currentTime = currentSlot >>= slotToEndPOSIXTime + +-- | Get the ending 'POSIXTime' of a 'Slot' related to +-- | our `QueryM` configuration. +-- see https://github.com/input-output-hk/plutus-apps/blob/fb8a39645e532841b6e38d42ecb957f1945833a5/plutus-ledger/src/Ledger/TimeSlot.hs +slotToEndPOSIXTime :: Slot -> QueryM POSIXTime +slotToEndPOSIXTime slot = do + futureSlot <- liftM (error "Unable to advance slot") + $ wrap <$> BigNum.add (unwrap slot) (BigNum.fromInt 1) + eraSummaries <- getEraSummaries + sysStart <- getSystemStart + futureTime <- liftEffect $ slotToPosixTime eraSummaries sysStart futureSlot + >>= hush >>> liftM (error "Unable to convert Slot to POSIXTime") + -- We assume that a slot is 1000 milliseconds here. + pure ((wrap <<< BigInt.fromInt $ -1) + futureTime) diff --git a/src/Serialization.js b/src/Serialization.js index e33a449490..690d3c52cc 100644 --- a/src/Serialization.js +++ b/src/Serialization.js @@ -337,10 +337,8 @@ exports.ppuSetTreasuryGrowthRate = setter("treasury_growth_rate"); exports.newProtocolVersion = major => minor => () => lib.ProtocolVersion.new(major, minor); -exports.ppuSetProtocolVersion = containerHelper => ppu => versions => () => - ppu.set_protocol_version( - containerHelper.pack(lib.ProtocolVersions, versions) - ); +exports.ppuSetProtocolVersion = ppu => version => () => + ppu.set_protocol_version(version); exports.ppuSetMinPoolCost = setter("min_pool_cost"); diff --git a/src/Serialization.purs b/src/Serialization.purs index 9a361c7d8c..ce571660d0 100644 --- a/src/Serialization.purs +++ b/src/Serialization.purs @@ -377,9 +377,8 @@ foreign import ppuSetTreasuryGrowthRate foreign import newProtocolVersion :: Int -> Int -> Effect ProtocolVersion foreign import ppuSetProtocolVersion - :: ContainerHelper - -> ProtocolParamUpdate - -> Array ProtocolVersion + :: ProtocolParamUpdate + -> ProtocolVersion -> Effect Unit foreign import ppuSetMinPoolCost @@ -556,9 +555,9 @@ convertProtocolParamUpdate mkUnitInterval >=> ppuSetExpansionRate ppu for_ treasuryGrowthRate $ mkUnitInterval >=> ppuSetTreasuryGrowthRate ppu - for_ protocolVersion $ - ppuSetProtocolVersion containerHelper ppu <=< - traverse \pv -> newProtocolVersion (UInt.toInt pv.major) + for_ protocolVersion \pv -> + ppuSetProtocolVersion ppu =<< + newProtocolVersion (UInt.toInt pv.major) (UInt.toInt pv.minor) for_ minPoolCost $ ppuSetMinPoolCost ppu for_ adaPerUtxoByte $ ppuSetAdaPerUtxoByte ppu diff --git a/src/Serialization/Types.purs b/src/Serialization/Types.purs index 73b30ff277..a48e05567c 100644 --- a/src/Serialization/Types.purs +++ b/src/Serialization/Types.purs @@ -47,7 +47,6 @@ module Serialization.Types , ProposedProtocolParameterUpdates , ProtocolParamUpdate , ProtocolVersion - , ProtocolVersions , PublicKey , PrivateKey , Redeemer @@ -138,7 +137,6 @@ foreign import data PoolParams :: Type foreign import data ProposedProtocolParameterUpdates :: Type foreign import data ProtocolParamUpdate :: Type foreign import data ProtocolVersion :: Type -foreign import data ProtocolVersions :: Type foreign import data PublicKey :: Type foreign import data PrivateKey :: Type foreign import data Redeemer :: Type diff --git a/src/Types/ScriptLookups.purs b/src/Types/ScriptLookups.purs index aebf80e90e..9d11a0ed6d 100644 --- a/src/Types/ScriptLookups.purs +++ b/src/Types/ScriptLookups.purs @@ -78,9 +78,7 @@ import Cardano.Types.Value import Control.Alt ((<|>)) import Control.Monad.Error.Class (catchError, throwError) import Control.Monad.Except.Trans (ExceptT(ExceptT), except, runExceptT) -import Control.Monad.Logger.Trans (LoggerT) import Control.Monad.Reader.Class (asks) -import Control.Monad.Reader.Trans (ReaderT) import Control.Monad.State.Trans (StateT, get, gets, put, runStateT) import Control.Monad.Trans.Class (lift) import Data.Array ((:), singleton, union) as Array @@ -111,12 +109,12 @@ import Effect (Effect) import Effect.Aff (Aff) import Effect.Aff.Class (liftAff) import Effect.Class (liftEffect) -import FromData (class FromData) import Hashing (datumHash) as Hashing import Helpers ((<\>), liftEither, liftM) +import IsData (class IsData) import Plutus.Conversion (fromPlutusTxOutput, fromPlutusValue) import Plutus.Types.Transaction (TransactionOutput) as Plutus -import QueryM (DefaultQueryConfig, QueryM, getDatumByHash) +import QueryM (QueryM, QueryMExtended, getDatumByHash) import QueryM.EraSummaries (getEraSummaries) import QueryM.ProtocolParameters (getProtocolParameters) import QueryM.SystemStart (getSystemStart) @@ -134,6 +132,7 @@ import Transaction , attachRedeemer , setScriptDataHash ) +import Type.Proxy (Proxy(Proxy)) import Types.Any (Any) import Types.Datum (DataHash, Datum) import Types.Interval @@ -186,7 +185,7 @@ import Types.TypedTxOut ) import Types.TypedValidator ( class DatumType - , class RedeemerType + , class ValidatorTypes , TypedValidator(TypedValidator) ) import Types.TypedValidator (generalise) as TV @@ -485,16 +484,14 @@ requireValue required = ValueSpentBalances { required, provided: mempty } -- We write `ReaderT QueryConfig Aff` below since type synonyms need to be fully -- applied. type ConstraintsM (a :: Type) (b :: Type) = - StateT (ConstraintProcessingState a) - (ReaderT DefaultQueryConfig (LoggerT Aff)) - b + StateT (ConstraintProcessingState a) (QueryMExtended ()) b -- The constraints don't precisely match those of Plutus: --- `forall a. (FromData (DatumType a), ToData (DatumType a), ToData (RedeemerType a))` --- as we don't have the same granularity on the classes, but the type `a` fixes --- a type `b` as seen below. We could alternatively create specific typeclasses: --- ToData (Datumtype a) <-> (Datumtype a b, ToData b) <= ToDataDatumType a b --- if we require granular control, similarly FromDataToDatumType a b etc. +-- `forall v. (FromData (DatumType v), ToData (DatumType v), ToData (RedeemerType v))` +-- as we don't have the same granularity on the classes, but the type `v` fixes +-- types `d` and `r` as seen below. We could alternatively create specific typeclasses: +-- ToData (DatumType v) <-> (DatumType v d, ToData d) <= ToDataDatumType v d +-- if we require granular control, similarly FromDataDatumType v d etc. -- We could use `MonadError` to clean up the `ExceptT`s below although we can't -- use the type alias because they need to be fully applied so this is perhaps -- more readable. @@ -503,13 +500,12 @@ type ConstraintsM (a :: Type) (b :: Type) = -- | Resolve some `TxConstraints` by modifying the `UnbalancedTx` in the -- | `ConstraintProcessingState` processLookupsAndConstraints - :: forall (a :: Type) (b :: Type) - . DatumType a b - => RedeemerType a b - => FromData b - => ToData b - => TxConstraints b b - -> ConstraintsM a (Either MkUnbalancedTxError Unit) + :: forall (validator :: Type) (datum :: Type) (redeemer :: Type) + . ValidatorTypes validator datum redeemer + => IsData datum + => IsData redeemer + => TxConstraints redeemer datum + -> ConstraintsM validator (Either MkUnbalancedTxError Unit) processLookupsAndConstraints (TxConstraints { constraints, ownInputs, ownOutputs }) = runExceptT do -- Hash all the MintingPolicys and Scripts beforehand. These maps are lost @@ -531,7 +527,7 @@ processLookupsAndConstraints mintRedeemers :: Array _ <- use _mintRedeemers <#> Map.toUnfoldable lift $ traverse_ (attachToCps attachRedeemer <<< snd) mintRedeemers - ExceptT $ foldConstraints addOwnInput ownInputs + ExceptT $ foldConstraints (addOwnInput (Proxy :: Proxy datum)) ownInputs ExceptT $ foldConstraints addOwnOutput ownOutputs ExceptT addScriptDataHash ExceptT addMissingValueSpent @@ -562,18 +558,17 @@ processLookupsAndConstraints -- Helper to run the stack and get back to `QueryM`. See comments in -- `processLookupsAndConstraints` regarding constraints. runConstraintsM - :: forall (a :: Type) (b :: Type) - . DatumType a b - => RedeemerType a b - => FromData b - => ToData b - => ScriptLookups a - -> TxConstraints b b - -> QueryM (Either MkUnbalancedTxError (ConstraintProcessingState a)) + :: forall (validator :: Type) (datum :: Type) (redeemer :: Type) + . ValidatorTypes validator datum redeemer + => IsData datum + => IsData redeemer + => ScriptLookups validator + -> TxConstraints redeemer datum + -> QueryM (Either MkUnbalancedTxError (ConstraintProcessingState validator)) runConstraintsM lookups txConstraints = do costModels <- getProtocolParameters <#> unwrap >>> _.costModels let - initCps :: ConstraintProcessingState a + initCps :: ConstraintProcessingState validator initCps = { unbalancedTx: emptyUnbalancedTx , valueSpentBalancesInputs: @@ -588,8 +583,9 @@ runConstraintsM lookups txConstraints = do } unpackTuple - :: Either MkUnbalancedTxError Unit /\ (ConstraintProcessingState a) - -> Either MkUnbalancedTxError (ConstraintProcessingState a) + :: Either MkUnbalancedTxError Unit /\ + (ConstraintProcessingState validator) + -> Either MkUnbalancedTxError (ConstraintProcessingState validator) unpackTuple (Left err /\ _) = Left err unpackTuple (_ /\ cps) = Right cps unpackTuple <$> @@ -598,13 +594,12 @@ runConstraintsM lookups txConstraints = do -- See comments in `processLookupsAndConstraints` regarding constraints. -- | Create an `UnbalancedTx` given `ScriptLookups` and `TxConstraints`. mkUnbalancedTx' - :: forall (a :: Type) (b :: Type) - . DatumType a b - => RedeemerType a b - => FromData b - => ToData b - => ScriptLookups a - -> TxConstraints b b + :: forall (validator :: Type) (datum :: Type) (redeemer :: Type) + . ValidatorTypes validator datum redeemer + => IsData datum + => IsData redeemer + => ScriptLookups validator + -> TxConstraints redeemer datum -> QueryM (Either MkUnbalancedTxError UnbalancedTx) mkUnbalancedTx' scriptLookups txConstraints = runConstraintsM scriptLookups txConstraints <#> map _.unbalancedTx @@ -637,13 +632,12 @@ instance Show UnattachedUnbalancedTx where -- | the server. The `Spend` redeemers will require reindexing and all hardcoded -- | to `zero` from this function. mkUnbalancedTx - :: forall (a :: Type) (b :: Type) - . DatumType a b - => RedeemerType a b - => FromData b - => ToData b - => ScriptLookups a - -> TxConstraints b b + :: forall (validator :: Type) (datum :: Type) (redeemer :: Type) + . ValidatorTypes validator datum redeemer + => IsData datum + => IsData redeemer + => ScriptLookups validator + -> TxConstraints redeemer datum -> QueryM (Either MkUnbalancedTxError UnattachedUnbalancedTx) mkUnbalancedTx scriptLookups txConstraints = runConstraintsM scriptLookups txConstraints <#> map @@ -727,14 +721,14 @@ updateUtxoIndex = runExceptT do -- | Add a typed input, checking the type of the output it spends. Return the value -- | of the spent output. addOwnInput - :: forall (a :: Type) (b :: Type) - . DatumType a b - => RedeemerType a b - => FromData b - => ToData b - => InputConstraint b - -> ConstraintsM a (Either MkUnbalancedTxError Unit) -addOwnInput (InputConstraint { txOutRef }) = do + :: forall (validator :: Type) (datum :: Type) (redeemer :: Type) + . ValidatorTypes validator datum redeemer + => IsData datum + => IsData redeemer + => Proxy datum + -> InputConstraint redeemer + -> ConstraintsM validator (Either MkUnbalancedTxError Unit) +addOwnInput _pd (InputConstraint { txOutRef }) = do networkId <- getNetworkId runExceptT do ScriptLookups { txOutputs, typedValidator } <- use _lookups @@ -754,12 +748,11 @@ addOwnInput (InputConstraint { txOutRef }) = do -- | Add a typed output and return its value. addOwnOutput - :: forall (a :: Type) (b :: Type) - . DatumType a b - => FromData b - => ToData b - => OutputConstraint b - -> ConstraintsM a (Either MkUnbalancedTxError Unit) + :: forall (validator :: Type) (datum :: Type) + . DatumType validator datum + => ToData datum + => OutputConstraint datum + -> ConstraintsM validator (Either MkUnbalancedTxError Unit) addOwnOutput (OutputConstraint { datum: d, value }) = do networkId <- getNetworkId runExceptT do @@ -1131,4 +1124,4 @@ getNetworkId :: forall (a :: Type) . ConstraintsM a NetworkId getNetworkId = use (_cpsToTxBody <<< _networkId) - >>= maybe (lift $ asks _.networkId) pure + >>= maybe (lift $ asks $ _.config >>> _.networkId) pure diff --git a/src/Types/TypedTxOut.purs b/src/Types/TypedTxOut.purs index c00ba7ec31..d5fe34f549 100644 --- a/src/Types/TypedTxOut.purs +++ b/src/Types/TypedTxOut.purs @@ -42,6 +42,7 @@ import Data.Show.Generic (genericShow) import FromData (class FromData, fromData) import Hashing (datumHash) as Hashing import Helpers (liftM) +import IsData (class IsData) import QueryM (QueryM, getDatumByHash) import Scripts (typedValidatorEnterpriseAddress) import Serialization.Address (Address, NetworkId) @@ -61,102 +62,94 @@ import Cardano.Types.Value (Value) -- | carries the address type. We don't include such a type in our setup. -- | Note that `TypedTxOut` is implicitly constrained by its smart -- | constructor. -newtype TypedTxOutRef (a :: Type) (b :: Type) = TypedTxOutRef - { txOutRef :: TransactionInput, typedTxOut :: TypedTxOut a b } +newtype TypedTxOutRef (validator :: Type) (datum :: Type) = TypedTxOutRef + { txOutRef :: TransactionInput, typedTxOut :: TypedTxOut validator datum } --- `DatumType a b` not needed but this replicates Plutus and provides extra +-- `DatumType validator datum` not needed but this replicates Plutus and provides extra -- type safety. -derive newtype instance (DatumType a b, Eq b) => Eq (TypedTxOutRef a b) +derive newtype instance + ( DatumType validator datum + , Eq datum + ) => + Eq (TypedTxOutRef validator datum) -- | Extract the `Address` of a `TypedTxOutRef` typedTxOutRefAddress - :: forall (a :: Type) (b :: Type) - . DatumType a b - => FromData b - => ToData b - => TypedTxOutRef a b + :: forall (validator :: Type) (datum :: Type) + . DatumType validator datum + => TypedTxOutRef validator datum -> Address typedTxOutRefAddress (TypedTxOutRef { typedTxOut }) = typedTxOutAddress typedTxOut -- | Extract the `DataHash` of a `TypedTxOutRef` typedTxOutRefDatumHash - :: forall (a :: Type) (b :: Type) - . DatumType a b - => FromData b - => ToData b - => TypedTxOutRef a b + :: forall (validator :: Type) (datum :: Type) + . DatumType validator datum + => TypedTxOutRef validator datum -> Maybe DataHash typedTxOutRefDatumHash (TypedTxOutRef { typedTxOut }) = typedTxOutDatumHash typedTxOut -- | Extract the `Value` of a `TypedTxOutRef` typedTxOutRefValue - :: forall (a :: Type) (b :: Type) - . DatumType a b - => FromData b - => ToData b - => TypedTxOutRef a b + :: forall (validator :: Type) (datum :: Type) + . DatumType validator datum + => TypedTxOutRef validator datum -> Value typedTxOutRefValue (TypedTxOutRef { typedTxOut }) = typedTxOutValue typedTxOut -- | Extract the `TransactionInput` of a `TypedTxOutRef` typedTxOutRefInput - :: forall (a :: Type) (b :: Type) - . DatumType a b - => FromData b - => ToData b - => TypedTxOutRef a b + :: forall (validator :: Type) (datum :: Type) + . DatumType validator datum + => TypedTxOutRef validator datum -> TransactionInput typedTxOutRefInput (TypedTxOutRef { txOutRef }) = txOutRef -- A `TransactionOutput` tagged by a phantom type: and the connection type of -- the output. DO NOT import as extra constraints are required so only import -- the smart constructor `mkTypedTxOut` -newtype TypedTxOut (a :: Type) (b :: Type) = TypedTxOut - { txOut :: TransactionOutput, data :: b } +newtype TypedTxOut (validator :: Type) (datum :: Type) = TypedTxOut + { txOut :: TransactionOutput, data :: datum } -- `DatumType a b` not needed but this replicates Plutus and provides extra -- type safety. -derive newtype instance (DatumType a b, Eq b) => Eq (TypedTxOut a b) +derive newtype instance + ( DatumType validator datum + , Eq datum + ) => + Eq (TypedTxOut validator datum) -- | Extract the `Address` of a `TypedTxOut` typedTxOutAddress - :: forall (a :: Type) (b :: Type) - . DatumType a b - => FromData b - => ToData b - => TypedTxOut a b + :: forall (validator :: Type) (datum :: Type) + . DatumType validator datum + => TypedTxOut validator datum -> Address typedTxOutAddress (TypedTxOut { txOut }) = (unwrap txOut).address -- | Extract the `DataHash` of a `TypedTxOut` typedTxOutDatumHash - :: forall (a :: Type) (b :: Type) - . DatumType a b - => FromData b - => ToData b - => TypedTxOut a b + :: forall (validator :: Type) (datum :: Type) + . DatumType validator datum + => TypedTxOut validator datum -> Maybe DataHash typedTxOutDatumHash (TypedTxOut { txOut }) = (unwrap txOut).dataHash -- | Extract the `Value` of a `TypedTxOut` typedTxOutValue - :: forall (a :: Type) (b :: Type) - . DatumType a b - => FromData b - => ToData b - => TypedTxOut a b + :: forall (validator :: Type) (datum :: Type) + . DatumType validator datum + => TypedTxOut validator datum -> Value typedTxOutValue (TypedTxOut { txOut }) = (unwrap txOut).amount -- | Extract the `TxOut` ~ `TransactionOutput` of a `TypedTxOut` typedTxOutTxOut - :: forall (a :: Type) (b :: Type) - . DatumType a b - => FromData b - => ToData b - => TypedTxOut a b + :: forall (validator :: Type) (datum :: Type) + . DatumType validator datum + => TypedTxOut validator datum -> TransactionOutput typedTxOutTxOut (TypedTxOut { txOut }) = txOut @@ -167,15 +160,14 @@ typedTxOutTxOut (TypedTxOut { txOut }) = txOut -- | constructor is required because extra constraints are needed. -- | `TransactionOutput` is tagged by a phantom type. mkTypedTxOut - :: forall (a :: Type) (b :: Type) - . DatumType a b - => FromData b - => ToData b + :: forall (validator :: Type) (datum :: Type) + . DatumType validator datum + => ToData datum => NetworkId - -> TypedValidator a - -> b + -> TypedValidator validator + -> datum -> Value - -> Maybe (TypedTxOut a b) + -> Maybe (TypedTxOut validator datum) mkTypedTxOut networkId typedVal dt amount = let mDHash = Hashing.datumHash $ Datum $ toData dt @@ -190,9 +182,9 @@ mkTypedTxOut networkId typedVal dt amount = wrap { address, amount, dataHash: pure dHash } where mkTypedTxOut' - :: b -- Data + :: datum -- Data -> TransactionOutput - -> TypedTxOut a b + -> TypedTxOut validator datum mkTypedTxOut' dat txOut = TypedTxOut { txOut, data: dat } -- | An error we can get while trying to type an existing transaction part. @@ -214,10 +206,10 @@ instance Show TypeCheckError where -- | Checks that the given validator hash is consistent with the actual validator. checkValidatorAddress - :: forall (a :: Type) (m :: Type -> Type) + :: forall (validator :: Type) (m :: Type -> Type) . Monad m => NetworkId - -> TypedValidator a + -> TypedValidator validator -> Address -> m (Either TypeCheckError Unit) checkValidatorAddress networkId typedVal actualAddr = runExceptT do @@ -240,27 +232,26 @@ checkValidatorAddress networkId typedVal actualAddr = runExceptT do -- | Checks that the given datum has the right type. checkDatum - :: forall (a :: Type) (b :: Type) (m :: Type -> Type) + :: forall (validator :: Type) (datum :: Type) (m :: Type -> Type) . Monad m - => DatumType a b - => FromData b - => TypedValidator a + => DatumType validator datum + => FromData datum + => TypedValidator validator -> Datum - -> m (Either TypeCheckError b) + -> m (Either TypeCheckError datum) checkDatum _ (Datum pd) = - runExceptT $ liftM (WrongDatumType pd) (fromData pd :: Maybe b) + runExceptT $ liftM (WrongDatumType pd) (fromData pd :: Maybe datum) -- | Create a `TypedTxOut` from an existing `TransactionInput` by -- | checking the types of its parts. typeTxOut - :: forall (a :: Type) (b :: Type) (m :: Type -> Type) - . DatumType a b - => FromData b - => ToData b + :: forall (validator :: Type) (datum :: Type) + . DatumType validator datum + => IsData datum => NetworkId - -> TypedValidator a + -> TypedValidator validator -> TransactionOutput - -> QueryM (Either TypeCheckError (TypedTxOut a b)) + -> QueryM (Either TypeCheckError (TypedTxOut validator datum)) typeTxOut networkId typedVal @@ -279,15 +270,14 @@ typeTxOut -- | against the validator script and be able to look up the `TransactionInput` to -- | which this reference points. typeTxOutRef - :: forall (a :: Type) (b :: Type) (m :: Type -> Type) - . DatumType a b - => FromData b - => ToData b + :: forall (validator :: Type) (datum :: Type) (m :: Type -> Type) + . DatumType validator datum + => IsData datum => NetworkId -> (TransactionInput -> Maybe TransactionOutput) - -> TypedValidator a + -> TypedValidator validator -> TransactionInput - -> QueryM (Either TypeCheckError (TypedTxOutRef a b)) + -> QueryM (Either TypeCheckError (TypedTxOutRef validator datum)) typeTxOutRef networkId lookupRef typedVal txOutRef = runExceptT do out <- liftM UnknownRef (lookupRef txOutRef) typedTxOut <- ExceptT $ typeTxOut networkId typedVal out diff --git a/src/Types/TypedValidator.purs b/src/Types/TypedValidator.purs index 9b7fdb3fd4..497e6da220 100644 --- a/src/Types/TypedValidator.purs +++ b/src/Types/TypedValidator.purs @@ -46,38 +46,49 @@ import Types.Scripts -- it suffices. -- | A typeclass that associates a type standing for a connection type with two -- | types, the type of the redeemer and the data script for that connection type. +class ValidatorTypes :: Type -> Type -> Type -> Constraint class - ( DatumType a b - , RedeemerType a b + ( DatumType validator datum + , RedeemerType validator redeemer ) <= - ValidatorTypes (a :: Type) (b :: Type) - | a -> b + ValidatorTypes validator datum redeemer + +instance + ( DatumType validator datum + , RedeemerType validator redeemer + ) => + ValidatorTypes validator datum redeemer -- | The type of the data of this connection type. -class DatumType (a :: Type) (b :: Type) | a -> b +class DatumType :: Type -> Type -> Constraint +class DatumType validator datum | validator -> datum instance DatumType Void Void -else instance DatumType Any PlutusData +instance DatumType Any PlutusData --- | Default instance -else instance DatumType a Unit +instance DatumType PlutusData Unit -- | The type of the redeemers of this connection type. -class RedeemerType (a :: Type) (b :: Type) | a -> b +class RedeemerType :: Type -> Type -> Constraint +class RedeemerType validator redeemer | validator -> redeemer instance RedeemerType Void Void -else instance RedeemerType Any PlutusData +instance RedeemerType Any PlutusData --- | Default instance -else instance RedeemerType a Unit +instance RedeemerType PlutusData Unit -- Replace `ScriptContext` by `Transaction` which contains all the scripts -- anyway: -- | The type of validators for the given connection type. -type ValidatorType (a :: Type) (b :: Type) = - DatumType a b => RedeemerType a b => b -> b -> Transaction -> Boolean +type ValidatorType (validator :: Type) (datum :: Type) (redeemer :: Type) = + DatumType validator datum + => RedeemerType validator redeemer + => datum + -> redeemer + -> Transaction + -> Boolean type WrappedValidatorType = PlutusData -> PlutusData -> PlutusData -> Effect Unit diff --git a/src/Wallet.js b/src/Wallet.js index c9b08aab6f..014a125325 100644 --- a/src/Wallet.js +++ b/src/Wallet.js @@ -1,13 +1,54 @@ /* global BROWSER_RUNTIME */ -exports._enableNami = () => window.cardano.nami.enable(); +const getIsWalletAvailableFunctionName = wallet => { + const strs = { + nami: "isNamiWalletAvailable", + gerowallet: "isGeroWalletAvailable", + }; -exports._enableGero = () => window.cardano.gerowallet.enable(); + return strs[wallet] || "is?WalletAvailable"; +}; -const isWalletAvailable = walletName => () => - typeof window.cardano != "undefined" && - typeof window.cardano[walletName] != "undefined" && - typeof window.cardano[walletName].enable == "function"; +const nodeEnvError = + "`window` is not an object. Are you trying to run a Contract with" + + " connected light wallet in NodeJS environment?"; + +const checkNotNode = () => { + if (typeof window != "object") { + throw nodeEnvError; + } +}; + +const enableWallet = wallet => () => { + const isAvailable = isWalletAvailable(wallet)(); + if (isAvailable) { + return window.cardano[wallet].enable().catch(e => { + throw ( + "enableWallet failed: " + + (typeof e.info == "string" ? e.info : e.toString()) + ); + }); + } else { + throw ( + "Wallet is not available. Use `" + + getIsWalletAvailableFunctionName(wallet) + + "` before connecting." + ); + } +}; + +exports._enableNami = enableWallet("nami"); + +exports._enableGero = enableWallet("gerowallet"); + +const isWalletAvailable = walletName => () => { + checkNotNode(); + return ( + typeof window.cardano != "undefined" && + typeof window.cardano[walletName] != "undefined" && + typeof window.cardano[walletName].enable == "function" + ); +}; exports._isNamiAvailable = isWalletAvailable("nami"); diff --git a/src/Wallet/Key.purs b/src/Wallet/Key.purs index cb83adaead..07bc2a6158 100644 --- a/src/Wallet/Key.purs +++ b/src/Wallet/Key.purs @@ -1,5 +1,5 @@ module Wallet.Key - ( KeyWallet + ( KeyWallet(KeyWallet) , PrivatePaymentKey(PrivatePaymentKey) , PrivateStakeKey(PrivateStakeKey) , privateKeysToKeyWallet @@ -43,12 +43,14 @@ import Serialization.Types (PrivateKey) ------------------------------------------------------------------------------- -- Key backend ------------------------------------------------------------------------------- -type KeyWallet = +newtype KeyWallet = KeyWallet { address :: NetworkId -> Aff Address , selectCollateral :: Utxos -> Maybe TransactionUnspentOutput , signTx :: Transaction -> Aff Transaction } +derive instance Newtype KeyWallet _ + newtype PrivatePaymentKey = PrivatePaymentKey PrivateKey derive instance Newtype PrivatePaymentKey _ @@ -59,7 +61,7 @@ derive instance Newtype PrivateStakeKey _ privateKeysToKeyWallet :: PrivatePaymentKey -> Maybe PrivateStakeKey -> KeyWallet -privateKeysToKeyWallet payKey mbStakeKey = +privateKeysToKeyWallet payKey mbStakeKey = KeyWallet { address , selectCollateral , signTx diff --git a/src/Wallet/Spec.purs b/src/Wallet/Spec.purs new file mode 100644 index 0000000000..89810cac14 --- /dev/null +++ b/src/Wallet/Spec.purs @@ -0,0 +1,23 @@ +module Wallet.Spec + ( WalletSpec(UseKeys, ConnectToNami, ConnectToGero) + , PrivateStakeKeySource(PrivateStakeKeyFile, PrivateStakeKeyValue) + , PrivatePaymentKeySource(PrivatePaymentKeyFile, PrivatePaymentKeyValue) + ) where + +import Data.Maybe (Maybe) +import Node.Path (FilePath) +import Wallet.Key (PrivatePaymentKey, PrivateStakeKey) + +data PrivatePaymentKeySource + = PrivatePaymentKeyFile FilePath + | PrivatePaymentKeyValue PrivatePaymentKey + +data PrivateStakeKeySource + = PrivateStakeKeyFile FilePath + | PrivateStakeKeyValue PrivateStakeKey + +-- | A data type to describe instructions on how to initialize a wallet. +data WalletSpec + = UseKeys PrivatePaymentKeySource (Maybe PrivateStakeKeySource) + | ConnectToNami + | ConnectToGero diff --git a/templates/ctl-scaffold/.gitattributes b/templates/ctl-scaffold/.gitattributes new file mode 100644 index 0000000000..0ee69dcf55 --- /dev/null +++ b/templates/ctl-scaffold/.gitattributes @@ -0,0 +1,3 @@ +node*.nix linguist-generated=true +spago-packages.nix linguist-generated=true +flake.lock linguist-generated=true diff --git a/templates/ctl-scaffold/.gitignore b/templates/ctl-scaffold/.gitignore new file mode 100644 index 0000000000..b458840656 --- /dev/null +++ b/templates/ctl-scaffold/.gitignore @@ -0,0 +1,19 @@ +/bower_components/ +/.pulp-cache/ +/output/ +/generated-docs/ +/.psc-package/ +/.psc* +/.purs* +/.psa* +/.spago +/.spago2nix* +result +result-* +.envrc +.direnv +/node_modules +/node_modules/ +.projectile +/dist/ +output.js diff --git a/templates/ctl-scaffold/Makefile b/templates/ctl-scaffold/Makefile new file mode 100644 index 0000000000..a7e01a24ce --- /dev/null +++ b/templates/ctl-scaffold/Makefile @@ -0,0 +1,19 @@ +SHELL := bash +.ONESHELL: +.SHELLFLAGS := -eu -o pipefail -c + +ps-sources := $$(fd -epurs) +ps-entrypoint := Scaffold.Main +ps-bundle = spago bundle-module -m ${ps-entrypoint} --to output.js + +run-dev: + @${ps-bundle} && BROWSER_RUNTIME=1 webpack-dev-server --progress + +run-build: + @${ps-bundle} && BROWSER_RUNTIME=1 webpack --mode=production + +check-format: + @purs-tidy check ${ps-sources} + +format: + @purs-tidy format-in-place ${ps-sources} diff --git a/templates/ctl-scaffold/README.md b/templates/ctl-scaffold/README.md new file mode 100644 index 0000000000..a48329bb3e --- /dev/null +++ b/templates/ctl-scaffold/README.md @@ -0,0 +1,13 @@ +# ctl-scaffold + +Welcome to your new CTL project! + +To enter the Nix environment and start working on it, run `nix develop` + +Please also see + +- Our documentation at https://github.com/Plutonomicon/cardano-transaction-lib/tree/develop/doc +- Our Pursuit docs at https://plutonomicon.github.io/cardano-transaction-lib/ +- Our Discord server https://discord.gg/c8kZWxzJ + +If you encounter problems and/or want to report a bug, you can open an issue at https://github.com/Plutonomicon/cardano-transaction-lib/issues. Please search for existing issues before! diff --git a/templates/ctl-scaffold/default.nix b/templates/ctl-scaffold/default.nix new file mode 100644 index 0000000000..41d50be500 --- /dev/null +++ b/templates/ctl-scaffold/default.nix @@ -0,0 +1,15 @@ +( + import + ( + let + lock = builtins.fromJSON (builtins.readFile ./flake.lock); + in + fetchTarball { + url = "https://github.com/edolstra/flake-compat/archive/${lock.nodes.flake-compat.locked.rev}.tar.gz"; + sha256 = lock.nodes.flake-compat.locked.narHash; + } + ) + { + src = ./.; + } +).defaultNix diff --git a/templates/ctl-scaffold/exe/Main.purs b/templates/ctl-scaffold/exe/Main.purs new file mode 100644 index 0000000000..f86a65fad7 --- /dev/null +++ b/templates/ctl-scaffold/exe/Main.purs @@ -0,0 +1,13 @@ +module Scaffold.Main (main) where + +import Contract.Prelude + +import Contract.Config as Contract.Config +import Contract.Monad as Contract.Monad +import Scaffold as Scaffold + +main :: Effect Unit +main = Contract.Monad.launchAff_ + $ void + $ Contract.Monad.runContract Contract.Config.testnetNamiConfig + $ Scaffold.contract diff --git a/templates/ctl-scaffold/flake.lock b/templates/ctl-scaffold/flake.lock new file mode 100644 index 0000000000..6a860ddff8 --- /dev/null +++ b/templates/ctl-scaffold/flake.lock @@ -0,0 +1,1933 @@ +{ + "nodes": { + "HTTP": { + "flake": false, + "locked": { + "lastModified": 1451647621, + "narHash": "sha256-oHIyw3x0iKBexEo49YeUDV1k74ZtyYKGR2gNJXXRxts=", + "owner": "phadej", + "repo": "HTTP", + "rev": "9bc0996d412fef1787449d841277ef663ad9a915", + "type": "github" + }, + "original": { + "owner": "phadej", + "repo": "HTTP", + "type": "github" + } + }, + "Win32-network": { + "flake": false, + "locked": { + "lastModified": 1627315969, + "narHash": "sha256-Hesb5GXSx0IwKSIi42ofisVELcQNX6lwHcoZcbaDiqc=", + "owner": "input-output-hk", + "repo": "Win32-network", + "rev": "3825d3abf75f83f406c1f7161883c438dac7277d", + "type": "github" + }, + "original": { + "owner": "input-output-hk", + "repo": "Win32-network", + "rev": "3825d3abf75f83f406c1f7161883c438dac7277d", + "type": "github" + } + }, + "Win32-network_2": { + "flake": false, + "locked": { + "lastModified": 1627315969, + "narHash": "sha256-Hesb5GXSx0IwKSIi42ofisVELcQNX6lwHcoZcbaDiqc=", + "owner": "input-output-hk", + "repo": "Win32-network", + "rev": "3825d3abf75f83f406c1f7161883c438dac7277d", + "type": "github" + }, + "original": { + "owner": "input-output-hk", + "repo": "Win32-network", + "rev": "3825d3abf75f83f406c1f7161883c438dac7277d", + "type": "github" + } + }, + "Win32-network_3": { + "flake": false, + "locked": { + "lastModified": 1627315969, + "narHash": "sha256-Hesb5GXSx0IwKSIi42ofisVELcQNX6lwHcoZcbaDiqc=", + "owner": "input-output-hk", + "repo": "Win32-network", + "rev": "3825d3abf75f83f406c1f7161883c438dac7277d", + "type": "github" + }, + "original": { + "owner": "input-output-hk", + "repo": "Win32-network", + "rev": "3825d3abf75f83f406c1f7161883c438dac7277d", + "type": "github" + } + }, + "bot-plutus-interface": { + "inputs": { + "Win32-network": "Win32-network_3", + "cardano-addresses": "cardano-addresses_2", + "cardano-base": "cardano-base_3", + "cardano-config": "cardano-config_2", + "cardano-crypto": "cardano-crypto_3", + "cardano-ledger": "cardano-ledger_3", + "cardano-node": "cardano-node_3", + "cardano-prelude": "cardano-prelude_3", + "cardano-wallet": "cardano-wallet_2", + "ekg-forward": "ekg-forward_2", + "ekg-json": "ekg-json_2", + "flake-compat": "flake-compat_4", + "flat": "flat_3", + "goblins": "goblins_3", + "haskell-nix": "haskell-nix", + "hedgehog-extras": "hedgehog-extras_2", + "hw-aeson": "hw-aeson", + "hysterical-screams": "hysterical-screams", + "io-sim": "io-sim_2", + "iohk-monitoring-framework": "iohk-monitoring-framework_3", + "iohk-nix": "iohk-nix_3", + "nixpkgs": [ + "ctl", + "plutip", + "bot-plutus-interface", + "haskell-nix", + "nixpkgs-unstable" + ], + "optparse-applicative": "optparse-applicative_3", + "ouroboros-network": "ouroboros-network_3", + "plutus": "plutus_2", + "plutus-apps": "plutus-apps", + "purescript-bridge": "purescript-bridge", + "quickcheck-dynamic": "quickcheck-dynamic", + "servant-purescript": "servant-purescript", + "typed-protocols": "typed-protocols_2" + }, + "locked": { + "lastModified": 1658231641, + "narHash": "sha256-WlyKzl+MvzKLSC21XZLNb5ZJdxJdLhLmFZZqwzTstzQ=", + "owner": "mlabs-haskell", + "repo": "bot-plutus-interface", + "rev": "e6e0c4aa81c7e5e6c109c18675759457d5fbbce2", + "type": "github" + }, + "original": { + "owner": "mlabs-haskell", + "repo": "bot-plutus-interface", + "rev": "e6e0c4aa81c7e5e6c109c18675759457d5fbbce2", + "type": "github" + } + }, + "cabal-32": { + "flake": false, + "locked": { + "lastModified": 1603716527, + "narHash": "sha256-X0TFfdD4KZpwl0Zr6x+PLxUt/VyKQfX7ylXHdmZIL+w=", + "owner": "haskell", + "repo": "cabal", + "rev": "48bf10787e27364730dd37a42b603cee8d6af7ee", + "type": "github" + }, + "original": { + "owner": "haskell", + "ref": "3.2", + "repo": "cabal", + "type": "github" + } + }, + "cabal-34": { + "flake": false, + "locked": { + "lastModified": 1640353650, + "narHash": "sha256-N1t6M3/wqj90AEdRkeC8i923gQYUpzSr8b40qVOZ1Rk=", + "owner": "haskell", + "repo": "cabal", + "rev": "942639c18c0cd8ec53e0a6f8d120091af35312cd", + "type": "github" + }, + "original": { + "owner": "haskell", + "ref": "3.4", + "repo": "cabal", + "type": "github" + } + }, + "cabal-36": { + "flake": false, + "locked": { + "lastModified": 1641652457, + "narHash": "sha256-BlFPKP4C4HRUJeAbdembX1Rms1LD380q9s0qVDeoAak=", + "owner": "haskell", + "repo": "cabal", + "rev": "f27667f8ec360c475027dcaee0138c937477b070", + "type": "github" + }, + "original": { + "owner": "haskell", + "ref": "3.6", + "repo": "cabal", + "type": "github" + } + }, + "cardano-addresses": { + "flake": false, + "locked": { + "lastModified": 1631515399, + "narHash": "sha256-XgXQKJHRKAFwIjONh19D/gKE0ARlhMXXcV74eZpd0lw=", + "owner": "input-output-hk", + "repo": "cardano-addresses", + "rev": "d2f86caa085402a953920c6714a0de6a50b655ec", + "type": "github" + }, + "original": { + "owner": "input-output-hk", + "repo": "cardano-addresses", + "rev": "d2f86caa085402a953920c6714a0de6a50b655ec", + "type": "github" + } + }, + "cardano-addresses_2": { + "flake": false, + "locked": { + "lastModified": 1655809189, + "narHash": "sha256-hYAvI7KlFnFRjMG8/JvDl733YnQUE1O26VMcr94h0oM=", + "owner": "input-output-hk", + "repo": "cardano-addresses", + "rev": "b6f2f3cef01a399376064194fd96711a5bdba4a7", + "type": "github" + }, + "original": { + "owner": "input-output-hk", + "repo": "cardano-addresses", + "rev": "b6f2f3cef01a399376064194fd96711a5bdba4a7", + "type": "github" + } + }, + "cardano-base": { + "flake": false, + "locked": { + "lastModified": 1635841753, + "narHash": "sha256-OXKsJ1UTj5kJ9xaThM54ZmxFAiFINTPKd4JQa4dPmEU=", + "owner": "input-output-hk", + "repo": "cardano-base", + "rev": "41545ba3ac6b3095966316a99883d678b5ab8da8", + "type": "github" + }, + "original": { + "owner": "input-output-hk", + "repo": "cardano-base", + "rev": "41545ba3ac6b3095966316a99883d678b5ab8da8", + "type": "github" + } + }, + "cardano-base_2": { + "flake": false, + "locked": { + "lastModified": 1654537609, + "narHash": "sha256-4b0keLjRaVSdEwfBXB1iT3QPlsutdxSltGfBufT4Clw=", + "owner": "input-output-hk", + "repo": "cardano-base", + "rev": "0f3a867493059e650cda69e20a5cbf1ace289a57", + "type": "github" + }, + "original": { + "owner": "input-output-hk", + "repo": "cardano-base", + "rev": "0f3a867493059e650cda69e20a5cbf1ace289a57", + "type": "github" + } + }, + "cardano-base_3": { + "flake": false, + "locked": { + "lastModified": 1654537609, + "narHash": "sha256-4b0keLjRaVSdEwfBXB1iT3QPlsutdxSltGfBufT4Clw=", + "owner": "input-output-hk", + "repo": "cardano-base", + "rev": "0f3a867493059e650cda69e20a5cbf1ace289a57", + "type": "github" + }, + "original": { + "owner": "input-output-hk", + "repo": "cardano-base", + "rev": "0f3a867493059e650cda69e20a5cbf1ace289a57", + "type": "github" + } + }, + "cardano-config": { + "flake": false, + "locked": { + "lastModified": 1641434343, + "narHash": "sha256-/+BX+QcRS3QcADRTqXXReHDRYpJa/+qlcl1E0C3TO+E=", + "owner": "input-output-hk", + "repo": "cardano-config", + "rev": "fe7855e981072d392513f9cf3994e0b6eba40d7d", + "type": "github" + }, + "original": { + "owner": "input-output-hk", + "repo": "cardano-config", + "rev": "fe7855e981072d392513f9cf3994e0b6eba40d7d", + "type": "github" + } + }, + "cardano-config_2": { + "flake": false, + "locked": { + "lastModified": 1642737642, + "narHash": "sha256-TNbpnR7llUgBN2WY7CryMxNVupBIUH01h1hRNHoxboY=", + "owner": "input-output-hk", + "repo": "cardano-config", + "rev": "1646e9167fab36c0bff82317743b96efa2d3adaa", + "type": "github" + }, + "original": { + "owner": "input-output-hk", + "repo": "cardano-config", + "rev": "1646e9167fab36c0bff82317743b96efa2d3adaa", + "type": "github" + } + }, + "cardano-configurations": { + "flake": false, + "locked": { + "lastModified": 1655361562, + "narHash": "sha256-b/z5RSgqTMQpEUSD4nbrBAr86PcQs+n6EMtn/YPeyj4=", + "owner": "input-output-hk", + "repo": "cardano-configurations", + "rev": "08e6c0572d5d48049fab521995b29607e0a91a9e", + "type": "github" + }, + "original": { + "owner": "input-output-hk", + "repo": "cardano-configurations", + "type": "github" + } + }, + "cardano-crypto": { + "flake": false, + "locked": { + "lastModified": 1604244485, + "narHash": "sha256-2Fipex/WjIRMrvx6F3hjJoAeMtFd2wGnZECT0kuIB9k=", + "owner": "input-output-hk", + "repo": "cardano-crypto", + "rev": "f73079303f663e028288f9f4a9e08bcca39a923e", + "type": "github" + }, + "original": { + "owner": "input-output-hk", + "repo": "cardano-crypto", + "rev": "f73079303f663e028288f9f4a9e08bcca39a923e", + "type": "github" + } + }, + "cardano-crypto_2": { + "flake": false, + "locked": { + "lastModified": 1604244485, + "narHash": "sha256-2Fipex/WjIRMrvx6F3hjJoAeMtFd2wGnZECT0kuIB9k=", + "owner": "input-output-hk", + "repo": "cardano-crypto", + "rev": "f73079303f663e028288f9f4a9e08bcca39a923e", + "type": "github" + }, + "original": { + "owner": "input-output-hk", + "repo": "cardano-crypto", + "rev": "f73079303f663e028288f9f4a9e08bcca39a923e", + "type": "github" + } + }, + "cardano-crypto_3": { + "flake": false, + "locked": { + "lastModified": 1604244485, + "narHash": "sha256-2Fipex/WjIRMrvx6F3hjJoAeMtFd2wGnZECT0kuIB9k=", + "owner": "input-output-hk", + "repo": "cardano-crypto", + "rev": "f73079303f663e028288f9f4a9e08bcca39a923e", + "type": "github" + }, + "original": { + "owner": "input-output-hk", + "repo": "cardano-crypto", + "rev": "f73079303f663e028288f9f4a9e08bcca39a923e", + "type": "github" + } + }, + "cardano-ledger": { + "flake": false, + "locked": { + "lastModified": 1639498285, + "narHash": "sha256-lRNfkGMHnpPO0T19FZY5BnuRkr0zTRZIkxZVgHH0fys=", + "owner": "input-output-hk", + "repo": "cardano-ledger", + "rev": "1a9ec4ae9e0b09d54e49b2a40c4ead37edadcce5", + "type": "github" + }, + "original": { + "owner": "input-output-hk", + "repo": "cardano-ledger", + "rev": "1a9ec4ae9e0b09d54e49b2a40c4ead37edadcce5", + "type": "github" + } + }, + "cardano-ledger_2": { + "flake": false, + "locked": { + "lastModified": 1655762257, + "narHash": "sha256-SaMhULHXgY0FiSKWc2dAYlgtbfPaFh/bUTgGqoNnMqY=", + "owner": "input-output-hk", + "repo": "cardano-ledger", + "rev": "ce3057e0863304ccb3f79d78c77136219dc786c6", + "type": "github" + }, + "original": { + "owner": "input-output-hk", + "repo": "cardano-ledger", + "rev": "ce3057e0863304ccb3f79d78c77136219dc786c6", + "type": "github" + } + }, + "cardano-ledger_3": { + "flake": false, + "locked": { + "lastModified": 1657127204, + "narHash": "sha256-4wcSA61TwoDTvJ6rx7tjEAJjQLO/cs8WGTHcOghNdTc=", + "owner": "input-output-hk", + "repo": "cardano-ledger", + "rev": "3be8a19083fc13d9261b1640e27dd389b51bb08e", + "type": "github" + }, + "original": { + "owner": "input-output-hk", + "repo": "cardano-ledger", + "rev": "3be8a19083fc13d9261b1640e27dd389b51bb08e", + "type": "github" + } + }, + "cardano-node": { + "flake": false, + "locked": { + "lastModified": 1643020087, + "narHash": "sha256-NPkY19Q5BJv1Ebf/biQ9fsbjh5gQuncoXQCslau/i6M=", + "owner": "input-output-hk", + "repo": "cardano-node", + "rev": "8909dea9b3996b8288f15f0e4f31fb0f63964197", + "type": "github" + }, + "original": { + "owner": "input-output-hk", + "repo": "cardano-node", + "rev": "8909dea9b3996b8288f15f0e4f31fb0f63964197", + "type": "github" + } + }, + "cardano-node_2": { + "flake": false, + "locked": { + "lastModified": 1656166930, + "narHash": "sha256-R7YGQ6UMG16ed9sGguDWq2cUgFnADeRdx8O2s2HqWRk=", + "owner": "input-output-hk", + "repo": "cardano-node", + "rev": "9f1d7dc163ee66410d912e48509d6a2300cfa68a", + "type": "github" + }, + "original": { + "owner": "input-output-hk", + "repo": "cardano-node", + "rev": "9f1d7dc163ee66410d912e48509d6a2300cfa68a", + "type": "github" + } + }, + "cardano-node_3": { + "flake": false, + "locked": { + "lastModified": 1657227628, + "narHash": "sha256-CP58qcHZJGYq1FzXCj8ll085TvnJoYMeXnVGVGLYH/w=", + "owner": "input-output-hk", + "repo": "cardano-node", + "rev": "c75451f0ffd7a60b5ad6c4263891e6c8acac105a", + "type": "github" + }, + "original": { + "owner": "input-output-hk", + "repo": "cardano-node", + "rev": "c75451f0ffd7a60b5ad6c4263891e6c8acac105a", + "type": "github" + } + }, + "cardano-prelude": { + "flake": false, + "locked": { + "lastModified": 1617089317, + "narHash": "sha256-kgX3DKyfjBb8/XcDEd+/adlETsFlp5sCSurHWgsFAQI=", + "owner": "input-output-hk", + "repo": "cardano-prelude", + "rev": "bb4ed71ba8e587f672d06edf9d2e376f4b055555", + "type": "github" + }, + "original": { + "owner": "input-output-hk", + "repo": "cardano-prelude", + "rev": "bb4ed71ba8e587f672d06edf9d2e376f4b055555", + "type": "github" + } + }, + "cardano-prelude_2": { + "flake": false, + "locked": { + "lastModified": 1617089317, + "narHash": "sha256-kgX3DKyfjBb8/XcDEd+/adlETsFlp5sCSurHWgsFAQI=", + "owner": "input-output-hk", + "repo": "cardano-prelude", + "rev": "bb4ed71ba8e587f672d06edf9d2e376f4b055555", + "type": "github" + }, + "original": { + "owner": "input-output-hk", + "repo": "cardano-prelude", + "rev": "bb4ed71ba8e587f672d06edf9d2e376f4b055555", + "type": "github" + } + }, + "cardano-prelude_3": { + "flake": false, + "locked": { + "lastModified": 1617089317, + "narHash": "sha256-kgX3DKyfjBb8/XcDEd+/adlETsFlp5sCSurHWgsFAQI=", + "owner": "input-output-hk", + "repo": "cardano-prelude", + "rev": "bb4ed71ba8e587f672d06edf9d2e376f4b055555", + "type": "github" + }, + "original": { + "owner": "input-output-hk", + "repo": "cardano-prelude", + "rev": "bb4ed71ba8e587f672d06edf9d2e376f4b055555", + "type": "github" + } + }, + "cardano-shell": { + "flake": false, + "locked": { + "lastModified": 1608537748, + "narHash": "sha256-PulY1GfiMgKVnBci3ex4ptk2UNYMXqGjJOxcPy2KYT4=", + "owner": "input-output-hk", + "repo": "cardano-shell", + "rev": "9392c75087cb9a3d453998f4230930dea3a95725", + "type": "github" + }, + "original": { + "owner": "input-output-hk", + "repo": "cardano-shell", + "type": "github" + } + }, + "cardano-wallet": { + "flake": false, + "locked": { + "lastModified": 1632116683, + "narHash": "sha256-Ju6XueTKP3FwRkKIE+7a32hcEJMGbxdqiznJNi9sYdc=", + "owner": "input-output-hk", + "repo": "cardano-wallet", + "rev": "ae7569293e94241ef6829139ec02bd91abd069df", + "type": "github" + }, + "original": { + "owner": "input-output-hk", + "repo": "cardano-wallet", + "rev": "ae7569293e94241ef6829139ec02bd91abd069df", + "type": "github" + } + }, + "cardano-wallet_2": { + "flake": false, + "locked": { + "lastModified": 1657745277, + "narHash": "sha256-+PrfQH6m7ROpHKNyo54MzLrL31tIvSZUQYnbBT70ekc=", + "owner": "input-output-hk", + "repo": "cardano-wallet", + "rev": "2ac308b00d9d4a3435f6b9594ded9495e2b217eb", + "type": "github" + }, + "original": { + "owner": "input-output-hk", + "repo": "cardano-wallet", + "rev": "2ac308b00d9d4a3435f6b9594ded9495e2b217eb", + "type": "github" + } + }, + "ctl": { + "inputs": { + "Win32-network": "Win32-network", + "cardano-addresses": "cardano-addresses", + "cardano-base": "cardano-base", + "cardano-config": "cardano-config", + "cardano-configurations": "cardano-configurations", + "cardano-crypto": "cardano-crypto", + "cardano-ledger": "cardano-ledger", + "cardano-node": "cardano-node", + "cardano-prelude": "cardano-prelude", + "cardano-wallet": "cardano-wallet", + "easy-purescript-nix": "easy-purescript-nix", + "ekg-forward": "ekg-forward", + "flake-compat": "flake-compat", + "flat": "flat", + "goblins": "goblins", + "haskell-nix": [ + "ctl", + "plutip", + "haskell-nix" + ], + "iohk-monitoring-framework": "iohk-monitoring-framework", + "iohk-nix": "iohk-nix", + "nixpkgs": [ + "ctl", + "plutip", + "nixpkgs" + ], + "ogmios": "ogmios", + "ogmios-datum-cache": "ogmios-datum-cache", + "optparse-applicative": "optparse-applicative_2", + "ouroboros-network": "ouroboros-network_2", + "plutip": "plutip", + "plutus": "plutus_3", + "purescript-bridge": "purescript-bridge_2", + "servant-purescript": "servant-purescript_2" + }, + "locked": { + "lastModified": 1659338089, + "narHash": "sha256-czjhC4uDnc8GXnmyGohnNCVV1/azmhH4jTCNp4JAucE=", + "owner": "Plutonomicon", + "repo": "cardano-transaction-lib", + "rev": "ec38c06c77c1b75a7e7f1dcb215db7816e44b339", + "type": "github" + }, + "original": { + "owner": "Plutonomicon", + "repo": "cardano-transaction-lib", + "rev": "ec38c06c77c1b75a7e7f1dcb215db7816e44b339", + "type": "github" + } + }, + "easy-purescript-nix": { + "flake": false, + "locked": { + "lastModified": 1649768932, + "narHash": "sha256-T96xGZV2AEP07smv/L2s5U7jY1LTdJEiTnA90gJ3Fco=", + "owner": "justinwoo", + "repo": "easy-purescript-nix", + "rev": "d56c436a66ec2a8a93b309c83693cef1507dca7a", + "type": "github" + }, + "original": { + "owner": "justinwoo", + "repo": "easy-purescript-nix", + "rev": "d56c436a66ec2a8a93b309c83693cef1507dca7a", + "type": "github" + } + }, + "ekg-forward": { + "flake": false, + "locked": { + "lastModified": 1642052814, + "narHash": "sha256-jwj/gh/A/PXhO6yVESV27k4yx9I8Id8fTa3m4ofPnP0=", + "owner": "input-output-hk", + "repo": "ekg-forward", + "rev": "297cd9db5074339a2fb2e5ae7d0780debb670c63", + "type": "github" + }, + "original": { + "owner": "input-output-hk", + "repo": "ekg-forward", + "rev": "297cd9db5074339a2fb2e5ae7d0780debb670c63", + "type": "github" + } + }, + "ekg-forward_2": { + "flake": false, + "locked": { + "lastModified": 1642052814, + "narHash": "sha256-jwj/gh/A/PXhO6yVESV27k4yx9I8Id8fTa3m4ofPnP0=", + "owner": "input-output-hk", + "repo": "ekg-forward", + "rev": "297cd9db5074339a2fb2e5ae7d0780debb670c63", + "type": "github" + }, + "original": { + "owner": "input-output-hk", + "repo": "ekg-forward", + "rev": "297cd9db5074339a2fb2e5ae7d0780debb670c63", + "type": "github" + } + }, + "ekg-json": { + "flake": false, + "locked": { + "lastModified": 1642583945, + "narHash": "sha256-VT8Ur585TCn03P2TVi6t92v2Z6tl8vKijICjse6ocv8=", + "owner": "vshabanov", + "repo": "ekg-json", + "rev": "00ebe7211c981686e65730b7144fbf5350462608", + "type": "github" + }, + "original": { + "owner": "vshabanov", + "repo": "ekg-json", + "rev": "00ebe7211c981686e65730b7144fbf5350462608", + "type": "github" + } + }, + "ekg-json_2": { + "flake": false, + "locked": { + "lastModified": 1642583945, + "narHash": "sha256-VT8Ur585TCn03P2TVi6t92v2Z6tl8vKijICjse6ocv8=", + "owner": "vshabanov", + "repo": "ekg-json", + "rev": "00ebe7211c981686e65730b7144fbf5350462608", + "type": "github" + }, + "original": { + "owner": "vshabanov", + "repo": "ekg-json", + "rev": "00ebe7211c981686e65730b7144fbf5350462608", + "type": "github" + } + }, + "flake-compat": { + "flake": false, + "locked": { + "lastModified": 1650374568, + "narHash": "sha256-Z+s0J8/r907g149rllvwhb4pKi8Wam5ij0st8PwAh+E=", + "owner": "edolstra", + "repo": "flake-compat", + "rev": "b4a34015c698c7793d592d66adbab377907a2be8", + "type": "github" + }, + "original": { + "owner": "edolstra", + "repo": "flake-compat", + "type": "github" + } + }, + "flake-compat_2": { + "flake": false, + "locked": { + "lastModified": 1641205782, + "narHash": "sha256-4jY7RCWUoZ9cKD8co0/4tFARpWB+57+r1bLLvXNJliY=", + "owner": "edolstra", + "repo": "flake-compat", + "rev": "b7547d3eed6f32d06102ead8991ec52ab0a4f1a7", + "type": "github" + }, + "original": { + "owner": "edolstra", + "repo": "flake-compat", + "type": "github" + } + }, + "flake-compat_3": { + "flake": false, + "locked": { + "lastModified": 1650374568, + "narHash": "sha256-Z+s0J8/r907g149rllvwhb4pKi8Wam5ij0st8PwAh+E=", + "owner": "edolstra", + "repo": "flake-compat", + "rev": "b4a34015c698c7793d592d66adbab377907a2be8", + "type": "github" + }, + "original": { + "owner": "edolstra", + "repo": "flake-compat", + "type": "github" + } + }, + "flake-compat_4": { + "flake": false, + "locked": { + "lastModified": 1650374568, + "narHash": "sha256-Z+s0J8/r907g149rllvwhb4pKi8Wam5ij0st8PwAh+E=", + "owner": "edolstra", + "repo": "flake-compat", + "rev": "b4a34015c698c7793d592d66adbab377907a2be8", + "type": "github" + }, + "original": { + "owner": "edolstra", + "repo": "flake-compat", + "type": "github" + } + }, + "flake-compat_5": { + "flake": false, + "locked": { + "lastModified": 1650374568, + "narHash": "sha256-Z+s0J8/r907g149rllvwhb4pKi8Wam5ij0st8PwAh+E=", + "owner": "edolstra", + "repo": "flake-compat", + "rev": "b4a34015c698c7793d592d66adbab377907a2be8", + "type": "github" + }, + "original": { + "owner": "edolstra", + "repo": "flake-compat", + "type": "github" + } + }, + "flake-compat_6": { + "flake": false, + "locked": { + "lastModified": 1650374568, + "narHash": "sha256-Z+s0J8/r907g149rllvwhb4pKi8Wam5ij0st8PwAh+E=", + "owner": "edolstra", + "repo": "flake-compat", + "rev": "b4a34015c698c7793d592d66adbab377907a2be8", + "type": "github" + }, + "original": { + "owner": "edolstra", + "repo": "flake-compat", + "type": "github" + } + }, + "flake-utils": { + "locked": { + "lastModified": 1644229661, + "narHash": "sha256-1YdnJAsNy69bpcjuoKdOYQX0YxZBiCYZo4Twxerqv7k=", + "owner": "numtide", + "repo": "flake-utils", + "rev": "3cecb5b042f7f209c56ffd8371b2711a290ec797", + "type": "github" + }, + "original": { + "owner": "numtide", + "repo": "flake-utils", + "type": "github" + } + }, + "flat": { + "flake": false, + "locked": { + "lastModified": 1628771504, + "narHash": "sha256-lRFND+ZnZvAph6ZYkr9wl9VAx41pb3uSFP8Wc7idP9M=", + "owner": "input-output-hk", + "repo": "flat", + "rev": "ee59880f47ab835dbd73bea0847dab7869fc20d8", + "type": "github" + }, + "original": { + "owner": "input-output-hk", + "repo": "flat", + "rev": "ee59880f47ab835dbd73bea0847dab7869fc20d8", + "type": "github" + } + }, + "flat_2": { + "flake": false, + "locked": { + "lastModified": 1628771504, + "narHash": "sha256-lRFND+ZnZvAph6ZYkr9wl9VAx41pb3uSFP8Wc7idP9M=", + "owner": "input-output-hk", + "repo": "flat", + "rev": "ee59880f47ab835dbd73bea0847dab7869fc20d8", + "type": "github" + }, + "original": { + "owner": "input-output-hk", + "repo": "flat", + "rev": "ee59880f47ab835dbd73bea0847dab7869fc20d8", + "type": "github" + } + }, + "flat_3": { + "flake": false, + "locked": { + "lastModified": 1628771504, + "narHash": "sha256-lRFND+ZnZvAph6ZYkr9wl9VAx41pb3uSFP8Wc7idP9M=", + "owner": "Quid2", + "repo": "flat", + "rev": "ee59880f47ab835dbd73bea0847dab7869fc20d8", + "type": "github" + }, + "original": { + "owner": "Quid2", + "repo": "flat", + "rev": "ee59880f47ab835dbd73bea0847dab7869fc20d8", + "type": "github" + } + }, + "ghc-8.6.5-iohk": { + "flake": false, + "locked": { + "lastModified": 1600920045, + "narHash": "sha256-DO6kxJz248djebZLpSzTGD6s8WRpNI9BTwUeOf5RwY8=", + "owner": "input-output-hk", + "repo": "ghc", + "rev": "95713a6ecce4551240da7c96b6176f980af75cae", + "type": "github" + }, + "original": { + "owner": "input-output-hk", + "ref": "release/8.6.5-iohk", + "repo": "ghc", + "type": "github" + } + }, + "goblins": { + "flake": false, + "locked": { + "lastModified": 1598362523, + "narHash": "sha256-z9ut0y6umDIjJIRjz9KSvKgotuw06/S8QDwOtVdGiJ0=", + "owner": "input-output-hk", + "repo": "goblins", + "rev": "cde90a2b27f79187ca8310b6549331e59595e7ba", + "type": "github" + }, + "original": { + "owner": "input-output-hk", + "repo": "goblins", + "rev": "cde90a2b27f79187ca8310b6549331e59595e7ba", + "type": "github" + } + }, + "goblins_2": { + "flake": false, + "locked": { + "lastModified": 1598362523, + "narHash": "sha256-z9ut0y6umDIjJIRjz9KSvKgotuw06/S8QDwOtVdGiJ0=", + "owner": "input-output-hk", + "repo": "goblins", + "rev": "cde90a2b27f79187ca8310b6549331e59595e7ba", + "type": "github" + }, + "original": { + "owner": "input-output-hk", + "repo": "goblins", + "rev": "cde90a2b27f79187ca8310b6549331e59595e7ba", + "type": "github" + } + }, + "goblins_3": { + "flake": false, + "locked": { + "lastModified": 1598362523, + "narHash": "sha256-z9ut0y6umDIjJIRjz9KSvKgotuw06/S8QDwOtVdGiJ0=", + "owner": "input-output-hk", + "repo": "goblins", + "rev": "cde90a2b27f79187ca8310b6549331e59595e7ba", + "type": "github" + }, + "original": { + "owner": "input-output-hk", + "repo": "goblins", + "rev": "cde90a2b27f79187ca8310b6549331e59595e7ba", + "type": "github" + } + }, + "hackage": { + "flake": false, + "locked": { + "lastModified": 1653441966, + "narHash": "sha256-aJFK0wDzoOrtb7ucZzKh5J+S2pThpwNCofl74s1olXU=", + "owner": "input-output-hk", + "repo": "hackage.nix", + "rev": "f7fe6ef8de52c43a9efa6fd4ac4902e5957dc573", + "type": "github" + }, + "original": { + "owner": "input-output-hk", + "repo": "hackage.nix", + "type": "github" + } + }, + "haskell-nix": { + "inputs": { + "HTTP": "HTTP", + "cabal-32": "cabal-32", + "cabal-34": "cabal-34", + "cabal-36": "cabal-36", + "cardano-shell": "cardano-shell", + "flake-utils": "flake-utils", + "ghc-8.6.5-iohk": "ghc-8.6.5-iohk", + "hackage": "hackage", + "hpc-coveralls": "hpc-coveralls", + "hydra": "hydra", + "nix-tools": "nix-tools", + "nixpkgs": [ + "ctl", + "plutip", + "bot-plutus-interface", + "haskell-nix", + "nixpkgs-unstable" + ], + "nixpkgs-2003": "nixpkgs-2003", + "nixpkgs-2105": "nixpkgs-2105", + "nixpkgs-2111": "nixpkgs-2111", + "nixpkgs-unstable": "nixpkgs-unstable", + "old-ghc-nix": "old-ghc-nix", + "stackage": "stackage" + }, + "locked": { + "lastModified": 1653486569, + "narHash": "sha256-342b0LPX6kaBuEX8KZV40FwCCFre1lCtjdTQIDEt9kw=", + "owner": "mlabs-haskell", + "repo": "haskell.nix", + "rev": "220f8a9cd166e726aea62843bdafa7ecded3375c", + "type": "github" + }, + "original": { + "owner": "mlabs-haskell", + "repo": "haskell.nix", + "type": "github" + } + }, + "hedgehog-extras": { + "flake": false, + "locked": { + "lastModified": 1647260073, + "narHash": "sha256-TR9i1J3HUYz3QnFQbfJPr/kGDahxZPojDsorYtRZeGU=", + "owner": "input-output-hk", + "repo": "hedgehog-extras", + "rev": "967d79533c21e33387d0227a5f6cc185203fe658", + "type": "github" + }, + "original": { + "owner": "input-output-hk", + "repo": "hedgehog-extras", + "rev": "967d79533c21e33387d0227a5f6cc185203fe658", + "type": "github" + } + }, + "hedgehog-extras_2": { + "flake": false, + "locked": { + "lastModified": 1647260073, + "narHash": "sha256-TR9i1J3HUYz3QnFQbfJPr/kGDahxZPojDsorYtRZeGU=", + "owner": "input-output-hk", + "repo": "hedgehog-extras", + "rev": "967d79533c21e33387d0227a5f6cc185203fe658", + "type": "github" + }, + "original": { + "owner": "input-output-hk", + "repo": "hedgehog-extras", + "rev": "967d79533c21e33387d0227a5f6cc185203fe658", + "type": "github" + } + }, + "hjsonpointer": { + "flake": false, + "locked": { + "lastModified": 1654184599, + "narHash": "sha256-y1UCtaVI5Zsb8MeOQA8XbSX3p4/JoroRTG9RGl0I7DY=", + "owner": "KtorZ", + "repo": "hjsonpointer", + "rev": "879f0e74d55eef76ceaec8f60ed07657ab84bad7", + "type": "github" + }, + "original": { + "owner": "KtorZ", + "repo": "hjsonpointer", + "rev": "879f0e74d55eef76ceaec8f60ed07657ab84bad7", + "type": "github" + } + }, + "hjsonschema": { + "flake": false, + "locked": { + "lastModified": 1654186606, + "narHash": "sha256-1UG+rP7Z/kxiqj2qcx70688u1P23RzopAim+MClo6PA=", + "owner": "KtorZ", + "repo": "hjsonschema", + "rev": "35e0b05c3867463363e67f00a5092cd39fa33313", + "type": "github" + }, + "original": { + "owner": "KtorZ", + "repo": "hjsonschema", + "rev": "35e0b05c3867463363e67f00a5092cd39fa33313", + "type": "github" + } + }, + "hpc-coveralls": { + "flake": false, + "locked": { + "lastModified": 1607498076, + "narHash": "sha256-8uqsEtivphgZWYeUo5RDUhp6bO9j2vaaProQxHBltQk=", + "owner": "sevanspowell", + "repo": "hpc-coveralls", + "rev": "14df0f7d229f4cd2e79f8eabb1a740097fdfa430", + "type": "github" + }, + "original": { + "owner": "sevanspowell", + "repo": "hpc-coveralls", + "type": "github" + } + }, + "hw-aeson": { + "flake": false, + "locked": { + "lastModified": 1649341404, + "narHash": "sha256-xO4/zPMBmZtBXFwHF8p3nw4TilrJHxH54mfg9CRnuO8=", + "owner": "haskell-works", + "repo": "hw-aeson", + "rev": "d99d2f3e39a287607418ae605b132a3deb2b753f", + "type": "github" + }, + "original": { + "owner": "haskell-works", + "repo": "hw-aeson", + "rev": "d99d2f3e39a287607418ae605b132a3deb2b753f", + "type": "github" + } + }, + "hydra": { + "inputs": { + "nix": "nix", + "nixpkgs": [ + "ctl", + "plutip", + "bot-plutus-interface", + "haskell-nix", + "hydra", + "nix", + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1646878427, + "narHash": "sha256-KtbrofMtN8GlM7D+n90kixr7QpSlVmdN+vK5CA/aRzc=", + "owner": "NixOS", + "repo": "hydra", + "rev": "28b682b85b7efc5cf7974065792a1f22203a5927", + "type": "github" + }, + "original": { + "id": "hydra", + "type": "indirect" + } + }, + "hysterical-screams": { + "flake": false, + "locked": { + "lastModified": 1654007733, + "narHash": "sha256-d4N3rUzg45BUs5Lx/kK7vXYsLMNoO15dlzo7t8lGIXA=", + "owner": "raduom", + "repo": "hysterical-screams", + "rev": "4c523469e9efd3f0d10d17da3304923b7b0e0674", + "type": "github" + }, + "original": { + "owner": "raduom", + "repo": "hysterical-screams", + "rev": "4c523469e9efd3f0d10d17da3304923b7b0e0674", + "type": "github" + } + }, + "io-sim": { + "flake": false, + "locked": { + "lastModified": 1653046584, + "narHash": "sha256-vFE67shdZScks67KezdKToLuk6k6wwyLFzshClO7Ym0=", + "owner": "input-output-hk", + "repo": "io-sim", + "rev": "f4183f274d88d0ad15817c7052df3a6a8b40e6dc", + "type": "github" + }, + "original": { + "owner": "input-output-hk", + "repo": "io-sim", + "rev": "f4183f274d88d0ad15817c7052df3a6a8b40e6dc", + "type": "github" + } + }, + "io-sim_2": { + "flake": false, + "locked": { + "lastModified": 1654253725, + "narHash": "sha256-TviSvCBEYtlKEo9qJmE8pCE25nMjDi8HeIAFniunaM8=", + "owner": "input-output-hk", + "repo": "io-sim", + "rev": "57e888b1894829056cb00b7b5785fdf6a74c3271", + "type": "github" + }, + "original": { + "owner": "input-output-hk", + "repo": "io-sim", + "rev": "57e888b1894829056cb00b7b5785fdf6a74c3271", + "type": "github" + } + }, + "iohk-monitoring-framework": { + "flake": false, + "locked": { + "lastModified": 1618904084, + "narHash": "sha256-v0L0pcyO2rP7/HCoGwFgHEMUOPBGwaRV0r+/JOhtKAk=", + "owner": "input-output-hk", + "repo": "iohk-monitoring-framework", + "rev": "808724ff8a19a33d0ed06f9ef59fbd900b08553c", + "type": "github" + }, + "original": { + "owner": "input-output-hk", + "repo": "iohk-monitoring-framework", + "rev": "808724ff8a19a33d0ed06f9ef59fbd900b08553c", + "type": "github" + } + }, + "iohk-monitoring-framework_2": { + "flake": false, + "locked": { + "lastModified": 1653619339, + "narHash": "sha256-0ia5UflYEmBYepj2gkJy9msknklI0UPtUavMEGwk3Wg=", + "owner": "input-output-hk", + "repo": "iohk-monitoring-framework", + "rev": "066f7002aac5a0efc20e49643fea45454f226caa", + "type": "github" + }, + "original": { + "owner": "input-output-hk", + "repo": "iohk-monitoring-framework", + "rev": "066f7002aac5a0efc20e49643fea45454f226caa", + "type": "github" + } + }, + "iohk-monitoring-framework_3": { + "flake": false, + "locked": { + "lastModified": 1653619339, + "narHash": "sha256-0ia5UflYEmBYepj2gkJy9msknklI0UPtUavMEGwk3Wg=", + "owner": "input-output-hk", + "repo": "iohk-monitoring-framework", + "rev": "066f7002aac5a0efc20e49643fea45454f226caa", + "type": "github" + }, + "original": { + "owner": "input-output-hk", + "repo": "iohk-monitoring-framework", + "rev": "066f7002aac5a0efc20e49643fea45454f226caa", + "type": "github" + } + }, + "iohk-nix": { + "inputs": { + "nixpkgs": "nixpkgs" + }, + "locked": { + "lastModified": 1658222743, + "narHash": "sha256-yFH01psqx30y5Ws4dBElLkxYpIxxqZx4G+jCVhsXpnA=", + "owner": "input-output-hk", + "repo": "iohk-nix", + "rev": "9a604d01bd4420ab7f396f14d1947fbe2ce7db8b", + "type": "github" + }, + "original": { + "owner": "input-output-hk", + "repo": "iohk-nix", + "type": "github" + } + }, + "iohk-nix_2": { + "inputs": { + "nixpkgs": "nixpkgs_2" + }, + "locked": { + "lastModified": 1649070135, + "narHash": "sha256-UFKqcOSdPWk3TYUCPHF22p1zf7aXQpCmmgf7UMg7fWA=", + "owner": "input-output-hk", + "repo": "iohk-nix", + "rev": "cecab9c71d1064f05f1615eead56ac0b9196bc20", + "type": "github" + }, + "original": { + "owner": "input-output-hk", + "repo": "iohk-nix", + "rev": "cecab9c71d1064f05f1615eead56ac0b9196bc20", + "type": "github" + } + }, + "iohk-nix_3": { + "flake": false, + "locked": { + "lastModified": 1653579289, + "narHash": "sha256-wveDdPsgB/3nAGAdFaxrcgLEpdi0aJ5kEVNtI+YqVfo=", + "owner": "input-output-hk", + "repo": "iohk-nix", + "rev": "edb2d2df2ebe42bbdf03a0711115cf6213c9d366", + "type": "github" + }, + "original": { + "owner": "input-output-hk", + "repo": "iohk-nix", + "type": "github" + } + }, + "lowdown-src": { + "flake": false, + "locked": { + "lastModified": 1633514407, + "narHash": "sha256-Dw32tiMjdK9t3ETl5fzGrutQTzh2rufgZV4A/BbxuD4=", + "owner": "kristapsdz", + "repo": "lowdown", + "rev": "d2c2b44ff6c27b936ec27358a2653caaef8f73b8", + "type": "github" + }, + "original": { + "owner": "kristapsdz", + "repo": "lowdown", + "type": "github" + } + }, + "nix": { + "inputs": { + "lowdown-src": "lowdown-src", + "nixpkgs": "nixpkgs_4", + "nixpkgs-regression": "nixpkgs-regression" + }, + "locked": { + "lastModified": 1643066034, + "narHash": "sha256-xEPeMcNJVOeZtoN+d+aRwolpW8mFSEQx76HTRdlhPhg=", + "owner": "NixOS", + "repo": "nix", + "rev": "a1cd7e58606a41fcf62bf8637804cf8306f17f62", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "2.6.0", + "repo": "nix", + "type": "github" + } + }, + "nix-tools": { + "flake": false, + "locked": { + "lastModified": 1649424170, + "narHash": "sha256-XgKXWispvv5RCvZzPb+p7e6Hy3LMuRjafKMl7kXzxGw=", + "owner": "input-output-hk", + "repo": "nix-tools", + "rev": "e109c94016e3b6e0db7ed413c793e2d4bdb24aa7", + "type": "github" + }, + "original": { + "owner": "input-output-hk", + "repo": "nix-tools", + "type": "github" + } + }, + "nixpkgs": { + "locked": { + "lastModified": 0, + "narHash": "sha256-cowVkScfUPlbBXUp08MeVk/wgm9E1zp1uC+9no2hZYw=", + "path": "/nix/store/0ki4clglxkgjw6znhpc0yg89pmy54xql-source", + "type": "path" + }, + "original": { + "id": "nixpkgs", + "type": "indirect" + } + }, + "nixpkgs-2003": { + "locked": { + "lastModified": 1620055814, + "narHash": "sha256-8LEHoYSJiL901bTMVatq+rf8y7QtWuZhwwpKE2fyaRY=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "1db42b7fe3878f3f5f7a4f2dc210772fd080e205", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixpkgs-20.03-darwin", + "repo": "nixpkgs", + "type": "github" + } + }, + "nixpkgs-2105": { + "locked": { + "lastModified": 1645296114, + "narHash": "sha256-y53N7TyIkXsjMpOG7RhvqJFGDacLs9HlyHeSTBioqYU=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "530a53dcbc9437363471167a5e4762c5fcfa34a1", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixpkgs-21.05-darwin", + "repo": "nixpkgs", + "type": "github" + } + }, + "nixpkgs-2111": { + "locked": { + "lastModified": 1648744337, + "narHash": "sha256-bYe1dFJAXovjqiaPKrmAbSBEK5KUkgwVaZcTbSoJ7hg=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "0a58eebd8ec65ffdef2ce9562784123a73922052", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixpkgs-21.11-darwin", + "repo": "nixpkgs", + "type": "github" + } + }, + "nixpkgs-regression": { + "locked": { + "lastModified": 1643052045, + "narHash": "sha256-uGJ0VXIhWKGXxkeNnq4TvV3CIOkUJ3PAoLZ3HMzNVMw=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "215d4d0fd80ca5163643b03a33fde804a29cc1e2", + "type": "github" + }, + "original": { + "id": "nixpkgs", + "rev": "215d4d0fd80ca5163643b03a33fde804a29cc1e2", + "type": "indirect" + } + }, + "nixpkgs-unstable": { + "locked": { + "lastModified": 1648219316, + "narHash": "sha256-Ctij+dOi0ZZIfX5eMhgwugfvB+WZSrvVNAyAuANOsnQ=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "30d3d79b7d3607d56546dd2a6b49e156ba0ec634", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixpkgs-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, + "nixpkgs_2": { + "locked": { + "lastModified": 1647122627, + "narHash": "sha256-w4hGsXYyMgJAQRSBxh7O6AAsawJSbudCxfQXhDRhwPQ=", + "path": "/nix/store/s6wigis38dnikj5y92jrrj7ywc38b78g-source", + "rev": "0f85665118d850aae5164d385d24783d0b16cf1b", + "type": "path" + }, + "original": { + "id": "nixpkgs", + "type": "indirect" + } + }, + "nixpkgs_3": { + "locked": { + "lastModified": 1634172192, + "narHash": "sha256-FBF4U/T+bMg4sEyT/zkgasvVquGzgdAf4y8uCosKMmo=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "2cf9db0e3d45b9d00f16f2836cb1297bcadc475e", + "type": "github" + }, + "original": { + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "2cf9db0e3d45b9d00f16f2836cb1297bcadc475e", + "type": "github" + } + }, + "nixpkgs_4": { + "locked": { + "lastModified": 1632864508, + "narHash": "sha256-d127FIvGR41XbVRDPVvozUPQ/uRHbHwvfyKHwEt5xFM=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "82891b5e2c2359d7e58d08849e4c89511ab94234", + "type": "github" + }, + "original": { + "id": "nixpkgs", + "ref": "nixos-21.05-small", + "type": "indirect" + } + }, + "ogmios": { + "inputs": { + "Win32-network": "Win32-network_2", + "cardano-base": "cardano-base_2", + "cardano-crypto": "cardano-crypto_2", + "cardano-ledger": "cardano-ledger_2", + "cardano-node": "cardano-node_2", + "cardano-prelude": "cardano-prelude_2", + "ekg-json": "ekg-json", + "flake-compat": "flake-compat_2", + "flat": "flat_2", + "goblins": "goblins_2", + "haskell-nix": [ + "ctl", + "haskell-nix" + ], + "hedgehog-extras": "hedgehog-extras", + "hjsonpointer": "hjsonpointer", + "hjsonschema": "hjsonschema", + "io-sim": "io-sim", + "iohk-monitoring-framework": "iohk-monitoring-framework_2", + "iohk-nix": "iohk-nix_2", + "nixpkgs": [ + "ctl", + "nixpkgs" + ], + "optparse-applicative": "optparse-applicative", + "ouroboros-network": "ouroboros-network", + "plutus": "plutus", + "typed-protocols": "typed-protocols", + "wai-routes": "wai-routes" + }, + "locked": { + "lastModified": 1656650330, + "narHash": "sha256-Rl5xNP3LVtuOzXXSsdAWNB3EXGRPsFPMvBO0TDUvSJE=", + "owner": "mlabs-haskell", + "repo": "ogmios", + "rev": "e406801eaeb32b28cd84357596ca1512bff27741", + "type": "github" + }, + "original": { + "owner": "mlabs-haskell", + "repo": "ogmios", + "rev": "e406801eaeb32b28cd84357596ca1512bff27741", + "type": "github" + } + }, + "ogmios-datum-cache": { + "inputs": { + "flake-compat": "flake-compat_3", + "nixpkgs": "nixpkgs_3", + "unstable_nixpkgs": "unstable_nixpkgs" + }, + "locked": { + "lastModified": 1658378474, + "narHash": "sha256-BGZNLo7ABgg6iFY84nYua6jgl8EqhM8bI2bLnLB+z8Q=", + "owner": "mlabs-haskell", + "repo": "ogmios-datum-cache", + "rev": "1e618a1949667ea3eb972fbaccf34414e8d17e89", + "type": "github" + }, + "original": { + "owner": "mlabs-haskell", + "repo": "ogmios-datum-cache", + "rev": "1e618a1949667ea3eb972fbaccf34414e8d17e89", + "type": "github" + } + }, + "old-ghc-nix": { + "flake": false, + "locked": { + "lastModified": 1631092763, + "narHash": "sha256-sIKgO+z7tj4lw3u6oBZxqIhDrzSkvpHtv0Kki+lh9Fg=", + "owner": "angerman", + "repo": "old-ghc-nix", + "rev": "af48a7a7353e418119b6dfe3cd1463a657f342b8", + "type": "github" + }, + "original": { + "owner": "angerman", + "ref": "master", + "repo": "old-ghc-nix", + "type": "github" + } + }, + "optparse-applicative": { + "flake": false, + "locked": { + "lastModified": 1628901899, + "narHash": "sha256-uQx+SEYsCH7JcG3xAT0eJck9yq3y0cvx49bvItLLer8=", + "owner": "input-output-hk", + "repo": "optparse-applicative", + "rev": "7497a29cb998721a9068d5725d49461f2bba0e7a", + "type": "github" + }, + "original": { + "owner": "input-output-hk", + "repo": "optparse-applicative", + "rev": "7497a29cb998721a9068d5725d49461f2bba0e7a", + "type": "github" + } + }, + "optparse-applicative_2": { + "flake": false, + "locked": { + "lastModified": 1628901899, + "narHash": "sha256-uQx+SEYsCH7JcG3xAT0eJck9yq3y0cvx49bvItLLer8=", + "owner": "input-output-hk", + "repo": "optparse-applicative", + "rev": "7497a29cb998721a9068d5725d49461f2bba0e7a", + "type": "github" + }, + "original": { + "owner": "input-output-hk", + "repo": "optparse-applicative", + "rev": "7497a29cb998721a9068d5725d49461f2bba0e7a", + "type": "github" + } + }, + "optparse-applicative_3": { + "flake": false, + "locked": { + "lastModified": 1628901899, + "narHash": "sha256-uQx+SEYsCH7JcG3xAT0eJck9yq3y0cvx49bvItLLer8=", + "owner": "input-output-hk", + "repo": "optparse-applicative", + "rev": "7497a29cb998721a9068d5725d49461f2bba0e7a", + "type": "github" + }, + "original": { + "owner": "input-output-hk", + "repo": "optparse-applicative", + "rev": "7497a29cb998721a9068d5725d49461f2bba0e7a", + "type": "github" + } + }, + "ouroboros-network": { + "flake": false, + "locked": { + "lastModified": 1654820431, + "narHash": "sha256-bmLD5sFsiny/eRv6MHrqGvo6I4QG9pO0psiHWGFZqro=", + "owner": "input-output-hk", + "repo": "ouroboros-network", + "rev": "a65c29b6a85e90d430c7f58d362b7eb097fd4949", + "type": "github" + }, + "original": { + "owner": "input-output-hk", + "repo": "ouroboros-network", + "rev": "a65c29b6a85e90d430c7f58d362b7eb097fd4949", + "type": "github" + } + }, + "ouroboros-network_2": { + "flake": false, + "locked": { + "lastModified": 1639752881, + "narHash": "sha256-fZ6FfG2z6HWDxjIHycLPSQHoYtfUmWZOX7lfAUE+s6M=", + "owner": "input-output-hk", + "repo": "ouroboros-network", + "rev": "d2d219a86cda42787325bb8c20539a75c2667132", + "type": "github" + }, + "original": { + "owner": "input-output-hk", + "repo": "ouroboros-network", + "rev": "d2d219a86cda42787325bb8c20539a75c2667132", + "type": "github" + } + }, + "ouroboros-network_3": { + "flake": false, + "locked": { + "lastModified": 1654820431, + "narHash": "sha256-bmLD5sFsiny/eRv6MHrqGvo6I4QG9pO0psiHWGFZqro=", + "owner": "input-output-hk", + "repo": "ouroboros-network", + "rev": "a65c29b6a85e90d430c7f58d362b7eb097fd4949", + "type": "github" + }, + "original": { + "owner": "input-output-hk", + "repo": "ouroboros-network", + "rev": "a65c29b6a85e90d430c7f58d362b7eb097fd4949", + "type": "github" + } + }, + "plutip": { + "inputs": { + "bot-plutus-interface": "bot-plutus-interface", + "flake-compat": "flake-compat_5", + "haskell-nix": [ + "ctl", + "plutip", + "bot-plutus-interface", + "haskell-nix" + ], + "iohk-nix": [ + "ctl", + "plutip", + "bot-plutus-interface", + "iohk-nix" + ], + "nixpkgs": [ + "ctl", + "plutip", + "bot-plutus-interface", + "haskell-nix", + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1658235706, + "narHash": "sha256-T5E4Qz/6ZlxVVJer6j5xFKvY4XGJPMbZsVxac8RbZ9w=", + "owner": "mlabs-haskell", + "repo": "plutip", + "rev": "d24b98162bcbcbfb4ca403ee62fdb890f2059f47", + "type": "github" + }, + "original": { + "owner": "mlabs-haskell", + "repo": "plutip", + "rev": "d24b98162bcbcbfb4ca403ee62fdb890f2059f47", + "type": "github" + } + }, + "plutus": { + "flake": false, + "locked": { + "lastModified": 1655404007, + "narHash": "sha256-8ZCD/f321fFs8k+FBfxnpYlm1+C+rKM8Io9K0CDCEqA=", + "owner": "input-output-hk", + "repo": "plutus", + "rev": "f680ac6979e069fcc013e4389ee607ff5fa6672f", + "type": "github" + }, + "original": { + "owner": "input-output-hk", + "repo": "plutus", + "rev": "f680ac6979e069fcc013e4389ee607ff5fa6672f", + "type": "github" + } + }, + "plutus-apps": { + "flake": false, + "locked": { + "lastModified": 1658170029, + "narHash": "sha256-/G3CrE2aXrhytDGOqhifmIT31gJcvI3FjuntztOi8DY=", + "owner": "gege251", + "repo": "plutus-apps", + "rev": "62342808fa7422ebea3233a7e031d3aa00c04672", + "type": "github" + }, + "original": { + "owner": "gege251", + "repo": "plutus-apps", + "rev": "62342808fa7422ebea3233a7e031d3aa00c04672", + "type": "github" + } + }, + "plutus_2": { + "flake": false, + "locked": { + "lastModified": 1656585904, + "narHash": "sha256-ATwDR5LX2RN9YfoPhTxV7REvFoJnM4x/CN9XZVZlalg=", + "owner": "input-output-hk", + "repo": "plutus", + "rev": "69ab98c384703172f898eb5bcad1078ded521426", + "type": "github" + }, + "original": { + "owner": "input-output-hk", + "repo": "plutus", + "rev": "69ab98c384703172f898eb5bcad1078ded521426", + "type": "github" + } + }, + "plutus_3": { + "flake": false, + "locked": { + "lastModified": 1632818067, + "narHash": "sha256-jiqrzS519eoHg9NqTr4UZOVme3uIACL17OCiDMn0LMo=", + "owner": "input-output-hk", + "repo": "plutus", + "rev": "1efbb276ef1a10ca6961d0fd32e6141e9798bd11", + "type": "github" + }, + "original": { + "owner": "input-output-hk", + "repo": "plutus", + "rev": "1efbb276ef1a10ca6961d0fd32e6141e9798bd11", + "type": "github" + } + }, + "purescript-bridge": { + "flake": false, + "locked": { + "lastModified": 1642802224, + "narHash": "sha256-/SbnmXrB9Y2rrPd6E79Iu5RDaKAKozIl685HQ4XdQTU=", + "owner": "input-output-hk", + "repo": "purescript-bridge", + "rev": "47a1f11825a0f9445e0f98792f79172efef66c00", + "type": "github" + }, + "original": { + "owner": "input-output-hk", + "repo": "purescript-bridge", + "rev": "47a1f11825a0f9445e0f98792f79172efef66c00", + "type": "github" + } + }, + "purescript-bridge_2": { + "flake": false, + "locked": { + "lastModified": 1612544328, + "narHash": "sha256-K7dg3rEnu/9HP1fqjV1iCl6pwA5UYwIEGzaaEPYmRo4=", + "owner": "shmish111", + "repo": "purescript-bridge", + "rev": "6a92d7853ea514be8b70bab5e72077bf5a510596", + "type": "github" + }, + "original": { + "owner": "shmish111", + "repo": "purescript-bridge", + "rev": "6a92d7853ea514be8b70bab5e72077bf5a510596", + "type": "github" + } + }, + "quickcheck-dynamic": { + "flake": false, + "locked": { + "lastModified": 1656927450, + "narHash": "sha256-TioJQASNrQX6B3n2Cv43X2olyT67//CFQqcpvNW7N60=", + "owner": "input-output-hk", + "repo": "quickcheck-dynamic", + "rev": "c272906361471d684440f76c297e29ab760f6a1e", + "type": "github" + }, + "original": { + "owner": "input-output-hk", + "repo": "quickcheck-dynamic", + "rev": "c272906361471d684440f76c297e29ab760f6a1e", + "type": "github" + } + }, + "root": { + "inputs": { + "ctl": "ctl", + "flake-compat": "flake-compat_6", + "nixpkgs": [ + "ctl", + "nixpkgs" + ] + } + }, + "servant-purescript": { + "flake": false, + "locked": { + "lastModified": 1642798070, + "narHash": "sha256-DH9ISydu5gxvN4xBuoXVv1OhYCaqGOtzWlACdJ0H64I=", + "owner": "input-output-hk", + "repo": "servant-purescript", + "rev": "44e7cacf109f84984cd99cd3faf185d161826963", + "type": "github" + }, + "original": { + "owner": "input-output-hk", + "repo": "servant-purescript", + "rev": "44e7cacf109f84984cd99cd3faf185d161826963", + "type": "github" + } + }, + "servant-purescript_2": { + "flake": false, + "locked": { + "lastModified": 1612956215, + "narHash": "sha256-aYOiBk578JTU9qhc9/B2BdmzDs8vb32P2sbwVWLt3YY=", + "owner": "shmish111", + "repo": "servant-purescript", + "rev": "a76104490499aa72d40c2790d10e9383e0dbde63", + "type": "github" + }, + "original": { + "owner": "shmish111", + "repo": "servant-purescript", + "rev": "a76104490499aa72d40c2790d10e9383e0dbde63", + "type": "github" + } + }, + "stackage": { + "flake": false, + "locked": { + "lastModified": 1653355076, + "narHash": "sha256-mQdOgAyFkLUJBPrVDZmZQ2JRtgHKOQkil//SDdcjP1U=", + "owner": "input-output-hk", + "repo": "stackage.nix", + "rev": "71b16ca68d6acd639121db43238896357fe53f54", + "type": "github" + }, + "original": { + "owner": "input-output-hk", + "repo": "stackage.nix", + "type": "github" + } + }, + "typed-protocols": { + "flake": false, + "locked": { + "lastModified": 1653046676, + "narHash": "sha256-5Wof5yTKb12EPY6B8LfapX18xNZZpF+rvhnQ88U6KdM=", + "owner": "input-output-hk", + "repo": "typed-protocols", + "rev": "181601bc3d9e9d21a671ce01e0b481348b3ca104", + "type": "github" + }, + "original": { + "owner": "input-output-hk", + "repo": "typed-protocols", + "rev": "181601bc3d9e9d21a671ce01e0b481348b3ca104", + "type": "github" + } + }, + "typed-protocols_2": { + "flake": false, + "locked": { + "lastModified": 1653046676, + "narHash": "sha256-5Wof5yTKb12EPY6B8LfapX18xNZZpF+rvhnQ88U6KdM=", + "owner": "input-output-hk", + "repo": "typed-protocols", + "rev": "181601bc3d9e9d21a671ce01e0b481348b3ca104", + "type": "github" + }, + "original": { + "owner": "input-output-hk", + "repo": "typed-protocols", + "rev": "181601bc3d9e9d21a671ce01e0b481348b3ca104", + "type": "github" + } + }, + "unstable_nixpkgs": { + "locked": { + "lastModified": 1653307806, + "narHash": "sha256-VPej3GE4IBMwYnXRfbiVqMWKa32+ysuvbHRkQXD0gTw=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "9d7aff488a8f9429d9e6cd82c10dffbf21907fb1", + "type": "github" + }, + "original": { + "owner": "NixOS", + "repo": "nixpkgs", + "type": "github" + } + }, + "wai-routes": { + "flake": false, + "locked": { + "lastModified": 1608703392, + "narHash": "sha256-MW4lZUBjTwqBT97q7YOCHKBZTVWo2yAuvPVqgRmc74Q=", + "owner": "KtorZ", + "repo": "wai-routes", + "rev": "d74b39683792649c01113f40bf57724dcf95c96a", + "type": "github" + }, + "original": { + "owner": "KtorZ", + "repo": "wai-routes", + "rev": "d74b39683792649c01113f40bf57724dcf95c96a", + "type": "github" + } + } + }, + "root": "root", + "version": 7 +} diff --git a/templates/ctl-scaffold/flake.nix b/templates/ctl-scaffold/flake.nix new file mode 100644 index 0000000000..91cfc25fe2 --- /dev/null +++ b/templates/ctl-scaffold/flake.nix @@ -0,0 +1,115 @@ +{ + description = "ctl-scaffold"; + + inputs = { + flake-compat = { + url = "github:edolstra/flake-compat"; + flake = false; + }; + ctl = { + type = "github"; + owner = "Plutonomicon"; + repo = "cardano-transaction-lib"; + rev = "ec38c06c77c1b75a7e7f1dcb215db7816e44b339"; + }; + nixpkgs.follows = "ctl/nixpkgs"; + }; + + outputs = { self, nixpkgs, ctl, ... }@inputs: + let + supportedSystems = [ + "x86_64-linux" + "x86_64-darwin" + "aarch64-linux" + "aarch64-darwin" + ]; + perSystem = nixpkgs.lib.genAttrs supportedSystems; + nixpkgsFor = system: import nixpkgs { + inherit system; + overlays = [ + ctl.overlays.purescript + ctl.overlays.runtime + ]; + }; + psProjectFor = system: + let + projectName = "ctl-scaffold"; + pkgs = nixpkgsFor system; + packageJson = ./package.json; + packageLock = ./package-lock.json; + src = builtins.path { + path = ./.; + name = "${projectName}-src"; + # Adjust the `filter` as necessary + filter = path: ftype: !(pkgs.lib.hasSuffix ".md" path); + }; + shell = { + packageLockOnly = true; + packages = with pkgs; [ + nodePackages.eslint + nodePackages.prettier + ]; + }; + in + pkgs.purescriptProject { + inherit pkgs src projectName; + }; + in + { + packages = perSystem (system: { + default = self.packages.${system}.ctl-scaffold-bundle-web; + ctl-scaffold-bundle-web = (psProjectFor system).bundlePursProject { + main = "Scaffold.Main"; + entrypoint = "index.js"; + }; + ctl-scaffold-runtime = (nixpkgsFor system).buildCtlRuntime { }; + }); + + apps = perSystem (system: { + default = self.apps.${system}.ctl-scaffold-runtime; + ctl-scaffold-runtime = (nixpkgsFor system).launchCtlRuntime { }; + docs = (psProjectFor system).launchSearchablePursDocs { }; + }); + + checks = perSystem (system: + let + pkgs = nixpkgsFor system; + in + { + ctl-scaffold-plutip-test = (psProjectFor system).runPlutipTest { + testMain = "Scaffold.Test.Main"; + }; + + formatting-check = pkgs.runCommand "formatting-check" + { + nativeBuildInputs = with pkgs; [ + fd + easy-ps.purs-tidy + nixpkgs-fmt + nodePackages.prettier + ]; + } + '' + cd ${self} + purs-tidy check $(fd -epurs) + nixpkgs-fmt --check $(fd -enix --exclude='spago*') + prettier -c $(fd -ejs) + touch $out + ''; + + js-lint-check = pkgs.runCommand "js-lint-check" + { + nativeBuildInputs = [ pkgs.nodePackages.eslint pkgs.fd ]; + } + '' + cd ${self} + eslint $(fd -ejs) + touch $out + ''; + }); + + devShells = perSystem (system: { + default = (psProjectFor system).devShell; + }); + }; +} diff --git a/templates/ctl-scaffold/index.html b/templates/ctl-scaffold/index.html new file mode 100644 index 0000000000..443f25804f --- /dev/null +++ b/templates/ctl-scaffold/index.html @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/templates/ctl-scaffold/index.js b/templates/ctl-scaffold/index.js new file mode 100644 index 0000000000..b9185a093b --- /dev/null +++ b/templates/ctl-scaffold/index.js @@ -0,0 +1,10 @@ +"use strict"; + +// This needs to be asynchronous to load the WASM from CSL +// +// You also need to call `spago bundle-module` to generate the module that is +// imported here. From the repository root, run: +// spago bundle-module -m
--to output.js +import("./output.js").then(m => m.main()); + +console.log("app starting"); diff --git a/templates/ctl-scaffold/package-lock.json b/templates/ctl-scaffold/package-lock.json new file mode 100644 index 0000000000..9ba74c9890 --- /dev/null +++ b/templates/ctl-scaffold/package-lock.json @@ -0,0 +1,4319 @@ +{ + "name": "ctl-scaffold", + "version": "0.1.0", + "lockfileVersion": 1, + "requires": true, + "dependencies": { + "@discoveryjs/json-ext": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/@discoveryjs/json-ext/-/json-ext-0.5.7.tgz", + "integrity": "sha512-dBVuXR082gk3jsFp7Rd/JI4kytwGHecnCoTtXFb7DB6CNHp4rg5k1bhg0nWdLGLnOV71lmDzGQaLMy8iPLY0pw==", + "dev": true + }, + "@emurgo/cardano-serialization-lib-browser": { + "version": "11.0.0-beta.1", + "resolved": "https://registry.npmjs.org/@emurgo/cardano-serialization-lib-browser/-/cardano-serialization-lib-browser-11.0.0-beta.1.tgz", + "integrity": "sha512-y2jxCPQBZIG1WTcNxPT8AVb1KjCUQV5nq0em32m4l4siHCQjA4STVVds1r61sSCyviP6dzrAJo4n5i/rs+zeKQ==" + }, + "@emurgo/cardano-serialization-lib-nodejs": { + "version": "11.0.0-beta.1", + "resolved": "https://registry.npmjs.org/@emurgo/cardano-serialization-lib-nodejs/-/cardano-serialization-lib-nodejs-11.0.0-beta.1.tgz", + "integrity": "sha512-YCsfZCXDDFGeX8dp0CFOksPEsTYbnJ8YbmwKc+Vm6e6Vv6yNpXRRhcwE0Zm29lGqLQ+gUbMmLZSclKiXAb52fA==" + }, + "@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" + } + }, + "@types/body-parser": { + "version": "1.19.2", + "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.2.tgz", + "integrity": "sha512-ALYone6pm6QmwZoAgeyNksccT9Q4AWZQ6PvfwR37GT6r6FWUPguq6sUmNGSMV2Wr761oQoBxwGGa6DR5o1DC9g==", + "dev": true, + "requires": { + "@types/connect": "*", + "@types/node": "*" + } + }, + "@types/bonjour": { + "version": "3.5.10", + "resolved": "https://registry.npmjs.org/@types/bonjour/-/bonjour-3.5.10.tgz", + "integrity": "sha512-p7ienRMiS41Nu2/igbJxxLDWrSZ0WxM8UQgCeO9KhoVF7cOVFkrKsiDr1EsJIla8vV3oEEjGcz11jc5yimhzZw==", + "dev": true, + "requires": { + "@types/node": "*" + } + }, + "@types/connect": { + "version": "3.4.35", + "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.35.tgz", + "integrity": "sha512-cdeYyv4KWoEgpBISTxWvqYsVy444DOqehiF3fM3ne10AmJ62RSyNkUnxMJXHQWRQQX2eR94m5y1IZyDwBjV9FQ==", + "dev": true, + "requires": { + "@types/node": "*" + } + }, + "@types/connect-history-api-fallback": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/@types/connect-history-api-fallback/-/connect-history-api-fallback-1.3.5.tgz", + "integrity": "sha512-h8QJa8xSb1WD4fpKBDcATDNGXghFj6/3GRWG6dhmRcu0RX1Ubasur2Uvx5aeEwlf0MwblEC2bMzzMQntxnw/Cw==", + "dev": true, + "requires": { + "@types/express-serve-static-core": "*", + "@types/node": "*" + } + }, + "@types/eslint": { + "version": "8.4.1", + "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-8.4.1.tgz", + "integrity": "sha512-GE44+DNEyxxh2Kc6ro/VkIj+9ma0pO0bwv9+uHSyBrikYOHr8zYcdPvnBOp1aw8s+CjRvuSx7CyWqRrNFQ59mA==", + "dev": true, + "requires": { + "@types/estree": "*", + "@types/json-schema": "*" + } + }, + "@types/eslint-scope": { + "version": "3.7.3", + "resolved": "https://registry.npmjs.org/@types/eslint-scope/-/eslint-scope-3.7.3.tgz", + "integrity": "sha512-PB3ldyrcnAicT35TWPs5IcwKD8S333HMaa2VVv4+wdvebJkjWuW/xESoB8IwRcog8HYVYamb1g/R31Qv5Bx03g==", + "dev": true, + "requires": { + "@types/eslint": "*", + "@types/estree": "*" + } + }, + "@types/estree": { + "version": "0.0.50", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-0.0.50.tgz", + "integrity": "sha512-C6N5s2ZFtuZRj54k2/zyRhNDjJwwcViAM3Nbm8zjBpbqAdZ00mr0CFxvSKeO8Y/e03WVFLpQMdHYVfUd6SB+Hw==", + "dev": true + }, + "@types/express": { + "version": "4.17.13", + "resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.13.tgz", + "integrity": "sha512-6bSZTPaTIACxn48l50SR+axgrqm6qXFIxrdAKaG6PaJk3+zuUr35hBlgT7vOmJcum+OEaIBLtHV/qloEAFITeA==", + "dev": true, + "requires": { + "@types/body-parser": "*", + "@types/express-serve-static-core": "^4.17.18", + "@types/qs": "*", + "@types/serve-static": "*" + } + }, + "@types/express-serve-static-core": { + "version": "4.17.28", + "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.17.28.tgz", + "integrity": "sha512-P1BJAEAW3E2DJUlkgq4tOL3RyMunoWXqbSCygWo5ZIWTjUgN1YnaXWW4VWl/oc8vs/XoYibEGBKP0uZyF4AHig==", + "dev": true, + "requires": { + "@types/node": "*", + "@types/qs": "*", + "@types/range-parser": "*" + } + }, + "@types/html-minifier-terser": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/@types/html-minifier-terser/-/html-minifier-terser-6.1.0.tgz", + "integrity": "sha512-oh/6byDPnL1zeNXFrDXFLyZjkr1MsBG667IM792caf1L2UPOOMf65NFzjUH/ltyfwjAGfs1rsX1eftK0jC/KIg==", + "dev": true + }, + "@types/http-proxy": { + "version": "1.17.8", + "resolved": "https://registry.npmjs.org/@types/http-proxy/-/http-proxy-1.17.8.tgz", + "integrity": "sha512-5kPLG5BKpWYkw/LVOGWpiq3nEVqxiN32rTgI53Sk12/xHFQ2rG3ehI9IO+O3W2QoKeyB92dJkoka8SUm6BX1pA==", + "dev": true, + "requires": { + "@types/node": "*" + } + }, + "@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/mime": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/@types/mime/-/mime-1.3.2.tgz", + "integrity": "sha512-YATxVxgRqNH6nHEIsvg6k2Boc1JHI9ZbH5iWFFv/MTkchz3b1ieGDa5T0a9RznNdI0KhVbdbWSN+KWWrQZRxTw==", + "dev": true + }, + "@types/node": { + "version": "17.0.14", + "resolved": "https://registry.npmjs.org/@types/node/-/node-17.0.14.tgz", + "integrity": "sha512-SbjLmERksKOGzWzPNuW7fJM7fk3YXVTFiZWB/Hs99gwhk+/dnrQRPBQjPW9aO+fi1tAffi9PrwFvsmOKmDTyng==" + }, + "@types/qs": { + "version": "6.9.7", + "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.7.tgz", + "integrity": "sha512-FGa1F62FT09qcrueBA6qYTrJPVDzah9a+493+o2PCXsesWHIn27G98TsSMs3WPNbZIEj4+VJf6saSFpvD+3Zsw==", + "dev": true + }, + "@types/range-parser": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.4.tgz", + "integrity": "sha512-EEhsLsD6UsDM1yFhAvy0Cjr6VwmpMWqFBCb9w07wVugF7w9nfajxLuVmngTIpgS6svCnm6Vaw+MZhoDCKnOfsw==", + "dev": true + }, + "@types/retry": { + "version": "0.12.1", + "resolved": "https://registry.npmjs.org/@types/retry/-/retry-0.12.1.tgz", + "integrity": "sha512-xoDlM2S4ortawSWORYqsdU+2rxdh4LRW9ytc3zmT37RIKQh6IHyKwwtKhKis9ah8ol07DCkZxPt8BBvPjC6v4g==", + "dev": true + }, + "@types/serve-index": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/@types/serve-index/-/serve-index-1.9.1.tgz", + "integrity": "sha512-d/Hs3nWDxNL2xAczmOVZNj92YZCS6RGxfBPjKzuu/XirCgXdpKEb88dYNbrYGint6IVWLNP+yonwVAuRC0T2Dg==", + "dev": true, + "requires": { + "@types/express": "*" + } + }, + "@types/serve-static": { + "version": "1.13.10", + "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.13.10.tgz", + "integrity": "sha512-nCkHGI4w7ZgAdNkrEu0bv+4xNV/XDqW+DydknebMOQwkpDGx8G+HTlj7R7ABI8i8nKxVw0wtKPi1D+lPOkh4YQ==", + "dev": true, + "requires": { + "@types/mime": "^1", + "@types/node": "*" + } + }, + "@types/sockjs": { + "version": "0.3.33", + "resolved": "https://registry.npmjs.org/@types/sockjs/-/sockjs-0.3.33.tgz", + "integrity": "sha512-f0KEEe05NvUnat+boPTZ0dgaLZ4SfSouXUgv5noUiefG2ajgKjmETo9ZJyuqsl7dfl2aHlLJUiki6B4ZYldiiw==", + "dev": true, + "requires": { + "@types/node": "*" + } + }, + "@types/ws": { + "version": "8.2.2", + "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.2.2.tgz", + "integrity": "sha512-NOn5eIcgWLOo6qW8AcuLZ7G8PycXu0xTxxkS6Q18VWFxgPUSOwV0pBj2a/4viNZVu25i7RIB7GttdkAIUUXOOg==", + "dev": true, + "requires": { + "@types/node": "*" + } + }, + "@types/yauzl": { + "version": "2.10.0", + "resolved": "https://registry.npmjs.org/@types/yauzl/-/yauzl-2.10.0.tgz", + "integrity": "sha512-Cn6WYCm0tXv8p6k+A8PvbDG763EDpBoTzHdA+Q/MF6H3sapGjCm9NzoaJncJS9tUKSuCoDs9XHxYYsQDgxR6kw==", + "optional": true, + "requires": { + "@types/node": "*" + } + }, + "@webassemblyjs/ast": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.11.1.tgz", + "integrity": "sha512-ukBh14qFLjxTQNTXocdyksN5QdM28S1CxHt2rdskFyL+xFV7VremuBLVbmCePj+URalXBENx/9Lm7lnhihtCSw==", + "dev": true, + "requires": { + "@webassemblyjs/helper-numbers": "1.11.1", + "@webassemblyjs/helper-wasm-bytecode": "1.11.1" + } + }, + "@webassemblyjs/floating-point-hex-parser": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.11.1.tgz", + "integrity": "sha512-iGRfyc5Bq+NnNuX8b5hwBrRjzf0ocrJPI6GWFodBFzmFnyvrQ83SHKhmilCU/8Jv67i4GJZBMhEzltxzcNagtQ==", + "dev": true + }, + "@webassemblyjs/helper-api-error": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-api-error/-/helper-api-error-1.11.1.tgz", + "integrity": "sha512-RlhS8CBCXfRUR/cwo2ho9bkheSXG0+NwooXcc3PAILALf2QLdFyj7KGsKRbVc95hZnhnERon4kW/D3SZpp6Tcg==", + "dev": true + }, + "@webassemblyjs/helper-buffer": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.11.1.tgz", + "integrity": "sha512-gwikF65aDNeeXa8JxXa2BAk+REjSyhrNC9ZwdT0f8jc4dQQeDQ7G4m0f2QCLPJiMTTO6wfDmRmj/pW0PsUvIcA==", + "dev": true + }, + "@webassemblyjs/helper-numbers": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-numbers/-/helper-numbers-1.11.1.tgz", + "integrity": "sha512-vDkbxiB8zfnPdNK9Rajcey5C0w+QJugEglN0of+kmO8l7lDb77AnlKYQF7aarZuCrv+l0UvqL+68gSDr3k9LPQ==", + "dev": true, + "requires": { + "@webassemblyjs/floating-point-hex-parser": "1.11.1", + "@webassemblyjs/helper-api-error": "1.11.1", + "@xtuc/long": "4.2.2" + } + }, + "@webassemblyjs/helper-wasm-bytecode": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.11.1.tgz", + "integrity": "sha512-PvpoOGiJwXeTrSf/qfudJhwlvDQxFgelbMqtq52WWiXC6Xgg1IREdngmPN3bs4RoO83PnL/nFrxucXj1+BX62Q==", + "dev": true + }, + "@webassemblyjs/helper-wasm-section": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.11.1.tgz", + "integrity": "sha512-10P9No29rYX1j7F3EVPX3JvGPQPae+AomuSTPiF9eBQeChHI6iqjMIwR9JmOJXwpnn/oVGDk7I5IlskuMwU/pg==", + "dev": true, + "requires": { + "@webassemblyjs/ast": "1.11.1", + "@webassemblyjs/helper-buffer": "1.11.1", + "@webassemblyjs/helper-wasm-bytecode": "1.11.1", + "@webassemblyjs/wasm-gen": "1.11.1" + } + }, + "@webassemblyjs/ieee754": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/ieee754/-/ieee754-1.11.1.tgz", + "integrity": "sha512-hJ87QIPtAMKbFq6CGTkZYJivEwZDbQUgYd3qKSadTNOhVY7p+gfP6Sr0lLRVTaG1JjFj+r3YchoqRYxNH3M0GQ==", + "dev": true, + "requires": { + "@xtuc/ieee754": "^1.2.0" + } + }, + "@webassemblyjs/leb128": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/leb128/-/leb128-1.11.1.tgz", + "integrity": "sha512-BJ2P0hNZ0u+Th1YZXJpzW6miwqQUGcIHT1G/sf72gLVD9DZ5AdYTqPNbHZh6K1M5VmKvFXwGSWZADz+qBWxeRw==", + "dev": true, + "requires": { + "@xtuc/long": "4.2.2" + } + }, + "@webassemblyjs/utf8": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/utf8/-/utf8-1.11.1.tgz", + "integrity": "sha512-9kqcxAEdMhiwQkHpkNiorZzqpGrodQQ2IGrHHxCy+Ozng0ofyMA0lTqiLkVs1uzTRejX+/O0EOT7KxqVPuXosQ==", + "dev": true + }, + "@webassemblyjs/wasm-edit": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.11.1.tgz", + "integrity": "sha512-g+RsupUC1aTHfR8CDgnsVRVZFJqdkFHpsHMfJuWQzWU3tvnLC07UqHICfP+4XyL2tnr1amvl1Sdp06TnYCmVkA==", + "dev": true, + "requires": { + "@webassemblyjs/ast": "1.11.1", + "@webassemblyjs/helper-buffer": "1.11.1", + "@webassemblyjs/helper-wasm-bytecode": "1.11.1", + "@webassemblyjs/helper-wasm-section": "1.11.1", + "@webassemblyjs/wasm-gen": "1.11.1", + "@webassemblyjs/wasm-opt": "1.11.1", + "@webassemblyjs/wasm-parser": "1.11.1", + "@webassemblyjs/wast-printer": "1.11.1" + } + }, + "@webassemblyjs/wasm-gen": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.11.1.tgz", + "integrity": "sha512-F7QqKXwwNlMmsulj6+O7r4mmtAlCWfO/0HdgOxSklZfQcDu0TpLiD1mRt/zF25Bk59FIjEuGAIyn5ei4yMfLhA==", + "dev": true, + "requires": { + "@webassemblyjs/ast": "1.11.1", + "@webassemblyjs/helper-wasm-bytecode": "1.11.1", + "@webassemblyjs/ieee754": "1.11.1", + "@webassemblyjs/leb128": "1.11.1", + "@webassemblyjs/utf8": "1.11.1" + } + }, + "@webassemblyjs/wasm-opt": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.11.1.tgz", + "integrity": "sha512-VqnkNqnZlU5EB64pp1l7hdm3hmQw7Vgqa0KF/KCNO9sIpI6Fk6brDEiX+iCOYrvMuBWDws0NkTOxYEb85XQHHw==", + "dev": true, + "requires": { + "@webassemblyjs/ast": "1.11.1", + "@webassemblyjs/helper-buffer": "1.11.1", + "@webassemblyjs/wasm-gen": "1.11.1", + "@webassemblyjs/wasm-parser": "1.11.1" + } + }, + "@webassemblyjs/wasm-parser": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.11.1.tgz", + "integrity": "sha512-rrBujw+dJu32gYB7/Lup6UhdkPx9S9SnobZzRVL7VcBH9Bt9bCBLEuX/YXOOtBsOZ4NQrRykKhffRWHvigQvOA==", + "dev": true, + "requires": { + "@webassemblyjs/ast": "1.11.1", + "@webassemblyjs/helper-api-error": "1.11.1", + "@webassemblyjs/helper-wasm-bytecode": "1.11.1", + "@webassemblyjs/ieee754": "1.11.1", + "@webassemblyjs/leb128": "1.11.1", + "@webassemblyjs/utf8": "1.11.1" + } + }, + "@webassemblyjs/wast-printer": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.11.1.tgz", + "integrity": "sha512-IQboUWM4eKzWW+N/jij2sRatKMh99QEelo3Eb2q0qXkvPRISAj8Qxtmw5itwqK+TTkBuUIE45AxYPToqPtL5gg==", + "dev": true, + "requires": { + "@webassemblyjs/ast": "1.11.1", + "@xtuc/long": "4.2.2" + } + }, + "@webpack-cli/configtest": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@webpack-cli/configtest/-/configtest-1.2.0.tgz", + "integrity": "sha512-4FB8Tj6xyVkyqjj1OaTqCjXYULB9FMkqQ8yGrZjRDrYh0nOE+7Lhs45WioWQQMV+ceFlE368Ukhe6xdvJM9Egg==", + "dev": true + }, + "@webpack-cli/info": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@webpack-cli/info/-/info-1.5.0.tgz", + "integrity": "sha512-e8tSXZpw2hPl2uMJY6fsMswaok5FdlGNRTktvFk2sD8RjH0hE2+XistawJx1vmKteh4NmGmNUrp+Tb2w+udPcQ==", + "dev": true, + "requires": { + "envinfo": "^7.7.3" + } + }, + "@webpack-cli/serve": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/@webpack-cli/serve/-/serve-1.7.0.tgz", + "integrity": "sha512-oxnCNGj88fL+xzV+dacXs44HcDwf1ovs3AuEzvP7mqXw7fQntqIhQ1BRmynh4qEKQSSSRSWVyXRjmTbZIX9V2Q==", + "dev": true + }, + "@xtuc/ieee754": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@xtuc/ieee754/-/ieee754-1.2.0.tgz", + "integrity": "sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA==", + "dev": true + }, + "@xtuc/long": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@xtuc/long/-/long-4.2.2.tgz", + "integrity": "sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==", + "dev": true + }, + "accepts": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", + "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==", + "dev": true, + "requires": { + "mime-types": "~2.1.34", + "negotiator": "0.6.3" + } + }, + "acorn": { + "version": "8.7.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.7.0.tgz", + "integrity": "sha512-V/LGr1APy+PXIwKebEWrkZPwoeoF+w1jiOBUmuxuiUIaOHtob8Qc9BTrYo7VuI5fR8tqsy+buA2WFooR5olqvQ==", + "dev": true + }, + "acorn-import-assertions": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/acorn-import-assertions/-/acorn-import-assertions-1.8.0.tgz", + "integrity": "sha512-m7VZ3jwz4eK6A4Vtt8Ew1/mNbP24u0FhdyfA7fSvnJR6LMdfOYnmuIrrJAgrYfYJ10F/otaHTtrtrtmHdMNzEw==", + "dev": true + }, + "agent-base": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", + "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", + "requires": { + "debug": "4" + }, + "dependencies": { + "debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "requires": { + "ms": "2.1.2" + } + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + } + } + }, + "aggregate-error": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-3.1.0.tgz", + "integrity": "sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==", + "dev": true, + "requires": { + "clean-stack": "^2.0.0", + "indent-string": "^4.0.0" + } + }, + "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" + } + }, + "ajv-formats": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-2.1.1.tgz", + "integrity": "sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==", + "dev": true, + "requires": { + "ajv": "^8.0.0" + }, + "dependencies": { + "ajv": { + "version": "8.9.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.9.0.tgz", + "integrity": "sha512-qOKJyNj/h+OWx7s5DePL6Zu1KeM9jPZhwBqs+7DzP6bGOvqzVCSf0xueYmVuaC/oQ/VtS2zLMLHdQFbkka+XDQ==", + "dev": true, + "requires": { + "fast-deep-equal": "^3.1.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2", + "uri-js": "^4.2.2" + } + }, + "json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "dev": true + } + } + }, + "ajv-keywords": { + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", + "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==", + "dev": true + }, + "ansi-html-community": { + "version": "0.0.8", + "resolved": "https://registry.npmjs.org/ansi-html-community/-/ansi-html-community-0.0.8.tgz", + "integrity": "sha512-1APHAyr3+PCamwNw3bXCPp4HFLONZt/yIH0sZp0/469KWNTEy+qN5jQ3GVX6DMZ1UXAi34yVwtTeaG/HpBuuzw==", + "dev": true + }, + "ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true + }, + "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" + } + }, + "array-flatten": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-2.1.2.tgz", + "integrity": "sha512-hNfzcOV8W4NdualtqBFPyVO+54DSJuZGY9qT4pRroB6S9e3iiido2ISIC5h9R2sPJ8H3FHCIiEnsv1lPXO3KtQ==", + "dev": true + }, + "asn1.js": { + "version": "5.4.1", + "resolved": "https://registry.npmjs.org/asn1.js/-/asn1.js-5.4.1.tgz", + "integrity": "sha512-+I//4cYPccV8LdmBLiX8CYvf9Sp3vQsrqu2QNXRcrbiWvcx/UdlFiqUJJzxRQxgsZmvhXhn4cSKeSmoFjVdupA==", + "requires": { + "bn.js": "^4.0.0", + "inherits": "^2.0.1", + "minimalistic-assert": "^1.0.0", + "safer-buffer": "^2.1.0" + }, + "dependencies": { + "bn.js": { + "version": "4.12.0", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", + "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==" + } + } + }, + "assert": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/assert/-/assert-2.0.0.tgz", + "integrity": "sha512-se5Cd+js9dXJnu6Ag2JFc00t+HmHOen+8Q+L7O9zI0PqQXr20uk2J0XQqMxZEeo5U50o8Nvmmx7dZrl+Ufr35A==", + "requires": { + "es6-object-assign": "^1.1.0", + "is-nan": "^1.2.1", + "object-is": "^1.0.1", + "util": "^0.12.0" + } + }, + "async": { + "version": "2.6.3", + "resolved": "https://registry.npmjs.org/async/-/async-2.6.3.tgz", + "integrity": "sha512-zflvls11DCy+dQWzTW2dzuilv8Z5X/pjfmZOWba6TNIVDm+2UDaJmXSOXlasHKfNBs8oo3M0aT50fDEWfKZjXg==", + "dev": true, + "requires": { + "lodash": "^4.17.14" + } + }, + "available-typed-arrays": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.5.tgz", + "integrity": "sha512-DMD0KiN46eipeziST1LPP/STfDU0sufISXmjSgvVsoU2tqxctQeASejWcfNtxYKqETM1UxQ8sp2OrSBWpHY6sw==" + }, + "b4a": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/b4a/-/b4a-1.6.0.tgz", + "integrity": "sha512-fsTxXxj1081Yq5MOQ06gZ5+e2QcSyP2U6NofdOWyq+lrNI4IjkZ+fLVmoQ6uUCiNg1NWePMMVq93vOTdbJmErw==" + }, + "balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" + }, + "base64-js": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==" + }, + "batch": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/batch/-/batch-0.6.1.tgz", + "integrity": "sha1-3DQxT05nkxgJP8dgJyUl+UvyXBY=", + "dev": true + }, + "big-integer": { + "version": "1.6.51", + "resolved": "https://registry.npmjs.org/big-integer/-/big-integer-1.6.51.tgz", + "integrity": "sha512-GPEid2Y9QU1Exl1rpO9B2IPJGHPSupF5GnVIP0blYvNOMer2bTvSWs1jGOUg04hTmu67nmLsQ9TBo1puaotBHg==" + }, + "binary-extensions": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", + "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==", + "dev": true + }, + "bl": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz", + "integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==", + "requires": { + "buffer": "^5.5.0", + "inherits": "^2.0.4", + "readable-stream": "^3.4.0" + }, + "dependencies": { + "buffer": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", + "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", + "requires": { + "base64-js": "^1.3.1", + "ieee754": "^1.1.13" + } + } + } + }, + "blake2b-wasm": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/blake2b-wasm/-/blake2b-wasm-2.4.0.tgz", + "integrity": "sha512-S1kwmW2ZhZFFFOghcx73+ZajEfKBqhP82JMssxtLVMxlaPea1p9uoLiUZ5WYyHn0KddwbLc+0vh4wR0KBNoT5w==", + "requires": { + "b4a": "^1.0.1", + "nanoassert": "^2.0.0" + } + }, + "bn.js": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-5.2.0.tgz", + "integrity": "sha512-D7iWRBvnZE8ecXiLj/9wbxH7Tk79fAh8IHaTNq1RWRixsS02W+5qS+iE9yq6RYl0asXx5tw0bLhmT5pIfbSquw==" + }, + "body-parser": { + "version": "1.19.1", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.19.1.tgz", + "integrity": "sha512-8ljfQi5eBk8EJfECMrgqNGWPEY5jWP+1IzkzkGdFFEwFQZZyaZ21UqdaHktgiMlH0xLHqIFtE/u2OYE5dOtViA==", + "dev": true, + "requires": { + "bytes": "3.1.1", + "content-type": "~1.0.4", + "debug": "2.6.9", + "depd": "~1.1.2", + "http-errors": "1.8.1", + "iconv-lite": "0.4.24", + "on-finished": "~2.3.0", + "qs": "6.9.6", + "raw-body": "2.4.2", + "type-is": "~1.6.18" + }, + "dependencies": { + "bytes": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.1.tgz", + "integrity": "sha512-dWe4nWO/ruEOY7HkUJ5gFt1DCFV9zPRoJr8pV0/ASQermOZjtq8jMjOprC0Kd10GLN+l7xaUPvxzJFWtxGu8Fg==", + "dev": true + } + } + }, + "bonjour": { + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/bonjour/-/bonjour-3.5.0.tgz", + "integrity": "sha1-jokKGD2O6aI5OzhExpGkK897yfU=", + "dev": true, + "requires": { + "array-flatten": "^2.1.0", + "deep-equal": "^1.0.1", + "dns-equal": "^1.0.0", + "dns-txt": "^2.0.2", + "multicast-dns": "^6.0.1", + "multicast-dns-service-types": "^1.1.0" + } + }, + "boolbase": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz", + "integrity": "sha1-aN/1++YMUes3cl6p4+0xDcwed24=", + "dev": true + }, + "brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "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" + } + }, + "brorand": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/brorand/-/brorand-1.1.0.tgz", + "integrity": "sha1-EsJe/kCkXjwyPrhnWgoM5XsiNx8=" + }, + "browserify-aes": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/browserify-aes/-/browserify-aes-1.2.0.tgz", + "integrity": "sha512-+7CHXqGuspUn/Sl5aO7Ea0xWGAtETPXNSAjHo48JfLdPWcMng33Xe4znFvQweqc/uzk5zSOI3H52CYnjCfb5hA==", + "requires": { + "buffer-xor": "^1.0.3", + "cipher-base": "^1.0.0", + "create-hash": "^1.1.0", + "evp_bytestokey": "^1.0.3", + "inherits": "^2.0.1", + "safe-buffer": "^5.0.1" + } + }, + "browserify-cipher": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/browserify-cipher/-/browserify-cipher-1.0.1.tgz", + "integrity": "sha512-sPhkz0ARKbf4rRQt2hTpAHqn47X3llLkUGn+xEJzLjwY8LRs2p0v7ljvI5EyoRO/mexrNunNECisZs+gw2zz1w==", + "requires": { + "browserify-aes": "^1.0.4", + "browserify-des": "^1.0.0", + "evp_bytestokey": "^1.0.0" + } + }, + "browserify-des": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/browserify-des/-/browserify-des-1.0.2.tgz", + "integrity": "sha512-BioO1xf3hFwz4kc6iBhI3ieDFompMhrMlnDFC4/0/vd5MokpuAc3R+LYbwTA9A5Yc9pq9UYPqffKpW2ObuwX5A==", + "requires": { + "cipher-base": "^1.0.1", + "des.js": "^1.0.0", + "inherits": "^2.0.1", + "safe-buffer": "^5.1.2" + } + }, + "browserify-rsa": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/browserify-rsa/-/browserify-rsa-4.1.0.tgz", + "integrity": "sha512-AdEER0Hkspgno2aR97SAf6vi0y0k8NuOpGnVH3O99rcA5Q6sh8QxcngtHuJ6uXwnfAXNM4Gn1Gb7/MV1+Ymbog==", + "requires": { + "bn.js": "^5.0.0", + "randombytes": "^2.0.1" + } + }, + "browserify-sign": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/browserify-sign/-/browserify-sign-4.2.1.tgz", + "integrity": "sha512-/vrA5fguVAKKAVTNJjgSm1tRQDHUU6DbwO9IROu/0WAzC8PKhucDSh18J0RMvVeHAn5puMd+QHC2erPRNf8lmg==", + "requires": { + "bn.js": "^5.1.1", + "browserify-rsa": "^4.0.1", + "create-hash": "^1.2.0", + "create-hmac": "^1.1.7", + "elliptic": "^6.5.3", + "inherits": "^2.0.4", + "parse-asn1": "^5.1.5", + "readable-stream": "^3.6.0", + "safe-buffer": "^5.2.0" + } + }, + "browserify-zlib": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/browserify-zlib/-/browserify-zlib-0.2.0.tgz", + "integrity": "sha512-Z942RysHXmJrhqk88FmKBVq/v5tqmSkDz7p54G/MGyjMnCFFnC79XWNbg+Vta8W6Wb2qtSZTSxIGkJrRpCFEiA==", + "requires": { + "pako": "~1.0.5" + } + }, + "browserslist": { + "version": "4.19.1", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.19.1.tgz", + "integrity": "sha512-u2tbbG5PdKRTUoctO3NBD8FQ5HdPh1ZXPHzp1rwaa5jTc+RV9/+RlWiAIKmjRPQF+xbGM9Kklj5bZQFa2s/38A==", + "dev": true, + "requires": { + "caniuse-lite": "^1.0.30001286", + "electron-to-chromium": "^1.4.17", + "escalade": "^3.1.1", + "node-releases": "^2.0.1", + "picocolors": "^1.0.0" + } + }, + "buffer": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz", + "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==", + "requires": { + "base64-js": "^1.3.1", + "ieee754": "^1.2.1" + } + }, + "buffer-crc32": { + "version": "0.2.13", + "resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.13.tgz", + "integrity": "sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ==" + }, + "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 + }, + "buffer-indexof": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/buffer-indexof/-/buffer-indexof-1.1.1.tgz", + "integrity": "sha512-4/rOEg86jivtPTeOUUT61jJO1Ya1TrR/OkqCSZDyq84WJh3LuuiphBYJN+fm5xufIk4XAFcEwte/8WzC8If/1g==", + "dev": true + }, + "buffer-xor": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/buffer-xor/-/buffer-xor-1.0.3.tgz", + "integrity": "sha1-JuYe0UIvtw3ULm42cp7VHYVf6Nk=" + }, + "bufferutil": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/bufferutil/-/bufferutil-4.0.5.tgz", + "integrity": "sha512-HTm14iMQKK2FjFLRTM5lAVcyaUzOnqbPtesFIvREgXpJHdQm8bWS+GkQgIkfaBYRHuCnea7w8UVNfwiAQhlr9A==", + "requires": { + "node-gyp-build": "^4.3.0" + }, + "dependencies": { + "node-gyp-build": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-4.3.0.tgz", + "integrity": "sha512-iWjXZvmboq0ja1pUGULQBexmxq8CV4xBhX7VDOTbL7ZR4FOowwY/VOtRxBN/yKxmdGoIp4j5ysNT4u3S2pDQ3Q==" + } + } + }, + "builtin-status-codes": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/builtin-status-codes/-/builtin-status-codes-3.0.0.tgz", + "integrity": "sha1-hZgoeOIbmOHGZCXgPQF0eI9Wnug=" + }, + "bytes": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.0.0.tgz", + "integrity": "sha1-0ygVQE1olpn4Wk6k+odV3ROpYEg=", + "dev": true + }, + "call-bind": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz", + "integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==", + "requires": { + "function-bind": "^1.1.1", + "get-intrinsic": "^1.0.2" + } + }, + "camel-case": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/camel-case/-/camel-case-4.1.2.tgz", + "integrity": "sha512-gxGWBrTT1JuMx6R+o5PTXMmUnhnVzLQ9SNutD4YqKtI6ap897t3tKECYla6gCWEkplXnlNybEkZg9GEGxKFCgw==", + "dev": true, + "requires": { + "pascal-case": "^3.1.2", + "tslib": "^2.0.3" + } + }, + "caniuse-lite": { + "version": "1.0.30001305", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001305.tgz", + "integrity": "sha512-p7d9YQMji8haf0f+5rbcv9WlQ+N5jMPfRAnUmZRlNxsNeBO3Yr7RYG6M2uTY1h9tCVdlkJg6YNNc4kiAiBLdWA==", + "dev": true + }, + "chokidar": { + "version": "3.5.3", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz", + "integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==", + "dev": true, + "requires": { + "anymatch": "~3.1.2", + "braces": "~3.0.2", + "fsevents": "~2.3.2", + "glob-parent": "~5.1.2", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.6.0" + } + }, + "chownr": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz", + "integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==" + }, + "chrome-trace-event": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/chrome-trace-event/-/chrome-trace-event-1.0.3.tgz", + "integrity": "sha512-p3KULyQg4S7NIHixdwbGX+nFHkoBiA4YQmyWtjb8XngSKV124nJmRysgAeujbUVb15vh+RvFUfCPqU7rXk+hZg==", + "dev": true + }, + "cipher-base": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/cipher-base/-/cipher-base-1.0.4.tgz", + "integrity": "sha512-Kkht5ye6ZGmwv40uUDZztayT2ThLQGfnj/T71N/XzeZeo3nf8foyW7zGTsPYkEya3m5f3cAypH+qe7YOrM1U2Q==", + "requires": { + "inherits": "^2.0.1", + "safe-buffer": "^5.0.1" + } + }, + "clean-css": { + "version": "5.2.4", + "resolved": "https://registry.npmjs.org/clean-css/-/clean-css-5.2.4.tgz", + "integrity": "sha512-nKseG8wCzEuji/4yrgM/5cthL9oTDc5UOQyFMvW/Q53oP6gLH690o1NbuTh6Y18nujr7BxlsFuS7gXLnLzKJGg==", + "dev": true, + "requires": { + "source-map": "~0.6.0" + } + }, + "clean-stack": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz", + "integrity": "sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==", + "dev": true + }, + "clone-deep": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/clone-deep/-/clone-deep-4.0.1.tgz", + "integrity": "sha512-neHB9xuzh/wk0dIHweyAXv2aPGZIVk3pLMe+/RNzINf17fe0OG96QroktYAUm7SM1PBnzTabaLboqqxDyMU+SQ==", + "dev": true, + "requires": { + "is-plain-object": "^2.0.4", + "kind-of": "^6.0.2", + "shallow-clone": "^3.0.0" + } + }, + "colorette": { + "version": "2.0.16", + "resolved": "https://registry.npmjs.org/colorette/-/colorette-2.0.16.tgz", + "integrity": "sha512-hUewv7oMjCp+wkBv5Rm0v87eJhq4woh5rSR+42YSQJKecCqgIqNkZ6lAlQms/BwHPJA5NKMRlpxPRv0n8HQW6g==", + "dev": true + }, + "commander": { + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-8.3.0.tgz", + "integrity": "sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww==", + "dev": true + }, + "compressible": { + "version": "2.0.18", + "resolved": "https://registry.npmjs.org/compressible/-/compressible-2.0.18.tgz", + "integrity": "sha512-AF3r7P5dWxL8MxyITRMlORQNaOA2IkAFaTr4k7BUumjPtRpGDTZpl0Pb1XCO6JeDCBdp126Cgs9sMxqSjgYyRg==", + "dev": true, + "requires": { + "mime-db": ">= 1.43.0 < 2" + } + }, + "compression": { + "version": "1.7.4", + "resolved": "https://registry.npmjs.org/compression/-/compression-1.7.4.tgz", + "integrity": "sha512-jaSIDzP9pZVS4ZfQ+TzvtiWhdpFhE2RDHz8QJkpX9SIpLq88VueF5jJw6t+6CUQcAoA6t+x89MLrWAqpfDE8iQ==", + "dev": true, + "requires": { + "accepts": "~1.3.5", + "bytes": "3.0.0", + "compressible": "~2.0.16", + "debug": "2.6.9", + "on-headers": "~1.0.2", + "safe-buffer": "5.1.2", + "vary": "~1.1.2" + }, + "dependencies": { + "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 + } + } + }, + "concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=" + }, + "connect-history-api-fallback": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/connect-history-api-fallback/-/connect-history-api-fallback-1.6.0.tgz", + "integrity": "sha512-e54B99q/OUoH64zYYRf3HBP5z24G38h5D3qXu23JGRoigpX5Ss4r9ZnDk3g0Z8uQC2x2lPaJ+UlWBc1ZWBWdLg==", + "dev": true + }, + "console-browserify": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/console-browserify/-/console-browserify-1.2.0.tgz", + "integrity": "sha512-ZMkYO/LkF17QvCPqM0gxw8yUzigAOZOSWSHg91FH6orS7vcEj5dVZTidN2fQ14yBSdg97RqhSNwLUXInd52OTA==" + }, + "constants-browserify": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/constants-browserify/-/constants-browserify-1.0.0.tgz", + "integrity": "sha1-wguW2MYXdIqvHBYCF2DNJ/y4y3U=" + }, + "content-disposition": { + "version": "0.5.4", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", + "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==", + "dev": true, + "requires": { + "safe-buffer": "5.2.1" + } + }, + "content-type": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz", + "integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==", + "dev": true + }, + "cookie": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.1.tgz", + "integrity": "sha512-ZwrFkGJxUR3EIoXtO+yVE69Eb7KlixbaeAWfBQB9vVsNn/o+Yw69gBWSSDK825hQNdN+wF8zELf3dFNl/kxkUA==", + "dev": true + }, + "cookie-signature": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", + "integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw=", + "dev": true + }, + "core-util-is": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", + "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==", + "dev": true + }, + "create-ecdh": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/create-ecdh/-/create-ecdh-4.0.4.tgz", + "integrity": "sha512-mf+TCx8wWc9VpuxfP2ht0iSISLZnt0JgWlrOKZiNqyUZWnjIaCIVNQArMHnCZKfEYRg6IM7A+NeJoN8gf/Ws0A==", + "requires": { + "bn.js": "^4.1.0", + "elliptic": "^6.5.3" + }, + "dependencies": { + "bn.js": { + "version": "4.12.0", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", + "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==" + } + } + }, + "create-hash": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/create-hash/-/create-hash-1.2.0.tgz", + "integrity": "sha512-z00bCGNHDG8mHAkP7CtT1qVu+bFQUPjYq/4Iv3C3kWjTFV10zIjfSoeqXo9Asws8gwSHDGj/hl2u4OGIjapeCg==", + "requires": { + "cipher-base": "^1.0.1", + "inherits": "^2.0.1", + "md5.js": "^1.3.4", + "ripemd160": "^2.0.1", + "sha.js": "^2.4.0" + } + }, + "create-hmac": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/create-hmac/-/create-hmac-1.1.7.tgz", + "integrity": "sha512-MJG9liiZ+ogc4TzUwuvbER1JRdgvUFSB5+VR/g5h82fGaIRWMWddtKBHi7/sVhfjQZ6SehlyhvQYrcYkaUIpLg==", + "requires": { + "cipher-base": "^1.0.3", + "create-hash": "^1.1.0", + "inherits": "^2.0.1", + "ripemd160": "^2.0.0", + "safe-buffer": "^5.0.1", + "sha.js": "^2.4.8" + } + }, + "cross-fetch": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/cross-fetch/-/cross-fetch-3.1.5.tgz", + "integrity": "sha512-lvb1SBsI0Z7GDwmuid+mU3kWVBwTVUbe7S0H52yaaAdQOXq2YktTCZdlAcNKFzE6QtRz0snpw9bNiPeOIkkQvw==", + "requires": { + "node-fetch": "2.6.7" + } + }, + "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" + } + }, + "crypto-browserify": { + "version": "3.12.0", + "resolved": "https://registry.npmjs.org/crypto-browserify/-/crypto-browserify-3.12.0.tgz", + "integrity": "sha512-fz4spIh+znjO2VjL+IdhEpRJ3YN6sMzITSBijk6FK2UvTqruSQW+/cCZTSNsMiZNvUeq0CqurF+dAbyiGOY6Wg==", + "requires": { + "browserify-cipher": "^1.0.0", + "browserify-sign": "^4.0.0", + "create-ecdh": "^4.0.0", + "create-hash": "^1.1.0", + "create-hmac": "^1.1.0", + "diffie-hellman": "^5.0.0", + "inherits": "^2.0.1", + "pbkdf2": "^3.0.3", + "public-encrypt": "^4.0.0", + "randombytes": "^2.0.0", + "randomfill": "^1.0.3" + } + }, + "css-select": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/css-select/-/css-select-4.2.1.tgz", + "integrity": "sha512-/aUslKhzkTNCQUB2qTX84lVmfia9NyjP3WpDGtj/WxhwBzWBYUV3DgUpurHTme8UTPcPlAD1DJ+b0nN/t50zDQ==", + "dev": true, + "requires": { + "boolbase": "^1.0.0", + "css-what": "^5.1.0", + "domhandler": "^4.3.0", + "domutils": "^2.8.0", + "nth-check": "^2.0.1" + } + }, + "css-what": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/css-what/-/css-what-5.1.0.tgz", + "integrity": "sha512-arSMRWIIFY0hV8pIxZMEfmMI47Wj3R/aWpZDDxWYCPEiOMv6tfOrnpDtgxBYPEQD4V0Y/958+1TdC3iWTFcUPw==", + "dev": true + }, + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + }, + "deep-equal": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/deep-equal/-/deep-equal-1.1.1.tgz", + "integrity": "sha512-yd9c5AdiqVcR+JjcwUQb9DkhJc8ngNr0MahEBGvDiJw8puWab2yZlh+nkasOnZP+EGTAP6rRp2JzJhJZzvNF8g==", + "dev": true, + "requires": { + "is-arguments": "^1.0.4", + "is-date-object": "^1.0.1", + "is-regex": "^1.0.4", + "object-is": "^1.0.1", + "object-keys": "^1.1.1", + "regexp.prototype.flags": "^1.2.0" + } + }, + "default-gateway": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/default-gateway/-/default-gateway-6.0.3.tgz", + "integrity": "sha512-fwSOJsbbNzZ/CUFpqFBqYfYNLj1NbMPm8MMCIzHjC83iSJRBEGmDUxU+WP661BaBQImeC2yHwXtz+P/O9o+XEg==", + "dev": true, + "requires": { + "execa": "^5.0.0" + } + }, + "define-lazy-prop": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/define-lazy-prop/-/define-lazy-prop-2.0.0.tgz", + "integrity": "sha512-Ds09qNh8yw3khSjiJjiUInaGX9xlqZDY7JVryGxdxV7NPeuqQfplOpQ66yJFZut3jLa5zOwkXw1g9EI2uKh4Og==", + "dev": true + }, + "define-properties": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz", + "integrity": "sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ==", + "requires": { + "object-keys": "^1.0.12" + } + }, + "del": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/del/-/del-6.0.0.tgz", + "integrity": "sha512-1shh9DQ23L16oXSZKB2JxpL7iMy2E0S9d517ptA1P8iw0alkPtQcrKH7ru31rYtKwF499HkTu+DRzq3TCKDFRQ==", + "dev": true, + "requires": { + "globby": "^11.0.1", + "graceful-fs": "^4.2.4", + "is-glob": "^4.0.1", + "is-path-cwd": "^2.2.0", + "is-path-inside": "^3.0.2", + "p-map": "^4.0.0", + "rimraf": "^3.0.2", + "slash": "^3.0.0" + }, + "dependencies": { + "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 + }, + "globby": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", + "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", + "dev": true, + "requires": { + "array-union": "^2.1.0", + "dir-glob": "^3.0.1", + "fast-glob": "^3.2.9", + "ignore": "^5.2.0", + "merge2": "^1.4.1", + "slash": "^3.0.0" + } + } + } + }, + "depd": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", + "integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak=", + "dev": true + }, + "des.js": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/des.js/-/des.js-1.0.1.tgz", + "integrity": "sha512-Q0I4pfFrv2VPd34/vfLrFOoRmlYj3OV50i7fskps1jZWK1kApMWWT9G6RRUeYedLcBDIhnSDaUvJMb3AhUlaEA==", + "requires": { + "inherits": "^2.0.1", + "minimalistic-assert": "^1.0.0" + } + }, + "destroy": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz", + "integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA=", + "dev": true + }, + "detect-node": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/detect-node/-/detect-node-2.1.0.tgz", + "integrity": "sha512-T0NIuQpnTvFDATNuHN5roPwSBG83rFsuO+MXXH9/3N1eFbn4wcPjttvjMLEPWJ0RGUYgQE7cGgS3tNxbqCGM7g==", + "dev": true + }, + "devtools-protocol": { + "version": "0.0.1019158", + "resolved": "https://registry.npmjs.org/devtools-protocol/-/devtools-protocol-0.0.1019158.tgz", + "integrity": "sha512-wvq+KscQ7/6spEV7czhnZc9RM/woz1AY+/Vpd8/h2HFMwJSdTliu7f/yr1A6vDdJfKICZsShqsYpEQbdhg8AFQ==" + }, + "diffie-hellman": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/diffie-hellman/-/diffie-hellman-5.0.3.tgz", + "integrity": "sha512-kqag/Nl+f3GwyK25fhUMYj81BUOrZ9IuJsjIcDE5icNM9FJHAVm3VcUDxdLPoQtTuUylWm6ZIknYJwwaPxsUzg==", + "requires": { + "bn.js": "^4.1.0", + "miller-rabin": "^4.0.0", + "randombytes": "^2.0.0" + }, + "dependencies": { + "bn.js": { + "version": "4.12.0", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", + "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==" + } + } + }, + "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" + } + }, + "dns-equal": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/dns-equal/-/dns-equal-1.0.0.tgz", + "integrity": "sha1-s55/HabrCnW6nBcySzR1PEfgZU0=", + "dev": true + }, + "dns-packet": { + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/dns-packet/-/dns-packet-1.3.4.tgz", + "integrity": "sha512-BQ6F4vycLXBvdrJZ6S3gZewt6rcrks9KBgM9vrhW+knGRqc8uEdT7fuCwloc7nny5xNoMJ17HGH0R/6fpo8ECA==", + "dev": true, + "requires": { + "ip": "^1.1.0", + "safe-buffer": "^5.0.1" + } + }, + "dns-txt": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/dns-txt/-/dns-txt-2.0.2.tgz", + "integrity": "sha1-uR2Ab10nGI5Ks+fRB9iBocxGQrY=", + "dev": true, + "requires": { + "buffer-indexof": "^1.0.0" + } + }, + "dom-converter": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/dom-converter/-/dom-converter-0.2.0.tgz", + "integrity": "sha512-gd3ypIPfOMr9h5jIKq8E3sHOTCjeirnl0WK5ZdS1AW0Odt0b1PaWaHdJ4Qk4klv+YB9aJBS7mESXjFoDQPu6DA==", + "dev": true, + "requires": { + "utila": "~0.4" + } + }, + "dom-serializer": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-1.3.2.tgz", + "integrity": "sha512-5c54Bk5Dw4qAxNOI1pFEizPSjVsx5+bpJKmL2kPn8JhBUq2q09tTCa3mjijun2NfK78NMouDYNMBkOrPZiS+ig==", + "dev": true, + "requires": { + "domelementtype": "^2.0.1", + "domhandler": "^4.2.0", + "entities": "^2.0.0" + } + }, + "domain-browser": { + "version": "4.22.0", + "resolved": "https://registry.npmjs.org/domain-browser/-/domain-browser-4.22.0.tgz", + "integrity": "sha512-IGBwjF7tNk3cwypFNH/7bfzBcgSCbaMOD3GsaY1AU/JRrnHnYgEM0+9kQt52iZxjNsjBtJYtao146V+f8jFZNw==" + }, + "domelementtype": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.2.0.tgz", + "integrity": "sha512-DtBMo82pv1dFtUmHyr48beiuq792Sxohr+8Hm9zoxklYPfa6n0Z3Byjj2IV7bmr2IyqClnqEQhfgHJJ5QF0R5A==", + "dev": true + }, + "domhandler": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-4.3.0.tgz", + "integrity": "sha512-fC0aXNQXqKSFTr2wDNZDhsEYjCiYsDWl3D01kwt25hm1YIPyDGHvvi3rw+PLqHAl/m71MaiF7d5zvBr0p5UB2g==", + "dev": true, + "requires": { + "domelementtype": "^2.2.0" + } + }, + "domutils": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/domutils/-/domutils-2.8.0.tgz", + "integrity": "sha512-w96Cjofp72M5IIhpjgobBimYEfoPjx1Vx0BSX9P30WBdZW2WIKU0T1Bd0kz2eNZ9ikjKgHbEyKx8BB6H1L3h3A==", + "dev": true, + "requires": { + "dom-serializer": "^1.0.1", + "domelementtype": "^2.2.0", + "domhandler": "^4.2.0" + } + }, + "dot-case": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/dot-case/-/dot-case-3.0.4.tgz", + "integrity": "sha512-Kv5nKlh6yRrdrGvxeJ2e5y2eRUpkUosIW4A2AS38zwSz27zu7ufDwQPi5Jhs3XAlGNetl3bmnGhQsMtkKJnj3w==", + "dev": true, + "requires": { + "no-case": "^3.0.4", + "tslib": "^2.0.3" + } + }, + "ee-first": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", + "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=", + "dev": true + }, + "electron-to-chromium": { + "version": "1.4.63", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.63.tgz", + "integrity": "sha512-e0PX/LRJPFRU4kzJKLvTobxyFdnANCvcoDCe8XcyTqP58nTWIwdsHvXLIl1RkB39X5yaosLaroMASWB0oIsgCA==", + "dev": true + }, + "elliptic": { + "version": "6.5.4", + "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.5.4.tgz", + "integrity": "sha512-iLhC6ULemrljPZb+QutR5TQGB+pdW6KGD5RSegS+8sorOZT+rdQFbsQFJgvN3eRqNALqJer4oQ16YvJHlU8hzQ==", + "requires": { + "bn.js": "^4.11.9", + "brorand": "^1.1.0", + "hash.js": "^1.0.0", + "hmac-drbg": "^1.0.1", + "inherits": "^2.0.4", + "minimalistic-assert": "^1.0.1", + "minimalistic-crypto-utils": "^1.0.1" + }, + "dependencies": { + "bn.js": { + "version": "4.12.0", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", + "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==" + } + } + }, + "encodeurl": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", + "integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=", + "dev": true + }, + "end-of-stream": { + "version": "1.4.4", + "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", + "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==", + "requires": { + "once": "^1.4.0" + } + }, + "enhanced-resolve": { + "version": "5.8.3", + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.8.3.tgz", + "integrity": "sha512-EGAbGvH7j7Xt2nc0E7D99La1OiEs8LnyimkRgwExpUMScN6O+3x9tIWs7PLQZVNx4YD+00skHXPXi1yQHpAmZA==", + "dev": true, + "requires": { + "graceful-fs": "^4.2.4", + "tapable": "^2.2.0" + } + }, + "entities": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-2.2.0.tgz", + "integrity": "sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A==", + "dev": true + }, + "envinfo": { + "version": "7.8.1", + "resolved": "https://registry.npmjs.org/envinfo/-/envinfo-7.8.1.tgz", + "integrity": "sha512-/o+BXHmB7ocbHEAs6F2EnG0ogybVVUdkRunTT2glZU9XAaGmhqskrvKwqXuDfNjEO0LZKWdejEEpnq8aM0tOaw==", + "dev": true + }, + "es-abstract": { + "version": "1.19.1", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.19.1.tgz", + "integrity": "sha512-2vJ6tjA/UfqLm2MPs7jxVybLoB8i1t1Jd9R3kISld20sIxPcTbLuggQOUxeWeAvIUkduv/CfMjuh4WmiXr2v9w==", + "requires": { + "call-bind": "^1.0.2", + "es-to-primitive": "^1.2.1", + "function-bind": "^1.1.1", + "get-intrinsic": "^1.1.1", + "get-symbol-description": "^1.0.0", + "has": "^1.0.3", + "has-symbols": "^1.0.2", + "internal-slot": "^1.0.3", + "is-callable": "^1.2.4", + "is-negative-zero": "^2.0.1", + "is-regex": "^1.1.4", + "is-shared-array-buffer": "^1.0.1", + "is-string": "^1.0.7", + "is-weakref": "^1.0.1", + "object-inspect": "^1.11.0", + "object-keys": "^1.1.1", + "object.assign": "^4.1.2", + "string.prototype.trimend": "^1.0.4", + "string.prototype.trimstart": "^1.0.4", + "unbox-primitive": "^1.0.1" + } + }, + "es-module-lexer": { + "version": "0.9.3", + "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-0.9.3.tgz", + "integrity": "sha512-1HQ2M2sPtxwnvOvT1ZClHyQDiggdNjURWpY2we6aMKCQiUVxTmVs2UYPLIrD84sS+kMdUwfBSylbJPwNnBrnHQ==", + "dev": true + }, + "es-to-primitive": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz", + "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==", + "requires": { + "is-callable": "^1.1.4", + "is-date-object": "^1.0.1", + "is-symbol": "^1.0.2" + } + }, + "es6-object-assign": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/es6-object-assign/-/es6-object-assign-1.1.0.tgz", + "integrity": "sha1-wsNYJlYkfDnqEHyx5mUrb58kUjw=" + }, + "escalade": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", + "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", + "dev": true + }, + "escape-html": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", + "integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg=", + "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" + } + }, + "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 + }, + "etag": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", + "integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=", + "dev": true + }, + "eventemitter3": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz", + "integrity": "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==", + "dev": true + }, + "events": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", + "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==" + }, + "evp_bytestokey": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/evp_bytestokey/-/evp_bytestokey-1.0.3.tgz", + "integrity": "sha512-/f2Go4TognH/KvCISP7OUsHn85hT9nUkxxA9BEWxFn+Oj9o8ZNLm/40hdlgSLyuOimsrTKLUMEorQexp/aPQeA==", + "requires": { + "md5.js": "^1.3.4", + "safe-buffer": "^5.1.1" + } + }, + "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" + }, + "dependencies": { + "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" + } + }, + "which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "requires": { + "isexe": "^2.0.0" + } + } + } + }, + "express": { + "version": "4.17.2", + "resolved": "https://registry.npmjs.org/express/-/express-4.17.2.tgz", + "integrity": "sha512-oxlxJxcQlYwqPWKVJJtvQiwHgosH/LrLSPA+H4UxpyvSS6jC5aH+5MoHFM+KABgTOt0APue4w66Ha8jCUo9QGg==", + "dev": true, + "requires": { + "accepts": "~1.3.7", + "array-flatten": "1.1.1", + "body-parser": "1.19.1", + "content-disposition": "0.5.4", + "content-type": "~1.0.4", + "cookie": "0.4.1", + "cookie-signature": "1.0.6", + "debug": "2.6.9", + "depd": "~1.1.2", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "finalhandler": "~1.1.2", + "fresh": "0.5.2", + "merge-descriptors": "1.0.1", + "methods": "~1.1.2", + "on-finished": "~2.3.0", + "parseurl": "~1.3.3", + "path-to-regexp": "0.1.7", + "proxy-addr": "~2.0.7", + "qs": "6.9.6", + "range-parser": "~1.2.1", + "safe-buffer": "5.2.1", + "send": "0.17.2", + "serve-static": "1.14.2", + "setprototypeof": "1.2.0", + "statuses": "~1.5.0", + "type-is": "~1.6.18", + "utils-merge": "1.0.1", + "vary": "~1.1.2" + }, + "dependencies": { + "array-flatten": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", + "integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=", + "dev": true + } + } + }, + "extract-zip": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extract-zip/-/extract-zip-2.0.1.tgz", + "integrity": "sha512-GDhU9ntwuKyGXdZBUgTIe+vXnWj0fppUEtMDL0+idd5Sta8TGpHssn/eusA9mrPr9qNDym6SxAYZjNvCn/9RBg==", + "requires": { + "@types/yauzl": "^2.9.1", + "debug": "^4.1.1", + "get-stream": "^5.1.0", + "yauzl": "^2.10.0" + }, + "dependencies": { + "debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "requires": { + "ms": "2.1.2" + } + }, + "get-stream": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz", + "integrity": "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==", + "requires": { + "pump": "^3.0.0" + } + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + } + } + }, + "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.11", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.11.tgz", + "integrity": "sha512-xrO3+1bxSo3ZVHAnqzyuewYT6aMFHRAd4Kcs92MAonjwQZLsK9d0SF1IyQ3k5PoirxTW0Oe/RqFgMQ6TcNE5Ew==", + "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" + } + }, + "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 + }, + "fastest-levenshtein": { + "version": "1.0.14", + "resolved": "https://registry.npmjs.org/fastest-levenshtein/-/fastest-levenshtein-1.0.14.tgz", + "integrity": "sha512-tFfWHjnuUfKE186Tfgr+jtaFc0mZTApEgKDOeyN+FwOqRkO/zK/3h1AiRd8u8CY53owL3CUmGr/oI9p/RdyLTA==", + "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" + } + }, + "faye-websocket": { + "version": "0.11.4", + "resolved": "https://registry.npmjs.org/faye-websocket/-/faye-websocket-0.11.4.tgz", + "integrity": "sha512-CzbClwlXAuiRQAlUyfqPgvPoNKTckTPGfwZV4ZdAhVcP2lh9KUxJg2b5GkE7XbjKQ3YJnQ9z6D9ntLAlB+tP8g==", + "dev": true, + "requires": { + "websocket-driver": ">=0.5.1" + } + }, + "fd-slicer": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/fd-slicer/-/fd-slicer-1.1.0.tgz", + "integrity": "sha512-cE1qsB/VwyQozZ+q1dGxR8LBYNZeofhEdUNGSMbQD3Gw2lAzX9Zb3uIU6Ebc/Fmyjo9AWWfnn0AUCHqtevs/8g==", + "requires": { + "pend": "~1.2.0" + } + }, + "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" + } + }, + "filter-obj": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/filter-obj/-/filter-obj-2.0.2.tgz", + "integrity": "sha512-lO3ttPjHZRfjMcxWKb1j1eDhTFsu4meeR3lnMcnBFhk6RuLhvEiuALu2TlfL310ph4lCYYwgF/ElIjdP739tdg==" + }, + "finalhandler": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.2.tgz", + "integrity": "sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA==", + "dev": true, + "requires": { + "debug": "2.6.9", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "on-finished": "~2.3.0", + "parseurl": "~1.3.3", + "statuses": "~1.5.0", + "unpipe": "~1.0.0" + } + }, + "find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "requires": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + } + }, + "follow-redirects": { + "version": "1.14.7", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.14.7.tgz", + "integrity": "sha512-+hbxoLbFMbRKDwohX8GkTataGqO6Jb7jGwpAlwgy2bIz25XtRm7KEzJM76R1WiNT5SwZkX4Y75SwBolkpmE7iQ==", + "dev": true + }, + "foreach": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/foreach/-/foreach-2.0.5.tgz", + "integrity": "sha1-C+4AUBiusmDQo6865ljdATbsG5k=" + }, + "forwarded": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", + "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", + "dev": true + }, + "fresh": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", + "integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac=", + "dev": true + }, + "fs-constants": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz", + "integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==" + }, + "fs-monkey": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/fs-monkey/-/fs-monkey-1.0.3.tgz", + "integrity": "sha512-cybjIfiiE+pTWicSCLFHSrXZ6EilF30oh91FDP9S2B051prEa7QWfrVTQm10/dDpswBDXZugPa1Ogu8Yh+HV0Q==", + "dev": true + }, + "fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=" + }, + "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==" + }, + "get-intrinsic": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.1.1.tgz", + "integrity": "sha512-kWZrnVM42QCiEA2Ig1bG8zjoIMOgxWwYCEeNdwY6Tv/cOSeGpcoX4pXHfKUxNKVoArnrEr2e9srnAxxGIraS9Q==", + "requires": { + "function-bind": "^1.1.1", + "has": "^1.0.3", + "has-symbols": "^1.0.1" + } + }, + "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 + }, + "get-symbol-description": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.0.0.tgz", + "integrity": "sha512-2EmdH1YvIQiZpltCNgkuiUnyukzxM/R6NDJX31Ke3BG1Nq5b0S2PhX59UKi9vZpPDQVdqn+1IcaAwnzTT5vCjw==", + "requires": { + "call-bind": "^1.0.2", + "get-intrinsic": "^1.1.1" + } + }, + "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" + } + }, + "glob-to-regexp": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz", + "integrity": "sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==", + "dev": true + }, + "graceful-fs": { + "version": "4.2.9", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.9.tgz", + "integrity": "sha512-NtNxqUcXgpW2iMrfqSfR73Glt39K+BLwWsPs94yR63v45T0Wbej7eRmL5cWfwEgqXnmjQp3zaJTshdRW/qC2ZQ==", + "dev": true + }, + "handle-thing": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/handle-thing/-/handle-thing-2.0.1.tgz", + "integrity": "sha512-9Qn4yBxelxoh2Ow62nP+Ka/kMnOXRi8BXnRaUwezLNhqelnN49xKz4F/dPP8OYLxLxq6JDtZb2i9XznUQbNPTg==", + "dev": true + }, + "has": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", + "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", + "requires": { + "function-bind": "^1.1.1" + } + }, + "has-bigints": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.0.1.tgz", + "integrity": "sha512-LSBS2LjbNBTf6287JEbEzvJgftkF5qFkmCo9hDRpAzKhUOlJ+hx8dd4USs00SgsUNwc4617J9ki5YtEClM2ffA==" + }, + "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 + }, + "has-symbols": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.2.tgz", + "integrity": "sha512-chXa79rL/UC2KlX17jo3vRGz0azaWEx5tGqZg5pO3NUyEJVB17dMruQlzCCOfUvElghKcm5194+BCRvi2Rv/Gw==" + }, + "has-tostringtag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.0.tgz", + "integrity": "sha512-kFjcSNhnlGV1kyoGk7OXKSawH5JOb/LzUc5w9B02hOTO0dfFRjbHQKvg1d6cf3HbeUmtU9VbbV3qzZ2Teh97WQ==", + "requires": { + "has-symbols": "^1.0.2" + } + }, + "hash-base": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/hash-base/-/hash-base-3.1.0.tgz", + "integrity": "sha512-1nmYp/rhMDiE7AYkDw+lLwlAzz0AntGIe51F3RfFfEqyQ3feY2eI/NcwC6umIQVOASPMsWJLJScWKSSvzL9IVA==", + "requires": { + "inherits": "^2.0.4", + "readable-stream": "^3.6.0", + "safe-buffer": "^5.2.0" + } + }, + "hash.js": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/hash.js/-/hash.js-1.1.7.tgz", + "integrity": "sha512-taOaskGt4z4SOANNseOviYDvjEJinIkRgmp7LbKP2YTTmVxWBl87s/uzK9r+44BclBSp2X7K1hqeNfz9JbBeXA==", + "requires": { + "inherits": "^2.0.3", + "minimalistic-assert": "^1.0.1" + } + }, + "he": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", + "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", + "dev": true + }, + "hmac-drbg": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/hmac-drbg/-/hmac-drbg-1.0.1.tgz", + "integrity": "sha1-0nRXAQJabHdabFRXk+1QL8DGSaE=", + "requires": { + "hash.js": "^1.0.3", + "minimalistic-assert": "^1.0.0", + "minimalistic-crypto-utils": "^1.0.1" + } + }, + "hpack.js": { + "version": "2.1.6", + "resolved": "https://registry.npmjs.org/hpack.js/-/hpack.js-2.1.6.tgz", + "integrity": "sha1-h3dMCUnlE/QuhFdbPEVoH63ioLI=", + "dev": true, + "requires": { + "inherits": "^2.0.1", + "obuf": "^1.0.0", + "readable-stream": "^2.0.1", + "wbuf": "^1.1.0" + }, + "dependencies": { + "readable-stream": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", + "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", + "dev": true, + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "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 + } + } + }, + "html-entities": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/html-entities/-/html-entities-2.3.2.tgz", + "integrity": "sha512-c3Ab/url5ksaT0WyleslpBEthOzWhrjQbg75y7XUsfSzi3Dgzt0l8w5e7DylRn15MTlMMD58dTfzddNS2kcAjQ==", + "dev": true + }, + "html-minifier-terser": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/html-minifier-terser/-/html-minifier-terser-6.1.0.tgz", + "integrity": "sha512-YXxSlJBZTP7RS3tWnQw74ooKa6L9b9i9QYXY21eUEvhZ3u9XLfv6OnFsQq6RxkhHygsaUMvYsZRV5rU/OVNZxw==", + "dev": true, + "requires": { + "camel-case": "^4.1.2", + "clean-css": "^5.2.2", + "commander": "^8.3.0", + "he": "^1.2.0", + "param-case": "^3.0.4", + "relateurl": "^0.2.7", + "terser": "^5.10.0" + } + }, + "html-webpack-plugin": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/html-webpack-plugin/-/html-webpack-plugin-5.5.0.tgz", + "integrity": "sha512-sy88PC2cRTVxvETRgUHFrL4No3UxvcH8G1NepGhqaTT+GXN2kTamqasot0inS5hXeg1cMbFDt27zzo9p35lZVw==", + "dev": true, + "requires": { + "@types/html-minifier-terser": "^6.0.0", + "html-minifier-terser": "^6.0.2", + "lodash": "^4.17.21", + "pretty-error": "^4.0.0", + "tapable": "^2.0.0" + } + }, + "htmlparser2": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-6.1.0.tgz", + "integrity": "sha512-gyyPk6rgonLFEDGoeRgQNaEUvdJ4ktTmmUh/h2t7s+M8oPpIPxgNACWa+6ESR57kXstwqPiCut0V8NRpcwgU7A==", + "dev": true, + "requires": { + "domelementtype": "^2.0.1", + "domhandler": "^4.0.0", + "domutils": "^2.5.2", + "entities": "^2.0.0" + } + }, + "http-deceiver": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/http-deceiver/-/http-deceiver-1.2.7.tgz", + "integrity": "sha1-+nFolEq5pRnTN8sL7HKE3D5yPYc=", + "dev": true + }, + "http-errors": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.8.1.tgz", + "integrity": "sha512-Kpk9Sm7NmI+RHhnj6OIWDI1d6fIoFAtFt9RLaTMRlg/8w49juAStsrBgp0Dp4OdxdVbRIeKhtCUvoi/RuAhO4g==", + "dev": true, + "requires": { + "depd": "~1.1.2", + "inherits": "2.0.4", + "setprototypeof": "1.2.0", + "statuses": ">= 1.5.0 < 2", + "toidentifier": "1.0.1" + } + }, + "http-parser-js": { + "version": "0.5.5", + "resolved": "https://registry.npmjs.org/http-parser-js/-/http-parser-js-0.5.5.tgz", + "integrity": "sha512-x+JVEkO2PoM8qqpbPbOL3cqHPwerep7OwzK7Ay+sMQjKzaKCqWvjoXm5tqMP9tXWWTnTzAjIhXg+J99XYuPhPA==", + "dev": true + }, + "http-proxy": { + "version": "1.18.1", + "resolved": "https://registry.npmjs.org/http-proxy/-/http-proxy-1.18.1.tgz", + "integrity": "sha512-7mz/721AbnJwIVbnaSv1Cz3Am0ZLT/UBwkC92VlxhXv/k/BBQfM2fXElQNC27BVGr0uwUpplYPQM9LnaBMR5NQ==", + "dev": true, + "requires": { + "eventemitter3": "^4.0.0", + "follow-redirects": "^1.0.0", + "requires-port": "^1.0.0" + } + }, + "http-proxy-middleware": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/http-proxy-middleware/-/http-proxy-middleware-2.0.2.tgz", + "integrity": "sha512-XtmDN5w+vdFTBZaYhdJAbMqn0DP/EhkUaAeo963mojwpKMMbw6nivtFKw07D7DDOH745L5k0VL0P8KRYNEVF/g==", + "dev": true, + "requires": { + "@types/http-proxy": "^1.17.8", + "http-proxy": "^1.18.1", + "is-glob": "^4.0.1", + "is-plain-obj": "^3.0.0", + "micromatch": "^4.0.2" + } + }, + "https-browserify": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/https-browserify/-/https-browserify-1.0.0.tgz", + "integrity": "sha1-7AbBDgo0wPL68Zn3/X/Hj//QPHM=" + }, + "https-proxy-agent": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz", + "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==", + "requires": { + "agent-base": "6", + "debug": "4" + }, + "dependencies": { + "debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "requires": { + "ms": "2.1.2" + } + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + } + } + }, + "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 + }, + "iconv-lite": { + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "dev": true, + "requires": { + "safer-buffer": ">= 2.1.2 < 3" + } + }, + "ieee754": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", + "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==" + }, + "ignore": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.0.tgz", + "integrity": "sha512-CmxgYGiEPCLhfLnpPp1MoRmifwEIOgjcHXxOBjv7mY96c+eWScsOP9c112ZyLdWHi0FxHjI+4uVhKYp/gcdRmQ==", + "dev": true + }, + "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" + } + }, + "indent-string": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz", + "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==", + "dev": true + }, + "inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", + "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==" + }, + "internal-slot": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.3.tgz", + "integrity": "sha512-O0DB1JC/sPyZl7cIo78n5dR7eUSwwpYPiXRhTzNxZVAMUuB8vlnRFyLxdrVToks6XPLVnFfbzaVd5WLjhgg+vA==", + "requires": { + "get-intrinsic": "^1.1.0", + "has": "^1.0.3", + "side-channel": "^1.0.4" + } + }, + "interpret": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/interpret/-/interpret-2.2.0.tgz", + "integrity": "sha512-Ju0Bz/cEia55xDwUWEa8+olFpCiQoypjnQySseKtmjNrnps3P+xfpUmGr90T7yjlVJmOtybRvPXhKMbHr+fWnw==", + "dev": true + }, + "ip": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/ip/-/ip-1.1.5.tgz", + "integrity": "sha1-vd7XARQpCCjAoDnnLvJfWq7ENUo=", + "dev": true + }, + "ipaddr.js": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-2.0.1.tgz", + "integrity": "sha512-1qTgH9NG+IIJ4yfKs2e6Pp1bZg8wbDbKHT21HrLIeYBTRLgMYKnMTPAuI3Lcs61nfx5h1xlXnbJtH1kX5/d/ng==", + "dev": true + }, + "is-arguments": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-arguments/-/is-arguments-1.1.1.tgz", + "integrity": "sha512-8Q7EARjzEnKpt/PCD7e1cgUS0a6X8u5tdSiMqXhojOdoV9TsMsiO+9VLC5vAmO8N7/GmXn7yjR8qnA6bVAEzfA==", + "requires": { + "call-bind": "^1.0.2", + "has-tostringtag": "^1.0.0" + } + }, + "is-bigint": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.0.4.tgz", + "integrity": "sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg==", + "requires": { + "has-bigints": "^1.0.1" + } + }, + "is-binary-path": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", + "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", + "dev": true, + "requires": { + "binary-extensions": "^2.0.0" + } + }, + "is-boolean-object": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.1.2.tgz", + "integrity": "sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA==", + "requires": { + "call-bind": "^1.0.2", + "has-tostringtag": "^1.0.0" + } + }, + "is-callable": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.4.tgz", + "integrity": "sha512-nsuwtxZfMX67Oryl9LCQ+upnC0Z0BgpwntpS89m1H/TLF0zNfzfLMV/9Wa/6MZsj0acpEjAO0KF1xT6ZdLl95w==" + }, + "is-core-module": { + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.9.0.tgz", + "integrity": "sha512-+5FPy5PnwmO3lvfMb0AsoPaBG+5KHUI0wYFXOtYPnVVVspTFUuMZNfNaNVRt3FZadstu2c8x23vykRW/NBoU6A==", + "dev": true, + "requires": { + "has": "^1.0.3" + } + }, + "is-date-object": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.5.tgz", + "integrity": "sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ==", + "requires": { + "has-tostringtag": "^1.0.0" + } + }, + "is-docker": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-2.2.1.tgz", + "integrity": "sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==", + "dev": true + }, + "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-generator-function": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/is-generator-function/-/is-generator-function-1.0.10.tgz", + "integrity": "sha512-jsEjy9l3yiXEQ+PsXdmBwEPcOxaXWLspKdplFUVI9vq1iZgIekeC0L167qeu86czQaxed3q/Uzuw0swL0irL8A==", + "requires": { + "has-tostringtag": "^1.0.0" + } + }, + "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-nan": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/is-nan/-/is-nan-1.3.2.tgz", + "integrity": "sha512-E+zBKpQ2t6MEo1VsonYmluk9NxGrbzpeeLC2xIViuO2EjU2xsXsBPwTr3Ykv9l08UYEVEdWeRZNouaZqF6RN0w==", + "requires": { + "call-bind": "^1.0.0", + "define-properties": "^1.1.3" + } + }, + "is-negative-zero": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.2.tgz", + "integrity": "sha512-dqJvarLawXsFbNDeJW7zAz8ItJ9cd28YufuuFzh0G8pNHjJMnY08Dv7sYX2uF5UpQOwieAeOExEYAWWfu7ZZUA==" + }, + "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-number-object": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.0.6.tgz", + "integrity": "sha512-bEVOqiRcvo3zO1+G2lVMy+gkkEm9Yh7cDMRusKKu5ZJKPUYSJwICTKZrNKHA2EbSP0Tu0+6B/emsYNHZyn6K8g==", + "requires": { + "has-tostringtag": "^1.0.0" + } + }, + "is-path-cwd": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/is-path-cwd/-/is-path-cwd-2.2.0.tgz", + "integrity": "sha512-w942bTcih8fdJPJmQHFzkS76NEP8Kzzvmw92cXsazb8intwLqPibPPdXf4ANdKV3rYMuuQYGIWtvz9JilB3NFQ==", + "dev": true + }, + "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 + }, + "is-plain-obj": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-3.0.0.tgz", + "integrity": "sha512-gwsOE28k+23GP1B6vFl1oVh/WOzmawBrKwo5Ev6wMKzPkaXaCDIQKzLnvsA42DRlbVTWorkgTKIviAKCWkfUwA==", + "dev": true + }, + "is-plain-object": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz", + "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==", + "dev": true, + "requires": { + "isobject": "^3.0.1" + } + }, + "is-regex": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.4.tgz", + "integrity": "sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==", + "requires": { + "call-bind": "^1.0.2", + "has-tostringtag": "^1.0.0" + } + }, + "is-shared-array-buffer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.1.tgz", + "integrity": "sha512-IU0NmyknYZN0rChcKhRO1X8LYz5Isj/Fsqh8NJOSf+N/hCOTwy29F32Ik7a+QszE63IdvmwdTPDd6cZ5pg4cwA==" + }, + "is-stream": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", + "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", + "dev": true + }, + "is-string": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.7.tgz", + "integrity": "sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg==", + "requires": { + "has-tostringtag": "^1.0.0" + } + }, + "is-symbol": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.4.tgz", + "integrity": "sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg==", + "requires": { + "has-symbols": "^1.0.2" + } + }, + "is-typed-array": { + "version": "1.1.8", + "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.8.tgz", + "integrity": "sha512-HqH41TNZq2fgtGT8WHVFVJhBVGuY3AnP3Q36K8JKXUxSxRgk/d+7NjmwG2vo2mYmXK8UYZKu0qH8bVP5gEisjA==", + "requires": { + "available-typed-arrays": "^1.0.5", + "call-bind": "^1.0.2", + "es-abstract": "^1.18.5", + "foreach": "^2.0.5", + "has-tostringtag": "^1.0.0" + } + }, + "is-weakref": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-weakref/-/is-weakref-1.0.2.tgz", + "integrity": "sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ==", + "requires": { + "call-bind": "^1.0.2" + } + }, + "is-wsl": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-2.2.0.tgz", + "integrity": "sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==", + "dev": true, + "requires": { + "is-docker": "^2.0.0" + } + }, + "isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", + "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 + }, + "isobject": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", + "integrity": "sha512-WhB9zCku7EGTj/HQQRz5aUQEUeoQZH2bWcltRErOpymJ4boYE6wL9Tbr23krRPSZ+C5zqNSrSw+Cc7sZZ4b7vg==", + "dev": true + }, + "jest-worker": { + "version": "27.4.6", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-27.4.6.tgz", + "integrity": "sha512-gHWJF/6Xi5CTG5QCvROr6GcmpIqNYpDJyc8A1h/DyXqH1tD6SnRCM0d3U5msV31D2LB/U+E0M+W4oyvKV44oNw==", + "dev": true, + "requires": { + "@types/node": "*", + "merge-stream": "^2.0.0", + "supports-color": "^8.0.0" + }, + "dependencies": { + "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" + } + } + } + }, + "json-parse-better-errors": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz", + "integrity": "sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==", + "dev": true + }, + "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 + }, + "jssha": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/jssha/-/jssha-3.2.0.tgz", + "integrity": "sha512-QuruyBENDWdN4tZwJbQq7/eAK85FqrI4oDbXjy5IBhYD+2pTJyBUWZe8ctWaCkrV0gy6AaelgOZZBMeswEa/6Q==" + }, + "kind-of": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", + "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", + "dev": true + }, + "loader-runner": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-4.2.0.tgz", + "integrity": "sha512-92+huvxMvYlMzMt0iIOukcwYBFpkYJdpl2xsZ7LrlayO7E8SOv+JJUEK17B/dJIHAOLMfh2dZZ/Y18WgmGtYNw==", + "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==", + "requires": { + "p-locate": "^4.1.0" + } + }, + "lodash": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", + "dev": true + }, + "lower-case": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/lower-case/-/lower-case-2.0.2.tgz", + "integrity": "sha512-7fm3l3NAF9WfN6W3JOmf5drwpVqX78JtoGJ3A6W0a6ZnldM41w2fV5D490psKFTpMds8TJse/eHLFFsNHHjHgg==", + "dev": true, + "requires": { + "tslib": "^2.0.3" + } + }, + "md5.js": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/md5.js/-/md5.js-1.3.5.tgz", + "integrity": "sha512-xitP+WxNPcTTOgnTJcrhM0xvdPepipPSf3I8EIpGKeFLjt3PlJLIDG3u8EX53ZIubkb+5U2+3rELYpEhHhzdkg==", + "requires": { + "hash-base": "^3.0.0", + "inherits": "^2.0.1", + "safe-buffer": "^5.1.2" + } + }, + "media-typer": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", + "integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=", + "dev": true + }, + "memfs": { + "version": "3.4.1", + "resolved": "https://registry.npmjs.org/memfs/-/memfs-3.4.1.tgz", + "integrity": "sha512-1c9VPVvW5P7I85c35zAdEr1TD5+F11IToIHIlrVIcflfnzPkJa0ZoYEoEdYDP8KgPFoSZ/opDrUsAoZWym3mtw==", + "dev": true, + "requires": { + "fs-monkey": "1.0.3" + } + }, + "merge-descriptors": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", + "integrity": "sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E=", + "dev": true + }, + "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": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", + "dev": true + }, + "methods": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", + "integrity": "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4=", + "dev": true + }, + "micromatch": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.4.tgz", + "integrity": "sha512-pRmzw/XUcwXGpD9aI9q/0XOwLNygjETJ8y0ao0wdqprrzDa4YnxLcz7fQRZr8voh8V10kGhABbNcHVk5wHgWwg==", + "dev": true, + "requires": { + "braces": "^3.0.1", + "picomatch": "^2.2.3" + } + }, + "miller-rabin": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/miller-rabin/-/miller-rabin-4.0.1.tgz", + "integrity": "sha512-115fLhvZVqWwHPbClyntxEVfVDfl9DLLTuJvq3g2O/Oxi8AiNouAHvDSzHS0viUJc+V5vm3eq91Xwqn9dp4jRA==", + "requires": { + "bn.js": "^4.0.0", + "brorand": "^1.0.1" + }, + "dependencies": { + "bn.js": { + "version": "4.12.0", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", + "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==" + } + } + }, + "mime": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", + "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", + "dev": true + }, + "mime-db": { + "version": "1.51.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.51.0.tgz", + "integrity": "sha512-5y8A56jg7XVQx2mbv1lu49NR4dokRnhZYTtL+KGfaa27uq4pSTXkwQkFJl4pkRMyNFz/EtYDSkiiEHx3F7UN6g==", + "dev": true + }, + "mime-types": { + "version": "2.1.34", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.34.tgz", + "integrity": "sha512-6cP692WwGIs9XXdOO4++N+7qjqv0rqxxVvJ3VHPh/Sc9mVZcQP+ZGhkKiTvWMQRr2tbHkJP/Yn7Y0npb3ZBs4A==", + "dev": true, + "requires": { + "mime-db": "1.51.0" + } + }, + "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 + }, + "minimalistic-assert": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz", + "integrity": "sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==" + }, + "minimalistic-crypto-utils": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz", + "integrity": "sha1-9sAMHAsIIkblxNmd+4x8CDsrWCo=" + }, + "minimatch": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", + "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "requires": { + "brace-expansion": "^1.1.7" + } + }, + "minimist": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", + "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==", + "dev": true + }, + "mkdirp": { + "version": "0.5.5", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz", + "integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==", + "dev": true, + "requires": { + "minimist": "^1.2.5" + } + }, + "mkdirp-classic": { + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz", + "integrity": "sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==" + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "dev": true + }, + "multicast-dns": { + "version": "6.2.3", + "resolved": "https://registry.npmjs.org/multicast-dns/-/multicast-dns-6.2.3.tgz", + "integrity": "sha512-ji6J5enbMyGRHIAkAOu3WdV8nggqviKCEKtXcOqfphZZtQrmHKycfynJ2V7eVPUA4NhJ6V7Wf4TmGbTwKE9B6g==", + "dev": true, + "requires": { + "dns-packet": "^1.3.1", + "thunky": "^1.0.2" + } + }, + "multicast-dns-service-types": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/multicast-dns-service-types/-/multicast-dns-service-types-1.1.0.tgz", + "integrity": "sha1-iZ8R2WhuXgXLkbNdXw5jt3PPyQE=", + "dev": true + }, + "nanoassert": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/nanoassert/-/nanoassert-2.0.0.tgz", + "integrity": "sha512-7vO7n28+aYO4J+8w96AzhmU8G+Y/xpPDJz/se19ICsqj/momRbb9mh9ZUtkoJ5X3nTnPdhEJyc0qnM6yAsHBaA==" + }, + "negotiator": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", + "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==", + "dev": true + }, + "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 + }, + "no-case": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/no-case/-/no-case-3.0.4.tgz", + "integrity": "sha512-fgAN3jGAh+RoxUGZHTSOLJIqUc2wmoBwGR4tbpNAKmmovFoWq0OdRkb0VkldReO2a2iBT/OEulG9XSUc10r3zg==", + "dev": true, + "requires": { + "lower-case": "^2.0.2", + "tslib": "^2.0.3" + } + }, + "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" + } + }, + "node-forge": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-1.2.1.tgz", + "integrity": "sha512-Fcvtbb+zBcZXbTTVwqGA5W+MKBj56UjVRevvchv5XrcyXbmNdesfZL37nlcWOfpgHhgmxApw3tQbTr4CqNmX4w==", + "dev": true + }, + "node-polyfill-webpack-plugin": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/node-polyfill-webpack-plugin/-/node-polyfill-webpack-plugin-1.1.4.tgz", + "integrity": "sha512-Z0XTKj1wRWO8o/Vjobsw5iOJCN+Sua3EZEUc2Ziy9CyVvmHKu6o+t4gUH9GOE0czyPR94LI6ZCV/PpcM8b5yow==", + "requires": { + "assert": "^2.0.0", + "browserify-zlib": "^0.2.0", + "buffer": "^6.0.3", + "console-browserify": "^1.2.0", + "constants-browserify": "^1.0.0", + "crypto-browserify": "^3.12.0", + "domain-browser": "^4.19.0", + "events": "^3.3.0", + "filter-obj": "^2.0.2", + "https-browserify": "^1.0.0", + "os-browserify": "^0.3.0", + "path-browserify": "^1.0.1", + "process": "^0.11.10", + "punycode": "^2.1.1", + "querystring-es3": "^0.2.1", + "readable-stream": "^3.6.0", + "stream-browserify": "^3.0.0", + "stream-http": "^3.2.0", + "string_decoder": "^1.3.0", + "timers-browserify": "^2.0.12", + "tty-browserify": "^0.0.1", + "url": "^0.11.0", + "util": "^0.12.4", + "vm-browserify": "^1.1.2" + }, + "dependencies": { + "string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "requires": { + "safe-buffer": "~5.2.0" + } + } + } + }, + "node-releases": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.1.tgz", + "integrity": "sha512-CqyzN6z7Q6aMeF/ktcMVTzhAHCEpf8SOarwpzpf8pNBY2k5/oM34UHldUwp8VKI7uxct2HxSRdJjBaZeESzcxA==", + "dev": true + }, + "normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "dev": true + }, + "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": { + "path-key": "^3.0.0" + } + }, + "nth-check": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.0.1.tgz", + "integrity": "sha512-it1vE95zF6dTT9lBsYbxvqh0Soy4SPowchj0UBGj/V6cTPnXXtQOPUbhZ6CmGzAD/rW22LQK6E96pcdJXk4A4w==", + "dev": true, + "requires": { + "boolbase": "^1.0.0" + } + }, + "object-inspect": { + "version": "1.12.0", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.0.tgz", + "integrity": "sha512-Ho2z80bVIvJloH+YzRmpZVQe87+qASmBUKZDWgx9cu+KDrX2ZDH/3tMy+gXbZETVGs2M8YdxObOh7XAtim9Y0g==" + }, + "object-is": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/object-is/-/object-is-1.1.5.tgz", + "integrity": "sha512-3cyDsyHgtmi7I7DfSSI2LDp6SK2lwvtbg0p0R1e0RvTqF5ceGx+K2dfSjm1bKDMVCFEDAQvy+o8c6a7VujOddw==", + "requires": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.3" + } + }, + "object-keys": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", + "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==" + }, + "object.assign": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.2.tgz", + "integrity": "sha512-ixT2L5THXsApyiUPYKmW+2EHpXXe5Ii3M+f4e+aJFAHao5amFRW6J0OO6c/LU8Be47utCx2GL89hxGB6XSmKuQ==", + "requires": { + "call-bind": "^1.0.0", + "define-properties": "^1.1.3", + "has-symbols": "^1.0.1", + "object-keys": "^1.1.1" + } + }, + "obuf": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/obuf/-/obuf-1.1.2.tgz", + "integrity": "sha512-PX1wu0AmAdPqOL1mWhqmlOd8kOIZQwGZw6rh7uby9fTc5lhaOWFLX3I6R1hrF9k3zUY40e6igsLGkDXK92LJNg==", + "dev": true + }, + "on-finished": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", + "integrity": "sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=", + "dev": true, + "requires": { + "ee-first": "1.1.1" + } + }, + "on-headers": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.0.2.tgz", + "integrity": "sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA==", + "dev": true + }, + "once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", + "requires": { + "wrappy": "1" + } + }, + "onetime": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", + "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", + "dev": true, + "requires": { + "mimic-fn": "^2.1.0" + } + }, + "open": { + "version": "8.4.0", + "resolved": "https://registry.npmjs.org/open/-/open-8.4.0.tgz", + "integrity": "sha512-XgFPPM+B28FtCCgSb9I+s9szOC1vZRSwgWsRUA5ylIxRTgKozqjOCrVOqGsYABPYK5qnfqClxZTFBa8PKt2v6Q==", + "dev": true, + "requires": { + "define-lazy-prop": "^2.0.0", + "is-docker": "^2.1.1", + "is-wsl": "^2.2.0" + } + }, + "os-browserify": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/os-browserify/-/os-browserify-0.3.0.tgz", + "integrity": "sha1-hUNzx/XCMVkU/Jv8a9gjj92h7Cc=" + }, + "p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "requires": { + "p-try": "^2.0.0" + } + }, + "p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "requires": { + "p-limit": "^2.2.0" + } + }, + "p-map": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/p-map/-/p-map-4.0.0.tgz", + "integrity": "sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ==", + "dev": true, + "requires": { + "aggregate-error": "^3.0.0" + } + }, + "p-retry": { + "version": "4.6.1", + "resolved": "https://registry.npmjs.org/p-retry/-/p-retry-4.6.1.tgz", + "integrity": "sha512-e2xXGNhZOZ0lfgR9kL34iGlU8N/KO0xZnQxVEwdeOvpqNDQfdnxIYizvWtK8RglUa3bGqI8g0R/BdfzLMxRkiA==", + "dev": true, + "requires": { + "@types/retry": "^0.12.0", + "retry": "^0.13.1" + }, + "dependencies": { + "retry": { + "version": "0.13.1", + "resolved": "https://registry.npmjs.org/retry/-/retry-0.13.1.tgz", + "integrity": "sha512-XQBQ3I8W1Cge0Seh+6gjj03LbmRFWuoszgK9ooCpwYIrhhoO80pfq4cUkU5DkknwfOfFteRwlZ56PYOGYyFWdg==", + "dev": true + } + } + }, + "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==" + }, + "pako": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz", + "integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==" + }, + "param-case": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/param-case/-/param-case-3.0.4.tgz", + "integrity": "sha512-RXlj7zCYokReqWpOPH9oYivUzLYZ5vAPIfEmCTNViosC78F8F0H9y7T7gG2M39ymgutxF5gcFEsyZQSph9Bp3A==", + "dev": true, + "requires": { + "dot-case": "^3.0.4", + "tslib": "^2.0.3" + } + }, + "parse-asn1": { + "version": "5.1.6", + "resolved": "https://registry.npmjs.org/parse-asn1/-/parse-asn1-5.1.6.tgz", + "integrity": "sha512-RnZRo1EPU6JBnra2vGHj0yhp6ebyjBZpmUCLHWiFhxlzvBCCpAuZ7elsBp1PVAbQN0/04VD/19rfzlBSwLstMw==", + "requires": { + "asn1.js": "^5.2.0", + "browserify-aes": "^1.0.0", + "evp_bytestokey": "^1.0.0", + "pbkdf2": "^3.0.3", + "safe-buffer": "^5.1.1" + } + }, + "parseurl": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", + "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", + "dev": true + }, + "pascal-case": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/pascal-case/-/pascal-case-3.1.2.tgz", + "integrity": "sha512-uWlGT3YSnK9x3BQJaOdcZwrnV6hPpd8jFH1/ucpiLRPh/2zCVJKS19E4GvYHvaCcACn3foXZ0cLB9Wrx1KGe5g==", + "dev": true, + "requires": { + "no-case": "^3.0.4", + "tslib": "^2.0.3" + } + }, + "path-browserify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-browserify/-/path-browserify-1.0.1.tgz", + "integrity": "sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g==" + }, + "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==" + }, + "path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=" + }, + "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 + }, + "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-to-regexp": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", + "integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w=", + "dev": true + }, + "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 + }, + "pbkdf2": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/pbkdf2/-/pbkdf2-3.1.2.tgz", + "integrity": "sha512-iuh7L6jA7JEGu2WxDwtQP1ddOpaJNC4KlDEFfdQajSGgGPNi4OyDc2R7QnbY2bR9QjBVGwgvTdNJZoE7RaxUMA==", + "requires": { + "create-hash": "^1.1.2", + "create-hmac": "^1.1.4", + "ripemd160": "^2.0.1", + "safe-buffer": "^5.0.1", + "sha.js": "^2.4.8" + } + }, + "pend": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz", + "integrity": "sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg==" + }, + "picocolors": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", + "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==", + "dev": true + }, + "picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true + }, + "pkg-dir": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", + "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", + "requires": { + "find-up": "^4.0.0" + } + }, + "portfinder": { + "version": "1.0.28", + "resolved": "https://registry.npmjs.org/portfinder/-/portfinder-1.0.28.tgz", + "integrity": "sha512-Se+2isanIcEqf2XMHjyUKskczxbPH7dQnlMjXX6+dybayyHvAf/TCgyMRlzf/B6QDhAEFOGes0pzRo3by4AbMA==", + "dev": true, + "requires": { + "async": "^2.6.2", + "debug": "^3.1.1", + "mkdirp": "^0.5.5" + }, + "dependencies": { + "debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "dev": true, + "requires": { + "ms": "^2.1.1" + } + }, + "ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true + } + } + }, + "pretty-error": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/pretty-error/-/pretty-error-4.0.0.tgz", + "integrity": "sha512-AoJ5YMAcXKYxKhuJGdcvse+Voc6v1RgnsR3nWcYU7q4t6z0Q6T86sv5Zq8VIRbOWWFpvdGE83LtdSMNd+6Y0xw==", + "dev": true, + "requires": { + "lodash": "^4.17.20", + "renderkid": "^3.0.0" + } + }, + "process": { + "version": "0.11.10", + "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz", + "integrity": "sha1-czIwDoQBYb2j5podHZGn1LwW8YI=" + }, + "process-nextick-args": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", + "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", + "dev": true + }, + "progress": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz", + "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==" + }, + "proxy-addr": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", + "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", + "dev": true, + "requires": { + "forwarded": "0.2.0", + "ipaddr.js": "1.9.1" + }, + "dependencies": { + "ipaddr.js": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", + "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", + "dev": true + } + } + }, + "proxy-from-env": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", + "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==" + }, + "public-encrypt": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/public-encrypt/-/public-encrypt-4.0.3.tgz", + "integrity": "sha512-zVpa8oKZSz5bTMTFClc1fQOnyyEzpl5ozpi1B5YcvBrdohMjH2rfsBtyXcuNuwjsDIXmBYlF2N5FlJYhR29t8Q==", + "requires": { + "bn.js": "^4.1.0", + "browserify-rsa": "^4.0.0", + "create-hash": "^1.1.0", + "parse-asn1": "^5.0.0", + "randombytes": "^2.0.1", + "safe-buffer": "^5.1.2" + }, + "dependencies": { + "bn.js": { + "version": "4.12.0", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", + "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==" + } + } + }, + "pump": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", + "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", + "requires": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } + }, + "punycode": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", + "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==" + }, + "puppeteer-core": { + "version": "15.5.0", + "resolved": "https://registry.npmjs.org/puppeteer-core/-/puppeteer-core-15.5.0.tgz", + "integrity": "sha512-5Q8EmF++MARczJD1JcRehVePlctxGG2TFHSxdCV8NqPOk44/cMySmZw2nETn+lwUOyp0L9afosMFTnT4KgmWgw==", + "requires": { + "cross-fetch": "3.1.5", + "debug": "4.3.4", + "devtools-protocol": "0.0.1019158", + "extract-zip": "2.0.1", + "https-proxy-agent": "5.0.1", + "pkg-dir": "4.2.0", + "progress": "2.0.3", + "proxy-from-env": "1.1.0", + "rimraf": "3.0.2", + "tar-fs": "2.1.1", + "unbzip2-stream": "1.4.3", + "ws": "8.8.0" + }, + "dependencies": { + "debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "requires": { + "ms": "2.1.2" + } + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + }, + "ws": { + "version": "8.8.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.8.0.tgz", + "integrity": "sha512-JDAgSYQ1ksuwqfChJusw1LSJ8BizJ2e/vVu5Lxjq3YvNJNlROv1ui4i+c/kUUrPheBvQl4c5UbERhTwKa6QBJQ==" + } + } + }, + "qs": { + "version": "6.9.6", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.9.6.tgz", + "integrity": "sha512-TIRk4aqYLNoJUbd+g2lEdz5kLWIuTMRagAXxl78Q0RiVjAOugHmeKNGdd3cwo/ktpf9aL9epCfFqWDEKysUlLQ==", + "dev": true + }, + "querystring": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/querystring/-/querystring-0.2.0.tgz", + "integrity": "sha1-sgmEkgO7Jd+CDadW50cAWHhSFiA=" + }, + "querystring-es3": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/querystring-es3/-/querystring-es3-0.2.1.tgz", + "integrity": "sha1-nsYfeQSYdXB9aUFFlv2Qek1xHnM=" + }, + "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 + }, + "randombytes": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", + "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", + "requires": { + "safe-buffer": "^5.1.0" + } + }, + "randomfill": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/randomfill/-/randomfill-1.0.4.tgz", + "integrity": "sha512-87lcbR8+MhcWcUiQ+9e+Rwx8MyR2P7qnt15ynUlbm3TU/fjbgz4GsvfSUDTemtCCtVCqb4ZcEFlyPNTh9bBTLw==", + "requires": { + "randombytes": "^2.0.5", + "safe-buffer": "^5.1.0" + } + }, + "range-parser": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", + "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", + "dev": true + }, + "raw-body": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.4.2.tgz", + "integrity": "sha512-RPMAFUJP19WIet/99ngh6Iv8fzAbqum4Li7AD6DtGaW2RpMB/11xDoalPiJMTbu6I3hkbMVkATvZrqb9EEqeeQ==", + "dev": true, + "requires": { + "bytes": "3.1.1", + "http-errors": "1.8.1", + "iconv-lite": "0.4.24", + "unpipe": "1.0.0" + }, + "dependencies": { + "bytes": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.1.tgz", + "integrity": "sha512-dWe4nWO/ruEOY7HkUJ5gFt1DCFV9zPRoJr8pV0/ASQermOZjtq8jMjOprC0Kd10GLN+l7xaUPvxzJFWtxGu8Fg==", + "dev": true + } + } + }, + "readable-stream": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", + "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", + "requires": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + } + }, + "readdirp": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "dev": true, + "requires": { + "picomatch": "^2.2.1" + } + }, + "rechoir": { + "version": "0.7.1", + "resolved": "https://registry.npmjs.org/rechoir/-/rechoir-0.7.1.tgz", + "integrity": "sha512-/njmZ8s1wVeR6pjTZ+0nCnv8SpZNRMT2D1RLOJQESlYFDBvwpTA4KWJpZ+sBJ4+vhjILRcK7JIFdGCdxEAAitg==", + "dev": true, + "requires": { + "resolve": "^1.9.0" + } + }, + "reconnecting-websocket": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/reconnecting-websocket/-/reconnecting-websocket-4.4.0.tgz", + "integrity": "sha512-D2E33ceRPga0NvTDhJmphEgJ7FUYF0v4lr1ki0csq06OdlxKfugGzN0dSkxM/NfqCxYELK4KcaTOUOjTV6Dcng==" + }, + "regexp.prototype.flags": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.4.1.tgz", + "integrity": "sha512-pMR7hBVUUGI7PMA37m2ofIdQCsomVnas+Jn5UPGAHQ+/LlwKm/aTLJHdasmHRzlfeZwHiAOaRSo2rbBDm3nNUQ==", + "dev": true, + "requires": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.3" + } + }, + "relateurl": { + "version": "0.2.7", + "resolved": "https://registry.npmjs.org/relateurl/-/relateurl-0.2.7.tgz", + "integrity": "sha1-VNvzd+UUQKypCkzSdGANP/LYiKk=", + "dev": true + }, + "renderkid": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/renderkid/-/renderkid-3.0.0.tgz", + "integrity": "sha512-q/7VIQA8lmM1hF+jn+sFSPWGlMkSAeNYcPLmDQx2zzuiDfaLrOmumR8iaUKlenFgh0XRPIUeSPlH3A+AW3Z5pg==", + "dev": true, + "requires": { + "css-select": "^4.1.3", + "dom-converter": "^0.2.0", + "htmlparser2": "^6.1.0", + "lodash": "^4.17.21", + "strip-ansi": "^6.0.1" + } + }, + "require-from-string": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", + "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", + "dev": true + }, + "requires-port": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz", + "integrity": "sha1-kl0mAdOaxIXgkc8NpcbmlNw9yv8=", + "dev": true + }, + "resolve": { + "version": "1.22.1", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.1.tgz", + "integrity": "sha512-nBpuuYuY5jFsli/JIs1oldw6fOQCBioohqWZg/2hiaOybXOft4lonv85uDOKXdf8rhyK159cxU5cDcK/NKk8zw==", + "dev": true, + "requires": { + "is-core-module": "^2.9.0", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + } + }, + "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": { + "resolve-from": "^5.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 + }, + "reusify": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", + "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", + "dev": true + }, + "rimraf": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "requires": { + "glob": "^7.1.3" + }, + "dependencies": { + "glob": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz", + "integrity": "sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==", + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + } + } + }, + "ripemd160": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/ripemd160/-/ripemd160-2.0.2.tgz", + "integrity": "sha512-ii4iagi25WusVoiC4B4lq7pbXfAp3D9v5CwfkY33vffw2+pkDjY1D8GaN7spsxvCSx8dkPqOZCEZyfxcmJG2IA==", + "requires": { + "hash-base": "^3.0.0", + "inherits": "^2.0.1" + } + }, + "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": { + "queue-microtask": "^1.2.2" + } + }, + "safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==" + }, + "safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" + }, + "schema-utils": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.1.1.tgz", + "integrity": "sha512-Y5PQxS4ITlC+EahLuXaY86TXfR7Dc5lw294alXOq86JAHCihAIZfqv8nNCWvaEJvaC51uN9hbLGeV0cFBdH+Fw==", + "dev": true, + "requires": { + "@types/json-schema": "^7.0.8", + "ajv": "^6.12.5", + "ajv-keywords": "^3.5.2" + } + }, + "select-hose": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/select-hose/-/select-hose-2.0.0.tgz", + "integrity": "sha1-Yl2GWPhlr0Psliv8N2o3NZpJlMo=", + "dev": true + }, + "selfsigned": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/selfsigned/-/selfsigned-2.0.0.tgz", + "integrity": "sha512-cUdFiCbKoa1mZ6osuJs2uDHrs0k0oprsKveFiiaBKCNq3SYyb5gs2HxhQyDNLCmL51ZZThqi4YNDpCK6GOP1iQ==", + "dev": true, + "requires": { + "node-forge": "^1.2.0" + } + }, + "send": { + "version": "0.17.2", + "resolved": "https://registry.npmjs.org/send/-/send-0.17.2.tgz", + "integrity": "sha512-UJYB6wFSJE3G00nEivR5rgWp8c2xXvJ3OPWPhmuteU0IKj8nKbG3DrjiOmLwpnHGYWAVwA69zmTm++YG0Hmwww==", + "dev": true, + "requires": { + "debug": "2.6.9", + "depd": "~1.1.2", + "destroy": "~1.0.4", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "fresh": "0.5.2", + "http-errors": "1.8.1", + "mime": "1.6.0", + "ms": "2.1.3", + "on-finished": "~2.3.0", + "range-parser": "~1.2.1", + "statuses": "~1.5.0" + }, + "dependencies": { + "ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true + } + } + }, + "serialize-javascript": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.0.tgz", + "integrity": "sha512-Qr3TosvguFt8ePWqsvRfrKyQXIiW+nGbYpy8XK24NQHE83caxWt+mIymTT19DGFbNWNLfEwsrkSmN64lVWB9ag==", + "dev": true, + "requires": { + "randombytes": "^2.1.0" + } + }, + "serve-index": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/serve-index/-/serve-index-1.9.1.tgz", + "integrity": "sha1-03aNabHn2C5c4FD/9bRTvqEqkjk=", + "dev": true, + "requires": { + "accepts": "~1.3.4", + "batch": "0.6.1", + "debug": "2.6.9", + "escape-html": "~1.0.3", + "http-errors": "~1.6.2", + "mime-types": "~2.1.17", + "parseurl": "~1.3.2" + }, + "dependencies": { + "http-errors": { + "version": "1.6.3", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.6.3.tgz", + "integrity": "sha1-i1VoC7S+KDoLW/TqLjhYC+HZMg0=", + "dev": true, + "requires": { + "depd": "~1.1.2", + "inherits": "2.0.3", + "setprototypeof": "1.1.0", + "statuses": ">= 1.4.0 < 2" + } + }, + "inherits": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=", + "dev": true + }, + "setprototypeof": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.0.tgz", + "integrity": "sha512-BvE/TwpZX4FXExxOxZyRGQQv651MSwmWKZGqvmPcRIjDqWub67kTKuIMx43cZZrS/cBBzwBcNDWoFxt2XEFIpQ==", + "dev": true + } + } + }, + "serve-static": { + "version": "1.14.2", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.14.2.tgz", + "integrity": "sha512-+TMNA9AFxUEGuC0z2mevogSnn9MXKb4fa7ngeRMJaaGv8vTwnIEkKi+QGvPt33HSnf8pRS+WGM0EbMtCJLKMBQ==", + "dev": true, + "requires": { + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "parseurl": "~1.3.3", + "send": "0.17.2" + } + }, + "setimmediate": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz", + "integrity": "sha1-KQy7Iy4waULX1+qbg3Mqt4VvgoU=" + }, + "setprototypeof": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", + "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==", + "dev": true + }, + "sha.js": { + "version": "2.4.11", + "resolved": "https://registry.npmjs.org/sha.js/-/sha.js-2.4.11.tgz", + "integrity": "sha512-QMEp5B7cftE7APOjk5Y6xgrbWu+WkLVQwk8JNjZ8nKRciZaByEW6MubieAiToS7+dwvrjGhH8jRXz3MVd0AYqQ==", + "requires": { + "inherits": "^2.0.1", + "safe-buffer": "^5.0.1" + } + }, + "shallow-clone": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/shallow-clone/-/shallow-clone-3.0.1.tgz", + "integrity": "sha512-/6KqX+GVUdqPuPPd2LxDDxzX6CAbjJehAAOKlNpqqUpAqPM6HeL8f+o3a+JsyGjn2lv0WY8UsTgUJjU9Ok55NA==", + "dev": true, + "requires": { + "kind-of": "^6.0.2" + } + }, + "shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "requires": { + "shebang-regex": "^3.0.0" + } + }, + "shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true + }, + "side-channel": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz", + "integrity": "sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==", + "requires": { + "call-bind": "^1.0.0", + "get-intrinsic": "^1.0.2", + "object-inspect": "^1.9.0" + } + }, + "signal-exit": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.6.tgz", + "integrity": "sha512-sDl4qMFpijcGw22U5w63KmD3cZJfBuFlVNbVMKje2keoKML7X2UzWbc4XrmEbDwg0NXJc3yv4/ox7b+JWb57kQ==", + "dev": true + }, + "slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "dev": true + }, + "sockjs": { + "version": "0.3.24", + "resolved": "https://registry.npmjs.org/sockjs/-/sockjs-0.3.24.tgz", + "integrity": "sha512-GJgLTZ7vYb/JtPSSZ10hsOYIvEYsjbNU+zPdIHcUaWVNUEPivzxku31865sSSud0Da0W4lEeOPlmw93zLQchuQ==", + "dev": true, + "requires": { + "faye-websocket": "^0.11.3", + "uuid": "^8.3.2", + "websocket-driver": "^0.7.4" + } + }, + "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 + }, + "source-map-support": { + "version": "0.5.21", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", + "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", + "dev": true, + "requires": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + } + }, + "spdy": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/spdy/-/spdy-4.0.2.tgz", + "integrity": "sha512-r46gZQZQV+Kl9oItvl1JZZqJKGr+oEkB08A6BzkiR7593/7IbtuncXHd2YoYeTsG4157ZssMu9KYvUHLcjcDoA==", + "dev": true, + "requires": { + "debug": "^4.1.0", + "handle-thing": "^2.0.0", + "http-deceiver": "^1.2.7", + "select-hose": "^2.0.0", + "spdy-transport": "^3.0.0" + }, + "dependencies": { + "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" + } + }, + "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 + } + } + }, + "spdy-transport": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/spdy-transport/-/spdy-transport-3.0.0.tgz", + "integrity": "sha512-hsLVFE5SjA6TCisWeJXFKniGGOpBgMLmerfO2aCyCU5s7nJ/rpAepqmFifv/GCbSbueEeAJJnmSQ2rKC/g8Fcw==", + "dev": true, + "requires": { + "debug": "^4.1.0", + "detect-node": "^2.0.4", + "hpack.js": "^2.1.6", + "obuf": "^1.1.2", + "readable-stream": "^3.0.6", + "wbuf": "^1.7.3" + }, + "dependencies": { + "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" + } + }, + "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 + } + } + }, + "statuses": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", + "integrity": "sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow=", + "dev": true + }, + "stream-browserify": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/stream-browserify/-/stream-browserify-3.0.0.tgz", + "integrity": "sha512-H73RAHsVBapbim0tU2JwwOiXUj+fikfiaoYAKHF3VJfA0pe2BCzkhAHBlLG6REzE+2WNZcxOXjK7lkso+9euLA==", + "requires": { + "inherits": "~2.0.4", + "readable-stream": "^3.5.0" + } + }, + "stream-http": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/stream-http/-/stream-http-3.2.0.tgz", + "integrity": "sha512-Oq1bLqisTyK3TSCXpPbT4sdeYNdmyZJv1LxpEm2vu1ZhK89kSE5YXwZc3cWk0MagGaKriBh9mCFbVGtO+vY29A==", + "requires": { + "builtin-status-codes": "^3.0.0", + "inherits": "^2.0.4", + "readable-stream": "^3.6.0", + "xtend": "^4.0.2" + } + }, + "string.prototype.trimend": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.4.tgz", + "integrity": "sha512-y9xCjw1P23Awk8EvTpcyL2NIr1j7wJ39f+k6lvRnSMz+mz9CGz9NYPelDk42kOz6+ql8xjfK8oYzy3jAP5QU5A==", + "requires": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.3" + } + }, + "string.prototype.trimstart": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.4.tgz", + "integrity": "sha512-jh6e984OBfvxS50tdY2nRZnoC5/mLFKOREQfw8t5yytkoUsJRNxvI/E39qu1sD0OtWI3OC0XgKSmcWwziwYuZw==", + "requires": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.3" + } + }, + "string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "requires": { + "safe-buffer": "~5.1.0" + }, + "dependencies": { + "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==" + } + } + }, + "strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "requires": { + "ansi-regex": "^5.0.1" + } + }, + "strip-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 + }, + "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 + }, + "tapable": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.1.tgz", + "integrity": "sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==", + "dev": true + }, + "tar-fs": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-2.1.1.tgz", + "integrity": "sha512-V0r2Y9scmbDRLCNex/+hYzvp/zyYjvFbHPNgVTKfQvVrb6guiE/fxP+XblDNR011utopbkex2nM4dHNV6GDsng==", + "requires": { + "chownr": "^1.1.1", + "mkdirp-classic": "^0.5.2", + "pump": "^3.0.0", + "tar-stream": "^2.1.4" + } + }, + "tar-stream": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-2.2.0.tgz", + "integrity": "sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==", + "requires": { + "bl": "^4.0.3", + "end-of-stream": "^1.4.1", + "fs-constants": "^1.0.0", + "inherits": "^2.0.3", + "readable-stream": "^3.1.1" + } + }, + "terser": { + "version": "5.10.0", + "resolved": "https://registry.npmjs.org/terser/-/terser-5.10.0.tgz", + "integrity": "sha512-AMmF99DMfEDiRJfxfY5jj5wNH/bYO09cniSqhfoyxc8sFoYIgkJy86G04UoZU5VjlpnplVu0K6Tx6E9b5+DlHA==", + "dev": true, + "requires": { + "commander": "^2.20.0", + "source-map": "~0.7.2", + "source-map-support": "~0.5.20" + }, + "dependencies": { + "commander": { + "version": "2.20.3", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", + "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", + "dev": true + }, + "source-map": { + "version": "0.7.3", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.3.tgz", + "integrity": "sha512-CkCj6giN3S+n9qrYiBTX5gystlENnRW5jZeNLHpe6aue+SrHcG5VYwujhW9s4dY31mEGsxBDrHR6oI69fTXsaQ==", + "dev": true + } + } + }, + "terser-webpack-plugin": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-5.3.1.tgz", + "integrity": "sha512-GvlZdT6wPQKbDNW/GDQzZFg/j4vKU96yl2q6mcUkzKOgW4gwf1Z8cZToUCrz31XHlPWH8MVb1r2tFtdDtTGJ7g==", + "dev": true, + "requires": { + "jest-worker": "^27.4.5", + "schema-utils": "^3.1.1", + "serialize-javascript": "^6.0.0", + "source-map": "^0.6.1", + "terser": "^5.7.2" + } + }, + "through": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", + "integrity": "sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==" + }, + "thunky": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/thunky/-/thunky-1.1.0.tgz", + "integrity": "sha512-eHY7nBftgThBqOyHGVN+l8gF0BucP09fMo0oO/Lb0w1OF80dJv+lDVpXG60WMQvkcxAkNybKsrEIE3ZtKGmPrA==", + "dev": true + }, + "timers-browserify": { + "version": "2.0.12", + "resolved": "https://registry.npmjs.org/timers-browserify/-/timers-browserify-2.0.12.tgz", + "integrity": "sha512-9phl76Cqm6FhSX9Xe1ZUAMLtm1BLkKj2Qd5ApyWkXzsMRaA7dgr81kf4wJmQf/hAvg8EEyJxDo3du/0KlhPiKQ==", + "requires": { + "setimmediate": "^1.0.4" + } + }, + "to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "requires": { + "is-number": "^7.0.0" + } + }, + "toidentifier": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", + "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", + "dev": true + }, + "tr46": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", + "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==" + }, + "tslib": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.1.tgz", + "integrity": "sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw==", + "dev": true + }, + "tty-browserify": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/tty-browserify/-/tty-browserify-0.0.1.tgz", + "integrity": "sha512-C3TaO7K81YvjCgQH9Q1S3R3P3BtN3RIM8n+OvX4il1K1zgE8ZhI0op7kClgkxtutIE8hQrcrHBXvIheqKUUCxw==" + }, + "type-is": { + "version": "1.6.18", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", + "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", + "dev": true, + "requires": { + "media-typer": "0.3.0", + "mime-types": "~2.1.24" + } + }, + "unbox-primitive": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.1.tgz", + "integrity": "sha512-tZU/3NqK3dA5gpE1KtyiJUrEB0lxnGkMFHptJ7q6ewdZ8s12QrODwNbhIJStmJkd1QDXa1NRA8aF2A1zk/Ypyw==", + "requires": { + "function-bind": "^1.1.1", + "has-bigints": "^1.0.1", + "has-symbols": "^1.0.2", + "which-boxed-primitive": "^1.0.2" + } + }, + "unbzip2-stream": { + "version": "1.4.3", + "resolved": "https://registry.npmjs.org/unbzip2-stream/-/unbzip2-stream-1.4.3.tgz", + "integrity": "sha512-mlExGW4w71ebDJviH16lQLtZS32VKqsSfk80GCfUlwT/4/hNRFsoscrF/c++9xinkMzECL1uL9DDwXqFWkruPg==", + "requires": { + "buffer": "^5.2.1", + "through": "^2.3.8" + }, + "dependencies": { + "buffer": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", + "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", + "requires": { + "base64-js": "^1.3.1", + "ieee754": "^1.1.13" + } + } + } + }, + "uniqid": { + "version": "5.4.0", + "resolved": "https://registry.npmjs.org/uniqid/-/uniqid-5.4.0.tgz", + "integrity": "sha512-38JRbJ4Fj94VmnC7G/J/5n5SC7Ab46OM5iNtSstB/ko3l1b5g7ALt4qzHFgGciFkyiRNtDXtLNb+VsxtMSE77A==" + }, + "unpipe": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", + "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=", + "dev": true + }, + "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": { + "punycode": "^2.1.0" + } + }, + "url": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/url/-/url-0.11.0.tgz", + "integrity": "sha1-ODjpfPxgUh63PFJajlW/3Z4uKPE=", + "requires": { + "punycode": "1.3.2", + "querystring": "0.2.0" + }, + "dependencies": { + "punycode": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.3.2.tgz", + "integrity": "sha1-llOgNvt8HuQjQvIyXM7v6jkmxI0=" + } + } + }, + "util": { + "version": "0.12.4", + "resolved": "https://registry.npmjs.org/util/-/util-0.12.4.tgz", + "integrity": "sha512-bxZ9qtSlGUWSOy9Qa9Xgk11kSslpuZwaxCg4sNIDj6FLucDab2JxnHwyNTCpHMtK1MjoQiWQ6DiUMZYbSrO+Sw==", + "requires": { + "inherits": "^2.0.3", + "is-arguments": "^1.0.4", + "is-generator-function": "^1.0.7", + "is-typed-array": "^1.1.3", + "safe-buffer": "^5.1.2", + "which-typed-array": "^1.1.2" + } + }, + "util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=" + }, + "utila": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/utila/-/utila-0.4.0.tgz", + "integrity": "sha1-ihagXURWV6Oupe7MWxKk+lN5dyw=", + "dev": true + }, + "utils-merge": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", + "integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM=", + "dev": true + }, + "uuid": { + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", + "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", + "dev": true + }, + "vary": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", + "integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw=", + "dev": true + }, + "vm-browserify": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/vm-browserify/-/vm-browserify-1.1.2.tgz", + "integrity": "sha512-2ham8XPWTONajOR0ohOKOHXkm3+gaBmGut3SRuu75xLd/RRaY6vqgh8NBYYk7+RW3u5AtzPQZG8F10LHkl0lAQ==" + }, + "watchpack": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.3.1.tgz", + "integrity": "sha512-x0t0JuydIo8qCNctdDrn1OzH/qDzk2+rdCOC3YzumZ42fiMqmQ7T3xQurykYMhYfHaPHTp4ZxAx2NfUo1K6QaA==", + "dev": true, + "requires": { + "glob-to-regexp": "^0.4.1", + "graceful-fs": "^4.1.2" + } + }, + "wbuf": { + "version": "1.7.3", + "resolved": "https://registry.npmjs.org/wbuf/-/wbuf-1.7.3.tgz", + "integrity": "sha512-O84QOnr0icsbFGLS0O3bI5FswxzRr8/gHwWkDlQFskhSPryQXvrTMxjxGP4+iWYoauLoBvfDpkrOauZ+0iZpDA==", + "dev": true, + "requires": { + "minimalistic-assert": "^1.0.0" + } + }, + "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==" + }, + "webpack": { + "version": "5.67.0", + "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.67.0.tgz", + "integrity": "sha512-LjFbfMh89xBDpUMgA1W9Ur6Rn/gnr2Cq1jjHFPo4v6a79/ypznSYbAyPgGhwsxBtMIaEmDD1oJoA7BEYw/Fbrw==", + "dev": true, + "requires": { + "@types/eslint-scope": "^3.7.0", + "@types/estree": "^0.0.50", + "@webassemblyjs/ast": "1.11.1", + "@webassemblyjs/wasm-edit": "1.11.1", + "@webassemblyjs/wasm-parser": "1.11.1", + "acorn": "^8.4.1", + "acorn-import-assertions": "^1.7.6", + "browserslist": "^4.14.5", + "chrome-trace-event": "^1.0.2", + "enhanced-resolve": "^5.8.3", + "es-module-lexer": "^0.9.0", + "eslint-scope": "5.1.1", + "events": "^3.2.0", + "glob-to-regexp": "^0.4.1", + "graceful-fs": "^4.2.9", + "json-parse-better-errors": "^1.0.2", + "loader-runner": "^4.2.0", + "mime-types": "^2.1.27", + "neo-async": "^2.6.2", + "schema-utils": "^3.1.0", + "tapable": "^2.1.1", + "terser-webpack-plugin": "^5.1.3", + "watchpack": "^2.3.1", + "webpack-sources": "^3.2.3" + } + }, + "webpack-cli": { + "version": "4.10.0", + "resolved": "https://registry.npmjs.org/webpack-cli/-/webpack-cli-4.10.0.tgz", + "integrity": "sha512-NLhDfH/h4O6UOy+0LSso42xvYypClINuMNBVVzX4vX98TmTaTUxwRbXdhucbFMd2qLaCTcLq/PdYrvi8onw90w==", + "dev": true, + "requires": { + "@discoveryjs/json-ext": "^0.5.0", + "@webpack-cli/configtest": "^1.2.0", + "@webpack-cli/info": "^1.5.0", + "@webpack-cli/serve": "^1.7.0", + "colorette": "^2.0.14", + "commander": "^7.0.0", + "cross-spawn": "^7.0.3", + "fastest-levenshtein": "^1.0.12", + "import-local": "^3.0.2", + "interpret": "^2.2.0", + "rechoir": "^0.7.0", + "webpack-merge": "^5.7.3" + }, + "dependencies": { + "commander": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz", + "integrity": "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==", + "dev": true + } + } + }, + "webpack-dev-middleware": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/webpack-dev-middleware/-/webpack-dev-middleware-5.3.1.tgz", + "integrity": "sha512-81EujCKkyles2wphtdrnPg/QqegC/AtqNH//mQkBYSMqwFVCQrxM6ktB2O/SPlZy7LqeEfTbV3cZARGQz6umhg==", + "dev": true, + "requires": { + "colorette": "^2.0.10", + "memfs": "^3.4.1", + "mime-types": "^2.1.31", + "range-parser": "^1.2.1", + "schema-utils": "^4.0.0" + }, + "dependencies": { + "ajv": { + "version": "8.9.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.9.0.tgz", + "integrity": "sha512-qOKJyNj/h+OWx7s5DePL6Zu1KeM9jPZhwBqs+7DzP6bGOvqzVCSf0xueYmVuaC/oQ/VtS2zLMLHdQFbkka+XDQ==", + "dev": true, + "requires": { + "fast-deep-equal": "^3.1.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2", + "uri-js": "^4.2.2" + } + }, + "ajv-keywords": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-5.1.0.tgz", + "integrity": "sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==", + "dev": true, + "requires": { + "fast-deep-equal": "^3.1.3" + } + }, + "json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "dev": true + }, + "schema-utils": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.0.0.tgz", + "integrity": "sha512-1edyXKgh6XnJsJSQ8mKWXnN/BVaIbFMLpouRUrXgVq7WYne5kw3MW7UPhO44uRXQSIpTSXoJbmrR2X0w9kUTyg==", + "dev": true, + "requires": { + "@types/json-schema": "^7.0.9", + "ajv": "^8.8.0", + "ajv-formats": "^2.1.1", + "ajv-keywords": "^5.0.0" + } + } + } + }, + "webpack-dev-server": { + "version": "4.7.4", + "resolved": "https://registry.npmjs.org/webpack-dev-server/-/webpack-dev-server-4.7.4.tgz", + "integrity": "sha512-nfdsb02Zi2qzkNmgtZjkrMOcXnYZ6FLKcQwpxT7MvmHKc+oTtDsBju8j+NMyAygZ9GW1jMEUpy3itHtqgEhe1A==", + "dev": true, + "requires": { + "@types/bonjour": "^3.5.9", + "@types/connect-history-api-fallback": "^1.3.5", + "@types/express": "^4.17.13", + "@types/serve-index": "^1.9.1", + "@types/sockjs": "^0.3.33", + "@types/ws": "^8.2.2", + "ansi-html-community": "^0.0.8", + "bonjour": "^3.5.0", + "chokidar": "^3.5.3", + "colorette": "^2.0.10", + "compression": "^1.7.4", + "connect-history-api-fallback": "^1.6.0", + "default-gateway": "^6.0.3", + "del": "^6.0.0", + "express": "^4.17.1", + "graceful-fs": "^4.2.6", + "html-entities": "^2.3.2", + "http-proxy-middleware": "^2.0.0", + "ipaddr.js": "^2.0.1", + "open": "^8.0.9", + "p-retry": "^4.5.0", + "portfinder": "^1.0.28", + "schema-utils": "^4.0.0", + "selfsigned": "^2.0.0", + "serve-index": "^1.9.1", + "sockjs": "^0.3.21", + "spdy": "^4.0.2", + "strip-ansi": "^7.0.0", + "webpack-dev-middleware": "^5.3.1", + "ws": "^8.4.2" + }, + "dependencies": { + "ajv": { + "version": "8.9.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.9.0.tgz", + "integrity": "sha512-qOKJyNj/h+OWx7s5DePL6Zu1KeM9jPZhwBqs+7DzP6bGOvqzVCSf0xueYmVuaC/oQ/VtS2zLMLHdQFbkka+XDQ==", + "dev": true, + "requires": { + "fast-deep-equal": "^3.1.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2", + "uri-js": "^4.2.2" + } + }, + "ajv-keywords": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-5.1.0.tgz", + "integrity": "sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==", + "dev": true, + "requires": { + "fast-deep-equal": "^3.1.3" + } + }, + "ansi-regex": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", + "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==", + "dev": true + }, + "json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "dev": true + }, + "schema-utils": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.0.0.tgz", + "integrity": "sha512-1edyXKgh6XnJsJSQ8mKWXnN/BVaIbFMLpouRUrXgVq7WYne5kw3MW7UPhO44uRXQSIpTSXoJbmrR2X0w9kUTyg==", + "dev": true, + "requires": { + "@types/json-schema": "^7.0.9", + "ajv": "^8.8.0", + "ajv-formats": "^2.1.1", + "ajv-keywords": "^5.0.0" + } + }, + "strip-ansi": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.0.1.tgz", + "integrity": "sha512-cXNxvT8dFNRVfhVME3JAe98mkXDYN2O1l7jmcwMnOslDeESg1rF/OZMtK0nRAhiari1unG5cD4jG3rapUAkLbw==", + "dev": true, + "requires": { + "ansi-regex": "^6.0.1" + } + }, + "ws": { + "version": "8.4.2", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.4.2.tgz", + "integrity": "sha512-Kbk4Nxyq7/ZWqr/tarI9yIt/+iNNFOjBXEWgTb4ydaNHBNGgvf2QHbS9fdfsndfjFlFwEd4Al+mw83YkaD10ZA==", + "dev": true + } + } + }, + "webpack-merge": { + "version": "5.8.0", + "resolved": "https://registry.npmjs.org/webpack-merge/-/webpack-merge-5.8.0.tgz", + "integrity": "sha512-/SaI7xY0831XwP6kzuwhKWVKDP9t1QY1h65lAFLbZqMPIuYcD9QAW4u9STIbU9kaJbPBB/geU/gLr1wDjOhQ+Q==", + "dev": true, + "requires": { + "clone-deep": "^4.0.1", + "wildcard": "^2.0.0" + } + }, + "webpack-sources": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-3.2.3.tgz", + "integrity": "sha512-/DyMEOrDgLKKIG0fmvtz+4dUX/3Ghozwgm6iPp8KRhvn+eQf9+Q7GWxVNMk3+uCPWfdXYC4ExGBckIXdFEfH1w==", + "dev": true + }, + "websocket-driver": { + "version": "0.7.4", + "resolved": "https://registry.npmjs.org/websocket-driver/-/websocket-driver-0.7.4.tgz", + "integrity": "sha512-b17KeDIQVjvb0ssuSDF2cYXSg2iztliJ4B9WdsuB6J952qCPKmnVq4DyW5motImXHDC1cBT/1UezrJVsKw5zjg==", + "dev": true, + "requires": { + "http-parser-js": ">=0.5.1", + "safe-buffer": ">=5.1.0", + "websocket-extensions": ">=0.1.1" + } + }, + "websocket-extensions": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/websocket-extensions/-/websocket-extensions-0.1.4.tgz", + "integrity": "sha512-OqedPIGOfsDlo31UNwYbCFMSaO9m9G/0faIHj5/dZFDMFqPTcx6UwqyOy3COEaEOg/9VsGIpdqn62W5KhoKSpg==", + "dev": true + }, + "whatwg-url": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", + "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", + "requires": { + "tr46": "~0.0.3", + "webidl-conversions": "^3.0.0" + } + }, + "which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "requires": { + "isexe": "^2.0.0" + } + }, + "which-boxed-primitive": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz", + "integrity": "sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==", + "requires": { + "is-bigint": "^1.0.1", + "is-boolean-object": "^1.1.0", + "is-number-object": "^1.0.4", + "is-string": "^1.0.5", + "is-symbol": "^1.0.3" + } + }, + "which-typed-array": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.7.tgz", + "integrity": "sha512-vjxaB4nfDqwKI0ws7wZpxIlde1XrLX5uB0ZjpfshgmapJMD7jJWhZI+yToJTqaFByF0eNBcYxbjmCzoRP7CfEw==", + "requires": { + "available-typed-arrays": "^1.0.5", + "call-bind": "^1.0.2", + "es-abstract": "^1.18.5", + "foreach": "^2.0.5", + "has-tostringtag": "^1.0.0", + "is-typed-array": "^1.1.7" + } + }, + "wildcard": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/wildcard/-/wildcard-2.0.0.tgz", + "integrity": "sha512-JcKqAHLPxcdb9KM49dufGXn2x3ssnfjbcaQdLlfZsL9rH9wgDQjUtDxbo8NE0F6SFvydeu1VhZe7hZuHsB2/pw==", + "dev": true + }, + "wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" + }, + "ws": { + "version": "8.4.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.4.0.tgz", + "integrity": "sha512-IHVsKe2pjajSUIl4KYMQOdlyliovpEPquKkqbwswulszzI7r0SfQrxnXdWAEqOlDCLrVSJzo+O1hAwdog2sKSQ==" + }, + "xhr2": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/xhr2/-/xhr2-0.2.1.tgz", + "integrity": "sha512-sID0rrVCqkVNUn8t6xuv9+6FViXjUVXq8H5rWOH2rz9fDNQEd4g0EA2XlcEdJXRz5BMEn4O1pJFdT+z4YHhoWw==" + }, + "xtend": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", + "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==" + }, + "yauzl": { + "version": "2.10.0", + "resolved": "https://registry.npmjs.org/yauzl/-/yauzl-2.10.0.tgz", + "integrity": "sha512-p4a9I6X6nu6IhoGmBqAcbJy1mlC4j27vEPZX9F4L4/vZT3Lyq1VkFHw/V/PUcB9Buo+DG3iHkT0x3Qya58zc3g==", + "requires": { + "buffer-crc32": "~0.2.3", + "fd-slicer": "~1.1.0" + } + } + } +} diff --git a/templates/ctl-scaffold/package.json b/templates/ctl-scaffold/package.json new file mode 100644 index 0000000000..1b3d97053e --- /dev/null +++ b/templates/ctl-scaffold/package.json @@ -0,0 +1,40 @@ +{ + "name": "ctl-scaffold", + "version": "0.1.0", + "description": "", + "main": "index.js", + "directories": { + "test": "test" + }, + "scripts": { + "test": "spago run --main Test.Main", + "dev": "make run-dev", + "build": "make run-build" + }, + "author": "", + "license": "MIT", + "dependencies": { + "@emurgo/cardano-serialization-lib-browser": "11.0.0-beta.1", + "@emurgo/cardano-serialization-lib-nodejs": "11.0.0-beta.1", + "big-integer": "1.6.51", + "blake2b-wasm": "2.4.0", + "bufferutil": "4.0.5", + "jssha": "3.2.0", + "node-polyfill-webpack-plugin": "1.1.4", + "puppeteer-core": "^15.3.2", + "reconnecting-websocket": "4.4.0", + "uniqid": "5.4.0", + "ws": "8.4.0", + "xhr2": "0.2.1" + }, + "devDependencies": { + "buffer": "6.0.3", + "html-webpack-plugin": "5.5.0", + "webpack": "5.67.0", + "webpack-cli": "4.10", + "webpack-dev-server": "4.7.4" + }, + "prettier": { + "arrowParens": "avoid" + } +} diff --git a/templates/ctl-scaffold/packages.dhall b/templates/ctl-scaffold/packages.dhall new file mode 100644 index 0000000000..2f9690c540 --- /dev/null +++ b/templates/ctl-scaffold/packages.dhall @@ -0,0 +1,368 @@ +{- +Welcome to your new Dhall package-set! + +Below are instructions for how to edit this file for most use +cases, so that you don't need to know Dhall to use it. + +## Warning: Don't Move This Top-Level Comment! + +Due to how `dhall format` currently works, this comment's +instructions cannot appear near corresponding sections below +because `dhall format` will delete the comment. However, +it will not delete a top-level comment like this one. + +## Use Cases + +Most will want to do one or both of these options: +1. Override/Patch a package's dependency +2. Add a package not already in the default package set + +This file will continue to work whether you use one or both options. +Instructions for each option are explained below. + +### Overriding/Patching a package + +Purpose: +- Change a package's dependency to a newer/older release than the + default package set's release +- Use your own modified version of some dependency that may + include new API, changed API, removed API by + using your custom git repo of the library rather than + the package set's repo + +Syntax: +where `entityName` is one of the following: +- dependencies +- repo +- version +------------------------------- +let upstream = -- +in upstream + with packageName.entityName = "new value" +------------------------------- + +Example: +------------------------------- +let upstream = -- +in upstream + with halogen.version = "master" + with halogen.repo = "https://example.com/path/to/git/repo.git" + + with halogen-vdom.version = "v4.0.0" +------------------------------- + +### Additions + +Purpose: +- Add packages that aren't already included in the default package set + +Syntax: +where `` is: +- a tag (i.e. "v4.0.0") +- a branch (i.e. "master") +- commit hash (i.e. "701f3e44aafb1a6459281714858fadf2c4c2a977") +------------------------------- +let upstream = -- +in upstream + with new-package-name = + { dependencies = + [ "dependency1" + , "dependency2" + ] + , repo = + "https://example.com/path/to/git/repo.git" + , version = + "" + } +------------------------------- + +Example: +------------------------------- +let upstream = -- +in upstream + with benchotron = + { dependencies = + [ "arrays" + , "exists" + , "profunctor" + , "strings" + , "quickcheck" + , "lcg" + , "transformers" + , "foldable-traversable" + , "exceptions" + , "node-fs" + , "node-buffer" + , "node-readline" + , "datetime" + , "now" + ] + , repo = + "https://github.com/hdgarrood/purescript-benchotron.git" + , version = + "v7.0.0" + } +------------------------------- +-} +let upstream = + https://github.com/purescript/package-sets/releases/download/psc-0.14.5-20211116/packages.dhall + sha256:7ba810597a275e43c83411d2ab0d4b3c54d0b551436f4b1632e9ff3eb62e327a + +let additions = + { aeson = + { dependencies = + [ "aff" + , "argonaut" + , "argonaut-codecs" + , "argonaut-core" + , "arrays" + , "bifunctors" + , "bigints" + , "const" + , "control" + , "effect" + , "either" + , "exceptions" + , "foldable-traversable" + , "foreign-object" + , "gen" + , "identity" + , "integers" + , "maybe" + , "newtype" + , "node-buffer" + , "node-fs-aff" + , "node-path" + , "nonempty" + , "numbers" + , "partial" + , "prelude" + , "quickcheck" + , "record" + , "sequences" + , "spec" + , "strings" + , "transformers" + , "tuples" + , "typelevel" + , "typelevel-prelude" + , "uint" + , "untagged-union" + ] + , repo = "https://github.com/mlabs-haskell/purescript-aeson.git" + , version = "286862a975f4bafbef15540c365bbbb0480e0bf7" + } + , aeson-helpers = + { dependencies = + [ "aff" + , "argonaut-codecs" + , "argonaut-core" + , "arrays" + , "bifunctors" + , "contravariant" + , "control" + , "effect" + , "either" + , "enums" + , "foldable-traversable" + , "foreign-object" + , "maybe" + , "newtype" + , "ordered-collections" + , "prelude" + , "profunctor" + , "psci-support" + , "quickcheck" + , "record" + , "spec" + , "spec-quickcheck" + , "transformers" + , "tuples" + , "typelevel-prelude" + ] + , repo = + "https://github.com/mlabs-haskell/purescript-bridge-aeson-helpers.git" + , version = "44d0dae060cf78babd4534320192b58c16a6f45b" + } + , sequences = + { dependencies = + [ "arrays" + , "assert" + , "console" + , "effect" + , "lazy" + , "maybe" + , "newtype" + , "nonempty" + , "partial" + , "prelude" + , "profunctor" + , "psci-support" + , "quickcheck" + , "quickcheck-laws" + , "tuples" + , "unfoldable" + , "unsafe-coerce" + ] + , repo = "https://github.com/hdgarrood/purescript-sequences" + , version = "v3.0.2" + } + , properties = + { dependencies = [ "prelude", "console" ] + , repo = "https://github.com/Risto-Stevcev/purescript-properties.git" + , version = "v0.2.0" + } + , lattice = + { dependencies = [ "prelude", "console", "properties" ] + , repo = "https://github.com/Risto-Stevcev/purescript-lattice.git" + , version = "v0.3.0" + } + , mote = + { dependencies = [ "these", "transformers", "arrays" ] + , repo = "https://github.com/garyb/purescript-mote" + , version = "v1.1.0" + } + , medea = + { dependencies = + [ "aff" + , "argonaut" + , "arrays" + , "bifunctors" + , "control" + , "effect" + , "either" + , "enums" + , "exceptions" + , "foldable-traversable" + , "foreign-object" + , "free" + , "integers" + , "lists" + , "maybe" + , "mote" + , "naturals" + , "newtype" + , "node-buffer" + , "node-fs-aff" + , "node-path" + , "nonempty" + , "ordered-collections" + , "parsing" + , "partial" + , "prelude" + , "psci-support" + , "quickcheck" + , "quickcheck-combinators" + , "safely" + , "spec" + , "strings" + , "these" + , "transformers" + , "typelevel" + , "tuples" + , "unicode" + , "unordered-collections" + , "unsafe-coerce" + ] + , repo = "https://github.com/juspay/medea-ps.git" + , version = "8b215851959aa8bbf33e6708df6bd683c89d1a5a" + } + , purescript-toppokki = + { dependencies = + [ "prelude" + , "record" + , "functions" + , "node-http" + , "aff-promise" + , "node-buffer" + , "node-fs-aff" + ] + , repo = "https://github.com/firefrorefiddle/purescript-toppokki" + , version = "6983e07bf0aa55ab779bcef12df3df339a2b5bd9" + } + , cardano-transaction-lib = + { dependencies = + [ "aeson" + , "aeson-helpers" + , "aff" + , "aff-promise" + , "aff-retry" + , "affjax" + , "arraybuffer-types" + , "arrays" + , "bifunctors" + , "bigints" + , "checked-exceptions" + , "console" + , "const" + , "contravariant" + , "control" + , "datetime" + , "debug" + , "effect" + , "either" + , "encoding" + , "enums" + , "exceptions" + , "foldable-traversable" + , "foreign" + , "foreign-object" + , "http-methods" + , "identity" + , "integers" + , "js-date" + , "lattice" + , "lists" + , "maybe" + , "medea" + , "media-types" + , "monad-logger" + , "mote" + , "newtype" + , "node-buffer" + , "node-child-process" + , "node-fs" + , "node-fs-aff" + , "node-path" + , "node-process" + , "node-streams" + , "nonempty" + , "optparse" + , "now" + , "numbers" + , "ordered-collections" + , "orders" + , "parallel" + , "partial" + , "posix-types" + , "prelude" + , "profunctor" + , "profunctor-lenses" + , "purescript-toppokki" + , "quickcheck" + , "quickcheck-combinators" + , "quickcheck-laws" + , "rationals" + , "record" + , "refs" + , "spec" + , "spec-quickcheck" + , "strings" + , "tailrec" + , "text-encoding" + , "these" + , "transformers" + , "tuples" + , "typelevel" + , "typelevel-prelude" + , "uint" + , "undefined" + , "unfoldable" + , "untagged-union" + , "variant" + ] + , repo = "https://github.com/Plutonomicon/cardano-transaction-lib.git" + , version = "ec38c06c77c1b75a7e7f1dcb215db7816e44b339" + } + } +in upstream // additions diff --git a/templates/ctl-scaffold/shell.nix b/templates/ctl-scaffold/shell.nix new file mode 100644 index 0000000000..778e3be5e3 --- /dev/null +++ b/templates/ctl-scaffold/shell.nix @@ -0,0 +1,15 @@ +( + import + ( + let + lock = builtins.fromJSON (builtins.readFile ./flake.lock); + in + fetchTarball { + url = "https://github.com/edolstra/flake-compat/archive/${lock.nodes.flake-compat.locked.rev}.tar.gz"; + sha256 = lock.nodes.flake-compat.locked.narHash; + } + ) + { + src = ./.; + } +).shellNix.default diff --git a/templates/ctl-scaffold/spago-packages.nix b/templates/ctl-scaffold/spago-packages.nix new file mode 100644 index 0000000000..6e395e2717 --- /dev/null +++ b/templates/ctl-scaffold/spago-packages.nix @@ -0,0 +1,1654 @@ +# This file was generated by Spago2Nix + +{ pkgs ? import {} }: + +let + inputs = { + + "aeson" = pkgs.stdenv.mkDerivation { + name = "aeson"; + version = "286862a975f4bafbef15540c365bbbb0480e0bf7"; + src = pkgs.fetchgit { + url = "https://github.com/mlabs-haskell/purescript-aeson.git"; + rev = "286862a975f4bafbef15540c365bbbb0480e0bf7"; + sha256 = "1d5h9n9f2qk8hjzqmhjfzwf86x3y60g3cm13gyvm5aaqjraaksvg"; + }; + phases = "installPhase"; + installPhase = "ln -s $src $out"; + }; + + "aeson-helpers" = pkgs.stdenv.mkDerivation { + name = "aeson-helpers"; + version = "44d0dae060cf78babd4534320192b58c16a6f45b"; + src = pkgs.fetchgit { + url = "https://github.com/mlabs-haskell/purescript-bridge-aeson-helpers.git"; + rev = "44d0dae060cf78babd4534320192b58c16a6f45b"; + sha256 = "1fgvaqvd9145zz5xw3fsa5vm75kp6bxcwa2nzq1dx2367h3a0zl0"; + }; + phases = "installPhase"; + installPhase = "ln -s $src $out"; + }; + + "aff" = pkgs.stdenv.mkDerivation { + name = "aff"; + version = "v6.0.0"; + src = pkgs.fetchgit { + url = "https://github.com/purescript-contrib/purescript-aff.git"; + rev = "d0eb009f2f47cb1f5ba1d8592d90c95e8e7ff75d"; + sha256 = "1780sgqyvbdgh8ynxmxn5d44vvhaz7kn9sv3l44c2s9q8xfjkfgm"; + }; + phases = "installPhase"; + installPhase = "ln -s $src $out"; + }; + + "aff-promise" = pkgs.stdenv.mkDerivation { + name = "aff-promise"; + version = "v3.0.0"; + src = pkgs.fetchgit { + url = "https://github.com/nwolverson/purescript-aff-promise.git"; + rev = "45cfba7f663fce12fe69285fe5acaa4ff025144c"; + sha256 = "12fnlwcrj5p6kc5rls7qxwg53zd83gkdpklpmp8jyav945hlgbj2"; + }; + phases = "installPhase"; + installPhase = "ln -s $src $out"; + }; + + "aff-retry" = pkgs.stdenv.mkDerivation { + name = "aff-retry"; + version = "v1.2.1"; + src = pkgs.fetchgit { + url = "https://github.com/Unisay/purescript-aff-retry.git"; + rev = "936fad803e3610f149df724ead288657a905cb84"; + sha256 = "08651ly153ywzviab0ipd0zrhwdr8nz4xfym45dlpbgabgrh8pra"; + }; + phases = "installPhase"; + installPhase = "ln -s $src $out"; + }; + + "affjax" = pkgs.stdenv.mkDerivation { + name = "affjax"; + version = "v12.0.0"; + src = pkgs.fetchgit { + url = "https://github.com/purescript-contrib/purescript-affjax.git"; + rev = "5be197edc213fbededb8da908f77b69908eaa6f8"; + sha256 = "1f2snaimnl9ry8f3kankfcyy50wkba54vvin4wsrglahqgs1nrgb"; + }; + phases = "installPhase"; + installPhase = "ln -s $src $out"; + }; + + "ansi" = pkgs.stdenv.mkDerivation { + name = "ansi"; + version = "v6.1.0"; + src = pkgs.fetchgit { + url = "https://github.com/hdgarrood/purescript-ansi.git"; + rev = "e89e6fede616bd16b001841cf30ac320c95313a6"; + sha256 = "1jsll0h7nz13zgscs036cnkkc6frnlcnk6fwjdwsyp6wbmjri2zm"; + }; + phases = "installPhase"; + installPhase = "ln -s $src $out"; + }; + + "argonaut" = pkgs.stdenv.mkDerivation { + name = "argonaut"; + version = "v8.0.0"; + src = pkgs.fetchgit { + url = "https://github.com/purescript-contrib/purescript-argonaut.git"; + rev = "e5137df76065c14e5de70c4e2820222bd7c78fc2"; + sha256 = "05sq1102rl1phm2gadx0gp966yvk9q1r492bb30q1m0nz762q4v2"; + }; + phases = "installPhase"; + installPhase = "ln -s $src $out"; + }; + + "argonaut-codecs" = pkgs.stdenv.mkDerivation { + name = "argonaut-codecs"; + version = "v8.1.0"; + src = pkgs.fetchgit { + url = "https://github.com/purescript-contrib/purescript-argonaut-codecs.git"; + rev = "b0a041d92bfd548e2cd793cc7c02363464325a13"; + sha256 = "11vmlq98s4jmg5grvdrrlfkqj9vk3la44ky8158a440ipcpinjkq"; + }; + phases = "installPhase"; + installPhase = "ln -s $src $out"; + }; + + "argonaut-core" = pkgs.stdenv.mkDerivation { + name = "argonaut-core"; + version = "v6.0.0"; + src = pkgs.fetchgit { + url = "https://github.com/purescript-contrib/purescript-argonaut-core.git"; + rev = "673971dee79667882a83f9fda7097e50530726f1"; + sha256 = "13ka4xybc8ql54xlkkhy4919nnapfigdlk51ja85f8xwhr64x9kq"; + }; + phases = "installPhase"; + installPhase = "ln -s $src $out"; + }; + + "argonaut-traversals" = pkgs.stdenv.mkDerivation { + name = "argonaut-traversals"; + version = "v9.0.0"; + src = pkgs.fetchgit { + url = "https://github.com/purescript-contrib/purescript-argonaut-traversals.git"; + rev = "36f2e368ceea1ed681bd8e2884eaca451945fc44"; + sha256 = "0bj88s7rz50jfhyawq4h97lvbr3h7pksbqnz4lmh714f5fda6ncx"; + }; + phases = "installPhase"; + installPhase = "ln -s $src $out"; + }; + + "arraybuffer-types" = pkgs.stdenv.mkDerivation { + name = "arraybuffer-types"; + version = "v3.0.1"; + src = pkgs.fetchgit { + url = "https://github.com/purescript-contrib/purescript-arraybuffer-types.git"; + rev = "48cd7f4887791db1d9c2daf5fd98b62ba00e15bd"; + sha256 = "09r6bhsiq9iqdsjf9p8m3p31qkszsipsafvy836mfdi8af6h5fv6"; + }; + phases = "installPhase"; + installPhase = "ln -s $src $out"; + }; + + "arrays" = pkgs.stdenv.mkDerivation { + name = "arrays"; + version = "v6.0.1"; + src = pkgs.fetchgit { + url = "https://github.com/purescript/purescript-arrays.git"; + rev = "c0aa3176b077ad7a46b11ef34487485c28142e53"; + sha256 = "0lm0m5hapimchzgfywr648pkw1hpggr6qibh8d19p2impbnc94c0"; + }; + phases = "installPhase"; + installPhase = "ln -s $src $out"; + }; + + "assert" = pkgs.stdenv.mkDerivation { + name = "assert"; + version = "v5.0.0"; + src = pkgs.fetchgit { + url = "https://github.com/purescript/purescript-assert.git"; + rev = "71a3b1f3b9917c23691fdbb1858de171be871a10"; + sha256 = "0r1l7j67an8dy1w4xdpr8nc30lsxv31xwqph9mkfh3nd49jlyyd3"; + }; + phases = "installPhase"; + installPhase = "ln -s $src $out"; + }; + + "avar" = pkgs.stdenv.mkDerivation { + name = "avar"; + version = "v4.0.0"; + src = pkgs.fetchgit { + url = "https://github.com/purescript-contrib/purescript-avar.git"; + rev = "ac3cbbb8d4b71ff19a78a3178355c089e44d3b4d"; + sha256 = "005046wl61w6r5v3qwd16srhcx82vdz3yvp4xzad2xaasb6iq55l"; + }; + phases = "installPhase"; + installPhase = "ln -s $src $out"; + }; + + "bifunctors" = pkgs.stdenv.mkDerivation { + name = "bifunctors"; + version = "v5.0.0"; + src = pkgs.fetchgit { + url = "https://github.com/purescript/purescript-bifunctors.git"; + rev = "a31d0fc4bbebf19d5e9b21b65493c28b8d3fba62"; + sha256 = "0xc2hf8ccdgqw3m9qcmr38kmzv05fsxvakd07wyrqshvkzg3xn0d"; + }; + phases = "installPhase"; + installPhase = "ln -s $src $out"; + }; + + "bigints" = pkgs.stdenv.mkDerivation { + name = "bigints"; + version = "v6.0.0"; + src = pkgs.fetchgit { + url = "https://github.com/sharkdp/purescript-bigints.git"; + rev = "d5151e04db7e18641fbb2b5892f4198b1cab5907"; + sha256 = "0x8s6d6q2rpfkk56bmayg57a7hl2h7sq9ljrxfc8sjnwd7mfs193"; + }; + phases = "installPhase"; + installPhase = "ln -s $src $out"; + }; + + "cardano-transaction-lib" = pkgs.stdenv.mkDerivation { + name = "cardano-transaction-lib"; + version = "ec38c06c77c1b75a7e7f1dcb215db7816e44b339"; + src = pkgs.fetchgit { + url = "https://github.com/Plutonomicon/cardano-transaction-lib.git"; + rev = "ec38c06c77c1b75a7e7f1dcb215db7816e44b339"; + sha256 = "1hdr821ag39hipw136mkyvbma99lcy41mckrbq3cz7c3ic5y2f3k"; + }; + phases = "installPhase"; + installPhase = "ln -s $src $out"; + }; + + "catenable-lists" = pkgs.stdenv.mkDerivation { + name = "catenable-lists"; + version = "v6.0.1"; + src = pkgs.fetchgit { + url = "https://github.com/purescript/purescript-catenable-lists.git"; + rev = "ee03395f2c5d59a7fd8529a0faac6ec1ebcbb682"; + sha256 = "1lz06fx0za5sl65wccn5fl37mw3x4jnvrriz1gg0aqsmm9lag7ss"; + }; + phases = "installPhase"; + installPhase = "ln -s $src $out"; + }; + + "checked-exceptions" = pkgs.stdenv.mkDerivation { + name = "checked-exceptions"; + version = "v3.1.1"; + src = pkgs.fetchgit { + url = "https://github.com/natefaubion/purescript-checked-exceptions.git"; + rev = "6ece020df25d01ee95474f7545f28e75dcfb0f0c"; + sha256 = "0z5n73n8za8w7d26xbdpkm8d70dlz08gm267rhb9ixxv25acjd36"; + }; + phases = "installPhase"; + installPhase = "ln -s $src $out"; + }; + + "console" = pkgs.stdenv.mkDerivation { + name = "console"; + version = "v5.0.0"; + src = pkgs.fetchgit { + url = "https://github.com/purescript/purescript-console.git"; + rev = "d7cb69ef8fed8a51466afe1b623868bb29e8586e"; + sha256 = "0fzzzqjgrz33pb2jf7cdqpg09ilxb7bsrc7sbfq52wjg0sx9aq6g"; + }; + phases = "installPhase"; + installPhase = "ln -s $src $out"; + }; + + "const" = pkgs.stdenv.mkDerivation { + name = "const"; + version = "v5.0.0"; + src = pkgs.fetchgit { + url = "https://github.com/purescript/purescript-const.git"; + rev = "3a3a4bdc44f71311cf27de9bd22039b110277540"; + sha256 = "0aq9qjbrvf8mf8hmas6imv4mg6n3zi13hkf449ns1hn12lw8qv4g"; + }; + phases = "installPhase"; + installPhase = "ln -s $src $out"; + }; + + "contravariant" = pkgs.stdenv.mkDerivation { + name = "contravariant"; + version = "v5.0.0"; + src = pkgs.fetchgit { + url = "https://github.com/purescript/purescript-contravariant.git"; + rev = "ae1a765f7ddbfd96ae1f12e399e46d554d8e3b38"; + sha256 = "029hb8i3n4759x4gc06wkfgr7wim5x1w5jy2bsiy42n0g731h5qc"; + }; + phases = "installPhase"; + installPhase = "ln -s $src $out"; + }; + + "control" = pkgs.stdenv.mkDerivation { + name = "control"; + version = "v5.0.0"; + src = pkgs.fetchgit { + url = "https://github.com/purescript/purescript-control.git"; + rev = "18d582e311f1f8523f9eb55fb93c91bd21e22837"; + sha256 = "06dc06yli4g5yr8fb9sdpqbhiaff37g977qcsbds9q2mlhnjgfx9"; + }; + phases = "installPhase"; + installPhase = "ln -s $src $out"; + }; + + "datetime" = pkgs.stdenv.mkDerivation { + name = "datetime"; + version = "v5.0.2"; + src = pkgs.fetchgit { + url = "https://github.com/purescript/purescript-datetime.git"; + rev = "e110462829ea656d2bc0924266d4edff222108d4"; + sha256 = "1mhzn2ymdkzki7wjlr9xrdbngm0886wmfbh2c46flnf9lmfyw54y"; + }; + phases = "installPhase"; + installPhase = "ln -s $src $out"; + }; + + "debug" = pkgs.stdenv.mkDerivation { + name = "debug"; + version = "v5.0.0"; + src = pkgs.fetchgit { + url = "https://github.com/garyb/purescript-debug.git"; + rev = "144305842dba81169a93b3a3cc75429d5c8389e9"; + sha256 = "09j69bgrq8nzw1l3aj1hka3y5ycmcsn9dlgf22k5ifrd74iic60y"; + }; + phases = "installPhase"; + installPhase = "ln -s $src $out"; + }; + + "distributive" = pkgs.stdenv.mkDerivation { + name = "distributive"; + version = "v5.0.0"; + src = pkgs.fetchgit { + url = "https://github.com/purescript/purescript-distributive.git"; + rev = "11f3f87ca5720899e1739cedb58dd6227cae6ad5"; + sha256 = "0788znmdyh6b1c9pln624ah397l88xmd3fxlxiy3z1qy8bzr4r54"; + }; + phases = "installPhase"; + installPhase = "ln -s $src $out"; + }; + + "effect" = pkgs.stdenv.mkDerivation { + name = "effect"; + version = "v3.0.0"; + src = pkgs.fetchgit { + url = "https://github.com/purescript/purescript-effect.git"; + rev = "985d97bd5721ddcc41304c55a7ca2bb0c0bfdc2a"; + sha256 = "1n9qr85knvpm4i0qhm8xbgfk46v9y843p76j278phfs9l6aywzsn"; + }; + phases = "installPhase"; + installPhase = "ln -s $src $out"; + }; + + "either" = pkgs.stdenv.mkDerivation { + name = "either"; + version = "v5.0.0"; + src = pkgs.fetchgit { + url = "https://github.com/purescript/purescript-either.git"; + rev = "c1a1af35684f10eecaf6ac7d38dbf6bd48af2ced"; + sha256 = "18dk159yyv7vs0xsnh9m5fajd7zy6zw5b2mpyd6nqdh3c6bb9wh6"; + }; + phases = "installPhase"; + installPhase = "ln -s $src $out"; + }; + + "encoding" = pkgs.stdenv.mkDerivation { + name = "encoding"; + version = "v0.0.7"; + src = pkgs.fetchgit { + url = "https://github.com/menelaos/purescript-encoding.git"; + rev = "0a4187136f9ea4ea51ddf635e3b3c2cd2461faac"; + sha256 = "1rsnn8g2lx24k9wflr1jj12281i0smprb76nfm2f61yqqiwgij4d"; + }; + phases = "installPhase"; + installPhase = "ln -s $src $out"; + }; + + "enums" = pkgs.stdenv.mkDerivation { + name = "enums"; + version = "v5.0.0"; + src = pkgs.fetchgit { + url = "https://github.com/purescript/purescript-enums.git"; + rev = "170d959644eb99e0025f4ab2e38f5f132fd85fa4"; + sha256 = "1lci5iy6s6cmh93bpkfcmp0j4n5dnij7dswb0075bk0kzd9xp7rs"; + }; + phases = "installPhase"; + installPhase = "ln -s $src $out"; + }; + + "exceptions" = pkgs.stdenv.mkDerivation { + name = "exceptions"; + version = "v5.0.0"; + src = pkgs.fetchgit { + url = "https://github.com/purescript/purescript-exceptions.git"; + rev = "410d0b8813592bda3c25028540eeb2cda312ddc9"; + sha256 = "1yjbrx34a0rnxgpvywb63n9jzhkdgb2q2acyzbwh290mrrggc95x"; + }; + phases = "installPhase"; + installPhase = "ln -s $src $out"; + }; + + "exists" = pkgs.stdenv.mkDerivation { + name = "exists"; + version = "v5.1.0"; + src = pkgs.fetchgit { + url = "https://github.com/purescript/purescript-exists.git"; + rev = "c34820f8b2d15be29abdd5097c3d636f5df8f28c"; + sha256 = "15qp52cpp2yvxihkzfmn6gabyvx5s6iz5lafvqhyfgp4wfnz0bds"; + }; + phases = "installPhase"; + installPhase = "ln -s $src $out"; + }; + + "exitcodes" = pkgs.stdenv.mkDerivation { + name = "exitcodes"; + version = "v4.0.0"; + src = pkgs.fetchgit { + url = "https://github.com/Risto-Stevcev/purescript-exitcodes.git"; + rev = "8a9a93fd1776aba4a14cdc9a31094c9e05b05348"; + sha256 = "16861bn1h6jz47i20sd2a0d3qdj52akkqpx43yllmsdggcawmjxc"; + }; + phases = "installPhase"; + installPhase = "ln -s $src $out"; + }; + + "foldable-traversable" = pkgs.stdenv.mkDerivation { + name = "foldable-traversable"; + version = "v5.0.1"; + src = pkgs.fetchgit { + url = "https://github.com/purescript/purescript-foldable-traversable.git"; + rev = "d581caf260772b1b446c11ac3c8be807b290b220"; + sha256 = "182na4np7hk2dqyxywy4jij2csrzx4bz02m6bq8yx1j27hlgjvsd"; + }; + phases = "installPhase"; + installPhase = "ln -s $src $out"; + }; + + "foreign" = pkgs.stdenv.mkDerivation { + name = "foreign"; + version = "v6.0.1"; + src = pkgs.fetchgit { + url = "https://github.com/purescript/purescript-foreign.git"; + rev = "7ee18c6689c56c89755172ea53326f948da10bd3"; + sha256 = "16j7712cck79p8q53xbhn4hs886bm0ls5wvmchrhqnaghj48m85g"; + }; + phases = "installPhase"; + installPhase = "ln -s $src $out"; + }; + + "foreign-object" = pkgs.stdenv.mkDerivation { + name = "foreign-object"; + version = "v3.0.0"; + src = pkgs.fetchgit { + url = "https://github.com/purescript/purescript-foreign-object.git"; + rev = "c9a7b7bb8bed1b87c5545c4ebe85a70f86c0e6b1"; + sha256 = "0accw6qd93qqry19rskjgl7y54xi2wd70rglbqyjx6c5ybcjnavr"; + }; + phases = "installPhase"; + installPhase = "ln -s $src $out"; + }; + + "fork" = pkgs.stdenv.mkDerivation { + name = "fork"; + version = "v5.0.0"; + src = pkgs.fetchgit { + url = "https://github.com/purescript-contrib/purescript-fork.git"; + rev = "153cc29e6e51fb1108368efc622d41d9f80bd707"; + sha256 = "1hyvaixza8151zbylk2kv859x48yyhla536lcjghcwd62vzfwmdn"; + }; + phases = "installPhase"; + installPhase = "ln -s $src $out"; + }; + + "form-urlencoded" = pkgs.stdenv.mkDerivation { + name = "form-urlencoded"; + version = "v6.0.2"; + src = pkgs.fetchgit { + url = "https://github.com/purescript-contrib/purescript-form-urlencoded.git"; + rev = "860b2c4bf0a848322d2077faaefbeb98762cb8d6"; + sha256 = "1pi3vxix10crisisnd94li1vmmgiawlh5lgl51z7ssd9azygg0b0"; + }; + phases = "installPhase"; + installPhase = "ln -s $src $out"; + }; + + "free" = pkgs.stdenv.mkDerivation { + name = "free"; + version = "v6.1.0"; + src = pkgs.fetchgit { + url = "https://github.com/purescript/purescript-free.git"; + rev = "fea875168b4da6dec7ab819d780d8ecde9f37dbd"; + sha256 = "038wbpmbv86q8s69fcvqb8fngkqc863axp7khzgnsqw55d1926r4"; + }; + phases = "installPhase"; + installPhase = "ln -s $src $out"; + }; + + "freet" = pkgs.stdenv.mkDerivation { + name = "freet"; + version = "v6.0.0"; + src = pkgs.fetchgit { + url = "https://github.com/purescript-contrib/purescript-freet.git"; + rev = "507c2edd9173cda5ad44dd0638133edd69fd9acd"; + sha256 = "0f5bibw604sd9ffmp51b3jppka88r54mh7sdz91zy5b92wgsy5yr"; + }; + phases = "installPhase"; + installPhase = "ln -s $src $out"; + }; + + "functions" = pkgs.stdenv.mkDerivation { + name = "functions"; + version = "v5.0.0"; + src = pkgs.fetchgit { + url = "https://github.com/purescript/purescript-functions.git"; + rev = "691b3345bc2feaf914e5299796c606b6a6bf9ca9"; + sha256 = "1gnk6xh5x04zcahn82gwp49qpglxd5jkfqn0i58m27jfihvblaxd"; + }; + phases = "installPhase"; + installPhase = "ln -s $src $out"; + }; + + "functors" = pkgs.stdenv.mkDerivation { + name = "functors"; + version = "v4.1.1"; + src = pkgs.fetchgit { + url = "https://github.com/purescript/purescript-functors.git"; + rev = "e936f7a8d2ec53a344c478ccada5add93273848c"; + sha256 = "0i1x14r54758s5jx5d7zy4l07mg6gabljadgybldnbpmdqk6b966"; + }; + phases = "installPhase"; + installPhase = "ln -s $src $out"; + }; + + "gen" = pkgs.stdenv.mkDerivation { + name = "gen"; + version = "v3.0.0"; + src = pkgs.fetchgit { + url = "https://github.com/purescript/purescript-gen.git"; + rev = "85c369f56545a3de834b7e7475a56bc9193bb4b4"; + sha256 = "1h396rqn1fc2c155i58vnaksqjrpajly128ah6wq1w426vwr1vrf"; + }; + phases = "installPhase"; + installPhase = "ln -s $src $out"; + }; + + "http-methods" = pkgs.stdenv.mkDerivation { + name = "http-methods"; + version = "v5.0.0"; + src = pkgs.fetchgit { + url = "https://github.com/purescript-contrib/purescript-http-methods.git"; + rev = "d373066a45017e886d1580cd359372368231de47"; + sha256 = "1g0ywd5zpckmhq28mr14yr4k28hiii1px8r8xbdx8nv45ryw69l3"; + }; + phases = "installPhase"; + installPhase = "ln -s $src $out"; + }; + + "identity" = pkgs.stdenv.mkDerivation { + name = "identity"; + version = "v5.0.0"; + src = pkgs.fetchgit { + url = "https://github.com/purescript/purescript-identity.git"; + rev = "5c150ac5ee4fa6f145932f6322a1020463dae8e9"; + sha256 = "0a58y71ihvb5b7plnn2sxsbphqzd9nzfafak4d5a576agn76q0ql"; + }; + phases = "installPhase"; + installPhase = "ln -s $src $out"; + }; + + "integers" = pkgs.stdenv.mkDerivation { + name = "integers"; + version = "v5.0.0"; + src = pkgs.fetchgit { + url = "https://github.com/purescript/purescript-integers.git"; + rev = "8a783f2d92596c43afca53066ac18eb389d15981"; + sha256 = "1rrygw0ai61brnvgap7dfhdzacyhg5439pz6yrmmyg32cvf0znhv"; + }; + phases = "installPhase"; + installPhase = "ln -s $src $out"; + }; + + "invariant" = pkgs.stdenv.mkDerivation { + name = "invariant"; + version = "v5.0.0"; + src = pkgs.fetchgit { + url = "https://github.com/purescript/purescript-invariant.git"; + rev = "c421b49dec7a1511073bb408a08bdd8c9d17d7b1"; + sha256 = "0vwkbh7kv00g50xjgvxc0mv5b99mrj6q0sxznxwk32hb9hkbhy5l"; + }; + phases = "installPhase"; + installPhase = "ln -s $src $out"; + }; + + "js-date" = pkgs.stdenv.mkDerivation { + name = "js-date"; + version = "v7.0.0"; + src = pkgs.fetchgit { + url = "https://github.com/purescript-contrib/purescript-js-date.git"; + rev = "a6834eef986e3af0490cb672dc4a8b4b089dcb15"; + sha256 = "1dpiwn65qww862ilpfbd06gwfazpxvz3jwvsjsdrcxqqfcbjp8n8"; + }; + phases = "installPhase"; + installPhase = "ln -s $src $out"; + }; + + "js-timers" = pkgs.stdenv.mkDerivation { + name = "js-timers"; + version = "v5.0.1"; + src = pkgs.fetchgit { + url = "https://github.com/purescript-contrib/purescript-js-timers.git"; + rev = "86afef13f457b9506acdfe88559ee18f1cd2c2d8"; + sha256 = "0008paz0qkz5n1pfrzagkkac6jny9z2rd1ij10ww2k1pkb9cy59z"; + }; + phases = "installPhase"; + installPhase = "ln -s $src $out"; + }; + + "js-uri" = pkgs.stdenv.mkDerivation { + name = "js-uri"; + version = "v2.0.0"; + src = pkgs.fetchgit { + url = "https://github.com/purescript-contrib/purescript-js-uri.git"; + rev = "6145d5e631be3d57d8a4a9cf976ae140713dee35"; + sha256 = "1q34ir93cqbcl9g49vv1qfj8jxbbdj7f85a14y4mzd7yjq0a042g"; + }; + phases = "installPhase"; + installPhase = "ln -s $src $out"; + }; + + "lattice" = pkgs.stdenv.mkDerivation { + name = "lattice"; + version = "v0.3.0"; + src = pkgs.fetchgit { + url = "https://github.com/Risto-Stevcev/purescript-lattice.git"; + rev = "aebe3686eba30f199d17964bfa892f0176c1742d"; + sha256 = "0n6030mj526l6c1bv7m5fp4qwikczpbaal17q10a67hmhljg2s8i"; + }; + phases = "installPhase"; + installPhase = "ln -s $src $out"; + }; + + "lazy" = pkgs.stdenv.mkDerivation { + name = "lazy"; + version = "v5.0.0"; + src = pkgs.fetchgit { + url = "https://github.com/purescript/purescript-lazy.git"; + rev = "2f73f61e7ac1ae1cfe05564112e3313530e673ff"; + sha256 = "1wxfx019911gbkifq266hgn67zwm89pxhi83bai77mva5n9j3f6l"; + }; + phases = "installPhase"; + installPhase = "ln -s $src $out"; + }; + + "lcg" = pkgs.stdenv.mkDerivation { + name = "lcg"; + version = "v3.0.0"; + src = pkgs.fetchgit { + url = "https://github.com/purescript/purescript-lcg.git"; + rev = "8fb2eb16bbba2cee1d115a6729659ac649da811b"; + sha256 = "04r9bmx9kc3jqx59hh9yqqkl95mf869la9as5h36jv85ynn464dx"; + }; + phases = "installPhase"; + installPhase = "ln -s $src $out"; + }; + + "lists" = pkgs.stdenv.mkDerivation { + name = "lists"; + version = "v6.0.1"; + src = pkgs.fetchgit { + url = "https://github.com/purescript/purescript-lists.git"; + rev = "6383c4f202b3f69474f9f7da182c2d42fcc3111c"; + sha256 = "0xmg918s3mqvfvwgjfqcs1yvcz6hy2n7h3ygqz2iyvk868gz25qs"; + }; + phases = "installPhase"; + installPhase = "ln -s $src $out"; + }; + + "literals" = pkgs.stdenv.mkDerivation { + name = "literals"; + version = "v0.2.0"; + src = pkgs.fetchgit { + url = "https://github.com/jvliwanag/purescript-literals.git"; + rev = "11457380e1b28c9526c41381eeb0ee840982db5c"; + sha256 = "1jjnpfmh9qfmffyrmcfnn9p25irmsmaji6lwrsrd2r9xdhpzmqg7"; + }; + phases = "installPhase"; + installPhase = "ln -s $src $out"; + }; + + "math" = pkgs.stdenv.mkDerivation { + name = "math"; + version = "v3.0.0"; + src = pkgs.fetchgit { + url = "https://github.com/purescript/purescript-math.git"; + rev = "59746cc74e23fb1f04e09342884c5d1e3943a04f"; + sha256 = "0hkf0vyiga21992d9vbvdbnzdkvgljmsi497jjas1rk3vhblx8sq"; + }; + phases = "installPhase"; + installPhase = "ln -s $src $out"; + }; + + "maybe" = pkgs.stdenv.mkDerivation { + name = "maybe"; + version = "v5.0.0"; + src = pkgs.fetchgit { + url = "https://github.com/purescript/purescript-maybe.git"; + rev = "8e96ca0187208e78e8df6a464c281850e5c9400c"; + sha256 = "0vyk3r9gklvv7awzpph7ra53zxxbin1ngmqflb5vvr2365v5xyqy"; + }; + phases = "installPhase"; + installPhase = "ln -s $src $out"; + }; + + "medea" = pkgs.stdenv.mkDerivation { + name = "medea"; + version = "8b215851959aa8bbf33e6708df6bd683c89d1a5a"; + src = pkgs.fetchgit { + url = "https://github.com/juspay/medea-ps.git"; + rev = "8b215851959aa8bbf33e6708df6bd683c89d1a5a"; + sha256 = "05gnar9l1li0v1vv12kga0sssskfm4f1x9y6smpmqbqg9396pmsq"; + }; + phases = "installPhase"; + installPhase = "ln -s $src $out"; + }; + + "media-types" = pkgs.stdenv.mkDerivation { + name = "media-types"; + version = "v5.0.0"; + src = pkgs.fetchgit { + url = "https://github.com/purescript-contrib/purescript-media-types.git"; + rev = "b6efa4c1e6808b31f399d8030b5938acec87cb48"; + sha256 = "0l51nd1w52756pyy3zliwmhfbin0px4pxr7d2h5vchl1wq895fja"; + }; + phases = "installPhase"; + installPhase = "ln -s $src $out"; + }; + + "mmorph" = pkgs.stdenv.mkDerivation { + name = "mmorph"; + version = "v6.0.0"; + src = pkgs.fetchgit { + url = "https://github.com/Thimoteus/purescript-mmorph.git"; + rev = "ebe16afbfa16dd600f3379ccedc7529417402393"; + sha256 = "0ds88hray8v0519n9k546qsc4qs8bj1k5h5az7nwfp0gaq0r5wpk"; + }; + phases = "installPhase"; + installPhase = "ln -s $src $out"; + }; + + "monad-logger" = pkgs.stdenv.mkDerivation { + name = "monad-logger"; + version = "v1.3.1"; + src = pkgs.fetchgit { + url = "https://github.com/cprussin/purescript-monad-logger.git"; + rev = "55441b4caf390bc38078a9c5c865efb105549cef"; + sha256 = "0r1cp2x6mamjca5r5rm5mp1gidlll72paqrjd3z0j69l7iy7dgas"; + }; + phases = "installPhase"; + installPhase = "ln -s $src $out"; + }; + + "mote" = pkgs.stdenv.mkDerivation { + name = "mote"; + version = "v1.1.0"; + src = pkgs.fetchgit { + url = "https://github.com/garyb/purescript-mote"; + rev = "29aea4ad7b013d50b42629c87b01cf0202451abd"; + sha256 = "00nckcd7w4djx9jh1hmg0fma55k6k7cw6pdcb96w107gykxgv5r7"; + }; + phases = "installPhase"; + installPhase = "ln -s $src $out"; + }; + + "naturals" = pkgs.stdenv.mkDerivation { + name = "naturals"; + version = "v3.0.0"; + src = pkgs.fetchgit { + url = "https://github.com/LiamGoodacre/purescript-naturals.git"; + rev = "53aaa11516cd1bb8429f33032802bf43a5b04644"; + sha256 = "0jaly95g46rbb7xwfv655pgm2bsp11p1iriasa0w79ryv0p488hi"; + }; + phases = "installPhase"; + installPhase = "ln -s $src $out"; + }; + + "newtype" = pkgs.stdenv.mkDerivation { + name = "newtype"; + version = "v4.0.0"; + src = pkgs.fetchgit { + url = "https://github.com/purescript/purescript-newtype.git"; + rev = "7b292fcd2ac7c4a25d7a7a8d3387d0ee7de89b13"; + sha256 = "1fgzbxslckva2psn0sia30hfakx8xchz3wx2kkh3w8rr4nn2py8v"; + }; + phases = "installPhase"; + installPhase = "ln -s $src $out"; + }; + + "node-buffer" = pkgs.stdenv.mkDerivation { + name = "node-buffer"; + version = "v7.0.1"; + src = pkgs.fetchgit { + url = "https://github.com/purescript-node/purescript-node-buffer.git"; + rev = "0721f1e8d768df48ae429547c8c60b121ca120cb"; + sha256 = "14bf3llsa20ivkwp5hlyk8v8zfzpzhhsni9pd8rfqdyzp6zrdx3b"; + }; + phases = "installPhase"; + installPhase = "ln -s $src $out"; + }; + + "node-child-process" = pkgs.stdenv.mkDerivation { + name = "node-child-process"; + version = "v7.1.0"; + src = pkgs.fetchgit { + url = "https://github.com/purescript-node/purescript-node-child-process.git"; + rev = "5c4e560eceead04efc1d5a3ec1f6de91bb1d512e"; + sha256 = "18va367xims00hmjwiasiifdfak3cbs0sp4sr52ihb20n19n6h5b"; + }; + phases = "installPhase"; + installPhase = "ln -s $src $out"; + }; + + "node-fs" = pkgs.stdenv.mkDerivation { + name = "node-fs"; + version = "v6.1.0"; + src = pkgs.fetchgit { + url = "https://github.com/purescript-node/purescript-node-fs.git"; + rev = "09a2b71a3a86f0cd19c46f4b6c40310cc1648909"; + sha256 = "1w97m2afn7yn757niknkbk7w6nyg4n5dabxr7gzfz368z1nkf45s"; + }; + phases = "installPhase"; + installPhase = "ln -s $src $out"; + }; + + "node-fs-aff" = pkgs.stdenv.mkDerivation { + name = "node-fs-aff"; + version = "v7.0.0"; + src = pkgs.fetchgit { + url = "https://github.com/purescript-node/purescript-node-fs-aff.git"; + rev = "1da5d326573c3b17c6d4dba3d0e0157e60869f91"; + sha256 = "10aglq89gbchykwlckmg5xsln705qha76f125snkmk056kq2w89h"; + }; + phases = "installPhase"; + installPhase = "ln -s $src $out"; + }; + + "node-http" = pkgs.stdenv.mkDerivation { + name = "node-http"; + version = "v6.0.0"; + src = pkgs.fetchgit { + url = "https://github.com/purescript-node/purescript-node-http.git"; + rev = "48a4da07051f0cc9a9d08fbfe8179ebf55aff39a"; + sha256 = "1521ab70jx7a9d7kk4gn1sk4w6knfi13pai1kanhrvwp5lfys5wl"; + }; + phases = "installPhase"; + installPhase = "ln -s $src $out"; + }; + + "node-net" = pkgs.stdenv.mkDerivation { + name = "node-net"; + version = "v2.0.1"; + src = pkgs.fetchgit { + url = "https://github.com/purescript-node/purescript-node-net.git"; + rev = "e25a2c538dfa524cd9b75bf12fd7a393efe2f7e9"; + sha256 = "17sx9r74kdjq85dafm5kisbvgdb0wn11lq9gaazpdirhshpm2wl5"; + }; + phases = "installPhase"; + installPhase = "ln -s $src $out"; + }; + + "node-path" = pkgs.stdenv.mkDerivation { + name = "node-path"; + version = "v4.0.0"; + src = pkgs.fetchgit { + url = "https://github.com/purescript-node/purescript-node-path.git"; + rev = "a2d7cf05e40b607ef7d048a3684cda788cd42890"; + sha256 = "1384qyf4v84wbahafzvqdxjllqy8qkd5dpkhsl3js444vsm2aplr"; + }; + phases = "installPhase"; + installPhase = "ln -s $src $out"; + }; + + "node-process" = pkgs.stdenv.mkDerivation { + name = "node-process"; + version = "v8.2.0"; + src = pkgs.fetchgit { + url = "https://github.com/purescript-node/purescript-node-process.git"; + rev = "e1e807ac7831d1a8a15e242964f7e5005e42f76b"; + sha256 = "0nl9r271s8f71a9wqfkadq9b490h8phwgqc61jbzhm4ags23pqpg"; + }; + phases = "installPhase"; + installPhase = "ln -s $src $out"; + }; + + "node-streams" = pkgs.stdenv.mkDerivation { + name = "node-streams"; + version = "v5.0.0"; + src = pkgs.fetchgit { + url = "https://github.com/purescript-node/purescript-node-streams.git"; + rev = "886bb2045685e3b9031687d69ccfed29972147bb"; + sha256 = "1jc3d4x0v77h8qcwq7hpwprsdr3gqmdfiyr1ph0kiy7r9bbrqwfx"; + }; + phases = "installPhase"; + installPhase = "ln -s $src $out"; + }; + + "node-url" = pkgs.stdenv.mkDerivation { + name = "node-url"; + version = "v5.0.0"; + src = pkgs.fetchgit { + url = "https://github.com/purescript-node/purescript-node-url.git"; + rev = "d5671f5e38051f4fa7acacd9ec157ed9dc6ded46"; + sha256 = "0w78q23vxa2nldy0dfj4nb5kv0pcrc1yq7dp1mysz7cdi9f72zp9"; + }; + phases = "installPhase"; + installPhase = "ln -s $src $out"; + }; + + "nonempty" = pkgs.stdenv.mkDerivation { + name = "nonempty"; + version = "v6.1.0"; + src = pkgs.fetchgit { + url = "https://github.com/purescript/purescript-nonempty.git"; + rev = "7696eaf915da5333173bca7d779a51f91a525b83"; + sha256 = "0hhhw5x5xvs2bd9373gklja1545glnzi1xc2sj16kkznnayrmvsn"; + }; + phases = "installPhase"; + installPhase = "ln -s $src $out"; + }; + + "now" = pkgs.stdenv.mkDerivation { + name = "now"; + version = "v5.0.0"; + src = pkgs.fetchgit { + url = "https://github.com/purescript-contrib/purescript-now.git"; + rev = "4c994dae8bb650787de1e4d9e709f2565fb354fb"; + sha256 = "1wa4j2h5rlw1lgfpm7rif3v6ksm8lplxl1x69zpk8hdf0cfyz4qm"; + }; + phases = "installPhase"; + installPhase = "ln -s $src $out"; + }; + + "nullable" = pkgs.stdenv.mkDerivation { + name = "nullable"; + version = "v5.0.0"; + src = pkgs.fetchgit { + url = "https://github.com/purescript-contrib/purescript-nullable.git"; + rev = "8b19c16b16593102ae5d5d9f5b42eea0e213e2f5"; + sha256 = "0jbmks8kwhpb5fr2b9nb70fjwh6zdnwirycvzr77jafcny24yrnl"; + }; + phases = "installPhase"; + installPhase = "ln -s $src $out"; + }; + + "numbers" = pkgs.stdenv.mkDerivation { + name = "numbers"; + version = "v8.0.0"; + src = pkgs.fetchgit { + url = "https://github.com/purescript/purescript-numbers.git"; + rev = "f5bbd96cbed58403c4445bd4c73df50fc8d86f46"; + sha256 = "00pm2x4kh4fm91r7nmik1v5jclkgh7gpxz13ambyqxbxbiqjq0vg"; + }; + phases = "installPhase"; + installPhase = "ln -s $src $out"; + }; + + "open-memoize" = pkgs.stdenv.mkDerivation { + name = "open-memoize"; + version = "v6.1.0"; + src = pkgs.fetchgit { + url = "https://github.com/purescript-open-community/purescript-open-memoize.git"; + rev = "20d5c14d3033d19044e2d49c11d02278bda72a54"; + sha256 = "10xaylggw22s41bdvxvy7jg16idwa7npwjnns4d65mjynh2ia6kv"; + }; + phases = "installPhase"; + installPhase = "ln -s $src $out"; + }; + + "options" = pkgs.stdenv.mkDerivation { + name = "options"; + version = "v6.0.0"; + src = pkgs.fetchgit { + url = "https://github.com/purescript-contrib/purescript-options.git"; + rev = "0309a42692251ce5e3d1d0be57d4f63f7143f858"; + sha256 = "04f70wfik1pi6nzfq2cn3la9z735akkadpx5cxbs4mx8xg032sjd"; + }; + phases = "installPhase"; + installPhase = "ln -s $src $out"; + }; + + "optparse" = pkgs.stdenv.mkDerivation { + name = "optparse"; + version = "v4.1.0"; + src = pkgs.fetchgit { + url = "https://github.com/f-o-a-m/purescript-optparse.git"; + rev = "04f2ed818f32390a9feff04b892f23c96ccb84cb"; + sha256 = "0b05wczcjnann0xw6vdaq2c1a2n9rcgvq9l29wa5461b5mvjyb80"; + }; + phases = "installPhase"; + installPhase = "ln -s $src $out"; + }; + + "ordered-collections" = pkgs.stdenv.mkDerivation { + name = "ordered-collections"; + version = "v2.0.2"; + src = pkgs.fetchgit { + url = "https://github.com/purescript/purescript-ordered-collections.git"; + rev = "1929b706b07e251995b6be51baa7995c61eb4d83"; + sha256 = "0g57043ylj3kldkm5vn233yd6hiamryhdfh72cxx9h3mn0ra8ghd"; + }; + phases = "installPhase"; + installPhase = "ln -s $src $out"; + }; + + "orders" = pkgs.stdenv.mkDerivation { + name = "orders"; + version = "v5.0.0"; + src = pkgs.fetchgit { + url = "https://github.com/purescript/purescript-orders.git"; + rev = "c25b7075426cf82bcb960495f28d2541c9a75510"; + sha256 = "0wwy3ycjll0s590ra35zf5gjvs86w97rln09bj428axhg7cvfl0a"; + }; + phases = "installPhase"; + installPhase = "ln -s $src $out"; + }; + + "parallel" = pkgs.stdenv.mkDerivation { + name = "parallel"; + version = "v5.0.0"; + src = pkgs.fetchgit { + url = "https://github.com/purescript/purescript-parallel.git"; + rev = "16b38a2e148639b04ae67e0ce63cc220da8857f7"; + sha256 = "0x8mvhgs8ygqj34xgyhk6gixqm32p2ymm00zg0zdw13g3lil9p4x"; + }; + phases = "installPhase"; + installPhase = "ln -s $src $out"; + }; + + "parsing" = pkgs.stdenv.mkDerivation { + name = "parsing"; + version = "v7.0.1"; + src = pkgs.fetchgit { + url = "https://github.com/purescript-contrib/purescript-parsing.git"; + rev = "a3e82c26ce2c74b5fa38ff7814d7c35bf9233af5"; + sha256 = "15yx51khg3niqiryc6qdii6lwdvni77ak7dkbf9w5zw4h1y51p9a"; + }; + phases = "installPhase"; + installPhase = "ln -s $src $out"; + }; + + "partial" = pkgs.stdenv.mkDerivation { + name = "partial"; + version = "v3.0.0"; + src = pkgs.fetchgit { + url = "https://github.com/purescript/purescript-partial.git"; + rev = "2f0a5239efab68179a684603263bcec8f1489b08"; + sha256 = "0acxf686hvaj793hyb7kfn9lf96kv3nk0lls2p9j095ylp55sldb"; + }; + phases = "installPhase"; + installPhase = "ln -s $src $out"; + }; + + "pipes" = pkgs.stdenv.mkDerivation { + name = "pipes"; + version = "v7.0.1"; + src = pkgs.fetchgit { + url = "https://github.com/felixschl/purescript-pipes.git"; + rev = "42e43f0961ad0fc3f1cef6986fe23ca9f48f6dda"; + sha256 = "0jzgzi34wqqdcfgznbpfv4b8al2prd36yshnndlvkqfv70smx3kh"; + }; + phases = "installPhase"; + installPhase = "ln -s $src $out"; + }; + + "posix-types" = pkgs.stdenv.mkDerivation { + name = "posix-types"; + version = "v5.0.0"; + src = pkgs.fetchgit { + url = "https://github.com/purescript-node/purescript-posix-types.git"; + rev = "e562680fce64b67e26741a61a51160a04fd3e7fb"; + sha256 = "1knhdnnmxx77qsjz3gk1ga7n713l303dxyn8zs46qh7p2hnkalkc"; + }; + phases = "installPhase"; + installPhase = "ln -s $src $out"; + }; + + "prelude" = pkgs.stdenv.mkDerivation { + name = "prelude"; + version = "v5.0.1"; + src = pkgs.fetchgit { + url = "https://github.com/purescript/purescript-prelude.git"; + rev = "68f8012bc2309d9bf5832cdf7316ad052d586905"; + sha256 = "1x0cacvv9mmw80vy6f40y0p959q1dz28fwjswhyd7ws6npbklcy0"; + }; + phases = "installPhase"; + installPhase = "ln -s $src $out"; + }; + + "profunctor" = pkgs.stdenv.mkDerivation { + name = "profunctor"; + version = "v5.0.0"; + src = pkgs.fetchgit { + url = "https://github.com/purescript/purescript-profunctor.git"; + rev = "4551b8e437a00268cc9b687cbe691d75e812e82b"; + sha256 = "0fvd2xiv77sp4jd4spgdp4i9812p6pdzzbg4pa96mbr0h19jf39c"; + }; + phases = "installPhase"; + installPhase = "ln -s $src $out"; + }; + + "profunctor-lenses" = pkgs.stdenv.mkDerivation { + name = "profunctor-lenses"; + version = "v7.0.1"; + src = pkgs.fetchgit { + url = "https://github.com/purescript-contrib/purescript-profunctor-lenses.git"; + rev = "9c3d87a6dab8eb785a93bff11aa183796dc93183"; + sha256 = "1wknj7g6vwk2ga1rq57l470h322308ddjn5bd3x2hhfkiy039kc3"; + }; + phases = "installPhase"; + installPhase = "ln -s $src $out"; + }; + + "properties" = pkgs.stdenv.mkDerivation { + name = "properties"; + version = "v0.2.0"; + src = pkgs.fetchgit { + url = "https://github.com/Risto-Stevcev/purescript-properties.git"; + rev = "ddcad0f6043cc665037538467a2e2e4173ef276a"; + sha256 = "1iwik230gs8v5sw1xd3bcrbfid3pgml0ck9a8k991ddshfscbq3i"; + }; + phases = "installPhase"; + installPhase = "ln -s $src $out"; + }; + + "psci-support" = pkgs.stdenv.mkDerivation { + name = "psci-support"; + version = "v5.0.0"; + src = pkgs.fetchgit { + url = "https://github.com/purescript/purescript-psci-support.git"; + rev = "f26fe8266a63494080476333e22f971404ea8846"; + sha256 = "16vhf8hapd7rcgmafmjpiq7smhzdh3300f2idk1q4kk01yxn8ddj"; + }; + phases = "installPhase"; + installPhase = "ln -s $src $out"; + }; + + "purescript-toppokki" = pkgs.stdenv.mkDerivation { + name = "purescript-toppokki"; + version = "6983e07bf0aa55ab779bcef12df3df339a2b5bd9"; + src = pkgs.fetchgit { + url = "https://github.com/firefrorefiddle/purescript-toppokki"; + rev = "6983e07bf0aa55ab779bcef12df3df339a2b5bd9"; + sha256 = "01arx2sp2k287cr4y96frnn6jlghcias9hwdr27yr28k4xa5bhfv"; + }; + phases = "installPhase"; + installPhase = "ln -s $src $out"; + }; + + "quickcheck" = pkgs.stdenv.mkDerivation { + name = "quickcheck"; + version = "v7.1.0"; + src = pkgs.fetchgit { + url = "https://github.com/purescript/purescript-quickcheck.git"; + rev = "990fa1cf14b48b827d9b2d115b1c6977c4b0a76d"; + sha256 = "1dxchng3r2mad0505a0c7cc35vs1f7y2xb5i13p59jpdz6ijqa9k"; + }; + phases = "installPhase"; + installPhase = "ln -s $src $out"; + }; + + "quickcheck-combinators" = pkgs.stdenv.mkDerivation { + name = "quickcheck-combinators"; + version = "v0.1.3"; + src = pkgs.fetchgit { + url = "https://github.com/athanclark/purescript-quickcheck-combinators.git"; + rev = "293e5af07ae47b61d4eae5defef4c0f472bfa9ca"; + sha256 = "0bqxz1k2khm1c3j5aqj6cmbw0gbrhs5hl6f16bbqjb8xhglv1wx2"; + }; + phases = "installPhase"; + installPhase = "ln -s $src $out"; + }; + + "quickcheck-laws" = pkgs.stdenv.mkDerivation { + name = "quickcheck-laws"; + version = "v6.0.1"; + src = pkgs.fetchgit { + url = "https://github.com/purescript-contrib/purescript-quickcheck-laws.git"; + rev = "464597522e5e001adc2619676584871f423b9ea0"; + sha256 = "1m397bh2w5a0wvms8rjgfxh71m7krmfkgk11j5krhz86b72k3izd"; + }; + phases = "installPhase"; + installPhase = "ln -s $src $out"; + }; + + "random" = pkgs.stdenv.mkDerivation { + name = "random"; + version = "v5.0.0"; + src = pkgs.fetchgit { + url = "https://github.com/purescript/purescript-random.git"; + rev = "3e02da113c7afbac37ea4e16188c39d3057314d5"; + sha256 = "1v6ykgp8jmx488hq8mgb0l0sf1nyhjs6wq0w279iyibk9jxc6nib"; + }; + phases = "installPhase"; + installPhase = "ln -s $src $out"; + }; + + "rationals" = pkgs.stdenv.mkDerivation { + name = "rationals"; + version = "v5.0.0"; + src = pkgs.fetchgit { + url = "https://github.com/anttih/purescript-rationals.git"; + rev = "8c52d8cc891d1223150a31416220aa9b99404442"; + sha256 = "1idvjvvx5kwmi8kj2ps95bcvlsgij1xgin4jfw3rmcqd930wqq6q"; + }; + phases = "installPhase"; + installPhase = "ln -s $src $out"; + }; + + "record" = pkgs.stdenv.mkDerivation { + name = "record"; + version = "v3.0.0"; + src = pkgs.fetchgit { + url = "https://github.com/purescript/purescript-record.git"; + rev = "091495d61fcaa9d8d8232e7b800f403a3165a38f"; + sha256 = "0yidfvwiajiv8xflfsi2p8dqnp0qmmcz9jry58jyn9ga82z2pqn6"; + }; + phases = "installPhase"; + installPhase = "ln -s $src $out"; + }; + + "refs" = pkgs.stdenv.mkDerivation { + name = "refs"; + version = "v5.0.0"; + src = pkgs.fetchgit { + url = "https://github.com/purescript/purescript-refs.git"; + rev = "f66d3cdf6a6bf4510e5181b3fac215054d8f1e2e"; + sha256 = "1jhc2v784jy8bvkqy4zsh2z7pnqrhwa8n5kx98xhxx73n1bf38sg"; + }; + phases = "installPhase"; + installPhase = "ln -s $src $out"; + }; + + "safe-coerce" = pkgs.stdenv.mkDerivation { + name = "safe-coerce"; + version = "v1.0.0"; + src = pkgs.fetchgit { + url = "https://github.com/purescript/purescript-safe-coerce.git"; + rev = "e719defd227d932da067a1f0d62a60b3d3ff3637"; + sha256 = "0m942lc23317izspz1sxw957mwl9yb9bgk8dh23f7b3a8w9hh8ff"; + }; + phases = "installPhase"; + installPhase = "ln -s $src $out"; + }; + + "safely" = pkgs.stdenv.mkDerivation { + name = "safely"; + version = "v4.0.1"; + src = pkgs.fetchgit { + url = "https://github.com/paf31/purescript-safely.git"; + rev = "19f854737e17b4d058e5a1504a960821db36e4ab"; + sha256 = "1mrpz19smjsamz4cci287z89q715chzxna0gpbvdgivlca4z6879"; + }; + phases = "installPhase"; + installPhase = "ln -s $src $out"; + }; + + "sequences" = pkgs.stdenv.mkDerivation { + name = "sequences"; + version = "v3.0.2"; + src = pkgs.fetchgit { + url = "https://github.com/hdgarrood/purescript-sequences"; + rev = "1f1d828ef30070569c812d0af23eb7253bb1e990"; + sha256 = "0mc0jjs1119c2nyd08yhdmliq3s47lhrdknhziga3lnbzja889k4"; + }; + phases = "installPhase"; + installPhase = "ln -s $src $out"; + }; + + "spec" = pkgs.stdenv.mkDerivation { + name = "spec"; + version = "v5.0.1"; + src = pkgs.fetchgit { + url = "https://github.com/purescript-spec/purescript-spec.git"; + rev = "2cfa11573dbb695c117efce0a8f76a3daba12e87"; + sha256 = "0hpca1sa738029ww74zpw31br5x339q35kzb10iqd55lp6611k80"; + }; + phases = "installPhase"; + installPhase = "ln -s $src $out"; + }; + + "spec-quickcheck" = pkgs.stdenv.mkDerivation { + name = "spec-quickcheck"; + version = "v4.0.0"; + src = pkgs.fetchgit { + url = "https://github.com/purescript-spec/purescript-spec-quickcheck.git"; + rev = "c2991f475b8fa11de8b68bcb5895b36be04d1e82"; + sha256 = "01xcbfyqzax9c5najbfy12q0nvfklfm37llj2vkmi3wgkskg4prz"; + }; + phases = "installPhase"; + installPhase = "ln -s $src $out"; + }; + + "st" = pkgs.stdenv.mkDerivation { + name = "st"; + version = "v5.0.1"; + src = pkgs.fetchgit { + url = "https://github.com/purescript/purescript-st.git"; + rev = "994eb5e650f3caedac385dcc61694f691df57983"; + sha256 = "14hz254f1y0k3v83z719np0ddrgbca0hdsd9dvv244i07vlvm2zj"; + }; + phases = "installPhase"; + installPhase = "ln -s $src $out"; + }; + + "strings" = pkgs.stdenv.mkDerivation { + name = "strings"; + version = "v5.0.0"; + src = pkgs.fetchgit { + url = "https://github.com/purescript/purescript-strings.git"; + rev = "157e372a23e4becd594d7e7bff6f372a6f63dd82"; + sha256 = "0hyaa4d8gyyvac2nxnwqkn2rvi5vax4bi4yv10mpk7rgb8rv7mb8"; + }; + phases = "installPhase"; + installPhase = "ln -s $src $out"; + }; + + "tailrec" = pkgs.stdenv.mkDerivation { + name = "tailrec"; + version = "v5.0.1"; + src = pkgs.fetchgit { + url = "https://github.com/purescript/purescript-tailrec.git"; + rev = "5fbf0ac05dc6ab1a228b2897630195eb7483b962"; + sha256 = "1jjl2q2hyhjcdxpamzr1cdlxhmq2bl170x5p3jajb9zgwkqx0x22"; + }; + phases = "installPhase"; + installPhase = "ln -s $src $out"; + }; + + "test-unit" = pkgs.stdenv.mkDerivation { + name = "test-unit"; + version = "v16.0.0"; + src = pkgs.fetchgit { + url = "https://github.com/bodil/purescript-test-unit.git"; + rev = "56d06897b621df5d2f619433d19ababb5bb8ebd1"; + sha256 = "0qz903phxkgrn7qdz1xi49bydkf5cbxssyb4xk029zi4lshb35mw"; + }; + phases = "installPhase"; + installPhase = "ln -s $src $out"; + }; + + "text-encoding" = pkgs.stdenv.mkDerivation { + name = "text-encoding"; + version = "v1.0.0"; + src = pkgs.fetchgit { + url = "https://github.com/AlexaDeWit/purescript-text-encoding.git"; + rev = "609ea0916f6817971d4a6c11b991b59715aaa096"; + sha256 = "1r6ihj6m6ahp1cjf4i25pq9a00r2mvgrd8794xiapzsaigljz42c"; + }; + phases = "installPhase"; + installPhase = "ln -s $src $out"; + }; + + "these" = pkgs.stdenv.mkDerivation { + name = "these"; + version = "v5.0.0"; + src = pkgs.fetchgit { + url = "https://github.com/purescript-contrib/purescript-these.git"; + rev = "38dcf86a9bd772091e1153f2f1c13223703599b7"; + sha256 = "0d6yg3lwgralh1kcm5cd4myyz66k9qzld61hc5dg3z92d96zbvlr"; + }; + phases = "installPhase"; + installPhase = "ln -s $src $out"; + }; + + "transformers" = pkgs.stdenv.mkDerivation { + name = "transformers"; + version = "v5.2.0"; + src = pkgs.fetchgit { + url = "https://github.com/purescript/purescript-transformers.git"; + rev = "1e5d4193b38c613c97ea1ebdb721c6b94cd8c50a"; + sha256 = "0lggimnq016v98ib6h68gnciraambxrfqm2s033wm34srcy8xs06"; + }; + phases = "installPhase"; + installPhase = "ln -s $src $out"; + }; + + "tuples" = pkgs.stdenv.mkDerivation { + name = "tuples"; + version = "v6.0.1"; + src = pkgs.fetchgit { + url = "https://github.com/purescript/purescript-tuples.git"; + rev = "d4fe8ffe9e8c512111ee0bc18a6ba0fd056a6773"; + sha256 = "0s2ar2gih4r34km8r8dqngh21s8899yb93mb7mips08ndy3ajq3a"; + }; + phases = "installPhase"; + installPhase = "ln -s $src $out"; + }; + + "type-equality" = pkgs.stdenv.mkDerivation { + name = "type-equality"; + version = "v4.0.0"; + src = pkgs.fetchgit { + url = "https://github.com/purescript/purescript-type-equality.git"; + rev = "f7644468f22ed267a15d398173d234fa6f45e2e0"; + sha256 = "126pg4zg3bsrn8dzvv75xp586nznxyswzgjlr7cag3ij3j1z0kl0"; + }; + phases = "installPhase"; + installPhase = "ln -s $src $out"; + }; + + "typelevel" = pkgs.stdenv.mkDerivation { + name = "typelevel"; + version = "v6.0.0"; + src = pkgs.fetchgit { + url = "https://github.com/bodil/purescript-typelevel.git"; + rev = "c7917aa6d43440608e6e04332e4c916a45976313"; + sha256 = "0gxj926ppx6d8inir589x0a30iv29hqc2y6vsa1n1c2vlcqv2zgd"; + }; + phases = "installPhase"; + installPhase = "ln -s $src $out"; + }; + + "typelevel-prelude" = pkgs.stdenv.mkDerivation { + name = "typelevel-prelude"; + version = "v6.0.0"; + src = pkgs.fetchgit { + url = "https://github.com/purescript/purescript-typelevel-prelude.git"; + rev = "83ddcdb23d06c8d5ea6196596a70438f42cd4afd"; + sha256 = "1vwf3yhn8mir5y41wvlyszkgd5fxvrcyfd0l8cn20c8vfq36yzgk"; + }; + phases = "installPhase"; + installPhase = "ln -s $src $out"; + }; + + "uint" = pkgs.stdenv.mkDerivation { + name = "uint"; + version = "v6.0.3"; + src = pkgs.fetchgit { + url = "https://github.com/purescript-contrib/purescript-uint.git"; + rev = "17fda2aff989ad7fa9f29171bf4c1196ca9ed504"; + sha256 = "1lwbkwc3yj0d5qmw7gni924wj47npgy1aqbc0ika4phc4q0shw8d"; + }; + phases = "installPhase"; + installPhase = "ln -s $src $out"; + }; + + "undefined" = pkgs.stdenv.mkDerivation { + name = "undefined"; + version = "v1.0.2"; + src = pkgs.fetchgit { + url = "https://github.com/bklaric/purescript-undefined.git"; + rev = "4012dc06b58feae301140bc081135d0f24c432b0"; + sha256 = "0kj504j3r9wr7m3yhm53bcfdzai0c2g99d2pdxlfinxk4pmixyrd"; + }; + phases = "installPhase"; + installPhase = "ln -s $src $out"; + }; + + "unfoldable" = pkgs.stdenv.mkDerivation { + name = "unfoldable"; + version = "v5.0.0"; + src = pkgs.fetchgit { + url = "https://github.com/purescript/purescript-unfoldable.git"; + rev = "bbcc2b062b9b7d3d61f123cfb32cc8c7fb811aa6"; + sha256 = "1v3bz04wj6hj7s6mcf49hajylg6w58n78q54sqi2ra2zq8h99kpw"; + }; + phases = "installPhase"; + installPhase = "ln -s $src $out"; + }; + + "unicode" = pkgs.stdenv.mkDerivation { + name = "unicode"; + version = "v5.0.0"; + src = pkgs.fetchgit { + url = "https://github.com/purescript-contrib/purescript-unicode.git"; + rev = "8e360802e31d080dec7f3ddf4d3329c56773490f"; + sha256 = "0sqvgl3il2rl3zxkbzsqb19wib108zvyw73jxiavpfdm6hdmnxvc"; + }; + phases = "installPhase"; + installPhase = "ln -s $src $out"; + }; + + "unordered-collections" = pkgs.stdenv.mkDerivation { + name = "unordered-collections"; + version = "v2.1.4"; + src = pkgs.fetchgit { + url = "https://github.com/fehrenbach/purescript-unordered-collections.git"; + rev = "1be289188cef093520098e318ec910cf3ea5b40d"; + sha256 = "0vgfpdymxvgqf3sh8ji2w2b01w3s294v5mh04046s21qaywdi1jh"; + }; + phases = "installPhase"; + installPhase = "ln -s $src $out"; + }; + + "unsafe-coerce" = pkgs.stdenv.mkDerivation { + name = "unsafe-coerce"; + version = "v5.0.0"; + src = pkgs.fetchgit { + url = "https://github.com/purescript/purescript-unsafe-coerce.git"; + rev = "ee24f0d3b94bf925d9c50fcc2b449579580178c0"; + sha256 = "0l2agnm1k910v4yp1hz19wrsrywsr5scb397762y7pigm3frzs8r"; + }; + phases = "installPhase"; + installPhase = "ln -s $src $out"; + }; + + "untagged-union" = pkgs.stdenv.mkDerivation { + name = "untagged-union"; + version = "v0.3.0"; + src = pkgs.fetchgit { + url = "https://github.com/jvliwanag/purescript-untagged-union.git"; + rev = "364e172e759ebe722bd7ec12a599d532b527c0ef"; + sha256 = "06013431acz8xry9dish8p2qyj18bi505fgfikpjiblxgjazl9zx"; + }; + phases = "installPhase"; + installPhase = "ln -s $src $out"; + }; + + "variant" = pkgs.stdenv.mkDerivation { + name = "variant"; + version = "v7.0.3"; + src = pkgs.fetchgit { + url = "https://github.com/natefaubion/purescript-variant.git"; + rev = "3f12411ede5edd342d25340c1babce9ae81d6793"; + sha256 = "1q2pky3gf177ihy2zjzqvp1cj18ycaki9vm4ghw18p7hf256lqmc"; + }; + phases = "installPhase"; + installPhase = "ln -s $src $out"; + }; + + "web-dom" = pkgs.stdenv.mkDerivation { + name = "web-dom"; + version = "v5.0.0"; + src = pkgs.fetchgit { + url = "https://github.com/purescript-web/purescript-web-dom.git"; + rev = "03dfc2f512e124615ab183ade357e3d54007c79d"; + sha256 = "06g9cp9fkzyfwbz5cs0wxjxgdydm9hy7756p2w4vx94myki20hgx"; + }; + phases = "installPhase"; + installPhase = "ln -s $src $out"; + }; + + "web-events" = pkgs.stdenv.mkDerivation { + name = "web-events"; + version = "v3.0.0"; + src = pkgs.fetchgit { + url = "https://github.com/purescript-web/purescript-web-events.git"; + rev = "c8a50893f04f54e2a59be7f885d25caef3589c57"; + sha256 = "1dxwrl2r39vazb3g1ka4dkpy6idyi17aq4hf9vvdsmcwf2jjwbn9"; + }; + phases = "installPhase"; + installPhase = "ln -s $src $out"; + }; + + "web-file" = pkgs.stdenv.mkDerivation { + name = "web-file"; + version = "v3.0.0"; + src = pkgs.fetchgit { + url = "https://github.com/purescript-web/purescript-web-file.git"; + rev = "3e42263b4392d82c0e379b7a481bbee9b38b1308"; + sha256 = "11x1inhr5pvs2iyg818cywwdskb33q777592sd3b4g4jyczcb1li"; + }; + phases = "installPhase"; + installPhase = "ln -s $src $out"; + }; + + "web-html" = pkgs.stdenv.mkDerivation { + name = "web-html"; + version = "v3.2.0"; + src = pkgs.fetchgit { + url = "https://github.com/purescript-web/purescript-web-html.git"; + rev = "3a249b966ee72c19874b4a2ec6db4059087500e4"; + sha256 = "1ds26vwyba0chhpa09m938brw9q8pxjk6z1n3d4nc30hvdkrjnbh"; + }; + phases = "installPhase"; + installPhase = "ln -s $src $out"; + }; + + "web-storage" = pkgs.stdenv.mkDerivation { + name = "web-storage"; + version = "v4.0.0"; + src = pkgs.fetchgit { + url = "https://github.com/purescript-web/purescript-web-storage.git"; + rev = "22fa56bac204c708e521e746ad4ca2b5810f62c5"; + sha256 = "1viy027k9qyr7mckqkvizwbwkfskc6frppsa1v9a0hq6gc08mpjx"; + }; + phases = "installPhase"; + installPhase = "ln -s $src $out"; + }; + + "web-xhr" = pkgs.stdenv.mkDerivation { + name = "web-xhr"; + version = "v4.1.0"; + src = pkgs.fetchgit { + url = "https://github.com/purescript-web/purescript-web-xhr.git"; + rev = "997b87caa6dcdf66b6db22f29f522d722559956b"; + sha256 = "0hzmqga8l24l20kyd98bcpd8bmz7by14vl311m9yfdg5mjkjg42g"; + }; + phases = "installPhase"; + installPhase = "ln -s $src $out"; + }; + + }; + + cpPackage = pkg: + let + target = ".spago/${pkg.name}/${pkg.version}"; + in '' + if [ ! -e ${target} ]; then + echo "Installing ${target}." + mkdir -p ${target} + cp --no-preserve=mode,ownership,timestamp -r ${toString pkg.outPath}/* ${target} + else + echo "${target} already exists. Skipping." + fi + ''; + + getGlob = pkg: ''".spago/${pkg.name}/${pkg.version}/src/**/*.purs"''; + + getStoreGlob = pkg: ''"${pkg.outPath}/src/**/*.purs"''; + +in { + inherit inputs; + + installSpagoStyle = pkgs.writeShellScriptBin "install-spago-style" '' + set -e + echo installing dependencies... + ${builtins.toString (builtins.map cpPackage (builtins.attrValues inputs))} + echo "echo done." + ''; + + buildSpagoStyle = pkgs.writeShellScriptBin "build-spago-style" '' + set -e + echo building project... + purs compile ${builtins.toString (builtins.map getGlob (builtins.attrValues inputs))} "$@" + echo done. + ''; + + buildFromNixStore = pkgs.writeShellScriptBin "build-from-store" '' + set -e + echo building project using sources from nix store... + purs compile ${builtins.toString ( + builtins.map getStoreGlob (builtins.attrValues inputs))} "$@" + echo done. + ''; + + mkBuildProjectOutput = + { src, purs }: + + pkgs.stdenv.mkDerivation { + name = "build-project-output"; + src = src; + + buildInputs = [ purs ]; + + installPhase = '' + mkdir -p $out + purs compile "$src/**/*.purs" ${builtins.toString + (builtins.map + (x: ''"${x.outPath}/src/**/*.purs"'') + (builtins.attrValues inputs))} + mv output $out + ''; + }; +} diff --git a/templates/ctl-scaffold/spago.dhall b/templates/ctl-scaffold/spago.dhall new file mode 100644 index 0000000000..6d527036b4 --- /dev/null +++ b/templates/ctl-scaffold/spago.dhall @@ -0,0 +1,13 @@ +{- +Welcome to a Spago project! +You can edit this file as you like. +-} +{ name = "ctl-package-example" +, dependencies = + [ "cardano-transaction-lib" + , "bigints" + , "uint" + ] +, packages = ./packages.dhall +, sources = [ "src/**/*.purs", "exe/**/*.purs", "test/**/*.purs" ] +} diff --git a/templates/ctl-scaffold/src/Scaffold.purs b/templates/ctl-scaffold/src/Scaffold.purs new file mode 100644 index 0000000000..3183cd3796 --- /dev/null +++ b/templates/ctl-scaffold/src/Scaffold.purs @@ -0,0 +1,11 @@ +module Scaffold (contract) where + +import Contract.Prelude + +import Contract.Address as Contract.Address +import Contract.Log as Contract.Log +import Contract.Monad (Contract) + +contract :: Contract () Unit +contract = Contract.Log.logInfo' <<< show =<< + Contract.Address.ownPaymentPubKeyHash diff --git a/templates/ctl-scaffold/test/Main.purs b/templates/ctl-scaffold/test/Main.purs new file mode 100644 index 0000000000..168d9172a8 --- /dev/null +++ b/templates/ctl-scaffold/test/Main.purs @@ -0,0 +1,54 @@ +module Scaffold.Test.Main (main) where + +import Contract.Prelude + +import Contract.Monad as Contract.Monad +import Contract.Test.Plutip as Contract.Test.Plutip +import Contract.Wallet as Contract.Wallet +import Data.BigInt as BigInt +import Data.UInt as UInt +import Scaffold as Scaffold + +main :: Effect Unit +main = Contract.Monad.launchAff_ $ do + let + distribution :: Contract.Test.Plutip.InitialUTxO + distribution = + [ BigInt.fromInt 5_000_000 + , BigInt.fromInt 2_000_000_000 + ] + Contract.Test.Plutip.runPlutipContract config distribution \alice -> + Contract.Wallet.withKeyWallet alice $ do + Scaffold.contract + +config :: Contract.Test.Plutip.PlutipConfig +config = + { host: "127.0.0.1" + , port: UInt.fromInt 8082 + , logLevel: Trace + , ogmiosConfig: + { port: UInt.fromInt 1338 + , host: "127.0.0.1" + , secure: false + , path: Nothing + } + , ogmiosDatumCacheConfig: + { port: UInt.fromInt 10000 + , host: "127.0.0.1" + , secure: false + , path: Nothing + } + , ctlServerConfig: + { port: UInt.fromInt 8083 + , host: "127.0.0.1" + , secure: false + , path: Nothing + } + , postgresConfig: + { host: "127.0.0.1" + , port: UInt.fromInt 5433 + , user: "ctxlib" + , password: "ctxlib" + , dbname: "ctxlib" + } + } diff --git a/templates/ctl-scaffold/webpack.config.js b/templates/ctl-scaffold/webpack.config.js new file mode 100644 index 0000000000..a21a26f4cb --- /dev/null +++ b/templates/ctl-scaffold/webpack.config.js @@ -0,0 +1,92 @@ +"use strict"; + +const path = require("path"); +const webpack = require("webpack"); +const HtmlWebpackPlugin = require("html-webpack-plugin"); +const NodePolyfillPlugin = require("node-polyfill-webpack-plugin"); + +module.exports = { + mode: "development", + + experiments: { + asyncWebAssembly: false, + layers: false, + lazyCompilation: false, + outputModule: true, + syncWebAssembly: true, + topLevelAwait: true, + }, + + devtool: "eval-source-map", + + stats: { errorDetails: true }, + + devServer: { + port: 4008, + }, + + // we can add more entrypoints as needed + entry: "./index.js", + + output: { + path: path.resolve(__dirname, "dist"), + filename: "bundle.js", + }, + + module: { + rules: [ + { + test: /\.(png|jpg|gif)$/i, + type: "asset", + }, + { + test: /\.plutus$/i, + type: "asset/source", + }, + ], + }, + + resolve: { + modules: [process.env.NODE_PATH], + extensions: [".js"], + fallback: { + buffer: require.resolve("buffer/"), + http: false, + url: false, + stream: false, + crypto: false, + https: false, + net: false, + tls: false, + zlib: false, + os: false, + path: false, + fs: false, + }, + alias: { + // You should update this path to the location of your compiled scripts, + // relative to `webpack.config.js` + Scripts: path.resolve(__dirname, "fixtures/scripts"), + }, + }, + + plugins: [ + new webpack.DefinePlugin({ + BROWSER_RUNTIME: !!process.env.BROWSER_RUNTIME, + }), + new NodePolyfillPlugin(), + new webpack.LoaderOptionsPlugin({ + debug: true, + }), + new HtmlWebpackPlugin({ + title: "ctl-scaffold", + template: "./index.html", + inject: false, // See stackoverflow.com/a/38292765/3067181 + }), + new webpack.ProvidePlugin({ + Buffer: ["buffer", "Buffer"], + }), + new webpack.ContextReplacementPlugin(/cardano-serialization-lib-browser/), + new webpack.ContextReplacementPlugin(/cardano-serialization-lib-nodejs/), + ], +}; diff --git a/test-data/chrome-extensions/gero_testnet_1.10.0_0.crx b/test-data/chrome-extensions/gero_testnet_1.10.0_0.crx new file mode 100644 index 0000000000..d4ad55b2fa Binary files /dev/null and b/test-data/chrome-extensions/gero_testnet_1.10.0_0.crx differ diff --git a/test-data/chrome-extensions/nami_3.2.5_1.crx b/test-data/chrome-extensions/nami_3.2.5_1.crx new file mode 100644 index 0000000000..bc30216394 Binary files /dev/null and b/test-data/chrome-extensions/nami_3.2.5_1.crx differ diff --git a/test-data/gero_settings.tar.gz b/test-data/gero_settings.tar.gz new file mode 100644 index 0000000000..8b5c253749 Binary files /dev/null and b/test-data/gero_settings.tar.gz differ diff --git a/test-data/nami_settings.tar.gz b/test-data/nami_settings.tar.gz new file mode 100644 index 0000000000..440fe75bba Binary files /dev/null and b/test-data/nami_settings.tar.gz differ diff --git a/test/AffInterface.purs b/test/AffInterface.purs index 3e76c72e66..9b71019c2a 100644 --- a/test/AffInterface.purs +++ b/test/AffInterface.purs @@ -23,8 +23,8 @@ import QueryM , getDatumsByHashes , runQueryM , submitTxOgmios - , traceQueryConfig ) +import QueryM.Config (testnetTraceQueryConfig) import QueryM.CurrentEpoch (getCurrentEpoch) import QueryM.EraSummaries (getEraSummaries) import QueryM.Ogmios (EraSummaries, OgmiosAddress, SystemStart) @@ -95,19 +95,17 @@ suite = do testOgmiosDatumCacheGetDatumsByHashes testOgmiosDatumCacheGetDatumByHash :: Aff Unit -testOgmiosDatumCacheGetDatumByHash = - traceQueryConfig >>= flip runQueryM do - -- Use this to trigger block fetching in order to actually get the datum: - -- ``` - -- curl localhost:9999/control/fetch_blocks -X POST -d '{"slot": 54066900, "id": "6eb2542a85f375d5fd6cbc1c768707b0e9fe8be85b7b1dd42a85017a70d2623d", "datumFilter": {"address": "addr_xyz"}}' -H 'Content-Type: application/json' - -- ``` - _datum <- getDatumByHash $ DataHash $ hexToByteArrayUnsafe - "f7c47c65216f7057569111d962a74de807de57e79f7efa86b4e454d42c875e4e" - pure unit +testOgmiosDatumCacheGetDatumByHash = runQueryM testnetTraceQueryConfig do + -- Use this to trigger block fetching in order to actually get the datum: + -- ``` + -- curl localhost:9999/control/fetch_blocks -X POST -d '{"slot": 54066900, "id": "6eb2542a85f375d5fd6cbc1c768707b0e9fe8be85b7b1dd42a85017a70d2623d", "datumFilter": {"address": "addr_xyz"}}' -H 'Content-Type: application/json' + -- ``` + void $ getDatumByHash $ DataHash $ hexToByteArrayUnsafe + "f7c47c65216f7057569111d962a74de807de57e79f7efa86b4e454d42c875e4e" testOgmiosDatumCacheGetDatumsByHashes :: Aff Unit testOgmiosDatumCacheGetDatumsByHashes = - traceQueryConfig >>= flip runQueryM do + runQueryM testnetTraceQueryConfig do -- Use this to trigger block fetching in order to actually get the datum: -- ``` -- curl localhost:9999/control/fetch_blocks -X POST -d '{"slot": 54066900, "id": "6eb2542a85f375d5fd6cbc1c768707b0e9fe8be85b7b1dd42a85017a70d2623d", "datumFilter": {"address": "addr_xyz"}}' -H 'Content-Type: application/json' @@ -119,17 +117,16 @@ testOgmiosDatumCacheGetDatumsByHashes = testUtxosAt :: OgmiosAddress -> Aff Unit testUtxosAt testAddr = case ogmiosAddressToAddress testAddr of Nothing -> liftEffect $ throw "Failed UtxosAt" - Just addr -> flip runQueryM (void $ utxosAt addr) =<< traceQueryConfig + Just addr -> runQueryM testnetTraceQueryConfig (void $ utxosAt addr) testGetChainTip :: Aff Unit testGetChainTip = do - flip runQueryM (void getChainTip) =<< traceQueryConfig + runQueryM testnetTraceQueryConfig (void getChainTip) testWaitUntilSlot :: Aff Unit testWaitUntilSlot = do - cfg <- traceQueryConfig - void $ runQueryM cfg do - getChainTip >>= case _ of + runQueryM testnetTraceQueryConfig do + void $ getChainTip >>= case _ of TipAtGenesis -> liftEffect $ throw "Tip is at genesis" Tip (ChainTip { slot }) -> do waitUntilSlot $ over Slot @@ -144,29 +141,28 @@ testFromOgmiosAddress testAddr = do testGetEraSummaries :: Aff Unit testGetEraSummaries = do - flip runQueryM (void getEraSummaries) =<< traceQueryConfig + runQueryM testnetTraceQueryConfig (void getEraSummaries) testSubmitTxFailure :: Aff Unit testSubmitTxFailure = do - flip runQueryM (void $ submitTxOgmios (wrap $ hexToByteArrayUnsafe "00")) =<< - traceQueryConfig + runQueryM testnetTraceQueryConfig + (void $ submitTxOgmios (wrap $ hexToByteArrayUnsafe "00")) testGetProtocolParameters :: Aff Unit testGetProtocolParameters = do - flip runQueryM (void getProtocolParameters) =<< - traceQueryConfig + runQueryM testnetTraceQueryConfig (void getProtocolParameters) testGetCurrentEpoch :: Aff Unit testGetCurrentEpoch = do - flip runQueryM (void getCurrentEpoch) =<< traceQueryConfig + runQueryM testnetTraceQueryConfig (void getCurrentEpoch) testGetSystemStart :: Aff Unit testGetSystemStart = do - flip runQueryM (void getSystemStart) =<< traceQueryConfig + runQueryM testnetTraceQueryConfig (void getSystemStart) testPosixTimeToSlot :: Aff Unit testPosixTimeToSlot = do - traceQueryConfig >>= flip runQueryM do + runQueryM testnetTraceQueryConfig do eraSummaries <- getEraSummaries sysStart <- getSystemStart let @@ -249,7 +245,7 @@ mkPosixTime = POSIXTime <<< unsafePartial fromJust <<< BigInt.fromString testSlotToPosixTime :: Aff Unit testSlotToPosixTime = do - traceQueryConfig >>= flip runQueryM do + runQueryM testnetTraceQueryConfig do eraSummaries <- getEraSummaries sysStart <- getSystemStart -- See *Testing far into the future note during hardforks:* for details on @@ -280,7 +276,7 @@ testSlotToPosixTime = do testPosixTimeToSlotError :: Aff Unit testPosixTimeToSlotError = do - traceQueryConfig >>= flip runQueryM do + runQueryM testnetTraceQueryConfig do eraSummaries <- getEraSummaries sysStart <- getSystemStart let diff --git a/test/Base64.purs b/test/Base64.purs new file mode 100644 index 0000000000..d8f36862e0 --- /dev/null +++ b/test/Base64.purs @@ -0,0 +1,20 @@ +module Test.Base64 (suite) where + +import Prelude + +import Base64 (fromByteArray, mkBase64String, toByteArray, unBase64String) +import Data.Maybe (Maybe(Just)) +import Effect.Class (liftEffect) +import Mote (group, test) +import Test.QuickCheck (quickCheck, (===)) +import TestM (TestPlanM) + +suite :: TestPlanM Unit +suite = do + group "Base64" do + test "toByteArray . fromByteArray = id" $ liftEffect do + quickCheck \bytes -> + toByteArray (fromByteArray bytes) === bytes + test "mkBase64String <<< unBase64String = Just" $ liftEffect do + quickCheck \base64Str -> + mkBase64String (unBase64String base64Str) === Just base64Str diff --git a/test/E2E.purs b/test/E2E.purs new file mode 100644 index 0000000000..835b7c3cb6 --- /dev/null +++ b/test/E2E.purs @@ -0,0 +1,40 @@ +module Test.E2E (main) where + +import Data.Newtype (wrap) +import Effect (Effect) +import Effect.Aff (launchAff_) +import Effect.Class (liftEffect) +import Mote (group) +import Prelude (Unit, ($), bind, discard, pure) +import Contract.Test.E2E (TestOptions, parseOptions) +import Test.E2E.Examples.Gero as Gero +import Test.E2E.Examples.Pkh2PkhGero as Pkh2PkhGero +import Test.E2E.Examples.Pkh2Pkh as Pkh2Pkh +import Test.E2E.Examples.AlwaysMints as AlwaysMints +import Test.E2E.Examples.AlwaysSucceeds as AlwaysSucceeds +import Test.E2E.Examples.Datums as Datums +import Test.E2E.Examples.MintsMultipleTokens as MintsMultipleTokens +import Test.E2E.Examples.SignMultiple as SignMultiple +import Test.Spec.Runner as SpecRunner +import Test.Utils as Utils +import TestM (TestPlanM) + +-- Run with `spago test --main Test.E2E` +main :: Effect Unit +main = launchAff_ $ do + options <- liftEffect parseOptions + Utils.interpretWithConfig + (SpecRunner.defaultConfig { timeout = pure $ wrap 500_000.0 }) + (testPlan options) + +-- Requires external services listed in README.md +testPlan :: TestOptions -> TestPlanM Unit +testPlan options = group "e2e tests" do + Gero.runExample options + Pkh2PkhGero.runExample options + Pkh2Pkh.runExample options + AlwaysMints.runExample options + AlwaysSucceeds.runExample options + Datums.runExample options + MintsMultipleTokens.runExample options + SignMultiple.runExample options diff --git a/test/E2E/Examples/AlwaysMints.purs b/test/E2E/Examples/AlwaysMints.purs new file mode 100644 index 0000000000..f278296e3d --- /dev/null +++ b/test/E2E/Examples/AlwaysMints.purs @@ -0,0 +1,18 @@ +module Test.E2E.Examples.AlwaysMints (runExample) where + +import Prelude + +import Contract.Test.E2E (TestOptions, WalletExt(NamiExt)) +import Test.E2E.Helpers + ( namiSign' + , namiConfirmAccess + , delaySec + , runE2ETest + ) +import TestM (TestPlanM) + +runExample :: TestOptions -> TestPlanM Unit +runExample options = runE2ETest "AlwaysMints" options NamiExt $ \example -> do + namiConfirmAccess example + delaySec 3.0 + namiSign' example diff --git a/test/E2E/Examples/AlwaysSucceeds.purs b/test/E2E/Examples/AlwaysSucceeds.purs new file mode 100644 index 0000000000..9b8dac1668 --- /dev/null +++ b/test/E2E/Examples/AlwaysSucceeds.purs @@ -0,0 +1,24 @@ +module Test.E2E.Examples.AlwaysSucceeds (runExample) where + +import Prelude + +import Contract.Test.E2E (TestOptions, WalletExt(NamiExt)) +import Test.E2E.Helpers + ( namiSign' + , namiConfirmAccess + , delaySec + , runE2ETest + ) +import TestM (TestPlanM) +import Effect.Console (log) +import Effect.Class (liftEffect) + +runExample :: TestOptions -> TestPlanM Unit +runExample options = runE2ETest "AlwaysSucceeds" options NamiExt $ \example -> + do + namiConfirmAccess example + namiSign' example + liftEffect $ log $ + " ...waiting before trying to spend script output (this will take a minute)" + delaySec 65.0 + namiSign' example diff --git a/test/E2E/Examples/Datums.purs b/test/E2E/Examples/Datums.purs new file mode 100644 index 0000000000..e35e3d1936 --- /dev/null +++ b/test/E2E/Examples/Datums.purs @@ -0,0 +1,17 @@ +module Test.E2E.Examples.Datums (runExample) where + +import Prelude + +import Contract.Test.E2E (TestOptions, WalletExt(NamiExt)) +import Test.E2E.Helpers + ( namiSign' + , namiConfirmAccess + , runE2ETest + ) +import TestM (TestPlanM) + +runExample :: TestOptions -> TestPlanM Unit +runExample options = runE2ETest "Datums" options NamiExt + $ \example -> do + namiConfirmAccess example + namiSign' example diff --git a/test/E2E/Examples/Gero.purs b/test/E2E/Examples/Gero.purs new file mode 100644 index 0000000000..7eee38d75c --- /dev/null +++ b/test/E2E/Examples/Gero.purs @@ -0,0 +1,14 @@ +module Test.E2E.Examples.Gero (runExample) where + +import Prelude + +import TestM (TestPlanM) +import Contract.Test.E2E (TestOptions, WalletExt(GeroExt)) +import Test.E2E.Helpers + ( geroConfirmAccess + , runE2ETest + ) + +runExample :: TestOptions -> TestPlanM Unit +runExample options = runE2ETest "Gero" options GeroExt geroConfirmAccess + diff --git a/test/E2E/Examples/MintsMultipleTokens.purs b/test/E2E/Examples/MintsMultipleTokens.purs new file mode 100644 index 0000000000..5d66b0a148 --- /dev/null +++ b/test/E2E/Examples/MintsMultipleTokens.purs @@ -0,0 +1,21 @@ +module Test.E2E.Examples.MintsMultipleTokens (runExample) where + +import Prelude + +import Contract.Test.E2E + ( TestOptions + , WalletExt(NamiExt) + ) +import Test.E2E.Helpers + ( namiSign' + , namiConfirmAccess + , runE2ETest + ) + +import TestM (TestPlanM) + +runExample :: TestOptions -> TestPlanM Unit +runExample options = runE2ETest "MintsMultipleTokens" options NamiExt $ + \example -> do + namiConfirmAccess example + namiSign' example diff --git a/test/E2E/Examples/Pkh2Pkh.purs b/test/E2E/Examples/Pkh2Pkh.purs new file mode 100644 index 0000000000..e661182346 --- /dev/null +++ b/test/E2E/Examples/Pkh2Pkh.purs @@ -0,0 +1,16 @@ +module Test.E2E.Examples.Pkh2Pkh (runExample) where + +import Prelude + +import Contract.Test.E2E (TestOptions, WalletExt(NamiExt)) +import Test.E2E.Helpers + ( namiSign' + , namiConfirmAccess + , runE2ETest + ) +import TestM (TestPlanM) + +runExample :: TestOptions -> TestPlanM Unit +runExample options = runE2ETest "Pkh2Pkh" options NamiExt $ \example -> do + namiConfirmAccess example + namiSign' example diff --git a/test/E2E/Examples/Pkh2PkhGero.purs b/test/E2E/Examples/Pkh2PkhGero.purs new file mode 100644 index 0000000000..8c8a7c7b7c --- /dev/null +++ b/test/E2E/Examples/Pkh2PkhGero.purs @@ -0,0 +1,19 @@ +module Test.E2E.Examples.Pkh2PkhGero (runExample) where + +import Prelude + +import TestM (TestPlanM) +import Contract.Test.E2E (TestOptions, WalletExt(GeroExt)) +import Test.E2E.Helpers + ( geroSign' + , geroConfirmAccess + , delaySec + , runE2ETest + ) + +runExample :: TestOptions -> TestPlanM Unit +runExample options = runE2ETest "Pkh2PkhGero" options GeroExt $ \example -> do + geroConfirmAccess example + delaySec 7.0 + geroSign' example + diff --git a/test/E2E/Examples/SignMultiple.purs b/test/E2E/Examples/SignMultiple.purs new file mode 100644 index 0000000000..432a42b62b --- /dev/null +++ b/test/E2E/Examples/SignMultiple.purs @@ -0,0 +1,21 @@ +module Test.E2E.Examples.SignMultiple (runExample) where + +import Prelude + +import Contract.Test.E2E (TestOptions, WalletExt(NamiExt)) +import Test.E2E.Helpers + ( namiSign' + , namiConfirmAccess + , delaySec + , runE2ETest + ) +import TestM (TestPlanM) + +runExample :: TestOptions -> TestPlanM Unit +runExample options = runE2ETest "SignMultiple" options NamiExt $ \example -> do + namiConfirmAccess example + namiSign' example + -- Wait a moment to avoid a race condition. After Nami gets confirmation, + -- it will take a few ms to return control to our example. + delaySec 1.0 + namiSign' example diff --git a/test/E2E/Helpers.purs b/test/E2E/Helpers.purs new file mode 100644 index 0000000000..9c748b7a5c --- /dev/null +++ b/test/E2E/Helpers.purs @@ -0,0 +1,86 @@ +-- | Augmented version of Contract.Test.E2E.Helpers, with some functions that +-- | are only useful for testing CTL itself. +module Test.E2E.Helpers + ( module E2EHelpers + , runE2ETest + , exampleUrl + , namiSign' + , geroSign' + ) where + +import Prelude + +import Contract.Test.E2E + ( TestOptions + , withBrowser + , WalletExt + , resetTestFeedback + , RunningExample + , WalletPassword + , checkSuccess + , delaySec + , geroSign + , namiSign + , withExample + ) +import Contract.Test.E2E + ( E2EOutput + , RunningExample(RunningExample) + , WalletPassword(WalletPassword) + , checkSuccess + , delaySec + , geroConfirmAccess + , geroSign + , namiConfirmAccess + , namiSign + , withExample + ) as E2EHelpers +import Control.Monad.Error.Class (try) +import Data.Newtype (wrap, unwrap) +import Effect.Aff (Aff) +import Effect.Class (liftEffect) +import Effect.Console (log) +import Mote (test) +import TestM (TestPlanM) +import Test.Spec.Assertions (shouldSatisfy) +import Toppokki as Toppokki + +exampleUrl :: String -> Toppokki.URL +exampleUrl exampleName = wrap $ "http://localhost:4008/?" <> exampleName + +testPasswordNami :: WalletPassword +testPasswordNami = wrap "ctlctlctl" + +testPasswordGero :: WalletPassword +testPasswordGero = wrap "VZVfu5rp1r" + +-- | Run an E2E test. Parameters are: +-- | String: Just a name for the logs +-- | Toppokki.URL: URL where the example is running +-- | TestOptions: Options to start the browser with +-- | WalletExt: An extension which should be used +-- | RunningExample -> Aff a: A function which runs the test +runE2ETest + :: forall (a :: Type) + . String + -> TestOptions + -> WalletExt + -> (RunningExample -> Aff a) + -> TestPlanM Unit +runE2ETest example opts ext f = test example $ withBrowser opts ext $ + \browser -> withExample (exampleUrl example) browser + ( \e -> do + liftEffect $ log $ "Start Example " <> example + resetTestFeedback (_.main $ unwrap e) + void $ try $ f e + delaySec 10.0 + liftEffect $ log $ "Example " <> example <> + " finished, check success..." + checkSuccess e >>= flip shouldSatisfy (_ == true) + ) + +namiSign' :: RunningExample -> Aff Unit +namiSign' = namiSign testPasswordNami + +geroSign' :: RunningExample -> Aff Unit +geroSign' = geroSign testPasswordGero diff --git a/test/Fixtures.purs b/test/Fixtures.purs index 39218e6826..d6b61d7f52 100644 --- a/test/Fixtures.purs +++ b/test/Fixtures.purs @@ -12,7 +12,11 @@ module Test.Fixtures ( addressString1 , cip25MetadataFixture1 + , cip25MetadataFixture2 + , cip25MetadataFixture3 , cip25MetadataJsonFixture1 + , cip25MetadataJsonFixture2 + , cip25MetadataJsonFixture3 , currencySymbol1 , ed25519KeyHashFixture1 , mkSampleTx @@ -59,6 +63,7 @@ module Test.Fixtures , witnessSetFixture3 , witnessSetFixture3Value , witnessSetFixture4 + , unsafeMkCip25String ) where import Prelude @@ -123,14 +128,15 @@ import Data.BigInt as BigInt import Data.Either (fromRight) import Data.Map as Map import Data.Maybe (Maybe(Just, Nothing), fromJust) -import Data.NonEmpty ((:|)) import Data.Set (Set) import Data.Set (singleton) as Set import Data.Tuple.Nested ((/\)) import Data.UInt as UInt import Deserialization.FromBytes (fromBytes) import Effect (Effect) -import Metadata.Cip25 +import Metadata.Cip25.Cip25String (Cip25String, mkCip25String) +import Metadata.Cip25.Common (Cip25TokenName(Cip25TokenName)) +import Metadata.Cip25.V2 ( Cip25Metadata(Cip25Metadata) , Cip25MetadataEntry(Cip25MetadataEntry) , Cip25MetadataFile(Cip25MetadataFile) @@ -169,9 +175,7 @@ import Types.Int as Int import Types.PlutusData as PD import Types.RawBytes (rawBytesFromIntArrayUnsafe, hexToRawBytesUnsafe) import Types.RedeemerTag (RedeemerTag(Spend)) -import Types.Scripts - ( MintingPolicyHash(MintingPolicyHash) - ) +import Types.Scripts (MintingPolicyHash(MintingPolicyHash)) import Types.TokenName (TokenName, mkTokenName) import Types.Transaction ( TransactionHash(TransactionHash) @@ -454,7 +458,7 @@ proposedProtocolParameterUpdates1 = ProposedProtocolParameterUpdates $ , extraEntropy: Nothing -- Just $ HashNonce $ hexToByteArrayUnsafe -- "5d677265fa5bb21ce6d8c7502aca70b9316d10e958611f3c6b758f6500000000" , protocolVersion: Just - [ { major: UInt.fromInt 1, minor: UInt.fromInt 1 } ] + { major: UInt.fromInt 1, minor: UInt.fromInt 1 } , minPoolCost: Just bigNumOne , adaPerUtxoByte: Just bigNumOne , costModels: Just costModelsFixture1 @@ -650,7 +654,7 @@ txBinaryFixture4 = \7f13c113ad5f9b22212703482cb30105a1581de01730b1b700d616d51555538e83d67f13c113\ \ad5f9b22212703482cb3010682a1581c5d677265fa5bb21ce6d8c7502aca70b9316d10e95861\ \1f3c6b758f65b4000101010219271003192710041903e8050106010701080109d81e8201010a\ - \d81e8201010bd81e8201010e8101011001110112a10098a61a000302590001011a00060bc719\ + \d81e8201010bd81e8201010e8201011001110112a10098a61a000302590001011a00060bc719\ \026d00011a000249f01903e800011a000249f018201a0025cea81971f70419744d186419744d\ \186419744d186419744d186419744d186419744d18641864186419744d18641a000249f01820\ \1a000249f018201a000249f018201a000249f01903e800011a000249f018201a000249f01903\ @@ -1123,33 +1127,35 @@ policyId = MintingPolicyHash scriptHash1 cip25MetadataFilesFixture1 :: Array Cip25MetadataFile cip25MetadataFilesFixture1 = Cip25MetadataFile <$> - [ { name: "file_name_1" - , mediaType: "media_type" - , uris: "uri1" :| [ "uri2", "uri3" ] + [ { name: unsafeMkCip25String "file_name_1" + , mediaType: unsafeMkCip25String "media_type" + , src: "uri1" } - , { name: "file_name_2" - , mediaType: "media_type_2" - , uris: "uri4" :| [ "uri5", "uri6" ] + , { name: unsafeMkCip25String "file_name_2" + , mediaType: unsafeMkCip25String "media_type_2" + , src: "uri4" } ] cip25MetadataEntryFixture1 :: Cip25MetadataEntry cip25MetadataEntryFixture1 = Cip25MetadataEntry { policyId: policyId - , assetName: tokenName1 - , imageUris: "image_uri1" :| [ "image_uri2", "image_uri3" ] - , mediaType: Just "media_type" - , description: [ "desc1", "desc2", "desc3" ] + , assetName: Cip25TokenName tokenName1 + , name: unsafeMkCip25String "ItestToken" + , image: "image_uri1" + , mediaType: Just $ unsafeMkCip25String "media_type" + , description: Just "desc1" , files: cip25MetadataFilesFixture1 } cip25MetadataEntryFixture2 :: Cip25MetadataEntry cip25MetadataEntryFixture2 = Cip25MetadataEntry { policyId: policyId - , assetName: tokenName2 - , imageUris: "image_uri1" :| [] + , assetName: Cip25TokenName tokenName2 + , name: unsafeMkCip25String "TestToken2" + , image: "image_uri1" , mediaType: Nothing - , description: [] + , description: Nothing , files: [] } @@ -1157,11 +1163,52 @@ cip25MetadataFixture1 :: Cip25Metadata cip25MetadataFixture1 = Cip25Metadata [ cip25MetadataEntryFixture1, cip25MetadataEntryFixture2 ] +cip25MetadataFixture2 :: Cip25Metadata +cip25MetadataFixture2 = Cip25Metadata + [ Cip25MetadataEntry + { policyId: policyId + , assetName: Cip25TokenName tokenName1 + , name: unsafeMkCip25String "ItestToken" + , image: "image_uri1" + , mediaType: Nothing + , description: Nothing + , files: [] + } + ] + +cip25MetadataFixture3 :: Cip25Metadata +cip25MetadataFixture3 = Cip25Metadata + [ Cip25MetadataEntry + { policyId: policyId + , assetName: Cip25TokenName tokenName1 + , name: unsafeMkCip25String "monkey.jpg" + , image: + -- checking long strings + "https://upload.wikimedia.org/wikipedia/commons/3/35/Olive_baboon_Ngorongoro.jpg?download" + , mediaType: Nothing + , description: Nothing + , files: [] + } + ] + +unsafeMkCip25String :: String -> Cip25String +unsafeMkCip25String str = unsafePartial $ fromJust $ mkCip25String str + cip25MetadataJsonFixture1 :: Effect Aeson cip25MetadataJsonFixture1 = readTextFile UTF8 "test/Fixtures/cip25MetadataJsonFixture1.json" >>= pure <<< fromRight aesonNull <<< parseJsonStringToAeson +cip25MetadataJsonFixture2 :: Effect Aeson +cip25MetadataJsonFixture2 = + readTextFile UTF8 "test/Fixtures/cip25MetadataJsonFixture2.json" >>= + pure <<< fromRight aesonNull <<< parseJsonStringToAeson + +cip25MetadataJsonFixture3 :: Effect Aeson +cip25MetadataJsonFixture3 = + readTextFile UTF8 "test/Fixtures/cip25MetadataJsonFixture3.json" >>= + pure <<< fromRight aesonNull <<< parseJsonStringToAeson + ogmiosEvaluateTxValidRespFixture :: Effect Aeson ogmiosEvaluateTxValidRespFixture = readTextFile UTF8 "test/Fixtures/OgmiosEvaluateTxValidRespFixture.json" >>= diff --git a/test/Fixtures/cip25MetadataJsonFixture1.json b/test/Fixtures/cip25MetadataJsonFixture1.json index eb934eda22..27df74f96b 100644 --- a/test/Fixtures/cip25MetadataJsonFixture1.json +++ b/test/Fixtures/cip25MetadataJsonFixture1.json @@ -3,35 +3,19 @@ "5d677265fa5bb21ce6d8c7502aca70b9316d10e958611f3c6b758f65": { "ItestToken": { "name": "ItestToken", - "image": [ - "image_uri1", - "image_uri2", - "image_uri3" - ], + "image": "image_uri1", "mediaType": "media_type", - "description": [ - "desc1", - "desc2", - "desc3" - ], + "description": "desc1", "files": [ { "name": "file_name_1", "mediaType": "media_type", - "src": [ - "uri1", - "uri2", - "uri3" - ] + "src": "uri1" }, { "name": "file_name_2", "mediaType": "media_type_2", - "src": [ - "uri4", - "uri5", - "uri6" - ] + "src": "uri4" } ] }, diff --git a/test/Fixtures/cip25MetadataJsonFixture2.json b/test/Fixtures/cip25MetadataJsonFixture2.json new file mode 100644 index 0000000000..df8f773867 --- /dev/null +++ b/test/Fixtures/cip25MetadataJsonFixture2.json @@ -0,0 +1,10 @@ +{ + "721": { + "5d677265fa5bb21ce6d8c7502aca70b9316d10e958611f3c6b758f65": { + "ItestToken": { + "name": "ItestToken", + "image": "image_uri1" + } + } + } +} diff --git a/test/Fixtures/cip25MetadataJsonFixture3.json b/test/Fixtures/cip25MetadataJsonFixture3.json new file mode 100644 index 0000000000..0a212349df --- /dev/null +++ b/test/Fixtures/cip25MetadataJsonFixture3.json @@ -0,0 +1,18 @@ +{ + "721": { + "5d677265fa5bb21ce6d8c7502aca70b9316d10e958611f3c6b758f65": { + "ItestToken": { + "name": "ItestToken", + "image": "image_uri1", + "description": "desc1", + "files": [ + { + "name": "file_name_1", + "mediaType": "media_type", + "src": "uri1" + } + ] + } + } + } +} diff --git a/test/Main.purs b/test/Main.purs index 8a44d0230d..f257cb91cc 100644 --- a/test/Main.purs +++ b/test/Main.purs @@ -5,7 +5,7 @@ import Prelude import Effect (Effect) import Effect.Aff (launchAff_) import Test.Integration as Integration -import Test.Unit as Unit +import Ctl.Test.Unit as Unit import Test.Utils as Utils main :: Effect Unit diff --git a/test/Metadata/Cip25.purs b/test/Metadata/Cip25.purs index fc818c675e..561c432197 100644 --- a/test/Metadata/Cip25.purs +++ b/test/Metadata/Cip25.purs @@ -3,27 +3,106 @@ module Test.Metadata.Cip25 (suite) where import Prelude import Aeson (decodeAeson) -import Effect.Class (liftEffect) -import Data.Either (Either(Right)) +import Data.Either (Either(Right), hush) import Data.Maybe (Maybe(Just)) +import Data.TextDecoder (decodeUtf8) +import Data.TextEncoding (encodeUtf8) +import Effect.Class (liftEffect) +import FromData (fromData) +import Metadata.Cip25.Cip25String + ( fromDataString + , toCip25Strings + , toDataString + , fromMetadataString + , toMetadataString + ) import Metadata.MetadataType (fromGeneralTxMetadata, toGeneralTxMetadata) import Mote (group, test) -import FromData (fromData) -import ToData (toData) +import Test.Fixtures + ( cip25MetadataFixture1 + , cip25MetadataFixture2 + , cip25MetadataFixture3 + , cip25MetadataJsonFixture1 + , cip25MetadataJsonFixture2 + , unsafeMkCip25String + ) +import Test.QuickCheck ((===)) +import Test.QuickCheck.Combinators ((==>)) import Test.Spec.Assertions (shouldEqual) +import Test.Spec.QuickCheck (quickCheck) import TestM (TestPlanM) -import Test.Fixtures (cip25MetadataFixture1, cip25MetadataJsonFixture1) +import ToData (toData) suite :: TestPlanM Unit suite = do group "CIP25 Metadata" do - test "MetadataType instance" do + test "Long string ToData encoding" do + -- decodeUtf8 is not an inverse of encodeUtf8 + quickCheck $ \str -> do + (hush (decodeUtf8 (encodeUtf8 str)) === Just str) ==> + (fromDataString (toDataString str) === Just str) + test "Long string ToMetadata encoding" do + -- decodeUtf8 is not an inverse of encodeUtf8 + quickCheck $ \str -> do + (hush (decodeUtf8 (encodeUtf8 str)) === Just str) ==> + (fromMetadataString (toMetadataString str) === Just str) + test "toCip25Strings #1" do + toCip25Strings "asd" `shouldEqual` [ unsafeMkCip25String "asd" ] + test "toCip25Strings #2" do + toCip25Strings str80Chars + `shouldEqual` + [ unsafeMkCip25String + "0123456789012345678901234567890123456789012345678901234567890123" + , unsafeMkCip25String "4567890123456789" + ] + test "toCip25Strings #3" do + toCip25Strings str160Chars + `shouldEqual` + [ unsafeMkCip25String + "0123456789012345678901234567890123456789012345678901234567890123" + , unsafeMkCip25String + "4567890123456789012345678901234567890123456789012345678901234567" + , unsafeMkCip25String "89012345678901234567890123456789" + ] + test "toCip25Strings #4" do + toCip25Strings + "0123456789012345678901234567890123456789012345678901234567890123" + `shouldEqual` + [ unsafeMkCip25String + "0123456789012345678901234567890123456789012345678901234567890123" + ] + test "MetadataType instance #1" do fromGeneralTxMetadata (toGeneralTxMetadata cip25MetadataFixture1) `shouldEqual` Just cip25MetadataFixture1 - test "FromData / ToData instances" do + test "MetadataType instance #2" do + fromGeneralTxMetadata (toGeneralTxMetadata cip25MetadataFixture2) + `shouldEqual` Just cip25MetadataFixture2 + test "MetadataType instance #3" do + fromGeneralTxMetadata (toGeneralTxMetadata cip25MetadataFixture3) + `shouldEqual` Just cip25MetadataFixture3 + test "FromData / ToData instances #1" do fromData (toData cip25MetadataFixture1) `shouldEqual` Just cip25MetadataFixture1 - test "DecodeJson instance" do + test "FromData / ToData instances #2" do + fromData (toData cip25MetadataFixture2) `shouldEqual` + Just cip25MetadataFixture2 + test "FromData / ToData instances #3" do + fromData (toData cip25MetadataFixture3) `shouldEqual` + Just cip25MetadataFixture3 + test "DecodeJson instance #1" do jsonFixture <- liftEffect cip25MetadataJsonFixture1 decodeAeson jsonFixture `shouldEqual` Right cip25MetadataFixture1 + test "DecodeJson instance #2" do + jsonFixture <- liftEffect cip25MetadataJsonFixture2 + decodeAeson jsonFixture `shouldEqual` + Right cip25MetadataFixture2 + +str40Chars :: String +str40Chars = "0123456789012345678901234567890123456789" + +str80Chars :: String +str80Chars = str40Chars <> str40Chars + +str160Chars :: String +str160Chars = str80Chars <> str80Chars diff --git a/test/Plutip.purs b/test/Plutip.purs new file mode 100644 index 0000000000..e1336770d1 --- /dev/null +++ b/test/Plutip.purs @@ -0,0 +1,412 @@ +-- | `plutip-server` PR: +-- | https://github.com/mlabs-haskell/plutip/pull/79 (run with `cabal run plutip-server`) +module Test.Plutip + ( main + ) where + +import Prelude + +import Contract.Address (ownPaymentPubKeyHash) +import Contract.Log (logInfo') +import Contract.Monad + ( Contract + , liftContractAffM + , liftContractM + , liftedE + , liftedM + ) +import Contract.PlutusData + ( PlutusData(Integer) + , Redeemer(Redeemer) + , getDatumByHash + , getDatumsByHashes + ) +import Contract.Prelude (mconcat) +import Contract.Prim.ByteArray (byteArrayFromAscii, hexToByteArrayUnsafe) +import Contract.ScriptLookups as Lookups +import Contract.Scripts (MintingPolicy, validatorHash) +import Contract.Test.Plutip + ( InitialUTxO + , runContractInEnv + , runPlutipContract + , withPlutipContractEnv + ) +import Contract.Transaction + ( BalancedSignedTransaction + , DataHash + , awaitTxConfirmed + , balanceAndSignTxM + , balanceAndSignTxE + , getTxByHash + , submit + , withBalancedAndSignedTxs + ) +import Contract.TxConstraints as Constraints +import Contract.Value (CurrencySymbol, TokenName) +import Contract.Value as Value +import Contract.Wallet (withKeyWallet) +import Control.Monad.Error.Class (withResource) +import Control.Monad.Reader (asks) +import Control.Parallel (parallel, sequential) +import Data.BigInt as BigInt +import Data.Log.Level (LogLevel(Trace)) +import Data.Map as Map +import Data.Maybe (Maybe(Just, Nothing), isNothing) +import Data.Newtype (unwrap, wrap) +import Data.Traversable (traverse_) +import Data.Tuple.Nested (type (/\), (/\)) +import Data.UInt as UInt +import Effect (Effect) +import Effect.Aff (launchAff_) +import Effect.Class (liftEffect) +import Effect.Console as Console +import Effect.Exception (throw) +import Effect.Ref as Ref +import Examples.AlwaysMints (alwaysMintsPolicy) +import Examples.AlwaysSucceeds as AlwaysSucceeds +import Examples.MintsMultipleTokens + ( mintingPolicyRdmrInt1 + , mintingPolicyRdmrInt2 + , mintingPolicyRdmrInt3 + ) +import Mote (group, test) +import Plutip.Server + ( startPlutipCluster + , startPlutipServer + , stopChildProcessWithPort + , stopPlutipCluster + ) +import Plutip.Types + ( PlutipConfig + , StartClusterResponse(ClusterStartupSuccess) + , StopClusterResponse(StopClusterSuccess) + ) +import Test.Spec.Assertions (shouldSatisfy) +import Test.Spec.Runner (defaultConfig) +import Test.Utils as Utils +import TestM (TestPlanM) +import Types.UsedTxOuts (TxOutRefCache) + +-- Run with `spago test --main Test.Plutip` +main :: Effect Unit +main = launchAff_ do + Utils.interpretWithConfig + -- we don't want to exit because we need to clean up after failure by + -- timeout + defaultConfig { timeout = Just $ wrap 30_000.0, exit = true } + suite + +config :: PlutipConfig +config = + { host: "127.0.0.1" + , port: UInt.fromInt 8082 + , logLevel: Trace + -- Server configs are used to deploy the corresponding services. + , ogmiosConfig: + { port: UInt.fromInt 1338 + , host: "127.0.0.1" + , secure: false + , path: Nothing + } + , ogmiosDatumCacheConfig: + { port: UInt.fromInt 10000 + , host: "127.0.0.1" + , secure: false + , path: Nothing + } + , ctlServerConfig: + { port: UInt.fromInt 8083 + , host: "127.0.0.1" + , secure: false + , path: Nothing + } + , postgresConfig: + { host: "127.0.0.1" + , port: UInt.fromInt 5433 + , user: "ctxlib" + , password: "ctxlib" + , dbname: "ctxlib" + } + } + +suite :: TestPlanM Unit +suite = do + group "Plutip" do + test "startPlutipCluster / stopPlutipCluster" do + withResource (startPlutipServer config) + (stopChildProcessWithPort config.port) $ const do + startRes <- startPlutipCluster config unit + startRes `shouldSatisfy` case _ of + ClusterStartupSuccess _ -> true + _ -> false + liftEffect $ Console.log $ "startPlutipCluster: " <> show startRes + stopRes <- stopPlutipCluster config + stopRes `shouldSatisfy` case _ of + StopClusterSuccess -> true + _ -> false + liftEffect $ Console.log $ "stopPlutipCluster: " <> show stopRes + + test "runPlutipContract" do + let + distribution :: InitialUTxO /\ InitialUTxO + distribution = + [ BigInt.fromInt 1_000_000_000 + , BigInt.fromInt 2_000_000_000 + ] /\ + [ BigInt.fromInt 2_000_000_000 ] + runPlutipContract config distribution \(alice /\ bob) -> do + withKeyWallet alice do + pure unit -- sign, balance, submit, etc. + withKeyWallet bob do + pure unit -- sign, balance, submit, etc. + + test "runPlutipContract: Pkh2Pkh" do + let + distribution :: InitialUTxO + distribution = + [ BigInt.fromInt 1_000_000_000 + , BigInt.fromInt 2_000_000_000 + ] + runPlutipContract config distribution \alice -> do + withKeyWallet alice do + pkh <- liftedM "Failed to get own PKH" ownPaymentPubKeyHash + let + constraints :: Constraints.TxConstraints Void Void + -- In real contracts, library users most likely want to use + -- `mustPayToPubKeyAddress` (we're not doing that because Plutip + -- does not provide stake keys). + constraints = Constraints.mustPayToPubKey pkh + $ Value.lovelaceValueOf + $ BigInt.fromInt 2_000_000 + + lookups :: Lookups.ScriptLookups Void + lookups = mempty + ubTx <- liftedE $ Lookups.mkUnbalancedTx lookups constraints + bsTx <- + liftedE $ balanceAndSignTxE ubTx + submitAndLog bsTx + + test "runPlutipContract: parallel Pkh2Pkh" do + let + distribution :: InitialUTxO /\ InitialUTxO + distribution = + [ BigInt.fromInt 1_000_000_000 + , BigInt.fromInt 2_000_000_000 + ] /\ + [ BigInt.fromInt 1_000_000_000 + , BigInt.fromInt 2_000_000_000 + ] + withPlutipContractEnv config distribution \env (alice /\ bob) -> + sequential ado + parallel $ runContractInEnv env $ withKeyWallet alice do + bobPkh <- liftedM "Failed to get PKH" $ withKeyWallet bob + ownPaymentPubKeyHash + let + constraints :: Constraints.TxConstraints Void Void + -- In real contracts, library users most likely want to use + -- `mustPayToPubKeyAddress` (we're not doing that because Plutip + -- does not provide stake keys). + constraints = Constraints.mustPayToPubKey bobPkh + $ Value.lovelaceValueOf + $ BigInt.fromInt 2_000_000 + + lookups :: Lookups.ScriptLookups Void + lookups = mempty + ubTx <- liftedE $ Lookups.mkUnbalancedTx lookups constraints + bsTx <- + liftedE $ balanceAndSignTxE ubTx + submitAndLog bsTx + parallel $ runContractInEnv env $ withKeyWallet bob do + alicePkh <- liftedM "Failed to get PKH" $ withKeyWallet alice + ownPaymentPubKeyHash + let + constraints :: Constraints.TxConstraints Void Void + -- In real contracts, library users most likely want to use + -- `mustPayToPubKeyAddress` (we're not doing that because Plutip + -- does not provide stake keys). + constraints = Constraints.mustPayToPubKey alicePkh + $ Value.lovelaceValueOf + $ BigInt.fromInt 2_000_000 + + lookups :: Lookups.ScriptLookups Void + lookups = mempty + ubTx <- liftedE $ Lookups.mkUnbalancedTx lookups constraints + bsTx <- + liftedE $ balanceAndSignTxE ubTx + submitAndLog bsTx + in unit + + test "runPlutipContract: AlwaysMints" do + let + distribution :: InitialUTxO + distribution = + [ BigInt.fromInt 5_000_000 + , BigInt.fromInt 2_000_000_000 + ] + runPlutipContract config distribution \alice -> do + withKeyWallet alice do + mp <- alwaysMintsPolicy + cs <- liftContractAffM "Cannot get cs" $ Value.scriptCurrencySymbol mp + tn <- liftContractM "Cannot make token name" + $ Value.mkTokenName + =<< byteArrayFromAscii "TheToken" + + let + constraints :: Constraints.TxConstraints Void Void + constraints = Constraints.mustMintValue + $ Value.singleton cs tn + $ BigInt.fromInt 100 + + lookups :: Lookups.ScriptLookups Void + lookups = Lookups.mintingPolicy mp + + ubTx <- liftedE $ Lookups.mkUnbalancedTx lookups constraints + bsTx <- + liftedM "Failed to balance/sign tx" $ balanceAndSignTxM ubTx + submitAndLog bsTx + + test "runPlutipContract: Datums" do + runPlutipContract config unit \_ -> do + let + mkDatumHash :: String -> DataHash + mkDatumHash = wrap <<< hexToByteArrayUnsafe + -- Nothing is expected, because we are in an empty chain. + -- This test only checks for ability to connect to ODC + logInfo' <<< show =<< getDatumByHash + ( mkDatumHash + "42be572a6d9a8a2ec0df04f14b0d4fcbe4a7517d74975dfff914514f12316252" + ) + logInfo' <<< show =<< getDatumsByHashes + [ mkDatumHash + "777093fe6dfffdb3bd2033ad71745f5e2319589e36be4bc9c8cca65ac2bfeb8f" + , mkDatumHash + "e8cb7d18e81b0be160c114c563c020dcc7bf148a1994b73912db3ea1318d488b" + ] + + test "runPlutipContract: MintsMultipleTokens" do + let + distribution :: InitialUTxO + distribution = + [ BigInt.fromInt 5_000_000 + , BigInt.fromInt 2_000_000_000 + ] + runPlutipContract config distribution \alice -> do + withKeyWallet alice do + tn1 <- mkTokenName "Token with a long name" + tn2 <- mkTokenName "Token" + mp1 /\ cs1 <- mkCurrencySymbol mintingPolicyRdmrInt1 + mp2 /\ cs2 <- mkCurrencySymbol mintingPolicyRdmrInt2 + mp3 /\ cs3 <- mkCurrencySymbol mintingPolicyRdmrInt3 + + let + constraints :: Constraints.TxConstraints Void Void + constraints = mconcat + [ Constraints.mustMintValueWithRedeemer + (Redeemer $ Integer (BigInt.fromInt 1)) + (Value.singleton cs1 tn1 one <> Value.singleton cs1 tn2 one) + , Constraints.mustMintValueWithRedeemer + (Redeemer $ Integer (BigInt.fromInt 2)) + (Value.singleton cs2 tn1 one <> Value.singleton cs2 tn2 one) + , Constraints.mustMintValueWithRedeemer + (Redeemer $ Integer (BigInt.fromInt 3)) + (Value.singleton cs3 tn1 one <> Value.singleton cs3 tn2 one) + ] + + lookups :: Lookups.ScriptLookups Void + lookups = + Lookups.mintingPolicy mp1 + <> Lookups.mintingPolicy mp2 + <> Lookups.mintingPolicy mp3 + + ubTx <- liftedE $ Lookups.mkUnbalancedTx lookups constraints + bsTx <- + liftedM "Failed to balance/sign tx" $ balanceAndSignTxM ubTx + submitAndLog bsTx + + test "runPlutipContract: SignMultiple" do + let + distribution :: InitialUTxO + distribution = + [ BigInt.fromInt 5_000_000 + , BigInt.fromInt 100_000_000 + ] + runPlutipContract config distribution \alice -> do + withKeyWallet alice do + pkh <- liftedM "Failed to get own PKH" ownPaymentPubKeyHash + let + constraints :: Constraints.TxConstraints Void Void + -- In real contracts, library users most likely want to use + -- `mustPayToPubKeyAddres` (we're not doing that because Plutip + -- does not provide stake keys). + constraints = Constraints.mustPayToPubKey pkh + $ Value.lovelaceValueOf + $ BigInt.fromInt 2_000_000 + + lookups :: Lookups.ScriptLookups Void + lookups = mempty + + ubTx1 <- liftedE $ Lookups.mkUnbalancedTx lookups constraints + ubTx2 <- liftedE $ Lookups.mkUnbalancedTx lookups constraints + + withBalancedAndSignedTxs [ ubTx1, ubTx2 ] $ \txs -> do + locked <- getLockedInputs + logInfo' $ "Locked inputs inside bracket (should be nonempty): " <> + show + locked + traverse_ submitAndLog txs + + locked <- getLockedInputs + logInfo' $ "Locked inputs after bracket (should be empty): " <> show + locked + unless (locked # Map.isEmpty) do + liftEffect $ throw "locked inputs map is not empty" + + test "runPlutipContract: AlwaysSucceeds" do + let + distribution :: InitialUTxO + distribution = + [ BigInt.fromInt 5_000_000 + , BigInt.fromInt 2_000_000_000 + ] + runPlutipContract config distribution \alice -> do + withKeyWallet alice do + validator <- AlwaysSucceeds.alwaysSucceedsScript + vhash <- liftContractAffM "Couldn't hash validator" + $ validatorHash validator + logInfo' "Attempt to lock value" + txId <- AlwaysSucceeds.payToAlwaysSucceeds vhash + awaitTxConfirmed txId + logInfo' "Try to spend locked values" + AlwaysSucceeds.spendFromAlwaysSucceeds vhash validator txId + +submitAndLog + :: forall (r :: Row Type). BalancedSignedTransaction -> Contract r Unit +submitAndLog bsTx = do + txId <- submit bsTx + logInfo' $ "Tx ID: " <> show txId + awaitTxConfirmed txId + mbTransaction <- getTxByHash txId + logInfo' $ "Tx: " <> show mbTransaction + liftEffect $ when (isNothing mbTransaction) do + void $ throw "Unable to get Tx contents" + when (mbTransaction /= Just (unwrap bsTx)) do + throw "Tx contents do not match" + +getLockedInputs :: forall (r :: Row Type). Contract r TxOutRefCache +getLockedInputs = do + cache <- asks (_.usedTxOuts <<< _.runtime <<< unwrap) + liftEffect $ Ref.read $ unwrap cache + +mkTokenName :: forall (r :: Row Type). String -> Contract r TokenName +mkTokenName = + liftContractM "Cannot make token name" + <<< (Value.mkTokenName <=< byteArrayFromAscii) + +mkCurrencySymbol + :: forall (r :: Row Type) + . Contract r MintingPolicy + -> Contract r (MintingPolicy /\ CurrencySymbol) +mkCurrencySymbol mintingPolicy = do + mp <- mintingPolicy + cs <- liftContractAffM "Cannot get cs" $ Value.scriptCurrencySymbol mp + pure (mp /\ cs) diff --git a/test/PrivateKey.purs b/test/PrivateKey.purs index 33f5e92a8f..07ce0ceb5f 100644 --- a/test/PrivateKey.purs +++ b/test/PrivateKey.purs @@ -1,5 +1,6 @@ module Test.PrivateKey where +import Contract.Config (testnetConfig) import Prelude import Cardano.Types.Transaction @@ -8,30 +9,39 @@ import Cardano.Types.Transaction , TransactionWitnessSet(TransactionWitnessSet) , Vkeywitness(Vkeywitness) ) -import Contract.Address (NetworkId(TestnetId)) -import Contract.Monad (configWithLogLevel, runContract) +import Contract.Monad (runContract) import Contract.Transaction (signTransaction) -import Contract.Wallet.KeyFile (mkKeyWalletFromFiles) import Data.Lens (_2, _Just, (^?)) import Data.Lens.Index (ix) import Data.Lens.Iso.Newtype (unto) import Data.Lens.Record (prop) -import Data.Log.Level (LogLevel(Trace)) import Data.Maybe (Maybe(Just)) import Mote (group, test) import Test.Fixtures (txFixture1) import Test.Spec.Assertions (shouldEqual) import TestM (TestPlanM) import Type.Proxy (Proxy(Proxy)) +import Wallet.Spec + ( PrivatePaymentKeySource(PrivatePaymentKeyFile) + , PrivateStakeKeySource(PrivateStakeKeyFile) + , WalletSpec(UseKeys) + ) suite :: TestPlanM Unit suite = do group "PrivateKey" $ do test "privateKeyFromFile" do - wallet <- mkKeyWalletFromFiles - "fixtures/test/parsing/PrivateKey/payment.skey" - (Just "fixtures/test/parsing/PrivateKey/stake.skey") - cfg <- configWithLogLevel TestnetId wallet Trace + let + cfg = + testnetConfig + { walletSpec = Just $ UseKeys + ( PrivatePaymentKeyFile + "fixtures/test/parsing/PrivateKey/payment.skey" + ) + ( Just $ PrivateStakeKeyFile + "fixtures/test/parsing/PrivateKey/stake.skey" + ) + } runContract cfg do mbTx <- signTransaction txFixture1 let diff --git a/test/Unit.purs b/test/Unit.purs index 7099efacf5..7539a0a03b 100644 --- a/test/Unit.purs +++ b/test/Unit.purs @@ -1,9 +1,10 @@ -module Test.Unit (main, testPlan) where +module Ctl.Test.Unit (main, testPlan) where import Prelude import Effect (Effect) import Effect.Aff (launchAff_) +import Test.Base64 as Base64 import Test.ByteArray as ByteArray import Test.Data as Data import Test.Deserialization as Deserialization @@ -26,13 +27,14 @@ import Test.UsedTxOuts as UsedTxOuts import Test.Utils as Utils import TestM (TestPlanM) --- Run with `spago test --main Test.Unit` +-- Run with `spago test --main Ctl.Test.Unit` main :: Effect Unit main = launchAff_ do Utils.interpret testPlan testPlan :: TestPlanM Unit testPlan = do + Base64.suite ByteArray.suite Cip25.suite Data.suite diff --git a/test/Utils.purs b/test/Utils.purs index d6d6d16859..3a4be7cb50 100644 --- a/test/Utils.purs +++ b/test/Utils.purs @@ -5,6 +5,8 @@ module Test.Utils , errMaybe , errEither , interpret + , interpretWithTimeout + , interpretWithConfig , toFromAesonTest , unsafeCall , readAeson @@ -26,6 +28,7 @@ import Data.Either (Either(Right), either) import Data.Foldable (sequence_) import Data.Maybe (Maybe(Just), maybe) import Data.Newtype (wrap) +import Data.Time.Duration (Milliseconds) import Effect.Aff (Aff, error) import Effect.Aff.Class (liftAff) import Effect.Class (class MonadEffect, liftEffect) @@ -38,6 +41,7 @@ import Test.Spec (Spec, describe, it, pending) import Test.Spec.Assertions (shouldEqual) import Test.Spec.Reporter (consoleReporter) import Test.Spec.Runner (defaultConfig, runSpec') +import Test.Spec.Runner as SpecRunner import TestM (TestPlanM) import Type.Proxy (Proxy) @@ -48,18 +52,26 @@ foreign import unsafeCall -- | is then interpreted here in a pure context, mainly due to some painful types -- | in Test.Spec which prohibit effects. interpret :: TestPlanM Unit -> Aff Unit -interpret spif = do +interpret = interpretWithConfig defaultConfig { timeout = Just (wrap 50000.0) } + +interpretWithTimeout :: Maybe Milliseconds -> TestPlanM Unit -> Aff Unit +interpretWithTimeout timeout spif = do + plan <- planT spif + runSpec' defaultConfig { timeout = timeout } [ consoleReporter ] $ + planToSpec plan + +interpretWithConfig :: SpecRunner.Config -> TestPlanM Unit -> Aff Unit +interpretWithConfig config spif = do plan <- planT spif - runSpec' defaultConfig { timeout = Just $ wrap 50000.0 } [ consoleReporter ] $ - go plan - where - go :: Plan (Const Void) (Aff Unit) -> Spec Unit - go = - foldPlan - (\x -> it x.label $ liftAff x.value) - pending - (\x -> describe x.label $ go x.value) - sequence_ + runSpec' config [ consoleReporter ] $ planToSpec plan + +planToSpec :: Plan (Const Void) (Aff Unit) -> Spec Unit +planToSpec = + foldPlan + (\x -> it x.label $ liftAff x.value) + pending + (\x -> describe x.label $ planToSpec x.value) + sequence_ -- | Test a boolean value, throwing the provided string as an error if `false` assertTrue