diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index df5d74693..bd9252650 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -12,16 +12,16 @@ same principles in Quint, see [Contributing to Apalache][]. ## Developer docs - - [roadmap](./doc/roadmap.md) - - [ADR001: Transpiler architecture](./doc/adr001-transpiler-architecture.md) - - [ADR002: Error codes](./doc/adr002-errors.md) + - [roadmap](./docs/roadmap.md) + - [ADR001: Transpiler architecture](./docs/pages/docs/architecture-decision-records/adr001-transpiler-architecture.md) + - [ADR002: Error codes](./docs/pages/docs/architecture-decision-records/adr002-errors.md) - [ADR003: Interface to visit Internal Representation - components](./doc/adr003-visiting-ir-components.md) - - [ADR004: An Effect System for Quint](./doc/adr004-effect-system.md) - - [ADR005: A Type System for Quint](./doc/adr005-type-system.md) - - [ADR006: Design of modules and lookup tables](./doc/adr006-modules.lit.md) - - [ADR007: Flattening](./doc/adr007-flattening.md) - - [ADR008: Obtaining and Launching Apalache from Quint](./doc/adr008-managing-apalache.md) + components](./docs/pages/docs/architecture-decision-records/adr003-visiting-ir-components.md) + - [ADR004: An Effect System for Quint](./docs/pages/docs/architecture-decision-records/adr004-effect-system.md) + - [ADR005: A Type System for Quint](./docs/pages/docs/architecture-decision-records/adr005-type-system.md) + - [ADR006: Design of modules and lookup tables](./docs/pages/docs/architecture-decision-records/adr006-modules.lit.md) + - [ADR007: Flattening](./docs/pages/docs/architecture-decision-records/adr007-flattening.md) + - [ADR008: Obtaining and Launching Apalache from Quint](./docs/pages/docs/architecture-decision-records/adr008-managing-apalache.md) ## Coordinating work @@ -268,7 +268,7 @@ Between installing the plugin from different sources, you may end up with multip [Apalache]: https://github.com/informalsystems/apalache [Contributing to Apalache]: https://github.com/informalsystems/apalache/blob/main/CONTRIBUTING.md [eslint]: https://eslint.org/ -[quint manual]: ./doc/quint.md +[quint manual]: ./docs/pages/docs/architecture-decision-records/quint.md [Installing quint]: https://github.com/informalsystems/quint/blob/main/quint/README.md#how-to-install [Language server protocol]: https://microsoft.github.io/language-server-protocol/ [quint unit tests]: https://github.com/informalsystems/quint/blob/main/quint/README.md#unit-tests diff --git a/doc/faq.md b/doc/faq.md deleted file mode 100644 index 9f6e1f790..000000000 --- a/doc/faq.md +++ /dev/null @@ -1,106 +0,0 @@ -# Frequently Asked Questions - -Here you can find answers to the questions that are often asked by people who -would like to start with Quint. - -### What are spells? - -Spells are simply Quint modules that contain often-used definitions. There is -nothing special about these definitions. They are probably a bit more -thought-out than a definition someone would write on the spot. Check the page -on [Spells][]. - -### Difference between `pure def` and `def` - -Definitions that are tagged as `pure def` are not allowed to read state -variables (that is, the names declared with `var`). Definitions that are tagged -with `def` are allowed to read state variables, though they do not have to. - -Pure definitions have the following important property: A **pure definition -always returns the same result for the same parameter values**. - -Consider the following operator definition: - -```bluespec -pure def min(x, y) = if (x < y) x else y -``` - -Whenever we call `min(2, 3)`, we get `2` as a result, no matter what the values of state variables are. - -Impure definitions have the following important property: -**An impure definition may produce different results for the same parameter -values**. An impure definition **may read** the values of state variables, which -may effect in different results. - -Consider the following definitions: - -```bluespec -var limit: int - -def minWithLimit(x, y) = min(min(x, y), limit) -``` - -In the above example, `minWithLimit(10, 20)` produces the value `10`, when -`limit == 100`, whereas `minWithLimit(10, 20)` produces the value `5`, when -`limit == 5`. - -### Difference between `val` and `def` - -The definitions that have at least one parameter should be tagged with `def`, -whereas the definitions that have no parameters should be tagged with `val`. We -could say that `val`'s are nullary `def`'s. - -As a result, a `pure val` is never changing its value in an execution. For example: - -```bluespec -pure val avogadro = 602214076^(23 - 8) -``` - -Note that an impure `val` may still depend on the value of a state variable. -For example: - -```bluespec -var time: int -var velocity: int - -val distance = velocity * time -``` - -In the above example, `distance` does not need any parameters, as it only -depends on the state variables. However, the value of `distance` is still -changing with the values of `time` and `velocity`. - -### Difference between `pure val` and `const` - -A value that is defined via `pure val` is constant in the sense that it never -changes in an execution. A `const` definition also declares a constant value. -However, the value of `const` is not fixed at the time of specification writing, -but it has to be fixed by instantiating the module. - -Here is an example that illustrates the difference between `pure val` and `const`: - -```bluespec -module fixed { - // this module is written for N=4, e.g., for N processes - pure val N = 4 - - pure val procs = 1.to(N) - // etc. -} - -module parameterized { - // this module is written for a parameter N - const N: int - - pure val procs = 1.to(N) - // etc. -} - -module instance4 { - import parameterized(N = 4) as I - - pure val procs = 1.to(I::N) -} -``` - -[Spells]: https://github.com/informalsystems/quint/tree/main/examples/spells \ No newline at end of file diff --git a/doc/img/Makefile b/doc/img/Makefile deleted file mode 100644 index 08fbef27e..000000000 --- a/doc/img/Makefile +++ /dev/null @@ -1,7 +0,0 @@ -# Compile all graphics dependencies - -all: *.svg - -%.svg: %.mer - # if mmdc is not found, run: brew install mermaid-cli - mmdc -i $< -o $@ diff --git a/doc/img/pipeline.mer b/doc/img/pipeline.mer deleted file mode 100644 index 0f31f3f23..000000000 --- a/doc/img/pipeline.mer +++ /dev/null @@ -1,4 +0,0 @@ -flowchart LR - p["1: Parser"] --> nr["2: NameResolver"] --> - tc["3: TypeChecker"] --> ca["4: CustomAnalyses"] --> tla["5: ToApalacheIR"] - nr --> p diff --git a/doc/img/pipeline.svg b/doc/img/pipeline.svg deleted file mode 100644 index b66cb034a..000000000 --- a/doc/img/pipeline.svg +++ /dev/null @@ -1 +0,0 @@ -
1: Parser
2: NameResolver
3: TypeChecker
4: CustomAnalyses
5: ToApalacheIR
\ No newline at end of file diff --git a/doc/previews.md b/doc/previews.md deleted file mode 100644 index 8d5c86f79..000000000 --- a/doc/previews.md +++ /dev/null @@ -1,17 +0,0 @@ -# Quick previews - -## VSCode plugin - - - -Install the [Quint VSCode -Plugin](https://marketplace.visualstudio.com/items?itemName=informal.quint-vscode) -from the VisualStudio Marketplace. - -## Command line tools - - - -Install the [Quint -CLI](https://github.com/informalsystems/quint/blob/main/quint/README.md) from -npm. diff --git a/docs/.gitignore b/docs/.gitignore new file mode 100644 index 000000000..a680367ef --- /dev/null +++ b/docs/.gitignore @@ -0,0 +1 @@ +.next diff --git a/doc/Makefile b/docs/Makefile similarity index 100% rename from doc/Makefile rename to docs/Makefile diff --git a/doc/README.md b/docs/README.md similarity index 100% rename from doc/README.md rename to docs/README.md diff --git a/tutorials/.gitattributes b/docs/codetour/.gitattributes similarity index 100% rename from tutorials/.gitattributes rename to docs/codetour/.gitattributes diff --git a/tutorials/.tours/booleans.tour b/docs/codetour/.tours/booleans.tour similarity index 95% rename from tutorials/.tours/booleans.tour rename to docs/codetour/.tours/booleans.tour index 9b1a76039..4f7a91434 100644 --- a/tutorials/.tours/booleans.tour +++ b/docs/codetour/.tours/booleans.tour @@ -88,7 +88,7 @@ }, { "title": "Suming it up", - "description": "\nWe have covered all the operators over Booleans.\nIf you want to see all operators in one place,\ncheck [booleans.qnt](./booleans.qnt).\n\nWe are experimenting with different kinds of tutorials.\nIt would be great to learn, whether you liked this tutorial format, or not.\nPlease vote in the\n[discussion](https://github.com/informalsystems/quint/discussions/516).\n ", + "description": "\nWe have covered all the operators over Booleans.\nIf you want to see all operators in one place,\ncheck [booleans.qnt](https://github.com/informalsystems/quint/tree/main/examples/tutorials/booleans.qnt).\n\nWe are experimenting with different kinds of tutorials.\nIt would be great to learn, whether you liked this tutorial format, or not.\nPlease vote in the\n[discussion](https://github.com/informalsystems/quint/discussions/516).\n ", "line": 64, "file": "lesson1-booleans/booleans.qnt" } diff --git a/tutorials/.tours/coin.tour b/docs/codetour/.tours/coin.tour similarity index 100% rename from tutorials/.tours/coin.tour rename to docs/codetour/.tours/coin.tour diff --git a/tutorials/.tours/hello.tour b/docs/codetour/.tours/hello.tour similarity index 85% rename from tutorials/.tours/hello.tour rename to docs/codetour/.tours/hello.tour index 3dbfcd1fd..f713ec19d 100644 --- a/tutorials/.tours/hello.tour +++ b/docs/codetour/.tours/hello.tour @@ -52,7 +52,7 @@ }, { "title": "Introduce a simple test", - "description": "\nWe have written all the important parts of our protocol. It would be nice not\nto only read the code, but also to execute it somehow. After all, this is what\nwe normally do with the code.\n\nSince we are specifying a distributed protocol, there may be many ways to\nexecute actions in different orders. In general, it is not even always clear,\nwhether our protocol has terminated or not. Luckily, our protocol is quite simple.\n\nTo test our protocol, we fix one particular execution sequence in `writeReadTest`:\n\n 1. Execute the initialization action `init`.\n 2. Execute the action `write`.\n 3. Execute the action `read`.\n\nWe could draw this execution sequence with UML sequence diagrams or state\ndiagrams. For example:\n\n![test1-sequence](../img/hello-test1.png)\n\nThe unfortunate fact about UML sequence diagrams and state charts is that they\nare given as figures, which have to be executed in the reader's brain.\n\nIn Quint, running `writeReadTest` is as simple as evaluating an expression. To try it\nout, run the command `quint`, which starts a REPL session, and execute the\nfollowing commands (written after the REPL prompt `>>> `):\n\n```sh\n$ quint -r hello.qnt\nQuint REPL v0.0.3\nType \".exit\" to exit, or \".help\" for more information\n>>> import hello.*\n\n>>> writeReadTest\ntrue\n```\n\nIn the above session, we load the module `hello` from the file `hello.qnt`,\nimport all definitions from the module `hello` and execute the run\n`writeReadTest`. As indicated with the result `true`, the run was executed\nsuccessfully. We can also evaluate the state variables `consoleOutput` and\n`readByUser` in the state produced by `writeReadTest`:\n\n```sh\n>>> consoleOutput\n\"Hello, world!\"\n>>> readByUser\n\"Hello, world!\"\n```\n\nRunning tests by hand in REPL may quickly become tedious. To automate that,\nuse the `test` command:\n\n \n>> quint test ./lesson0-helloworld/hello.qnt\n\n\n\n**Exercise:** Carefully read the code of `read` and `write` again. Explain,\nwhether it is possible to execute `read` and `write` in the same state.\n\n**Exercise:** Explain, whether it is possible to execute `read` or `write`\nafter executing the run called `writeReadTest`.\n ", + "description": "\nWe have written all the important parts of our protocol. It would be nice not\nto only read the code, but also to execute it somehow. After all, this is what\nwe normally do with the code.\n\nSince we are specifying a distributed protocol, there may be many ways to\nexecute actions in different orders. In general, it is not even always clear,\nwhether our protocol has terminated or not. Luckily, our protocol is quite simple.\n\nTo test our protocol, we fix one particular execution sequence in `writeReadTest`:\n\n 1. Execute the initialization action `init`.\n 2. Execute the action `write`.\n 3. Execute the action `read`.\n\nWe could draw this execution sequence with UML sequence diagrams or state\ndiagrams. For example:\n\n![test1-sequence](./img/hello-test1.png)\n\nThe unfortunate fact about UML sequence diagrams and state charts is that they\nare given as figures, which have to be executed in the reader's brain.\n\nIn Quint, running `writeReadTest` is as simple as evaluating an expression. To try it\nout, run the command `quint`, which starts a REPL session, and execute the\nfollowing commands (written after the REPL prompt `>>> `):\n\n```sh\n$ quint -r hello.qnt\nQuint REPL v0.0.3\nType \".exit\" to exit, or \".help\" for more information\n>>> import hello.*\n\n>>> writeReadTest\ntrue\n```\n\nIn the above session, we load the module `hello` from the file `hello.qnt`,\nimport all definitions from the module `hello` and execute the run\n`writeReadTest`. As indicated with the result `true`, the run was executed\nsuccessfully. We can also evaluate the state variables `consoleOutput` and\n`readByUser` in the state produced by `writeReadTest`:\n\n```sh\n>>> consoleOutput\n\"Hello, world!\"\n>>> readByUser\n\"Hello, world!\"\n```\n\nRunning tests by hand in REPL may quickly become tedious. To automate that,\nuse the `test` command:\n\n \n>> quint test ./lesson0-helloworld/hello.qnt\n\n\n\n**Exercise:** Carefully read the code of `read` and `write` again. Explain,\nwhether it is possible to execute `read` and `write` in the same state.\n\n**Exercise:** Explain, whether it is possible to execute `read` or `write`\nafter executing the run called `writeReadTest`.\n ", "line": 44, "file": "lesson0-helloworld/hello.qnt" }, diff --git a/tutorials/img/hello-test1.png b/docs/codetour/.tours/img/hello-test1.png similarity index 100% rename from tutorials/img/hello-test1.png rename to docs/codetour/.tours/img/hello-test1.png diff --git a/tutorials/img/kettle-drawing.png b/docs/codetour/.tours/img/kettle-drawing.png similarity index 100% rename from tutorials/img/kettle-drawing.png rename to docs/codetour/.tours/img/kettle-drawing.png diff --git a/tutorials/img/kettle-state1.svg b/docs/codetour/.tours/img/kettle-state1.svg similarity index 100% rename from tutorials/img/kettle-state1.svg rename to docs/codetour/.tours/img/kettle-state1.svg diff --git a/tutorials/img/kettle-state2.svg b/docs/codetour/.tours/img/kettle-state2.svg similarity index 100% rename from tutorials/img/kettle-state2.svg rename to docs/codetour/.tours/img/kettle-state2.svg diff --git a/tutorials/img/kettle-state3.svg b/docs/codetour/.tours/img/kettle-state3.svg similarity index 100% rename from tutorials/img/kettle-state3.svg rename to docs/codetour/.tours/img/kettle-state3.svg diff --git a/tutorials/img/repl-machine.png b/docs/codetour/.tours/img/repl-machine.png similarity index 100% rename from tutorials/img/repl-machine.png rename to docs/codetour/.tours/img/repl-machine.png diff --git a/tutorials/img/tutorials-1671180664875.gif b/docs/codetour/.tours/img/tutorials-1671180664875.gif similarity index 100% rename from tutorials/img/tutorials-1671180664875.gif rename to docs/codetour/.tours/img/tutorials-1671180664875.gif diff --git a/tutorials/.tours/integers.tour b/docs/codetour/.tours/integers.tour similarity index 100% rename from tutorials/.tours/integers.tour rename to docs/codetour/.tours/integers.tour diff --git a/tutorials/.tours/sets.tour b/docs/codetour/.tours/sets.tour similarity index 93% rename from tutorials/.tours/sets.tour rename to docs/codetour/.tours/sets.tour index 4372bd9a4..a9f244f3e 100644 --- a/tutorials/.tours/sets.tour +++ b/docs/codetour/.tours/sets.tour @@ -4,7 +4,7 @@ "steps": [ { "title": "Introduction", - "description": "\nIn this tutorial, we explain the most powerful data structure of Quint.\nYes, that's right, sets are the most powerful data structure.\n\nTo make the tutorial a bit more entertaining, we will help an anonymous crypto\n[Degen](https://www.coingecko.com/en/glossary/degen), who asked us to help\nwith Quint. We only know their handle\n[@KryptoCoffeeCat](https://twitter.com/KryptoCoffeeCat).\nThey want to trade a few coins over different exchanges. They have collected\nall the pairs of coins they could exchange, but this table is not easy to\nanalyze:\n\n| Coin A | Coin B |\n| -------- | ------- |\n| BTC | USDC |\n| BTC | USDT |\n| ETH | USDC |\n| ETH | USDT |\n| EVMOS | USDC |\n| EVMOS | WETH |\n| ETH | WETH |\n| ATOM | EVMOS |\n| ATOM | JUNO |\n| ATOM | OSMO |\n| ATOM | JUNO |\n| EVMOS | JUNO |\n| EVMOS | OSMO |\n\nFor example, @KryptoCoffeeCat want to know whether they could exchange BTC\nfor ATOM by doing up to three swaps. Unfortunately, @KryptoCoffeeCat have lost\naccess to their browser, as they did not update it as often as it was required.\nNow they are stuck with the\n[Quint REPL](https://github.com/informalsystems/quint/blob/main/doc/quint.md#command-repl).\nFortunately, they have found the\n[Quint cheatsheet](https://github.com/informalsystems/quint/blob/main/doc/quint-cheatsheet.pdf).\nWe will help them!\n\nIn this tutorial, you will see how to:\n \n - use tuples,\n \n - use sets,\n\n - do plenty of interesting computations over sets.\n\n \n ", + "description": "\nIn this tutorial, we explain the most powerful data structure of Quint.\nYes, that's right, sets are the most powerful data structure.\n\nTo make the tutorial a bit more entertaining, we will help an anonymous crypto\n[Degen](https://www.coingecko.com/en/glossary/degen), who asked us to help\nwith Quint. We only know their handle\n[@KryptoCoffeeCat](https://twitter.com/KryptoCoffeeCat).\nThey want to trade a few coins over different exchanges. They have collected\nall the pairs of coins they could exchange, but this table is not easy to\nanalyze:\n\n| Coin A | Coin B |\n| -------- | ------- |\n| BTC | USDC |\n| BTC | USDT |\n| ETH | USDC |\n| ETH | USDT |\n| EVMOS | USDC |\n| EVMOS | WETH |\n| ETH | WETH |\n| ATOM | EVMOS |\n| ATOM | JUNO |\n| ATOM | OSMO |\n| ATOM | JUNO |\n| EVMOS | JUNO |\n| EVMOS | OSMO |\n\nFor example, @KryptoCoffeeCat want to know whether they could exchange BTC\nfor ATOM by doing up to three swaps. Unfortunately, @KryptoCoffeeCat have lost\naccess to their browser, as they did not update it as often as it was required.\nNow they are stuck with the\n[Quint REPL](https://quint-lang.org/docs/quint#command-repl).\nFortunately, they have found the\n[Quint cheatsheet](https://quint-lang.org/quint-cheatsheet.pdf).\nWe will help them!\n\nIn this tutorial, you will see how to:\n \n - use tuples,\n \n - use sets,\n\n - do plenty of interesting computations over sets.\n\n \n ", "line": 5, "file": "lesson4-sets/sets.qnt" }, @@ -82,7 +82,7 @@ }, { "title": "Conclusions", - "description": "\n\n@KryptoCoffeeCat has learned a lot in this tutorial.\nBy looking at the\n[Quint cheatsheet](https://github.com/informalsystems/quint/blob/main/doc/quint-cheatsheet.pdf),\nthey have found that this tutorial did not cover two operators on sets.\nCan you find them too?\n\nIf you are writing code in programming languages such as JavaScript, Rust,\nGolang, Java, Python, and similar, sets may be not your everyday data\nstructure (such as arrays and lists), though there is a good chance that you\nhave used sets before. In Quint, it's the opposite. If there is a problem,\nthen sets are most likely there to solve it. If not, you can use maps or lists,\nwhich will be explained in follow up tutorials.\n\n ", + "description": "\n\n@KryptoCoffeeCat has learned a lot in this tutorial.\nBy looking at the\n[Quint cheatsheet](https://quint-lang.org/quint-cheatsheet.pdf),\nthey have found that this tutorial did not cover two operators on sets.\nCan you find them too?\n\nIf you are writing code in programming languages such as JavaScript, Rust,\nGolang, Java, Python, and similar, sets may be not your everyday data\nstructure (such as arrays and lists), though there is a good chance that you\nhave used sets before. In Quint, it's the opposite. If there is a problem,\nthen sets are most likely there to solve it. If not, you can use maps or lists,\nwhich will be explained in follow up tutorials.\n\n ", "line": 120, "file": "lesson4-sets/sets.qnt" } diff --git a/tutorials/Makefile b/docs/codetour/Makefile similarity index 56% rename from tutorials/Makefile rename to docs/codetour/Makefile index 9412c140c..9b537a00a 100644 --- a/tutorials/Makefile +++ b/docs/codetour/Makefile @@ -1,5 +1,5 @@ ## -# Quint tutorials +# Quint Codetour and markdown tutorials # # @file # @version 0.1 @@ -11,21 +11,18 @@ tutorials: lesson0-helloworld/hello.qnt \ lesson1-booleans/booleans.qnt \ lesson2-integers/integers.qnt \ lesson3-anatomy/coin.qnt \ - lesson4-sets/sets.qnt \ - repl/kettle.qnt + lesson4-sets/sets.qnt @echo "Updated all tutorials" -./repl/kettle.qnt: ./repl/repl.md - lmt $^ - cat ./repl/replTest.txt |\ - egrep '^(>>>|\.\.\.) ' | sed 's/^>>> //g' | sed 's/^\.\.\. //g' \ - >./repl/replTestIn.txt - cat ./repl/replTest.txt |\ - egrep -v '^(>>>|\.\.\.) ' >./repl/replTestOut.txt - %.qnt: %.template.qnt ./script/mk-tutorial.py $^ $* # move the generated tour to the CodeTour's hidden directory mv $*.tour .tours + cp -r img .tours/ + # move the generated markdown files to the website directory + mv $*.md ../pages/docs/lessons/ + cp -r img ../public/lessons/ + # move the generated quint files to the examples directory + cp $*.qnt ../../examples/tutorials/ # end diff --git a/docs/codetour/img/hello-test1.png b/docs/codetour/img/hello-test1.png new file mode 100644 index 000000000..2e031478b Binary files /dev/null and b/docs/codetour/img/hello-test1.png differ diff --git a/docs/codetour/img/kettle-drawing.png b/docs/codetour/img/kettle-drawing.png new file mode 100644 index 000000000..8aea2c97b Binary files /dev/null and b/docs/codetour/img/kettle-drawing.png differ diff --git a/docs/codetour/img/kettle-state1.svg b/docs/codetour/img/kettle-state1.svg new file mode 100644 index 000000000..451ab0651 --- /dev/null +++ b/docs/codetour/img/kettle-state1.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/docs/codetour/img/kettle-state2.svg b/docs/codetour/img/kettle-state2.svg new file mode 100644 index 000000000..fbc66d14b --- /dev/null +++ b/docs/codetour/img/kettle-state2.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/docs/codetour/img/kettle-state3.svg b/docs/codetour/img/kettle-state3.svg new file mode 100644 index 000000000..6683f4ce8 --- /dev/null +++ b/docs/codetour/img/kettle-state3.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/docs/codetour/img/repl-machine.png b/docs/codetour/img/repl-machine.png new file mode 100644 index 000000000..88cf04195 Binary files /dev/null and b/docs/codetour/img/repl-machine.png differ diff --git a/tutorials/lesson0-helloworld/hello.qnt b/docs/codetour/lesson0-helloworld/hello.qnt similarity index 100% rename from tutorials/lesson0-helloworld/hello.qnt rename to docs/codetour/lesson0-helloworld/hello.qnt diff --git a/tutorials/lesson0-helloworld/hello.template.qnt b/docs/codetour/lesson0-helloworld/hello.template.qnt similarity index 98% rename from tutorials/lesson0-helloworld/hello.template.qnt rename to docs/codetour/lesson0-helloworld/hello.template.qnt index 9e922818a..d204a7bfe 100644 --- a/tutorials/lesson0-helloworld/hello.template.qnt +++ b/docs/codetour/lesson0-helloworld/hello.template.qnt @@ -27,7 +27,7 @@ that we have to describe two importants aspects of the protocol: If you would like to see the complete code before diving into -the details, check [hello.qnt](./hello.qnt). +the details, check [hello.qnt](https://github.com/informalsystems/quint/tree/main/examples/tutorials/hello.qnt). @@ -270,7 +270,7 @@ To test our protocol, we fix one particular execution sequence in `writeReadTest We could draw this execution sequence with UML sequence diagrams or state diagrams. For example: -![test1-sequence](../img/hello-test1.png) +![test1-sequence](./img/hello-test1.png) The unfortunate fact about UML sequence diagrams and state charts is that they are given as figures, which have to be executed in the reader's brain. diff --git a/tutorials/lesson0-helloworld/hello.xml b/docs/codetour/lesson0-helloworld/hello.xml similarity index 98% rename from tutorials/lesson0-helloworld/hello.xml rename to docs/codetour/lesson0-helloworld/hello.xml index 65bd9b61c..991e96d05 100644 --- a/tutorials/lesson0-helloworld/hello.xml +++ b/docs/codetour/lesson0-helloworld/hello.xml @@ -22,7 +22,7 @@ that we have to describe two importants aspects of the protocol: If you would like to see the complete code before diving into -the details, check [hello.qnt](./hello.qnt). +the details, check [hello.qnt](https://github.com/informalsystems/quint/tree/main/examples/tutorials/hello.qnt). @@ -209,7 +209,7 @@ To test our protocol, we fix one particular execution sequence in `writeReadTest We could draw this execution sequence with UML sequence diagrams or state diagrams. For example: -![test1-sequence](../img/hello-test1.png) +![test1-sequence](./img/hello-test1.png) The unfortunate fact about UML sequence diagrams and state charts is that they are given as figures, which have to be executed in the reader's brain. diff --git a/tutorials/lesson1-booleans/booleans.qnt b/docs/codetour/lesson1-booleans/booleans.qnt similarity index 100% rename from tutorials/lesson1-booleans/booleans.qnt rename to docs/codetour/lesson1-booleans/booleans.qnt diff --git a/tutorials/lesson1-booleans/booleans.template.qnt b/docs/codetour/lesson1-booleans/booleans.template.qnt similarity index 99% rename from tutorials/lesson1-booleans/booleans.template.qnt rename to docs/codetour/lesson1-booleans/booleans.template.qnt index a9ce95fe4..ad089bb2a 100644 --- a/tutorials/lesson1-booleans/booleans.template.qnt +++ b/docs/codetour/lesson1-booleans/booleans.template.qnt @@ -346,7 +346,7 @@ Try `iff` for all combinations of `false` and `true`: We have covered all the operators over Booleans. If you want to see all operators in one place, -check [booleans.qnt](./booleans.qnt). +check [booleans.qnt](https://github.com/informalsystems/quint/tree/main/examples/tutorials/booleans.qnt). We are experimenting with different kinds of tutorials. It would be great to learn, whether you liked this tutorial format, or not. diff --git a/docs/codetour/lesson1-booleans/booleans.xml b/docs/codetour/lesson1-booleans/booleans.xml new file mode 100644 index 000000000..9ff5395d8 --- /dev/null +++ b/docs/codetour/lesson1-booleans/booleans.xml @@ -0,0 +1,263 @@ + + Lesson 1 - Booleans + + + Introduction + +In this lesson, you will learn how to use Booleans. It should take you real quick to learn this lesson. + +**Do not skip this lesson.** It introduces a few important language concepts, not just the Booleans. + +If you would like to see the complete code before diving into +the details, check [booleans.qnt](./booleans.qnt). + + + lesson1-booleans/booleans.qnt + 4 + + + lesson1-booleans/booleans.qnt + 10 + Boolean literals + +Quint has two built-in values of `bool` type, called Boolean literals: + + - `false` is the value that represents the value "false". + - `true` is the value that represents the value "true". + +Note that Quint is strict with respect to Boolean values. +There are only `false` and `true`. They are comparable +only to Boolean values, and there are no implicit conversions +from other types to the Boolean type. + +You cannot modify Boolean values. You can carry them +around as variable values, or in data structures. + +Evaluate the Boolean literals in REPL: + echo "false" | quint + echo "true" | quint + + + + lesson1-booleans/booleans.qnt + 13 + Boolean negation + +The simplest operation we can do with a Boolean value is negation. + +Evaluate the negation of `false` and `true`: + + echo "not(false)" | quint + echo "not(true)" | quint + + + + lesson1-booleans/booleans.qnt + 16 + Boolean equality + + +We can compare Booleans for equality. The rules for equality are straightforward. + +Try comparing `false` and `true` in all possible combinations: + + + echo "false == false" | quint + echo "false == true" | quint + echo "true == false" | quint + echo "true == true" | quint + + +One important feature of equality is that the arguments should have the same type. +Hence, Booleans can be only compared to Booleans. The following expressions +produces type errors. Try them: + + + echo "false == 0" | quint + echo "true == 1" | quint + + + + lesson1-booleans/booleans.qnt + 19 + Boolean inequality + +We can compare Booleans for inequality. It is simply the opposite of `x == y`. + +Try comparing `false` and `true` in all possible combinations: + + echo "false != false" | quint + echo "false != true" | quint + echo "true != false" | quint + echo "true != true" | quint + + + + lesson1-booleans/booleans.qnt + 22 + Dot form + + +If you prefer object-oriented style, you can also write `x.not()`. Try it out: + + + echo "false.not()" | quint + + +**This is a general principle:** You can write `foo(x)` as `x.foo()` and vice versa. + +Be careful about not writing `false.not`, as it would be understood as record access: + + + echo "false.not" | quint + + + + lesson1-booleans/booleans.qnt + 25 + Boolean "and" + +Now it is time to learn about Boolean "and". + +Evaluate all possible combinations of `false` and `true`: + + echo "false and false" | quint + echo "false and true" | quint + echo "true and false" | quint + echo "true and true" | quint + + + + lesson1-booleans/booleans.qnt + 28 + Dot form for binary operators + + +Similar to the operator `not`, you can use the object-oriented form for +Boolean "and". Try it: + + + echo "false.and(true)" | quint + + +**This is a general principle:** You can replace `bar(x, y)` with `x.bar(y)`, +and vice versa. + +This form may be useful with nested formulas like: + + ``` + x.and(y).and(z) + ``` + + + + + lesson1-booleans/booleans.qnt + 31 + And works for more than two arguments + +As you see, "and" does not have to apply to two arguments only. You can use it with multiple arguments: + + echo "and(true, false, true)" | quint + echo "and(true, false, true, false)" | quint + echo "and(true, false, true, false, true)" | quint + + + + lesson1-booleans/booleans.qnt + 38 + The and {...} form + + +Sometimes, we have to write complex expressions over Booleans. Yeah, that happens. + +In this case, you can use the convenient form and { ... }, which is just syntax sugar for and(...). + +Try it: + + + echo "and { false == false, true == true }" | quint + + + + lesson1-booleans/booleans.qnt + 41 + Boolean "or" + +If there is "and", there must be "or". +Evaluate all possible combinations of `false` and `true`: + + echo "false or false" | quint + echo "false or true" | quint + echo "true or false" | quint + echo "true or true" | quint + + + + lesson1-booleans/booleans.qnt + 54 + Other forms of "or" + +Similar to "and", we can use the dot-notation, "or" over multiple arguments, +and the `or { ... }`. + + + + lesson1-booleans/booleans.qnt + 58 + Boolean implication + +Perhaps, you remember Boolean implication from your math classes. +In some languages, it is written as `x -> y` or `x => y`. +In Quint is written as `x implies y`, or, alternatively, `x.implies(y)`. + +If you don't like it, you don't have to use it: +`x implies y` is equivalent to `not(x) or y`. + +Try the implication for all combinations of `false` and `true`: + + echo "false implies false" | quint + echo "false implies true" | quint + echo "true implies false" | quint + echo "true implies true" | quint + + + + lesson1-booleans/booleans.qnt + 63 + Boolean equivalence + +Finally, we have the equivalence operator `x iff y`, or, alternatively, `x.iff(y)`. +It is really like `x == y`, but `x iff y` does a bit more: +It requires `x` and `y` to be Booleans. + +What is it is good for? If you know what protocol invariants are, +`x iff y` looks nice when writing invariants and temporal formulas. +If you are not familiar with invariants and temporal formulas, +you probably do not need `iff` yet. + +Try `iff` for all combinations of `false` and `true`: + + echo "false iff false" | quint + echo "false iff true" | quint + echo "true iff false" | quint + echo "true iff true" | quint + + + + lesson1-booleans/booleans.qnt + 63 + Suming it up + +We have covered all the operators over Booleans. +If you want to see all operators in one place, +check [booleans.qnt](https://github.com/informalsystems/quint/tree/main/examples/tutorials/booleans.qnt). + +We are experimenting with different kinds of tutorials. +It would be great to learn, whether you liked this tutorial format, or not. +Please vote in the +[discussion](https://github.com/informalsystems/quint/discussions/516). + + + + diff --git a/tutorials/lesson2-integers/integers.qnt b/docs/codetour/lesson2-integers/integers.qnt similarity index 100% rename from tutorials/lesson2-integers/integers.qnt rename to docs/codetour/lesson2-integers/integers.qnt diff --git a/tutorials/lesson2-integers/integers.template.qnt b/docs/codetour/lesson2-integers/integers.template.qnt similarity index 100% rename from tutorials/lesson2-integers/integers.template.qnt rename to docs/codetour/lesson2-integers/integers.template.qnt diff --git a/docs/codetour/lesson2-integers/integers.xml b/docs/codetour/lesson2-integers/integers.xml new file mode 100644 index 000000000..4e6e9159e --- /dev/null +++ b/docs/codetour/lesson2-integers/integers.xml @@ -0,0 +1,249 @@ + + Lesson 2 - Integers + + + Introduction + +This lesson teaches you the basics of operations over integers. +If you have programming experience, you know most of these operators. +So it should not take you long to finish this lesson. +Do not skip this lesson, as some of the operators may still surprise you. + + lesson2-integers/integers.qnt + 4 + + + lesson2-integers/integers.qnt + 13 + Integer literals + +Integer literals are written using the standard syntax: +0, 1, -1, 2, -2, ..., 314159265358979323846264338327950288419716939937510. + +It is important to understand that Quint integers are big integers. +There is neither a minimum integer, nor there is a maximum integer. + +It's quite easy to express the standard 32-bit, 64-bit, 128-bit, and 512-bit +integers with Quint integers. We will cover this in a follow up tutorial. + +We omit several integer operators that produce sets. These operators are covered in +the tutorial on sets. + + + + lesson2-integers/integers.qnt + 17 + Integer exponentiation + + +We start with the exponentiation `i^j` over integers, +which is also written as `pow(i, j)` in many languages. +The result of `i^j` is simply `i` multiplied by itself `j - 1` times. +This is the first operator, as it lets us to write nice examples +in the following steps. + +To get a better intuition at how `i^j` works, run the following examples: + + + echo "2^32" | quint + 4294967296 + echo "2^64" | quint + 18446744073709551616 + echo "2^256" | quint + 115792089237316195423570985008687907853269984665640564039457584007913129639936 + echo "2^512" | quint + 13407807929942597099574024998205846127479365820592393377723561443721764030073546976801874298166903427690031858186486050853753882811946569946433649006084096 + + +Exponentiation is not defined on all of its arguments. +Try the following examples to get the intuition. + + echo "(-2)^3" | quint + -8 + echo "(-2)^4" | quint + 16 + echo "2^(-4)" | quint + error + echo "0^3" | quint + 0 + echo "0^0" | quint + error + echo "0^(-2)" | quint + error + + + + lesson2-integers/integers.qnt + 20 + Integer addition + + +We can add two integers by writing `i + j`. Importantly, integers cannot +overflow, as they are big integers. + +Try this simple example: + + + echo "2022 + 2023" | quint + 4045 + + +It is important to remember that integer addition satisfies the laws +of commutativity and associativity in Quint: + + + echo "11 + 17 == 17 + 11" | quint + true + echo "(11 + 17) + 19 == 11 + (17 + 19)" | quint + true + + + + lesson2-integers/integers.qnt + 23 + Integer subtraction + + +We can add two integers by writing `i - j`. + + - Try this and see the result of the evaluation: + + + echo "2022 - 2023" | quint + -1 + + + - Addition and subtraction have the same priority. + Hence they are applied from left to right. + + + echo "11 + 13 - 17" | quint + 7 + + **Exercise:** Is subtraction commutative? + + + + + lesson2-integers/integers.qnt + 26 + Integer multiplication + + +We can multiply two integers by writing `i * j`. +Remember that multiplication cannot produce any side effects like overflows: + + + echo "2^32 * 2^32 == 2^64" | quint + true + + +It is important to remember that integer multiplication satisfies the laws +of commutativity and associativity in Quint: + + + echo "11 * 17 == 17 * 11" | quint + true + echo "(11 * 17) * 19 == 11 * (17 * 19)" | quint + true + + + + lesson2-integers/integers.qnt + 33 + Integer division and remainder + + +The operators `i / j` and `i % j` compute the integer part and the remainder +when dividing `i` by `j`, respectively. These operators have the following +property for all integer values of `i` and all integer values of `j != 0`: +`i == (i / j) * j + i % j`. + +Try the following examples to check your intuition: + + + echo "99 / 2 == 49" | quint + true + echo "99 % 2 == 1" | quint + true + echo "98 % 2 == 0" | quint + true + echo "((2^64 - 1) % 2^64 + 1) % 2^64" | quint + 0 + echo "(2^64 - 123) % 2^63" | quint + 0 + + + **Exercise:** Is division commutative? + + + + + lesson2-integers/integers.qnt + 52 + Integer comparison + + +These are the standard comparison operators that you know, for sure: + + - Less than: `i < j` + - Less than or equal: `i <= j` + - Greater than: `i > j` + - Greater than or equal: `i >= j` + - Equals to: `i == j` + - Not equal to: `i != j` + +Check your intuition by running the following examples: + + echo "10 < 11" | quint + true + echo "10 < 10" | quint + false + echo "10 <= 11" | quint + true + echo "10 <= 10" | quint + true + echo "10 <= 9" | quint + true + echo "11 > 10" | quint + true + echo "10 > 10" | quint + false + echo "11 >= 10" | quint + true + echo "10 >= 10" | quint + true + echo "10 >= 9" | quint + true + echo "10 == 10" | quint + true + echo "10 == 11" | quint + false + echo "10 != 10" | quint + false + echo "10 != 11" | quint + true + + + + lesson2-integers/integers.qnt + 55 + Integer negation + + +We almost forgot about the integer negation! +Not surprisingly, `-i` negates an integer `i`. +Recall that Quint integers are big integers. +Hence, no overflow is possible. + +Try a few examples: + + + echo "-(3 + 2)" | quint + -5 + echo "-(-2^63) == 2^63" | quint + true + + + + diff --git a/tutorials/lesson3-anatomy/coin.qnt b/docs/codetour/lesson3-anatomy/coin.qnt similarity index 100% rename from tutorials/lesson3-anatomy/coin.qnt rename to docs/codetour/lesson3-anatomy/coin.qnt diff --git a/tutorials/lesson3-anatomy/coin.template.qnt b/docs/codetour/lesson3-anatomy/coin.template.qnt similarity index 99% rename from tutorials/lesson3-anatomy/coin.template.qnt rename to docs/codetour/lesson3-anatomy/coin.template.qnt index ef859f81d..2de104336 100644 --- a/tutorials/lesson3-anatomy/coin.template.qnt +++ b/docs/codetour/lesson3-anatomy/coin.template.qnt @@ -66,7 +66,7 @@ In this tutorial, you will see how to: If you would like to see the complete code before diving into -the details, check [coin.qnt](./coin.qnt). +the details, check [coin.qnt](https://github.com/informalsystems/quint/tree/main/examples/tutorials/coin.qnt). $file diff --git a/docs/codetour/lesson3-anatomy/coin.xml b/docs/codetour/lesson3-anatomy/coin.xml new file mode 100644 index 000000000..db47988c5 --- /dev/null +++ b/docs/codetour/lesson3-anatomy/coin.xml @@ -0,0 +1,721 @@ + + Lesson 3 - Basic anatomy of a protocol in Quint + + + Introduction + +In this tutorial, we explain the standard structure of a Quint protocol. +Although Quint does not impose a very rigid structure on protocol designers, +we have found that following common practices helps protocol authors and +their readers to cooperate more effectively. + +As a running example, we are using the subcurrency smart contract that +is introduced in the Solidity documentation, see +[Subcurrency](https://docs.soliditylang.org/en/v0.8.17/introduction-to-smart-contracts.html#subcurrency-example). +You do not have to know Solidity to understand this tutorial. +On the contrary, you may acquire some basic understanding of Solidity contracts +by reading the Quint protocol specification. + +The subcurrency smart contract defines a basic protocol that has the following features: + + - A single user (the protocol creator) is assigned the "minter" role. + + - The minter may generate new coins on the balances of the users, including themselves. + + - The users may send coins to other users, provided that they have enough coins + on their balance. + +In this tutorial, you will see how to: + + - Specify basic type aliases. + + - Define handy functions that can be used independently of the protocol. + + - Define the structure of the protocol states. + + - Define helper functions that read the current state, but do not modify it. + + - Define actions that read and modify the current state of the protocol. + + - Define basic protocol invariants. + + - Write basic protocol tests. + + - Write protocol tests that use input non-determinism. + + - Run a basic randomized test to discover an invariant violation. + + + +If you would like to see the complete code before diving into +the details, check [coin.qnt](https://github.com/informalsystems/quint/tree/main/examples/tutorials/coin.qnt). + + + lesson3-anatomy/coin.qnt + 16 + + + Declare a single module + +Quint specifications are organized in modules, and may consist of one or many +top-level module declarations. +Nested module declarations are currently not supported. + +In this tutorial, we declare a single module. In general, we recommend to start +with a single module and introduce multiple modules only if you are going to +reuse various parts of your protocol. + + lesson3-anatomy/coin.qnt + 17 + + + Declare types + +Similar to programming languages, it is common in Quint to declare type +aliases for the types that appear in the protocol description often. +A type alias is simply a declaration of the form: + +```scala +type [name] = [tp]; +``` + +In the above definition, `[name]` is a unique identifier that will be associated +with the type, and `[tp]` is the type to be associated with the name, e.g., +`int` or `Set[int]`. To see a complete description of all available types, visit the Section +[Type System 1.2](https://github.com/informalsystems/quint/blob/main/doc/lang.md#type-system-12) +of the language manual. + +Although it is convenient to declare type aliases, it is not mandatory. +Protocol authors should decide together with their audience, whether they prefer +minimal type definitions, or they like abundance of types. Quint replaces type aliases +with the types on the right-hand sides of `type`. For example, it does not distinguish between +different kinds of "integers", when they are referred to via different type aliases. + + lesson3-anatomy/coin.qnt + 28 + + + Declare pure functions + + +It often happens that a protocol requires auxilliary definitions that do not +depend on the protocol state, but only on the values of their parameters. +Such computations are often called "pure". In the above code, we define two +pure definitions: + + - The pure value `MAX_UINT`. + + - The pure definition `isUInt` that computes if a given integer `i` is within + the range from 0 to MAX_UINT (inclusive). + +The main property of pure values is that they always return the same value. +Pure definitions always return the same value, if they are supplied with the +same arguments. + +To see that no state is needed, evaluate these definitions in REPL +(read-evaluate-print-loop): + + echo "MAX_UINT" | quint -r coin.qnt::coin + echo "MAX_UINT" | quint -r ./lesson3-anatomy/coin.qnt::coin + + echo "isUInt(22)" | quint -r coin.qnt::coin + echo "isUInt(22)" | quint -r ./lesson3-anatomy/coin.qnt::coin + + echo "isUInt(-1)" | quint -r coin.qnt::coin + echo "isUInt(-1)" | quint -r ./lesson3-anatomy/coin.qnt::coin + + echo "isUInt(MAX_UINT + 1)" | quint -r coin.qnt::coin + echo "isUInt(MAX_UINT + 1)" | quint -r ./lesson3-anatomy/coin.qnt::coin + + +The functional layer is actually quite powerful; a lot of protocol behavior can +be defined here without referring to protocol state. + +As you can see, we have omitted the type of `MAX_UINT` but specified the type of +`isUInt`. Most of the time, the type checker can infer the types of values and +operator definitions, and giving additional type annotations is up to you. In +rare cases, the type checker may get confused, and then explicit type +annotations will help you in figuring out the issue. + + + lesson3-anatomy/coin.qnt + 37 + + + Declare the protocol parameters + + +In this step, we are starting to describe the protocol structure and behavior +in terms of a state machine. + +It often happens that protocols are parameterized in the sense that the protocol +may be instantiated for different parameter values, and different instances +still make sense. The typical parameters are: + + - the set of all possible addresses, + + - the address that performs a special role, + + - minimal and maximal values, + + - timeout values. + +For this purpose, Quint offers `const` declarations. You can see one of them +in the commented out section of the code above. You may be wondering, what is +the difference between `const` and `pure val`. They mean to express the same +concept: A value that stays the same for all computations. However, they differ +in the time when they are bound to a value: + + - The `pure val` values are immediately defined via an expression in the + right-hand side. + + - The `const` values are first declared and later they are substituted + with actual values via an `instance` declaration. + +Constant declarations are not fully supported yet. They will be available +as soon as the issue [#528](https://github.com/informalsystems/quint/issues/528) +is closed. + +At the moment, we simply declare the value for a small set of addresses `ADDR`, +in order to be able to iterate on the protocol specification quickly. + + lesson3-anatomy/coin.qnt + 46 + + + Declare the protocol state + + +As a next step, we declare state variables, which together represent +a state of the protocol. In the above code, we introduce two such variables: + + - The variable `minter` to store the minter's address. + The type of this variable is `Addr`. + + - The variable `balances` to store the balances of all users. + The type of this variable is `Addr -> UInt`, which means a map from + values of type `Addr` to values of type `UInt`. + +Variable definitions always require type. Otherwise, it may be too hard +for the type checker to infer the types of the state variables. + +If you compare the above variable definitions to the relevant variable +declarations in the Solidity contract, you should see that our variable +declarations are conceptually similar, modulo the slightly different syntax +of Solidity: + + +```Solidity + // in Solidity: + address public minter; + mapping (address => uint) public balances; +``` + + lesson3-anatomy/coin.qnt + 51 + + + Declare operators over states + + +It is often convenient to define a few helper operators. + +We start with the definition of `state` that represents the entire +state as a record. This is what we do in the above code with the definition of +`state`. Notice that the definition of `state` is prefixed with `val`, not `pure val`. +Since `state` accesses state variables is it impure. + +You can try to evaluate the definition of `state` in REPL right away: + + echo "state" | quint -r coin.qnt::coin + echo "state" | quint -r ./lesson3-anatomy/coin.qnt::coin + + +If you tried that, you saw several error messages. The reason is that the state +variables are not initialized by default. We would have to introduce an +initialization action, which is usually called `init`. You will see how to +do that a few steps later. + + + lesson3-anatomy/coin.qnt + 53 + + + Declare `require` and `totalSupply` + + +In the above code, we introduce a helper definition `require` that +evaluates its Boolean parameter. As you might have noticed, this +definition is not doing anything useful. We introduce it to make the code +a bit more accessible for first-time readers who are familiar with +Solidity. + +The definition of `totalSupply` may look a bit complex, +if you have never seen similar code before. Let's break it down into smaller +pieces: + + - The definition of `totalSupply` defines a value, but not a `pure` one. + Hence, even though `totalSupply` does not take any parameters, + it implicitly depends on the state. As a result, `totalSupply` + may evaluate to different values in different states, that is, + in those states where the values of `balances` differ. + + - `ADDR.fold(0, f)` iterates over the set of addresses in *some order* + and for every address `a`, it applies `f(s, a)` for the accumulator value `s`, + which is initialized with 0. In our example, the operator `f` is defined as + an anonymous lambda operator: `(sum, a) => sum + balances.get(a)`. For our + definition of `ADDR`, the computed value would be equal to: + + ```scala + ((((0 + balances.get("null")) + balances.get("alice")) + balances.get("bob")) + + balances.get("charlie")) + balances.get("eve") + ``` + + *Note that the order of the addresses in the brackets may be different from + the one above. Hence, you should not rely on a particular order when + using `fold` over sets. We are fine when using commutative operators such as `+` and `*`.* + + + lesson3-anatomy/coin.qnt + 59 + + + Declare an initializer `init` + + +As you may have seen in the previous steps, the state variables `minter` and `balances` +are not initialized by default. In order to compute an initial state (there may be +several!), we define a special action that we call an *initializer*. In our +code, such an action is called `init`. + +This definition is essential for describing the protocol. When you read somebody else's +protocol, it is one of the key parts to look at. + +If we go back to the Solidity code, it looks as follows: + +```Solidity + // in Solidity + constructor() { + minter = msg.sender; + } +``` + +In the Solidity code, the constructor is using an implicit parameter `msg.sender`, +which is propagated from a user request by the Ethereum Virtual Machine. Quint +does not have any built-in mechanism for reading user input. Instead, Quint +offers a very powerful mechanism of non-deterministic choice. This is +exactly what we do with the following line of code: + +```scala +nondet sender = oneOf(ADDR) +``` + +This expression non-deterministically chooses one value from the set `ADDR` +(assuming that the set is not empty) and binds this value to the name `sender`. +The qualifier `nondet` indicates that the value of `sender` is special: the +name `sender` is bound to a fixed value, but `sender` *may evaluate to two +different values* when `init` is called twice or is called in different runs. +This behavior may look +complicated, but this is exactly what we expect from user input, too: +The user may submit different inputs, even if the protocol resides in +two identical states. + +For more details, check +[oneOf](https://github.com/informalsystems/quint/blob/main/doc/builtin.md#pure-def-oneof-seta--a) +in the reference manual. + + + lesson3-anatomy/coin.qnt + 65 + + + Assign initial values to the state variables + + +The rest of `init` is simple: +The value of `minter` is set to the value of `sender`, and the value +of `balances` is set to the map that maps all addresses from `ADDR` +to value 0. Notice that the variables `minter` and `balances` are not assigned +their new values immediately; they are assigned once `init` is +evaluated completely (and only if `init` evaluated to `true`). + +Now we can call `init` and evaluate an initialized state in REPL: + + echo "init\n state" | quint -r coin.qnt::coin + echo "init\n state" | quint -r ./lesson3-anatomy/coin.qnt::coin + + +**Exercise:** Call `init` multiple times in REPL and evaluate `state` after +each call. Did you get the same initial states every time or were some states +different? + +Remember that we called `init` an initializer? This is because `init` +only assigns new values to state variables, but does not read their previous values. + +**Exercise:** Modify the assignment to `balances` in such a way that `minter` +gets 1000 coins, while the other addresses get 0 coins, as before. +Hint: use `if (cond) value1 else value2`. + + + + lesson3-anatomy/coin.qnt + 70 + + + Defining the action `send` + + +Similar to Solidity, we define the action `mint`. In contrast to `init`, +we have decided to avoid non-deterministic choice in `mint`. +Instead we are passing the user inputs as action parameters. You will see +later that this makes debugging and testing easier. + +If you understood how `init` works, the behavior of `mint` should be also +easy to figure out. If you wonder what `get` and `set` are doing, here is +the explanation: + + - `balances.get(receiver)` produces the value assigned to the key `receiver` + in the map `balances`. If the key `receiver` has no value assigned in + the map `balances`, REPL would show a runtime error. + For more details, check + [get](https://github.com/informalsystems/quint/blob/main/doc/builtin.md#pure-def-get-a---b-a--b) + in the reference manual. + + - `balances.set(receiver, newBal)` produces a new map, that assigns + the value of `newBal` to the key `receiver`, and keeps the other key-value + pairs as in `balances`. + For more details, check + [set](https://github.com/informalsystems/quint/blob/main/doc/builtin.md#pure-def-set-a---b-a-b--a---b) + in the reference manual. + +Now it's time to mint some coins in REPL! Try the following: + + echo 'init\n mint(minter, "bob", 2023)\n state' | quint -r coin.qnt::coin + echo 'init\n mint(minter, "bob", 2023)\n state' | quint -r ./lesson3-anatomy/coin.qnt::coin + + +As you can see, we can mint coins for Bob. + +**Exercise:** Mint more coins for Bob, actually, mint `MAX_UINT` coins. +Do you understand what happened in this case? + + + + lesson3-anatomy/coin.qnt + 87 + + + Defining the action `send` + + +If you understood the mechanics of the action `mint`, you should easily +figure out the behavior of `send`. + +Play with `mint` and `send` in REPL! The simplest scenario would be: + + echo 'init\n mint(minter, "bob", 2023)\n send("bob", "eve", 1024)\n state' | quint -r coin.qnt::coin + echo 'init\n mint(minter, "bob", 2023)\n send("bob", "eve", 1024)\n state' | quint -r ./lesson3-anatomy/coin.qnt::coin + + + lesson3-anatomy/coin.qnt + 113 + + + Defining a protocol step + + +Finally, we can put together `mint` and `send` to describe every possible +transition of the protocol! To this end, we non-deterministically choose +the sender, the receiver, and the amount (from the set of integers from 0 +to `MAX_UINT`, inclusive). The expression `any { ... }` executes one of its +arguments; if both could be executed, one of them is executed +non-deterministically. + +**Exercise:** Run REPL, execute `init` once and execute `step` multiple times. +Do you understand why some of the occurrences of `step` evaluate to `false` +and some evaluate to `true`? It may help you, if you print `state` after `init` +and `step`. + +We have defined the state variables, the initializer, and a step of the protocol. +This is the minimal set of tasks for defining a working protocol. +What we have achieved here is great! You can play with the protocol, feed it +with different parameters in REPL, and experiment with your protocol. + +Although you should definitely play with the protocol at this point, we urge you +not to stop here! In the next steps, we show you the real magic of Quint. + + lesson3-anatomy/coin.qnt + 125 + + + Expected properties + + +Having defined the protocol behavior with `init` and `step`, it is time to think +about what we expect from the protocol. This is a good place for specifying +protocol invariants and temporal properties. + + + lesson3-anatomy/coin.qnt + 127 + + + Defining the most basic invariant + + +One of the most basic properties that we expect from the protocol is defined +by the property `balancesRangeInv`. This property goes over all addresses +in `ADDR` and tests, whether the value stored in `balances` for every address +is in the range from `0` to `MAX_UINT` (inclusive). + +We can immediately check this invariant for a few states: + + echo 'init\n balancesRangeInv\n mint(minter, "bob", 2023)\n balancesRangeInv\n send("bob", "eve", 1024)\n balancesRangeInv\n ' | quint -r coin.qnt::coin + echo 'init\n balancesRangeInv\n mint(minter, "bob", 2023)\n balancesRangeInv\n send("bob", "eve", 1024)\n balancesRangeInv\n ' | quint -r ./lesson3-anatomy/coin.qnt::coin + + +Properties like `balancesRangeInv` are called *invariants*, because we expect +them to hold in every state that could be produced by `init` and a number of +`step` actions. Intuitively, whatever happens, the property should hold true. + +It is important to distinguish between the properties that we would like to be +invariants and the properties that we have proven to be invariants. +To be precise, the former properties are called *invariant candidates*, +whereas the latter are actually called *invariants*. + + + + lesson3-anatomy/coin.qnt + 133 + + + Defining the total supply invariant + + +Another basic invariant that we intuitively expect to hold is defined in +`totalSupplyDoesNotOverflowInv`. It is so simple that it should hold true, right? +If we have your attention now, read further! + +**Exercise:** Extend the protocol with a state variable called `totalMinted`, +initialize it with 0 and increase it in `mint` with the `amount` passed to `mint`. + + + lesson3-anatomy/coin.qnt + 140 + + + Temporal properties + + +After we have defined state invariants, we should normally think about +temporal properties in general such as safety and liveness. However, +temporal properties are an advanced topic, and smart +contracts are typically focused on safety. Thus, it is safe to skip this topic +for now. It is just important to know that temporal properties are labelled +with the qualifier `temporal`. + + + lesson3-anatomy/coin.qnt + 148 + + + Tests + + +So far, we have been running sequences of actions in REPL, to get +basic understanding of the protocol mechanics. While REPL is capable of +replaying actions one-by-one, it would be more convenient to run something +similar to unit tests or integration tests, which are ubiquitous in +programming languages. + +We normally add tests in the very bottom of the protocol module, or in +a separate module. + +Quint introduces *runs* to express "happy paths" and tests. The above code +shows a simple test `sendWithoutMintTest`. +In this test, the action `init` runs first. If it evaluates to `true`, +then the action `send(...)` is run. Since this action is composed with +`fail()`, the action `send(...)` is expected to evaluate to `false`. + +Go ahead and see if this test goes through: + + echo 'sendWithoutMintTest' | quint -r coin.qnt::coin + echo 'sendWithoutMintTest' | quint -r ./lesson3-anatomy/coin.qnt::coin + + +**Exercise:** Actually, if you look carefully at the code of `send`, +you can find one value for `amount` that makes `send` work even without +prior minting. What is this value? + + + lesson3-anatomy/coin.qnt + 156 + + + A single data point test + + +We can write longer tests that are similar to unit/integration tests +in normal programming languages. For instance, the above test `mintSendTest` +makes sure that the exact sequence of `mint` and `send` transactions goes +through and the resulting balances have the expected values. + + echo 'mintSendTest' | quint -r coin.qnt::coin + echo 'mintSendTest' | quint -r ./lesson3-anatomy/coin.qnt::coin + + +**Exercise:** Change some numbers in the test, run it again in REPL and observe +what happens. + +Although it is better to have a test like `mintSendTest` than no test at all, +`mintSendTest` is testing only one data point. We can do better in Quint, +see the next step. + + + + lesson3-anatomy/coin.qnt + 168 + + + Testing with non-deterministic inputs + + +Instead of testing a sequence of transactions for a carefully crafted +single input, we could fix a sequence of transactions and let the computer +find the inputs that fail the test. This is exactly what we are doing in +the above test `mintTwiceThenSendError`. The test non-deterministically +chooses the amounts of coins to mint and send and then executes the actions +`mint`, `mint`, and `send`. As the values are chosen non-deterministically, +we know that some of the inputs should fail `send`. Our hypothesis is that +`send` should never fail when Eve has enough coins on her account. +If she does not, we simply ignore this choice of inputs in the else-branch. + +Let's run this test: + + echo 'mintTwiceThenSendError' | quint -r coin.qnt::coin + echo 'mintTwiceThenSendError' | quint -r ./lesson3-anatomy/coin.qnt::coin + + + +If you lucky, it fails right away. If it does not fail, run it multiple times, +until it fails. To see why it failed, evaluate `state` after executing +the test. Do you understand why our hypothesis was wrong? + +**Exercise:** Fix the condition in `mintTwiceThenSendError`, +so that the test never fails. + +If you carefully look at `mintTwiceThenSendError`, you will see that it is still +a single data point test, though the data point (the inputs) are chosen +non-deterministically every time we run the test. In fact, REPL implements +non-determinism via random choice. + +If you do not want to sit the whole day and run the test, you could integrate +it into continuous integration, so it runs from time to time for different +inputs. Quint comes with the command `test` that is designed for exactly this +purpose. Try the command below. Most likely, it would find a violation +after just a few tests. + + + quint test --match mintTwiceThenSendError coin.qnt + quint test --match mintTwiceThenSendError ./lesson3-anatomy/coin.qnt + + +Also, we had to fix the sequence of actions in our test. We can do +better with Quint. + + + + lesson3-anatomy/coin.qnt + 192 + + + Testing with non-deterministic inputs and control + + +It is often hard to find a good sequence of actions that breaks an invariant. +Similar to how we let the computer to find the right inputs, we can use the +computing power to look for bad sequences of actions. + +Quint REPL actually supports random search for sequences of actions that violate +an invariant. Its UX is a bit ad hoc at the moment. We will improve it in the +future. You can try it right away: + + quint run --invariant totalSupplyDoesNotOverflowInv coin.qnt + quint run --invariant totalSupplyDoesNotOverflowInv ./lesson3-anatomy/coin.qnt + + + +The above command in REPL randomly produces sequences of steps, +starting with `init` and continuing with `step`, up to 20 steps. It checks the +invariant `totalSupplyDoesNotOverflowInv` after every step. If the invariant is +violated, the random search stops and returns `false`. If no invariant violaion +is found, the search returns `true` after enumerating the specified number of runs, +which is 10000 in our example. The search parameters such as the number of +runs and steps can be tuned. Check the options of `run`: + + + + quint run --help + quint run --help + + + lesson3-anatomy/coin.qnt + 197 + + + Does random testing always find bugs? + + +To be honest, our example is relatively simple, and we were quite lucky +that the invariant `totalSupplyDoesNotOverflowInv` was often violated by +a completely random search. For more complex protocols, random search +often gets stuck while looking for non-interesting inputs or sequences +of actions. Hence, if you have a hunch about where an error could potentially +be, you could restrict the scope of the search by: + + - Restricting the scope of non-determinstic choices, e.g., + by making every set in `oneOf(S)` smaller. + + - Restricting the choice of actions, e.g., by removing non-essential + actions from `step`. + +Although the above tricks may help you in detecting some bugs, it is well +known that there is always a probability of missing a bug with random search. + +If you are looking for better guarantees of correctness, Quint will be soon +integrated with the +[Apalache model checker](https://github.com/informalsystems/apalache). +The model checker looks for counterexamples more exhaustively, by solving +equations. In some cases, it may even give you a guarantee that there is no bug, +if it has not found any. + + + lesson3-anatomy/coin.qnt + 198 + + + lesson3-anatomy/coin.qnt + 199 + Suming it up + + +This was a long tutorial. We have specified the whole protocol, +experimented with it, wrote several tests and even found some surprising +behavior of this protocol. We hope that by going through this tutorial +you have got an idea of how you could specify your own protocol in Quint +and make Quint useful for solving your problems with protocols! + +We have used a Solidity contract as the running example. Actually, +there is not much special about Solidity in this coin protocol. +A similar protocol could be implemented in +[Cosmos SDK](https://tutorials.cosmos.network/) +or [Cosmwasm](https://cosmwasm.com/). + +We are experimenting with different kinds of tutorials. +It would be great to learn whether you liked this tutorial format, or not. +Please vote in the +[discussion](https://github.com/informalsystems/quint/discussions/595). + + + + diff --git a/tutorials/lesson4-sets/sets.qnt b/docs/codetour/lesson4-sets/sets.qnt similarity index 100% rename from tutorials/lesson4-sets/sets.qnt rename to docs/codetour/lesson4-sets/sets.qnt diff --git a/tutorials/lesson4-sets/sets.template.qnt b/docs/codetour/lesson4-sets/sets.template.qnt similarity index 98% rename from tutorials/lesson4-sets/sets.template.qnt rename to docs/codetour/lesson4-sets/sets.template.qnt index edad617f1..7669a8283 100644 --- a/tutorials/lesson4-sets/sets.template.qnt +++ b/docs/codetour/lesson4-sets/sets.template.qnt @@ -41,9 +41,9 @@ For example, @KryptoCoffeeCat want to know whether they could exchange BTC for ATOM by doing up to three swaps. Unfortunately, @KryptoCoffeeCat have lost access to their browser, as they did not update it as often as it was required. Now they are stuck with the -[Quint REPL](https://github.com/informalsystems/quint/blob/main/doc/quint.md#command-repl). +[Quint REPL](https://quint-lang.org/docs/quint#command-repl). Fortunately, they have found the -[Quint cheatsheet](https://github.com/informalsystems/quint/blob/main/doc/quint-cheatsheet.pdf). +[Quint cheatsheet](https://quint-lang.org/quint-cheatsheet.pdf). We will help them! In this tutorial, you will see how to: @@ -57,7 +57,7 @@ In this tutorial, you will see how to: If you would like to see the complete code before diving into -the details, check [sets.qnt](./sets.qnt). +the details, check [sets.qnt](https://github.com/informalsystems/quint/tree/main/examples/tutorials/sets.qnt). $file @@ -578,7 +578,7 @@ single click: @KryptoCoffeeCat has learned a lot in this tutorial. By looking at the -[Quint cheatsheet](https://github.com/informalsystems/quint/blob/main/doc/quint-cheatsheet.pdf), +[Quint cheatsheet](https://quint-lang.org/quint-cheatsheet.pdf), they have found that this tutorial did not cover two operators on sets. Can you find them too? diff --git a/docs/codetour/lesson4-sets/sets.xml b/docs/codetour/lesson4-sets/sets.xml new file mode 100644 index 000000000..038887ce8 --- /dev/null +++ b/docs/codetour/lesson4-sets/sets.xml @@ -0,0 +1,448 @@ + + Lesson 4 - Fun with sets + + + Introduction + +In this tutorial, we explain the most powerful data structure of Quint. +Yes, that's right, sets are the most powerful data structure. + +To make the tutorial a bit more entertaining, we will help an anonymous crypto +[Degen](https://www.coingecko.com/en/glossary/degen), who asked us to help +with Quint. We only know their handle +[@KryptoCoffeeCat](https://twitter.com/KryptoCoffeeCat). +They want to trade a few coins over different exchanges. They have collected +all the pairs of coins they could exchange, but this table is not easy to +analyze: + +| Coin A | Coin B | +| -------- | ------- | +| BTC | USDC | +| BTC | USDT | +| ETH | USDC | +| ETH | USDT | +| EVMOS | USDC | +| EVMOS | WETH | +| ETH | WETH | +| ATOM | EVMOS | +| ATOM | JUNO | +| ATOM | OSMO | +| ATOM | JUNO | +| EVMOS | JUNO | +| EVMOS | OSMO | + +For example, @KryptoCoffeeCat want to know whether they could exchange BTC +for ATOM by doing up to three swaps. Unfortunately, @KryptoCoffeeCat have lost +access to their browser, as they did not update it as often as it was required. +Now they are stuck with the +[Quint REPL](https://quint-lang.org/docs/quint#command-repl). +Fortunately, they have found the +[Quint cheatsheet](https://quint-lang.org/quint-cheatsheet.pdf). +We will help them! + +In this tutorial, you will see how to: + + - use tuples, + + - use sets, + + - do plenty of interesting computations over sets. + + + +If you would like to see the complete code before diving into +the details, check [sets.qnt](https://github.com/informalsystems/quint/tree/main/examples/tutorials/sets.qnt). + + + lesson4-sets/sets.qnt + 4 + + + Defining swap pairs + +Since @KryptoCoffeeCat write down swap pairs in their table, +the most natural way of representing a row in this table is a pair. +Hence, we declare the type `Pair` as `(str, str)`. This type is a special +case of a tuple. + +In general, tuples of the same type contain a fixed number +of elements, which can have different types themselves. +Tuples are a very simple data structure in Quint that basically +has two kinds of operations: + + + - We can construct a tuple. For example: + + echo '("BTC", "ETH")' | quint + + + - We can access a tuple element via special fields `_1`, `_2`, etc. + For example: + + echo '("BTC", "ETH")._2' | quint + + lesson4-sets/sets.qnt + 13 + + + Defining available pairs + +Once we have decided to use tuples to represent the rows of the table, +we can define the table of all pairs as the set `availablePairs`. + +Would it make sense to use an array or a list instead? The answer is yes, +provided that our goal was to write an efficient implementation. However, +our goal is to describe the problem in a way that is free of irrelevant +implementation details. In this case, the set is the best data structure: + + - The table of swap pairs was written in *some* order, but this order is + actually irrelevant. @KryptoCoffeeCat wrote the pairs in some order, + but they could do it differently. + + - The table *should not contain duplicate pairs*. If you look carefully, + @KryptoCoffeeCat wrote the pair `("ATOM", "JUNO")` twice. + We can see that Quint removed the duplicates: + + echo 'availablePairs' | quint -r sets.qnt::sets + echo 'availablePairs' | quint -r ./lesson4-sets/sets.qnt::sets + + lesson4-sets/sets.qnt + 21 + + + Testing set elements + +The simplest thing @KryptoCoffeeCat can do with `availablePairs` is +to check, which pairs are in the table. For example, try the +following definitions: + + echo 'hasAtomJuno' | quint -r sets.qnt::sets + echo 'hasAtomJuno' | quint -r ./lesson4-sets/sets.qnt::sets + + echo 'hasEvmosEth' | quint -r sets.qnt::sets + echo 'hasEvmosEth' | quint -r ./lesson4-sets/sets.qnt::sets + + lesson4-sets/sets.qnt + 25 + + + Testing some elements and all elements + +Now @KryptoCoffeeCat check whether they could swap Atom for some coins +and that they cannot swap Secret for any coin: + + echo 'hasAtom' | quint -r sets.qnt::sets + echo 'hasAtom' | quint -r ./lesson4-sets/sets.qnt::sets + + echo 'missingScrt' | quint -r sets.qnt::sets + echo 'missingScrt' | quint -r ./lesson4-sets/sets.qnt::sets + + +This works. However, @KryptoCoffeeCat notice that their definition of +`hasAtom` was not exactly right. The definition tests only the first element +of the pair. Actually, @KryptoCoffeeCat realize that the order of coins in +every pair does not matter. So we should treat the pair `("ATOM", "JUNO")` +to be the same as the pair `("JUNO", "ATOM")`. But this does not work as +expected: + + + echo '("ATOM", "JUNO") == ("JUNO", "ATOM")' | quint + + +After a bit of thinking @KryptoCoffeeCat realizs that they already know how to +express that the order of coins does not matter: Just use a set instead of a tuple. +Yes, @KryptoCoffeeCat, this is the way! + + + lesson4-sets/sets.qnt + 31 + + + Mapping pairs to sets + +After having the moment of revelation @KryptoCoffeeCat, decide to redefine the +set `availablePairs`. This time the set should contain sets like +`Set("ATOM", "JUNO")` instead of pairs like `("ATOM", "JUNO")`. +@KryptoCoffeeCat does not want to write the whole table again. So they quickly +map every pair `p` in `availablePairs` to the `Set(p._1, p._2)`. + +The type of `availableUnorderedPairs` is `Set[Set[str]]`, that is, +a set that contains sets of strings. Will that work in Quint? @KryptoCoffeeCat's +friends tell them that programming languages need a bit of boilerplate like +defining hashes, comparators, and equality. Sets of sets work in Quint out of +the box: + + echo 'availableUnorderedPairs' | quint -r sets.qnt::sets + echo 'availableUnorderedPairs' | quint -r ./lesson4-sets/sets.qnt::sets + + +Now @KryptoCoffeeCat can fix the definition of `hasAtom`. They write down +the definition `hasAtomRight`. This makes them happy. Time to have a coffee! + + + lesson4-sets/sets.qnt + 37 + + + Filtering sets + +The next thing @KryptoCoffeeCat want to do with `availableUnorderedPairs` is +to see, which pairs can be swapped with Atom. +To this end, they filter the set `availableUnorderedPairs` as in `atomPairs`: + + echo 'atomPairs' | quint -r sets.qnt::sets + echo 'atomPairs' | quint -r ./lesson4-sets/sets.qnt::sets + + +The set `atomPairs` contains only those unordered pairs that satisfy this +condition. + + + lesson4-sets/sets.qnt + 40 + + + Counting the number of elements + +To see how many swap pairs are available, @KryptoCoffeeCat +use the operator `size` in the definition `howMany`: + + echo 'howMany' | quint -r sets.qnt::sets + echo 'howMany' | quint -r ./lesson4-sets/sets.qnt::sets + + lesson4-sets/sets.qnt + 43 + + + Flattening sets + +Now @KryptoCoffeeCat wonder what kind of coins could be swapped at all. +This seems to be hard to figure out. Fortunately, they discover the right +operator in the cheatsheet. It's called `flatten`: + + echo 'availableUnorderedPairs.flatten()' | quint -r sets.qnt::sets + echo 'availableUnorderedPairs.flatten()' | quint -r ./lesson4-sets/sets.qnt::sets + + +It is easy to see how `flatten` works by evaluating +`availableUnorderedPairs.flatten()`. This operator computes the set +that contains all elements of the set's direct elements, which must be sets +themselves. + + + lesson4-sets/sets.qnt + 49 + + + Reachability via one or two swaps + +@KryptoCoffeeCat are a bit tired of this tutorial. Their real goal is to find +the coins that could be bought by one or two swaps. This sounds hard but it +is not. + +How to find, whether a pair from `availableUnorderedPairs` involves a +coin from `someCoins`? For example, assume that we have an unordered pair +`Set("ATOM", "EVMOS")` and a set `someCoinsExample` that is defined as +`Set("ATOM", "ETH", "USDT")`. +Since `Set("ATOM", "EVMOS")` is a two-element set, +it's easy: We compute the intersection of `Set("ATOM", "EVMOS")` +and `someCoinsExample`. If the pair involves a coin from +`someCoinsExample`, such an intersection should contain exactly +one coin. Let's check that: + + echo 'Set("ATOM", "EVMOS").intersect(someCoinsExample)' | quint -r sets.qnt::sets + echo 'Set("ATOM", "EVMOS").intersect(someCoinsExample)' | quint -r ./lesson4-sets/sets.qnt::sets + + +This works, so @KryptoCoffeeCat filter the whole set of `availableUnorderedPairs` +with the following condition: + + + echo 'availableUnorderedPairs.filter(p => p.intersect(someCoinsExample).size() == 1)' | quint -r sets.qnt::sets + echo 'availableUnorderedPairs.filter(p => p.intersect(someCoinsExample).size() == 1)' | quint -r ./lesson4-sets/sets.qnt::sets + + +However, there is a small issue: The result has the type of a set of two-element +sets (of strings), whereas they need a set of strings (coins). +@KryptoCoffeeCat already know how to solve this problem, by calling `flatten()`: + + + echo 'availableUnorderedPairs.filter(p => p.intersect(someCoinsExample).size() == 1).flatten()' | quint -r sets.qnt::sets + echo 'availableUnorderedPairs.filter(p => p.intersect(someCoinsExample).size() == 1).flatten()' | quint -r ./lesson4-sets/sets.qnt::sets + + +Finally, they do not need the coins that they started with, that is, `someCoinsExample`. +Hence, they add `exclude(someCoinsExample)` to the result. The complete +definition is written in `buyableVia1Swap`. + +@KryptoCoffeeCat want to see how this actually works. So they try: + + + echo 'buyableVia1Swap(Set("ATOM"))' | quint -r sets.qnt::sets + echo 'buyableVia1Swap(Set("ATOM"))' | quint -r ./lesson4-sets/sets.qnt::sets + + +This seems to work! + +Now @KryptoCoffeeCat want to see what is possible to buy by doing exactly two +swaps. This is also easy: Just call `buyableVia1Swap` twice. This works: + + + echo 'buyableVia2Swaps(Set("ATOM"))' | quint -r sets.qnt::sets + echo 'buyableVia2Swaps(Set("ATOM"))' | quint -r ./lesson4-sets/sets.qnt::sets + + +And how would they find out what could be bought by one or two swaps? +Also easy: Just take the set union of what is possible in one or two swaps: + + + echo 'buyableVia1or2Swaps(Set("ATOM"))' | quint -r sets.qnt::sets + echo 'buyableVia1or2Swaps(Set("ATOM"))' | quint -r ./lesson4-sets/sets.qnt::sets + + lesson4-sets/sets.qnt + 70 + + + Reachability via multiple swaps + +@KryptoCoffeeCat want to know how to find the coins that could be bought +after a given number of swaps, not just 1 or 2. This is also quite easy. +First, we have to understand how to iterate over numbers 1, 2, ..., n. +There are several ways to do that in Quint. Since we are talking about sets +in this tutorial, we will see how to do that using sets. + +Quint has the operator `i.to(j)` that constructs the set `Set(i, ..., j)`. +Let's try it: + + echo '3.to(10)' | quint + + +Now @KryptoCoffeeCat want to write something like this in pseudo JavaScript: + +```js +var prevCoins = someCoins +for (let i = 0; i < n; i++) { + prevCoins = buyableVia1Swap(prevCoins) +} +// use prevCoins +``` + +@KryptoCoffeeCat find a blog post on medium.com about +[Exploring folds](https://medium.com/@zaid.naom/exploring-folds-a-powerful-pattern-of-functional-programming-3036974205c8) +in JavaScript. It happens that Quint supports exactly this pattern, though it +has a slightly different syntax: + +```bluespec +1.to(n) + .fold(someCoins, + (prevCoins, i) => buyableVia1Swap(prevCoins)) +``` + +The above expression iterates over the set `1.to(n)` in *some order* +and passes the previously computed value as the first argument `prevCoins` +and a set element as the second argument `i`. It's important that the order +of iteration is not known in advance. However, it does not matter here, as we +are not even using `i`. @KryptoCoffeeCat could even have written `_` +instead of `i` in the above expression, which would indicate that the second +argument is not used: + +```bluespec +1.to(n) + .fold(someCoins, + (prevCoins, _) => buyableVia1Swap(prevCoins)) +``` + +Now @KryptoCoffeeCat can compute what they can buy via various numbers of swaps: + + + echo 'buyableNSwaps(Set("ATOM"), 3)' | quint -r sets.qnt::sets + echo 'buyableNSwaps(Set("ATOM"), 3)' | quint -r ./lesson4-sets/sets.qnt::sets + echo 'buyableNSwaps(Set("ATOM"), 4)' | quint -r sets.qnt::sets + echo 'buyableNSwaps(Set("ATOM"), 4)' | quint -r ./lesson4-sets/sets.qnt::sets + echo 'buyableNSwaps(Set("ATOM"), 5)' | quint -r sets.qnt::sets + echo 'buyableNSwaps(Set("ATOM"), 5)' | quint -r ./lesson4-sets/sets.qnt::sets + + lesson4-sets/sets.qnt + 75 + + + Power to the folds! + +When @KryptoCoffeeCat decided to learn a bit more about `fold`, they have found +that this operator is quite powerful. For instance, they could write their +own versions of `exists`, `forall`, `filter`, `map`, and `flatten`. +Does it mean that they should use `fold` everywhere now? Better not. +Although folds can express a lot of things, they should be used when really +required. After all, `exists`, `forall`, `filter`, `map`, `flatten` +are much easier to read. + + lesson4-sets/sets.qnt + 98 + + + Computing cycles with powersets + +OK, here is a real challenge for @KryptoCoffeeCat! Can they find four pairs +that could be swapped in some order, so the sequence of swaps starts with +one coin and ends with the same coin? Degens love swap cycles! + +To do that, they first write `availableUnorderedPairs.powerset()`, which +blasts `availableUnorderedPairs` into a huge set that contains all combinations +of pairs. These sets have all possible sizes. @KryptoCoffeeCat filter out the +sets that are not quadruples. How many quadruples are there? Let's see: + + echo 'quads.size()' | quint -r sets.qnt::sets + echo 'quads.size()' | quint -r ./lesson4-sets/sets.qnt::sets + + +Phew. It will take @KryptoCoffeeCat hours to analyze all these pairs by hand. +How do we know that some pairs form a cycle? Let's have a look at an example: + +| Coin A | Coin B | +| -------- | ------- | +| JUNO | ATOM | +| ATOM | OSMO | +| OSMO | EVMOS | +| EVMOS | JUNO | + +There is an interesting property here. Every coin appears exactly twice in +the above set. By browsing [Wikipedia](https://en.wikipedia.org/wiki/Main_Page), +@KryptoCoffeeCat found that a slightly more general observation was made by +Leonard Euler about 300 years ago, when he analyzed +[the bridges of Königsberg](https://en.wikipedia.org/wiki/Seven_Bridges_of_K%C3%B6nigsberg). +Not bad, @KryptoCoffeeCat! + +After figuring out the cycles, @KryptoCoffeeCat write the predicate `isCycle` +and finally the definition `cycles4`. Now they compute all these cycles in a +single click: + + + echo 'cycles4' | quint -r sets.qnt::sets + echo 'cycles4' | quint -r ./lesson4-sets/sets.qnt::sets + + lesson4-sets/sets.qnt + 118 + + + lesson4-sets/sets.qnt + 119 + Conclusions + + +@KryptoCoffeeCat has learned a lot in this tutorial. +By looking at the +[Quint cheatsheet](https://quint-lang.org/quint-cheatsheet.pdf), +they have found that this tutorial did not cover two operators on sets. +Can you find them too? + +If you are writing code in programming languages such as JavaScript, Rust, +Golang, Java, Python, and similar, sets may be not your everyday data +structure (such as arrays and lists), though there is a good chance that you +have used sets before. In Quint, it's the opposite. If there is a problem, +then sets are most likely there to solve it. If not, you can use maps or lists, +which will be explained in follow up tutorials. + + + + + diff --git a/tutorials/script/mk-tutorial.py b/docs/codetour/script/mk-tutorial.py similarity index 99% rename from tutorials/script/mk-tutorial.py rename to docs/codetour/script/mk-tutorial.py index 082379888..8b7bc10ca 100755 --- a/tutorials/script/mk-tutorial.py +++ b/docs/codetour/script/mk-tutorial.py @@ -135,7 +135,7 @@ def stepsStr(i): isEmpty = list(filter(lambda l: l.strip() != "", snippet)) == [] if not isEmpty: out.write('\n**Code snippet:**\n\n') - out.write('```bluespec\n') + out.write('```quint\n') for l in snippet: out.write(l) diff --git a/docs/components/home/code_sample.mdx b/docs/components/home/code_sample.mdx new file mode 100644 index 000000000..82c9a46cd --- /dev/null +++ b/docs/components/home/code_sample.mdx @@ -0,0 +1,20 @@ +```quint filename="bank.qnt" +/// A state variable to store the balance of each account +var balances: str -> int + +pure val ADDRESSES = Set("alice", "bob", "charlie") + +action withdraw(account, amount) = { + // Decrement balance of account by amount + // Whoops, we forgot to check for enough balance + balances' = balances.setBy(account, curr => curr - amount) +} + +// ... + +/// Invariant: Account balances should never be negative +val no_negatives = ADDRESSES.forall(addr => + balances.get(addr) >= 0 +) +``` + diff --git a/docs/components/home/index.tsx b/docs/components/home/index.tsx new file mode 100644 index 000000000..2cb2dfd2b --- /dev/null +++ b/docs/components/home/index.tsx @@ -0,0 +1,93 @@ +import { Code, Pre } from "nextra/components" + +import React from "react"; +import { useState } from 'react' +import { useTheme } from 'nextra-theme-docs' +import CodeSample from './code_sample.mdx' +import ViolationSample from './violation_sample.mdx' + +import Image from 'next/image' + +function informalSystemsLogo() { + const { resolvedTheme } = useTheme() + if (resolvedTheme == "dark") { + return ( + Informal Systems + ); + } else { + return ( + Informal Systems + ); + } +} + +const components = { + pre: Pre, + code: Code, +} + +export function Home() { + const [isViolationVisible, setViolationVisible] = useState(false); + + const handleToggleVisibility = () => { + setViolationVisible(true); + }; + + return (
+
+
+ +
+ +
+

Quint

+

A modern and executable specification language

+
+
+

Executable

+
    +
  • English and Markdown: not checked, not executable
  • +
  • Quint: checked names and types, executable
  • +
+
+ +

Specification language

+
    +
  • Programming languages: define how things happen in detail
  • +
  • Specification languages: define only what you care about
  • +
+
+ +

Modern

+
    +
  • Existing specification languages: Mathy syntax, old GUI tools
  • +
  • Quint: Familiar syntax, CLI-first, tools for VSCode, Vim and Emacs
  • +
+ + + Get started + + +
+ {informalSystemsLogo()} +
+
+ +
+ +
+ +
+
+ +
+
+
+
+
+
+ + ) +} diff --git a/docs/components/home/violation_sample.mdx b/docs/components/home/violation_sample.mdx new file mode 100644 index 000000000..257a4d923 --- /dev/null +++ b/docs/components/home/violation_sample.mdx @@ -0,0 +1,15 @@ +```sh +$ quint run bank.qnt --invariant=no_negatives +``` +```ansi +An example execution: + +[State 0] { balances: Map("alice" -> 0, "bob" -> 0, "charlie" -> 0) } + +[State 1] { balances: Map("alice" -> -63, "bob" -> 0, "charlie" -> 0) } + +[violation] Found an issue (44ms). +Use --seed=0x4e85b3a53f7ef to reproduce. +Use --verbosity=3 to show executions. +error: Invariant violated +``` diff --git a/tutorials/default.nix b/docs/default.nix similarity index 100% rename from tutorials/default.nix rename to docs/default.nix diff --git a/doc/generated/adr006before.qnt b/docs/generated/adr006before.qnt similarity index 100% rename from doc/generated/adr006before.qnt rename to docs/generated/adr006before.qnt diff --git a/docs/next-env.d.ts b/docs/next-env.d.ts new file mode 100644 index 000000000..4f11a03dc --- /dev/null +++ b/docs/next-env.d.ts @@ -0,0 +1,5 @@ +/// +/// + +// NOTE: This file should not be edited +// see https://nextjs.org/docs/basic-features/typescript for more information. diff --git a/docs/next.config.js b/docs/next.config.js new file mode 100644 index 000000000..1fde93df7 --- /dev/null +++ b/docs/next.config.js @@ -0,0 +1,29 @@ +const { getHighlighter, BUNDLED_LANGUAGES } = require('shiki') + +const withNextra = require('nextra')({ + theme: 'nextra-theme-docs', + themeConfig: './theme.config.jsx', + latex: true, + mdxOptions: { + rehypePrettyCodeOptions: { + getHighlighter: options => { + return getHighlighter({ + ...options, + langs: [ + ...BUNDLED_LANGUAGES, + // custom grammar options, see the Shiki documentation for how to provide these options + { + id: 'quint', + scopeName: 'source.quint', + aliases: ['qnt'], // Along with id, aliases will be included in the allowed names you can use when writing markdown. + path: '../../../vscode/quint-vscode/syntaxes/quint.tmLanguage.json' + } + ] + }) + } + } + } +}) + +module.exports = withNextra() +module.exports = withNextra({ output: 'export', images: { unoptimized: true } }) diff --git a/docs/package-lock.json b/docs/package-lock.json new file mode 100644 index 000000000..e0c3cd8de --- /dev/null +++ b/docs/package-lock.json @@ -0,0 +1,6621 @@ +{ + "name": "doc", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "dependencies": { + "next": "^14.2.4", + "nextra": "^2.13.4", + "nextra-theme-docs": "^2.13.4", + "react": "^18.3.1", + "react-dom": "^18.3.1", + "react-svg": "^16.1.34" + }, + "devDependencies": { + "@types/node": "20.14.5", + "autoprefixer": "^10.4.19", + "postcss": "^8.4.38", + "tailwindcss": "^3.4.4", + "typescript": "5.4.5" + } + }, + "node_modules/@alloc/quick-lru": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@alloc/quick-lru/-/quick-lru-5.2.0.tgz", + "integrity": "sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@babel/runtime": { + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.24.7.tgz", + "integrity": "sha512-UwgBRMjJP+xv857DCngvqXI3Iq6J4v0wXmwc6sapg+zyhbwmQX67LUEFrkK5tbyJ30jGuG3ZvWpBiB9LCy1kWw==", + "dependencies": { + "regenerator-runtime": "^0.14.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@braintree/sanitize-url": { + "version": "6.0.4", + "resolved": "https://registry.npmjs.org/@braintree/sanitize-url/-/sanitize-url-6.0.4.tgz", + "integrity": "sha512-s3jaWicZd0pkP0jf5ysyHUI/RE7MHos6qlToFcGWXVp+ykHOy77OUMrfbgJ9it2C5bow7OIQwYYaHjk9XlBQ2A==" + }, + "node_modules/@headlessui/react": { + "version": "1.7.19", + "resolved": "https://registry.npmjs.org/@headlessui/react/-/react-1.7.19.tgz", + "integrity": "sha512-Ll+8q3OlMJfJbAKM/+/Y2q6PPYbryqNTXDbryx7SXLIDamkF6iQFbriYHga0dY44PvDhvvBWCx1Xj4U5+G4hOw==", + "dependencies": { + "@tanstack/react-virtual": "^3.0.0-beta.60", + "client-only": "^0.0.1" + }, + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "react": "^16 || ^17 || ^18", + "react-dom": "^16 || ^17 || ^18" + } + }, + "node_modules/@isaacs/cliui": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", + "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==", + "dev": true, + "dependencies": { + "string-width": "^5.1.2", + "string-width-cjs": "npm:string-width@^4.2.0", + "strip-ansi": "^7.0.1", + "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", + "wrap-ansi": "^8.1.0", + "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@jridgewell/gen-mapping": { + "version": "0.3.5", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.5.tgz", + "integrity": "sha512-IzL8ZoEDIBRWEzlCcRhOaCupYyN5gdIK+Q6fbFdPDg6HqX6jpkItn7DFIpW9LQzXG6Df9sA7+OKnq0qlz/GaQg==", + "dev": true, + "dependencies": { + "@jridgewell/set-array": "^1.2.1", + "@jridgewell/sourcemap-codec": "^1.4.10", + "@jridgewell/trace-mapping": "^0.3.24" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", + "dev": true, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/set-array": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.2.1.tgz", + "integrity": "sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==", + "dev": true, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.4.15", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz", + "integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==", + "dev": true + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.25", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz", + "integrity": "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==", + "dev": true, + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, + "node_modules/@mdx-js/mdx": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/@mdx-js/mdx/-/mdx-2.3.0.tgz", + "integrity": "sha512-jLuwRlz8DQfQNiUCJR50Y09CGPq3fLtmtUQfVrj79E0JWu3dvsVcxVIcfhR5h0iXu+/z++zDrYeiJqifRynJkA==", + "dependencies": { + "@types/estree-jsx": "^1.0.0", + "@types/mdx": "^2.0.0", + "estree-util-build-jsx": "^2.0.0", + "estree-util-is-identifier-name": "^2.0.0", + "estree-util-to-js": "^1.1.0", + "estree-walker": "^3.0.0", + "hast-util-to-estree": "^2.0.0", + "markdown-extensions": "^1.0.0", + "periscopic": "^3.0.0", + "remark-mdx": "^2.0.0", + "remark-parse": "^10.0.0", + "remark-rehype": "^10.0.0", + "unified": "^10.0.0", + "unist-util-position-from-estree": "^1.0.0", + "unist-util-stringify-position": "^3.0.0", + "unist-util-visit": "^4.0.0", + "vfile": "^5.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/@mdx-js/mdx/node_modules/unist-util-visit": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/unist-util-visit/-/unist-util-visit-4.1.2.tgz", + "integrity": "sha512-MSd8OUGISqHdVvfY9TPhyK2VdUrPgxkUtWSuMHF6XAAFuL4LokseigBnZtPnJMu+FbynTkFNnFlyjxpVKujMRg==", + "dependencies": { + "@types/unist": "^2.0.0", + "unist-util-is": "^5.0.0", + "unist-util-visit-parents": "^5.1.1" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/@mdx-js/mdx/node_modules/unist-util-visit-parents": { + "version": "5.1.3", + "resolved": "https://registry.npmjs.org/unist-util-visit-parents/-/unist-util-visit-parents-5.1.3.tgz", + "integrity": "sha512-x6+y8g7wWMyQhL1iZfhIPhDAs7Xwbn9nRosDXl7qoPTSCy0yNxnKc+hWokFifWQIDGi154rdUqKvbCa4+1kLhg==", + "dependencies": { + "@types/unist": "^2.0.0", + "unist-util-is": "^5.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/@mdx-js/react": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/@mdx-js/react/-/react-2.3.0.tgz", + "integrity": "sha512-zQH//gdOmuu7nt2oJR29vFhDv88oGPmVw6BggmrHeMI+xgEkp1B2dX9/bMBSYtK0dyLX/aOmesKS09g222K1/g==", + "dependencies": { + "@types/mdx": "^2.0.0", + "@types/react": ">=16" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + }, + "peerDependencies": { + "react": ">=16" + } + }, + "node_modules/@napi-rs/simple-git": { + "version": "0.1.16", + "resolved": "https://registry.npmjs.org/@napi-rs/simple-git/-/simple-git-0.1.16.tgz", + "integrity": "sha512-C5wRPw9waqL2jk3jEDeJv+f7ScuO3N0a39HVdyFLkwKxHH4Sya4ZbzZsu2JLi6eEqe7RuHipHL6mC7B2OfYZZw==", + "engines": { + "node": ">= 10" + }, + "optionalDependencies": { + "@napi-rs/simple-git-android-arm-eabi": "0.1.16", + "@napi-rs/simple-git-android-arm64": "0.1.16", + "@napi-rs/simple-git-darwin-arm64": "0.1.16", + "@napi-rs/simple-git-darwin-x64": "0.1.16", + "@napi-rs/simple-git-linux-arm-gnueabihf": "0.1.16", + "@napi-rs/simple-git-linux-arm64-gnu": "0.1.16", + "@napi-rs/simple-git-linux-arm64-musl": "0.1.16", + "@napi-rs/simple-git-linux-x64-gnu": "0.1.16", + "@napi-rs/simple-git-linux-x64-musl": "0.1.16", + "@napi-rs/simple-git-win32-arm64-msvc": "0.1.16", + "@napi-rs/simple-git-win32-x64-msvc": "0.1.16" + } + }, + "node_modules/@napi-rs/simple-git-android-arm-eabi": { + "version": "0.1.16", + "resolved": "https://registry.npmjs.org/@napi-rs/simple-git-android-arm-eabi/-/simple-git-android-arm-eabi-0.1.16.tgz", + "integrity": "sha512-dbrCL0Pl5KZG7x7tXdtVsA5CO6At5ohDX3myf5xIYn9kN4jDFxsocl8bNt6Vb/hZQoJd8fI+k5VlJt+rFhbdVw==", + "cpu": [ + "arm" + ], + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@napi-rs/simple-git-android-arm64": { + "version": "0.1.16", + "resolved": "https://registry.npmjs.org/@napi-rs/simple-git-android-arm64/-/simple-git-android-arm64-0.1.16.tgz", + "integrity": "sha512-xYz+TW5J09iK8SuTAKK2D5MMIsBUXVSs8nYp7HcMi8q6FCRO7yJj96YfP9PvKsc/k64hOyqGmL5DhCzY9Cu1FQ==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@napi-rs/simple-git-darwin-arm64": { + "version": "0.1.16", + "resolved": "https://registry.npmjs.org/@napi-rs/simple-git-darwin-arm64/-/simple-git-darwin-arm64-0.1.16.tgz", + "integrity": "sha512-XfgsYqxhUE022MJobeiX563TJqyQyX4FmYCnqrtJwAfivESVeAJiH6bQIum8dDEYMHXCsG7nL8Ok0Dp8k2m42g==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@napi-rs/simple-git-darwin-x64": { + "version": "0.1.16", + "resolved": "https://registry.npmjs.org/@napi-rs/simple-git-darwin-x64/-/simple-git-darwin-x64-0.1.16.tgz", + "integrity": "sha512-tkEVBhD6vgRCbeWsaAQqM3bTfpIVGeitamPPRVSbsq8qgzJ5Dx6ZedH27R7KSsA/uao7mZ3dsrNLXbu1Wy5MzA==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@napi-rs/simple-git-linux-arm-gnueabihf": { + "version": "0.1.16", + "resolved": "https://registry.npmjs.org/@napi-rs/simple-git-linux-arm-gnueabihf/-/simple-git-linux-arm-gnueabihf-0.1.16.tgz", + "integrity": "sha512-R6VAyNnp/yRaT7DV1Ao3r67SqTWDa+fNq2LrNy0Z8gXk2wB9ZKlrxFtLPE1WSpWknWtyRDLpRlsorh7Evk7+7w==", + "cpu": [ + "arm" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@napi-rs/simple-git-linux-arm64-gnu": { + "version": "0.1.16", + "resolved": "https://registry.npmjs.org/@napi-rs/simple-git-linux-arm64-gnu/-/simple-git-linux-arm64-gnu-0.1.16.tgz", + "integrity": "sha512-LAGI0opFKw/HBMCV2qIBK3uWSEW9h4xd2ireZKLJy8DBPymX6NrWIamuxYNyCuACnFdPRxR4LaRFy4J5ZwuMdw==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@napi-rs/simple-git-linux-arm64-musl": { + "version": "0.1.16", + "resolved": "https://registry.npmjs.org/@napi-rs/simple-git-linux-arm64-musl/-/simple-git-linux-arm64-musl-0.1.16.tgz", + "integrity": "sha512-I57Ph0F0Yn2KW93ep+V1EzKhACqX0x49vvSiapqIsdDA2PifdEWLc1LJarBolmK7NKoPqKmf6lAKKO9lhiZzkg==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@napi-rs/simple-git-linux-x64-gnu": { + "version": "0.1.16", + "resolved": "https://registry.npmjs.org/@napi-rs/simple-git-linux-x64-gnu/-/simple-git-linux-x64-gnu-0.1.16.tgz", + "integrity": "sha512-AZYYFY2V7hlcQASPEOWyOa3e1skzTct9QPzz0LiDM3f/hCFY/wBaU2M6NC5iG3d2Kr38heuyFS/+JqxLm5WaKA==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@napi-rs/simple-git-linux-x64-musl": { + "version": "0.1.16", + "resolved": "https://registry.npmjs.org/@napi-rs/simple-git-linux-x64-musl/-/simple-git-linux-x64-musl-0.1.16.tgz", + "integrity": "sha512-9TyMcYSBJwjT8jwjY9m24BZbu7ozyWTjsmYBYNtK3B0Um1Ov6jthSNneLVvouQ6x+k3Ow+00TiFh6bvmT00r8g==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@napi-rs/simple-git-win32-arm64-msvc": { + "version": "0.1.16", + "resolved": "https://registry.npmjs.org/@napi-rs/simple-git-win32-arm64-msvc/-/simple-git-win32-arm64-msvc-0.1.16.tgz", + "integrity": "sha512-uslJ1WuAHCYJWui6xjsyT47SjX6KOHDtClmNO8hqKz1pmDSNY7AjyUY8HxvD1lK9bDnWwc4JYhikS9cxCqHybw==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@napi-rs/simple-git-win32-x64-msvc": { + "version": "0.1.16", + "resolved": "https://registry.npmjs.org/@napi-rs/simple-git-win32-x64-msvc/-/simple-git-win32-x64-msvc-0.1.16.tgz", + "integrity": "sha512-SoEaVeCZCDF1MP+M9bMSXsZWgEjk4On9GWADO5JOulvzR1bKjk0s9PMHwe/YztR9F0sJzrCxwtvBZowhSJsQPg==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/env": { + "version": "14.2.4", + "resolved": "https://registry.npmjs.org/@next/env/-/env-14.2.4.tgz", + "integrity": "sha512-3EtkY5VDkuV2+lNmKlbkibIJxcO4oIHEhBWne6PaAp+76J9KoSsGvNikp6ivzAT8dhhBMYrm6op2pS1ApG0Hzg==" + }, + "node_modules/@next/swc-darwin-arm64": { + "version": "14.2.4", + "resolved": "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-14.2.4.tgz", + "integrity": "sha512-AH3mO4JlFUqsYcwFUHb1wAKlebHU/Hv2u2kb1pAuRanDZ7pD/A/KPD98RHZmwsJpdHQwfEc/06mgpSzwrJYnNg==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-darwin-x64": { + "version": "14.2.4", + "resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-14.2.4.tgz", + "integrity": "sha512-QVadW73sWIO6E2VroyUjuAxhWLZWEpiFqHdZdoQ/AMpN9YWGuHV8t2rChr0ahy+irKX5mlDU7OY68k3n4tAZTg==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-linux-arm64-gnu": { + "version": "14.2.4", + "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-14.2.4.tgz", + "integrity": "sha512-KT6GUrb3oyCfcfJ+WliXuJnD6pCpZiosx2X3k66HLR+DMoilRb76LpWPGb4tZprawTtcnyrv75ElD6VncVamUQ==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-linux-arm64-musl": { + "version": "14.2.4", + "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-14.2.4.tgz", + "integrity": "sha512-Alv8/XGSs/ytwQcbCHwze1HmiIkIVhDHYLjczSVrf0Wi2MvKn/blt7+S6FJitj3yTlMwMxII1gIJ9WepI4aZ/A==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-linux-x64-gnu": { + "version": "14.2.4", + "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-14.2.4.tgz", + "integrity": "sha512-ze0ShQDBPCqxLImzw4sCdfnB3lRmN3qGMB2GWDRlq5Wqy4G36pxtNOo2usu/Nm9+V2Rh/QQnrRc2l94kYFXO6Q==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-linux-x64-musl": { + "version": "14.2.4", + "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-14.2.4.tgz", + "integrity": "sha512-8dwC0UJoc6fC7PX70csdaznVMNr16hQrTDAMPvLPloazlcaWfdPogq+UpZX6Drqb1OBlwowz8iG7WR0Tzk/diQ==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-win32-arm64-msvc": { + "version": "14.2.4", + "resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-14.2.4.tgz", + "integrity": "sha512-jxyg67NbEWkDyvM+O8UDbPAyYRZqGLQDTPwvrBBeOSyVWW/jFQkQKQ70JDqDSYg1ZDdl+E3nkbFbq8xM8E9x8A==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-win32-ia32-msvc": { + "version": "14.2.4", + "resolved": "https://registry.npmjs.org/@next/swc-win32-ia32-msvc/-/swc-win32-ia32-msvc-14.2.4.tgz", + "integrity": "sha512-twrmN753hjXRdcrZmZttb/m5xaCBFa48Dt3FbeEItpJArxriYDunWxJn+QFXdJ3hPkm4u7CKxncVvnmgQMY1ag==", + "cpu": [ + "ia32" + ], + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-win32-x64-msvc": { + "version": "14.2.4", + "resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-14.2.4.tgz", + "integrity": "sha512-tkLrjBzqFTP8DVrAAQmZelEahfR9OxWpFR++vAI9FBhCiIxtwHwBHC23SBHCTURBtwB4kc/x44imVOnkKGNVGg==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@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, + "dependencies": { + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@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, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@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, + "dependencies": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@pkgjs/parseargs": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", + "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==", + "dev": true, + "optional": true, + "engines": { + "node": ">=14" + } + }, + "node_modules/@popperjs/core": { + "version": "2.11.8", + "resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.11.8.tgz", + "integrity": "sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A==", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/popperjs" + } + }, + "node_modules/@swc/counter": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/@swc/counter/-/counter-0.1.3.tgz", + "integrity": "sha512-e2BR4lsJkkRlKZ/qCHPw9ZaSxc0MVUd7gtbtaB7aMvHeJVYe8sOB8DBZkP2DtISHGSku9sCK6T6cnY0CtXrOCQ==" + }, + "node_modules/@swc/helpers": { + "version": "0.5.5", + "resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.5.tgz", + "integrity": "sha512-KGYxvIOXcceOAbEk4bi/dVLEK9z8sZ0uBB3Il5b1rhfClSpcX0yfRO0KmTkqR2cnQDymwLB+25ZyMzICg/cm/A==", + "dependencies": { + "@swc/counter": "^0.1.3", + "tslib": "^2.4.0" + } + }, + "node_modules/@tanem/svg-injector": { + "version": "10.1.68", + "resolved": "https://registry.npmjs.org/@tanem/svg-injector/-/svg-injector-10.1.68.tgz", + "integrity": "sha512-UkJajeR44u73ujtr5GVSbIlELDWD/mzjqWe54YMK61ljKxFcJoPd9RBSaO7xj02ISCWUqJW99GjrS+sVF0UnrA==", + "dependencies": { + "@babel/runtime": "^7.23.2", + "content-type": "^1.0.5", + "tslib": "^2.6.2" + } + }, + "node_modules/@tanstack/react-virtual": { + "version": "3.5.1", + "resolved": "https://registry.npmjs.org/@tanstack/react-virtual/-/react-virtual-3.5.1.tgz", + "integrity": "sha512-jIsuhfgy8GqA67PdWqg73ZB2LFE+HD9hjWL1L6ifEIZVyZVAKpYmgUG4WsKQ005aEyImJmbuimPiEvc57IY0Aw==", + "dependencies": { + "@tanstack/virtual-core": "3.5.1" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/tannerlinsley" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0", + "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0" + } + }, + "node_modules/@tanstack/virtual-core": { + "version": "3.5.1", + "resolved": "https://registry.npmjs.org/@tanstack/virtual-core/-/virtual-core-3.5.1.tgz", + "integrity": "sha512-046+AUSiDru/V9pajE1du8WayvBKeCvJ2NmKPy/mR8/SbKKrqmSbj7LJBfXE+nSq4f5TBXvnCzu0kcYebI9WdQ==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/tannerlinsley" + } + }, + "node_modules/@theguild/remark-mermaid": { + "version": "0.0.5", + "resolved": "https://registry.npmjs.org/@theguild/remark-mermaid/-/remark-mermaid-0.0.5.tgz", + "integrity": "sha512-e+ZIyJkEv9jabI4m7q29wZtZv+2iwPGsXJ2d46Zi7e+QcFudiyuqhLhHG/3gX3ZEB+hxTch+fpItyMS8jwbIcw==", + "dependencies": { + "mermaid": "^10.2.2", + "unist-util-visit": "^5.0.0" + }, + "peerDependencies": { + "react": "^18.2.0" + } + }, + "node_modules/@theguild/remark-npm2yarn": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/@theguild/remark-npm2yarn/-/remark-npm2yarn-0.2.1.tgz", + "integrity": "sha512-jUTFWwDxtLEFtGZh/TW/w30ySaDJ8atKWH8dq2/IiQF61dPrGfETpl0WxD0VdBfuLOeU14/kop466oBSRO/5CA==", + "dependencies": { + "npm-to-yarn": "^2.1.0", + "unist-util-visit": "^5.0.0" + } + }, + "node_modules/@types/acorn": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/@types/acorn/-/acorn-4.0.6.tgz", + "integrity": "sha512-veQTnWP+1D/xbxVrPC3zHnCZRjSrKfhbMUlEA43iMZLu7EsnTtkJklIuwrCPbOi8YkvDQAiW05VQQFvvz9oieQ==", + "dependencies": { + "@types/estree": "*" + } + }, + "node_modules/@types/d3-scale": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/@types/d3-scale/-/d3-scale-4.0.8.tgz", + "integrity": "sha512-gkK1VVTr5iNiYJ7vWDI+yUFFlszhNMtVeneJ6lUTKPjprsvLLI9/tgEGiXJOnlINJA8FyA88gfnQsHbybVZrYQ==", + "dependencies": { + "@types/d3-time": "*" + } + }, + "node_modules/@types/d3-scale-chromatic": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@types/d3-scale-chromatic/-/d3-scale-chromatic-3.0.3.tgz", + "integrity": "sha512-laXM4+1o5ImZv3RpFAsTRn3TEkzqkytiOY0Dz0sq5cnd1dtNlk6sHLon4OvqaiJb28T0S/TdsBI3Sjsy+keJrw==" + }, + "node_modules/@types/d3-time": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@types/d3-time/-/d3-time-3.0.3.tgz", + "integrity": "sha512-2p6olUZ4w3s+07q3Tm2dbiMZy5pCDfYwtLXXHUnVzXgQlZ/OyPtUz6OL382BkOuGlLXqfT+wqv8Fw2v8/0geBw==" + }, + "node_modules/@types/debug": { + "version": "4.1.12", + "resolved": "https://registry.npmjs.org/@types/debug/-/debug-4.1.12.tgz", + "integrity": "sha512-vIChWdVG3LG1SMxEvI/AK+FWJthlrqlTu7fbrlywTkkaONwk/UAGaULXRlf8vkzFBLVm0zkMdCquhL5aOjhXPQ==", + "dependencies": { + "@types/ms": "*" + } + }, + "node_modules/@types/estree": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.5.tgz", + "integrity": "sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==" + }, + "node_modules/@types/estree-jsx": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/@types/estree-jsx/-/estree-jsx-1.0.5.tgz", + "integrity": "sha512-52CcUVNFyfb1A2ALocQw/Dd1BQFNmSdkuC3BkZ6iqhdMfQz7JWOFRuJFloOzjk+6WijU56m9oKXFAXc7o3Towg==", + "dependencies": { + "@types/estree": "*" + } + }, + "node_modules/@types/hast": { + "version": "2.3.10", + "resolved": "https://registry.npmjs.org/@types/hast/-/hast-2.3.10.tgz", + "integrity": "sha512-McWspRw8xx8J9HurkVBfYj0xKoE25tOFlHGdx4MJ5xORQrMGZNqJhVQWaIbm6Oyla5kYOXtDiopzKRJzEOkwJw==", + "dependencies": { + "@types/unist": "^2" + } + }, + "node_modules/@types/js-yaml": { + "version": "4.0.9", + "resolved": "https://registry.npmjs.org/@types/js-yaml/-/js-yaml-4.0.9.tgz", + "integrity": "sha512-k4MGaQl5TGo/iipqb2UDG2UwjXziSWkh0uysQelTlJpX1qGlpUZYm8PnO4DxG1qBomtJUdYJ6qR6xdIah10JLg==" + }, + "node_modules/@types/katex": { + "version": "0.16.7", + "resolved": "https://registry.npmjs.org/@types/katex/-/katex-0.16.7.tgz", + "integrity": "sha512-HMwFiRujE5PjrgwHQ25+bsLJgowjGjm5Z8FVSf0N6PwgJrwxH0QxzHYDcKsTfV3wva0vzrpqMTJS2jXPr5BMEQ==" + }, + "node_modules/@types/mdast": { + "version": "3.0.15", + "resolved": "https://registry.npmjs.org/@types/mdast/-/mdast-3.0.15.tgz", + "integrity": "sha512-LnwD+mUEfxWMa1QpDraczIn6k0Ee3SMicuYSSzS6ZYl2gKS09EClnJYGd8Du6rfc5r/GZEk5o1mRb8TaTj03sQ==", + "dependencies": { + "@types/unist": "^2" + } + }, + "node_modules/@types/mdx": { + "version": "2.0.13", + "resolved": "https://registry.npmjs.org/@types/mdx/-/mdx-2.0.13.tgz", + "integrity": "sha512-+OWZQfAYyio6YkJb3HLxDrvnx6SWWDbC0zVPfBRzUk0/nqoDyf6dNxQi3eArPe8rJ473nobTMQ/8Zk+LxJ+Yuw==" + }, + "node_modules/@types/ms": { + "version": "0.7.34", + "resolved": "https://registry.npmjs.org/@types/ms/-/ms-0.7.34.tgz", + "integrity": "sha512-nG96G3Wp6acyAgJqGasjODb+acrI7KltPiRxzHPXnP3NgI28bpQDRv53olbqGXbfcgF5aiiHmO3xpwEpS5Ld9g==" + }, + "node_modules/@types/node": { + "version": "20.14.5", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.14.5.tgz", + "integrity": "sha512-aoRR+fJkZT2l0aGOJhuA8frnCSoNX6W7U2mpNq63+BxBIj5BQFt8rHy627kijCmm63ijdSdwvGgpUsU6MBsZZA==", + "dev": true, + "dependencies": { + "undici-types": "~5.26.4" + } + }, + "node_modules/@types/prop-types": { + "version": "15.7.12", + "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.12.tgz", + "integrity": "sha512-5zvhXYtRNRluoE/jAp4GVsSduVUzNWKkOZrCDBWYtE7biZywwdC2AcEzg+cSMLFRfVgeAFqpfNabiPjxFddV1Q==" + }, + "node_modules/@types/react": { + "version": "18.3.3", + "resolved": "https://registry.npmjs.org/@types/react/-/react-18.3.3.tgz", + "integrity": "sha512-hti/R0pS0q1/xx+TsI73XIqk26eBsISZ2R0wUijXIngRK9R/e7Xw/cXVxQK7R5JjW+SV4zGcn5hXjudkN/pLIw==", + "dependencies": { + "@types/prop-types": "*", + "csstype": "^3.0.2" + } + }, + "node_modules/@types/unist": { + "version": "2.0.10", + "resolved": "https://registry.npmjs.org/@types/unist/-/unist-2.0.10.tgz", + "integrity": "sha512-IfYcSBWE3hLpBg8+X2SEa8LVkJdJEkT2Ese2aaLs3ptGdVtABxndrMaxuFlQ1qdFf9Q5rDvDpxI3WwgvKFAsQA==" + }, + "node_modules/@ungap/structured-clone": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.2.0.tgz", + "integrity": "sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==" + }, + "node_modules/acorn": { + "version": "8.12.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.12.0.tgz", + "integrity": "sha512-RTvkC4w+KNXrM39/lWCUaG0IbRkWdCv7W/IOW9oU6SawyxulvkQy5HQPVTKxEjczcUvapcrw3cFx/60VN/NRNw==", + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-jsx": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", + "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", + "peerDependencies": { + "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, + "node_modules/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, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/ansi-sequence-parser": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ansi-sequence-parser/-/ansi-sequence-parser-1.1.1.tgz", + "integrity": "sha512-vJXt3yiaUL4UU546s3rPXlsry/RnM730G1+HkpKE012AN0sx1eOrxSu95oKDIonskeLTijMgqWZ3uDEe3NFvyg==" + }, + "node_modules/ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dependencies": { + "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/any-promise": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/any-promise/-/any-promise-1.3.0.tgz", + "integrity": "sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==", + "dev": true + }, + "node_modules/anymatch": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", + "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", + "dev": true, + "dependencies": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/arch": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/arch/-/arch-2.2.0.tgz", + "integrity": "sha512-Of/R0wqp83cgHozfIYLbBMnej79U/SVGOOyuB3VVFv1NRM/PSFMK12x9KVtiYzJqmnU5WR2qp0Z5rHb7sWGnFQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/arg": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/arg/-/arg-1.0.0.tgz", + "integrity": "sha512-Wk7TEzl1KqvTGs/uyhmHO/3XLd3t1UeU4IstvPXVzGPM522cTjqjNZ99esCkcL52sjqjo8e8CTBcWhkxvGzoAw==" + }, + "node_modules/argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "dependencies": { + "sprintf-js": "~1.0.2" + } + }, + "node_modules/astring": { + "version": "1.8.6", + "resolved": "https://registry.npmjs.org/astring/-/astring-1.8.6.tgz", + "integrity": "sha512-ISvCdHdlTDlH5IpxQJIex7BWBywFWgjJSVdwst+/iQCoEYnyOaQ95+X1JGshuBjGp6nxKUy1jMgE3zPqN7fQdg==", + "bin": { + "astring": "bin/astring" + } + }, + "node_modules/autoprefixer": { + "version": "10.4.19", + "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.19.tgz", + "integrity": "sha512-BaENR2+zBZ8xXhM4pUaKUxlVdxZ0EZhjvbopwnXmxRUfqDmwSpC2lAi/QXvx7NRdPCo1WKEcEF6mV64si1z4Ew==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/autoprefixer" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "dependencies": { + "browserslist": "^4.23.0", + "caniuse-lite": "^1.0.30001599", + "fraction.js": "^4.3.7", + "normalize-range": "^0.1.2", + "picocolors": "^1.0.0", + "postcss-value-parser": "^4.2.0" + }, + "bin": { + "autoprefixer": "bin/autoprefixer" + }, + "engines": { + "node": "^10 || ^12 || >=14" + }, + "peerDependencies": { + "postcss": "^8.1.0" + } + }, + "node_modules/bail": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/bail/-/bail-2.0.2.tgz", + "integrity": "sha512-0xO6mYd7JB2YesxDKplafRpsiOzPt9V02ddPCLbY1xYGPOX24NTyN50qnUxgCPcSoYMhKpAuBTjQoRZCAkUDRw==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/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 + }, + "node_modules/binary-extensions": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz", + "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==", + "dev": true, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/braces": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", + "dev": true, + "dependencies": { + "fill-range": "^7.1.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/browserslist": { + "version": "4.23.1", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.23.1.tgz", + "integrity": "sha512-TUfofFo/KsK/bWZ9TWQ5O26tsWW4Uhmt8IYklbnUa70udB6P2wA7w7o4PY4muaEPBQaAX+CEnmmIA41NVHtPVw==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "dependencies": { + "caniuse-lite": "^1.0.30001629", + "electron-to-chromium": "^1.4.796", + "node-releases": "^2.0.14", + "update-browserslist-db": "^1.0.16" + }, + "bin": { + "browserslist": "cli.js" + }, + "engines": { + "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" + } + }, + "node_modules/busboy": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/busboy/-/busboy-1.6.0.tgz", + "integrity": "sha512-8SFQbg/0hQ9xy3UNTB0YEnsNBbWfhf7RtnzpL7TkBiTBRfrQ9Fxcnz7VJsleJpyp6rVLvXiuORqjlHi5q+PYuA==", + "dependencies": { + "streamsearch": "^1.1.0" + }, + "engines": { + "node": ">=10.16.0" + } + }, + "node_modules/camelcase-css": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/camelcase-css/-/camelcase-css-2.0.1.tgz", + "integrity": "sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA==", + "dev": true, + "engines": { + "node": ">= 6" + } + }, + "node_modules/caniuse-lite": { + "version": "1.0.30001634", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001634.tgz", + "integrity": "sha512-fbBYXQ9q3+yp1q1gBk86tOFs4pyn/yxFm5ZNP18OXJDfA3txImOY9PhfxVggZ4vRHDqoU8NrKU81eN0OtzOgRA==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/caniuse-lite" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ] + }, + "node_modules/ccount": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/ccount/-/ccount-2.0.1.tgz", + "integrity": "sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/chalk": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.3.0.tgz", + "integrity": "sha512-Az5zJR2CBujap2rqXGaJKaPHyJ0IrUimvYNX+ncCy8PJP4ltOGTrHUIo097ZaL2zMeKYpiCdqDvS6zdrTFok3Q==", + "dependencies": { + "ansi-styles": "^3.1.0", + "escape-string-regexp": "^1.0.5", + "supports-color": "^4.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/chalk/node_modules/escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/character-entities": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/character-entities/-/character-entities-2.0.2.tgz", + "integrity": "sha512-shx7oQ0Awen/BRIdkjkvz54PnEEI/EjwXDSIZp86/KKdbafHh1Df/RYGBhn4hbe2+uKC9FnT5UCEdyPz3ai9hQ==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/character-entities-html4": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/character-entities-html4/-/character-entities-html4-2.1.0.tgz", + "integrity": "sha512-1v7fgQRj6hnSwFpq1Eu0ynr/CDEw0rXo2B61qXrLNdHZmPKgb7fqS1a2JwF0rISo9q77jDI8VMEHoApn8qDoZA==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/character-entities-legacy": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/character-entities-legacy/-/character-entities-legacy-3.0.0.tgz", + "integrity": "sha512-RpPp0asT/6ufRm//AJVwpViZbGM/MkjQFxJccQRHmISF/22NBtsHqAWmL+/pmkPWoIUJdWyeVleTl1wydHATVQ==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/character-reference-invalid": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/character-reference-invalid/-/character-reference-invalid-2.0.1.tgz", + "integrity": "sha512-iBZ4F4wRbyORVsu0jPV7gXkOsGYjGHPmAyv+HiHG8gi5PtC9KI2j1+v8/tlibRvjoWX027ypmG/n0HtO5t7unw==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/chokidar": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", + "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", + "dev": true, + "dependencies": { + "anymatch": "~3.1.2", + "braces": "~3.0.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" + }, + "engines": { + "node": ">= 8.10.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" + } + }, + "node_modules/chokidar/node_modules/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, + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/client-only": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/client-only/-/client-only-0.0.1.tgz", + "integrity": "sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA==" + }, + "node_modules/clipboardy": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/clipboardy/-/clipboardy-1.2.2.tgz", + "integrity": "sha512-16KrBOV7bHmHdxcQiCvfUFYVFyEah4FI8vYT1Fr7CGSA4G+xBWMEfUEQJS1hxeHGtI9ju1Bzs9uXSbj5HZKArw==", + "dependencies": { + "arch": "^2.1.0", + "execa": "^0.8.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/clsx": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz", + "integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==", + "engines": { + "node": ">=6" + } + }, + "node_modules/color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dependencies": { + "color-name": "1.1.3" + } + }, + "node_modules/color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==" + }, + "node_modules/comma-separated-tokens": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/comma-separated-tokens/-/comma-separated-tokens-2.0.3.tgz", + "integrity": "sha512-Fu4hJdvzeylCfQPp9SGWidpzrMs7tTrlu6Vb8XGaRGck8QSNZJJp538Wrb60Lax4fPwR64ViY468OIUTbRlGZg==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/commander": { + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-8.3.0.tgz", + "integrity": "sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww==", + "engines": { + "node": ">= 12" + } + }, + "node_modules/compute-scroll-into-view": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/compute-scroll-into-view/-/compute-scroll-into-view-3.1.0.tgz", + "integrity": "sha512-rj8l8pD4bJ1nx+dAkMhV1xB5RuZEyVysfxJqB1pRchh1KVvwOv9b7CGB8ZfjTImVv2oF+sYMUkMZq6Na5Ftmbg==" + }, + "node_modules/content-type": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz", + "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/cose-base": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/cose-base/-/cose-base-1.0.3.tgz", + "integrity": "sha512-s9whTXInMSgAp/NVXVNuVxVKzGH2qck3aQlVHxDCdAEPgtMKwc4Wq6/QKhgdEdgbLSi9rBTAcPoRa6JpiG4ksg==", + "dependencies": { + "layout-base": "^1.0.0" + } + }, + "node_modules/cross-spawn": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-5.1.0.tgz", + "integrity": "sha512-pTgQJ5KC0d2hcY8eyL1IzlBPYjTkyH72XRZPnLyKus2mBfNjQs3klqbJU2VILqZryAZUt9JOb3h/mWMy23/f5A==", + "dependencies": { + "lru-cache": "^4.0.1", + "shebang-command": "^1.2.0", + "which": "^1.2.9" + } + }, + "node_modules/cssesc": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz", + "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==", + "dev": true, + "bin": { + "cssesc": "bin/cssesc" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/csstype": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz", + "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==" + }, + "node_modules/cytoscape": { + "version": "3.29.2", + "resolved": "https://registry.npmjs.org/cytoscape/-/cytoscape-3.29.2.tgz", + "integrity": "sha512-2G1ycU28Nh7OHT9rkXRLpCDP30MKH1dXJORZuBhtEhEW7pKwgPi77ImqlCWinouyE1PNepIOGZBOrE84DG7LyQ==", + "engines": { + "node": ">=0.10" + } + }, + "node_modules/cytoscape-cose-bilkent": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/cytoscape-cose-bilkent/-/cytoscape-cose-bilkent-4.1.0.tgz", + "integrity": "sha512-wgQlVIUJF13Quxiv5e1gstZ08rnZj2XaLHGoFMYXz7SkNfCDOOteKBE6SYRfA9WxxI/iBc3ajfDoc6hb/MRAHQ==", + "dependencies": { + "cose-base": "^1.0.0" + }, + "peerDependencies": { + "cytoscape": "^3.2.0" + } + }, + "node_modules/d3": { + "version": "7.9.0", + "resolved": "https://registry.npmjs.org/d3/-/d3-7.9.0.tgz", + "integrity": "sha512-e1U46jVP+w7Iut8Jt8ri1YsPOvFpg46k+K8TpCb0P+zjCkjkPnV7WzfDJzMHy1LnA+wj5pLT1wjO901gLXeEhA==", + "dependencies": { + "d3-array": "3", + "d3-axis": "3", + "d3-brush": "3", + "d3-chord": "3", + "d3-color": "3", + "d3-contour": "4", + "d3-delaunay": "6", + "d3-dispatch": "3", + "d3-drag": "3", + "d3-dsv": "3", + "d3-ease": "3", + "d3-fetch": "3", + "d3-force": "3", + "d3-format": "3", + "d3-geo": "3", + "d3-hierarchy": "3", + "d3-interpolate": "3", + "d3-path": "3", + "d3-polygon": "3", + "d3-quadtree": "3", + "d3-random": "3", + "d3-scale": "4", + "d3-scale-chromatic": "3", + "d3-selection": "3", + "d3-shape": "3", + "d3-time": "3", + "d3-time-format": "4", + "d3-timer": "3", + "d3-transition": "3", + "d3-zoom": "3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-array": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/d3-array/-/d3-array-3.2.4.tgz", + "integrity": "sha512-tdQAmyA18i4J7wprpYq8ClcxZy3SC31QMeByyCFyRt7BVHdREQZ5lpzoe5mFEYZUWe+oq8HBvk9JjpibyEV4Jg==", + "dependencies": { + "internmap": "1 - 2" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-axis": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/d3-axis/-/d3-axis-3.0.0.tgz", + "integrity": "sha512-IH5tgjV4jE/GhHkRV0HiVYPDtvfjHQlQfJHs0usq7M30XcSBvOotpmH1IgkcXsO/5gEQZD43B//fc7SRT5S+xw==", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-brush": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/d3-brush/-/d3-brush-3.0.0.tgz", + "integrity": "sha512-ALnjWlVYkXsVIGlOsuWH1+3udkYFI48Ljihfnh8FZPF2QS9o+PzGLBslO0PjzVoHLZ2KCVgAM8NVkXPJB2aNnQ==", + "dependencies": { + "d3-dispatch": "1 - 3", + "d3-drag": "2 - 3", + "d3-interpolate": "1 - 3", + "d3-selection": "3", + "d3-transition": "3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-chord": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-chord/-/d3-chord-3.0.1.tgz", + "integrity": "sha512-VE5S6TNa+j8msksl7HwjxMHDM2yNK3XCkusIlpX5kwauBfXuyLAtNg9jCp/iHH61tgI4sb6R/EIMWCqEIdjT/g==", + "dependencies": { + "d3-path": "1 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-color": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/d3-color/-/d3-color-3.1.0.tgz", + "integrity": "sha512-zg/chbXyeBtMQ1LbD/WSoW2DpC3I0mpmPdW+ynRTj/x2DAWYrIY7qeZIHidozwV24m4iavr15lNwIwLxRmOxhA==", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-contour": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/d3-contour/-/d3-contour-4.0.2.tgz", + "integrity": "sha512-4EzFTRIikzs47RGmdxbeUvLWtGedDUNkTcmzoeyg4sP/dvCexO47AaQL7VKy/gul85TOxw+IBgA8US2xwbToNA==", + "dependencies": { + "d3-array": "^3.2.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-delaunay": { + "version": "6.0.4", + "resolved": "https://registry.npmjs.org/d3-delaunay/-/d3-delaunay-6.0.4.tgz", + "integrity": "sha512-mdjtIZ1XLAM8bm/hx3WwjfHt6Sggek7qH043O8KEjDXN40xi3vx/6pYSVTwLjEgiXQTbvaouWKynLBiUZ6SK6A==", + "dependencies": { + "delaunator": "5" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-dispatch": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-dispatch/-/d3-dispatch-3.0.1.tgz", + "integrity": "sha512-rzUyPU/S7rwUflMyLc1ETDeBj0NRuHKKAcvukozwhshr6g6c5d8zh4c2gQjY2bZ0dXeGLWc1PF174P2tVvKhfg==", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-drag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/d3-drag/-/d3-drag-3.0.0.tgz", + "integrity": "sha512-pWbUJLdETVA8lQNJecMxoXfH6x+mO2UQo8rSmZ+QqxcbyA3hfeprFgIT//HW2nlHChWeIIMwS2Fq+gEARkhTkg==", + "dependencies": { + "d3-dispatch": "1 - 3", + "d3-selection": "3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-dsv": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-dsv/-/d3-dsv-3.0.1.tgz", + "integrity": "sha512-UG6OvdI5afDIFP9w4G0mNq50dSOsXHJaRE8arAS5o9ApWnIElp8GZw1Dun8vP8OyHOZ/QJUKUJwxiiCCnUwm+Q==", + "dependencies": { + "commander": "7", + "iconv-lite": "0.6", + "rw": "1" + }, + "bin": { + "csv2json": "bin/dsv2json.js", + "csv2tsv": "bin/dsv2dsv.js", + "dsv2dsv": "bin/dsv2dsv.js", + "dsv2json": "bin/dsv2json.js", + "json2csv": "bin/json2dsv.js", + "json2dsv": "bin/json2dsv.js", + "json2tsv": "bin/json2dsv.js", + "tsv2csv": "bin/dsv2dsv.js", + "tsv2json": "bin/dsv2json.js" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-dsv/node_modules/commander": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz", + "integrity": "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==", + "engines": { + "node": ">= 10" + } + }, + "node_modules/d3-ease": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-ease/-/d3-ease-3.0.1.tgz", + "integrity": "sha512-wR/XK3D3XcLIZwpbvQwQ5fK+8Ykds1ip7A2Txe0yxncXSdq1L9skcG7blcedkOX+ZcgxGAmLX1FrRGbADwzi0w==", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-fetch": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-fetch/-/d3-fetch-3.0.1.tgz", + "integrity": "sha512-kpkQIM20n3oLVBKGg6oHrUchHM3xODkTzjMoj7aWQFq5QEM+R6E4WkzT5+tojDY7yjez8KgCBRoj4aEr99Fdqw==", + "dependencies": { + "d3-dsv": "1 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-force": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/d3-force/-/d3-force-3.0.0.tgz", + "integrity": "sha512-zxV/SsA+U4yte8051P4ECydjD/S+qeYtnaIyAs9tgHCqfguma/aAQDjo85A9Z6EKhBirHRJHXIgJUlffT4wdLg==", + "dependencies": { + "d3-dispatch": "1 - 3", + "d3-quadtree": "1 - 3", + "d3-timer": "1 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-format": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/d3-format/-/d3-format-3.1.0.tgz", + "integrity": "sha512-YyUI6AEuY/Wpt8KWLgZHsIU86atmikuoOmCfommt0LYHiQSPjvX2AcFc38PX0CBpr2RCyZhjex+NS/LPOv6YqA==", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-geo": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/d3-geo/-/d3-geo-3.1.1.tgz", + "integrity": "sha512-637ln3gXKXOwhalDzinUgY83KzNWZRKbYubaG+fGVuc/dxO64RRljtCTnf5ecMyE1RIdtqpkVcq0IbtU2S8j2Q==", + "dependencies": { + "d3-array": "2.5.0 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-hierarchy": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/d3-hierarchy/-/d3-hierarchy-3.1.2.tgz", + "integrity": "sha512-FX/9frcub54beBdugHjDCdikxThEqjnR93Qt7PvQTOHxyiNCAlvMrHhclk3cD5VeAaq9fxmfRp+CnWw9rEMBuA==", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-interpolate": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-interpolate/-/d3-interpolate-3.0.1.tgz", + "integrity": "sha512-3bYs1rOD33uo8aqJfKP3JWPAibgw8Zm2+L9vBKEHJ2Rg+viTR7o5Mmv5mZcieN+FRYaAOWX5SJATX6k1PWz72g==", + "dependencies": { + "d3-color": "1 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-path": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/d3-path/-/d3-path-3.1.0.tgz", + "integrity": "sha512-p3KP5HCf/bvjBSSKuXid6Zqijx7wIfNW+J/maPs+iwR35at5JCbLUT0LzF1cnjbCHWhqzQTIN2Jpe8pRebIEFQ==", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-polygon": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-polygon/-/d3-polygon-3.0.1.tgz", + "integrity": "sha512-3vbA7vXYwfe1SYhED++fPUQlWSYTTGmFmQiany/gdbiWgU/iEyQzyymwL9SkJjFFuCS4902BSzewVGsHHmHtXg==", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-quadtree": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-quadtree/-/d3-quadtree-3.0.1.tgz", + "integrity": "sha512-04xDrxQTDTCFwP5H6hRhsRcb9xxv2RzkcsygFzmkSIOJy3PeRJP7sNk3VRIbKXcog561P9oU0/rVH6vDROAgUw==", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-random": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-random/-/d3-random-3.0.1.tgz", + "integrity": "sha512-FXMe9GfxTxqd5D6jFsQ+DJ8BJS4E/fT5mqqdjovykEB2oFbTMDVdg1MGFxfQW+FBOGoB++k8swBrgwSHT1cUXQ==", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-sankey": { + "version": "0.12.3", + "resolved": "https://registry.npmjs.org/d3-sankey/-/d3-sankey-0.12.3.tgz", + "integrity": "sha512-nQhsBRmM19Ax5xEIPLMY9ZmJ/cDvd1BG3UVvt5h3WRxKg5zGRbvnteTyWAbzeSvlh3tW7ZEmq4VwR5mB3tutmQ==", + "dependencies": { + "d3-array": "1 - 2", + "d3-shape": "^1.2.0" + } + }, + "node_modules/d3-sankey/node_modules/d3-array": { + "version": "2.12.1", + "resolved": "https://registry.npmjs.org/d3-array/-/d3-array-2.12.1.tgz", + "integrity": "sha512-B0ErZK/66mHtEsR1TkPEEkwdy+WDesimkM5gpZr5Dsg54BiTA5RXtYW5qTLIAcekaS9xfZrzBLF/OAkB3Qn1YQ==", + "dependencies": { + "internmap": "^1.0.0" + } + }, + "node_modules/d3-sankey/node_modules/d3-path": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/d3-path/-/d3-path-1.0.9.tgz", + "integrity": "sha512-VLaYcn81dtHVTjEHd8B+pbe9yHWpXKZUC87PzoFmsFrJqgFwDe/qxfp5MlfsfM1V5E/iVt0MmEbWQ7FVIXh/bg==" + }, + "node_modules/d3-sankey/node_modules/d3-shape": { + "version": "1.3.7", + "resolved": "https://registry.npmjs.org/d3-shape/-/d3-shape-1.3.7.tgz", + "integrity": "sha512-EUkvKjqPFUAZyOlhY5gzCxCeI0Aep04LwIRpsZ/mLFelJiUfnK56jo5JMDSE7yyP2kLSb6LtF+S5chMk7uqPqw==", + "dependencies": { + "d3-path": "1" + } + }, + "node_modules/d3-sankey/node_modules/internmap": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/internmap/-/internmap-1.0.1.tgz", + "integrity": "sha512-lDB5YccMydFBtasVtxnZ3MRBHuaoE8GKsppq+EchKL2U4nK/DmEpPHNH8MZe5HkMtpSiTSOZwfN0tzYjO/lJEw==" + }, + "node_modules/d3-scale": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/d3-scale/-/d3-scale-4.0.2.tgz", + "integrity": "sha512-GZW464g1SH7ag3Y7hXjf8RoUuAFIqklOAq3MRl4OaWabTFJY9PN/E1YklhXLh+OQ3fM9yS2nOkCoS+WLZ6kvxQ==", + "dependencies": { + "d3-array": "2.10.0 - 3", + "d3-format": "1 - 3", + "d3-interpolate": "1.2.0 - 3", + "d3-time": "2.1.1 - 3", + "d3-time-format": "2 - 4" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-scale-chromatic": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/d3-scale-chromatic/-/d3-scale-chromatic-3.1.0.tgz", + "integrity": "sha512-A3s5PWiZ9YCXFye1o246KoscMWqf8BsD9eRiJ3He7C9OBaxKhAd5TFCdEx/7VbKtxxTsu//1mMJFrEt572cEyQ==", + "dependencies": { + "d3-color": "1 - 3", + "d3-interpolate": "1 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-selection": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/d3-selection/-/d3-selection-3.0.0.tgz", + "integrity": "sha512-fmTRWbNMmsmWq6xJV8D19U/gw/bwrHfNXxrIN+HfZgnzqTHp9jOmKMhsTUjXOJnZOdZY9Q28y4yebKzqDKlxlQ==", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-shape": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/d3-shape/-/d3-shape-3.2.0.tgz", + "integrity": "sha512-SaLBuwGm3MOViRq2ABk3eLoxwZELpH6zhl3FbAoJ7Vm1gofKx6El1Ib5z23NUEhF9AsGl7y+dzLe5Cw2AArGTA==", + "dependencies": { + "d3-path": "^3.1.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-time": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/d3-time/-/d3-time-3.1.0.tgz", + "integrity": "sha512-VqKjzBLejbSMT4IgbmVgDjpkYrNWUYJnbCGo874u7MMKIWsILRX+OpX/gTk8MqjpT1A/c6HY2dCA77ZN0lkQ2Q==", + "dependencies": { + "d3-array": "2 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-time-format": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/d3-time-format/-/d3-time-format-4.1.0.tgz", + "integrity": "sha512-dJxPBlzC7NugB2PDLwo9Q8JiTR3M3e4/XANkreKSUxF8vvXKqm1Yfq4Q5dl8budlunRVlUUaDUgFt7eA8D6NLg==", + "dependencies": { + "d3-time": "1 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-timer": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-timer/-/d3-timer-3.0.1.tgz", + "integrity": "sha512-ndfJ/JxxMd3nw31uyKoY2naivF+r29V+Lc0svZxe1JvvIRmi8hUsrMvdOwgS1o6uBHmiz91geQ0ylPP0aj1VUA==", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-transition": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-transition/-/d3-transition-3.0.1.tgz", + "integrity": "sha512-ApKvfjsSR6tg06xrL434C0WydLr7JewBB3V+/39RMHsaXTOG0zmt/OAXeng5M5LBm0ojmxJrpomQVZ1aPvBL4w==", + "dependencies": { + "d3-color": "1 - 3", + "d3-dispatch": "1 - 3", + "d3-ease": "1 - 3", + "d3-interpolate": "1 - 3", + "d3-timer": "1 - 3" + }, + "engines": { + "node": ">=12" + }, + "peerDependencies": { + "d3-selection": "2 - 3" + } + }, + "node_modules/d3-zoom": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/d3-zoom/-/d3-zoom-3.0.0.tgz", + "integrity": "sha512-b8AmV3kfQaqWAuacbPuNbL6vahnOJflOhexLzMMNLga62+/nh0JzvJ0aO/5a5MVgUFGS7Hu1P9P03o3fJkDCyw==", + "dependencies": { + "d3-dispatch": "1 - 3", + "d3-drag": "2 - 3", + "d3-interpolate": "1 - 3", + "d3-selection": "2 - 3", + "d3-transition": "2 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/dagre-d3-es": { + "version": "7.0.10", + "resolved": "https://registry.npmjs.org/dagre-d3-es/-/dagre-d3-es-7.0.10.tgz", + "integrity": "sha512-qTCQmEhcynucuaZgY5/+ti3X/rnszKZhEQH/ZdWdtP1tA/y3VoHJzcVrO9pjjJCNpigfscAtoUB5ONcd2wNn0A==", + "dependencies": { + "d3": "^7.8.2", + "lodash-es": "^4.17.21" + } + }, + "node_modules/dayjs": { + "version": "1.11.11", + "resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.11.tgz", + "integrity": "sha512-okzr3f11N6WuqYtZSvm+F776mB41wRZMhKP+hc34YdW+KmtYYK9iqvHSwo2k9FEH3fhGXvOPV6yz2IcSrfRUDg==" + }, + "node_modules/debug": { + "version": "4.3.5", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.5.tgz", + "integrity": "sha512-pt0bNEmneDIvdL1Xsd9oDQ/wrQRkXDT4AUWlNZNPKvW5x/jyO9VFXkJUP07vQ2upmw5PlaITaPKc31jK13V+jg==", + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/decode-named-character-reference": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/decode-named-character-reference/-/decode-named-character-reference-1.0.2.tgz", + "integrity": "sha512-O8x12RzrUF8xyVcY0KJowWsmaJxQbmy0/EtnNtHRpsOcT7dFk5W598coHqBVpmWo1oQQfsCqfCmkZN5DJrZVdg==", + "dependencies": { + "character-entities": "^2.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/delaunator": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/delaunator/-/delaunator-5.0.1.tgz", + "integrity": "sha512-8nvh+XBe96aCESrGOqMp/84b13H9cdKbG5P2ejQCh4d4sK9RL4371qou9drQjMhvnPmhWl5hnmqbEE0fXr9Xnw==", + "dependencies": { + "robust-predicates": "^3.0.2" + } + }, + "node_modules/dequal": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz", + "integrity": "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==", + "engines": { + "node": ">=6" + } + }, + "node_modules/devlop": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/devlop/-/devlop-1.1.0.tgz", + "integrity": "sha512-RWmIqhcFf1lRYBvNmr7qTNuyCt/7/ns2jbpp1+PalgE/rDQcBT0fioSMUpJ93irlUhC5hrg4cYqe6U+0ImW0rA==", + "dependencies": { + "dequal": "^2.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/didyoumean": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/didyoumean/-/didyoumean-1.2.2.tgz", + "integrity": "sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw==", + "dev": true + }, + "node_modules/diff": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/diff/-/diff-5.2.0.tgz", + "integrity": "sha512-uIFDxqpRZGZ6ThOk84hEfqWoHx2devRFvpTZcTHur85vImfaxUbTW9Ryh4CpCuDnToOP1CEtXKIgytHBPVff5A==", + "engines": { + "node": ">=0.3.1" + } + }, + "node_modules/dlv": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/dlv/-/dlv-1.1.3.tgz", + "integrity": "sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==", + "dev": true + }, + "node_modules/dompurify": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/dompurify/-/dompurify-3.1.5.tgz", + "integrity": "sha512-lwG+n5h8QNpxtyrJW/gJWckL+1/DQiYMX8f7t8Z2AZTPw1esVrqjI63i7Zc2Gz0aKzLVMYC1V1PL/ky+aY/NgA==" + }, + "node_modules/eastasianwidth": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", + "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", + "dev": true + }, + "node_modules/electron-to-chromium": { + "version": "1.4.805", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.805.tgz", + "integrity": "sha512-8W4UJwX/w9T0QSzINJckTKG6CYpAUTqsaWcWIsdud3I1FYJcMgW9QqT1/4CBff/pP/TihWh13OmiyY8neto6vw==", + "dev": true + }, + "node_modules/elkjs": { + "version": "0.9.3", + "resolved": "https://registry.npmjs.org/elkjs/-/elkjs-0.9.3.tgz", + "integrity": "sha512-f/ZeWvW/BCXbhGEf1Ujp29EASo/lk1FDnETgNKwJrsVvGZhUWCZyg3xLJjAsxfOmt8KjswHmI5EwCQcPMpOYhQ==" + }, + "node_modules/emoji-regex": { + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", + "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", + "dev": true + }, + "node_modules/entities": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", + "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==", + "engines": { + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/escalade": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.2.tgz", + "integrity": "sha512-ErCHMCae19vR8vQGe50xIsVomy19rg6gFu3+r3jkEO46suLMWBksvVyoGgQV+jOfl84ZSOSlmv6Gxa89PmTGmA==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/escape-string-regexp": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-5.0.0.tgz", + "integrity": "sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw==", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/esprima": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", + "bin": { + "esparse": "bin/esparse.js", + "esvalidate": "bin/esvalidate.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/estree-util-attach-comments": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/estree-util-attach-comments/-/estree-util-attach-comments-2.1.1.tgz", + "integrity": "sha512-+5Ba/xGGS6mnwFbXIuQiDPTbuTxuMCooq3arVv7gPZtYpjp+VXH/NkHAP35OOefPhNG/UGqU3vt/LTABwcHX0w==", + "dependencies": { + "@types/estree": "^1.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/estree-util-build-jsx": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/estree-util-build-jsx/-/estree-util-build-jsx-2.2.2.tgz", + "integrity": "sha512-m56vOXcOBuaF+Igpb9OPAy7f9w9OIkb5yhjsZuaPm7HoGi4oTOQi0h2+yZ+AtKklYFZ+rPC4n0wYCJCEU1ONqg==", + "dependencies": { + "@types/estree-jsx": "^1.0.0", + "estree-util-is-identifier-name": "^2.0.0", + "estree-walker": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/estree-util-is-identifier-name": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/estree-util-is-identifier-name/-/estree-util-is-identifier-name-2.1.0.tgz", + "integrity": "sha512-bEN9VHRyXAUOjkKVQVvArFym08BTWB0aJPppZZr0UNyAqWsLaVfAqP7hbaTJjzHifmB5ebnR8Wm7r7yGN/HonQ==", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/estree-util-to-js": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/estree-util-to-js/-/estree-util-to-js-1.2.0.tgz", + "integrity": "sha512-IzU74r1PK5IMMGZXUVZbmiu4A1uhiPgW5hm1GjcOfr4ZzHaMPpLNJjR7HjXiIOzi25nZDrgFTobHTkV5Q6ITjA==", + "dependencies": { + "@types/estree-jsx": "^1.0.0", + "astring": "^1.8.0", + "source-map": "^0.7.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/estree-util-value-to-estree": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/estree-util-value-to-estree/-/estree-util-value-to-estree-1.3.0.tgz", + "integrity": "sha512-Y+ughcF9jSUJvncXwqRageavjrNPAI+1M/L3BI3PyLp1nmgYTGUXU6t5z1Y7OWuThoDdhPME07bQU+d5LxdJqw==", + "dependencies": { + "is-plain-obj": "^3.0.0" + }, + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/estree-util-visit": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/estree-util-visit/-/estree-util-visit-1.2.1.tgz", + "integrity": "sha512-xbgqcrkIVbIG+lI/gzbvd9SGTJL4zqJKBFttUl5pP27KhAjtMKbX/mQXJ7qgyXpMgVy/zvpm0xoQQaGL8OloOw==", + "dependencies": { + "@types/estree-jsx": "^1.0.0", + "@types/unist": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/estree-walker": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-3.0.3.tgz", + "integrity": "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==", + "dependencies": { + "@types/estree": "^1.0.0" + } + }, + "node_modules/execa": { + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/execa/-/execa-0.8.0.tgz", + "integrity": "sha512-zDWS+Rb1E8BlqqhALSt9kUhss8Qq4nN3iof3gsOdyINksElaPyNBtKUMTR62qhvgVWR0CqCX7sdnKe4MnUbFEA==", + "dependencies": { + "cross-spawn": "^5.0.1", + "get-stream": "^3.0.0", + "is-stream": "^1.1.0", + "npm-run-path": "^2.0.0", + "p-finally": "^1.0.0", + "signal-exit": "^3.0.0", + "strip-eof": "^1.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/extend": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", + "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==" + }, + "node_modules/extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==", + "dependencies": { + "is-extendable": "^0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/fast-glob": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.2.tgz", + "integrity": "sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==", + "dev": true, + "dependencies": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.4" + }, + "engines": { + "node": ">=8.6.0" + } + }, + "node_modules/fast-glob/node_modules/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, + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/fastq": { + "version": "1.17.1", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.17.1.tgz", + "integrity": "sha512-sRVD3lWVIXWg6By68ZN7vho9a1pQcN/WBFaAAsDDFzlJjvoGx0P8z7V1t72grFJfJhu3YPZBuu25f7Kaw2jN1w==", + "dev": true, + "dependencies": { + "reusify": "^1.0.4" + } + }, + "node_modules/fill-range": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", + "dev": true, + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/flexsearch": { + "version": "0.7.43", + "resolved": "https://registry.npmjs.org/flexsearch/-/flexsearch-0.7.43.tgz", + "integrity": "sha512-c5o/+Um8aqCSOXGcZoqZOm+NqtVwNsvVpWv6lfmSclU954O3wvQKxxK8zj74fPaSJbXpSLTs4PRhh+wnoCXnKg==" + }, + "node_modules/focus-visible": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/focus-visible/-/focus-visible-5.2.0.tgz", + "integrity": "sha512-Rwix9pBtC1Nuy5wysTmKy+UjbDJpIfg8eHjw0rjZ1mX4GNLz1Bmd16uDpI3Gk1i70Fgcs8Csg2lPm8HULFg9DQ==" + }, + "node_modules/foreground-child": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.2.1.tgz", + "integrity": "sha512-PXUUyLqrR2XCWICfv6ukppP96sdFwWbNEnfEMt7jNsISjMsvaLNinAHNDYyvkyU+SZG2BTSbT5NjG+vZslfGTA==", + "dev": true, + "dependencies": { + "cross-spawn": "^7.0.0", + "signal-exit": "^4.0.1" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/foreground-child/node_modules/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, + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/foreground-child/node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/foreground-child/node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/foreground-child/node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/foreground-child/node_modules/signal-exit": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", + "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", + "dev": true, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/foreground-child/node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/fraction.js": { + "version": "4.3.7", + "resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-4.3.7.tgz", + "integrity": "sha512-ZsDfxO51wGAXREY55a7la9LScWpwv9RxIrYABrlvOFBlH/ShPnrtsXeuUIfXKKOVicNxQ+o8JTbJvjS4M89yew==", + "dev": true, + "engines": { + "node": "*" + }, + "funding": { + "type": "patreon", + "url": "https://github.com/sponsors/rawify" + } + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "dev": true, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-stream": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-3.0.0.tgz", + "integrity": "sha512-GlhdIUuVakc8SJ6kK0zAFbiGzRFzNnY4jUuEbV9UROo4Y+0Ny4fjvcZFVTeDA4odpFyOQzaw6hXukJSq/f28sQ==", + "engines": { + "node": ">=4" + } + }, + "node_modules/git-up": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/git-up/-/git-up-7.0.0.tgz", + "integrity": "sha512-ONdIrbBCFusq1Oy0sC71F5azx8bVkvtZtMJAsv+a6lz5YAmbNnLD6HAB4gptHZVLPR8S2/kVN6Gab7lryq5+lQ==", + "dependencies": { + "is-ssh": "^1.4.0", + "parse-url": "^8.1.0" + } + }, + "node_modules/git-url-parse": { + "version": "13.1.1", + "resolved": "https://registry.npmjs.org/git-url-parse/-/git-url-parse-13.1.1.tgz", + "integrity": "sha512-PCFJyeSSdtnbfhSNRw9Wk96dDCNx+sogTe4YNXeXSJxt7xz5hvXekuRn9JX7m+Mf4OscCu8h+mtAl3+h5Fo8lQ==", + "dependencies": { + "git-up": "^7.0.0" + } + }, + "node_modules/github-slugger": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/github-slugger/-/github-slugger-2.0.0.tgz", + "integrity": "sha512-IaOQ9puYtjrkq7Y0Ygl9KDZnrf/aiUJYUpVf89y8kyaxbRG7Y1SrX/jaumrv81vc61+kiMempujsM3Yw7w5qcw==" + }, + "node_modules/glob": { + "version": "10.4.1", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.4.1.tgz", + "integrity": "sha512-2jelhlq3E4ho74ZyVLN03oKdAZVUa6UDZzFLVH1H7dnoax+y9qyaq8zBkfDIggjniU19z0wU18y16jMB2eyVIw==", + "dev": true, + "dependencies": { + "foreground-child": "^3.1.0", + "jackspeak": "^3.1.2", + "minimatch": "^9.0.4", + "minipass": "^7.1.2", + "path-scurry": "^1.11.1" + }, + "bin": { + "glob": "dist/esm/bin.mjs" + }, + "engines": { + "node": ">=16 || 14 >=14.18" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/glob-parent": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", + "dev": true, + "dependencies": { + "is-glob": "^4.0.3" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/graceful-fs": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==" + }, + "node_modules/gray-matter": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/gray-matter/-/gray-matter-4.0.3.tgz", + "integrity": "sha512-5v6yZd4JK3eMI3FqqCouswVqwugaA9r4dNZB1wwcmrD02QkV5H0y7XBQW8QwQqEaZY1pM9aqORSORhJRdNK44Q==", + "dependencies": { + "js-yaml": "^3.13.1", + "kind-of": "^6.0.2", + "section-matter": "^1.0.0", + "strip-bom-string": "^1.0.0" + }, + "engines": { + "node": ">=6.0" + } + }, + "node_modules/has-flag": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-2.0.0.tgz", + "integrity": "sha512-P+1n3MnwjR/Epg9BBo1KT8qbye2g2Ou4sFumihwt6I4tsUX7jnLcX4BTOSKg/B1ZrIYMN9FcEnG4x5a7NB8Eng==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/hash-obj": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/hash-obj/-/hash-obj-4.0.0.tgz", + "integrity": "sha512-FwO1BUVWkyHasWDW4S8o0ssQXjvyghLV2rfVhnN36b2bbcj45eGiuzdn9XOvOpjV3TKQD7Gm2BWNXdE9V4KKYg==", + "dependencies": { + "is-obj": "^3.0.0", + "sort-keys": "^5.0.0", + "type-fest": "^1.0.2" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/hasown": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", + "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "dev": true, + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/hast-util-from-dom": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/hast-util-from-dom/-/hast-util-from-dom-5.0.0.tgz", + "integrity": "sha512-d6235voAp/XR3Hh5uy7aGLbM3S4KamdW0WEgOaU1YoewnuYw4HXb5eRtv9g65m/RFGEfUY1Mw4UqCc5Y8L4Stg==", + "dependencies": { + "@types/hast": "^3.0.0", + "hastscript": "^8.0.0", + "web-namespaces": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hast-util-from-dom/node_modules/@types/hast": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@types/hast/-/hast-3.0.4.tgz", + "integrity": "sha512-WPs+bbQw5aCj+x6laNGWLH3wviHtoCv/P3+otBhbOhJgG8qtpdAMlTCxLtsTWA7LH1Oh/bFCHsBn0TPS5m30EQ==", + "dependencies": { + "@types/unist": "*" + } + }, + "node_modules/hast-util-from-html": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/hast-util-from-html/-/hast-util-from-html-2.0.1.tgz", + "integrity": "sha512-RXQBLMl9kjKVNkJTIO6bZyb2n+cUH8LFaSSzo82jiLT6Tfc+Pt7VQCS+/h3YwG4jaNE2TA2sdJisGWR+aJrp0g==", + "dependencies": { + "@types/hast": "^3.0.0", + "devlop": "^1.1.0", + "hast-util-from-parse5": "^8.0.0", + "parse5": "^7.0.0", + "vfile": "^6.0.0", + "vfile-message": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hast-util-from-html-isomorphic": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/hast-util-from-html-isomorphic/-/hast-util-from-html-isomorphic-2.0.0.tgz", + "integrity": "sha512-zJfpXq44yff2hmE0XmwEOzdWin5xwH+QIhMLOScpX91e/NSGPsAzNCvLQDIEPyO2TXi+lBmU6hjLIhV8MwP2kw==", + "dependencies": { + "@types/hast": "^3.0.0", + "hast-util-from-dom": "^5.0.0", + "hast-util-from-html": "^2.0.0", + "unist-util-remove-position": "^5.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hast-util-from-html-isomorphic/node_modules/@types/hast": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@types/hast/-/hast-3.0.4.tgz", + "integrity": "sha512-WPs+bbQw5aCj+x6laNGWLH3wviHtoCv/P3+otBhbOhJgG8qtpdAMlTCxLtsTWA7LH1Oh/bFCHsBn0TPS5m30EQ==", + "dependencies": { + "@types/unist": "*" + } + }, + "node_modules/hast-util-from-html/node_modules/@types/hast": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@types/hast/-/hast-3.0.4.tgz", + "integrity": "sha512-WPs+bbQw5aCj+x6laNGWLH3wviHtoCv/P3+otBhbOhJgG8qtpdAMlTCxLtsTWA7LH1Oh/bFCHsBn0TPS5m30EQ==", + "dependencies": { + "@types/unist": "*" + } + }, + "node_modules/hast-util-from-html/node_modules/@types/unist": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/@types/unist/-/unist-3.0.2.tgz", + "integrity": "sha512-dqId9J8K/vGi5Zr7oo212BGii5m3q5Hxlkwy3WpYuKPklmBEvsbMYYyLxAQpSffdLl/gdW0XUpKWFvYmyoWCoQ==" + }, + "node_modules/hast-util-from-html/node_modules/unist-util-stringify-position": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/unist-util-stringify-position/-/unist-util-stringify-position-4.0.0.tgz", + "integrity": "sha512-0ASV06AAoKCDkS2+xw5RXJywruurpbC4JZSm7nr7MOt1ojAzvyyaO+UxZf18j8FCF6kmzCZKcAgN/yu2gm2XgQ==", + "dependencies": { + "@types/unist": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hast-util-from-html/node_modules/vfile": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/vfile/-/vfile-6.0.1.tgz", + "integrity": "sha512-1bYqc7pt6NIADBJ98UiG0Bn/CHIVOoZ/IyEkqIruLg0mE1BKzkOXY2D6CSqQIcKqgadppE5lrxgWXJmXd7zZJw==", + "dependencies": { + "@types/unist": "^3.0.0", + "unist-util-stringify-position": "^4.0.0", + "vfile-message": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hast-util-from-parse5": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/hast-util-from-parse5/-/hast-util-from-parse5-8.0.1.tgz", + "integrity": "sha512-Er/Iixbc7IEa7r/XLtuG52zoqn/b3Xng/w6aZQ0xGVxzhw5xUFxcRqdPzP6yFi/4HBYRaifaI5fQ1RH8n0ZeOQ==", + "dependencies": { + "@types/hast": "^3.0.0", + "@types/unist": "^3.0.0", + "devlop": "^1.0.0", + "hastscript": "^8.0.0", + "property-information": "^6.0.0", + "vfile": "^6.0.0", + "vfile-location": "^5.0.0", + "web-namespaces": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hast-util-from-parse5/node_modules/@types/hast": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@types/hast/-/hast-3.0.4.tgz", + "integrity": "sha512-WPs+bbQw5aCj+x6laNGWLH3wviHtoCv/P3+otBhbOhJgG8qtpdAMlTCxLtsTWA7LH1Oh/bFCHsBn0TPS5m30EQ==", + "dependencies": { + "@types/unist": "*" + } + }, + "node_modules/hast-util-from-parse5/node_modules/@types/unist": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/@types/unist/-/unist-3.0.2.tgz", + "integrity": "sha512-dqId9J8K/vGi5Zr7oo212BGii5m3q5Hxlkwy3WpYuKPklmBEvsbMYYyLxAQpSffdLl/gdW0XUpKWFvYmyoWCoQ==" + }, + "node_modules/hast-util-from-parse5/node_modules/unist-util-stringify-position": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/unist-util-stringify-position/-/unist-util-stringify-position-4.0.0.tgz", + "integrity": "sha512-0ASV06AAoKCDkS2+xw5RXJywruurpbC4JZSm7nr7MOt1ojAzvyyaO+UxZf18j8FCF6kmzCZKcAgN/yu2gm2XgQ==", + "dependencies": { + "@types/unist": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hast-util-from-parse5/node_modules/vfile": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/vfile/-/vfile-6.0.1.tgz", + "integrity": "sha512-1bYqc7pt6NIADBJ98UiG0Bn/CHIVOoZ/IyEkqIruLg0mE1BKzkOXY2D6CSqQIcKqgadppE5lrxgWXJmXd7zZJw==", + "dependencies": { + "@types/unist": "^3.0.0", + "unist-util-stringify-position": "^4.0.0", + "vfile-message": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hast-util-is-element": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/hast-util-is-element/-/hast-util-is-element-3.0.0.tgz", + "integrity": "sha512-Val9mnv2IWpLbNPqc/pUem+a7Ipj2aHacCwgNfTiK0vJKl0LF+4Ba4+v1oPHFpf3bLYmreq0/l3Gud9S5OH42g==", + "dependencies": { + "@types/hast": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hast-util-is-element/node_modules/@types/hast": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@types/hast/-/hast-3.0.4.tgz", + "integrity": "sha512-WPs+bbQw5aCj+x6laNGWLH3wviHtoCv/P3+otBhbOhJgG8qtpdAMlTCxLtsTWA7LH1Oh/bFCHsBn0TPS5m30EQ==", + "dependencies": { + "@types/unist": "*" + } + }, + "node_modules/hast-util-parse-selector": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/hast-util-parse-selector/-/hast-util-parse-selector-4.0.0.tgz", + "integrity": "sha512-wkQCkSYoOGCRKERFWcxMVMOcYE2K1AaNLU8DXS9arxnLOUEWbOXKXiJUNzEpqZ3JOKpnha3jkFrumEjVliDe7A==", + "dependencies": { + "@types/hast": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hast-util-parse-selector/node_modules/@types/hast": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@types/hast/-/hast-3.0.4.tgz", + "integrity": "sha512-WPs+bbQw5aCj+x6laNGWLH3wviHtoCv/P3+otBhbOhJgG8qtpdAMlTCxLtsTWA7LH1Oh/bFCHsBn0TPS5m30EQ==", + "dependencies": { + "@types/unist": "*" + } + }, + "node_modules/hast-util-raw": { + "version": "9.0.3", + "resolved": "https://registry.npmjs.org/hast-util-raw/-/hast-util-raw-9.0.3.tgz", + "integrity": "sha512-ICWvVOF2fq4+7CMmtCPD5CM4QKjPbHpPotE6+8tDooV0ZuyJVUzHsrNX+O5NaRbieTf0F7FfeBOMAwi6Td0+yQ==", + "dependencies": { + "@types/hast": "^3.0.0", + "@types/unist": "^3.0.0", + "@ungap/structured-clone": "^1.0.0", + "hast-util-from-parse5": "^8.0.0", + "hast-util-to-parse5": "^8.0.0", + "html-void-elements": "^3.0.0", + "mdast-util-to-hast": "^13.0.0", + "parse5": "^7.0.0", + "unist-util-position": "^5.0.0", + "unist-util-visit": "^5.0.0", + "vfile": "^6.0.0", + "web-namespaces": "^2.0.0", + "zwitch": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hast-util-raw/node_modules/@types/hast": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@types/hast/-/hast-3.0.4.tgz", + "integrity": "sha512-WPs+bbQw5aCj+x6laNGWLH3wviHtoCv/P3+otBhbOhJgG8qtpdAMlTCxLtsTWA7LH1Oh/bFCHsBn0TPS5m30EQ==", + "dependencies": { + "@types/unist": "*" + } + }, + "node_modules/hast-util-raw/node_modules/@types/unist": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/@types/unist/-/unist-3.0.2.tgz", + "integrity": "sha512-dqId9J8K/vGi5Zr7oo212BGii5m3q5Hxlkwy3WpYuKPklmBEvsbMYYyLxAQpSffdLl/gdW0XUpKWFvYmyoWCoQ==" + }, + "node_modules/hast-util-raw/node_modules/unist-util-position": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/unist-util-position/-/unist-util-position-5.0.0.tgz", + "integrity": "sha512-fucsC7HjXvkB5R3kTCO7kUjRdrS0BJt3M/FPxmHMBOm8JQi2BsHAHFsy27E0EolP8rp0NzXsJ+jNPyDWvOJZPA==", + "dependencies": { + "@types/unist": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hast-util-raw/node_modules/unist-util-stringify-position": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/unist-util-stringify-position/-/unist-util-stringify-position-4.0.0.tgz", + "integrity": "sha512-0ASV06AAoKCDkS2+xw5RXJywruurpbC4JZSm7nr7MOt1ojAzvyyaO+UxZf18j8FCF6kmzCZKcAgN/yu2gm2XgQ==", + "dependencies": { + "@types/unist": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hast-util-raw/node_modules/vfile": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/vfile/-/vfile-6.0.1.tgz", + "integrity": "sha512-1bYqc7pt6NIADBJ98UiG0Bn/CHIVOoZ/IyEkqIruLg0mE1BKzkOXY2D6CSqQIcKqgadppE5lrxgWXJmXd7zZJw==", + "dependencies": { + "@types/unist": "^3.0.0", + "unist-util-stringify-position": "^4.0.0", + "vfile-message": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hast-util-to-estree": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/hast-util-to-estree/-/hast-util-to-estree-2.3.3.tgz", + "integrity": "sha512-ihhPIUPxN0v0w6M5+IiAZZrn0LH2uZomeWwhn7uP7avZC6TE7lIiEh2yBMPr5+zi1aUCXq6VoYRgs2Bw9xmycQ==", + "dependencies": { + "@types/estree": "^1.0.0", + "@types/estree-jsx": "^1.0.0", + "@types/hast": "^2.0.0", + "@types/unist": "^2.0.0", + "comma-separated-tokens": "^2.0.0", + "estree-util-attach-comments": "^2.0.0", + "estree-util-is-identifier-name": "^2.0.0", + "hast-util-whitespace": "^2.0.0", + "mdast-util-mdx-expression": "^1.0.0", + "mdast-util-mdxjs-esm": "^1.0.0", + "property-information": "^6.0.0", + "space-separated-tokens": "^2.0.0", + "style-to-object": "^0.4.1", + "unist-util-position": "^4.0.0", + "zwitch": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hast-util-to-parse5": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/hast-util-to-parse5/-/hast-util-to-parse5-8.0.0.tgz", + "integrity": "sha512-3KKrV5ZVI8if87DVSi1vDeByYrkGzg4mEfeu4alwgmmIeARiBLKCZS2uw5Gb6nU9x9Yufyj3iudm6i7nl52PFw==", + "dependencies": { + "@types/hast": "^3.0.0", + "comma-separated-tokens": "^2.0.0", + "devlop": "^1.0.0", + "property-information": "^6.0.0", + "space-separated-tokens": "^2.0.0", + "web-namespaces": "^2.0.0", + "zwitch": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hast-util-to-parse5/node_modules/@types/hast": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@types/hast/-/hast-3.0.4.tgz", + "integrity": "sha512-WPs+bbQw5aCj+x6laNGWLH3wviHtoCv/P3+otBhbOhJgG8qtpdAMlTCxLtsTWA7LH1Oh/bFCHsBn0TPS5m30EQ==", + "dependencies": { + "@types/unist": "*" + } + }, + "node_modules/hast-util-to-text": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/hast-util-to-text/-/hast-util-to-text-4.0.2.tgz", + "integrity": "sha512-KK6y/BN8lbaq654j7JgBydev7wuNMcID54lkRav1P0CaE1e47P72AWWPiGKXTJU271ooYzcvTAn/Zt0REnvc7A==", + "dependencies": { + "@types/hast": "^3.0.0", + "@types/unist": "^3.0.0", + "hast-util-is-element": "^3.0.0", + "unist-util-find-after": "^5.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hast-util-to-text/node_modules/@types/hast": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@types/hast/-/hast-3.0.4.tgz", + "integrity": "sha512-WPs+bbQw5aCj+x6laNGWLH3wviHtoCv/P3+otBhbOhJgG8qtpdAMlTCxLtsTWA7LH1Oh/bFCHsBn0TPS5m30EQ==", + "dependencies": { + "@types/unist": "*" + } + }, + "node_modules/hast-util-to-text/node_modules/@types/unist": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/@types/unist/-/unist-3.0.2.tgz", + "integrity": "sha512-dqId9J8K/vGi5Zr7oo212BGii5m3q5Hxlkwy3WpYuKPklmBEvsbMYYyLxAQpSffdLl/gdW0XUpKWFvYmyoWCoQ==" + }, + "node_modules/hast-util-whitespace": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/hast-util-whitespace/-/hast-util-whitespace-2.0.1.tgz", + "integrity": "sha512-nAxA0v8+vXSBDt3AnRUNjyRIQ0rD+ntpbAp4LnPkumc5M9yUbSMa4XDU9Q6etY4f1Wp4bNgvc1yjiZtsTTrSng==", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hastscript": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/hastscript/-/hastscript-8.0.0.tgz", + "integrity": "sha512-dMOtzCEd3ABUeSIISmrETiKuyydk1w0pa+gE/uormcTpSYuaNJPbX1NU3JLyscSLjwAQM8bWMhhIlnCqnRvDTw==", + "dependencies": { + "@types/hast": "^3.0.0", + "comma-separated-tokens": "^2.0.0", + "hast-util-parse-selector": "^4.0.0", + "property-information": "^6.0.0", + "space-separated-tokens": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hastscript/node_modules/@types/hast": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@types/hast/-/hast-3.0.4.tgz", + "integrity": "sha512-WPs+bbQw5aCj+x6laNGWLH3wviHtoCv/P3+otBhbOhJgG8qtpdAMlTCxLtsTWA7LH1Oh/bFCHsBn0TPS5m30EQ==", + "dependencies": { + "@types/unist": "*" + } + }, + "node_modules/html-void-elements": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/html-void-elements/-/html-void-elements-3.0.0.tgz", + "integrity": "sha512-bEqo66MRXsUGxWHV5IP0PUiAWwoEjba4VCzg0LjFJBpchPaTfyfCKTG6bc5F8ucKec3q5y6qOdGyYTSBEvhCrg==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/iconv-lite": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", + "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/inline-style-parser": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/inline-style-parser/-/inline-style-parser-0.1.1.tgz", + "integrity": "sha512-7NXolsK4CAS5+xvdj5OMMbI962hU/wvwoxk+LWR9Ek9bVtyuuYScDN6eS0rUm6TxApFpw7CX1o4uJzcd4AyD3Q==" + }, + "node_modules/internmap": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/internmap/-/internmap-2.0.3.tgz", + "integrity": "sha512-5Hh7Y1wQbvY5ooGgPbDaL5iYLAPzMTUrjMulskHLH6wnv/A+1q5rgEaiuqEjB+oxGXIVZs1FF+R/KPN3ZSQYYg==", + "engines": { + "node": ">=12" + } + }, + "node_modules/intersection-observer": { + "version": "0.12.2", + "resolved": "https://registry.npmjs.org/intersection-observer/-/intersection-observer-0.12.2.tgz", + "integrity": "sha512-7m1vEcPCxXYI8HqnL8CKI6siDyD+eIWSwgB3DZA+ZTogxk9I4CDnj4wilt9x/+/QbHI4YG5YZNmC6458/e9Ktg==" + }, + "node_modules/is-alphabetical": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-alphabetical/-/is-alphabetical-2.0.1.tgz", + "integrity": "sha512-FWyyY60MeTNyeSRpkM2Iry0G9hpr7/9kD40mD/cGQEuilcZYS4okz8SN2Q6rLCJ8gbCt6fN+rC+6tMGS99LaxQ==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/is-alphanumerical": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-alphanumerical/-/is-alphanumerical-2.0.1.tgz", + "integrity": "sha512-hmbYhX/9MUMF5uh7tOXyK/n0ZvWpad5caBA17GsC6vyuCqaWliRG5K1qS9inmUhEMaOBIW7/whAnSwveW/LtZw==", + "dependencies": { + "is-alphabetical": "^2.0.0", + "is-decimal": "^2.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/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, + "dependencies": { + "binary-extensions": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-buffer": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-2.0.5.tgz", + "integrity": "sha512-i2R6zNFDwgEHJyQUtJEk0XFi1i0dPFn/oqjK3/vPCcDeJvW5NQ83V8QbicfF1SupOaB0h8ntgBC2YiE7dfyctQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "engines": { + "node": ">=4" + } + }, + "node_modules/is-core-module": { + "version": "2.13.1", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.13.1.tgz", + "integrity": "sha512-hHrIjvZsftOsvKSn2TRYl63zvxsgE0K+0mYMoH6gD4omR5IWB2KynivBQczo3+wF1cCkjzvptnI9Q0sPU66ilw==", + "dev": true, + "dependencies": { + "hasown": "^2.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-decimal": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-decimal/-/is-decimal-2.0.1.tgz", + "integrity": "sha512-AAB9hiomQs5DXWcRB1rqsxGUstbRroFOPPVAomNk/3XHR5JyEZChOyTWe2oayKnsSsr/kcGqF+z6yuH6HHpN0A==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/is-extendable": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", + "integrity": "sha512-5BMULNob1vgFX6EjQw5izWDxrecWK9AM72rugNr0TFldMOi0fj6Jk+zeKIt0xGj4cEfQIJth4w3OKWOJ4f+AFw==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/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, + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-hexadecimal": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-hexadecimal/-/is-hexadecimal-2.0.1.tgz", + "integrity": "sha512-DgZQp241c8oO6cA1SbTEWiXeoxV42vlcJxgH+B3hi1AiqqKruZR3ZGF8In3fj4+/y/7rHvlOZLZtgJ/4ttYGZg==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/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, + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/is-obj": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-obj/-/is-obj-3.0.0.tgz", + "integrity": "sha512-IlsXEHOjtKhpN8r/tRFj2nDyTmHvcfNeu/nrRIcXE17ROeatXchkojffa1SpdqW4cr/Fj6QkEf/Gn4zf6KKvEQ==", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/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==", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-reference": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/is-reference/-/is-reference-3.0.2.tgz", + "integrity": "sha512-v3rht/LgVcsdZa3O2Nqs+NMowLOxeOm7Ay9+/ARQ2F+qEoANRcqrjAZKGN0v8ymUetZGgkp26LTnGT7H0Qo9Pg==", + "dependencies": { + "@types/estree": "*" + } + }, + "node_modules/is-ssh": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/is-ssh/-/is-ssh-1.4.0.tgz", + "integrity": "sha512-x7+VxdxOdlV3CYpjvRLBv5Lo9OJerlYanjwFrPR9fuGPjCiNiCzFgAWpiLAohSbsnH4ZAys3SBh+hq5rJosxUQ==", + "dependencies": { + "protocols": "^2.0.1" + } + }, + "node_modules/is-stream": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz", + "integrity": "sha512-uQPm8kcs47jx38atAcWTVxyltQYoPT68y9aWYdV6yWXSyW8mzSat0TL6CiWdZeCdF3KrAvpVtnHbTv4RN+rqdQ==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==" + }, + "node_modules/jackspeak": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.0.tgz", + "integrity": "sha512-JVYhQnN59LVPFCEcVa2C3CrEKYacvjRfqIQl+h8oi91aLYQVWRYbxjPcv1bUiUy/kLmQaANrYfNMCO3kuEDHfw==", + "dev": true, + "dependencies": { + "@isaacs/cliui": "^8.0.2" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + }, + "optionalDependencies": { + "@pkgjs/parseargs": "^0.11.0" + } + }, + "node_modules/jiti": { + "version": "1.21.6", + "resolved": "https://registry.npmjs.org/jiti/-/jiti-1.21.6.tgz", + "integrity": "sha512-2yTgeWTWzMWkHu6Jp9NKgePDaYHbntiwvYuuJLbbN9vl7DC9DvXKOB2BC3ZZ92D3cvV/aflH0osDfwpHepQ53w==", + "dev": true, + "bin": { + "jiti": "bin/jiti.js" + } + }, + "node_modules/js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==" + }, + "node_modules/js-yaml": { + "version": "3.14.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", + "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", + "dependencies": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/jsonc-parser": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-3.2.1.tgz", + "integrity": "sha512-AilxAyFOAcK5wA1+LeaySVBrHsGQvUFCDWXKpZjzaL0PqW+xfBOttn8GNtWKFWqneyMZj41MWF9Kl6iPWLwgOA==" + }, + "node_modules/katex": { + "version": "0.16.10", + "resolved": "https://registry.npmjs.org/katex/-/katex-0.16.10.tgz", + "integrity": "sha512-ZiqaC04tp2O5utMsl2TEZTXxa6WSC4yo0fv5ML++D3QZv/vx2Mct0mTlRx3O+uUkjfuAgOkzsCmq5MiUEsDDdA==", + "funding": [ + "https://opencollective.com/katex", + "https://github.com/sponsors/katex" + ], + "dependencies": { + "commander": "^8.3.0" + }, + "bin": { + "katex": "cli.js" + } + }, + "node_modules/khroma": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/khroma/-/khroma-2.1.0.tgz", + "integrity": "sha512-Ls993zuzfayK269Svk9hzpeGUKob/sIgZzyHYdjQoAdQetRKpOLj+k/QQQ/6Qi0Yz65mlROrfd+Ev+1+7dz9Kw==" + }, + "node_modules/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==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/kleur": { + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/kleur/-/kleur-4.1.5.tgz", + "integrity": "sha512-o+NO+8WrRiQEE4/7nwRJhN1HWpVmJm511pBHUxPLtp0BUISzlBplORYSmTclCnJvQq2tKu/sgl3xVpkc7ZWuQQ==", + "engines": { + "node": ">=6" + } + }, + "node_modules/layout-base": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/layout-base/-/layout-base-1.0.2.tgz", + "integrity": "sha512-8h2oVEZNktL4BH2JCOI90iD1yXwL6iNW7KcCKT2QZgQJR2vbqDsldCTPRU9NifTCqHZci57XvQQ15YTu+sTYPg==" + }, + "node_modules/lilconfig": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-2.1.0.tgz", + "integrity": "sha512-utWOt/GHzuUxnLKxB6dk81RoOeoNeHgbrXiuGk4yyF5qlRz+iIVWu56E2fqGHFrXz0QNUhLB/8nKqvRH66JKGQ==", + "dev": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/lines-and-columns": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", + "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", + "dev": true + }, + "node_modules/lodash-es": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash-es/-/lodash-es-4.17.21.tgz", + "integrity": "sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw==" + }, + "node_modules/lodash.get": { + "version": "4.4.2", + "resolved": "https://registry.npmjs.org/lodash.get/-/lodash.get-4.4.2.tgz", + "integrity": "sha512-z+Uw/vLuy6gQe8cfaFWD7p0wVv8fJl3mbzXh33RS+0oW2wvUqiRXiQ69gLWSLpgB5/6sU+r6BlQR0MBILadqTQ==" + }, + "node_modules/longest-streak": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/longest-streak/-/longest-streak-3.1.0.tgz", + "integrity": "sha512-9Ri+o0JYgehTaVBBDoMqIl8GXtbWg711O3srftcHhZ0dqnETqLaoIK0x17fUw9rFSlK/0NlsKe0Ahhyl5pXE2g==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/loose-envify": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", + "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", + "dependencies": { + "js-tokens": "^3.0.0 || ^4.0.0" + }, + "bin": { + "loose-envify": "cli.js" + } + }, + "node_modules/lru-cache": { + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.1.5.tgz", + "integrity": "sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g==", + "dependencies": { + "pseudomap": "^1.0.2", + "yallist": "^2.1.2" + } + }, + "node_modules/markdown-extensions": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/markdown-extensions/-/markdown-extensions-1.1.1.tgz", + "integrity": "sha512-WWC0ZuMzCyDHYCasEGs4IPvLyTGftYwh6wIEOULOF0HXcqZlhwRzrK0w2VUlxWA98xnvb/jszw4ZSkJ6ADpM6Q==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/markdown-table": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/markdown-table/-/markdown-table-3.0.3.tgz", + "integrity": "sha512-Z1NL3Tb1M9wH4XESsCDEksWoKTdlUafKc4pt0GRwjUyXaCFZ+dc3g2erqB6zm3szA2IUSi7VnPI+o/9jnxh9hw==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/match-sorter": { + "version": "6.3.4", + "resolved": "https://registry.npmjs.org/match-sorter/-/match-sorter-6.3.4.tgz", + "integrity": "sha512-jfZW7cWS5y/1xswZo8VBOdudUiSd9nifYRWphc9M5D/ee4w4AoXLgBEdRbgVaxbMuagBPeUC5y2Hi8DO6o9aDg==", + "dependencies": { + "@babel/runtime": "^7.23.8", + "remove-accents": "0.5.0" + } + }, + "node_modules/mdast-util-definitions": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/mdast-util-definitions/-/mdast-util-definitions-5.1.2.tgz", + "integrity": "sha512-8SVPMuHqlPME/z3gqVwWY4zVXn8lqKv/pAhC57FuJ40ImXyBpmO5ukh98zB2v7Blql2FiHjHv9LVztSIqjY+MA==", + "dependencies": { + "@types/mdast": "^3.0.0", + "@types/unist": "^2.0.0", + "unist-util-visit": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-definitions/node_modules/unist-util-visit": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/unist-util-visit/-/unist-util-visit-4.1.2.tgz", + "integrity": "sha512-MSd8OUGISqHdVvfY9TPhyK2VdUrPgxkUtWSuMHF6XAAFuL4LokseigBnZtPnJMu+FbynTkFNnFlyjxpVKujMRg==", + "dependencies": { + "@types/unist": "^2.0.0", + "unist-util-is": "^5.0.0", + "unist-util-visit-parents": "^5.1.1" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-definitions/node_modules/unist-util-visit-parents": { + "version": "5.1.3", + "resolved": "https://registry.npmjs.org/unist-util-visit-parents/-/unist-util-visit-parents-5.1.3.tgz", + "integrity": "sha512-x6+y8g7wWMyQhL1iZfhIPhDAs7Xwbn9nRosDXl7qoPTSCy0yNxnKc+hWokFifWQIDGi154rdUqKvbCa4+1kLhg==", + "dependencies": { + "@types/unist": "^2.0.0", + "unist-util-is": "^5.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-find-and-replace": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/mdast-util-find-and-replace/-/mdast-util-find-and-replace-2.2.2.tgz", + "integrity": "sha512-MTtdFRz/eMDHXzeK6W3dO7mXUlF82Gom4y0oOgvHhh/HXZAGvIQDUvQ0SuUx+j2tv44b8xTHOm8K/9OoRFnXKw==", + "dependencies": { + "@types/mdast": "^3.0.0", + "escape-string-regexp": "^5.0.0", + "unist-util-is": "^5.0.0", + "unist-util-visit-parents": "^5.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-find-and-replace/node_modules/unist-util-visit-parents": { + "version": "5.1.3", + "resolved": "https://registry.npmjs.org/unist-util-visit-parents/-/unist-util-visit-parents-5.1.3.tgz", + "integrity": "sha512-x6+y8g7wWMyQhL1iZfhIPhDAs7Xwbn9nRosDXl7qoPTSCy0yNxnKc+hWokFifWQIDGi154rdUqKvbCa4+1kLhg==", + "dependencies": { + "@types/unist": "^2.0.0", + "unist-util-is": "^5.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-from-markdown": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/mdast-util-from-markdown/-/mdast-util-from-markdown-1.3.1.tgz", + "integrity": "sha512-4xTO/M8c82qBcnQc1tgpNtubGUW/Y1tBQ1B0i5CtSoelOLKFYlElIr3bvgREYYO5iRqbMY1YuqZng0GVOI8Qww==", + "dependencies": { + "@types/mdast": "^3.0.0", + "@types/unist": "^2.0.0", + "decode-named-character-reference": "^1.0.0", + "mdast-util-to-string": "^3.1.0", + "micromark": "^3.0.0", + "micromark-util-decode-numeric-character-reference": "^1.0.0", + "micromark-util-decode-string": "^1.0.0", + "micromark-util-normalize-identifier": "^1.0.0", + "micromark-util-symbol": "^1.0.0", + "micromark-util-types": "^1.0.0", + "unist-util-stringify-position": "^3.0.0", + "uvu": "^0.5.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-gfm": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/mdast-util-gfm/-/mdast-util-gfm-2.0.2.tgz", + "integrity": "sha512-qvZ608nBppZ4icQlhQQIAdc6S3Ffj9RGmzwUKUWuEICFnd1LVkN3EktF7ZHAgfcEdvZB5owU9tQgt99e2TlLjg==", + "dependencies": { + "mdast-util-from-markdown": "^1.0.0", + "mdast-util-gfm-autolink-literal": "^1.0.0", + "mdast-util-gfm-footnote": "^1.0.0", + "mdast-util-gfm-strikethrough": "^1.0.0", + "mdast-util-gfm-table": "^1.0.0", + "mdast-util-gfm-task-list-item": "^1.0.0", + "mdast-util-to-markdown": "^1.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-gfm-autolink-literal": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/mdast-util-gfm-autolink-literal/-/mdast-util-gfm-autolink-literal-1.0.3.tgz", + "integrity": "sha512-My8KJ57FYEy2W2LyNom4n3E7hKTuQk/0SES0u16tjA9Z3oFkF4RrC/hPAPgjlSpezsOvI8ObcXcElo92wn5IGA==", + "dependencies": { + "@types/mdast": "^3.0.0", + "ccount": "^2.0.0", + "mdast-util-find-and-replace": "^2.0.0", + "micromark-util-character": "^1.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-gfm-footnote": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/mdast-util-gfm-footnote/-/mdast-util-gfm-footnote-1.0.2.tgz", + "integrity": "sha512-56D19KOGbE00uKVj3sgIykpwKL179QsVFwx/DCW0u/0+URsryacI4MAdNJl0dh+u2PSsD9FtxPFbHCzJ78qJFQ==", + "dependencies": { + "@types/mdast": "^3.0.0", + "mdast-util-to-markdown": "^1.3.0", + "micromark-util-normalize-identifier": "^1.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-gfm-strikethrough": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/mdast-util-gfm-strikethrough/-/mdast-util-gfm-strikethrough-1.0.3.tgz", + "integrity": "sha512-DAPhYzTYrRcXdMjUtUjKvW9z/FNAMTdU0ORyMcbmkwYNbKocDpdk+PX1L1dQgOID/+vVs1uBQ7ElrBQfZ0cuiQ==", + "dependencies": { + "@types/mdast": "^3.0.0", + "mdast-util-to-markdown": "^1.3.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-gfm-table": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/mdast-util-gfm-table/-/mdast-util-gfm-table-1.0.7.tgz", + "integrity": "sha512-jjcpmNnQvrmN5Vx7y7lEc2iIOEytYv7rTvu+MeyAsSHTASGCCRA79Igg2uKssgOs1i1po8s3plW0sTu1wkkLGg==", + "dependencies": { + "@types/mdast": "^3.0.0", + "markdown-table": "^3.0.0", + "mdast-util-from-markdown": "^1.0.0", + "mdast-util-to-markdown": "^1.3.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-gfm-task-list-item": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/mdast-util-gfm-task-list-item/-/mdast-util-gfm-task-list-item-1.0.2.tgz", + "integrity": "sha512-PFTA1gzfp1B1UaiJVyhJZA1rm0+Tzn690frc/L8vNX1Jop4STZgOE6bxUhnzdVSB+vm2GU1tIsuQcA9bxTQpMQ==", + "dependencies": { + "@types/mdast": "^3.0.0", + "mdast-util-to-markdown": "^1.3.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-math": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/mdast-util-math/-/mdast-util-math-2.0.2.tgz", + "integrity": "sha512-8gmkKVp9v6+Tgjtq6SYx9kGPpTf6FVYRa53/DLh479aldR9AyP48qeVOgNZ5X7QUK7nOy4yw7vg6mbiGcs9jWQ==", + "dependencies": { + "@types/mdast": "^3.0.0", + "longest-streak": "^3.0.0", + "mdast-util-to-markdown": "^1.3.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-mdx": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/mdast-util-mdx/-/mdast-util-mdx-2.0.1.tgz", + "integrity": "sha512-38w5y+r8nyKlGvNjSEqWrhG0w5PmnRA+wnBvm+ulYCct7nsGYhFVb0lljS9bQav4psDAS1eGkP2LMVcZBi/aqw==", + "dependencies": { + "mdast-util-from-markdown": "^1.0.0", + "mdast-util-mdx-expression": "^1.0.0", + "mdast-util-mdx-jsx": "^2.0.0", + "mdast-util-mdxjs-esm": "^1.0.0", + "mdast-util-to-markdown": "^1.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-mdx-expression": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/mdast-util-mdx-expression/-/mdast-util-mdx-expression-1.3.2.tgz", + "integrity": "sha512-xIPmR5ReJDu/DHH1OoIT1HkuybIfRGYRywC+gJtI7qHjCJp/M9jrmBEJW22O8lskDWm562BX2W8TiAwRTb0rKA==", + "dependencies": { + "@types/estree-jsx": "^1.0.0", + "@types/hast": "^2.0.0", + "@types/mdast": "^3.0.0", + "mdast-util-from-markdown": "^1.0.0", + "mdast-util-to-markdown": "^1.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-mdx-jsx": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/mdast-util-mdx-jsx/-/mdast-util-mdx-jsx-2.1.4.tgz", + "integrity": "sha512-DtMn9CmVhVzZx3f+optVDF8yFgQVt7FghCRNdlIaS3X5Bnym3hZwPbg/XW86vdpKjlc1PVj26SpnLGeJBXD3JA==", + "dependencies": { + "@types/estree-jsx": "^1.0.0", + "@types/hast": "^2.0.0", + "@types/mdast": "^3.0.0", + "@types/unist": "^2.0.0", + "ccount": "^2.0.0", + "mdast-util-from-markdown": "^1.1.0", + "mdast-util-to-markdown": "^1.3.0", + "parse-entities": "^4.0.0", + "stringify-entities": "^4.0.0", + "unist-util-remove-position": "^4.0.0", + "unist-util-stringify-position": "^3.0.0", + "vfile-message": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-mdx-jsx/node_modules/unist-util-remove-position": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/unist-util-remove-position/-/unist-util-remove-position-4.0.2.tgz", + "integrity": "sha512-TkBb0HABNmxzAcfLf4qsIbFbaPDvMO6wa3b3j4VcEzFVaw1LBKwnW4/sRJ/atSLSzoIg41JWEdnE7N6DIhGDGQ==", + "dependencies": { + "@types/unist": "^2.0.0", + "unist-util-visit": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-mdx-jsx/node_modules/unist-util-visit": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/unist-util-visit/-/unist-util-visit-4.1.2.tgz", + "integrity": "sha512-MSd8OUGISqHdVvfY9TPhyK2VdUrPgxkUtWSuMHF6XAAFuL4LokseigBnZtPnJMu+FbynTkFNnFlyjxpVKujMRg==", + "dependencies": { + "@types/unist": "^2.0.0", + "unist-util-is": "^5.0.0", + "unist-util-visit-parents": "^5.1.1" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-mdx-jsx/node_modules/unist-util-visit-parents": { + "version": "5.1.3", + "resolved": "https://registry.npmjs.org/unist-util-visit-parents/-/unist-util-visit-parents-5.1.3.tgz", + "integrity": "sha512-x6+y8g7wWMyQhL1iZfhIPhDAs7Xwbn9nRosDXl7qoPTSCy0yNxnKc+hWokFifWQIDGi154rdUqKvbCa4+1kLhg==", + "dependencies": { + "@types/unist": "^2.0.0", + "unist-util-is": "^5.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-mdx-jsx/node_modules/vfile-message": { + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/vfile-message/-/vfile-message-3.1.4.tgz", + "integrity": "sha512-fa0Z6P8HUrQN4BZaX05SIVXic+7kE3b05PWAtPuYP9QLHsLKYR7/AlLW3NtOrpXRLeawpDLMsVkmk5DG0NXgWw==", + "dependencies": { + "@types/unist": "^2.0.0", + "unist-util-stringify-position": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-mdxjs-esm": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/mdast-util-mdxjs-esm/-/mdast-util-mdxjs-esm-1.3.1.tgz", + "integrity": "sha512-SXqglS0HrEvSdUEfoXFtcg7DRl7S2cwOXc7jkuusG472Mmjag34DUDeOJUZtl+BVnyeO1frIgVpHlNRWc2gk/w==", + "dependencies": { + "@types/estree-jsx": "^1.0.0", + "@types/hast": "^2.0.0", + "@types/mdast": "^3.0.0", + "mdast-util-from-markdown": "^1.0.0", + "mdast-util-to-markdown": "^1.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-phrasing": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/mdast-util-phrasing/-/mdast-util-phrasing-3.0.1.tgz", + "integrity": "sha512-WmI1gTXUBJo4/ZmSk79Wcb2HcjPJBzM1nlI/OUWA8yk2X9ik3ffNbBGsU+09BFmXaL1IBb9fiuvq6/KMiNycSg==", + "dependencies": { + "@types/mdast": "^3.0.0", + "unist-util-is": "^5.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-to-hast": { + "version": "13.2.0", + "resolved": "https://registry.npmjs.org/mdast-util-to-hast/-/mdast-util-to-hast-13.2.0.tgz", + "integrity": "sha512-QGYKEuUsYT9ykKBCMOEDLsU5JRObWQusAolFMeko/tYPufNkRffBAQjIE+99jbA87xv6FgmjLtwjh9wBWajwAA==", + "dependencies": { + "@types/hast": "^3.0.0", + "@types/mdast": "^4.0.0", + "@ungap/structured-clone": "^1.0.0", + "devlop": "^1.0.0", + "micromark-util-sanitize-uri": "^2.0.0", + "trim-lines": "^3.0.0", + "unist-util-position": "^5.0.0", + "unist-util-visit": "^5.0.0", + "vfile": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-to-hast/node_modules/@types/hast": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@types/hast/-/hast-3.0.4.tgz", + "integrity": "sha512-WPs+bbQw5aCj+x6laNGWLH3wviHtoCv/P3+otBhbOhJgG8qtpdAMlTCxLtsTWA7LH1Oh/bFCHsBn0TPS5m30EQ==", + "dependencies": { + "@types/unist": "*" + } + }, + "node_modules/mdast-util-to-hast/node_modules/@types/mdast": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/@types/mdast/-/mdast-4.0.4.tgz", + "integrity": "sha512-kGaNbPh1k7AFzgpud/gMdvIm5xuECykRR+JnWKQno9TAXVa6WIVCGTPvYGekIDL4uwCZQSYbUxNBSb1aUo79oA==", + "dependencies": { + "@types/unist": "*" + } + }, + "node_modules/mdast-util-to-hast/node_modules/@types/unist": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/@types/unist/-/unist-3.0.2.tgz", + "integrity": "sha512-dqId9J8K/vGi5Zr7oo212BGii5m3q5Hxlkwy3WpYuKPklmBEvsbMYYyLxAQpSffdLl/gdW0XUpKWFvYmyoWCoQ==" + }, + "node_modules/mdast-util-to-hast/node_modules/micromark-util-character": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/micromark-util-character/-/micromark-util-character-2.1.0.tgz", + "integrity": "sha512-KvOVV+X1yLBfs9dCBSopq/+G1PcgT3lAK07mC4BzXi5E7ahzMAF8oIupDDJ6mievI6F+lAATkbQQlQixJfT3aQ==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/mdast-util-to-hast/node_modules/micromark-util-encode": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-util-encode/-/micromark-util-encode-2.0.0.tgz", + "integrity": "sha512-pS+ROfCXAGLWCOc8egcBvT0kf27GoWMqtdarNfDcjb6YLuV5cM3ioG45Ys2qOVqeqSbjaKg72vU+Wby3eddPsA==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ] + }, + "node_modules/mdast-util-to-hast/node_modules/micromark-util-sanitize-uri": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-util-sanitize-uri/-/micromark-util-sanitize-uri-2.0.0.tgz", + "integrity": "sha512-WhYv5UEcZrbAtlsnPuChHUAsu/iBPOVaEVsntLBIdpibO0ddy8OzavZz3iL2xVvBZOpolujSliP65Kq0/7KIYw==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "micromark-util-character": "^2.0.0", + "micromark-util-encode": "^2.0.0", + "micromark-util-symbol": "^2.0.0" + } + }, + "node_modules/mdast-util-to-hast/node_modules/micromark-util-symbol": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-2.0.0.tgz", + "integrity": "sha512-8JZt9ElZ5kyTnO94muPxIGS8oyElRJaiJO8EzV6ZSyGQ1Is8xwl4Q45qU5UOg+bGH4AikWziz0iN4sFLWs8PGw==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ] + }, + "node_modules/mdast-util-to-hast/node_modules/micromark-util-types": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-util-types/-/micromark-util-types-2.0.0.tgz", + "integrity": "sha512-oNh6S2WMHWRZrmutsRmDDfkzKtxF+bc2VxLC9dvtrDIRFln627VsFP6fLMgTryGDljgLPjkrzQSDcPrjPyDJ5w==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ] + }, + "node_modules/mdast-util-to-hast/node_modules/unist-util-position": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/unist-util-position/-/unist-util-position-5.0.0.tgz", + "integrity": "sha512-fucsC7HjXvkB5R3kTCO7kUjRdrS0BJt3M/FPxmHMBOm8JQi2BsHAHFsy27E0EolP8rp0NzXsJ+jNPyDWvOJZPA==", + "dependencies": { + "@types/unist": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-to-hast/node_modules/unist-util-stringify-position": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/unist-util-stringify-position/-/unist-util-stringify-position-4.0.0.tgz", + "integrity": "sha512-0ASV06AAoKCDkS2+xw5RXJywruurpbC4JZSm7nr7MOt1ojAzvyyaO+UxZf18j8FCF6kmzCZKcAgN/yu2gm2XgQ==", + "dependencies": { + "@types/unist": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-to-hast/node_modules/vfile": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/vfile/-/vfile-6.0.1.tgz", + "integrity": "sha512-1bYqc7pt6NIADBJ98UiG0Bn/CHIVOoZ/IyEkqIruLg0mE1BKzkOXY2D6CSqQIcKqgadppE5lrxgWXJmXd7zZJw==", + "dependencies": { + "@types/unist": "^3.0.0", + "unist-util-stringify-position": "^4.0.0", + "vfile-message": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-to-markdown": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/mdast-util-to-markdown/-/mdast-util-to-markdown-1.5.0.tgz", + "integrity": "sha512-bbv7TPv/WC49thZPg3jXuqzuvI45IL2EVAr/KxF0BSdHsU0ceFHOmwQn6evxAh1GaoK/6GQ1wp4R4oW2+LFL/A==", + "dependencies": { + "@types/mdast": "^3.0.0", + "@types/unist": "^2.0.0", + "longest-streak": "^3.0.0", + "mdast-util-phrasing": "^3.0.0", + "mdast-util-to-string": "^3.0.0", + "micromark-util-decode-string": "^1.0.0", + "unist-util-visit": "^4.0.0", + "zwitch": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-to-markdown/node_modules/unist-util-visit": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/unist-util-visit/-/unist-util-visit-4.1.2.tgz", + "integrity": "sha512-MSd8OUGISqHdVvfY9TPhyK2VdUrPgxkUtWSuMHF6XAAFuL4LokseigBnZtPnJMu+FbynTkFNnFlyjxpVKujMRg==", + "dependencies": { + "@types/unist": "^2.0.0", + "unist-util-is": "^5.0.0", + "unist-util-visit-parents": "^5.1.1" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-to-markdown/node_modules/unist-util-visit-parents": { + "version": "5.1.3", + "resolved": "https://registry.npmjs.org/unist-util-visit-parents/-/unist-util-visit-parents-5.1.3.tgz", + "integrity": "sha512-x6+y8g7wWMyQhL1iZfhIPhDAs7Xwbn9nRosDXl7qoPTSCy0yNxnKc+hWokFifWQIDGi154rdUqKvbCa4+1kLhg==", + "dependencies": { + "@types/unist": "^2.0.0", + "unist-util-is": "^5.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-to-string": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/mdast-util-to-string/-/mdast-util-to-string-3.2.0.tgz", + "integrity": "sha512-V4Zn/ncyN1QNSqSBxTrMOLpjr+IKdHl2v3KVLoWmDPscP4r9GcCi71gjgvUV1SFSKh92AjAG4peFuBl2/YgCJg==", + "dependencies": { + "@types/mdast": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/merge2": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", + "dev": true, + "engines": { + "node": ">= 8" + } + }, + "node_modules/mermaid": { + "version": "10.9.1", + "resolved": "https://registry.npmjs.org/mermaid/-/mermaid-10.9.1.tgz", + "integrity": "sha512-Mx45Obds5W1UkW1nv/7dHRsbfMM1aOKA2+Pxs/IGHNonygDHwmng8xTHyS9z4KWVi0rbko8gjiBmuwwXQ7tiNA==", + "dependencies": { + "@braintree/sanitize-url": "^6.0.1", + "@types/d3-scale": "^4.0.3", + "@types/d3-scale-chromatic": "^3.0.0", + "cytoscape": "^3.28.1", + "cytoscape-cose-bilkent": "^4.1.0", + "d3": "^7.4.0", + "d3-sankey": "^0.12.3", + "dagre-d3-es": "7.0.10", + "dayjs": "^1.11.7", + "dompurify": "^3.0.5", + "elkjs": "^0.9.0", + "katex": "^0.16.9", + "khroma": "^2.0.0", + "lodash-es": "^4.17.21", + "mdast-util-from-markdown": "^1.3.0", + "non-layered-tidy-tree-layout": "^2.0.2", + "stylis": "^4.1.3", + "ts-dedent": "^2.2.0", + "uuid": "^9.0.0", + "web-worker": "^1.2.0" + } + }, + "node_modules/micromark": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/micromark/-/micromark-3.2.0.tgz", + "integrity": "sha512-uD66tJj54JLYq0De10AhWycZWGQNUvDI55xPgk2sQM5kn1JYlhbCMTtEeT27+vAhW2FBQxLlOmS3pmA7/2z4aA==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "@types/debug": "^4.0.0", + "debug": "^4.0.0", + "decode-named-character-reference": "^1.0.0", + "micromark-core-commonmark": "^1.0.1", + "micromark-factory-space": "^1.0.0", + "micromark-util-character": "^1.0.0", + "micromark-util-chunked": "^1.0.0", + "micromark-util-combine-extensions": "^1.0.0", + "micromark-util-decode-numeric-character-reference": "^1.0.0", + "micromark-util-encode": "^1.0.0", + "micromark-util-normalize-identifier": "^1.0.0", + "micromark-util-resolve-all": "^1.0.0", + "micromark-util-sanitize-uri": "^1.0.0", + "micromark-util-subtokenize": "^1.0.0", + "micromark-util-symbol": "^1.0.0", + "micromark-util-types": "^1.0.1", + "uvu": "^0.5.0" + } + }, + "node_modules/micromark-core-commonmark": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/micromark-core-commonmark/-/micromark-core-commonmark-1.1.0.tgz", + "integrity": "sha512-BgHO1aRbolh2hcrzL2d1La37V0Aoz73ymF8rAcKnohLy93titmv62E0gP8Hrx9PKcKrqCZ1BbLGbP3bEhoXYlw==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "decode-named-character-reference": "^1.0.0", + "micromark-factory-destination": "^1.0.0", + "micromark-factory-label": "^1.0.0", + "micromark-factory-space": "^1.0.0", + "micromark-factory-title": "^1.0.0", + "micromark-factory-whitespace": "^1.0.0", + "micromark-util-character": "^1.0.0", + "micromark-util-chunked": "^1.0.0", + "micromark-util-classify-character": "^1.0.0", + "micromark-util-html-tag-name": "^1.0.0", + "micromark-util-normalize-identifier": "^1.0.0", + "micromark-util-resolve-all": "^1.0.0", + "micromark-util-subtokenize": "^1.0.0", + "micromark-util-symbol": "^1.0.0", + "micromark-util-types": "^1.0.1", + "uvu": "^0.5.0" + } + }, + "node_modules/micromark-extension-gfm": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/micromark-extension-gfm/-/micromark-extension-gfm-2.0.3.tgz", + "integrity": "sha512-vb9OoHqrhCmbRidQv/2+Bc6pkP0FrtlhurxZofvOEy5o8RtuuvTq+RQ1Vw5ZDNrVraQZu3HixESqbG+0iKk/MQ==", + "dependencies": { + "micromark-extension-gfm-autolink-literal": "^1.0.0", + "micromark-extension-gfm-footnote": "^1.0.0", + "micromark-extension-gfm-strikethrough": "^1.0.0", + "micromark-extension-gfm-table": "^1.0.0", + "micromark-extension-gfm-tagfilter": "^1.0.0", + "micromark-extension-gfm-task-list-item": "^1.0.0", + "micromark-util-combine-extensions": "^1.0.0", + "micromark-util-types": "^1.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/micromark-extension-gfm-autolink-literal": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/micromark-extension-gfm-autolink-literal/-/micromark-extension-gfm-autolink-literal-1.0.5.tgz", + "integrity": "sha512-z3wJSLrDf8kRDOh2qBtoTRD53vJ+CWIyo7uyZuxf/JAbNJjiHsOpG1y5wxk8drtv3ETAHutCu6N3thkOOgueWg==", + "dependencies": { + "micromark-util-character": "^1.0.0", + "micromark-util-sanitize-uri": "^1.0.0", + "micromark-util-symbol": "^1.0.0", + "micromark-util-types": "^1.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/micromark-extension-gfm-footnote": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/micromark-extension-gfm-footnote/-/micromark-extension-gfm-footnote-1.1.2.tgz", + "integrity": "sha512-Yxn7z7SxgyGWRNa4wzf8AhYYWNrwl5q1Z8ii+CSTTIqVkmGZF1CElX2JI8g5yGoM3GAman9/PVCUFUSJ0kB/8Q==", + "dependencies": { + "micromark-core-commonmark": "^1.0.0", + "micromark-factory-space": "^1.0.0", + "micromark-util-character": "^1.0.0", + "micromark-util-normalize-identifier": "^1.0.0", + "micromark-util-sanitize-uri": "^1.0.0", + "micromark-util-symbol": "^1.0.0", + "micromark-util-types": "^1.0.0", + "uvu": "^0.5.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/micromark-extension-gfm-strikethrough": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/micromark-extension-gfm-strikethrough/-/micromark-extension-gfm-strikethrough-1.0.7.tgz", + "integrity": "sha512-sX0FawVE1o3abGk3vRjOH50L5TTLr3b5XMqnP9YDRb34M0v5OoZhG+OHFz1OffZ9dlwgpTBKaT4XW/AsUVnSDw==", + "dependencies": { + "micromark-util-chunked": "^1.0.0", + "micromark-util-classify-character": "^1.0.0", + "micromark-util-resolve-all": "^1.0.0", + "micromark-util-symbol": "^1.0.0", + "micromark-util-types": "^1.0.0", + "uvu": "^0.5.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/micromark-extension-gfm-table": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/micromark-extension-gfm-table/-/micromark-extension-gfm-table-1.0.7.tgz", + "integrity": "sha512-3ZORTHtcSnMQEKtAOsBQ9/oHp9096pI/UvdPtN7ehKvrmZZ2+bbWhi0ln+I9drmwXMt5boocn6OlwQzNXeVeqw==", + "dependencies": { + "micromark-factory-space": "^1.0.0", + "micromark-util-character": "^1.0.0", + "micromark-util-symbol": "^1.0.0", + "micromark-util-types": "^1.0.0", + "uvu": "^0.5.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/micromark-extension-gfm-tagfilter": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/micromark-extension-gfm-tagfilter/-/micromark-extension-gfm-tagfilter-1.0.2.tgz", + "integrity": "sha512-5XWB9GbAUSHTn8VPU8/1DBXMuKYT5uOgEjJb8gN3mW0PNW5OPHpSdojoqf+iq1xo7vWzw/P8bAHY0n6ijpXF7g==", + "dependencies": { + "micromark-util-types": "^1.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/micromark-extension-gfm-task-list-item": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/micromark-extension-gfm-task-list-item/-/micromark-extension-gfm-task-list-item-1.0.5.tgz", + "integrity": "sha512-RMFXl2uQ0pNQy6Lun2YBYT9g9INXtWJULgbt01D/x8/6yJ2qpKyzdZD3pi6UIkzF++Da49xAelVKUeUMqd5eIQ==", + "dependencies": { + "micromark-factory-space": "^1.0.0", + "micromark-util-character": "^1.0.0", + "micromark-util-symbol": "^1.0.0", + "micromark-util-types": "^1.0.0", + "uvu": "^0.5.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/micromark-extension-math": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/micromark-extension-math/-/micromark-extension-math-2.1.2.tgz", + "integrity": "sha512-es0CcOV89VNS9wFmyn+wyFTKweXGW4CEvdaAca6SWRWPyYCbBisnjaHLjWO4Nszuiud84jCpkHsqAJoa768Pvg==", + "dependencies": { + "@types/katex": "^0.16.0", + "katex": "^0.16.0", + "micromark-factory-space": "^1.0.0", + "micromark-util-character": "^1.0.0", + "micromark-util-symbol": "^1.0.0", + "micromark-util-types": "^1.0.0", + "uvu": "^0.5.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/micromark-extension-mdx-expression": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/micromark-extension-mdx-expression/-/micromark-extension-mdx-expression-1.0.8.tgz", + "integrity": "sha512-zZpeQtc5wfWKdzDsHRBY003H2Smg+PUi2REhqgIhdzAa5xonhP03FcXxqFSerFiNUr5AWmHpaNPQTBVOS4lrXw==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "@types/estree": "^1.0.0", + "micromark-factory-mdx-expression": "^1.0.0", + "micromark-factory-space": "^1.0.0", + "micromark-util-character": "^1.0.0", + "micromark-util-events-to-acorn": "^1.0.0", + "micromark-util-symbol": "^1.0.0", + "micromark-util-types": "^1.0.0", + "uvu": "^0.5.0" + } + }, + "node_modules/micromark-extension-mdx-jsx": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/micromark-extension-mdx-jsx/-/micromark-extension-mdx-jsx-1.0.5.tgz", + "integrity": "sha512-gPH+9ZdmDflbu19Xkb8+gheqEDqkSpdCEubQyxuz/Hn8DOXiXvrXeikOoBA71+e8Pfi0/UYmU3wW3H58kr7akA==", + "dependencies": { + "@types/acorn": "^4.0.0", + "@types/estree": "^1.0.0", + "estree-util-is-identifier-name": "^2.0.0", + "micromark-factory-mdx-expression": "^1.0.0", + "micromark-factory-space": "^1.0.0", + "micromark-util-character": "^1.0.0", + "micromark-util-symbol": "^1.0.0", + "micromark-util-types": "^1.0.0", + "uvu": "^0.5.0", + "vfile-message": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/micromark-extension-mdx-jsx/node_modules/vfile-message": { + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/vfile-message/-/vfile-message-3.1.4.tgz", + "integrity": "sha512-fa0Z6P8HUrQN4BZaX05SIVXic+7kE3b05PWAtPuYP9QLHsLKYR7/AlLW3NtOrpXRLeawpDLMsVkmk5DG0NXgWw==", + "dependencies": { + "@types/unist": "^2.0.0", + "unist-util-stringify-position": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/micromark-extension-mdx-md": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/micromark-extension-mdx-md/-/micromark-extension-mdx-md-1.0.1.tgz", + "integrity": "sha512-7MSuj2S7xjOQXAjjkbjBsHkMtb+mDGVW6uI2dBL9snOBCbZmoNgDAeZ0nSn9j3T42UE/g2xVNMn18PJxZvkBEA==", + "dependencies": { + "micromark-util-types": "^1.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/micromark-extension-mdxjs": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/micromark-extension-mdxjs/-/micromark-extension-mdxjs-1.0.1.tgz", + "integrity": "sha512-7YA7hF6i5eKOfFUzZ+0z6avRG52GpWR8DL+kN47y3f2KhxbBZMhmxe7auOeaTBrW2DenbbZTf1ea9tA2hDpC2Q==", + "dependencies": { + "acorn": "^8.0.0", + "acorn-jsx": "^5.0.0", + "micromark-extension-mdx-expression": "^1.0.0", + "micromark-extension-mdx-jsx": "^1.0.0", + "micromark-extension-mdx-md": "^1.0.0", + "micromark-extension-mdxjs-esm": "^1.0.0", + "micromark-util-combine-extensions": "^1.0.0", + "micromark-util-types": "^1.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/micromark-extension-mdxjs-esm": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/micromark-extension-mdxjs-esm/-/micromark-extension-mdxjs-esm-1.0.5.tgz", + "integrity": "sha512-xNRBw4aoURcyz/S69B19WnZAkWJMxHMT5hE36GtDAyhoyn/8TuAeqjFJQlwk+MKQsUD7b3l7kFX+vlfVWgcX1w==", + "dependencies": { + "@types/estree": "^1.0.0", + "micromark-core-commonmark": "^1.0.0", + "micromark-util-character": "^1.0.0", + "micromark-util-events-to-acorn": "^1.0.0", + "micromark-util-symbol": "^1.0.0", + "micromark-util-types": "^1.0.0", + "unist-util-position-from-estree": "^1.1.0", + "uvu": "^0.5.0", + "vfile-message": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/micromark-extension-mdxjs-esm/node_modules/vfile-message": { + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/vfile-message/-/vfile-message-3.1.4.tgz", + "integrity": "sha512-fa0Z6P8HUrQN4BZaX05SIVXic+7kE3b05PWAtPuYP9QLHsLKYR7/AlLW3NtOrpXRLeawpDLMsVkmk5DG0NXgWw==", + "dependencies": { + "@types/unist": "^2.0.0", + "unist-util-stringify-position": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/micromark-factory-destination": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/micromark-factory-destination/-/micromark-factory-destination-1.1.0.tgz", + "integrity": "sha512-XaNDROBgx9SgSChd69pjiGKbV+nfHGDPVYFs5dOoDd7ZnMAE+Cuu91BCpsY8RT2NP9vo/B8pds2VQNCLiu0zhg==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "micromark-util-character": "^1.0.0", + "micromark-util-symbol": "^1.0.0", + "micromark-util-types": "^1.0.0" + } + }, + "node_modules/micromark-factory-label": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/micromark-factory-label/-/micromark-factory-label-1.1.0.tgz", + "integrity": "sha512-OLtyez4vZo/1NjxGhcpDSbHQ+m0IIGnT8BoPamh+7jVlzLJBH98zzuCoUeMxvM6WsNeh8wx8cKvqLiPHEACn0w==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "micromark-util-character": "^1.0.0", + "micromark-util-symbol": "^1.0.0", + "micromark-util-types": "^1.0.0", + "uvu": "^0.5.0" + } + }, + "node_modules/micromark-factory-mdx-expression": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/micromark-factory-mdx-expression/-/micromark-factory-mdx-expression-1.0.9.tgz", + "integrity": "sha512-jGIWzSmNfdnkJq05c7b0+Wv0Kfz3NJ3N4cBjnbO4zjXIlxJr+f8lk+5ZmwFvqdAbUy2q6B5rCY//g0QAAaXDWA==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "@types/estree": "^1.0.0", + "micromark-util-character": "^1.0.0", + "micromark-util-events-to-acorn": "^1.0.0", + "micromark-util-symbol": "^1.0.0", + "micromark-util-types": "^1.0.0", + "unist-util-position-from-estree": "^1.0.0", + "uvu": "^0.5.0", + "vfile-message": "^3.0.0" + } + }, + "node_modules/micromark-factory-mdx-expression/node_modules/vfile-message": { + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/vfile-message/-/vfile-message-3.1.4.tgz", + "integrity": "sha512-fa0Z6P8HUrQN4BZaX05SIVXic+7kE3b05PWAtPuYP9QLHsLKYR7/AlLW3NtOrpXRLeawpDLMsVkmk5DG0NXgWw==", + "dependencies": { + "@types/unist": "^2.0.0", + "unist-util-stringify-position": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/micromark-factory-space": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/micromark-factory-space/-/micromark-factory-space-1.1.0.tgz", + "integrity": "sha512-cRzEj7c0OL4Mw2v6nwzttyOZe8XY/Z8G0rzmWQZTBi/jjwyw/U4uqKtUORXQrR5bAZZnbTI/feRV/R7hc4jQYQ==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "micromark-util-character": "^1.0.0", + "micromark-util-types": "^1.0.0" + } + }, + "node_modules/micromark-factory-title": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/micromark-factory-title/-/micromark-factory-title-1.1.0.tgz", + "integrity": "sha512-J7n9R3vMmgjDOCY8NPw55jiyaQnH5kBdV2/UXCtZIpnHH3P6nHUKaH7XXEYuWwx/xUJcawa8plLBEjMPU24HzQ==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "micromark-factory-space": "^1.0.0", + "micromark-util-character": "^1.0.0", + "micromark-util-symbol": "^1.0.0", + "micromark-util-types": "^1.0.0" + } + }, + "node_modules/micromark-factory-whitespace": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/micromark-factory-whitespace/-/micromark-factory-whitespace-1.1.0.tgz", + "integrity": "sha512-v2WlmiymVSp5oMg+1Q0N1Lxmt6pMhIHD457whWM7/GUlEks1hI9xj5w3zbc4uuMKXGisksZk8DzP2UyGbGqNsQ==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "micromark-factory-space": "^1.0.0", + "micromark-util-character": "^1.0.0", + "micromark-util-symbol": "^1.0.0", + "micromark-util-types": "^1.0.0" + } + }, + "node_modules/micromark-util-character": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/micromark-util-character/-/micromark-util-character-1.2.0.tgz", + "integrity": "sha512-lXraTwcX3yH/vMDaFWCQJP1uIszLVebzUa3ZHdrgxr7KEU/9mL4mVgCpGbyhvNLNlauROiNUq7WN5u7ndbY6xg==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "micromark-util-symbol": "^1.0.0", + "micromark-util-types": "^1.0.0" + } + }, + "node_modules/micromark-util-chunked": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/micromark-util-chunked/-/micromark-util-chunked-1.1.0.tgz", + "integrity": "sha512-Ye01HXpkZPNcV6FiyoW2fGZDUw4Yc7vT0E9Sad83+bEDiCJ1uXu0S3mr8WLpsz3HaG3x2q0HM6CTuPdcZcluFQ==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "micromark-util-symbol": "^1.0.0" + } + }, + "node_modules/micromark-util-classify-character": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/micromark-util-classify-character/-/micromark-util-classify-character-1.1.0.tgz", + "integrity": "sha512-SL0wLxtKSnklKSUplok1WQFoGhUdWYKggKUiqhX+Swala+BtptGCu5iPRc+xvzJ4PXE/hwM3FNXsfEVgoZsWbw==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "micromark-util-character": "^1.0.0", + "micromark-util-symbol": "^1.0.0", + "micromark-util-types": "^1.0.0" + } + }, + "node_modules/micromark-util-combine-extensions": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/micromark-util-combine-extensions/-/micromark-util-combine-extensions-1.1.0.tgz", + "integrity": "sha512-Q20sp4mfNf9yEqDL50WwuWZHUrCO4fEyeDCnMGmG5Pr0Cz15Uo7KBs6jq+dq0EgX4DPwwrh9m0X+zPV1ypFvUA==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "micromark-util-chunked": "^1.0.0", + "micromark-util-types": "^1.0.0" + } + }, + "node_modules/micromark-util-decode-numeric-character-reference": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/micromark-util-decode-numeric-character-reference/-/micromark-util-decode-numeric-character-reference-1.1.0.tgz", + "integrity": "sha512-m9V0ExGv0jB1OT21mrWcuf4QhP46pH1KkfWy9ZEezqHKAxkj4mPCy3nIH1rkbdMlChLHX531eOrymlwyZIf2iw==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "micromark-util-symbol": "^1.0.0" + } + }, + "node_modules/micromark-util-decode-string": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/micromark-util-decode-string/-/micromark-util-decode-string-1.1.0.tgz", + "integrity": "sha512-YphLGCK8gM1tG1bd54azwyrQRjCFcmgj2S2GoJDNnh4vYtnL38JS8M4gpxzOPNyHdNEpheyWXCTnnTDY3N+NVQ==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "decode-named-character-reference": "^1.0.0", + "micromark-util-character": "^1.0.0", + "micromark-util-decode-numeric-character-reference": "^1.0.0", + "micromark-util-symbol": "^1.0.0" + } + }, + "node_modules/micromark-util-encode": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/micromark-util-encode/-/micromark-util-encode-1.1.0.tgz", + "integrity": "sha512-EuEzTWSTAj9PA5GOAs992GzNh2dGQO52UvAbtSOMvXTxv3Criqb6IOzJUBCmEqrrXSblJIJBbFFv6zPxpreiJw==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ] + }, + "node_modules/micromark-util-events-to-acorn": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/micromark-util-events-to-acorn/-/micromark-util-events-to-acorn-1.2.3.tgz", + "integrity": "sha512-ij4X7Wuc4fED6UoLWkmo0xJQhsktfNh1J0m8g4PbIMPlx+ek/4YdW5mvbye8z/aZvAPUoxgXHrwVlXAPKMRp1w==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "@types/acorn": "^4.0.0", + "@types/estree": "^1.0.0", + "@types/unist": "^2.0.0", + "estree-util-visit": "^1.0.0", + "micromark-util-symbol": "^1.0.0", + "micromark-util-types": "^1.0.0", + "uvu": "^0.5.0", + "vfile-message": "^3.0.0" + } + }, + "node_modules/micromark-util-events-to-acorn/node_modules/vfile-message": { + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/vfile-message/-/vfile-message-3.1.4.tgz", + "integrity": "sha512-fa0Z6P8HUrQN4BZaX05SIVXic+7kE3b05PWAtPuYP9QLHsLKYR7/AlLW3NtOrpXRLeawpDLMsVkmk5DG0NXgWw==", + "dependencies": { + "@types/unist": "^2.0.0", + "unist-util-stringify-position": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/micromark-util-html-tag-name": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/micromark-util-html-tag-name/-/micromark-util-html-tag-name-1.2.0.tgz", + "integrity": "sha512-VTQzcuQgFUD7yYztuQFKXT49KghjtETQ+Wv/zUjGSGBioZnkA4P1XXZPT1FHeJA6RwRXSF47yvJ1tsJdoxwO+Q==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ] + }, + "node_modules/micromark-util-normalize-identifier": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/micromark-util-normalize-identifier/-/micromark-util-normalize-identifier-1.1.0.tgz", + "integrity": "sha512-N+w5vhqrBihhjdpM8+5Xsxy71QWqGn7HYNUvch71iV2PM7+E3uWGox1Qp90loa1ephtCxG2ftRV/Conitc6P2Q==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "micromark-util-symbol": "^1.0.0" + } + }, + "node_modules/micromark-util-resolve-all": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/micromark-util-resolve-all/-/micromark-util-resolve-all-1.1.0.tgz", + "integrity": "sha512-b/G6BTMSg+bX+xVCshPTPyAu2tmA0E4X98NSR7eIbeC6ycCqCeE7wjfDIgzEbkzdEVJXRtOG4FbEm/uGbCRouA==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "micromark-util-types": "^1.0.0" + } + }, + "node_modules/micromark-util-sanitize-uri": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/micromark-util-sanitize-uri/-/micromark-util-sanitize-uri-1.2.0.tgz", + "integrity": "sha512-QO4GXv0XZfWey4pYFndLUKEAktKkG5kZTdUNaTAkzbuJxn2tNBOr+QtxR2XpWaMhbImT2dPzyLrPXLlPhph34A==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "micromark-util-character": "^1.0.0", + "micromark-util-encode": "^1.0.0", + "micromark-util-symbol": "^1.0.0" + } + }, + "node_modules/micromark-util-subtokenize": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/micromark-util-subtokenize/-/micromark-util-subtokenize-1.1.0.tgz", + "integrity": "sha512-kUQHyzRoxvZO2PuLzMt2P/dwVsTiivCK8icYTeR+3WgbuPqfHgPPy7nFKbeqRivBvn/3N3GBiNC+JRTMSxEC7A==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "micromark-util-chunked": "^1.0.0", + "micromark-util-symbol": "^1.0.0", + "micromark-util-types": "^1.0.0", + "uvu": "^0.5.0" + } + }, + "node_modules/micromark-util-symbol": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-1.1.0.tgz", + "integrity": "sha512-uEjpEYY6KMs1g7QfJ2eX1SQEV+ZT4rUD3UcF6l57acZvLNK7PBZL+ty82Z1qhK1/yXIY4bdx04FKMgR0g4IAag==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ] + }, + "node_modules/micromark-util-types": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/micromark-util-types/-/micromark-util-types-1.1.0.tgz", + "integrity": "sha512-ukRBgie8TIAcacscVHSiddHjO4k/q3pnedmzMQ4iwDcK0FtFCohKOlFbaOL/mPgfnPsL3C1ZyxJa4sbWrBl3jg==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ] + }, + "node_modules/micromatch": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.7.tgz", + "integrity": "sha512-LPP/3KorzCwBxfeUuZmaR6bG2kdeHSbe0P2tY3FLRU4vYrjYz5hI4QZwV0njUx3jeuKe67YukQ1LSPZBKDqO/Q==", + "dev": true, + "dependencies": { + "braces": "^3.0.3", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/minimatch": { + "version": "9.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.4.tgz", + "integrity": "sha512-KqWh+VchfxcMNRAJjj2tnsSJdNbHsVgnkBhTNrW7AjVo6OvLtxw8zfT9oLw1JSohlFzJ8jCoTgaoXvJ+kHt6fw==", + "dev": true, + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/minipass": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", + "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", + "dev": true, + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, + "node_modules/mri": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/mri/-/mri-1.2.0.tgz", + "integrity": "sha512-tzzskb3bG8LvYGFF/mDTpq3jpI6Q9wc3LEmBaghu+DdCssd1FakN7Bc0hVNmEyGq1bq3RgfkCb3cmQLpNPOroA==", + "engines": { + "node": ">=4" + } + }, + "node_modules/ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + }, + "node_modules/mz": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/mz/-/mz-2.7.0.tgz", + "integrity": "sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==", + "dev": true, + "dependencies": { + "any-promise": "^1.0.0", + "object-assign": "^4.0.1", + "thenify-all": "^1.0.0" + } + }, + "node_modules/nanoid": { + "version": "3.3.7", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.7.tgz", + "integrity": "sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/next": { + "version": "14.2.4", + "resolved": "https://registry.npmjs.org/next/-/next-14.2.4.tgz", + "integrity": "sha512-R8/V7vugY+822rsQGQCjoLhMuC9oFj9SOi4Cl4b2wjDrseD0LRZ10W7R6Czo4w9ZznVSshKjuIomsRjvm9EKJQ==", + "dependencies": { + "@next/env": "14.2.4", + "@swc/helpers": "0.5.5", + "busboy": "1.6.0", + "caniuse-lite": "^1.0.30001579", + "graceful-fs": "^4.2.11", + "postcss": "8.4.31", + "styled-jsx": "5.1.1" + }, + "bin": { + "next": "dist/bin/next" + }, + "engines": { + "node": ">=18.17.0" + }, + "optionalDependencies": { + "@next/swc-darwin-arm64": "14.2.4", + "@next/swc-darwin-x64": "14.2.4", + "@next/swc-linux-arm64-gnu": "14.2.4", + "@next/swc-linux-arm64-musl": "14.2.4", + "@next/swc-linux-x64-gnu": "14.2.4", + "@next/swc-linux-x64-musl": "14.2.4", + "@next/swc-win32-arm64-msvc": "14.2.4", + "@next/swc-win32-ia32-msvc": "14.2.4", + "@next/swc-win32-x64-msvc": "14.2.4" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.1.0", + "@playwright/test": "^1.41.2", + "react": "^18.2.0", + "react-dom": "^18.2.0", + "sass": "^1.3.0" + }, + "peerDependenciesMeta": { + "@opentelemetry/api": { + "optional": true + }, + "@playwright/test": { + "optional": true + }, + "sass": { + "optional": true + } + } + }, + "node_modules/next-mdx-remote": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/next-mdx-remote/-/next-mdx-remote-4.4.1.tgz", + "integrity": "sha512-1BvyXaIou6xy3XoNF4yaMZUCb6vD2GTAa5ciOa6WoO+gAUTYsb1K4rI/HSC2ogAWLrb/7VSV52skz07vOzmqIQ==", + "dependencies": { + "@mdx-js/mdx": "^2.2.1", + "@mdx-js/react": "^2.2.1", + "vfile": "^5.3.0", + "vfile-matter": "^3.0.1" + }, + "engines": { + "node": ">=14", + "npm": ">=7" + }, + "peerDependencies": { + "react": ">=16.x <=18.x", + "react-dom": ">=16.x <=18.x" + } + }, + "node_modules/next-seo": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/next-seo/-/next-seo-6.5.0.tgz", + "integrity": "sha512-MfzUeWTN/x/rsKp/1n0213eojO97lIl0unxqbeCY+6pAucViHDA8GSLRRcXpgjsSmBxfCFdfpu7LXbt4ANQoNQ==", + "peerDependencies": { + "next": "^8.1.1-canary.54 || >=9.0.0", + "react": ">=16.0.0", + "react-dom": ">=16.0.0" + } + }, + "node_modules/next-themes": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/next-themes/-/next-themes-0.2.1.tgz", + "integrity": "sha512-B+AKNfYNIzh0vqQQKqQItTS8evEouKD7H5Hj3kmuPERwddR2TxvDSFZuTj6T7Jfn1oyeUyJMydPl1Bkxkh0W7A==", + "peerDependencies": { + "next": "*", + "react": "*", + "react-dom": "*" + } + }, + "node_modules/next/node_modules/postcss": { + "version": "8.4.31", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.31.tgz", + "integrity": "sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "dependencies": { + "nanoid": "^3.3.6", + "picocolors": "^1.0.0", + "source-map-js": "^1.0.2" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/nextra": { + "version": "2.13.4", + "resolved": "https://registry.npmjs.org/nextra/-/nextra-2.13.4.tgz", + "integrity": "sha512-7of2rSBxuUa3+lbMmZwG9cqgftcoNOVQLTT6Rxf3EhBR9t1EI7b43dted8YoqSNaigdE3j1CoyNkX8N/ZzlEpw==", + "dependencies": { + "@headlessui/react": "^1.7.17", + "@mdx-js/mdx": "^2.3.0", + "@mdx-js/react": "^2.3.0", + "@napi-rs/simple-git": "^0.1.9", + "@theguild/remark-mermaid": "^0.0.5", + "@theguild/remark-npm2yarn": "^0.2.0", + "clsx": "^2.0.0", + "github-slugger": "^2.0.0", + "graceful-fs": "^4.2.11", + "gray-matter": "^4.0.3", + "katex": "^0.16.9", + "lodash.get": "^4.4.2", + "next-mdx-remote": "^4.2.1", + "p-limit": "^3.1.0", + "rehype-katex": "^7.0.0", + "rehype-pretty-code": "0.9.11", + "rehype-raw": "^7.0.0", + "remark-gfm": "^3.0.1", + "remark-math": "^5.1.1", + "remark-reading-time": "^2.0.1", + "shiki": "^0.14.3", + "slash": "^3.0.0", + "title": "^3.5.3", + "unist-util-remove": "^4.0.0", + "unist-util-visit": "^5.0.0", + "zod": "^3.22.3" + }, + "engines": { + "node": ">=16" + }, + "peerDependencies": { + "next": ">=9.5.3", + "react": ">=16.13.1", + "react-dom": ">=16.13.1" + } + }, + "node_modules/nextra-theme-docs": { + "version": "2.13.4", + "resolved": "https://registry.npmjs.org/nextra-theme-docs/-/nextra-theme-docs-2.13.4.tgz", + "integrity": "sha512-2XOoMfwBCTYBt8ds4ZHftt9Wyf2XsykiNo02eir/XEYB+sGeUoE77kzqfidjEOKCSzOHYbK9BDMcg2+B/2vYRw==", + "dependencies": { + "@headlessui/react": "^1.7.17", + "@popperjs/core": "^2.11.8", + "clsx": "^2.0.0", + "escape-string-regexp": "^5.0.0", + "flexsearch": "^0.7.31", + "focus-visible": "^5.2.0", + "git-url-parse": "^13.1.0", + "intersection-observer": "^0.12.2", + "match-sorter": "^6.3.1", + "next-seo": "^6.0.0", + "next-themes": "^0.2.1", + "scroll-into-view-if-needed": "^3.1.0", + "zod": "^3.22.3" + }, + "peerDependencies": { + "next": ">=9.5.3", + "nextra": "2.13.4", + "react": ">=16.13.1", + "react-dom": ">=16.13.1" + } + }, + "node_modules/node-releases": { + "version": "2.0.14", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.14.tgz", + "integrity": "sha512-y10wOWt8yZpqXmOgRo77WaHEmhYQYGNA6y421PKsKYWEK8aW+cqAphborZDhqfyKrbZEN92CN1X2KbafY2s7Yw==", + "dev": true + }, + "node_modules/non-layered-tidy-tree-layout": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/non-layered-tidy-tree-layout/-/non-layered-tidy-tree-layout-2.0.2.tgz", + "integrity": "sha512-gkXMxRzUH+PB0ax9dUN0yYF0S25BqeAYqhgMaLUFmpXLEk7Fcu8f4emJuOAY0V8kjDICxROIKsTAKsV/v355xw==" + }, + "node_modules/normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/normalize-range": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/normalize-range/-/normalize-range-0.1.2.tgz", + "integrity": "sha512-bdok/XvKII3nUpklnV6P2hxtMNrCboOjAcyBuQnWEhO665FwrSNRxU+AqpsyvO6LgGYPspN+lu5CLtw4jPRKNA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/npm-run-path": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-2.0.2.tgz", + "integrity": "sha512-lJxZYlT4DW/bRUtFh1MQIWqmLwQfAxnqWG4HhEdjMlkrJYnJn0Jrr2u3mgxqaWsdiBc76TYkTG/mhrnYTuzfHw==", + "dependencies": { + "path-key": "^2.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/npm-to-yarn": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/npm-to-yarn/-/npm-to-yarn-2.2.1.tgz", + "integrity": "sha512-O/j/ROyX0KGLG7O6Ieut/seQ0oiTpHF2tXAcFbpdTLQFiaNtkyTXXocM1fwpaa60dg1qpWj0nHlbNhx6qwuENQ==", + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/nebrelbug/npm-to-yarn?sponsor=1" + } + }, + "node_modules/object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-hash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/object-hash/-/object-hash-3.0.0.tgz", + "integrity": "sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==", + "dev": true, + "engines": { + "node": ">= 6" + } + }, + "node_modules/p-finally": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz", + "integrity": "sha512-LICb2p9CB7FS+0eR1oqWnHhp0FljGLZCWBE9aix0Uye9W8LTQPwMTYVGWQWIw9RdQiDg4+epXQODwIYJtSJaow==", + "engines": { + "node": ">=4" + } + }, + "node_modules/p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "dependencies": { + "yocto-queue": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/parse-entities": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/parse-entities/-/parse-entities-4.0.1.tgz", + "integrity": "sha512-SWzvYcSJh4d/SGLIOQfZ/CoNv6BTlI6YEQ7Nj82oDVnRpwe/Z/F1EMx42x3JAOwGBlCjeCH0BRJQbQ/opHL17w==", + "dependencies": { + "@types/unist": "^2.0.0", + "character-entities": "^2.0.0", + "character-entities-legacy": "^3.0.0", + "character-reference-invalid": "^2.0.0", + "decode-named-character-reference": "^1.0.0", + "is-alphanumerical": "^2.0.0", + "is-decimal": "^2.0.0", + "is-hexadecimal": "^2.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/parse-numeric-range": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/parse-numeric-range/-/parse-numeric-range-1.3.0.tgz", + "integrity": "sha512-twN+njEipszzlMJd4ONUYgSfZPDxgHhT9Ahed5uTigpQn90FggW4SA/AIPq/6a149fTbE9qBEcSwE3FAEp6wQQ==" + }, + "node_modules/parse-path": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/parse-path/-/parse-path-7.0.0.tgz", + "integrity": "sha512-Euf9GG8WT9CdqwuWJGdf3RkUcTBArppHABkO7Lm8IzRQp0e2r/kkFnmhu4TSK30Wcu5rVAZLmfPKSBBi9tWFog==", + "dependencies": { + "protocols": "^2.0.0" + } + }, + "node_modules/parse-url": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/parse-url/-/parse-url-8.1.0.tgz", + "integrity": "sha512-xDvOoLU5XRrcOZvnI6b8zA6n9O9ejNk/GExuz1yBuWUGn9KA97GI6HTs6u02wKara1CeVmZhH+0TZFdWScR89w==", + "dependencies": { + "parse-path": "^7.0.0" + } + }, + "node_modules/parse5": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-7.1.2.tgz", + "integrity": "sha512-Czj1WaSVpaoj0wbhMzLmWD69anp2WH7FXMB9n1Sy8/ZFF9jolSQVMu1Ij5WIyGmcBmhk7EOndpO4mIpihVqAXw==", + "dependencies": { + "entities": "^4.4.0" + }, + "funding": { + "url": "https://github.com/inikulin/parse5?sponsor=1" + } + }, + "node_modules/path-key": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz", + "integrity": "sha512-fEHGKCSmUSDPv4uoj8AlD+joPlq3peND+HRYyxFz4KPw4z926S/b8rIuFs2FYJg3BwsxJf6A9/3eIdLaYC+9Dw==", + "engines": { + "node": ">=4" + } + }, + "node_modules/path-parse": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", + "dev": true + }, + "node_modules/path-scurry": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz", + "integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==", + "dev": true, + "dependencies": { + "lru-cache": "^10.2.0", + "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" + }, + "engines": { + "node": ">=16 || 14 >=14.18" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/path-scurry/node_modules/lru-cache": { + "version": "10.2.2", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.2.2.tgz", + "integrity": "sha512-9hp3Vp2/hFQUiIwKo8XCeFVnrg8Pk3TYNPIR7tJADKi5YfcF7vEaK7avFHTlSy3kOKYaJQaalfEo6YuXdceBOQ==", + "dev": true, + "engines": { + "node": "14 || >=16.14" + } + }, + "node_modules/periscopic": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/periscopic/-/periscopic-3.1.0.tgz", + "integrity": "sha512-vKiQ8RRtkl9P+r/+oefh25C3fhybptkHKCZSPlcXiJux2tJF55GnEj3BVn4A5gKfq9NWWXXrxkHBwVPUfH0opw==", + "dependencies": { + "@types/estree": "^1.0.0", + "estree-walker": "^3.0.0", + "is-reference": "^3.0.0" + } + }, + "node_modules/picocolors": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.1.tgz", + "integrity": "sha512-anP1Z8qwhkbmu7MFP5iTt+wQKXgwzf7zTyGlcdzabySa9vd0Xt392U0rVmz9poOaBj0uHJKyyo9/upk0HrEQew==" + }, + "node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true, + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/pify": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "integrity": "sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/pirates": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.6.tgz", + "integrity": "sha512-saLsH7WeYYPiD25LDuLRRY/i+6HaPYr6G1OUlN39otzkSTxKnubR9RTxS3/Kk50s1g2JTgFwWQDQyplC5/SHZg==", + "dev": true, + "engines": { + "node": ">= 6" + } + }, + "node_modules/postcss": { + "version": "8.4.38", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.38.tgz", + "integrity": "sha512-Wglpdk03BSfXkHoQa3b/oulrotAkwrlLDRSOb9D0bN86FdRyE9lppSp33aHNPgBa0JKCoB+drFLZkQoRRYae5A==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "dependencies": { + "nanoid": "^3.3.7", + "picocolors": "^1.0.0", + "source-map-js": "^1.2.0" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/postcss-import": { + "version": "15.1.0", + "resolved": "https://registry.npmjs.org/postcss-import/-/postcss-import-15.1.0.tgz", + "integrity": "sha512-hpr+J05B2FVYUAXHeK1YyI267J/dDDhMU6B6civm8hSY1jYJnBXxzKDKDswzJmtLHryrjhnDjqqp/49t8FALew==", + "dev": true, + "dependencies": { + "postcss-value-parser": "^4.0.0", + "read-cache": "^1.0.0", + "resolve": "^1.1.7" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "postcss": "^8.0.0" + } + }, + "node_modules/postcss-js": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/postcss-js/-/postcss-js-4.0.1.tgz", + "integrity": "sha512-dDLF8pEO191hJMtlHFPRa8xsizHaM82MLfNkUHdUtVEV3tgTp5oj+8qbEqYM57SLfc74KSbw//4SeJma2LRVIw==", + "dev": true, + "dependencies": { + "camelcase-css": "^2.0.1" + }, + "engines": { + "node": "^12 || ^14 || >= 16" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + "peerDependencies": { + "postcss": "^8.4.21" + } + }, + "node_modules/postcss-load-config": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/postcss-load-config/-/postcss-load-config-4.0.2.tgz", + "integrity": "sha512-bSVhyJGL00wMVoPUzAVAnbEoWyqRxkjv64tUl427SKnPrENtq6hJwUojroMz2VB+Q1edmi4IfrAPpami5VVgMQ==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "dependencies": { + "lilconfig": "^3.0.0", + "yaml": "^2.3.4" + }, + "engines": { + "node": ">= 14" + }, + "peerDependencies": { + "postcss": ">=8.0.9", + "ts-node": ">=9.0.0" + }, + "peerDependenciesMeta": { + "postcss": { + "optional": true + }, + "ts-node": { + "optional": true + } + } + }, + "node_modules/postcss-load-config/node_modules/lilconfig": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-3.1.2.tgz", + "integrity": "sha512-eop+wDAvpItUys0FWkHIKeC9ybYrTGbU41U5K7+bttZZeohvnY7M9dZ5kB21GNWiFT2q1OoPTvncPCgSOVO5ow==", + "dev": true, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/antonk52" + } + }, + "node_modules/postcss-nested": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/postcss-nested/-/postcss-nested-6.0.1.tgz", + "integrity": "sha512-mEp4xPMi5bSWiMbsgoPfcP74lsWLHkQbZc3sY+jWYd65CUwXrUaTp0fmNpa01ZcETKlIgUdFN/MpS2xZtqL9dQ==", + "dev": true, + "dependencies": { + "postcss-selector-parser": "^6.0.11" + }, + "engines": { + "node": ">=12.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + "peerDependencies": { + "postcss": "^8.2.14" + } + }, + "node_modules/postcss-selector-parser": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.1.0.tgz", + "integrity": "sha512-UMz42UD0UY0EApS0ZL9o1XnLhSTtvvvLe5Dc2H2O56fvRZi+KulDyf5ctDhhtYJBGKStV2FL1fy6253cmLgqVQ==", + "dev": true, + "dependencies": { + "cssesc": "^3.0.0", + "util-deprecate": "^1.0.2" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/postcss-value-parser": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", + "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==", + "dev": true + }, + "node_modules/prop-types": { + "version": "15.8.1", + "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz", + "integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==", + "dependencies": { + "loose-envify": "^1.4.0", + "object-assign": "^4.1.1", + "react-is": "^16.13.1" + } + }, + "node_modules/property-information": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/property-information/-/property-information-6.5.0.tgz", + "integrity": "sha512-PgTgs/BlvHxOu8QuEN7wi5A0OmXaBcHpmCSTehcs6Uuu9IkDIEo13Hy7n898RHfrQ49vKCoGeWZSaAK01nwVig==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/protocols": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/protocols/-/protocols-2.0.1.tgz", + "integrity": "sha512-/XJ368cyBJ7fzLMwLKv1e4vLxOju2MNAIokcr7meSaNcVbWz/CPcW22cP04mwxOErdA5mwjA8Q6w/cdAQxVn7Q==" + }, + "node_modules/pseudomap": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz", + "integrity": "sha512-b/YwNhb8lk1Zz2+bXXpS/LK9OisiZZ1SNsSLxN1x2OXVEhW2Ckr/7mWE5vrC1ZTiJlD9g19jWszTmJsB+oEpFQ==" + }, + "node_modules/queue-microtask": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/react": { + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/react/-/react-18.3.1.tgz", + "integrity": "sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==", + "dependencies": { + "loose-envify": "^1.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/react-dom": { + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.3.1.tgz", + "integrity": "sha512-5m4nQKp+rZRb09LNH59GM4BxTh9251/ylbKIbpe7TpGxfJ+9kv6BLkLBXIjjspbgbnIBNqlI23tRnTWT0snUIw==", + "dependencies": { + "loose-envify": "^1.1.0", + "scheduler": "^0.23.2" + }, + "peerDependencies": { + "react": "^18.3.1" + } + }, + "node_modules/react-is": { + "version": "16.13.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", + "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==" + }, + "node_modules/react-svg": { + "version": "16.1.34", + "resolved": "https://registry.npmjs.org/react-svg/-/react-svg-16.1.34.tgz", + "integrity": "sha512-L4ak1qNFLgzVbHm0xQEpHoIOqb3um/B0ybahd3U2TKoGZxb0JaPVI5lsAhvSng2P1kcsYEok2Z7RpcKx7arJGw==", + "dependencies": { + "@babel/runtime": "^7.24.1", + "@tanem/svg-injector": "^10.1.68", + "@types/prop-types": "^15.7.12", + "prop-types": "^15.8.1" + }, + "peerDependencies": { + "react": "^16.0.0 || ^17.0.0 || ^18.0.0", + "react-dom": "^16.0.0 || ^17.0.0 || ^18.0.0" + } + }, + "node_modules/read-cache": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz", + "integrity": "sha512-Owdv/Ft7IjOgm/i0xvNDZ1LrRANRfew4b2prF3OWMQLxLfu3bS8FVhCsrSCMK4lR56Y9ya+AThoTpDCTxCmpRA==", + "dev": true, + "dependencies": { + "pify": "^2.3.0" + } + }, + "node_modules/readdirp": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "dev": true, + "dependencies": { + "picomatch": "^2.2.1" + }, + "engines": { + "node": ">=8.10.0" + } + }, + "node_modules/reading-time": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/reading-time/-/reading-time-1.5.0.tgz", + "integrity": "sha512-onYyVhBNr4CmAxFsKS7bz+uTLRakypIe4R+5A824vBSkQy/hB3fZepoVEf8OVAxzLvK+H/jm9TzpI3ETSm64Kg==" + }, + "node_modules/regenerator-runtime": { + "version": "0.14.1", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz", + "integrity": "sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==" + }, + "node_modules/rehype-katex": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/rehype-katex/-/rehype-katex-7.0.0.tgz", + "integrity": "sha512-h8FPkGE00r2XKU+/acgqwWUlyzve1IiOKwsEkg4pDL3k48PiE0Pt+/uLtVHDVkN1yA4iurZN6UES8ivHVEQV6Q==", + "dependencies": { + "@types/hast": "^3.0.0", + "@types/katex": "^0.16.0", + "hast-util-from-html-isomorphic": "^2.0.0", + "hast-util-to-text": "^4.0.0", + "katex": "^0.16.0", + "unist-util-visit-parents": "^6.0.0", + "vfile": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/rehype-katex/node_modules/@types/hast": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@types/hast/-/hast-3.0.4.tgz", + "integrity": "sha512-WPs+bbQw5aCj+x6laNGWLH3wviHtoCv/P3+otBhbOhJgG8qtpdAMlTCxLtsTWA7LH1Oh/bFCHsBn0TPS5m30EQ==", + "dependencies": { + "@types/unist": "*" + } + }, + "node_modules/rehype-katex/node_modules/@types/unist": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/@types/unist/-/unist-3.0.2.tgz", + "integrity": "sha512-dqId9J8K/vGi5Zr7oo212BGii5m3q5Hxlkwy3WpYuKPklmBEvsbMYYyLxAQpSffdLl/gdW0XUpKWFvYmyoWCoQ==" + }, + "node_modules/rehype-katex/node_modules/unist-util-stringify-position": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/unist-util-stringify-position/-/unist-util-stringify-position-4.0.0.tgz", + "integrity": "sha512-0ASV06AAoKCDkS2+xw5RXJywruurpbC4JZSm7nr7MOt1ojAzvyyaO+UxZf18j8FCF6kmzCZKcAgN/yu2gm2XgQ==", + "dependencies": { + "@types/unist": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/rehype-katex/node_modules/vfile": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/vfile/-/vfile-6.0.1.tgz", + "integrity": "sha512-1bYqc7pt6NIADBJ98UiG0Bn/CHIVOoZ/IyEkqIruLg0mE1BKzkOXY2D6CSqQIcKqgadppE5lrxgWXJmXd7zZJw==", + "dependencies": { + "@types/unist": "^3.0.0", + "unist-util-stringify-position": "^4.0.0", + "vfile-message": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/rehype-pretty-code": { + "version": "0.9.11", + "resolved": "https://registry.npmjs.org/rehype-pretty-code/-/rehype-pretty-code-0.9.11.tgz", + "integrity": "sha512-Eq90eCYXQJISktfRZ8PPtwc5SUyH6fJcxS8XOMnHPUQZBtC6RYo67gGlley9X2nR8vlniPj0/7oCDEYHKQa/oA==", + "dependencies": { + "@types/hast": "^2.0.0", + "hash-obj": "^4.0.0", + "parse-numeric-range": "^1.3.0" + }, + "engines": { + "node": ">=16" + }, + "peerDependencies": { + "shiki": "*" + } + }, + "node_modules/rehype-raw": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/rehype-raw/-/rehype-raw-7.0.0.tgz", + "integrity": "sha512-/aE8hCfKlQeA8LmyeyQvQF3eBiLRGNlfBJEvWH7ivp9sBqs7TNqBL5X3v157rM4IFETqDnIOO+z5M/biZbo9Ww==", + "dependencies": { + "@types/hast": "^3.0.0", + "hast-util-raw": "^9.0.0", + "vfile": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/rehype-raw/node_modules/@types/hast": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@types/hast/-/hast-3.0.4.tgz", + "integrity": "sha512-WPs+bbQw5aCj+x6laNGWLH3wviHtoCv/P3+otBhbOhJgG8qtpdAMlTCxLtsTWA7LH1Oh/bFCHsBn0TPS5m30EQ==", + "dependencies": { + "@types/unist": "*" + } + }, + "node_modules/rehype-raw/node_modules/@types/unist": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/@types/unist/-/unist-3.0.2.tgz", + "integrity": "sha512-dqId9J8K/vGi5Zr7oo212BGii5m3q5Hxlkwy3WpYuKPklmBEvsbMYYyLxAQpSffdLl/gdW0XUpKWFvYmyoWCoQ==" + }, + "node_modules/rehype-raw/node_modules/unist-util-stringify-position": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/unist-util-stringify-position/-/unist-util-stringify-position-4.0.0.tgz", + "integrity": "sha512-0ASV06AAoKCDkS2+xw5RXJywruurpbC4JZSm7nr7MOt1ojAzvyyaO+UxZf18j8FCF6kmzCZKcAgN/yu2gm2XgQ==", + "dependencies": { + "@types/unist": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/rehype-raw/node_modules/vfile": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/vfile/-/vfile-6.0.1.tgz", + "integrity": "sha512-1bYqc7pt6NIADBJ98UiG0Bn/CHIVOoZ/IyEkqIruLg0mE1BKzkOXY2D6CSqQIcKqgadppE5lrxgWXJmXd7zZJw==", + "dependencies": { + "@types/unist": "^3.0.0", + "unist-util-stringify-position": "^4.0.0", + "vfile-message": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/remark-gfm": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/remark-gfm/-/remark-gfm-3.0.1.tgz", + "integrity": "sha512-lEFDoi2PICJyNrACFOfDD3JlLkuSbOa5Wd8EPt06HUdptv8Gn0bxYTdbU/XXQ3swAPkEaGxxPN9cbnMHvVu1Ig==", + "dependencies": { + "@types/mdast": "^3.0.0", + "mdast-util-gfm": "^2.0.0", + "micromark-extension-gfm": "^2.0.0", + "unified": "^10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/remark-math": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/remark-math/-/remark-math-5.1.1.tgz", + "integrity": "sha512-cE5T2R/xLVtfFI4cCePtiRn+e6jKMtFDR3P8V3qpv8wpKjwvHoBA4eJzvX+nVrnlNy0911bdGmuspCSwetfYHw==", + "dependencies": { + "@types/mdast": "^3.0.0", + "mdast-util-math": "^2.0.0", + "micromark-extension-math": "^2.0.0", + "unified": "^10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/remark-mdx": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/remark-mdx/-/remark-mdx-2.3.0.tgz", + "integrity": "sha512-g53hMkpM0I98MU266IzDFMrTD980gNF3BJnkyFcmN+dD873mQeD5rdMO3Y2X+x8umQfbSE0PcoEDl7ledSA+2g==", + "dependencies": { + "mdast-util-mdx": "^2.0.0", + "micromark-extension-mdxjs": "^1.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/remark-parse": { + "version": "10.0.2", + "resolved": "https://registry.npmjs.org/remark-parse/-/remark-parse-10.0.2.tgz", + "integrity": "sha512-3ydxgHa/ZQzG8LvC7jTXccARYDcRld3VfcgIIFs7bI6vbRSxJJmzgLEIIoYKyrfhaY+ujuWaf/PJiMZXoiCXgw==", + "dependencies": { + "@types/mdast": "^3.0.0", + "mdast-util-from-markdown": "^1.0.0", + "unified": "^10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/remark-reading-time": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/remark-reading-time/-/remark-reading-time-2.0.1.tgz", + "integrity": "sha512-fy4BKy9SRhtYbEHvp6AItbRTnrhiDGbqLQTSYVbQPGuRCncU1ubSsh9p/W5QZSxtYcUXv8KGL0xBgPLyNJA1xw==", + "dependencies": { + "estree-util-is-identifier-name": "^2.0.0", + "estree-util-value-to-estree": "^1.3.0", + "reading-time": "^1.3.0", + "unist-util-visit": "^3.1.0" + } + }, + "node_modules/remark-reading-time/node_modules/unist-util-visit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/unist-util-visit/-/unist-util-visit-3.1.0.tgz", + "integrity": "sha512-Szoh+R/Ll68QWAyQyZZpQzZQm2UPbxibDvaY8Xc9SUtYgPsDzx5AWSk++UUt2hJuow8mvwR+rG+LQLw+KsuAKA==", + "dependencies": { + "@types/unist": "^2.0.0", + "unist-util-is": "^5.0.0", + "unist-util-visit-parents": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/remark-reading-time/node_modules/unist-util-visit-parents": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/unist-util-visit-parents/-/unist-util-visit-parents-4.1.1.tgz", + "integrity": "sha512-1xAFJXAKpnnJl8G7K5KgU7FY55y3GcLIXqkzUj5QF/QVP7biUm0K0O2oqVkYsdjzJKifYeWn9+o6piAK2hGSHw==", + "dependencies": { + "@types/unist": "^2.0.0", + "unist-util-is": "^5.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/remark-rehype": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/remark-rehype/-/remark-rehype-10.1.0.tgz", + "integrity": "sha512-EFmR5zppdBp0WQeDVZ/b66CWJipB2q2VLNFMabzDSGR66Z2fQii83G5gTBbgGEnEEA0QRussvrFHxk1HWGJskw==", + "dependencies": { + "@types/hast": "^2.0.0", + "@types/mdast": "^3.0.0", + "mdast-util-to-hast": "^12.1.0", + "unified": "^10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/remark-rehype/node_modules/mdast-util-to-hast": { + "version": "12.3.0", + "resolved": "https://registry.npmjs.org/mdast-util-to-hast/-/mdast-util-to-hast-12.3.0.tgz", + "integrity": "sha512-pits93r8PhnIoU4Vy9bjW39M2jJ6/tdHyja9rrot9uujkN7UTU9SDnE6WNJz/IGyQk3XHX6yNNtrBH6cQzm8Hw==", + "dependencies": { + "@types/hast": "^2.0.0", + "@types/mdast": "^3.0.0", + "mdast-util-definitions": "^5.0.0", + "micromark-util-sanitize-uri": "^1.1.0", + "trim-lines": "^3.0.0", + "unist-util-generated": "^2.0.0", + "unist-util-position": "^4.0.0", + "unist-util-visit": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/remark-rehype/node_modules/unist-util-visit": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/unist-util-visit/-/unist-util-visit-4.1.2.tgz", + "integrity": "sha512-MSd8OUGISqHdVvfY9TPhyK2VdUrPgxkUtWSuMHF6XAAFuL4LokseigBnZtPnJMu+FbynTkFNnFlyjxpVKujMRg==", + "dependencies": { + "@types/unist": "^2.0.0", + "unist-util-is": "^5.0.0", + "unist-util-visit-parents": "^5.1.1" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/remark-rehype/node_modules/unist-util-visit-parents": { + "version": "5.1.3", + "resolved": "https://registry.npmjs.org/unist-util-visit-parents/-/unist-util-visit-parents-5.1.3.tgz", + "integrity": "sha512-x6+y8g7wWMyQhL1iZfhIPhDAs7Xwbn9nRosDXl7qoPTSCy0yNxnKc+hWokFifWQIDGi154rdUqKvbCa4+1kLhg==", + "dependencies": { + "@types/unist": "^2.0.0", + "unist-util-is": "^5.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/remove-accents": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/remove-accents/-/remove-accents-0.5.0.tgz", + "integrity": "sha512-8g3/Otx1eJaVD12e31UbJj1YzdtVvzH85HV7t+9MJYk/u3XmkOUJ5Ys9wQrf9PCPK8+xn4ymzqYCiZl6QWKn+A==" + }, + "node_modules/resolve": { + "version": "1.22.8", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.8.tgz", + "integrity": "sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==", + "dev": true, + "dependencies": { + "is-core-module": "^2.13.0", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + }, + "bin": { + "resolve": "bin/resolve" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/reusify": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", + "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", + "dev": true, + "engines": { + "iojs": ">=1.0.0", + "node": ">=0.10.0" + } + }, + "node_modules/robust-predicates": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/robust-predicates/-/robust-predicates-3.0.2.tgz", + "integrity": "sha512-IXgzBWvWQwE6PrDI05OvmXUIruQTcoMDzRsOd5CDvHCVLcLHMTSYvOK5Cm46kWqlV3yAbuSpBZdJ5oP5OUoStg==" + }, + "node_modules/run-parallel": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", + "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "dependencies": { + "queue-microtask": "^1.2.2" + } + }, + "node_modules/rw": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/rw/-/rw-1.3.3.tgz", + "integrity": "sha512-PdhdWy89SiZogBLaw42zdeqtRJ//zFd2PgQavcICDUgJT5oW10QCRKbJ6bg4r0/UY2M6BWd5tkxuGFRvCkgfHQ==" + }, + "node_modules/sade": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/sade/-/sade-1.8.1.tgz", + "integrity": "sha512-xal3CZX1Xlo/k4ApwCFrHVACi9fBqJ7V+mwhBsuf/1IOKbBy098Fex+Wa/5QMubw09pSZ/u8EY8PWgevJsXp1A==", + "dependencies": { + "mri": "^1.1.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" + }, + "node_modules/scheduler": { + "version": "0.23.2", + "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.2.tgz", + "integrity": "sha512-UOShsPwz7NrMUqhR6t0hWjFduvOzbtv7toDH1/hIrfRNIDBnnBWd0CwJTGvTpngVlmwGCdP9/Zl/tVrDqcuYzQ==", + "dependencies": { + "loose-envify": "^1.1.0" + } + }, + "node_modules/scroll-into-view-if-needed": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/scroll-into-view-if-needed/-/scroll-into-view-if-needed-3.1.0.tgz", + "integrity": "sha512-49oNpRjWRvnU8NyGVmUaYG4jtTkNonFZI86MmGRDqBphEK2EXT9gdEUoQPZhuBM8yWHxCWbobltqYO5M4XrUvQ==", + "dependencies": { + "compute-scroll-into-view": "^3.0.2" + } + }, + "node_modules/section-matter": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/section-matter/-/section-matter-1.0.0.tgz", + "integrity": "sha512-vfD3pmTzGpufjScBh50YHKzEu2lxBWhVEHsNGoEXmCmn2hKGfeNLYMzCJpe8cD7gqX7TJluOVpBkAequ6dgMmA==", + "dependencies": { + "extend-shallow": "^2.0.1", + "kind-of": "^6.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/shebang-command": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz", + "integrity": "sha512-EV3L1+UQWGor21OmnvojK36mhg+TyIKDh3iFBKBohr5xeXIhNBcx8oWdgkTEEQ+BEFFYdLRuqMfd5L84N1V5Vg==", + "dependencies": { + "shebang-regex": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/shebang-regex": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz", + "integrity": "sha512-wpoSFAxys6b2a2wHZ1XpDSgD7N9iVjg29Ph9uV/uaP9Ex/KXlkTZTeddxDPSYQpgvzKLGJke2UU0AzoGCjNIvQ==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/shiki": { + "version": "0.14.7", + "resolved": "https://registry.npmjs.org/shiki/-/shiki-0.14.7.tgz", + "integrity": "sha512-dNPAPrxSc87ua2sKJ3H5dQ/6ZaY8RNnaAqK+t0eG7p0Soi2ydiqbGOTaZCqaYvA/uZYfS1LJnemt3Q+mSfcPCg==", + "dependencies": { + "ansi-sequence-parser": "^1.1.0", + "jsonc-parser": "^3.2.0", + "vscode-oniguruma": "^1.7.0", + "vscode-textmate": "^8.0.0" + } + }, + "node_modules/signal-exit": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==" + }, + "node_modules/slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "engines": { + "node": ">=8" + } + }, + "node_modules/sort-keys": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/sort-keys/-/sort-keys-5.0.0.tgz", + "integrity": "sha512-Pdz01AvCAottHTPQGzndktFNdbRA75BgOfeT1hH+AMnJFv8lynkPi42rfeEhpx1saTEI3YNMWxfqu0sFD1G8pw==", + "dependencies": { + "is-plain-obj": "^4.0.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/sort-keys/node_modules/is-plain-obj": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-4.1.0.tgz", + "integrity": "sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg==", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/source-map": { + "version": "0.7.4", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.4.tgz", + "integrity": "sha512-l3BikUxvPOcn5E74dZiq5BGsTb5yEwhaTSzccU6t4sDOH8NWJCstKO5QT2CvtFoK6F0saL7p9xHAqHOlCPJygA==", + "engines": { + "node": ">= 8" + } + }, + "node_modules/source-map-js": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.0.tgz", + "integrity": "sha512-itJW8lvSA0TXEphiRoawsCksnlf8SyvmFzIhltqAHluXd88pkCd+cXJVHTDwdCr0IzwptSm035IHQktUu1QUMg==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/space-separated-tokens": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/space-separated-tokens/-/space-separated-tokens-2.0.2.tgz", + "integrity": "sha512-PEGlAwrG8yXGXRjW32fGbg66JAlOAwbObuqVoJpv/mRgoWDQfgH1wDPvtzWyUSNAXBGSk8h755YDbbcEy3SH2Q==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/sprintf-js": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", + "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==" + }, + "node_modules/streamsearch": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/streamsearch/-/streamsearch-1.1.0.tgz", + "integrity": "sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg==", + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/string-width": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", + "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", + "dev": true, + "dependencies": { + "eastasianwidth": "^0.2.0", + "emoji-regex": "^9.2.2", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/string-width-cjs": { + "name": "string-width", + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/string-width-cjs/node_modules/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, + "engines": { + "node": ">=8" + } + }, + "node_modules/string-width-cjs/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true + }, + "node_modules/string-width-cjs/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/stringify-entities": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/stringify-entities/-/stringify-entities-4.0.4.tgz", + "integrity": "sha512-IwfBptatlO+QCJUo19AqvrPNqlVMpW9YEL2LIVY+Rpv2qsjCGxaDLNRgeGsQWJhfItebuJhsGSLjaBbNSQ+ieg==", + "dependencies": { + "character-entities-html4": "^2.0.0", + "character-entities-legacy": "^3.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/strip-ansi": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", + "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", + "dev": true, + "dependencies": { + "ansi-regex": "^6.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "node_modules/strip-ansi-cjs": { + "name": "strip-ansi", + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi-cjs/node_modules/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, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-bom-string": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/strip-bom-string/-/strip-bom-string-1.0.0.tgz", + "integrity": "sha512-uCC2VHvQRYu+lMh4My/sFNmF2klFymLX1wHJeXnbEJERpV/ZsVuonzerjfrGpIGF7LBVa1O7i9kjiWvJiFck8g==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/strip-eof": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/strip-eof/-/strip-eof-1.0.0.tgz", + "integrity": "sha512-7FCwGGmx8mD5xQd3RPUvnSpUXHM3BWuzjtpD4TXsfcZ9EL4azvVVUscFYwD9nx8Kh+uCBC00XBtAykoMHwTh8Q==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/style-to-object": { + "version": "0.4.4", + "resolved": "https://registry.npmjs.org/style-to-object/-/style-to-object-0.4.4.tgz", + "integrity": "sha512-HYNoHZa2GorYNyqiCaBgsxvcJIn7OHq6inEga+E6Ke3m5JkoqpQbnFssk4jwe+K7AhGa2fcha4wSOf1Kn01dMg==", + "dependencies": { + "inline-style-parser": "0.1.1" + } + }, + "node_modules/styled-jsx": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/styled-jsx/-/styled-jsx-5.1.1.tgz", + "integrity": "sha512-pW7uC1l4mBZ8ugbiZrcIsiIvVx1UmTfw7UkC3Um2tmfUq9Bhk8IiyEIPl6F8agHgjzku6j0xQEZbfA5uSgSaCw==", + "dependencies": { + "client-only": "0.0.1" + }, + "engines": { + "node": ">= 12.0.0" + }, + "peerDependencies": { + "react": ">= 16.8.0 || 17.x.x || ^18.0.0-0" + }, + "peerDependenciesMeta": { + "@babel/core": { + "optional": true + }, + "babel-plugin-macros": { + "optional": true + } + } + }, + "node_modules/stylis": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/stylis/-/stylis-4.3.2.tgz", + "integrity": "sha512-bhtUjWd/z6ltJiQwg0dUfxEJ+W+jdqQd8TbWLWyeIJHlnsqmGLRFFd8e5mA0AZi/zx90smXRlN66YMTcaSFifg==" + }, + "node_modules/sucrase": { + "version": "3.35.0", + "resolved": "https://registry.npmjs.org/sucrase/-/sucrase-3.35.0.tgz", + "integrity": "sha512-8EbVDiu9iN/nESwxeSxDKe0dunta1GOlHufmSSXxMD2z2/tMZpDMpvXQGsc+ajGo8y2uYUmixaSRUc/QPoQ0GA==", + "dev": true, + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.2", + "commander": "^4.0.0", + "glob": "^10.3.10", + "lines-and-columns": "^1.1.6", + "mz": "^2.7.0", + "pirates": "^4.0.1", + "ts-interface-checker": "^0.1.9" + }, + "bin": { + "sucrase": "bin/sucrase", + "sucrase-node": "bin/sucrase-node" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, + "node_modules/sucrase/node_modules/commander": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-4.1.1.tgz", + "integrity": "sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==", + "dev": true, + "engines": { + "node": ">= 6" + } + }, + "node_modules/supports-color": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-4.5.0.tgz", + "integrity": "sha512-ycQR/UbvI9xIlEdQT1TQqwoXtEldExbCEAJgRo5YXlmSKjv6ThHnP9/vwGa1gr19Gfw+LkFd7KqYMhzrRC5JYw==", + "dependencies": { + "has-flag": "^2.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/supports-preserve-symlinks-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", + "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/tailwindcss": { + "version": "3.4.4", + "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.4.4.tgz", + "integrity": "sha512-ZoyXOdJjISB7/BcLTR6SEsLgKtDStYyYZVLsUtWChO4Ps20CBad7lfJKVDiejocV4ME1hLmyY0WJE3hSDcmQ2A==", + "dev": true, + "dependencies": { + "@alloc/quick-lru": "^5.2.0", + "arg": "^5.0.2", + "chokidar": "^3.5.3", + "didyoumean": "^1.2.2", + "dlv": "^1.1.3", + "fast-glob": "^3.3.0", + "glob-parent": "^6.0.2", + "is-glob": "^4.0.3", + "jiti": "^1.21.0", + "lilconfig": "^2.1.0", + "micromatch": "^4.0.5", + "normalize-path": "^3.0.0", + "object-hash": "^3.0.0", + "picocolors": "^1.0.0", + "postcss": "^8.4.23", + "postcss-import": "^15.1.0", + "postcss-js": "^4.0.1", + "postcss-load-config": "^4.0.1", + "postcss-nested": "^6.0.1", + "postcss-selector-parser": "^6.0.11", + "resolve": "^1.22.2", + "sucrase": "^3.32.0" + }, + "bin": { + "tailwind": "lib/cli.js", + "tailwindcss": "lib/cli.js" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/tailwindcss/node_modules/arg": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/arg/-/arg-5.0.2.tgz", + "integrity": "sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==", + "dev": true + }, + "node_modules/thenify": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/thenify/-/thenify-3.3.1.tgz", + "integrity": "sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==", + "dev": true, + "dependencies": { + "any-promise": "^1.0.0" + } + }, + "node_modules/thenify-all": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/thenify-all/-/thenify-all-1.6.0.tgz", + "integrity": "sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA==", + "dev": true, + "dependencies": { + "thenify": ">= 3.1.0 < 4" + }, + "engines": { + "node": ">=0.8" + } + }, + "node_modules/title": { + "version": "3.5.3", + "resolved": "https://registry.npmjs.org/title/-/title-3.5.3.tgz", + "integrity": "sha512-20JyowYglSEeCvZv3EZ0nZ046vLarO37prvV0mbtQV7C8DJPGgN967r8SJkqd3XK3K3lD3/Iyfp3avjfil8Q2Q==", + "dependencies": { + "arg": "1.0.0", + "chalk": "2.3.0", + "clipboardy": "1.2.2", + "titleize": "1.0.0" + }, + "bin": { + "title": "bin/title.js" + } + }, + "node_modules/titleize": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/titleize/-/titleize-1.0.0.tgz", + "integrity": "sha512-TARUb7z1pGvlLxgPk++7wJ6aycXF3GJ0sNSBTAsTuJrQG5QuZlkUQP+zl+nbjAh4gMX9yDw9ZYklMd7vAfJKEw==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/trim-lines": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/trim-lines/-/trim-lines-3.0.1.tgz", + "integrity": "sha512-kRj8B+YHZCc9kQYdWfJB2/oUl9rA99qbowYYBtr4ui4mZyAQ2JpvVBd/6U2YloATfqBhBTSMhTpgBHtU0Mf3Rg==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/trough": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/trough/-/trough-2.2.0.tgz", + "integrity": "sha512-tmMpK00BjZiUyVyvrBK7knerNgmgvcV/KLVyuma/SC+TQN167GrMRciANTz09+k3zW8L8t60jWO1GpfkZdjTaw==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/ts-dedent": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/ts-dedent/-/ts-dedent-2.2.0.tgz", + "integrity": "sha512-q5W7tVM71e2xjHZTlgfTDoPF/SmqKG5hddq9SzR49CH2hayqRKJtQ4mtRlSxKaJlR/+9rEM+mnBHf7I2/BQcpQ==", + "engines": { + "node": ">=6.10" + } + }, + "node_modules/ts-interface-checker": { + "version": "0.1.13", + "resolved": "https://registry.npmjs.org/ts-interface-checker/-/ts-interface-checker-0.1.13.tgz", + "integrity": "sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==", + "dev": true + }, + "node_modules/tslib": { + "version": "2.6.3", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.3.tgz", + "integrity": "sha512-xNvxJEOUiWPGhUuUdQgAJPKOOJfGnIyKySOc09XkKsgdUV/3E2zvwZYdejjmRgPCgcym1juLH3226yA7sEFJKQ==" + }, + "node_modules/type-fest": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-1.4.0.tgz", + "integrity": "sha512-yGSza74xk0UG8k+pLh5oeoYirvIiWo5t0/o3zHHAO2tRDiZcxWP7fywNlXhqb6/r6sWvwi+RsyQMWhVLe4BVuA==", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/typescript": { + "version": "5.4.5", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.4.5.tgz", + "integrity": "sha512-vcI4UpRgg81oIRUFwR0WSIHKt11nJ7SAVlYNIu+QpqeyXP+gpQJy/Z4+F0aGxSE4MqwjyXvW/TzgkLAx2AGHwQ==", + "dev": true, + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/undici-types": { + "version": "5.26.5", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", + "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==", + "dev": true + }, + "node_modules/unified": { + "version": "10.1.2", + "resolved": "https://registry.npmjs.org/unified/-/unified-10.1.2.tgz", + "integrity": "sha512-pUSWAi/RAnVy1Pif2kAoeWNBa3JVrx0MId2LASj8G+7AiHWoKZNTomq6LG326T68U7/e263X6fTdcXIy7XnF7Q==", + "dependencies": { + "@types/unist": "^2.0.0", + "bail": "^2.0.0", + "extend": "^3.0.0", + "is-buffer": "^2.0.0", + "is-plain-obj": "^4.0.0", + "trough": "^2.0.0", + "vfile": "^5.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unified/node_modules/is-plain-obj": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-4.1.0.tgz", + "integrity": "sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg==", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/unist-util-find-after": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/unist-util-find-after/-/unist-util-find-after-5.0.0.tgz", + "integrity": "sha512-amQa0Ep2m6hE2g72AugUItjbuM8X8cGQnFoHk0pGfrFeT9GZhzN5SW8nRsiGKK7Aif4CrACPENkA6P/Lw6fHGQ==", + "dependencies": { + "@types/unist": "^3.0.0", + "unist-util-is": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-find-after/node_modules/@types/unist": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/@types/unist/-/unist-3.0.2.tgz", + "integrity": "sha512-dqId9J8K/vGi5Zr7oo212BGii5m3q5Hxlkwy3WpYuKPklmBEvsbMYYyLxAQpSffdLl/gdW0XUpKWFvYmyoWCoQ==" + }, + "node_modules/unist-util-find-after/node_modules/unist-util-is": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/unist-util-is/-/unist-util-is-6.0.0.tgz", + "integrity": "sha512-2qCTHimwdxLfz+YzdGfkqNlH0tLi9xjTnHddPmJwtIG9MGsdbutfTc4P+haPD7l7Cjxf/WZj+we5qfVPvvxfYw==", + "dependencies": { + "@types/unist": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-generated": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/unist-util-generated/-/unist-util-generated-2.0.1.tgz", + "integrity": "sha512-qF72kLmPxAw0oN2fwpWIqbXAVyEqUzDHMsbtPvOudIlUzXYFIeQIuxXQCRCFh22B7cixvU0MG7m3MW8FTq/S+A==", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-is": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/unist-util-is/-/unist-util-is-5.2.1.tgz", + "integrity": "sha512-u9njyyfEh43npf1M+yGKDGVPbY/JWEemg5nH05ncKPfi+kBbKBJoTdsogMu33uhytuLlv9y0O7GH7fEdwLdLQw==", + "dependencies": { + "@types/unist": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-position": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/unist-util-position/-/unist-util-position-4.0.4.tgz", + "integrity": "sha512-kUBE91efOWfIVBo8xzh/uZQ7p9ffYRtUbMRZBNFYwf0RK8koUMx6dGUfwylLOKmaT2cs4wSW96QoYUSXAyEtpg==", + "dependencies": { + "@types/unist": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-position-from-estree": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/unist-util-position-from-estree/-/unist-util-position-from-estree-1.1.2.tgz", + "integrity": "sha512-poZa0eXpS+/XpoQwGwl79UUdea4ol2ZuCYguVaJS4qzIOMDzbqz8a3erUCOmubSZkaOuGamb3tX790iwOIROww==", + "dependencies": { + "@types/unist": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-remove": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/unist-util-remove/-/unist-util-remove-4.0.0.tgz", + "integrity": "sha512-b4gokeGId57UVRX/eVKej5gXqGlc9+trkORhFJpu9raqZkZhU0zm8Doi05+HaiBsMEIJowL+2WtQ5ItjsngPXg==", + "dependencies": { + "@types/unist": "^3.0.0", + "unist-util-is": "^6.0.0", + "unist-util-visit-parents": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-remove-position": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/unist-util-remove-position/-/unist-util-remove-position-5.0.0.tgz", + "integrity": "sha512-Hp5Kh3wLxv0PHj9m2yZhhLt58KzPtEYKQQ4yxfYFEO7EvHwzyDYnduhHnY1mDxoqr7VUwVuHXk9RXKIiYS1N8Q==", + "dependencies": { + "@types/unist": "^3.0.0", + "unist-util-visit": "^5.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-remove-position/node_modules/@types/unist": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/@types/unist/-/unist-3.0.2.tgz", + "integrity": "sha512-dqId9J8K/vGi5Zr7oo212BGii5m3q5Hxlkwy3WpYuKPklmBEvsbMYYyLxAQpSffdLl/gdW0XUpKWFvYmyoWCoQ==" + }, + "node_modules/unist-util-remove/node_modules/@types/unist": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/@types/unist/-/unist-3.0.2.tgz", + "integrity": "sha512-dqId9J8K/vGi5Zr7oo212BGii5m3q5Hxlkwy3WpYuKPklmBEvsbMYYyLxAQpSffdLl/gdW0XUpKWFvYmyoWCoQ==" + }, + "node_modules/unist-util-remove/node_modules/unist-util-is": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/unist-util-is/-/unist-util-is-6.0.0.tgz", + "integrity": "sha512-2qCTHimwdxLfz+YzdGfkqNlH0tLi9xjTnHddPmJwtIG9MGsdbutfTc4P+haPD7l7Cjxf/WZj+we5qfVPvvxfYw==", + "dependencies": { + "@types/unist": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-stringify-position": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/unist-util-stringify-position/-/unist-util-stringify-position-3.0.3.tgz", + "integrity": "sha512-k5GzIBZ/QatR8N5X2y+drfpWG8IDBzdnVj6OInRNWm1oXrzydiaAT2OQiA8DPRRZyAKb9b6I2a6PxYklZD0gKg==", + "dependencies": { + "@types/unist": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-visit": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/unist-util-visit/-/unist-util-visit-5.0.0.tgz", + "integrity": "sha512-MR04uvD+07cwl/yhVuVWAtw+3GOR/knlL55Nd/wAdblk27GCVt3lqpTivy/tkJcZoNPzTwS1Y+KMojlLDhoTzg==", + "dependencies": { + "@types/unist": "^3.0.0", + "unist-util-is": "^6.0.0", + "unist-util-visit-parents": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-visit-parents": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/unist-util-visit-parents/-/unist-util-visit-parents-6.0.1.tgz", + "integrity": "sha512-L/PqWzfTP9lzzEa6CKs0k2nARxTdZduw3zyh8d2NVBnsyvHjSX4TWse388YrrQKbvI8w20fGjGlhgT96WwKykw==", + "dependencies": { + "@types/unist": "^3.0.0", + "unist-util-is": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-visit-parents/node_modules/@types/unist": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/@types/unist/-/unist-3.0.2.tgz", + "integrity": "sha512-dqId9J8K/vGi5Zr7oo212BGii5m3q5Hxlkwy3WpYuKPklmBEvsbMYYyLxAQpSffdLl/gdW0XUpKWFvYmyoWCoQ==" + }, + "node_modules/unist-util-visit-parents/node_modules/unist-util-is": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/unist-util-is/-/unist-util-is-6.0.0.tgz", + "integrity": "sha512-2qCTHimwdxLfz+YzdGfkqNlH0tLi9xjTnHddPmJwtIG9MGsdbutfTc4P+haPD7l7Cjxf/WZj+we5qfVPvvxfYw==", + "dependencies": { + "@types/unist": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-visit/node_modules/@types/unist": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/@types/unist/-/unist-3.0.2.tgz", + "integrity": "sha512-dqId9J8K/vGi5Zr7oo212BGii5m3q5Hxlkwy3WpYuKPklmBEvsbMYYyLxAQpSffdLl/gdW0XUpKWFvYmyoWCoQ==" + }, + "node_modules/unist-util-visit/node_modules/unist-util-is": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/unist-util-is/-/unist-util-is-6.0.0.tgz", + "integrity": "sha512-2qCTHimwdxLfz+YzdGfkqNlH0tLi9xjTnHddPmJwtIG9MGsdbutfTc4P+haPD7l7Cjxf/WZj+we5qfVPvvxfYw==", + "dependencies": { + "@types/unist": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/update-browserslist-db": { + "version": "1.0.16", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.16.tgz", + "integrity": "sha512-KVbTxlBYlckhF5wgfyZXTWnMn7MMZjMu9XG8bPlliUOP9ThaF4QnhP8qrjrH7DRzHfSk0oQv1wToW+iA5GajEQ==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "dependencies": { + "escalade": "^3.1.2", + "picocolors": "^1.0.1" + }, + "bin": { + "update-browserslist-db": "cli.js" + }, + "peerDependencies": { + "browserslist": ">= 4.21.0" + } + }, + "node_modules/util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", + "dev": true + }, + "node_modules/uuid": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz", + "integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==", + "funding": [ + "https://github.com/sponsors/broofa", + "https://github.com/sponsors/ctavan" + ], + "bin": { + "uuid": "dist/bin/uuid" + } + }, + "node_modules/uvu": { + "version": "0.5.6", + "resolved": "https://registry.npmjs.org/uvu/-/uvu-0.5.6.tgz", + "integrity": "sha512-+g8ENReyr8YsOc6fv/NVJs2vFdHBnBNdfE49rshrTzDWOlUx4Gq7KOS2GD8eqhy2j+Ejq29+SbKH8yjkAqXqoA==", + "dependencies": { + "dequal": "^2.0.0", + "diff": "^5.0.0", + "kleur": "^4.0.3", + "sade": "^1.7.3" + }, + "bin": { + "uvu": "bin.js" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/vfile": { + "version": "5.3.7", + "resolved": "https://registry.npmjs.org/vfile/-/vfile-5.3.7.tgz", + "integrity": "sha512-r7qlzkgErKjobAmyNIkkSpizsFPYiUPuJb5pNW1RB4JcYVZhs4lIbVqk8XPk033CV/1z8ss5pkax8SuhGpcG8g==", + "dependencies": { + "@types/unist": "^2.0.0", + "is-buffer": "^2.0.0", + "unist-util-stringify-position": "^3.0.0", + "vfile-message": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/vfile-location": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/vfile-location/-/vfile-location-5.0.2.tgz", + "integrity": "sha512-NXPYyxyBSH7zB5U6+3uDdd6Nybz6o6/od9rk8bp9H8GR3L+cm/fC0uUTbqBmUTnMCUDslAGBOIKNfvvb+gGlDg==", + "dependencies": { + "@types/unist": "^3.0.0", + "vfile": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/vfile-location/node_modules/@types/unist": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/@types/unist/-/unist-3.0.2.tgz", + "integrity": "sha512-dqId9J8K/vGi5Zr7oo212BGii5m3q5Hxlkwy3WpYuKPklmBEvsbMYYyLxAQpSffdLl/gdW0XUpKWFvYmyoWCoQ==" + }, + "node_modules/vfile-location/node_modules/unist-util-stringify-position": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/unist-util-stringify-position/-/unist-util-stringify-position-4.0.0.tgz", + "integrity": "sha512-0ASV06AAoKCDkS2+xw5RXJywruurpbC4JZSm7nr7MOt1ojAzvyyaO+UxZf18j8FCF6kmzCZKcAgN/yu2gm2XgQ==", + "dependencies": { + "@types/unist": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/vfile-location/node_modules/vfile": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/vfile/-/vfile-6.0.1.tgz", + "integrity": "sha512-1bYqc7pt6NIADBJ98UiG0Bn/CHIVOoZ/IyEkqIruLg0mE1BKzkOXY2D6CSqQIcKqgadppE5lrxgWXJmXd7zZJw==", + "dependencies": { + "@types/unist": "^3.0.0", + "unist-util-stringify-position": "^4.0.0", + "vfile-message": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/vfile-matter": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/vfile-matter/-/vfile-matter-3.0.1.tgz", + "integrity": "sha512-CAAIDwnh6ZdtrqAuxdElUqQRQDQgbbIrYtDYI8gCjXS1qQ+1XdLoK8FIZWxJwn0/I+BkSSZpar3SOgjemQz4fg==", + "dependencies": { + "@types/js-yaml": "^4.0.0", + "is-buffer": "^2.0.0", + "js-yaml": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/vfile-matter/node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==" + }, + "node_modules/vfile-matter/node_modules/js-yaml": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/vfile-message": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/vfile-message/-/vfile-message-4.0.2.tgz", + "integrity": "sha512-jRDZ1IMLttGj41KcZvlrYAaI3CfqpLpfpf+Mfig13viT6NKvRzWZ+lXz0Y5D60w6uJIBAOGq9mSHf0gktF0duw==", + "dependencies": { + "@types/unist": "^3.0.0", + "unist-util-stringify-position": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/vfile-message/node_modules/@types/unist": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/@types/unist/-/unist-3.0.2.tgz", + "integrity": "sha512-dqId9J8K/vGi5Zr7oo212BGii5m3q5Hxlkwy3WpYuKPklmBEvsbMYYyLxAQpSffdLl/gdW0XUpKWFvYmyoWCoQ==" + }, + "node_modules/vfile-message/node_modules/unist-util-stringify-position": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/unist-util-stringify-position/-/unist-util-stringify-position-4.0.0.tgz", + "integrity": "sha512-0ASV06AAoKCDkS2+xw5RXJywruurpbC4JZSm7nr7MOt1ojAzvyyaO+UxZf18j8FCF6kmzCZKcAgN/yu2gm2XgQ==", + "dependencies": { + "@types/unist": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/vfile/node_modules/vfile-message": { + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/vfile-message/-/vfile-message-3.1.4.tgz", + "integrity": "sha512-fa0Z6P8HUrQN4BZaX05SIVXic+7kE3b05PWAtPuYP9QLHsLKYR7/AlLW3NtOrpXRLeawpDLMsVkmk5DG0NXgWw==", + "dependencies": { + "@types/unist": "^2.0.0", + "unist-util-stringify-position": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/vscode-oniguruma": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/vscode-oniguruma/-/vscode-oniguruma-1.7.0.tgz", + "integrity": "sha512-L9WMGRfrjOhgHSdOYgCt/yRMsXzLDJSL7BPrOZt73gU0iWO4mpqzqQzOz5srxqTvMBaR0XZTSrVWo4j55Rc6cA==" + }, + "node_modules/vscode-textmate": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/vscode-textmate/-/vscode-textmate-8.0.0.tgz", + "integrity": "sha512-AFbieoL7a5LMqcnOF04ji+rpXadgOXnZsxQr//r83kLPr7biP7am3g9zbaZIaBGwBRWeSvoMD4mgPdX3e4NWBg==" + }, + "node_modules/web-namespaces": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/web-namespaces/-/web-namespaces-2.0.1.tgz", + "integrity": "sha512-bKr1DkiNa2krS7qxNtdrtHAmzuYGFQLiQ13TsorsdT6ULTkPLKuu5+GsFpDlg6JFjUTwX2DyhMPG2be8uPrqsQ==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/web-worker": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/web-worker/-/web-worker-1.3.0.tgz", + "integrity": "sha512-BSR9wyRsy/KOValMgd5kMyr3JzpdeoR9KVId8u5GVlTTAtNChlsE4yTxeY7zMdNSyOmoKBv8NH2qeRY9Tg+IaA==" + }, + "node_modules/which": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", + "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "which": "bin/which" + } + }, + "node_modules/wrap-ansi": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", + "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", + "dev": true, + "dependencies": { + "ansi-styles": "^6.1.0", + "string-width": "^5.0.1", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrap-ansi-cjs": { + "name": "wrap-ansi", + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/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, + "engines": { + "node": ">=8" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "node_modules/wrap-ansi-cjs/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true + }, + "node_modules/wrap-ansi-cjs/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/wrap-ansi/node_modules/ansi-styles": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", + "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/yallist": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz", + "integrity": "sha512-ncTzHV7NvsQZkYe1DW7cbDLm0YpzHmZF5r/iyP3ZnQtMiJ+pjzisCiMNI+Sj+xQF5pXhSHxSB3uDbsBTzY/c2A==" + }, + "node_modules/yaml": { + "version": "2.4.5", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.4.5.tgz", + "integrity": "sha512-aBx2bnqDzVOyNKfsysjA2ms5ZlnjSAW2eG3/L5G/CSujfjLJTJsEw1bGw8kCf04KodQWk1pxlGnZ56CRxiawmg==", + "dev": true, + "bin": { + "yaml": "bin.mjs" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/yocto-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/zod": { + "version": "3.23.8", + "resolved": "https://registry.npmjs.org/zod/-/zod-3.23.8.tgz", + "integrity": "sha512-XBx9AXhXktjUqnepgTiE5flcKIYWi/rme0Eaj+5Y0lftuGBq+jyRu/md4WnuxqgP1ubdpNCsYEYPxrzVHD8d6g==", + "funding": { + "url": "https://github.com/sponsors/colinhacks" + } + }, + "node_modules/zwitch": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/zwitch/-/zwitch-2.0.4.tgz", + "integrity": "sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + } + } +} diff --git a/docs/package.json b/docs/package.json new file mode 100644 index 000000000..084e2eaa3 --- /dev/null +++ b/docs/package.json @@ -0,0 +1,21 @@ +{ + "dependencies": { + "next": "^14.2.4", + "nextra": "^2.13.4", + "nextra-theme-docs": "^2.13.4", + "react": "^18.3.1", + "react-dom": "^18.3.1", + "react-svg": "^16.1.34" + }, + "scripts": { + "dev": "next", + "build": "next build" + }, + "devDependencies": { + "@types/node": "20.14.5", + "autoprefixer": "^10.4.19", + "postcss": "^8.4.38", + "tailwindcss": "^3.4.4", + "typescript": "5.4.5" + } +} diff --git a/docs/pages/_app.mdx b/docs/pages/_app.mdx new file mode 100644 index 000000000..4700262ae --- /dev/null +++ b/docs/pages/_app.mdx @@ -0,0 +1,5 @@ +import '../style.css' + +export default function MyApp({ Component, pageProps }) { + return +} diff --git a/docs/pages/_meta.json b/docs/pages/_meta.json new file mode 100644 index 000000000..82b0ecf30 --- /dev/null +++ b/docs/pages/_meta.json @@ -0,0 +1,17 @@ +{ + "index": { + "type": "page", + "title": "Home", + "theme": { + "layout": "raw" + } + }, + "docs": { + "type": "page", + "title": "Documentation" + }, + "community": { + "type": "page", + "title": "Community" + } +} diff --git a/docs/pages/community/index.mdx b/docs/pages/community/index.mdx new file mode 100644 index 000000000..dd60b73c6 --- /dev/null +++ b/docs/pages/community/index.mdx @@ -0,0 +1,8 @@ +# Community +- Join the chat in the [Telegram group](https://t.me/quint_lang) or in the [Zulip stream](https://informal-systems.zulipchat.com/#narrow/stream/378959-quint) +- Join the [Quint discussions on GitHub](https://github.com/informalsystems/quint/discussions) +- [Contribute your spell](https://github.com/informalsystems/quint/tree/main/examples/spells/contribute-your-spell.md) to the collection of Quint spells +- [Contribute](https://github.com/informalsystems/quint/tree/main/CONTRIBUTING.md) to the development of Quint +- Join our co-design meetings: We hold fortnightly meetings with users and those + interested in contributing to the design and development of Quint. Contact us if + you would like an invitation. diff --git a/docs/pages/docs/_meta.json b/docs/pages/docs/_meta.json new file mode 100644 index 000000000..a0bbcf49d --- /dev/null +++ b/docs/pages/docs/_meta.json @@ -0,0 +1,25 @@ +{ + "index": "Introduction", + "getting-started": "Getting Started", + "lang": "Language Manual", + "quint": "CLI Manual", + "builtin": "Built-in Operators", + "previews": "Tool previews", + "faq": "FAQ", + "-- Learning": { + "type": "separator", + "title": "Learning" + }, + "repl": "Using the REPL", + "lessons": "Lessons", + "blogposts": "Blog Posts", + "literate": "Literate Specifications", + "-- Development": { + "type": "separator", + "title": "Design & Development" + }, + "design-principles": "", + "architecture-decision-records": "Architecture Decision Records (ADRs)", + "rfcs": "Requests for Comments (RFCs)", + "stories": "User Stories" +} diff --git a/docs/pages/docs/architecture-decision-records/_meta.json b/docs/pages/docs/architecture-decision-records/_meta.json new file mode 100644 index 000000000..c028b1023 --- /dev/null +++ b/docs/pages/docs/architecture-decision-records/_meta.json @@ -0,0 +1,10 @@ +{ + "adr001-transpiler-architecture": "Transpiler architecture", + "adr002-errors": "Errors", + "adr003-visiting-ir-components": "Visiting IR components", + "adr004-effect-system": "Effect System", + "adr005-type-system": "Type System", + "adr006-modules.lit": "Modules", + "adr007-flattening": "Flattening", + "adr008-managing-apalache": "Managing Apalache" +} diff --git a/doc/adr001-transpiler-architecture.md b/docs/pages/docs/architecture-decision-records/adr001-transpiler-architecture.md similarity index 99% rename from doc/adr001-transpiler-architecture.md rename to docs/pages/docs/architecture-decision-records/adr001-transpiler-architecture.md index e491d1cff..c9e35f412 100644 --- a/doc/adr001-transpiler-architecture.md +++ b/docs/pages/docs/architecture-decision-records/adr001-transpiler-architecture.md @@ -396,5 +396,5 @@ field `apalacheJson`: it may emit an error, see [Errors][]. -[Errors]: ./adr002-errors.md +[Errors]: ./adr002-errors [Apalache JSON]: https://apalache.informal.systems/docs/adr/005adr-json.html diff --git a/doc/adr002-errors.md b/docs/pages/docs/architecture-decision-records/adr002-errors.md similarity index 100% rename from doc/adr002-errors.md rename to docs/pages/docs/architecture-decision-records/adr002-errors.md diff --git a/doc/adr003-visiting-ir-components.md b/docs/pages/docs/architecture-decision-records/adr003-visiting-ir-components.md similarity index 100% rename from doc/adr003-visiting-ir-components.md rename to docs/pages/docs/architecture-decision-records/adr003-visiting-ir-components.md diff --git a/doc/adr004-effect-system.md b/docs/pages/docs/architecture-decision-records/adr004-effect-system.md similarity index 99% rename from doc/adr004-effect-system.md rename to docs/pages/docs/architecture-decision-records/adr004-effect-system.md index e2c9fa07c..ca7a00f86 100644 --- a/doc/adr004-effect-system.md +++ b/docs/pages/docs/architecture-decision-records/adr004-effect-system.md @@ -375,14 +375,14 @@ takes a list of component kinds and returns a function that takes the arity of the operator and returns the signature for that arity. The signature for arity 2 is the following: -```bluespec +```quint propagateComponents(['read', 'temporal'])(2): (Read[r1] & Temporal[t1], Read[r2] & Temporal[t2]) => Read[r1, r2] & Temporal[t1, t2] ``` Here are some examples of signatures for non-general operators: -```bluespec +```quint always: (Read[r] & Temporal[t]) => Temporal[r, t] guess: (Read[r1], (Read[r1]) => Read[r2] & Update[u]) => Read[r1, r2] & Update[u] assign: (Read[r1], Read[r2]) => Read[r2] & Update[r1] @@ -398,7 +398,7 @@ inferred effect. Consider the following example: -```bluespec +```quint module foo { var x: int @@ -431,7 +431,7 @@ possible to report meaningful error messages. Consider the following spec that updates variable `x` twice: -```bluespec +```quint module foo { var x: int diff --git a/doc/adr005-type-system.md b/docs/pages/docs/architecture-decision-records/adr005-type-system.md similarity index 98% rename from doc/adr005-type-system.md rename to docs/pages/docs/architecture-decision-records/adr005-type-system.md index a80d5fdcd..4f8ee6070 100644 --- a/doc/adr005-type-system.md +++ b/docs/pages/docs/architecture-decision-records/adr005-type-system.md @@ -25,7 +25,7 @@ as basis, the proposed type system should be able to infer simple types, match annotations and handle row types from records and tuples variants. The type grammar for Quint is already defined at the [Language -Manual](https://github.com/informalsystems/quint/blob/main/doc/lang.md#type-system-12), +Manual](/docs/lang), so this ADR will cover only type inference and type checking. ## Options diff --git a/doc/adr006-modules.lit.md b/docs/pages/docs/architecture-decision-records/adr006-modules.lit.md similarity index 98% rename from doc/adr006-modules.lit.md rename to docs/pages/docs/architecture-decision-records/adr006-modules.lit.md index 06ff0b96f..b17815bc2 100644 --- a/doc/adr006-modules.lit.md +++ b/docs/pages/docs/architecture-decision-records/adr006-modules.lit.md @@ -64,7 +64,7 @@ We encapsulate pure functional definitions in a distinct module (called `fun` below), and import its definitions in the module that specifies the general behavior of the state machine (called `general` below): -```bluespec generated/adr006before.qnt += +```quint generated/adr006before.qnt += module fun { pure def dec(i) = i - 1 pure def inc(i) = i + 1 @@ -109,7 +109,7 @@ graph LR We define constants, variables, and actions of `general` as follows: -```bluespec "definitions" += +```quint "definitions" += const N: int var x: int @@ -122,7 +122,7 @@ We define constants, variables, and actions of `general` as follows: Having defined `general`, we define its fixed-size instances in the module `fixed`: -```bluespec generated/adr006before.qnt += +```quint generated/adr006before.qnt += module fixed { module I3 = general(N = 3) module I5 = general(N = 5) @@ -243,7 +243,7 @@ currently point to the same variable. Recall how we produced new instances in our example: -```bluespec +```quint module general { module I3 = general(N = 3) module I5 = general(N = 5) @@ -345,7 +345,7 @@ operator parameters. This rework is planned in [issue Let's revisit the definition of the module `fun`: -```bluespec generated/adr006after.qnt += +```quint generated/adr006after.qnt += module fun { pure def dec(i) = i - 1 pure def inc(i) = i + 1 @@ -420,7 +420,7 @@ graph TB Now, let's have a look at the module `general`: -```bluespec generated/adr006after.qnt += +```quint generated/adr006after.qnt += module general { import fun.* @@ -511,7 +511,7 @@ module and tune them with the supplied expressions for the constants. Effectively, the flattener would transform the module `fixed` of our example into the following code: -```bluespec generated/adr006instances.qnt += +```quint generated/adr006instances.qnt += module fixed { // the instance general(N = 3) @@ -557,7 +557,7 @@ The last name may be replaced with `_` to indicate that no prefix is required. Using this syntax, our example looks like follows: -```bluespec +```quint module fixed { import general(N = 3).* import general(N = 5) as I5 diff --git a/doc/adr007-flattening.md b/docs/pages/docs/architecture-decision-records/adr007-flattening.md similarity index 99% rename from doc/adr007-flattening.md rename to docs/pages/docs/architecture-decision-records/adr007-flattening.md index 19f5bb594..df6cec015 100644 --- a/doc/adr007-flattening.md +++ b/docs/pages/docs/architecture-decision-records/adr007-flattening.md @@ -89,7 +89,7 @@ For instances, we need to ensure uniqueness, since the same names in the instanc ##### Namespaces for exports Exports have a particularity: they can remove namespaces. Consider the example: -```bluespec +```quint module A { const N: int val a = N @@ -106,7 +106,7 @@ In this example, `export A1.*` is taking the definitions from `A1` and making th This scenario is tricky and, as discussed in a Quint meeting, we could not support it in the first version. Current flattening does support this, but it is not able to distinguish different instances, so problems arise. We are not sure how useful and clear this scenario is for users. So, exports that change the namespaces of the previously imported definitions are not supported in the first version of the new flattener. We should raise a proper error when an export like that is found. The example above could be rewritten as: -```bluespec +```quint module A { const N: int val a = N @@ -137,7 +137,7 @@ All of this tinkering on names and the addition of a matching import statement m #### Example Take this module as example, where `A` is instanced twice with the same name: -```bluespec +```quint module A { const N: int // id 1 val a = N // id 2 @@ -155,7 +155,7 @@ module C { ``` The instance flattener will create two new modules: `B::A1` and `C::A1`, with new ids for the definitions inside it. -```bluespec +```quint module A { const N: int // id 1 val a = N // id 2 diff --git a/doc/adr008-managing-apalache.md b/docs/pages/docs/architecture-decision-records/adr008-managing-apalache.md similarity index 100% rename from doc/adr008-managing-apalache.md rename to docs/pages/docs/architecture-decision-records/adr008-managing-apalache.md diff --git a/tutorials/blogpost0-secretsanta/secret_santa.org b/docs/pages/docs/blogposts/secret_santa.mdx similarity index 69% rename from tutorials/blogpost0-secretsanta/secret_santa.org rename to docs/pages/docs/blogposts/secret_santa.mdx index 4edc24063..084a585af 100644 --- a/tutorials/blogpost0-secretsanta/secret_santa.org +++ b/docs/pages/docs/blogposts/secret_santa.mdx @@ -1,30 +1,50 @@ -* Holiday protocols: secret santa with Quint 🎅 - -Hi everyone, and happy holiday season! I'm Gabriela, I work on [[https://github.com/informalsystems/quint][Quint]], and today I'll walk you through my end-of-the-year holiday experiment: writing a specification for the secret santa game :santa:. - -Secret santa is a game for trading presents between participants. We write down the participants' names on individual pieces of paper, fold them, and put them in a bowl. The bowl is passed around to the participants, with each of them taking one piece of paper from the bowl, deciding the person the picker should gift a present to. This goes on until the bowl is empty, and everyone has someone to gift :gift:. - -I played this game at least once a year since I was a little kid, and well, different groups have different standards for the drawing process. I'm always one of the (potentially annoying) people who will make everyone play by the safest rules (well, that applies to many other scenarios, and I guess that's how I ended up working on Quint :grimacing:). So welcome aboard if you are one of these people too, and if you are not, this blogpost might introduce some complications for your next secret santa game. - -Disclaimer: This is not a super didactic tutorial, but rather a fun example of how I personally appreciate Quint (or formal specification in general). If you are looking for tutorials, we have some better introductory material [[https://github.com/informalsystems/quint/tree/main/tutorials][here]], and more documentation [[https://github.com/informalsystems/quint/tree/main/doc][here]]. In any way, I still recommend skimming through this as a potential motivational material :heart:. - -The complete spec described here is available in the [[https://github.com/informalsystems/quint/tree/main/examples/games/secret-santa/secret_santa.qnt][examples folder]]. - -** Basic secret santa - -Let's start with the basics: we have a =bowl= of recipient names. As each +# Holiday protocols: secret santa with Quint 🎅 + +Hi everyone, and happy holiday season! I'm Gabriela, I work on +[Quint](https://github.com/informalsystems/quint), and today I'll walk you +through my end-of-the-year holiday experiment: writing a specification for the +secret santa game 🎅. + +Secret santa is a game for trading presents between participants. We write down +the participants' names on individual pieces of paper, fold them, and put them +in a bowl. The bowl is passed around to the participants, with each of them +taking one piece of paper from the bowl, deciding the person the picker should +gift a present to. This goes on until the bowl is empty, and everyone has +someone to gift 🎁. + +I played this game at least once a year since I was a little kid, and well, +different groups have different standards for the drawing process. I'm always +one of the (potentially annoying) people who will make everyone play by the +safest rules (well, that applies to many other scenarios, and I guess that's how +I ended up working on Quint 😬). So welcome aboard if you are one of these +people too, and if you are not, this blogpost might introduce some complications +for your next secret santa game. + +Disclaimer: This is not a super didactic tutorial, but rather a fun example of +how I personally appreciate Quint (or formal specification in general). If you +are looking for tutorials, we have some better introductory material +[here](/docs/lessons), and more +documentation [here](/docs). In +any way, I still recommend skimming through this as a potential motivational +material ❤️. + +The complete spec described here is available in the [examples folder](https://github.com/informalsystems/quint/tree/main/examples/games/secret-santa/secret_santa.qnt). + +## Basic secret santa + +Let's start with the basics: we have a `bowl` of recipient names. As each participant draws its recipient, we record the drawn name in a map called -=recipient_for_santa=. The keys of the map are the santas giving gifts, and the -values are the gift-receivers for each santa. Those will be our two *state -variables*, meaning we'll be changing their values as the game progresses. +`recipient_for_santa`. The keys of the map are the santas giving gifts, and the +values are the gift-receivers for each santa. Those will be our two **state +variables**, meaning we'll be changing their values as the game progresses. The set of participants in the game is a constant, which can be seen as a module parameter (more on that later). We also import a library with some utility functions. The import statement -should point to the [[https://github.com/informalsystems/quint/blob/main/examples/spells/basicSpells.qnt][basicSpells.qnt]] file, from the quint repo, on your local file +should point to the [basicSpells.qnt](https://github.com/informalsystems/quint/blob/main/examples/spells/basicSpells.qnt) file, from the quint repo, on your local file system. -#+begin_src bluespec +```quint module secret_santa { import basicSpells.* from "../../spells/basicSpells" @@ -36,27 +56,28 @@ module secret_santa { /// The bowl of participants, containing a paper piece for each participant's name var bowl: Set[str] } -#+end_src +``` Now that we have the state variables, we need to specify their values in the initial state: -#+begin_src bluespec + +```quint /// The initial state action init = all { recipient_for_santa' = Map(), // No santas or recipients bowl' = participants, // Every participant's name in the bowl } -#+end_src +``` +The following definitions are helpers: the set of santas are the keys of the `recipent_for_santa` map, while the recipients are the values of that map. These values are not fixed. Rather, they work as projections, or views, on the current state. When the values for the `recipient_for_santa` state variable change, the values for these helpers will change as well. -The following definitions are helpers: the set of santas are the keys of the =recipent_for_santa= map, while the recipients are the values of that map. These values are not fixed. Rather, they work as projections, or views, on the current state. When the values for the =recipient_for_santa= state variable change, the values for these helpers will change as well. -#+begin_src bluespec +```quint val santas = recipient_for_santa.keys() val recipients = recipient_for_santa.values() -#+end_src +``` -Finally, we define the system's transitions, which is just the drawing process. At each step, we non-deterministically pick the participant who is going to draw, and then, non-deterministically, draw one of the names from the bowl. We register that on our =recipient_for_santa= map and remove the drawn name from the bowl. When the bowl gets empty, we still need to define what happens. In this case, the drawing is over, and all state variables will stay the same - at least until the next holidays :christmas_tree:. When no variables change, we call that a =stutter= step. +Finally, we define the system's transitions, which is just the drawing process. At each step, we non-deterministically pick the participant who is going to draw, and then, non-deterministically, draw one of the names from the bowl. We register that on our `recipient_for_santa` map and remove the drawn name from the bowl. When the bowl gets empty, we still need to define what happens. In this case, the drawing is over, and all state variables will stay the same - at least until the next holidays :christmas_tree:. When no variables change, we call that a `stutter` step. -#+begin_src bluespec +```quint action draw_recipient(santa: str): bool = { nondet recipient = oneOf(bowl) all { @@ -77,23 +98,23 @@ action step = nondet next_santa = participants.exclude(santas).oneOf() draw_recipient(next_santa) } -#+end_src +``` This is a very basic protocol, but already ensures one of the properties I'm interested in: no sad kids with no presents at the revelation - everyone gets a santa. That is, if the bowl is empty. In other words, the bowl being empty -*implies* that all the participants will receive gifts :gift:. +**implies** that all the participants will receive gifts 🎁. -#+begin_src bluespec +```quint val everyone_gets_a_santa = empty(bowl) implies (participants == recipients) -#+end_src +``` Before we can check that our spec satisfies this property, we need to define a value for our module's constant. Constants are parameters to a module, so we need -to instantiate that module and specify the =participants=. Let's use the Quint +to instantiate that module and specify the `participants`. Let's use the Quint developer team as an example! -#+begin_src bluespec +```quint module secret_santa { // ... all the code from before } @@ -101,60 +122,65 @@ module secret_santa { module quint_team_secret_santa { import secret_santa(participants = Set("Gabriela", "Igor", "Jure", "Shon", "Thomas")).* } -#+end_src +``` -Now we can use Quint's =verify= command to check that all possible executions -(up to a certain length -- 10 steps, by default) satisfy this property for the =quint_team_secret_santa= module. +Now we can use Quint's `verify` command to check that all possible executions +(up to a certain length -- 10 steps, by default) satisfy this property for the `quint_team_secret_santa` module. -#+begin_src sh +```sh quint verify secret_santa.qnt --main=quint_team_secret_santa --invariant=everyone_gets_a_santa -#+end_src +``` We get a successful result - the property holds! -#+begin_src sh + +```sh [ok] No violation found (2119ms). You may increase --max-steps. -#+end_src +``` Let's try a different property! People don't really want to buy themselves -presents. /Well, perhaps except for that teenage cousin who would rather not be -playing, but their mom forced them to. But let's not consider that./ So let's +presents. *Well, perhaps except for that teenage cousin who would rather not be +playing, but their mom forced them to. But let's not consider that.* So let's write a property stating that, for each santa, they are not their own recipient. -#+begin_src bluespec + +```quint val no_person_is_self_santa = santas.forall(santa => get(recipient_for_santa, santa) != santa ) -#+end_src +``` And then check that property: -#+begin_src sh + +```sh quint verify secret_santa.qnt --main=quint_team_secret_santa --invariant=no_person_is_self_santa -#+end_src +``` -This time, we find out that the property does *not* hold! Quint provides us with a minimal counterexample. -#+begin_src bluespec +This time, we find out that the property does **not** hold! Quint provides us with a minimal counterexample. +```sh An example execution: [State 0] { quint_team_secret_santa::secret_santa::bowl: Set("Gabriela", "Igor", "Jure", "Shon", "Thomas"), quint_team_secret_santa::secret_santa::recipient_for_santa: Map() } [State 1] { quint_team_secret_santa::secret_santa::bowl: Set("Igor", "Jure", "Shon", "Thomas"), quint_team_secret_santa::secret_santa::recipient_for_santa: Map("Gabriela" -> "Gabriela") } - + [violation] Found an issue (2068ms). error: found a counterexample -#+end_src +``` In this example, Gabriela (that's me) got themself in the very first draw and would have to buy their own present. Not great! A better secret santa game has an additional, currently unspecified, step: after a person draws a name from the bowl, they should confirm that they didn't get themself before the drawing can continue. If someone draws themself, one of two strategies can take place: - 1. They immediately put their name back and draw again. - 2. The whole game gets reset: everyone who has already drawn a paper puts it back, and it all starts again. + +1. They immediately put their name back and draw again. +2. The whole game gets reset: everyone who has already drawn a paper puts it back, and it all starts again. I want to try both strategies in Quint, and then show how (2) is better than (1). Usually, people don't want to reset the whole thing and get tempted to do (1), and that's when I say "no no no no no". Well, now I can show them the Quint spec and counterexamples, and they won't have any arguments! But first, let's add the confirmation step to our existing spec, which will be used by both strategies. -First, we add a variable and change our =init= definition to include an initialization for it. We should also make sure it also stutters in our =stutter= helper definition. -#+begin_src bluespec +First, we add a variable and change our `init` definition to include an initialization for it. We should also make sure it also stutters in our `stutter` helper definition. + +```quint /// Information about the last draw type LastDraw = | Self(str) // The name of someone who drew themself @@ -171,32 +197,35 @@ action stutter = all { // ... last_draw' = last_draw, } -#+end_src +``` -Then, we update the =draw_recipent= action to include the confirmation, that is, =last_draw= should be updated to the participant who drew, and its confirmed value should be true if and only if the participant got someone other than themself. -#+begin_src bluespec +Then, we update the `draw_recipent` action to include the confirmation, that is, `last_draw` should be updated to the participant who drew, and its confirmed value should be true if and only if the participant got someone other than themself. + +```quint action draw_recipient(santa: str): bool = { - nondet recipient = oneOf(bowl) - all { - // ... - last_draw' = if (santa == recipient) Self(santa) else Ok +nondet recipient = oneOf(bowl) +all { + // ... + last_draw' = if (santa == recipient) Self(santa) else Ok } -#+end_src +``` -With this variable in place, we can now write a new invariant stating that our predicate =no_person_is_self_santa= should be satisfied if the last draw was ok. That is, the condition *implies* the predicate. -#+begin_src bluespec +With this variable in place, we can now write a new invariant stating that our predicate `no_person_is_self_santa` should be satisfied if the last draw was ok. That is, the condition **implies** the predicate. + +```quint val inv = (last_draw == Ok) implies no_person_is_self_santa -#+end_src +``` Defining the variable on its own doesn't help us with satisfying that property, tho. We need to define a strategy to deal with scenarios where the last draw didn't turn out right. -** Secret santa with the redraw strategy + +## Secret santa with the redraw strategy In this strategy, when a participant draws themself, they should pick another name. They can either first put their name back in the bowl, and then redraw; or first redraw and then put their name back, ensuring they won't get themself again. Both of them should work the same in the end, since we are not considering efficiency here. But let's define the latter. -#+begin_src bluespec +```quint action redraw(participant: str): bool = { // Draw from the current bowl, without having first put paper back nondet new_recipient = oneOf(bowl) @@ -208,10 +237,11 @@ action redraw(participant: str): bool = { last_draw' = Ok // We know this is OK due to the precondition } } -#+end_src +``` We should call the redraw action whenever we find a self-draw. It is important to ensure that the bowl is not empty when we call that action since we need at least one name in the bowl to be drawn. -#+begin_src bluespec + +```quint action step_with_redraw = match last_draw { | Ok => @@ -221,60 +251,69 @@ action step_with_redraw = redraw(participant), } } -#+end_src +``` -Let's check if our new invariant =inv= (defined as =(last_draw == Ok) implies no_person_is_self_santa=) is satisfied with our new step definition. We now have to specify our step definition with =--step=step_with_redraw=, otherwise, Quint will use the default name =step=. -#+begin_src sh +Let's check if our new invariant `inv` (defined as `(last_draw == Ok) implies no_person_is_self_santa` is satisfied with our new step definition. We now have to specify our step definition with `--step=step_with_redraw`, otherwise, Quint will use the default name `step`. + +```sh quint verify secret_santa.qnt --main=quint_team_secret_santa --invariant=inv --step=step_with_redraw -#+end_src +``` The property is successfully checked. -#+begin_src sh + +```sh [ok] No violation found (4360ms). -#+end_src +``` -** Secret santa with the reset strategy -In the reset strategy, we restart the whole game when some confirmation is negative. The definition looks quite nice! We could define this in a different module and plug everything together with import statements - but let's keep things simpler here and define yet another step action called =step_with_reset=. -#+begin_src bluespec +## Secret santa with the reset strategy + +In the reset strategy, we restart the whole game when some confirmation is negative. The definition looks quite nice! We could define this in a different module and plug everything together with import statements - but let's keep things simpler here and define yet another step action called `step_with_reset`. + +```quint action step_with_reset = if (last_draw == Ok) { step } else { init } -#+end_src +``` Now, let's check the property. -#+begin_src sh + +```sh quint verify secret_santa.qnt --main=quint_team_secret_santa --invariant=inv --step=step_with_reset -#+end_src +``` The property is successfully checked. -#+begin_src sh + +```sh [ok] No violation found (2492ms). -#+end_src +``` -** Redrawing is not good enough! + +## Redrawing is not good enough! Although both strategies can guarantee that, if the last draw was confirmed, then no person is their own santa, I still see two scenarios where the redraw strategy might have problems, while the reset strategy does not. -1. If the player who draws themself is the last player, and the bowl gets empty, there is nothing to be done to solve the issue. -2. If some of the players have a good memory and pay attention, they will have information about who may and may not be someone's santa, and even potentially find out who is a santa of someone, or their own santa! We need to preserve the "secret" part of this game! + +1. If the player who draws themself is the last player, and the bowl gets empty, there is nothing to be done to solve the issue. +2. If some of the players have a good memory and pay attention, they will have information about who may and may not be someone's santa, and even potentially find out who is a santa of someone, or their own santa! We need to preserve the "secret" part of this game! To show how (1) can happen, we should use temporal properties. However, since that requires a deeper explanation, and the tooling for it is not the most stable at the moment, I'll leave this one for next year. Instead, let's play around with (2). (2) does not need all the players to have a great memory, so let's say only I (Gabriela) am actually paying attention and memorizing some stuff. For that, let's introduce my memory as a state variable. -#+begin_src bluespec + +```quint /// Who had already drawn a paper when someone got themself. This way, I know /// that none of those people can be the santa of the person who is drawing. var gabrielas_memory: str -> Set[str] -#+end_src +``` -This is what I will be memorizing during the game: for each person that gets themself, who has already drawn by the time they got themself. Let's think of an example, using the Quint team as the set of participants (that is =Set("Gabriela", "Igor", "Jure", "Shon", "Thomas")=): Let's say Shon draws and confirms, then Igor draws and confirms, then Thomas draws and makes a negative confirmation. At that point, I know that neither Shon nor Igor had drawn Thomas, otherwise, the paper with Thomas' name wouldn't be in the bowl when he drew. So my memory map becomes =Map("Thomas" -> Set("Shon", "Igor", "Thomas"))=, which tells me that neither Shon nor Igor is Thomas' santa. Thomas themself is also part of the set because that makes things easier to represent, but we could also choose to remove the participant themself from the set. +This is what I will be memorizing during the game: for each person that gets themself, who has already drawn by the time they got themself. Let's think of an example, using the Quint team as the set of participants (that is `Set("Gabriela", "Igor", "Jure", "Shon", "Thomas")`): Let's say Shon draws and confirms, then Igor draws and confirms, then Thomas draws and makes a negative confirmation. At that point, I know that neither Shon nor Igor had drawn Thomas, otherwise, the paper with Thomas' name wouldn't be in the bowl when he drew. So my memory map becomes `Map("Thomas" -> Set("Shon", "Igor", "Thomas"))`, which tells me that neither Shon nor Igor is Thomas' santa. Thomas themself is also part of the set because that makes things easier to represent, but we could also choose to remove the participant themself from the set. This memorization is only relevant in the redraw strategy, as the reset strategy reinitializes the whole process on self-sdraws, making any memorization useless from that point on. Therefore, let's only play with memorization in the redraw version. For that let's define how the memory variable is initialized and updated: -#+begin_src bluespec +```quint action init = all { // ... gabrielas_memory' = Map(), @@ -307,14 +346,15 @@ action step_with_redraw = memorize(participant), } } -#+end_src +``` + -*** Is there a scenario where I find out who is someone's santa? +### Is there a scenario where I find out who is someone's santa? Now let's define a property that is true when I am able to deduce someone's santa: -#+begin_src bluespec +```quint /// true iff Gabriela can find out who is a santa for someone. /// That is, if exists a participant where find_out_a_santa_for participant is Some() val gabriela_finds_out_a_santa = participants.exists(participant => { @@ -325,20 +365,23 @@ val gabriela_finds_out_a_santa = participants.exists(participant => { false } }) -#+end_src +``` Finally, the invariant we want to check is that Gabriela should NOT be able to find out a santa. -#+begin_src bluespec + +```quint val safe_from_memorizers = not(gabriela_finds_out_a_santa) -#+end_src +``` + +Let's verify it, with the redraw version of `step_for_confirmation` -Let's verify it, with the redraw version of =step_for_confirmation= -#+begin_src sh +```sh quint verify secret_santa.qnt --main=quint_team_secret_santa --invariant=safe_from_memorizers --step=step_with_redraw -#+end_src +``` -We get a violation! After 5 steps, we get to a point where I know that Shon is my santa :santa: -#+begin_src bluespec +We get a violation! After 5 steps, we get to a point where I know that Shon is my santa 🎅 + +```sh ... [State 5] { @@ -352,12 +395,13 @@ We get a violation! After 5 steps, we get to a point where I know that Shon is m [violation] Found an issue (2628ms). error: found a counterexample -#+end_src +``` -On state 5, my memory is =Map("Gabriela" -> Set("Gabriela", "Igor", "Jure", "Thomas"))=, so only Shon can possibly be my (non-secret) santa! +On state 5, my memory is `Map("Gabriela" -> Set("Gabriela", "Igor", "Jure", "Thomas"))`, so only Shon can possibly be my (non-secret) santa! Here, we only checked for the presence of the worst scenario: finding out someone's santa. This only happens if the second to last person redraws, and therefore their name is the only one in the bowl when the last person (in this case, Shon) draws. However, memorizers can also find partial information that can also ruin the game a bit, i.e. knowing for sure that a person who always gives the best gifts couldn't possibly have drawn me - that's a bummer, right? -Well, what actually bothers me is having possible flaws in the drawing protocol like this, and that's why every year I insist on the reset strategy. There are some other interesting properties of secret santa that I'd like to explore, especially in the revelation procedure. But it's almost Christmas already, which means it's time for me to get ready for some beach time: I'm in Brazil, and we get Christmas during summer, and that's my favorite time of the year :sunny:. So let's talk about secret santa Quint specs again next year. +Well, what actually bothers me is having possible flaws in the drawing protocol like this, and that's why every year I insist on the reset strategy. There are some other interesting properties of secret santa that I'd like to explore, especially in the revelation procedure. But it's almost Christmas already, which means it's time for me to get ready for some beach time: I'm in Brazil, and we get Christmas during summer, and that's my favorite time of the year ☀️. So let's talk about secret santa Quint specs again next year. Wish you all a happy holiday season and a lovely new year! + diff --git a/doc/builtin.md b/docs/pages/docs/builtin.md similarity index 77% rename from doc/builtin.md rename to docs/pages/docs/builtin.md index 141979d92..dcf79dfb7 100644 --- a/doc/builtin.md +++ b/docs/pages/docs/builtin.md @@ -1,38 +1,50 @@ # Documentation for builtin -## `pure val Nat: Set[int]` +## Nat + +Signature: `pure val Nat: Set[int]` The infinite set of all natural numbers. Infinite sets cannot be enumerated. Hence some operators that require iteration may be unsupported. -## `pure val Int: Set[int]` +## Int + +Signature: `pure val Int: Set[int]` The infinite set of all integers. Infinite sets cannot be enumerated. Hence some operators that require iteration may be unsupported. -## `pure val Bool: Set[bool]` +## Bool + +Signature: `pure val Bool: Set[bool]` The set of all booleans That is, Set(false, true) -## `pure def eq: (t, t) => bool` +## eq + +Signature: `pure def eq: (t, t) => bool` `a.eq(b)` is `true` when `a` and `b` are equal values of the same type. It can be used in the infix form as `==` or as a named operator `eq`. -## `pure def neq: (t, t) => bool` +## neq + +Signature: `pure def neq: (t, t) => bool` `a.neq(b)` is `true` when `a` and `b` are not equal values of the same type. It can be used in the infix form as `!=` or as a named operator `neq`. -## `pure def iff: (bool, bool) => bool` +## iff + +Signature: `pure def iff: (bool, bool) => bool` `p.iff(q)` is `true` when `p` and `q` are equal values of the bool type. @@ -40,14 +52,16 @@ This is the logical equivalence operator. ### Examples -``` +```quint assert(iff(true, true)) assert(iff(false, false)) assert(not(iff(true, false))) assert(not(iff(false, true))) ``` -## `pure def implies: (bool, bool) => bool` +## implies + +Signature: `pure def implies: (bool, bool) => bool` `p.implies(q)` is true when `not(p) or q` is true. @@ -55,20 +69,24 @@ This is the material implication operator. ### Examples -``` +```quint assert(true.implies(true)) assert(false.implies(false)) assert(not(true.implies(false))) assert(not(false.implies(true))) ``` -## `pure def not: (bool) => bool` +## not + +Signature: `pure def not: (bool) => bool` `not(p)` is `true` when `p` is `false`. This is the negation opearator. -## `pure def exists: (Set[a], (a) => bool) => bool` +## exists + +Signature: `pure def exists: (Set[a], (a) => bool) => bool` `s.exists(p)` is true when there is an element in `s` that satisfies `p`. @@ -76,12 +94,14 @@ This is the existential quantifier. ### Examples -``` +```quint assert(Set(1, 2, 3).exists(n => n == 2)) assert(not(Set(1, 2, 3).exists(n => n == 4))) ``` -## `pure def forall: (Set[a], (a) => bool) => bool` +## forall + +Signature: `pure def forall: (Set[a], (a) => bool) => bool` `s.forall(p)` is true when all elements in `s` satisfy `p`. @@ -89,12 +109,14 @@ This is the universal quantifier. ### Examples -``` +```quint assert(Set(1, 2, 3).forall(n => n > 0)) assert(not(Set(1, 2, 3).forall(n => n > 1))) ``` -## `pure def in: (a, Set[a]) => bool` +## in + +Signature: `pure def in: (a, Set[a]) => bool` `e.in(s)` is true when the element `e` is in the set `s`. @@ -104,12 +126,14 @@ See also: `contains` ### Examples -``` +```quint assert(1.in(Set(1, 2, 3))) assert(not(4.in(Set(1, 2, 3)))) ``` -## `pure def contains: (Set[a], a) => bool` +## contains + +Signature: `pure def contains: (Set[a], a) => bool` `s.contains(e)` is true when the element `e` is in the set `s`. @@ -119,12 +143,14 @@ See also: `in` ### Examples -``` +```quint assert(Set(1, 2, 3).contains(1)) assert(not(Set(1, 2, 3).contains(4))) ``` -## `pure def union: (Set[a], Set[a]) => Set[a]` +## union + +Signature: `pure def union: (Set[a], Set[a]) => Set[a]` `s1.union(s2)` is the set of elements that are in `s1` or in `s2`. @@ -132,11 +158,13 @@ This is the set union operator. ### Examples -``` +```quint assert(Set(1, 2, 3).union(Set(2, 3, 4)) == Set(1, 2, 3, 4)) ``` -## `pure def intersect: (Set[a], Set[a]) => Set[a]` +## intersect + +Signature: `pure def intersect: (Set[a], Set[a]) => Set[a]` `s1.intersect(s2)` is the set of elements that are in both sets `s1` and `s2`. @@ -144,11 +172,13 @@ This is the set intersection operator. ### Examples -``` +```quint assert(Set(1, 2, 3).intersect(Set(2, 3, 4)) == Set(2, 3)) ``` -## `pure def exclude: (Set[a], Set[a]) => Set[a]` +## exclude + +Signature: `pure def exclude: (Set[a], Set[a]) => Set[a]` `s1.exclude(s2)` is the set of elements in `s1` that are not in `s2`. @@ -156,32 +186,38 @@ This is the set difference operator. ### Examples -``` +```quint assert(Set(1, 2, 3).exclude(Set(2, 3, 4)) == Set(1)) ``` -## `pure def subseteq: (Set[a], Set[a]) => bool` +## subseteq + +Signature: `pure def subseteq: (Set[a], Set[a]) => bool` `s1.subseteq(s2)` is true when all elements in `s1` are also in `s2`. ### Examples -``` +```quint assert(Set(1, 2, 3).subseteq(Set(1, 2, 3, 4))) assert(not(Set(1, 2, 3).subseteq(Set(1, 2)))) ``` -## `pure def filter: (Set[a], (a) => bool) => Set[a]` +## filter + +Signature: `pure def filter: (Set[a], (a) => bool) => Set[a]` `s.filter(p)` is the set of elements in `s` that satisfy `p`. ### Examples -``` +```quint assert(Set(1, 2, 3).filter(n => n > 1) == Set(2, 3)) ``` -## `pure def map: (Set[a], (a) => b) => Set[b]` +## map + +Signature: `pure def map: (Set[a], (a) => b) => Set[b]` `s.map(f)` is the set of elements obtained by applying `f` to @@ -189,11 +225,13 @@ to each element in `s`. I.e., `{ f(x) : x \in s}`. ### Examples -``` +```quint assert(Set(1, 2, 3).map(n => n > 1) == Set(false, true, true)) ``` -## `pure def fold: (Set[a], b, (b, a) => b) => b` +## fold + +Signature: `pure def fold: (Set[a], b, (b, a) => b) => b` `l.fold(z, f)` reduces the elements in `s` using `f`, starting with `z`. @@ -204,21 +242,23 @@ the operator must be associative and commutative or else it has undefined behavi ### Examples -``` +```quint val sum = Set(1, 2, 3, 4).fold(0, (x, y) => x + y) assert(sum == 10) val mul = Set(1, 2, 3, 4).fold(1, (x, y) => x * y) assert(mul == 24) ``` -## `pure def powerset: (Set[a]) => Set[Set[a]]` +## powerset + +Signature: `pure def powerset: (Set[a]) => Set[Set[a]]` `s.powerset()` is the set of all subsets of `s`, including the empty set and the set itself. ### Examples -``` +```quint assert(Set(1, 2).powerset() == Set( Set(), Set(1), @@ -227,17 +267,21 @@ assert(Set(1, 2).powerset() == Set( )) ``` -## `pure def flatten: (Set[Set[a]]) => Set[a]` +## flatten + +Signature: `pure def flatten: (Set[Set[a]]) => Set[a]` `s.flatten()` is the set of all elements in the sets in `s`. ### Examples -``` +```quint assert(Set(Set(1, 2), Set(3, 4)).flatten() == Set(1, 2, 3, 4)) ``` -## `pure def allLists: (Set[a]) => Set[List[a]]` +## allLists + +Signature: `pure def allLists: (Set[a]) => Set[List[a]]` `s.allLists()` is the set of all lists containing elements in `s`. This is an infinite set unless `s` is the empty set. @@ -246,64 +290,76 @@ Like other inifite sets, this is not supported in any execution/verification for ### Examples -``` +```quint assert(Set(1, 2).allLists().contains([])) assert(Set(1, 2).allLists().contains([1, 1, 1, 1, 2, 1])) ``` -## `pure def allListsUpTo: (Set[a], int) => Set[List[a]]` +## allListsUpTo + +Signature: `pure def allListsUpTo: (Set[a], int) => Set[List[a]]` `s.allListsUpTo(l)` is the set of all lists of elements in `s` with length <= `l` -``` +```quint assert(Set(1, 2).allListsUpTo(1) == Set([], [1], [2])) assert(Set(1).allListsUpTo(2) == Set([], [1], [1, 1])) ``` -## `pure def chooseSome: (Set[a]) => a` +## chooseSome + +Signature: `pure def chooseSome: (Set[a]) => a` `s.chooseSome()` is, deterministically, one element of `s`. ### Examples -``` +```quint assert(Set(1, 2, 3).chooseSome() == 1) assert(Set(1, 2, 3).filter(x => x > 2).chooseSome() == 3) ``` -## `pure def oneOf: (Set[a]) => a` +## oneOf + +Signature: `pure def oneOf: (Set[a]) => a` `s.oneOf()` is, non-deterministically, one element of `s`. ### Examples -``` +```quint nondet x = oneOf(Set(1, 2, 3)) assert(x.in(Set(1, 2, 3))) ``` -## `pure def isFinite: (Set[a]) => bool` +## isFinite + +Signature: `pure def isFinite: (Set[a]) => bool` `s.isFinite()` is true when `s` is a finite set ### Examples -``` +```quint assert(Set(1, 2, 3).isFinite()) assert(!Nat.isFinite()) ``` -## `pure def size: (Set[a]) => int` +## size + +Signature: `pure def size: (Set[a]) => int` `s.size()` is the cardinality of `s`. ### Examples -``` +```quint assert(Set(1, 2, 3).size() == 3) ``` -## `pure def get: ((a -> b), a) => b` +## get + +Signature: `pure def get: ((a -> b), a) => b` `m.get(k)` is the value for `k` in `m`. @@ -311,52 +367,60 @@ If `k` is not in `m` then the behavior is undefined. ### Examples -``` +```quint pure val m = Map(1 -> true, 2 -> false) assert(m.get(1) == true) ``` -## `pure def keys: ((a -> b)) => Set[a]` +## keys + +Signature: `pure def keys: ((a -> b)) => Set[a]` `m.keys()` is the set of keys in the map `m`. ### Examples -``` +```quint pure val m = Map(1 -> true, 2 -> false) assert(m.keys() == Set(1, 2)) ``` -## `pure def mapBy: (Set[a], (a) => b) => (a -> b)` +## mapBy + +Signature: `pure def mapBy: (Set[a], (a) => b) => (a -> b)` `s.mapBy(f)` is the map from `x` to `f(x)` for each element `x` in `s`. ### Examples -``` +```quint pure val m = Set(1, 2, 3).mapBy(x => x * x) assert(m == Map(1 -> 1, 2 -> 4, 3 -> 9)) ``` -## `pure def setToMap: (Set[(a, b)]) => (a -> b)` +## setToMap + +Signature: `pure def setToMap: (Set[(a, b)]) => (a -> b)` `s.setToMap()` for a set of pairs `s` is the map from the first element of each pair to the second. ### Examples -``` +```quint pure val m = Set((1, true), (2, false)).setToMap() assert(m == Map(1 -> true, 2 -> false)) ``` -## `pure def setOfMaps: (Set[a], Set[b]) => Set[(a -> b)]` +## setOfMaps + +Signature: `pure def setOfMaps: (Set[a], Set[b]) => Set[(a -> b)]` `keys.setOfMaps(values)` is the set of all maps from `keys` to `values`. ### Examples -``` +```quint pure val s = Set(1, 2).setOfMaps(set(true, false)) assert(s == Set( Map(1 -> true, 2 -> true), @@ -366,7 +430,9 @@ assert(s == Set( )) ``` -## `pure def set: ((a -> b), a, b) => (a -> b)` +## set + +Signature: `pure def set: ((a -> b), a, b) => (a -> b)` `m.set(k, v)` is the map `m` but with the key `k` mapped to `v` if `k.in(keys(m))` @@ -374,14 +440,16 @@ If `k` is not a key in `m`, this operator has undefined behavior. ### Examples -``` +```quint pure val m = Map(1 -> true, 2 -> false) pure val m2 = m.set(2, true) assert(m == Map(1 -> true, 2 -> false)) assert(m2 == Map(1 -> true, 2 -> true)) ``` -## `pure def setBy: ((a -> b), a, (b) => b) => (a -> b)` +## setBy + +Signature: `pure def setBy: ((a -> b), a, (b) => b) => (a -> b)` `m.setBy(k, f)` is a map with the same keys as `m` but with `k` set to `f(m.get(k))`. @@ -389,20 +457,22 @@ If `k` is not present in `m`, this operator has undefined behavior. ### Examples -``` +```quint pure val m = Map(1 -> true, 2 -> false) pure val m2 = m.setBy(2, x => !x) assert(m == Map(1 -> true, 2 -> false)) assert(m2 == Map(1 -> true, 2 -> true)) ``` -## `pure def put: ((a -> b), a, b) => (a -> b)` +## put + +Signature: `pure def put: ((a -> b), a, b) => (a -> b)` `m.put(k, v)` is the map `m` but with the key `k` mapped to `v`. ### Examples -``` +```quint pure val m = Map(1 -> true, 2 -> false) pure val m2 = m.put(2, true) pure val m3 = m.put(3, true) @@ -411,27 +481,33 @@ assert(m2 == Map(1 -> true, 2 -> true)) assert(m3 == Map(1 -> true, 2 -> false, 3 -> true)) ``` -## `pure def append: (List[a], a) => List[a]` +## append + +Signature: `pure def append: (List[a], a) => List[a]` `l.append(e)` is the list `l` with the element `e` appended. ### Examples -``` +```quint assert(List(1, 2, 3).append(4) == List(1, 2, 3, 4)) ``` -## `pure def concat: (List[a], List[a]) => List[a]` +## concat + +Signature: `pure def concat: (List[a], List[a]) => List[a]` `l1.concat(l2)` is the list `l1` with `l2` concatenated at the end. ### Examples -``` +```quint assert(List(1, 2, 3).append(List(4, 5, 6)) == List(1, 2, 3, 4, 5, 6)) ``` -## `pure def head: (List[a]) => a` +## head + +Signature: `pure def head: (List[a]) => a` `l.head()` is the first element of `l`. @@ -439,11 +515,13 @@ If `l` is empty, the behavior is undefined. ### Examples -``` +```quint assert(List(1, 2, 3).head() == 1) ``` -## `pure def tail: (List[a]) => List[a]` +## tail + +Signature: `pure def tail: (List[a]) => List[a]` `l.tail()` is the list `l` without the first element. @@ -451,21 +529,25 @@ If `l` is empty, the behavior is undefined. ### Examples -``` +```quint assert(List(1, 2, 3).tail() == List(2, 3)) ``` -## `pure def length: (List[a]) => int` +## length + +Signature: `pure def length: (List[a]) => int` `l.length()` is the length of the list `l`. ### Examples -``` +```quint assert(List(1, 2, 3).length() == 3) ``` -## `pure def nth: (List[a], int) => a` +## nth + +Signature: `pure def nth: (List[a], int) => a` `l.nth(i)` is the `i+1`th element of the list `l`. @@ -473,21 +555,25 @@ If `i` is negative or greater than or equal to `l.length()`, the behavior is und ### Examples -``` +```quint assert(List(1, 2, 3).nth(1) == 2) ``` -## `pure def indices: (List[a]) => Set[int]` +## indices + +Signature: `pure def indices: (List[a]) => Set[int]` `l.indices()` is the set of indices of `l`. ### Examples -``` +```quint assert(List(1, 2, 3).indices() == Set(0, 1, 2)) ``` -## `pure def replaceAt: (List[a], int, a) => List[a]` +## replaceAt + +Signature: `pure def replaceAt: (List[a], int, a) => List[a]` `l.replaceAt(i, e)` is the list `l` with the `i+1`th element replaced by `e`. @@ -495,11 +581,13 @@ If `i` is negative or greater than or equal to `l.length()`, the behavior is und ### Examples -``` +```quint assert(List(1, 2, 3).replaceAt(1, 4) == List(1, 4, 3)) ``` -## `pure def slice: (List[a], int, int) => List[a]` +## slice + +Signature: `pure def slice: (List[a], int, int) => List[a]` `l.slice(i, j)` is the list of elements of `l` between indices `i` and `j`. @@ -513,11 +601,13 @@ The behavior is undefined when: ### Examples -``` +```quint assert(List(1, 2, 3, 4, 5).slice(1, 3) == List(2, 3)) ``` -## `pure def range: (int, int) => List[int]` +## range + +Signature: `pure def range: (int, int) => List[int]` `range(i, j)` is the list of integers between `i` and `j`. @@ -527,11 +617,13 @@ The behavior is undefined when `i` is greater than `j`. ### Examples -``` +```quint assert(range(1, 3) == List(1, 2)) ``` -## `pure def select: (List[a], (a) => bool) => List[a]` +## select + +Signature: `pure def select: (List[a], (a) => bool) => List[a]` `l.select(p)` is the list of elements of `l` that satisfy the predicate `p`. @@ -539,11 +631,13 @@ Preserves the order of the elements. ### Examples -``` +```quint assert(List(1, 2, 3).select(x -> x % 2 == 0) == List(2)) ``` -## `pure def foldl: (List[a], b, (b, a) => b) => b` +## foldl + +Signature: `pure def foldl: (List[a], b, (b, a) => b) => b` `l.foldl(z, f)` reduces the elements in `l` using `f`, starting with `z` from the left. @@ -552,80 +646,104 @@ I.e., `f(f(f(z, x0), x1)..., xn)`. ### Examples -``` +```quint pure val sum = List(1, 2, 3, 4).foldl(0, (x, y) => x + y) assert(sum == 10) pure val l = List(1, 2, 3, 4).foldl(List(), (l, e) => l.append(e)) assert(l == List(1, 2, 3, 4)) ``` -## `pure def iadd: (int, int) => int` +## iadd + +Signature: `pure def iadd: (int, int) => int` `a.iadd(b)` is the integer addition of `a` and `b`. It can be used in the infix form as `+` or as a named operator `iadd`. -## `pure def isub: (int, int) => int` +## isub + +Signature: `pure def isub: (int, int) => int` `a.isub(b)` is the integer subtraction of `b` from `a`. It can be used in the infix form as `-` or as a named operator `isub`. -## `pure def imul: (int, int) => int` +## imul + +Signature: `pure def imul: (int, int) => int` `a.imul(b)` is the integer multiplication of `a` and `b`. It can be used in the infix form as `*` or as a named operator `imul`. -## `pure def idiv: (int, int) => int` +## idiv + +Signature: `pure def idiv: (int, int) => int` `a.idiv(b)` is the integer division of `a` by `b`. It can be used in the infix form as `/` or as a named operator `idiv`. -## `pure def imod: (int, int) => int` +## imod + +Signature: `pure def imod: (int, int) => int` `a.imod(b)` is the integer modulus of `a` and `b`. It can be used in the infix form as `%` or as a named operator `imod`. -## `pure def ipow: (int, int) => int` +## ipow + +Signature: `pure def ipow: (int, int) => int` `a.ipow(b)` is the integer exponentiation of `a` by `b`. It can be used in the infix form as `^` or as a named operator `ipow`. -## `pure def ilt: (int, int) => bool` +## ilt + +Signature: `pure def ilt: (int, int) => bool` `a.ilt(b)` is the integer less than comparison of `a` and `b`. It can be used in the infix form as `<` or as a named operator `ilt`. -## `pure def igt: (int, int) => bool` +## igt + +Signature: `pure def igt: (int, int) => bool` `a.igt(b)` is the integer greater than comparison of `a` and `b`. It can be used in the infix form as `>` or as a named operator `igt`. -## `pure def ilte: (int, int) => bool` +## ilte + +Signature: `pure def ilte: (int, int) => bool` `a.ilte(b)` is the integer less than or equal to comparison of `a` and `b`. It can be used in the infix form as `<=` or as a named operator `ilte`. -## `pure def igte: (int, int) => bool` +## igte + +Signature: `pure def igte: (int, int) => bool` `a.igte(b)` is the integer greater than or equal to comparison of `a` and `b`. It can be used in the infix form as `>=` or as a named operator `igte`. -## `pure def iuminus: (int) => int` +## iuminus + +Signature: `pure def iuminus: (int) => int` `iuminus(a)` is `-1 * a`. This is the unary minus operator -## `pure def to: (int, int) => Set[int]` +## to + +Signature: `pure def to: (int, int) => Set[int]` `i.to(j)` is the set of integers between `i` and `j`. @@ -635,24 +753,28 @@ The behavior is undefined when `i` is greater than `j`. ### Examples -``` +```quint assert(1.to(3) == Set(1, 2, 3)) ``` -## `temporal always: (bool) => bool` +## always + +Signature: `temporal always: (bool) => bool` `always(p)` is true when `p` is true for every transition of the system. ### Examples -``` +```quint var x: int action Init = x' = 0 action Next = x' = x + 1 temporal Property = always(next(x) > x) ``` -## `temporal eventually: (bool) => bool` +## eventually + +Signature: `temporal eventually: (bool) => bool` `eventually(p)` is true when `p` is true for some transition of the system. @@ -660,27 +782,31 @@ temporal Property = always(next(x) > x) ### Examples -``` +```quint var x: int action Init = x' = 0 action Next = x' = x + 1 temporal Property = eventually(x == 10) ``` -## `temporal next: (a) => a` +## next + +Signature: `temporal next: (a) => a` `next(a)` is the value of `a` in the next state of a transition. ### Examples -``` +```quint var x: int action Init = x' = 0 action Next = x' = x + 1 temporal Property = next(x) == x + 1 ``` -## `temporal orKeep: (bool, a) => bool` +## orKeep + +Signature: `temporal orKeep: (bool, a) => bool` `orKeep(a, v)` is true when `a` is true or the values for the set of variables `v` are unchanged. @@ -691,14 +817,16 @@ This is the stuttering operator. ### Examples -``` +```quint var x: int action Init = x' = 0 action Next = x' = x + 1 temporal Spec = Init and always(Next.orKeep(Set(x))) ``` -## `temporal mustChange: (bool, a) => bool` +## mustChange + +Signature: `temporal mustChange: (bool, a) => bool` `mustChange(a, v)` is true when `a` is true and the values for the set of variables `v` are changed. @@ -709,7 +837,7 @@ This is the no-stuttering operator. ### Examples -``` +```quint var x: int action Init = x' = 0 action Next = any { @@ -720,7 +848,9 @@ temporal Spec = Init and always(Next.mustChange(Set(x))) temporal Property = Spec.implies(always(next(x) > x)) ``` -## `temporal enabled: (bool) => bool` +## enabled + +Signature: `temporal enabled: (bool) => bool` `enabled(a)` is true when the action `a` is enabled in the current state. @@ -729,7 +859,7 @@ and it's postcontitions are satisfiable. ### Examples -``` +```quint var x: int action Init = x' = 0 action Under10 = all { @@ -746,7 +876,7 @@ temporal Property = always(and { }) ``` -``` +```quint var x: int action Init = x' = 0 action impossible = { @@ -756,7 +886,9 @@ action impossible = { temporal Property = always(not(enabled(impossible))) ``` -## `temporal weakFair: (bool, a) => bool` +## weakFair + +Signature: `temporal weakFair: (bool, a) => bool` `weakFair(a, v)` is true when `eventually(always(a.mustChange(v).enabled())).implies(always(eventually(a.mustChange(v))))` @@ -772,7 +904,7 @@ The weak fairness condition can be expressed in English as (from Specifying Syst ### Examples -``` +```quint var x: int action Init = x' = 0 action Next = any { @@ -783,7 +915,9 @@ action Next = any { temporal Property = Next.weakFair(Set(x)).implies(eventually(x == 10)) ``` -## `temporal strongFair: (bool, a) => bool` +## strongFair + +Signature: `temporal strongFair: (bool, a) => bool` `strongFair(a, v)` is true when `always(eventually(a.mustChange(v).enabled())).implies(always(eventually(a.mustChange(v))))` @@ -798,7 +932,7 @@ The strong fairness condition can be expressed in English as (from Specifying Sy ### Examples -``` +```quint var x: int action Init = x' = 0 action cycleTo1 = all { x == 0, x' = 1 }, @@ -816,7 +950,9 @@ action Next = any { temporal Property = breakCycle.strongFair(Set(x)).implies(eventually(x == 2)) ``` -## `action assign: (a, a) => bool` +## assign + +Signature: `action assign: (a, a) => bool` `assign(n, v)` is true when the state variable named `n` has the value `v` in the next state of a transition. @@ -827,14 +963,16 @@ Can be written as `n' = v`. ### Examples -``` +```quint var x: int action Init = x' = 0 // Next defines all transitions from a number to its successor. action Next = x' = x + 1 ``` -## `action ite: (bool, a, a) => a` +## ite + +Signature: `action ite: (bool, a, a) => a` `ite(c, t, e)` is `t` when `c` is true and `e` when `c` is false. @@ -844,18 +982,20 @@ Can be used with actions. ### Examples -``` +```quint pure val m = if (1 == 2) 3 else 4 assert(m == 4) ``` -``` +```quint var x: int action a = if (x == 0) x' = 3 else x' = 4 run test = (x' = 0).then(a).then(assert(x == 3)) ``` -## `action then: (bool, bool) => bool` +## then + +Signature: `action then: (bool, bool) => bool` `a.then(b)` is true for a step from `s1` to `s2` if there is a state `t` such that @@ -867,12 +1007,14 @@ This is the action composition operator. If `a` evaluates to `false`, then ### Examples -``` +```quint var x: int run test = (x' = 1).then(x' = 2).then(x' = 3).then(assert(x == 3)) ``` -## `action expect: (bool, bool) => bool` +## expect + +Signature: `action expect: (bool, bool) => bool` `a.expect(b)` is true for a step from `s1` to `s2` if @@ -885,14 +1027,16 @@ evaluation of `a.expect(b)` fails with an error message. ### Examples -``` +```quint var n: int run expectConditionOkTest = (n' = 0).then(n' = 3).expect(n == 3) run expectConditionFailsTest = (n' = 0).then(n' = 3).expect(n == 4) run expectRunFailsTest = (n' = 0).then(all { n == 2, n' = 3 }).expect(n == 4) ``` -## `action reps: (int, (int) => bool) => bool` +## reps + +Signature: `action reps: (int, (int) => bool) => bool` `n.reps(i => A(i))` or `n.reps(A)` the action `A`, `n` times. The iteration number, starting with 0, is passed as an argument of `A`. @@ -908,18 +1052,22 @@ The semantics of this operator is as follows: ### Examples -``` +```quint var x: int run test = (x' = 0).then(3.reps(i => x' = x + 1)).then(assert(x == 3)) ``` -## `action fail: (bool) => bool` +## fail + +Signature: `action fail: (bool) => bool` `a.fail()` evaluates to `true` if and only if action `a` evaluates to `false`. This operator is good for writing tests that expect an action to fail. -## `action assert: (bool) => bool` +## assert + +Signature: `action assert: (bool) => bool` `assert(p)` is an action that is true when `p` is true. @@ -927,12 +1075,12 @@ It does not change the state. ### Examples -``` +```quint var x: int run test = (x' = 0).then(3.reps(x' = x + 1)).then(assert(x == 3)) -``` +```quint -``` +```quint var x: int action Init = x' = 0 action Next = x' = x + 1 @@ -940,7 +1088,9 @@ action Next = x' = x + 1 run test = Init.then(all { Next, assert(x > 0) }) ``` -## `pure def q::debug: (str, a) => a` +## q::debug + +Signature: `pure def q::debug: (str, a) => a` `q::debug(msg, value)` prints the given message and value to the console, separated by a space. @@ -950,7 +1100,7 @@ so that it can be used directly within expressions. ### Examples -``` +```quint var x: int >>> (x' = 0).then(3.reps(i => x' = q::debug("new x:", x + 1))) > new x: 1 diff --git a/doc/design-principles.md b/docs/pages/docs/design-principles.md similarity index 100% rename from doc/design-principles.md rename to docs/pages/docs/design-principles.md diff --git a/docs/pages/docs/faq.mdx b/docs/pages/docs/faq.mdx new file mode 100644 index 000000000..1499a8e08 --- /dev/null +++ b/docs/pages/docs/faq.mdx @@ -0,0 +1,158 @@ +# Frequently Asked Questions +Here you can find answers to the questions that are often asked by people who +would like to start with Quint. + +
+export function FAQBox({ title, children }) { + return ( +
+ + {title} + +
{children}
+
+ ) +} + + +Main similarities: +- It is possible to get fuzz tests out of Quint +- Both require some sort of property to check + +Main differences: +- Writing a Quint model is very different from setting up a fuzzer +- Fuzzers can fail to find rare bugs, while `quint verify` will always find issues if there are any. + + + + +Adding a new language to an ecosystem often finds resistance, and you might be +tempted to use Python or Typescript or whatever you already know. It is possible +to write a reference implementation in some language and then implement some +simulation for it, looking for bugs. Here are some reasons to use Quint instead of that: +- Quint is a specification language. It is more restrictive so you are forced to + write things in a way that's possible to *formally verify* your model. It is + very hard to formally verifying arbitrary code in programming languages. +- Quint already has a simulator! We are on the path to making it the best + simulator ever. While it is probably not there yet, it's likely better than + something you'll hack in a couple of weeks. If your project's focus is not + writing a simulator, you might want to use ours. + + + + +Yes, and you probably want to use [Literate Specifications](./literate.md) to interleave Quint blocks with natural language descriptions. + +While Markdown accepts all sorts of errors, Quint specs are executable. You get +name resolution and type checking for your spec, even if you don't want to run +it. This means you'll get rid of ambiguity, references to names that are never +defined and incompatible interfaces - which are the biggest problems in common +specifications. + + + + Spells are simply Quint modules that contain often-used definitions. There is + nothing special about these definitions. They are probably a bit more + thought-out than a definition someone would write on the spot. Check the page + on [Spells][]. + + + + +Definitions that are tagged as `pure def` are not allowed to read state +variables (that is, the names declared with `var`). Definitions that are tagged +with `def` are allowed to read state variables, though they do not have to. + +Pure definitions have the following important property: A **pure definition +always returns the same result for the same parameter values**. + +Consider the following operator definition: + +```quint +pure def min(x, y) = if (x < y) x else y +``` + +Whenever we call `min(2, 3)`, we get `2` as a result, no matter what the values of state variables are. + +Impure definitions have the following important property: +**An impure definition may produce different results for the same parameter +values**. An impure definition **may read** the values of state variables, which +may effect in different results. + +Consider the following definitions: + +```quint +var limit: int + +def minWithLimit(x, y) = min(min(x, y), limit) +``` + +In the above example, `minWithLimit(10, 20)` produces the value `10`, when +`limit == 100`, whereas `minWithLimit(10, 20)` produces the value `5`, when +`limit == 5`. + + + + +The definitions that have at least one parameter should be tagged with `def`, +whereas the definitions that have no parameters should be tagged with `val`. We +could say that `val`'s are nullary `def`'s. + +As a result, a `pure val` is never changing its value in an execution. For example: + +```quint +pure val avogadro = 602214076^(23 - 8) +``` + +Note that an impure `val` may still depend on the value of a state variable. +For example: + +```quint +var time: int +var velocity: int + +val distance = velocity * time +``` + +In the above example, `distance` does not need any parameters, as it only +depends on the state variables. However, the value of `distance` is still +changing with the values of `time` and `velocity`. + + + + +A value that is defined via `pure val` is constant in the sense that it never +changes in an execution. A `const` definition also declares a constant value. +However, the value of `const` is not fixed at the time of specification writing, +but it has to be fixed by instantiating the module. + +Here is an example that illustrates the difference between `pure val` and `const`: + +```quint +module fixed { + // this module is written for N=4, e.g., for N processes + pure val N = 4 + + pure val procs = 1.to(N) + // etc. +} + +module parameterized { + // this module is written for a parameter N + const N: int + + pure val procs = 1.to(N) + // etc. +} + +module instance4 { + import parameterized(N = 4) as I + + pure val procs = 1.to(I::N) +} +``` + + +[Spells]: https://github.com/informalsystems/quint/tree/main/examples/spells diff --git a/docs/pages/docs/getting-started.mdx b/docs/pages/docs/getting-started.mdx new file mode 100644 index 000000000..712da90c9 --- /dev/null +++ b/docs/pages/docs/getting-started.mdx @@ -0,0 +1,156 @@ +import { Steps, Tabs, Callout } from 'nextra/components' +import Image from 'next/image' + +# Getting Started + +Welcome to Quint! In this short guide, we'll cover everything from installation to your very first verification. + + +### Install Quint with npm + +Install [node/npm](https://nodejs.org/en/download/package-manager) if you don't have it already. Then, open your terminal and run: + +```sh +npm i @informalsystems/quint -g +``` + +### Setup your code editor + +For the best experience writting Quint, you should set up your code editor with the Quint tools. The VSCode setup is, by far, the easiest, so you might want to use it if you want a quick start. + + + +Quint extension on VSCode + Install the [Quint extension](https://marketplace.visualstudio.com/items?itemName=informal.quint-vscode): + 1. Open the extensions panel by pressing `Ctrl+Shift+X` or clicking the extensions icon. + 2. Search for Quint and click Install. + + + We have 2 packages for enabling support for Quint in Emacs. + + 1. `quint-mode` is a major mode that enables simple syntax highlighting + 2. `lsp-quint` is a client for `lsp-mode`, enabling IDE features provided by `quint-language-server` + + These packages are not published anywhere for the moment. Get the files from [Quint's git repository](https://github.com/informalsystems/quint/tree/main/editor-plugins/emacs) (i.e. by cloning it) and then add a configuration like the following (which uses `use-package`): + + ```elisp + (load-file "/editor-plugins/emacs/quint-mode.el") + (load-file "/editor-plugins/emacs/lsp-quint.el") + (require 'quint-mode) + (add-to-list 'auto-mode-alist '("\\.qnt" . quint-mode)) + (use-package lsp-quint + :ensure t + :hook (quint-mode . lsp)) + ``` + + + + First, you'll need to install the language server via npm: + ```sh + npm i @informalsystems/quint-language-server -g + ``` + + For Neovim: + 1. Download the [quint.vim](https://github.com/informalsystems/quint/blob/main/editor-plugins/vim/quint.vim) file and move it to `~/.config/nvim/syntax` + 2. Enable automatic syntax highlighting and language server integration by adding the following lines to your `~/.config/nvim/init.vim`: +```vim-script +autocmd FileType quint lua vim.lsp.start({name = 'quint', cmd = {'quint-language-server', '--stdio'}, root_dir = vim.fs.dirname()}) +au BufRead,BufNewFile *.qnt setfiletype quint +au BufNewFile,BufReadPost *.qnt runtime syntax/quint.vim +``` + + For Vim, follow the extended instructions in [here](https://github.com/informalsystems/quint/tree/main/editor-plugins/vim). + + + +### Write a specification + +In order to run Quint, we first need a specification. Let's use this simple bank specification, which has a bug. + +```quint +module bank { + /// A state variable to store the balance of each account + var balances: str -> int + + pure val ADDRESSES = Set("alice", "bob", "charlie") + + action deposit(account, amount) = { + // Increment balance of account by amount + balances' = balances.setBy(account, curr => curr + amount) + } + + action withdraw(account, amount) = { + // Decrement balance of account by amount + balances' = balances.setBy(account, curr => curr - amount) + } + + action init = { + // At the initial state, all balances are zero + balances' = ADDRESSES.mapBy(_ => 0) + } + + action step = { + // Non-deterministically pick an address and an amount + nondet account = ADDRESSES.oneOf() + nondet amount = 1.to(100).oneOf() + // Non-deterministically choose to either deposit or withdraw + any { + deposit(account, amount), + withdraw(account, amount), + } + } + + /// An invariant stating that all accounts should have a non-negative balance + val no_negatives = ADDRESSES.forall(addr => balances.get(addr) >= 0) +} +``` + +Create a `bank.qnt` file and add the contents above to it. Then, let's try to check the invariant `no_negatives`. This invariant states that none of the balances should be less then zero, ever. An invariant is something that needs to be true for all reachable states. + +### Find a violation + +To look for violations of this invariant, we can use the `run` subcommand, which will simulate a bunch of executions while checking the invariant. In a terminal, whilw on the directory of this file, execute: +```sh +quint run bank.qnt --invariant=no_negatives +``` + +This should result in a violation. You can inspect the balances and find a negative value. If you want to understand better how we got to such state, try using the `--mbt` flag, which includes additional metadata on the violation trace (usually used for testing purposes. MBT stands for Model Based Testing): +```sh +quint run bank.qnt --invariant=no_negatives --mbt +``` + +Now, you should see that a withdraw action was taken right before the balances went negative. + +### Fix the issue + +Let's update the `withdraw` definition to prevent this scenario - users should not be able to withdraw more than they currently have. + +```quint +action withdraw(account, amount) = all { // <- all of the following need to hold + // A precondition, there should be enough to withdraw + balances.get(account) >= amount, + // Decrement balance of account by amount + balances' = balances.setBy(account, curr => curr - amount), +} +``` + +We can run the simulator again. This time, it should find no violation and return an `[ok]` result: +```sh +quint run bank.qnt --invariant=no_negatives +``` + +### Verify the result + +However, the simulator might miss several executions. To make sure we fixed the problem, we should run the model checker, by using the `verify` subcommand. + + +The model checker requires Java Development Kit >= 17. We recommend version 17 of the [Eclipse Temurin](https://adoptium.net/) or [Zulu](https://www.azul.com/downloads/?version=java-17-lts&package=jdk#download-openjdk) builds of OpenJDK. + +```sh +quint verify bank.qnt --invariant=no_negatives +``` + +This will verify all possible executions of up to 10 steps. We can be confident that we fixed the issue after seeing the `[ok]` result from the `verify` command. + + +That's it! Now that you have the tools and know the workflow, you might want to learn how to write your own specs! Check out the Quint [Lessons](/docs/lessons) and [Examples ↗](https://github.com/informalsystems/quint/tree/main/examples). diff --git a/docs/pages/docs/index.mdx b/docs/pages/docs/index.mdx new file mode 100644 index 000000000..d8d0a910d --- /dev/null +++ b/docs/pages/docs/index.mdx @@ -0,0 +1,19 @@ +# Documentation + +Welcome to the Quint documentation! Here are some main pointers. + +## Language + +- [Language lessons](docs/lessons) +- [Syntax specification](docs/lang) +- [Cheatsheet ↗](/quint-cheatsheet.pdf) +- [API documentation for built-in operators](docs/builtin) +- [Examples ↗](https://github.com/informalsystems/quint/tree/main/examples) +- [Frequently asked questions](docs/faq) + +## Tooling + +- [Tool video previews](docs/previews) +- [REPL tutorial](docs/repl) +- [CLI manual](docs/quint) +- [Literate executable specifications](docs/literate) diff --git a/doc/lang.md b/docs/pages/docs/lang.md similarity index 93% rename from doc/lang.md rename to docs/pages/docs/lang.md index 43abf07cd..b2dede764 100644 --- a/doc/lang.md +++ b/docs/pages/docs/lang.md @@ -2,90 +2,13 @@ | Revision | Date | Author | |:---------|:-----------|:--------------------------------------------------------| -| 35 | 12.12.2023 | Igor Konnov, Shon Feder, Jure Kukovec, Gabriela Moreira, Thomas Pani | +| 36 | 26.06.2024 | Igor Konnov, Shon Feder, Jure Kukovec, Gabriela Moreira, Thomas Pani | This document presents language constructs in the same order as the [summary of -TLA+](https://lamport.azurewebsites.net/tla/summary.pdf). - - - -Table of Contents -================= - -* [Summary of Quint](#summary-of-quint) - * [Identifiers and strings](#identifiers-and-strings) - * [Comments](#comments) - * [Types](#types) - * [Type System 1\.2](#type-system-12) - * [Modes](#modes) - * [Module\-level constructs](#module-level-constructs) - * [Module definition](#module-definition) - * [Constant declarations](#constant-declarations) - * [Assumptions](#assumptions) - * [Variable definitions](#variable-definitions) - * [Operator definitions](#operator-definitions) - * [No recursive functions and operators](#no-recursive-functions-and-operators) - * [Module instances](#module-instances) - * [Type aliases](#type-aliases) - * [Theorems](#theorems) - * [Imports](#imports) - * [Namespaces and imports](#namespaces-and-imports) - * [Stateless and stateful modules](#stateless-and-stateful-modules) - * [Namespaces](#namespaces) - * [Imports](#imports-1) - * [Quint expression syntax](#quint-expression-syntax) - * [Literals](#literals) - * [Names](#names) - * [Braces and parentheses](#braces-and-parentheses) - * [Lambdas (aka Anonymous Operators)](#lambdas-aka-anonymous-operators) - * [Two forms of operator application](#two-forms-of-operator-application) - * [Boolean operators and equality](#boolean-operators-and-equality) - * [Block disjunctions](#block-disjunctions) - * [Block conjunctions](#block-conjunctions) - * [Flow operators](#flow-operators) - * [Condition](#condition) - * [Cases (removed)](#cases-removed) - * [Sets](#sets) - * [Set constructor](#set-constructor) - * [Non\-deterministic choice](#non-deterministic-choice) - * [Other set operators](#other-set-operators) - * [Maps (aka Functions)](#maps-aka-functions) - * [Records](#records) - * [Tuples](#tuples) - * [Sum Types](#sum-types) - * [Lists (aka Sequences)](#lists-aka-sequences) - * [Integers](#integers) - * [Nested operator definitions](#nested-operator-definitions) - * [Operators on actions](#operators-on-actions) - * [Delayed assignment](#delayed-assignment) - * [Non\-deterministic choice](#non-deterministic-choice-1) - * [Assert](#assert) - * [Runs](#runs) - * [Then](#then) - * [Reps](#reps) - * [Example](#example) - * [Expect](#expect) - * [Fail](#fail) - * [Temporal operators](#temporal-operators) - * [Always](#always) - * [Eventually](#eventually) - * [Next](#next) - * [Unchanged (removed)](#unchanged-removed) - * [OrKeep](#orkeep) - * [MustChange](#mustchange) - * [Enabled](#enabled) - * [Fairness](#fairness) - * [Other temporal operators](#other-temporal-operators) - * [Unbounded quantifiers](#unbounded-quantifiers) - * [Instances](#instances) - * [Common case 1](#common-case-1) - * [Common case 2](#common-case-2) - * [The general case](#the-general-case) - * [No anonymous instances](#no-anonymous-instances) - * [Discussion](#discussion) - - - +TLA+](https://lamport.azurewebsites.net/tla/summary.pdf). Although we keep it +up-to-date, this document originated Quint and precedes it's implementation. It +includes a lot of references to TLA+, the language which served as the basis of +quint, semantically. ## Identifiers and strings @@ -179,14 +102,14 @@ A type is one of the following: It is often convenient to declare a type alias. You can use `type` to define an alias inside a module definition. For instance: -```bluespec +```quint type Temperature = int ``` A type alias specified with type parameters defines a polymorphic type constructor for instances of the defined type. For instance, given -```bluespec +```quint type Option[a] = | Some(a) | None @@ -197,7 +120,7 @@ You can then construct concrete types such as `Option[int]` or A type identifier can also introduce an uninterpreted type by defining a type without any constructors for values of that type: -```bluespec +```quint type MY_TYPE ``` @@ -246,7 +169,7 @@ This is intentional: We do not want to mix actions with temporal formulas. A module definition is introduced like follows: -```bluespec +```quint // module definition module Foo { // declarations @@ -345,7 +268,7 @@ The operator names should not clash with the names of other definitions in the module scope. Importantly, there are **no recursive operators** (though you can fold over a list or a set, see below). -```bluespec +```quint // a static constant value, which is not changing over time pure val Nodes: Set[int] = 1 to 10 @@ -500,7 +423,7 @@ In our example, module `Foo` has four names in its namespace: `N`, `x`, Quint does not allow for nested modules. However, you can mimick nested namespaces by using `::`. For example: -```bluespec +```quint module Top { var x: int @@ -516,7 +439,7 @@ Note that in the above example, `Inner::x2` is treated as a qualified identifier Shadowing names is allowed. For example: -```bluespec +```quint module OS { var clock: int @@ -529,7 +452,7 @@ PS: Let us know if you are bothered by this. We are considering making it a togg *No order.* In contrast to TLA+, namespaces are not ordered. It is perfectly fine to write out-of-order definitions, like in many programming languages: -```bluespec +```quint module outOfOrder { def positive(x) = max(x, 0) @@ -545,7 +468,7 @@ Quint is not Fashion Police. Similar to mainstream programming languages, Quint supports name imports: -```bluespec +```quint module Math { def sqr(x) = x * x def pow(x, y) = x^y @@ -565,7 +488,7 @@ would expect, imports are not allowed to introduce name collisions. To avoid writing too many import statements, you can use `*`. For example: -```bluespec +```quint module Math { def sqr(x) = x * x def pow(x, y) = x^y @@ -582,7 +505,7 @@ module ImportAll { *No re-export by default.* It is important to know that `import` does not automatically introduce any definitions in the module that uses `import`. Hence, the following example produces a lookup error: -```bluespec +```quint module A { pure val greeting = "hello" } @@ -611,7 +534,7 @@ English` of TLA+. *Re-export.* Similar to TypeScript, we can re-export imported definitions via `export`. Here is the way to fix the above example: -```bluespec +```quint module A { pure val greeting = "hello" } @@ -645,7 +568,7 @@ In the above example, the definition `F` is auxiliary to `G`. In Quint, we do not hide definitions. However, if you really want to do that, `import ` allows you to model this behavior via a proxy module: -```bluespec +```quint module A::Impl { pure val secretNum = 123 } @@ -800,7 +723,7 @@ The former is a two-argument lambda operator, whereas the latter is a single-argument lambda operator that accepts a pair as its first argument. The latter form `((x, y)) => e2` is equivalent to: -```bluespec +```quint (t => val x = t._1 val y = t._2 @@ -1751,7 +1674,7 @@ The semantics of this operator is as follows: ##### Example -```bluespec +```quint var x: int run test = (x' = 0).then(3.reps(_ => x' = x + 1)).then(assert(x == 3)) ``` @@ -1798,7 +1721,7 @@ The semantics of this operator is as follows: ##### Example -```bluespec +```quint var n: int run expectConditionOkTest = (n' = 0).then(n' = 3).expect(n == 3) run expectConditionFailsTest = (n' = 0).then(n' = 3).expect(n == 4) @@ -2000,7 +1923,7 @@ module `I` is called an instance of `M`. The most common example is shown below: -```bluespec +```quint module Voting { const Value: Set[int] const Acceptor: Set[str] @@ -2044,7 +1967,7 @@ INSTANCE Bar WITH x = z Quint has similar syntax sugar for doing that: -```bluespec +```quint module Bar { const c: int def A(y) = c + y diff --git a/tutorials/README.md b/docs/pages/docs/lessons.md similarity index 61% rename from tutorials/README.md rename to docs/pages/docs/lessons.md index 1ff7d0adf..b296ba757 100644 --- a/tutorials/README.md +++ b/docs/pages/docs/lessons.md @@ -2,7 +2,7 @@ We are introducing language tutorials in two formats. -## 1. Classic markdown +## 1. Classic tutorials These are simply tutorials written in Markdown. The only thing needed is your browser. If you want to run the examples in REPL, check how to install @@ -10,12 +10,11 @@ browser. If you want to run the examples in REPL, check how to install These tutorials are currently available in the classic format: - - [Hello, world!](./lesson0-helloworld/hello.md) - - [REPL](./repl/repl.md) - - [Tutorial on Booleans](./lesson1-booleans/booleans.md) - - [Tutorial on integers](./lesson2-integers/integers.md) - - [Tutorial on basic protocol anatomy and tests](./lesson3-anatomy/coin.md) - - [Tutorial on sets](./lesson4-sets/sets.md) + - [Hello, world!](./lessons/hello.md) + - [Tutorial on Booleans](./lessons/booleans.md) + - [Tutorial on integers](./lessons/integers.md) + - [Tutorial on basic protocol anatomy and tests](./lessons/coin.md) + - [Tutorial on sets](./lessons/sets.md) ## 2. CodeTour tutorials @@ -25,10 +24,10 @@ tutorials using [CodeTour][]. You have to install two extensions: - [Quint VSCode][] is our language extension, - [CodeTour][] is the extension of replaying code tutorials. -To start a tour, open the [tutorials](./) folder in VSCode and start the tour: +To start a tour, open the [codetour](https://github.com/informalsystems/quint/tree/main/docs/codetour) folder in VSCode and start the tour: -![CodeTour Animation](./img/tutorials-1671180664875.gif) +![CodeTour Animation](../../public/codetour.gif) -[Quint]: ../quint/README.md +[Quint]: ./getting-started [Quint VSCode]: https://marketplace.visualstudio.com/items?itemName=informal.quint-vscode [CodeTour]: https://marketplace.visualstudio.com/items?itemName=vsls-contrib.codetour diff --git a/docs/pages/docs/lessons/_meta.json b/docs/pages/docs/lessons/_meta.json new file mode 100644 index 000000000..b4ffc695a --- /dev/null +++ b/docs/pages/docs/lessons/_meta.json @@ -0,0 +1,7 @@ +{ + "hello": "Hello, World!", + "booleans": "Booleans", + "integers": "Integers", + "coin": "Anatomy", + "sets": "Sets" +} diff --git a/tutorials/lesson1-booleans/booleans.md b/docs/pages/docs/lessons/booleans.md similarity index 97% rename from tutorials/lesson1-booleans/booleans.md rename to docs/pages/docs/lessons/booleans.md index 202a88c0f..e41638192 100644 --- a/tutorials/lesson1-booleans/booleans.md +++ b/docs/pages/docs/lessons/booleans.md @@ -17,7 +17,7 @@ the details, check [booleans.qnt](./booleans.qnt). **Code snippet:** -```bluespec +```quint // false is a built-in constant pure val myFalse = false @@ -58,7 +58,7 @@ echo "true" | quint **Code snippet:** -```bluespec +```quint // Boolean negation, which is written as `!x` in some languages pure def myNot(x) = not(x) @@ -86,7 +86,7 @@ echo "not(true)" | quint **Code snippet:** -```bluespec +```quint // you can compare Booleans for equality pure def myEq(x, y) = x == y @@ -140,7 +140,7 @@ echo "true == 1" | quint **Code snippet:** -```bluespec +```quint // you can compare Booleans for inequality pure def myNeq(x, y) = x != y @@ -178,7 +178,7 @@ echo "true != true" | quint **Code snippet:** -```bluespec +```quint // you can also write negation like that pure def myNot2(x) = x.not() @@ -210,7 +210,7 @@ echo "false.not" | quint **Code snippet:** -```bluespec +```quint // Boolean "and", which is written as `x && y` in some languages pure def myAnd(x, y) = x and y @@ -248,7 +248,7 @@ echo "true and true" | quint **Code snippet:** -```bluespec +```quint // You can also write Boolean "and" like that in the OOP form pure def myAnd2(x, y) = x.and(y) @@ -281,7 +281,7 @@ This form may be useful with nested formulas like: **Code snippet:** -```bluespec +```quint // We can apply "and" to more than two arguments pure def myAnd3(x, y, z) = and(x, y, z) @@ -312,7 +312,7 @@ echo "and(true, false, true, false, true)" | quint **Code snippet:** -```bluespec +```quint /// When your expressions get bigger, you can stack them in `and { ... }` pure def myAnd4(x, y, z) = and { @@ -342,7 +342,7 @@ echo "and { false == false, true == true }" | quint **Code snippet:** -```bluespec +```quint // Boolean "or", which is written as `x || y` in some languages. pure def myOr(x, y) = x or y @@ -379,7 +379,7 @@ echo "true or true" | quint **Code snippet:** -```bluespec +```quint /// You can also write Boolean "or" like that in the OOP form pure def myOr2(x, y) = x.or(y) @@ -405,7 +405,7 @@ and the `or { ... }`. **Code snippet:** -```bluespec +```quint /// Boolean implication. /// This operator is equivalent to `not(x).or(y)` as well as to `if (x) y else true`. @@ -449,7 +449,7 @@ echo "true implies true" | quint **Code snippet:** -```bluespec +```quint /// Boolean equivalence. /// It is equivalent to x == y, but this operator requires the arguments @@ -496,7 +496,7 @@ echo "true iff true" | quint We have covered all the operators over Booleans. If you want to see all operators in one place, -check [booleans.qnt](./booleans.qnt). +check [booleans.qnt](https://github.com/informalsystems/quint/tree/main/examples/tutorials/booleans.qnt). We are experimenting with different kinds of tutorials. It would be great to learn, whether you liked this tutorial format, or not. diff --git a/tutorials/lesson3-anatomy/coin.md b/docs/pages/docs/lessons/coin.md similarity index 99% rename from tutorials/lesson3-anatomy/coin.md rename to docs/pages/docs/lessons/coin.md index fcf883471..63334168b 100644 --- a/tutorials/lesson3-anatomy/coin.md +++ b/docs/pages/docs/lessons/coin.md @@ -48,7 +48,7 @@ In this tutorial, you will see how to: If you would like to see the complete code before diving into -the details, check [coin.qnt](./coin.qnt). +the details, check [coin.qnt](https://github.com/informalsystems/quint/tree/main/examples/tutorials/coin.qnt). ## 2. Declare a single module @@ -56,7 +56,7 @@ the details, check [coin.qnt](./coin.qnt). **Code snippet:** -```bluespec +```quint module coin { ``` @@ -75,7 +75,7 @@ reuse various parts of your protocol. **Code snippet:** -```bluespec +```quint // TYPE DEFINITIONS @@ -116,7 +116,7 @@ different kinds of "integers", when they are referred to via different type alia **Code snippet:** -```bluespec +```quint // FUNCTIONAL LAYER: // Values and functions that are state-independent @@ -184,7 +184,7 @@ annotations will help you in figuring out the issue. **Code snippet:** -```bluespec +```quint // STATE MACHINE: // State-dependent definitions and actions @@ -238,7 +238,7 @@ in order to be able to iterate on the protocol specification quickly. **Code snippet:** -```bluespec +```quint // the minter's address var minter: Addr @@ -279,7 +279,7 @@ of Solidity: **Code snippet:** -```bluespec +```quint // a handy definition to query the whole state in REPL at once val state = { minter: minter, balances: balances } ``` @@ -313,7 +313,7 @@ do that a few steps later. **Code snippet:** -```bluespec +```quint // a helper function to make preconditions similar to Solidity def require(cond: bool): bool = cond @@ -362,7 +362,7 @@ pieces: **Code snippet:** -```bluespec +```quint // state initialization action init: bool = { @@ -421,7 +421,7 @@ in the reference manual. **Code snippet:** -```bluespec +```quint all { minter' = sender, balances' = ADDR.mapBy(a => 0) @@ -464,7 +464,7 @@ Hint: use `if (cond) value1 else value2`. **Code snippet:** -```bluespec +```quint // Sends an amount of newly created coins to an address. // Can only be called by the minter, that is, the contract creator. @@ -529,7 +529,7 @@ Do you understand what happened in this case? **Code snippet:** -```bluespec +```quint // Sends an amount of existing coins from any caller (sender) // to a receiver's address. @@ -577,7 +577,7 @@ echo 'init\n mint(minter, "bob", 2023)\n send("bob", "eve", 1024)\n state' | qui **Code snippet:** -```bluespec +```quint // All possible behaviors of the protocol in one action. action step: bool = { @@ -620,7 +620,7 @@ not to stop here! In the next steps, we show you the real magic of Quint. **Code snippet:** -```bluespec +```quint // INVARIANTS AND TEMPORAL PROPERTIES ``` @@ -638,7 +638,7 @@ protocol invariants and temporal properties. **Code snippet:** -```bluespec +```quint // One of the simplest properties is that all balances are within the uint range. // While it is automatically guaranteed by Solidity, our specification is using @@ -678,7 +678,7 @@ whereas the latter are actually called *invariants*. **Code snippet:** -```bluespec +```quint // It is desirable that the total supply of tokens fits into UInt. // Otherwise, a blockchain implementation or the user interface @@ -704,7 +704,7 @@ initialize it with 0 and increase it in `mint` with the `amount` passed to `mint **Code snippet:** -```bluespec +```quint // The temporal property that says the following: // Assume that we want to check the temporal property `NoSupplyOverflow` @@ -731,7 +731,7 @@ with the qualifier `temporal`. **Code snippet:** -```bluespec +```quint // TESTS @@ -778,7 +778,7 @@ prior minting. What is this value? **Code snippet:** -```bluespec +```quint // `mint`, then `send` run mintSendTest = { @@ -820,7 +820,7 @@ see the next step. **Code snippet:** -```bluespec +```quint // Mint some coins for Eve and Bob. // Test that Eve can always send coins to Bob, @@ -903,7 +903,7 @@ better with Quint. **Code snippet:** -```bluespec +```quint // to run the random simulator for 10000 executions, each up to 10 actions, // execute the following in the command line: diff --git a/tutorials/lesson0-helloworld/hello.md b/docs/pages/docs/lessons/hello.md similarity index 97% rename from tutorials/lesson0-helloworld/hello.md rename to docs/pages/docs/lessons/hello.md index 912a3b0a8..b1b6ec190 100644 --- a/tutorials/lesson0-helloworld/hello.md +++ b/docs/pages/docs/lessons/hello.md @@ -22,7 +22,7 @@ that we have to describe two importants aspects of the protocol: If you would like to see the complete code before diving into -the details, check [hello.qnt](./hello.qnt). +the details, check [hello.qnt](https://github.com/informalsystems/quint/tree/main/examples/tutorials/hello.qnt). ## 2. Declare a module @@ -30,7 +30,7 @@ the details, check [hello.qnt](./hello.qnt). **Code snippet:** -```bluespec +```quint module hello { ``` @@ -43,7 +43,7 @@ As a first necessary step, we declare a top-level module for our protocol. **Code snippet:** -```bluespec +```quint // the state variable to keep the output by the computer var consoleOutput: str @@ -66,7 +66,7 @@ All state variables require a type. We use `str`, that is, the string type. **Code snippet:** -```bluespec +```quint // the state variable to keep the out read by the user var readByUser: str @@ -83,7 +83,7 @@ by the user. This state variable has the string type too. **Code snippet:** -```bluespec +```quint // initialize the state machine that captures the protocol action init = all { @@ -132,7 +132,7 @@ read the next step. **Code snippet:** -```bluespec +```quint // write "Hello, world!" in consoleOutput of the state machine, // if the console output is clean @@ -190,7 +190,7 @@ it has to explicitely assign values to all of the state variables. **Code snippet:** -```bluespec +```quint // read the message from `consoleOutput` into `readByUser`, // if the console output is not clean @@ -214,7 +214,7 @@ how we did it for `write`. **Code snippet:** -```bluespec +```quint // execute a single step of the state machine: // it may be `read` or `write`, whatever is available @@ -252,7 +252,7 @@ that `any { ... }` chooses one of the actions *non-deterministically*. **Code snippet:** -```bluespec +```quint // a simple test that demonstrates an interaction between // the computer and the user @@ -277,7 +277,7 @@ To test our protocol, we fix one particular execution sequence in `writeReadTest We could draw this execution sequence with UML sequence diagrams or state diagrams. For example: -![test1-sequence](../img/hello-test1.png) +![test1-sequence](./img/hello-test1.png) The unfortunate fact about UML sequence diagrams and state charts is that they are given as figures, which have to be executed in the reader's brain. diff --git a/docs/pages/docs/lessons/img/hello-test1.png b/docs/pages/docs/lessons/img/hello-test1.png new file mode 100644 index 000000000..2e031478b Binary files /dev/null and b/docs/pages/docs/lessons/img/hello-test1.png differ diff --git a/tutorials/lesson2-integers/integers.md b/docs/pages/docs/lessons/integers.md similarity index 98% rename from tutorials/lesson2-integers/integers.md rename to docs/pages/docs/lessons/integers.md index e0e9d49fe..ddae58569 100644 --- a/tutorials/lesson2-integers/integers.md +++ b/docs/pages/docs/lessons/integers.md @@ -14,7 +14,7 @@ Do not skip this lesson, as some of the operators may still surprise you. **Code snippet:** -```bluespec +```quint // 0 is an integer literal pure val int0 = 0 @@ -45,7 +45,7 @@ the tutorial on sets. **Code snippet:** -```bluespec +```quint // i^j is the integer exponentiation, that is, // `i` multiplied by itself `j - 1` times @@ -124,7 +124,7 @@ echo "0^(-2)" | quint **Code snippet:** -```bluespec +```quint // i + j is the integer addition pure def myAdd(i, j) = i + j @@ -166,7 +166,7 @@ echo "(11 + 17) + 19 == 11 + (17 + 19)" | quint **Code snippet:** -```bluespec +```quint // i - j is the integer subtraction pure def mySub(i, j) = i - j @@ -205,7 +205,7 @@ echo "11 + 13 - 17" | quint **Code snippet:** -```bluespec +```quint // i * j is the integer multiplication pure def myMul(i, j) = i * j @@ -245,7 +245,7 @@ echo "(11 * 17) * 19 == 11 * (17 * 19)" | quint **Code snippet:** -```bluespec +```quint // i / j is the integer division pure def myDiv(i, j) = i / j @@ -300,7 +300,7 @@ echo "(2^64 - 123) % 2^63" | quint **Code snippet:** -```bluespec +```quint // `i > j` is true if and only if `i` is greater than `j` pure def myGreaterThan(i, j) = i > j @@ -412,7 +412,7 @@ echo "10 != 11" | quint **Code snippet:** -```bluespec +```quint // -i is the integer negation pure def myUnaryMinus(i) = -i diff --git a/tutorials/lesson4-sets/sets.md b/docs/pages/docs/lessons/sets.md similarity index 97% rename from tutorials/lesson4-sets/sets.md rename to docs/pages/docs/lessons/sets.md index 00b83beb3..e99005fd8 100644 --- a/tutorials/lesson4-sets/sets.md +++ b/docs/pages/docs/lessons/sets.md @@ -34,9 +34,9 @@ For example, @KryptoCoffeeCat want to know whether they could exchange BTC for ATOM by doing up to three swaps. Unfortunately, @KryptoCoffeeCat have lost access to their browser, as they did not update it as often as it was required. Now they are stuck with the -[Quint REPL](https://github.com/informalsystems/quint/blob/main/doc/quint.md#command-repl). +[Quint REPL](https://quint-lang.org/docs/quint#command-repl). Fortunately, they have found the -[Quint cheatsheet](https://github.com/informalsystems/quint/blob/main/doc/quint-cheatsheet.pdf). +[Quint cheatsheet](https://quint-lang.org/quint-cheatsheet.pdf). We will help them! In this tutorial, you will see how to: @@ -51,7 +51,7 @@ In this tutorial, you will see how to: If you would like to see the complete code before diving into -the details, check [sets.qnt](./sets.qnt). +the details, check [sets.qnt](https://github.com/informalsystems/quint/tree/main/examples/tutorials/sets.qnt). ## 2. Defining swap pairs @@ -59,7 +59,7 @@ the details, check [sets.qnt](./sets.qnt). **Code snippet:** -```bluespec +```quint module sets { // We represent swap pairs as tuples of two strings type Pair = (str, str) @@ -106,7 +106,7 @@ echo '("BTC", "ETH")._2' | quint **Code snippet:** -```bluespec +```quint // all available swap pairs as a set of tuples pure val availablePairs = Set( @@ -146,7 +146,7 @@ echo 'availablePairs' | quint -r sets.qnt::sets **Code snippet:** -```bluespec +```quint pure val hasAtomJuno = ("ATOM", "JUNO").in(availablePairs) @@ -175,7 +175,7 @@ echo 'hasEvmosEth' | quint -r sets.qnt::sets **Code snippet:** -```bluespec +```quint pure val hasAtom = availablePairs.exists(p => p._1 == "ATOM") @@ -223,7 +223,7 @@ Yes, @KryptoCoffeeCat, this is the way! **Code snippet:** -```bluespec +```quint // swap pairs as two-element sets (unordered pairs) pure val availableUnorderedPairs = @@ -261,7 +261,7 @@ the definition `hasAtomRight`. This makes them happy. Time to have a coffee! **Code snippet:** -```bluespec +```quint // all unordered pairs that contain ATOM at one end pure val atomPairs = availableUnorderedPairs.filter(p => p.contains("ATOM")) @@ -288,7 +288,7 @@ condition. **Code snippet:** -```bluespec +```quint // the number of swap pairs pure val howMany = availableUnorderedPairs.size() @@ -310,7 +310,7 @@ echo 'howMany' | quint -r sets.qnt::sets **Code snippet:** -```bluespec +```quint // get all coins that appear in pairs pure def coinsInPairs(pairs: Set[Set[str]]): Set[str] = pairs.flatten() @@ -342,7 +342,7 @@ themselves. **Code snippet:** -```bluespec +```quint // an example of `someCoins` in `buyableVia1Swap` pure def someCoinsExample = Set("ATOM", "ETH", "USDT") @@ -444,7 +444,7 @@ echo 'buyableVia1or2Swaps(Set("ATOM"))' | quint -r sets.qnt::sets **Code snippet:** -```bluespec +```quint // which coins can we buy via n swaps when starting from `someCoins` pure def buyableNSwaps(someCoins, n) = @@ -527,7 +527,7 @@ echo 'buyableNSwaps(Set("ATOM"), 5)' | quint -r sets.qnt::sets **Code snippet:** -```bluespec +```quint // inSet.exists(myFun) can be expressed via `fold`, when `inSet` is finite pure def myExists(inSet: Set[a], pred: a => bool): bool = @@ -568,7 +568,7 @@ are much easier to read. **Code snippet:** -```bluespec +```quint // all combinations of unordered pairs that have exactly 4 pairs pure val quads = @@ -641,7 +641,7 @@ echo 'cycles4' | quint -r sets.qnt::sets @KryptoCoffeeCat has learned a lot in this tutorial. By looking at the -[Quint cheatsheet](https://github.com/informalsystems/quint/blob/main/doc/quint-cheatsheet.pdf), +[Quint cheatsheet](https://quint-lang.org/quint-cheatsheet.pdf), they have found that this tutorial did not cover two operators on sets. Can you find them too? diff --git a/doc/literate.md b/docs/pages/docs/literate.md similarity index 93% rename from doc/literate.md rename to docs/pages/docs/literate.md index f02b017d8..239846d04 100644 --- a/doc/literate.md +++ b/docs/pages/docs/literate.md @@ -10,13 +10,13 @@ model based testing. ## Prerequisites - An [installation of go](https://go.dev/doc/install) -- An [installation of quint](../../quint/README.md) +- An [installation of quint](/docs/getting-started) - An [installation of make](https://en.wikipedia.org/wiki/Make_(software)) (probably already on your system) - An installation of lmt via `go install github.com/driusan/lmt@latest` (or following the [lmt instructions to install from source](https://github.com/driusan/lmt#installing-lmt)) -If you use nix, you can use [this nix expression](../tutorials/default.nix). +If you use nix, you can use [this nix expression](https://github.com/informalsystems/quint/tree/main/docs/default.nix). ## Writing a literate spec diff --git a/docs/pages/docs/previews.md b/docs/pages/docs/previews.md new file mode 100644 index 000000000..857b26ca7 --- /dev/null +++ b/docs/pages/docs/previews.md @@ -0,0 +1,17 @@ +# Quick previews + +## VSCode plugin + + + +Install the [Quint VSCode +Plugin](https://marketplace.visualstudio.com/items?itemName=informal.quint-vscode) +from the VisualStudio Marketplace. + +## Command line tools + + + +Install the [Quint +CLI](https://www.npmjs.com/package/@informalsystems/quint) from +npm. diff --git a/doc/quint.md b/docs/pages/docs/quint.md similarity index 99% rename from doc/quint.md rename to docs/pages/docs/quint.md index 569ae9a64..8947bb038 100644 --- a/doc/quint.md +++ b/docs/pages/docs/quint.md @@ -2,7 +2,7 @@ | Revision | Date | Author | |---------:|:----------:|:------------------------| -| 8 | 2023-04-07 | Igor Konnov, Shon Feder | +| 9 | 2024-06-26 | Igor Konnov, Shon Feder, Gabriela Moreira | **WARNING**: *This is a preliminary manual in the style of [Working Backwards]. Some commands are not implemented yet.* @@ -22,7 +22,7 @@ The main commands of `quint` are as follows: similar to stateful property-based testing - [x] `test` runs unit tests against a Quint specification - [x] `verify` verifies a Quint specification with Apalache - - [ ] `docs` produces documentation + - [x] `docs` produces documentation - [ ] `lint` checks a Quint specification for known deficiencies - [ ] `indent` indents a Quint specification @@ -30,7 +30,9 @@ In the following, we give details about the above commands. ## Installation -See [README](../quint/README.md). +``` sh +npm i @informalsystems/quint -g +``` ## Command `repl` diff --git a/tutorials/repl/repl.md b/docs/pages/docs/repl.md similarity index 85% rename from tutorials/repl/repl.md rename to docs/pages/docs/repl.md index 54069bcf7..65ee22466 100644 --- a/tutorials/repl/repl.md +++ b/docs/pages/docs/repl.md @@ -11,8 +11,8 @@ https://github.com/driusan/lmt As a result, this document produces two checkable artifacts: - 1. ./repl/kettle.qnt is the specification that we construct. - 2. ./repl/replTest.txt is the replayable REPL session. + 1. ../../../examples/tutorials/repl/kettle.qnt is the specification that we construct. + 2. ../../../examples/tutorials/repl/replTest.txt is the replayable REPL session. --> A [REPL][] is a read-eval-print loop. A REPL is usually a good way to start @@ -21,26 +21,11 @@ learning a language. Most likely, you have seen a REPL before. If you had not, here is a simple state machine that describes how it works from the user's point of view: -![REPL machine](../img/repl-machine.png) - -# Table of contents - - * [1. Installation](#1-installation) - * [2. Running REPL](#2-running-repl) - * [3. Evaluating expressions](#3-evaluating-expressions) - * [4. Introducing values and definitions](#4-introducing-values-and-definitions) - * [5. Describing a state machine](#5-describing-a-state-machine) - + [5.1. Introducing state variables](#51-introducing-state-variables) - + [5.2. Initializing state variables](#52-initializing-state-variables) - + [5.3. Updating state variables with actions](#53-updating-state-variables-with-actions) - + [5.4. Introducing control non-determinism](#54-introducing-control-non-determinism) - + [5.5. Introducing data non-determinism](#55-introducing-data-non-determinism) - * [6. Saving and loading the REPL session](#6-saving-and-loading-the-repl-session) - * [7. Further reading](#7-further-reading) +![REPL machine](../../public/repl-machine.png) ## 1. Installation -You have to install `quint` first. See the [README](../../README.md#installation). +You have to install `quint` first. See [Getting Started](getting-started). ## 2. Running REPL @@ -63,10 +48,11 @@ You can type `.help` and then press `` for supported REPL commands. ## 3. Preloading definitions In this tutorial, we are interactively constructing the module -[kettle.qnt](./kettle.qnt). You can load this file in the [VSCode Plugin][] and -read it, if you would like to have a better overview of the module that we are -constructing. You can also load this file into REPL, if you only want to -evaluate expressions without copying the definitions by hand: +[kettle.qnt](https://github.com/informalsystems/quint/tree/main/examples/tutorials/repl/kettle.qnt). +You can load this file in the [VSCode Plugin][] and read it, if you would like +to have a better overview of the module that we are constructing. You can also +load this file into REPL, if you only want to evaluate expressions without +copying the definitions by hand: ```sh $ quint -r kettle.qnt::kettle @@ -75,7 +61,7 @@ $ quint -r kettle.qnt::kettle When you load `kettle.qnt` this way, REPL prints `true` indicating that the module has been loaded successfully: -```bluespec ./repl/replTest.txt += +```quint ../../../examples/tutorials/repl/replTest.txt += true ``` @@ -92,14 +78,14 @@ specifications in Markdown and like to connect it to Quint specifications. The core interaction with a REPL is to enter an expression and get back the result of its evaluation. Like this: -```bluespec ./repl/replTest.txt += +```quint ../../../examples/tutorials/repl/replTest.txt += >>> 1 + 3 4 ``` Or like this: -```bluespec ./repl/replTest.txt += +```quint ../../../examples/tutorials/repl/replTest.txt += >>> Set(1, 2, 3).map(i => i * 2) Set(2, 4, 6) ``` @@ -109,7 +95,7 @@ Set(2, 4, 6) In this tutorial, we are interactively constructing the module [kettle.qnt](./kettle.qnt) that has the following structure: -```bluespec ./repl/kettle.qnt += +```quint ../../../examples/tutorials/repl/kettle.qnt += // -*- mode: Bluespec; -*- // The example from the REPL tutorial module kettle { @@ -123,7 +109,7 @@ implicit module. Whenever we write four leading spaces ` ` instead of `>>> ` and `... `, we append the contents to the file [kettle.qnt](./kettle.qnt). For example: -```bluespec "definitions" += +```quint "definitions" += // an example of a definition val isThisMyFirstDefinition = true ``` @@ -139,7 +125,7 @@ REPL context. For instance: Let's declare two immutable values: -```bluespec "definitions" += +```quint "definitions" += // a constant value that always returns 100 val boilingTemperature = 100 @@ -151,7 +137,7 @@ Let's declare two immutable values: And we'll also define an operation to derive temperature in fahrenheit given a temperature in Celsius: -```bluespec "definitions" += +```quint "definitions" += // conversion from Celsius to Fahrenheit def fahrenheit(celsius) = celsius * 9 / 5 + 32 @@ -159,7 +145,7 @@ temperature in Celsius: We can use the values and definitions in later expressions and declarations: -```bluespec ./repl/replTest.txt += +```quint ../../../examples/tutorials/repl/replTest.txt += >>> fahrenheit(freezingTemperature) 32 >>> fahrenheit(boilingTemperature) @@ -173,7 +159,7 @@ true We can also write new definitions by referring to the previously defined operators: -```bluespec "definitions" += +```quint "definitions" += // the low range of a thermometer (fixed) val veryCold = fahrenheit(-40) @@ -184,7 +170,7 @@ operators: This is how REPL evaluates the expressions that use the above definitions: -```bluespec ./repl/replTest.txt += +```quint ../../../examples/tutorials/repl/replTest.txt += >>> veryCold -40 >>> veryHot @@ -204,12 +190,12 @@ great tool for learning the language. But REPL can do more for you: It can simulate a state machine. In this section, we interactively specify a model of an electric kettle: -![An electric kettle](../img/kettle-drawing.png) +![An electric kettle](../../public/kettle-drawing.png) Informally, we can describe the most basic operation of a kettle like the one above with the following [state machine][]: -![State chart](../img/kettle-state1.svg) +![State chart](../../public/kettle-state1.svg) If you think, "this diagram is not very realistic", you are right. We will extend it later. @@ -219,7 +205,7 @@ it later. In addition to definitions and values, we also declare state variables: -```bluespec "definitions" += +```quint "definitions" += // the current temperature in the kettle var temperature: int @@ -234,9 +220,9 @@ In addition to definitions and values, we also declare state variables: By default, a state variable is not assigned any value and a reference to a declared but unassigned state variable will produce a runtime error: -```bluespec ./repl/replTest.txt += +```quint ../../../examples/tutorials/repl/replTest.txt += >>> temperature -runtime error: error: Variable temperature is not set +runtime error: error: [QNT502] Variable temperature is not set var temperature: int ^^^^^^^^^^^^^^^^^^^^ @@ -249,7 +235,7 @@ Undefined state variables are not very useful. Hence, we have to introduce an action to initialize the state machine and move it forward. For instance, here is how we initialize our kettle: -```bluespec "definitions" += +```quint "definitions" += // a state initializer action init = all { temperature' = 20, @@ -262,7 +248,7 @@ is how we initialize our kettle: The action `init` is just an action definition, which can be applied later. To apply it, we simply type `init`: -```bluespec ./repl/replTest.txt += +```quint ../../../examples/tutorials/repl/replTest.txt += >>> init true ``` @@ -274,7 +260,7 @@ prevented the action from being applied; more on that later. Now we can check that `init` has indeed initialized the state variables as expected: -```bluespec ./repl/replTest.txt += +```quint ../../../examples/tutorials/repl/replTest.txt += >>> temperature 20 >>> heatingOn @@ -284,7 +270,7 @@ false To make it easier to see how our state evolves, let's also declare a value that collects all of our state variables in a single record: -```bluespec "definitions" += +```quint "definitions" += // a handy definition that captures the state in a record val kettleState = { heatingOn: heatingOn, @@ -297,7 +283,7 @@ collects all of our state variables in a single record: If we evaluate `kettleState` in the current state, we should get the following output: -```bluespec ./repl/replTest.txt += +```quint ../../../examples/tutorials/repl/replTest.txt += >>> kettleState { beeping: false, heatingOn: false, temperature: 20 } ``` @@ -307,7 +293,7 @@ output: Similar to how we introduced `init`, we introduce the action `pressButton`, which turns on the heating element of our kettle. -```bluespec "definitions" += +```quint "definitions" += // turn on the heating element action pressButton = all { not(heatingOn), @@ -321,14 +307,14 @@ which turns on the heating element of our kettle. Again, we have just defined the action `pressButton`, but that action is not applied automatically. To apply it, we simply type its name: -```bluespec ./repl/replTest.txt += +```quint ../../../examples/tutorials/repl/replTest.txt += >>> pressButton true ``` We can check our state variables to make sure that the action indeed took place: -```bluespec ./repl/replTest.txt += +```quint ../../../examples/tutorials/repl/replTest.txt += >>> kettleState { beeping: false, heatingOn: true, temperature: 20 } ``` @@ -338,7 +324,7 @@ The heat is on now! Interestingly, if we try to apply `pressButton` once again, it would not work, as indicated by the `false` result: -```bluespec ./repl/replTest.txt += +```quint ../../../examples/tutorials/repl/replTest.txt += >>> pressButton false ``` @@ -352,7 +338,7 @@ Similar to `pressButton`, we can define the `failover` action that turns off the kettle when the temperature reaches 100. If the value 100 makes you puzzled, call `fahrenheit(100)` in REPL ;-) -```bluespec "definitions" += +```quint "definitions" += // turn off the kettle when the temperature is too high action failover = all { heatingOn, @@ -366,14 +352,14 @@ puzzled, call `fahrenheit(100)` in REPL ;-) If we apply `failover` to the current state, it will not execute: -```bluespec ./repl/replTest.txt += +```quint ../../../examples/tutorials/repl/replTest.txt += >>> failover false ``` Indeed, `temperature` is equal to 20 in the current state of REPL: -```bluespec ./repl/replTest.txt += +```quint ../../../examples/tutorials/repl/replTest.txt += >>> temperature 20 ``` @@ -382,7 +368,7 @@ To properly test `failover`, we should probably define the action `heat` in our spec. But since we are in REPL, we can also take a shortcut, by simply setting the state variables to the state we need: -```bluespec ./repl/replTest.txt += +```quint ../../../examples/tutorials/repl/replTest.txt += >>> all { temperature' = 100, heatingOn' = true, beeping' = false } true ``` @@ -396,7 +382,7 @@ values of `heatingOn` and `beeping` would have become undefined. Now we can apply `failover`: -```bluespec ./repl/replTest.txt += +```quint ../../../examples/tutorials/repl/replTest.txt += >>> failover true >>> heatingOn @@ -411,7 +397,7 @@ To reset the state to the previous one, we can simply evaluate `init` and `pressButton` again (there is a better way to do that, which we will show later): -```bluespec ./repl/replTest.txt += +```quint ../../../examples/tutorials/repl/replTest.txt += >>> init true >>> pressButton @@ -422,7 +408,7 @@ true Now it is time to specify the action `heat`: -```bluespec "definitions" += +```quint "definitions" += // heat up the water by 1C action heat = all { heatingOn, @@ -436,7 +422,7 @@ Now it is time to specify the action `heat`: By applying `heat` several times, we can see that it heats up the kettle a bit: -```bluespec ./repl/replTest.txt += +```quint ../../../examples/tutorials/repl/replTest.txt += >>> heat true >>> temperature @@ -459,11 +445,11 @@ heating element off, namely, the `failover` action. Normally, a kettle can be turned off without waiting until it boils to 100C. Hence, we add one more action `depressButton` in our diagram: -![State chart 2](../img/kettle-state2.svg) +![State chart 2](../../public/kettle-state2.svg) This action should be easy to define: -```bluespec "definitions" += +```quint "definitions" += // turn off the heating element action depressButton = all { heatingOn, @@ -477,7 +463,7 @@ This action should be easy to define: Now we can execute four actions and observe that we managed to heat the kettle by 1 degree and turn it off: -```bluespec ./repl/replTest.txt += +```quint ../../../examples/tutorials/repl/replTest.txt += >>> init true >>> pressButton @@ -493,7 +479,7 @@ true Notice that our specification allows for a new interesting behavior. Evaluate the following expressions: -```bluespec ./repl/replTest.txt += +```quint ../../../examples/tutorials/repl/replTest.txt += >>> all { heatingOn' = true, temperature' = 100, beeping' = false } true >>> depressButton @@ -527,7 +513,7 @@ How do we describe in the specification that one of the actions may apply, whichever happens first, and we do not control which one? Quint has the operator `any` to do exactly this: -```bluespec ./repl/replTest.txt += +```quint ../../../examples/tutorials/repl/replTest.txt += >>> all { heatingOn' = true, temperature' = 100, beeping' = false } true >>> any { @@ -548,7 +534,7 @@ actions behaves like a deterministic program. We will introduce Now it is time to define all possible transitions of the kettle in one place: -```bluespec "definitions" += +```quint "definitions" += // one step of the state machine action step = any { pressButton, @@ -562,7 +548,7 @@ Now it is time to define all possible transitions of the kettle in one place: Having defined `step`, we can conveniently execute steps without specifying the action that should be executed next: -```bluespec ./repl/replTest.txt += +```quint ../../../examples/tutorials/repl/replTest.txt += >>> init true >>> step @@ -581,7 +567,7 @@ that the state is different from the initial one: -```bluespec +```quint >>> kettleState { heatingOn: true, beeping: false, temperature: 21 } ``` @@ -602,7 +588,7 @@ temperature is always set to 20 (Celsius). Most likely, you are using your kettle under different temperatures too. Let's update the state diagram, to reflect the reality a bit better: -![State chart 3](../img/kettle-state3.svg) +![State chart 3](../../public/kettle-state3.svg) I never tried to melt ice with an electric kettle, but it should probably work as expected? @@ -611,7 +597,7 @@ It looks like we have to describe multiple possible initial states that differ in temperature. This is how we can do that in Quint, specifying that the temperature should be somewhere in the range of -40 to 40 degrees: -```bluespec "definitions" += +```quint "definitions" += // initialize the state machine with non-determinism action initNondet = all { heatingOn' = false, @@ -624,7 +610,7 @@ temperature should be somewhere in the range of -40 to 40 degrees: Let's see how it works. Execute `initNondet` for the first time: -```bluespec ./repl/replTest.txt += +```quint ../../../examples/tutorials/repl/replTest.txt += >>> initNondet true ``` @@ -633,7 +619,7 @@ Here is an example of a state that we may get into: -```bluespec +```quint >>> kettleState { heatingOn: false, beeping: false, temperature: -27 } ``` @@ -642,7 +628,7 @@ If we execute `initNondet` more, we obtain different states. Try it: -```bluespec +```quint >>> initNondet true >>> kettleState @@ -658,7 +644,7 @@ temperature from the interval `[-40, 40]` and sets `heatingOn` and `beeping` to `false`. The magic is done by the special syntax form of Quint that looks like follows: -```bluespec +```quint nondet myElem = mySet.oneOf() expr ``` @@ -678,7 +664,7 @@ close the REPL. You can save the REPL session with the builtin command `.save`: -```bluespec ./repl/replTest.txt += +```quint ../../../examples/tutorials/repl/replTest.txt += >>> .save kettle.qnt Session saved to: kettle.qnt @@ -689,7 +675,7 @@ You can edit this file in your editor of choice and load it back to REPL: -```bluespec +```quint >>> .clear >>> .load kettle.qnt ``` @@ -724,4 +710,4 @@ features, check [Tutorial 3][]. [VSCode Plugin]: https://marketplace.visualstudio.com/items?itemName=informal.quint-vscode [Literate programming]: https://en.wikipedia.org/wiki/Literate_programming [lmt]: https://github.com/driusan/lmt -[Tutorial 3]: ../lesson3-anatomy/coin.md +[Tutorial 3]: lessons/coin diff --git a/docs/pages/docs/rfcs/rfc001-sum-types.md b/docs/pages/docs/rfcs/rfc001-sum-types.md new file mode 100644 index 000000000..d1aeab944 --- /dev/null +++ b/docs/pages/docs/rfcs/rfc001-sum-types.md @@ -0,0 +1,1207 @@ +--- +author: Shon Feder +date: \<2023-08-03 Thu\> +title: "Extend Quint with Row-Polymorphic Sum Types" +--- + +# RFC 001: Extend Quint with Row-Polymorphic Sum Types + +This section gives a concise overview of the principle proposal. In +depth discussion follows in [2](#Discussion). + +## Concrete syntax + +The concrete syntax falls into three categories, corresponding three +aspects of the semantics: + +- Declaring that a type is allowed in a context. +- Constructing an element of the declared type. +- Eliminating an element of the declared type (to derive an element of + some other type). + +Here we merely state the proposed syntax. See [2.4](#Concrete Syntax) +for consideration of motivations and trade offs. + +### Declaration + +The proposed syntax fits complements our syntax for records and follows +the precedent of most languages that include support for general +sum-types: + +``` quint +type T = + | A(int) + | B(str) + | C +``` + +As indicated by `C`, labels with no associated expression type are +allowed. These are sugar for a label associated with the unit type +(i.e., the empty record). The above is therefore a representation of + +``` quint +type T = + | A(int) + | B(str) + | C({}) +``` + +### Constructors + +Internally, we need to need add an operator `variant` similar to the +"injection" operator from +[Leijen05](https://www.microsoft.com/en-us/research/publication/extensible-records-with-scoped-labels/): + +``` haskell + :: ∀αr . α → -- injection +``` + +This operation injects an expression of type `α` into a sum-type that +includes a variant associating label `l` with type `α`. However, if we +don't want to expose the row-polymorphism to users, we'll need a more +restrictive typing. This is discussed in the section on typing rules. + +We provide syntax sugar for users: when a sum type declaration is +parsed, constructor operations for each variant will be generated +internally. E.g., for the type `T` defined above, we will add to the +context the following operators: + +``` quint +pure def A(x:int): T = variant(A, x) +pure def B(x:str): T = variant(B, x) +pure def C(): T = variant(C, {}) +``` + +### Case analyses + +Expressions of a sum type can be eliminated using a case analysis via a +`match` statement. Following Rust's syntax, we write that as + +``` quint +match e { + | A(a) => ... + | B(b) => ... + | C => ... +} +``` + +This construct can either be a primitive or syntax sugar for a more +primitive `decompose` operator, discussed below. + +We could also consider a case analysis +`{ A(a) => e1, B(b) => e2, C => e3 }` – where `e1, e2, e3 : S` – as +syntax sugar for an anonymous operator of type `T +=> S`. `match` would then be syntax sugar for operator application. This +follows Scala's case blocks or OCaml's `function` keyword and would +allow idioms such as: + +``` quint +setOfTs.map({ A(a) => e1 | B(b) => e2 | C => e3 }) +``` + +instead of requiring + +``` quint +setOfTs.map(x => match x { A(a) => e1| B(b) => e2| C => e3 }) +``` + +## Statics + +These type rules assume we keep our current approach of using quint +strings for labels. But see my argument for simplifying our approach +under [2.6.3](#*Drop the exotic operators). See the discussion in +[2.3](#*Statics) below for a detailed explanation and analysis. + +### Construction + +The typing rule for constructing a variant of a sum type: + +$$ +\frac +{ +\Gamma \vdash e \colon (t, c) \quad +\Gamma \vdash `l' \colon str \quad +definedType(s) \quad +free(v) +} +{ +\Gamma \vdash \ variant(`l', e) \ : +(s, c \land s \sim \langle \ l \colon t | v \ \rangle) +} +$$ + +This rule is substantially different from +[Leijen05](https://www.microsoft.com/en-us/research/publication/extensible-records-with-scoped-labels/)'s + +``` haskell + :: ∀αr . α → -- injection +``` + +because we have decided not to expose the underlying row-polymorphism +for sum types at this point. This introduces the non-trivial +complication of needing to introduce the typing context onto our +judgments (this is what we gesture at with the side condition +`definedType(s)`). + +### Elimination + +The typing rule for eliminating a variant of a sum type via case +analysis: + +$$ +\frac{ +\Gamma \vdash e : (s, c) \quad +\Gamma, x_1 \vdash e_1 : (t, c_1) \quad \ldots \quad \Gamma, x_n \vdash e_n : (t, c_n) \quad +\Gamma, \langle v \rangle \vdash e_{n+1} : (t, c_{n+1}) \quad +fresh(v) +}{ +\Gamma \vdash \ match \ e \ \{ i_1 : x_1 \Rightarrow e_1, \ldots, i_n : x_n \Rightarrow e_n \} : (t, +c \land c_1 \land \ldots \land c_n \land c_{n+1} \land +s \sim \langle i_1 : t_1, \ldots, i_n : t_n | v \rangle) +} +$$ + +This gives a rule in our system that is sufficient to capture +[Leijen05](https://www.microsoft.com/en-us/research/publication/extensible-records-with-scoped-labels/)'s + +``` haskell +(l ∈ _ ? _ : _) :: ∀αβr . → (α → β) → ( → β) → β -- decomposition +``` + +since we can define decomposition for any label `L` via + +``` quint +def decomposeL(e: (L(a) | r), f: a => b, default : r => b) = + match e { + | L(x) => f(x) + | r => default(r) + } +``` + +However we can define `match` as syntax sugar for the decompose +primitive if we prefer. + +## Dynamics + +The dynamics in the simulator should be straightforward and is not +discussed here. Translation to Apalache for symbolic execution in the +model checker is also expected to be relatively straight forward, since +Apalache has a very similar form of row-based sum typing. + +The general rules for eager evaluation can be found in +[PFPL](https://www.cs.cmu.edu/~rwh/pfpl.html), section 11.2. Additional +design work for this will be prepared if needed. + +------------------------------------------------------------------------ + +This concludes the tl;dr overview of the proposal. The remaining is an +indepth (still v. rough in places, discussion). + +# Discussion + +## Motivation + +Quint's type system currently supports product types. Product types +(i.e., records, with tuples as a special case where fields are indexed +by an ordinal) let us specify *conjunctions* of data types in a way that +is verifiable statically. This lets us describe more complex data +structures in terms of values of specific types that **must** be +packaged together. E.g., we might define a rectangle by its length and +width and a triangle by the lengths of its three sides. Using Quint's +existing syntax for product types, we'd specify this as follows: + +``` quint +type Rectangle = + { l : int + , w : int } +type Triangle = + { a : int + , b : int + , c : int } +``` + +Quint's type system does not yet have the the dual construct, [sum +types](https://en.wikipedia.org/wiki/Tagged_union) (aka "variants", +"co-products", or "tagged unions"). Sum types specify *disjunctions* of +data types in a way that is verifiable statically. This lets us describe +mutually exclusive alternatives between distinct data structures that +**may** occur together and be treated uniformly in some context. E.g., +we might wish to specify a datatype for shapes, so we can work with +collections that include both rectangles and triangles. Using one of the +proposed syntax option that will be motivated in the following, this +could be specified as + +``` quint +type Shape = + | Rect(rectangle) + | Tri(triangle) +``` + +Having both product types and sum types (co-product types) gives us a +simple and powerful idiom for specifying families of data structures: + +- We describe *what must be given together* to form a product of the + specified type, and so *what we may always make use of* by projection + when we are given such a product. +- We describe *which alternatives may be supplied* to form a co-product + of a specified type, and so *what we must be prepared to handle* + during case analysis when we are given such a co-product. + +E.g., a `rectangle` is defined by *both* a length *and* a width, +packaged together, while a `shape` is defined *either* by a rectangle +*or* a triangle. With these definitions established, we can then go on +to form and reason about collections of shapes like `Set[shape]`, or +define properties common to all shapes like +`isEquilateral : shape => bool`[^1]. + +## Context + +### Existing plans and previous work + +We have always planned to support co-products in quint: their utility is +well known and widely appreciated by engineers with experience in modern +programming languages. We introduced co-products to Apalache in + for the same +reasons. The design and implementation of the latter was worked out by +based on the paper ["Extensible Records with Scoped +Labels"](https://www.microsoft.com/en-us/research/publication/extensible-records-with-scoped-labels/). +At the core of this design is a simple use of row-polymorphism that +enables both extensible variants and extensible records, giving us +products and co-products in a one neat package. The quint type system +was also developed using row-polymorphism following this design. As a +result of this forethought, extension of quint's type system and +addition of syntax to support sum-types is expected to be relatively +straightforward. + +### The gist of extensible row-typed records and sum types + +The core concept in the row-based approach we've opted for is the +following: we can use the same construct, called a "row", to represent +the *conjoined* labeled fields of a product type and the *alternative* +labeled choices of a sum type. That the row types are polymorphic lets +us extend the products and sums using row variables. + +E.g., given the row + +$$ +i_1 : t_1 \ , \ldots \ , i_n : i_n | v +$$ + +with each $t_k$-typed field indexed by label $i_k$ for $1 \le k \le n$ +and the free row variable $v$, then + +$$ +\{i_1 : t_1 \ , \ldots \ , i_n : i_n | v\} +$$ + +is an open record conjoining the fields, and + +$$ +\langle i_1 : t_1 \ , \ldots \ , i_n : i_n | v \rangle +$$ + +is an open sum type presenting the fields as (mutually exclusive) +alternatives. Both types are extensible by substituting $v$ with another +(possibly open row). To represent a closed row, we omit the trailing +$| v$. + +### Quint's current type system + +The [current type system supported by +quint](https://github.com/informalsystems/quint/tree/main/doc/adr005-type-system.md) +is based on a simplified version of the constraint-based system +presented in ["Complete and Decidable Type Inference for +GADTs"](https://www.microsoft.com/en-us/research/publication/complete-and-decidable-type-inference-for-gadts/) +augmented with extensible (currently, just) records based on "Extensible +Records with Scoped Labels". A wrinkle in this genealogy is that quint's +type system includes neither GADTs nor scoped labels (and even the +extensiblity supported for records is limited). Moreover, due to their +respective foci, neither of the referenced papers includes a +formalization the complete statics for product types or sum types, and +while we have implemented support for product types in quint, we don't +have our typing rules recorded. + +## Statics + +This section discusses the typing judgements that will allow us to +statically verify correct introduction and elimination of expressions +that are variants of a sum type. The following considerations have +informed the structure in which the proposed statics are discussed: + +- Since sum-types are dual to product types, I consider their + complementary typing rules together: first I will present the relevant + rule for product types, then propose the complementary rule for sum + types. This should help maintain consistency between the two kinds of + typing judgements and ensure our implementations of both harmonize. +- Since we don't have our existing product formation or elimination + rules described separate from the implementation, transcribing them + here can serve to juice our intuition, supplement our design + documentation, and perhaps give opportunity for refinement. +- Since our homegrown type system has some idiosyncrasies that can + obscure the essence of the constructs under discussion, I precede the + exposition of each rule with a text-book example adapted from + [Practical Foundations for Programming + Languages](https://www.cs.cmu.edu/~rwh/pfpl.html). This is only meant + as a clarifying touchstone. + +### Eliminating products and introducing sums + +The elimination of products via projection and the introduction of sums +via injection are the simplest of the two pairs of rules. + +1. Projection + + Here is a concrete example of projecting a value out of a record + using our current syntax: + + ``` quint + val r : {a:int} = {a:1} + val ex : int = r.a + // Or, using our exotic field operator, which is currently the normal form + val ex_ : int = r.field("a") + ``` + + A textbook rule for eliminating an expression with a finite product + types can be given as + + $$ + \frac + { \Gamma \vdash e \colon \{ i_1 \hookrightarrow \tau_1, \ \ldots, \ i_n \hookrightarrow \tau_n \} \quad (1 \le k \le n)} + { \Gamma \vdash e.i_k \colon \tau_k } + $$ + + Where $i$ is drawn from a finite set of indexes used to label the + components of the product (e.g., fields of a record or positions in + a tuple) and $i_j \hookrightarrow \tau_j$ maps the index $i_j$ to + the corresponding type $\tau_j$. + + This rule tells us that, when an expression $e$ with a product type + is derivable from a context, we can eliminate it by projecting out + of $e$ with an index $i_k$ (included in the type), giving an + expression of the type $t_k$ corresponding to that index. If we're + given a bunch of stuff packaged together we can take out just the + one part we want. + + In our current system, typechecking the projection of a value out of + a record + [implements](https://github.com/informalsystems/quint/blob/545b14fb8c19ac71d8f08fb8500ce9cc3cabf678/quint/src/types/specialConstraints.ts#L91-L120) + the following rule + + $$ + \frac + { \Gamma \vdash e \colon (r, c) \quad \Gamma \vdash `l' \colon str \quad fresh(t) } + { \Gamma \vdash \ field(e, `l') \ \colon (t, c \land r \sim \{ \ l \colon t | tail\_t \ \}) } + $$ + + where + + - we use the judgement syntax established in + [ADR5](https://github.com/informalsystems/quint/tree/main/doc/adr005-type-system.md), + in which $\Gamma \vdash e : (t, c)$ means that, in the typing + context $\Gamma$, expression $e$ can be derived with type $t$ + under constraints $c$, + - $fresh(t)$ is a side condition requiring the type variable $t$ to + be fresh in $\Gamma$, + - $`l'$ is a string literal with the internal representation $l$, + - $c$ are the constraints derived for the type $r$, + - $tail\_t$ is a free row-variable constructed by prefixing the + fresh variable $t$ with "tail", + - $\{ \ l \colon t | tail\_t \ \}$ is the open row-based record type + with field, $l$ assigned type $t$ and free row- left as a free + variable, + - and $r \sim \{ \ l \colon t | tail\_t \ \}$ is a unification + constraint. + + Comparing the textbook rule with the rule in our system helps make + the particular qualities and idiosyncrasies of our system very + clear. + + The most critical difference w/r/t to the complexity of the typing + rules derives form the fact that our system subordinates + construction and elimination of records to the language level + operator application rather than implementing it via a special + constructs that work with product indexes (labels) directly. This is + what necessitates the consideration of the string literal $`l'$ in + our premise. In our rule for type checking record projections we + "lift" quint expressions (string literals for records and ints for + products) into product indexes. + + The most salient difference is the use of unification constraints. + This saves us having to "inspect" the record type to ensure the + label is present and obtain its type. These are both accomplished + instead via the unification of $r$ with the minimal open record + including the fresh type $t$, which will end up holding the inferred + type for the projected value iff the unification goes through. This + feature of our type system is of special note for our aim of + introducing sum-types: almost all the logic for ensuring the + correctness of our typing judgements is delegated to the unification + rules for the row-types that carry our fields for product type and + sum types alike. + +2. Injection + + Here is a concrete example of injecting a value into a sum type: + + ``` quint + val n : int = 1 + val ex : A(int) = A(1) + ``` + + A textbook rule for eliminating an expression belonging to a finite + product type can be given as + + $$ + \frac + { \Gamma \vdash e \colon \tau_k \quad (1 \le k \le n)} + { \Gamma \vdash i_k \cdot e \colon \langle i_1 \hookrightarrow \tau_1, \ \ldots, \ i_n \hookrightarrow \tau_n \rangle } + $$ + + Where $i$ is drawn from a finite set of indexes used to label the + possible alternatives of the co-product and + $i_j \hookrightarrow \tau_j$ maps the index $i_j$ to the + corresponding type $\tau_j$. We use $\langle \ldots \rangle$ to + indicate the labeling is now disjunctive and $i_k \cdot e$ as the + injection of $e$ into the sum type using label $i_k$. Note the + symmetry with complementary rule for projection out of a record: the + only difference is that the (now disjunctive) row (resp. (now + injected) expression) is swapped from premise to conclusion (resp. + from conclusion to premise). + + This rule tells us that, when an expression $e$ with a type $t_k$ is + derivable from a context, we can include it as an alternative in our + sum type by injecting it with the label $i_k$, giving an element of + our sum type. If we're given a thing that has a type allowed by our + alternatives, it can included among our alternatives. + + If we were following the row-based approach outlined in + [Leijen05](https://www.microsoft.com/en-us/research/publication/extensible-records-with-scoped-labels/), + then the proposed rule in our system, formed by seeking the same + symmetry w/r/t projection out from a product, would be: + + $$ + \frac + { \Gamma \vdash e \colon (t, c) \quad \Gamma \vdash `l' \colon str \quad fresh(s) } + { \Gamma \vdash \ variant(`l', e) \ \colon (s, c \land s \sim \{ \ l \colon t | tail\_s \ \}) } + $$ + + Comparing this with our current rule for projecting out of records, + we see the same symmetry: the (now disjunctive) row type is + synthesized instead of being taken from the context. + + However, if we don't want to expose the row-polymorphism to users, + we need a more constrained rule that will ensure the free row + variable is not surfaced. We can address this by replacing the side + condition requiring $s$ to be free with a side condition requiring + that there it be defined, and in our constraint check that we can + unify that defined type with a row that contains the given label + with the expected type and is otherwise open. + + $$ + \frac + { + \Gamma \vdash e \colon (t, c) \quad + \Gamma \vdash `l' \colon str \quad + definedType(s) \quad + free(v) + } + { + \Gamma \vdash \ variant(`l', e) \ : + (s, c \land s \sim \langle \ l \colon t | v \ \rangle) + } + $$ + + Igor has voiced a strong preference that we do not allow anonymous + or row-polymorphic sum types, which is why the last rule is + proposed. It does complicate our typing rules, as it requires we + draw from the typing context. + +### Introducing products and eliminating sums + +Forming expressions of product types by backing them into records and +eliminating expressions of sum types by case analysis exhibit the same +duality, tho they are a bit more complex. + +1. Packing expressions into records + + Here is a concrete example of forming a record using our current + syntax: + + ``` quint + val n : int = 1 + val s : str = "one" + val ex : {a : int, b : str} = {a : n, b : s} + // Or, using our exotic Rec operator, which is currently the normal form + val ex_ : {a : int, b : str} = Rec("a", n, "b", s) + ``` + + A textbook introduction rule for finite products is given as + + $$ + \frac + { \Gamma \vdash e_1 \colon \tau_1 \quad \ldots \quad \Gamma \vdash e_n \colon \tau_n } + { \Gamma \vdash \{ i_1 \hookrightarrow e_1, \ldots, i_n \hookrightarrow e_n \} \colon \{ i_1 \hookrightarrow \tau_1, \ldots, i_n \hookrightarrow \tau_n \} } + $$ + + This tells us that for any expressions + $e_1 : \tau_1, \ldots, e_n : \tau_n$ derivable from our context we + can form a product that indexes those $n$ expressions by + $i_1, \ldots, i_n$ distinct labels, and packages all data together + in an expression of type + $\{ i_1 \hookrightarrow \tau_1, \ldots, i_n \hookrightarrow \tau_n \}$. + If we're given all the things of the needed types, we can conjoint + them all together into one compound package. + + The following rule describes our current implementation: + + $$ + \frac + { \Gamma \vdash (`i_1`, e_1 \colon (t_1, c_1)) \quad \ldots \quad \Gamma \vdash (`i_1`, e_n \colon (t_n, c_n)) \quad fresh(s) } + { \Gamma \vdash Rec(`i_1`, e_1, \ldots, `i_n`, e_n) \ \colon \ (s, c_1 \land \ldots \land c_n \land s \sim \{ i_1 \colon t_1, \ldots, i_n \colon t_n \}) } + $$ + + The requirement that our labels show up in the premise as quint + strings paired with each element of the appropriate type is, again, + an artifact of the exotic operator discussed later, as is the `Rec` + operator in the conclusion that consumes these strings. Ignoring + those details, this rule is quite similar to the textbook rule, + except we use unification of the fresh variable `s` to propagate the + type of the constructed record, and we have to do some bookkeeping + with the constraints from each of the elements that will be packaged + into the record. + +2. Performing case analysis + + Here is a concrete example of case analysis to eliminate an + expression belonging to a sum type using the proposed syntax + variants: + + ``` quint + val e : T = A(1) + def describeInt(n: int): str = if (n < 0) then "negative" else "positive" + val ex : str = match e { + | A(x) => describeInt(x) + | B(x) => x + } + ``` + + A textbook rule for eliminating an expression that is a variant of a + finite sum type can be given as + + $$ + \frac{ + \Gamma \vdash e \colon + \langle i_1 \hookrightarrow \tau_1, \ldots, i_n \hookrightarrow \tau_n \rangle + \quad + \Gamma, x_1 : \tau_1 \vdash e_1 \colon \tau + \quad + \ldots + \quad + \Gamma, x_n : \tau_n \vdash e_n \colon \tau + } + { \Gamma \vdash \ match \ e \ + \{ i_1 \cdot x_1 \hookrightarrow e_1 | \ldots | i_n \cdot x_n \hookrightarrow e_n \} : \tau } + $$ + + Note the complementary symmetry compared with the textbook rule for + product construction: product construction requires `n` expressions + to conclude with a single record-type expression combining them all; + while sum type elimination requires a single sum-typed expression + and `n` ways to convert each of the `n` alternatives of the sum type + to conclude with a single expression of a type that does not need to + appear in the sum type at all. + + The proposed rule for quint's type system is given without an + attempt to reproduce our practice of using quint strings. This can + be added in later if needed: + + $$ + \frac{ + \Gamma \vdash e : (s, c) \quad + \Gamma, x_1 \vdash e_1 : (t, c_1) \quad \ldots \quad \Gamma, x_n \vdash e_n : (t, c_n) \quad + \Gamma, \langle v \rangle \vdash e_{n+1} : (t, c_{n+1}) \quad + fresh(v) + }{ + \Gamma \vdash \ match \ e \ \{ i_1 : x_1 \Rightarrow e_1, \ldots, i_n : x_n \Rightarrow e_n \} : (t, + c \land c_1 \land \ldots \land c_n \land c_{n+1} \land + s \sim \langle i_1 : t_1, \ldots, i_n : t_n | v \rangle) + } + $$ + + Compared with quint's rule for product construction we see the same + complementary symmetry. However, we also see a striking difference: + there is no row variable occurring in the product construction, but + the row variable plays an essential function in sum type elimination + of row-based variants. Row types in records are useful for extension + operations (i.e., which we don't support in quint currently) and for + operators that work over some known fields but leave the rest of the + record contents variable. But the core idea formalized in a product + type is that the constructor *must* package all the specified things + together so that the recipient *can* chose any thing; thus, when a + record is constructed we must supply all the things and there is no + room for variability in the row. For sum types, in contrast the + constructor *can* supply any one thing (of a valid alternate type), + and requires the recipient *must* be prepared to handle every + possible alternative. + + In the presence of row-polymorphis, however, the responsibility of + the recipient is relaxed: the recipient can just handle a subset of + the possible alternatives, and if the expression falls under a label + they are not prepared to handle, they can pass the remaining + responsibility on to another handler. + + Here is a concrete example using the proposed syntax, note how we + narrow the type of `T`: + + ``` quint + type T = A | B;; + def f(e) = match e { + | A => 1 + | B => 2 + | _ => 0 + } + + // f can be applied to a value of type T + let a : T = A + let ex : int = f(A) + + // but since it has a fallback for an open row, it can also handle any other variant + let foo = Foo + let ex_ : int = f(foo) + ``` + + Here's the equivalent evaluated in OCaml as proof of concept: + + ``` ocaml + utop # + type t = [`A | `B] + let f = function + | `A -> 1 + | `B -> 2 + | _ -> 0 + let ex = f `A, f `Foo + ;; + type t = [ `A | `B ] + val f : [> `A | `B ] -> int = + val ex : int * int = (1, 0) + ``` + + All the features just discussed that come from row-polymorphism will + not be available since we are choosing to suppress the row-typing. + +## Concrete Syntax + +### Why not support "polymorphic variants" + +Our sum type system is based on row-polymorphism. The only widely used +language I've found that uses row-polymorphism for extensible sum types +is OCaml (and the alternative surface syntax, ReScript/Reason). For +examples of their syntax for this interesting construct, see + +ReScript + + +OCaml + + +These very flexible types are powerful, but they introduce challenges to +the syntax (and semantics) of programs. For example, supporting +anonymous variant types requires a way of constructing variants without +pre-defined constructors. Potential approaches to address this include: + +- A special syntax that (ideally) mirrors the syntax of the type. +- A special lexical marker on the labels (what ReScrips and OCaml do), + e.g., `` `A 1 `` or `#a 1` instead of `A(1)`. + +However, we have instead opted to hide the row-polymorphism, and not +expose this. + +### Declaration + +1. Why use the `|` syntax to separate alternatives? + + - In programming `|` is [widely used for + disjunction](https://en.wikipedia.org/wiki/Vertical_bar#Disjunction): + - regex + - boolean "or" + - bitwise "or" + - alternatives in BNF + - parallel execution on the pi-calculus + - Many modern languages with support for sum types (or the more + general union types) use `|`, including + - [Python](https://docs.python.org/3/library/typing.html#typing.Union) + - [TypeScript](https://www.typescriptlang.org/docs/handbook/2/everyday-types.html#union-types) + - (of course) the MLs, Haskell, F#, Elm, OCaml, etc. + +2. Why not use `,` to separate alternatives? + + We have discussed modeling our concrete syntax for sum type + declarations on Rust. But without changes to other parts of our + language, this would leave the concrete syntax for type declarations + too similar to record type declarations. + + This similarity is aggravated by the fact that we currently don't + enforce any case-based syntactic rules to differentiate identifiers + used for operator names, variables, or module names, and we are + currently planning to extend this same flexibility to variant + labels, just as we do for record field labels. Thus, we could end up + with a pair of declarations like: + + ``` quint + type T = { + A : int, + b : str, + } + + type S = { + A(int), + b(str), + } + ``` + + We are not confident that the difference between `_:_` and `_(_)` + will be enough to keep readers from confusing the two. + + But the chance of mistaking a record and sum type declaration is + actually compounding a worse possible confusion: the part of a + sum-type record enclosed in brackets is syntactically + indistinguishable from a block of operators applications. + + Given we tend to read data structures from the outside in, we feel + confident that we were going to avoid confusion by requiring + declarations to use `|` to demarcate alternatives: + + ``` quint + type T = { + a : int, + B : str, + } + + type S = + | A(int) + | b(str) + ``` + + The latter seems much clearer to our team, and if we reflect this + syntax also in `match`, it will give another foothold to help + readers gather meaning when skimming the code. + +3. Could we just copy Rust exactly? + + In Rust, sum types are declared like this: + + ``` rust + enum T { + A(i64), + B(i64), + C + } + ``` + + If we just adopted this syntax directly without also changing our + syntax for records, we would introduce + + - Breaks with our current convention around type declarations and + use of keywords. + - May mislead users to try injecting values into the type via Rust's + `T::A(x)` syntax, which clashes with our current module syntax. + - This would move us closer to Rust but further from languages like + [TypeScript](https://www.typescriptlang.org/docs/handbook/2/everyday-types.html#type-aliases) + and Python. + + Rust't syntax makes pretty good sense within the context of the + rest of Rust's syntax. Here is an overview: + + **declaration** + + ``` rust + struct Pair { + fst: u64, + snd: String + } + + enum Either { + Left(u64), + Right(String) + } + ``` + + **construction** + + ``` rust + let s = Either::Left(4) + let p = Pair{ + fst: 4, + snd: "Two" + } + ``` + + **elimination** + + ``` rust + let two = p.snd + let four = s match { + Either::Left(_) => 4, + Either::Right(_) => 4 + } + ``` + + Note how the various syntactic elements work together to give + consistent yet clearly differentiated forms of expression for dual + constructs: + + - The prefix keyword for declarations consistently (`enum` vs. + `struct`). + - A unique constructor name is used consistently for forming a + record or variant. + - Lexical rules add clear syntactic markers: + - Data constructors must begin with a capital letter + - Field names of a struct (and method names of a trait, and module + names) must + + begin with a lowercase letter. + - This ensures the syntax for module access and sum type + construction are unambiguous: `mymod::foo(x)` vs + `MySumType::Foo(x)`. + - The caps/lower difference also helps reflect the duality between + record fields and sum type alternatives. + + Compare with the syntax proposed for quint this RFC: + + **declaration** + + ``` quint + type Pair = { + fst: int, + snd: str, + } + + type Either = + | left(int), + | right(str) + ``` + + **construction** + + ``` quint + let s = left(4) + let p = { + fst: 4, + snd: "Two" + } + ``` + + **elimination** + + ``` rust + let two = p.snd + let four = s match { + | left(_) => 4 + | right(_) => 4 + } + ``` + + Following the current quint syntax, we don't differentiate + declaration with keywords or data construction with prefix use of + type names. But we make up for this lost signal with the evident + difference between `|` and `,`. This also compensates for the + inability to differentiate based on capitalization. + + Finally, by adopting a syntax that if very similar to C++-like + languages (like Rust), we risk presenting false friends. There are + numerous subtle differences between quint and Rust, and if we lull + users into thinking the syntax is roughly the same, they are likely + to be disappointed when they discover that, e.g., + + - Unlike Rust, conditions in `if` must be wrapped in `(...)` + - Unlike Rust, conditions must have an `else` branch + - Unlike Rust, `let` is not used for binding + - Unlike Rust, type parameters are surrounded in `[...]` rather than + `<...>` + - Unlike Rust, operator are declared with `def` + - Unlike Rust, type variables must begin with a lower-case letter + + In short, given how many ways we differ from Rust syntax already, + adopting Rust's syntax for sum types would be confusing in the + context of our current suntax and possibly lead to incorrect + expectations. + +4. What if we want to be more Rust-like + + If we want to use a syntax for sum types that is closer to, or + exactly the same as, Rust's, then we should make at least the + following changes to the rest of our syntax to preserve harmony: + + - Require a prefix name relating to a type when constructing data, + e.g., `Foo + {a: 1, b: 2}` for constructing a record of type `Foo`. (Note + this would mean dropping support for anonymous records types.) + - Introduce a lexical distinction between capitalized identifiers, + used for data constructors (and type constants), and uncapitalized + identifiers, used for records field labels and operators. + - Use keywords consistently for type declarations. + + Taking these changes into account, we could render the previous + quint examples thus: + + **declaration** + + ``` quint + type Pair = struct { + fst: u64, + snd: String + } + + type Either = enum { + Left(u64), + Right(String) + } + ``` + + **construction** + + ``` quint + let s = Left(4) + let p = Pair { + fst: 4, + snd: "Two" + } + ``` + + **elimination** + + ``` rust + let two = p.snd + let four = s match { + Left(_) => 4, + Right(_) => 4, + } + ``` + +### Case analysis + +Ergonomic support for sum types requires eliminators, ideally in the +form if case analysis by pattern matching. + +The proposed syntax is close to [Rust's pattern +syntax](https://doc.rust-lang.org/book/ch18-03-pattern-syntax.html#matching-literals), +modulo swapping `|` for `,` to be consistent with the type declaration +syntax. + +Here's some example Rust for comparison + +``` rust +match x { + A => println!("a"), + B => println!("b"), + C(v) => println!("cv"), + _ => println!("anything"), +} +``` + +The `match` is a close analogue to our existing `if` expressions, and +the reuse of the `=>` hints at the connection between case elimination +and anonymous operators. The comma separated alternatives enclosed in +`{...}` follow the variadic boolean action operators, which seems +fitting, since sum types are disjunction over data. + +One question if we adopt some form of pattern-based case analysis is how +far we generalize the construct. Do we support pattern matching on +scalars like ints and symbols? Do we support pattern matching to +deconstruct compound data such as records and lists? What about sets? Do +we allow pattern expressions to serve as anonymous operator (like +Scala)? + +My guess is that in most cases the gains in expressivity of specs would +justify the investment, but it is probably best to start with limiting +support to defined sum types and seeing where we are after that. + +Until we have pattern matching introduced, we should flag a parsing +error if the deconstructor argument is not a free variable, and inform +the user that full pattern matching isn't yet supported. + +### Sketch of an alternative syntax + +The syntax being proposed is chosen because it is familiar to Rust +programmers, and is deemed sufficient so long as we don't need to expose +the underlying row polymorphism. However, it has the down-sides of being +very similar to the syntax for records, which might lead to confusion. +I've also considered a more distinctive alternative which is also more +consistently complementary to our records syntax. This group of +alternatives follows +[Leijen05](https://www.microsoft.com/en-us/research/publication/extensible-records-with-scoped-labels/): + +1. Declaration + + Reflecting the fact that both records and sum types are based on + rows, we use the same pairing (`:`) and enumerating (`,`) syntax, + but signal to move from a conjunctive to a disjunctive meaning of + the row by changing the brackets: + + ``` quint + type T = + < A : int + , B : str + > + ``` + +2. Injection + + Injection uses a syntax that is dual with projection on records: `.` + projects values out of products and injects them into co-products: + + ``` quint + val a : T = + ``` + + Since this option gives a syntactically unambiguous representation + of variant formation, there is no need to generate special injector + operator, and `<_._>` can be the normal form for injection. + + Annotation of anonymous sum types is clear and unambiguous: + + ``` quint + def f(n: int): = + if (n >= 0) else + ``` + + Compare with the corresponding annotation for a record type: + + ``` quint + def f(n: int): {C:int, D:str | s} = + if (n >= 0) {C:n, D:"positive"} else {C:n, B:"negative"} + ``` + +3. Elimination + + Finally, elimination uses a syntax that is dual to record + construction, signaling the similarity thru use of the surrounding + curly braces, and difference via the presence of the fat arrows + (this syntax is similar to the one proposed): + + ``` quint + match e { + A : a => ..., + B : b => ... + } + ``` + +## High-level implementation plan + +Add parsing and extension of the IR for the syntax + + +Add generation of constructor operators + + +Add rules for type checking + + +Add support in the simulator + + +Add support for converting to Apalache + + +## Additional consideration + +### Pattern matching + +We may want to consider support for pattern matching at some point. I +suspect the `match` construct will make users want this, but then again +perhaps quint is simple enough that we can do without this complication. +We'd also have to consider carefully how this would work with conversion +to the Apalache IR. + +### User defined parametric type constructors + +If we want to gain the most value from the addition of sum types we +should allow users to define parametric types such as `Option` and +`Either`. See . + +### Drop the exotic operators + +- Remove the special product type operators `fieldNames`, `Rec`, `with`, + `label`, and `index`, or add support for first-class labels As is, I + think these are not worth the complexity and overhead. + +Compare our rule with the projection operation from "Extensible Records +with Scoped Labels", which does not receive the label \`l' as a string, +instead treating it as a special piece of syntax: + +``` haskell +(_.l) :: ∀r α. (l|r) ⇒ {l :: α | r } → α` +``` + +Another point of comparison is Haskell's ["Datatypes with Field +Labels"](https://www.haskell.org/onlinereport/haskell2010/haskellch3.html#x8-490003.15), +which generates a projection function for each label, so that defining +the datatype + +``` haskell +data S = S1 { a :: Int, b :: String } +``` + +will produce functions + +``` haskell +a :: S -> Int +b :: S -> String +``` + +1. Benefits + + 1. Simplified typing rules + + Abandoning this subordination to normal operator application + would leave us with a rule like the following for record + projection: + + $$ + \frac + { \Gamma \vdash e \colon (r, c) \quad fresh(t) } + { \Gamma \vdash \ e.l \ \colon (t, c \land r \sim \{ \ l \colon t | tail\_t \ \}) } + $$ + + This would allow us to remove the checks for string literals, + instead leaving that to the outermost, syntactic level of our + static analysis. A similar simplification would follow for + record construction: the rule for `Rec` would not need to + validate that it had received an even number of argument of + alternating string literals and values, since this would be + statically guaranteed by the parsing rules for the + $\{ l_1 : v_1, \ldots, l_n : v_n \}$ syntax. This would be a + case of opting for the ["Parse, don't + validate"](https://lexi-lambda.github.io/blog/2019/11/05/parse-don-t-validate/) + strategy. + + 2. Safer language + + The added surface area introduced by these operators have + contributed to several bugs, including at least the ones + discussed here: + + - + - + - + + 3. More maintainable code base + + We are losing structure in our internal representation of + expressions, which means we get less value out of typescript + type system, have to do more work when converting to the + Apalache IR, and have to deal with more fussy details. By + converting the rich structures of records into an internal + representation `Rec(expr1, ..., expr1)` + + - we lose the ability to ensure we are forming records correctly + statically + - have to do manual arithmetic to ensure fields and values are + paired up correctly + - essentially reduce ourselves to a "stringly typed" + representation, relying entirely on detecting the `Rec` value + in the `kind` field. + +[^1]: The expressive power of these simple constructs comes from the + nice algebraic properties revealed when values of a type are treated + as equal up-to ismorphism. See, e.g., + diff --git a/doc/rfcs/rfc001-sum-types/rfc001-erc-sum-types-example.qnt b/docs/pages/docs/rfcs/rfc001-sum-types/rfc001-erc-sum-types-example.qnt similarity index 100% rename from doc/rfcs/rfc001-sum-types/rfc001-erc-sum-types-example.qnt rename to docs/pages/docs/rfcs/rfc001-sum-types/rfc001-erc-sum-types-example.qnt diff --git a/doc/rfcs/rfc001-sum-types/rfc001-sum-types.org b/docs/pages/docs/rfcs/rfc001-sum-types/rfc001-sum-types.org similarity index 99% rename from doc/rfcs/rfc001-sum-types/rfc001-sum-types.org rename to docs/pages/docs/rfcs/rfc001-sum-types/rfc001-sum-types.org index ce6131536..3326b5cfa 100644 --- a/doc/rfcs/rfc001-sum-types/rfc001-sum-types.org +++ b/docs/pages/docs/rfcs/rfc001-sum-types/rfc001-sum-types.org @@ -1,11 +1,12 @@ #+TITLE: RFC 001: Extend Quint with Row-Polymorphic Sum Types #+AUTHOR: Shon Feder #+DATE: <2023-08-03 Thu> +#+OPTIONS: toc:nil #+LATEX_COMPILER: xelatex * Overview This section gives a concise overview of the principle proposal. In -depth discussion follows in [[Discussion][Discuisson]]. +depth discussion follows in [[Discussion][Discussion]]. ** Concrete syntax The concrete syntax falls into three categories, corresponding three aspects of diff --git a/doc/rfcs/rfc001-sum-types/rfc001-sum-types.pdf b/docs/pages/docs/rfcs/rfc001-sum-types/rfc001-sum-types.pdf similarity index 100% rename from doc/rfcs/rfc001-sum-types/rfc001-sum-types.pdf rename to docs/pages/docs/rfcs/rfc001-sum-types/rfc001-sum-types.pdf diff --git a/doc/rfcs/rfc001-sum-types/rfc001-sum-types.tex b/docs/pages/docs/rfcs/rfc001-sum-types/rfc001-sum-types.tex similarity index 100% rename from doc/rfcs/rfc001-sum-types/rfc001-sum-types.tex rename to docs/pages/docs/rfcs/rfc001-sum-types/rfc001-sum-types.tex diff --git a/doc/rfc007-foreign-calls.md b/docs/pages/docs/rfcs/rfc007-foreign-calls.md similarity index 98% rename from doc/rfc007-foreign-calls.md rename to docs/pages/docs/rfcs/rfc007-foreign-calls.md index 2b4d7470d..b409416b1 100644 --- a/doc/rfc007-foreign-calls.md +++ b/docs/pages/docs/rfcs/rfc007-foreign-calls.md @@ -1,3 +1,9 @@ +--- +author: Igor Konnov +date: 2023-04-05 +title: "Design Space of Foreign Calls in Quint" +--- + # RFC007: Design space of foreign calls in Quint | Revision | Date | Author | @@ -212,9 +218,9 @@ space outlined by the four examples is quite large. [SHA-2]: https://en.wikipedia.org/wiki/SHA-2 [Cosmos Accounts]: https://docs.cosmos.network/v0.45/basics/accounts.html#signatures [ITF Format]: https://apalache.informal.systems/docs/adr/015adr-trace.html -[Quint manual]: https://github.com/informalsystems/quint/blob/main/doc/quint.md +[Quint manual]: /docs/quint [Issue 2453]: https://github.com/informalsystems/apalache/issues/2453 [Issue 143]: https://github.com/informalsystems/quint/issues/143 [eval]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/eval [NodeJS VM]: https://nodejs.org/api/vm.html -[JSON-RPC]: https://en.wikipedia.org/wiki/JSON-RPC#Implementations \ No newline at end of file +[JSON-RPC]: https://en.wikipedia.org/wiki/JSON-RPC#Implementations diff --git a/doc/story008-repl-traces.md b/docs/pages/docs/stories/story008-repl-traces.md similarity index 95% rename from doc/story008-repl-traces.md rename to docs/pages/docs/stories/story008-repl-traces.md index 840a5a2e0..8f1ebdd97 100644 --- a/doc/story008-repl-traces.md +++ b/docs/pages/docs/stories/story008-repl-traces.md @@ -1,4 +1,10 @@ -# Story008: Simulator Traces in REPL +--- +author: Igor Konnov +date: 2023-05-05 +title: "Simulator Traces in REPL" +--- + +# Story 008: Simulator Traces in REPL | Revision | Date | Author | |---------:|:----------:|:------------------------| @@ -36,7 +42,7 @@ around and which values they carry.* Assume that we have initialized the protocol state with `init`: -```bluespec +```quint >>> init true ``` @@ -44,7 +50,7 @@ true **Current interface**. Currently, we can inspect the state, by evaluating the variable names: -```bluespec +```quint >>> temperature 20 >>> heatingOn @@ -59,7 +65,7 @@ highlight this idea. A workaround is to introduce a definition like the following one: -```bluespec +```quint // a handy definition that captures the state in a record val kettleState = { heatingOn: heatingOn, @@ -72,7 +78,7 @@ val kettleState = { system state as a record that is accessible via a standard name. We decided to use the name `q::state`. Here is an example: -```bluespec +```quint >>> q::state { beeping: false, @@ -83,7 +89,7 @@ use the name `q::state`. Here is an example: Since `q::state` is simply a `val`, we would be do all kinds of programmatic things with it: -```bluespec +```quint >>> q::state.fieldNames() Set("beeping", "heatingOn", "temperature") @@ -102,7 +108,7 @@ progressing.* Assume that we have executed a few steps: -```bluespec +```quint >>> init true >>> pressButton @@ -127,7 +133,7 @@ name such as `q::trace`. For the sequence `init.then(pressButton).then(heat).then(depressButton)`, we expect `q::trace` to evaluate as follows: -```bluespec +```quint >>> q::trace [ // 0 @@ -147,7 +153,7 @@ helpful comments: It should print the state numbers and highlight the active state (with a `*`). However, the fact that `q::trace` is a value, lets us to access it programmatically: -```bluespec +```quint >>> q::trace.length() 4 >>> q::trace[3].temperature - q::trace[0].temperature @@ -164,7 +170,7 @@ so I can evaluate invariants against them and explore different paths.* As in Story 2, assume that we have executed a few steps: -```bluespec +```quint >>> init true >>> pressButton @@ -187,7 +193,7 @@ executing `init`, then `pressButton`, then `heat`. The following REPL session highlights the expected interface: -```bluespec +```quint >>> init.then(pressButton).then(heat).then(depressButton) true >>> q::jump(2) @@ -218,7 +224,7 @@ important, since some of the actions may be non-deterministic. ### 3.2. Continue trace from the active state -```bluespec +```quint >>> // ...continue the above session >>> heat true @@ -244,7 +250,7 @@ cleared, and the new state is added after the active state. The trace is accumulating the states, even when `init` is executed. Hence, at some point, we have to clear the trace: -```bluespec +```quint >>> // ...continue the above session >>> q::reset true @@ -259,4 +265,4 @@ true [#347]: https://github.com/informalsystems/quint/issues/347 [#303]: https://github.com/informalsystems/quint/issues/303 [#288]: https://github.com/informalsystems/quint/issues/288 -[kettle]: https://github.com/informalsystems/quint/blob/main/tutorials/repl/kettle.qnt \ No newline at end of file +[kettle]: https://github.com/informalsystems/quint/blob/main/docs/repl/kettle.qnt diff --git a/docs/pages/index.mdx b/docs/pages/index.mdx new file mode 100644 index 000000000..cbc5687ee --- /dev/null +++ b/docs/pages/index.mdx @@ -0,0 +1,8 @@ +--- +title: "Quint" +description: "An executable specification language" +--- + +import { Home } from "../components/home"; + + diff --git a/docs/postcss.config.js b/docs/postcss.config.js new file mode 100644 index 000000000..33ad091d2 --- /dev/null +++ b/docs/postcss.config.js @@ -0,0 +1,6 @@ +module.exports = { + plugins: { + tailwindcss: {}, + autoprefixer: {}, + }, +} diff --git a/docs/public/background-pattern-gradient-mini.png b/docs/public/background-pattern-gradient-mini.png new file mode 100644 index 000000000..0d75de94b Binary files /dev/null and b/docs/public/background-pattern-gradient-mini.png differ diff --git a/docs/public/codetour.gif b/docs/public/codetour.gif new file mode 100644 index 000000000..68c874a01 Binary files /dev/null and b/docs/public/codetour.gif differ diff --git a/docs/public/favicon.ico b/docs/public/favicon.ico new file mode 100644 index 000000000..6b4a66ec9 Binary files /dev/null and b/docs/public/favicon.ico differ diff --git a/docs/public/icon-dark.png b/docs/public/icon-dark.png new file mode 100644 index 000000000..1864749aa Binary files /dev/null and b/docs/public/icon-dark.png differ diff --git a/docs/public/icon-light.png b/docs/public/icon-light.png new file mode 100644 index 000000000..cbeb5bf67 Binary files /dev/null and b/docs/public/icon-light.png differ diff --git a/docs/public/informal-systems-white.png b/docs/public/informal-systems-white.png new file mode 100644 index 000000000..3db0c3c2f Binary files /dev/null and b/docs/public/informal-systems-white.png differ diff --git a/docs/public/informal-systems.png b/docs/public/informal-systems.png new file mode 100644 index 000000000..95726c7fe Binary files /dev/null and b/docs/public/informal-systems.png differ diff --git a/docs/public/kettle-drawing.png b/docs/public/kettle-drawing.png new file mode 100644 index 000000000..8aea2c97b Binary files /dev/null and b/docs/public/kettle-drawing.png differ diff --git a/docs/public/kettle-state1.svg b/docs/public/kettle-state1.svg new file mode 100644 index 000000000..451ab0651 --- /dev/null +++ b/docs/public/kettle-state1.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/docs/public/kettle-state2.svg b/docs/public/kettle-state2.svg new file mode 100644 index 000000000..fbc66d14b --- /dev/null +++ b/docs/public/kettle-state2.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/docs/public/kettle-state3.svg b/docs/public/kettle-state3.svg new file mode 100644 index 000000000..6683f4ce8 --- /dev/null +++ b/docs/public/kettle-state3.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/docs/public/og.png b/docs/public/og.png new file mode 100644 index 000000000..f23640397 Binary files /dev/null and b/docs/public/og.png differ diff --git a/doc/quint-cheatsheet.pdf b/docs/public/quint-cheatsheet.pdf similarity index 100% rename from doc/quint-cheatsheet.pdf rename to docs/public/quint-cheatsheet.pdf diff --git a/doc/img/quint-cli-20230310.mp4 b/docs/public/quint-cli-20230310.mp4 similarity index 100% rename from doc/img/quint-cli-20230310.mp4 rename to docs/public/quint-cli-20230310.mp4 diff --git a/docs/public/repl-machine.png b/docs/public/repl-machine.png new file mode 100644 index 000000000..88cf04195 Binary files /dev/null and b/docs/public/repl-machine.png differ diff --git a/doc/img/vscode-2023-03-10.mp4 b/docs/public/vscode-2023-03-10.mp4 similarity index 100% rename from doc/img/vscode-2023-03-10.mp4 rename to docs/public/vscode-2023-03-10.mp4 diff --git a/docs/public/vscode.png b/docs/public/vscode.png new file mode 100644 index 000000000..590ec2570 Binary files /dev/null and b/docs/public/vscode.png differ diff --git a/doc/quint-cheatsheet.key b/docs/quint-cheatsheet.key similarity index 100% rename from doc/quint-cheatsheet.key rename to docs/quint-cheatsheet.key diff --git a/doc/roadmap.md b/docs/roadmap.md similarity index 100% rename from doc/roadmap.md rename to docs/roadmap.md diff --git a/docs/style.css b/docs/style.css new file mode 100644 index 000000000..b8e172874 --- /dev/null +++ b/docs/style.css @@ -0,0 +1,68 @@ +@tailwind base; +@tailwind components; +@tailwind utilities; + +dl { + margin-top: 1rem; + margin-bottom: 1rem; +} +dt { + margin-top: 1rem; +} +dd { + margin-top: 0.5rem; + padding-left: 1rem; +} + +footer { + max-height: 10%; +} + +.nx-text-primary-600 { + color: hsl(var(--nextra-primary-hue), var(--nextra-primary-saturation), 57%); +} +:is(html[class~="dark"] .dark\:nx-text-primary-600) { + color: hsl(var(--nextra-primary-hue), var(--nextra-primary-saturation), 57%); +} + +.text-lg code { + font-size: 1rem; +} + +:root { + --shiki-color-text: #414141; + --shiki-color-background: transparent; + --shiki-token-constant: #1976d2; + --shiki-token-string: #9d6ce5; + --shiki-token-comment: #aaa; + --shiki-token-keyword: #9d6ce5; + --shiki-token-parameter: #ff9800; + --shiki-token-function: #6f42c1; + --shiki-token-string-expression: #1976d2; + --shiki-token-punctuation: #212121; + --shiki-token-link: #22863a; +} + +.dark { + --shiki-color-text: #d1d1d1; + --shiki-token-constant: #79b8ff; + --shiki-token-string: #ffab70; + --shiki-token-comment: #6b737c; + --shiki-token-keyword: #ffab70; + --shiki-token-parameter: #ff9800; + --shiki-token-function: #b392f0; + --shiki-token-string-expression: #b392f0; + --shiki-token-punctuation: #bbb; + --shiki-token-link: #ffab70; +} +/* .violation-sample-container { */ +/* max-height: 0; */ +/* overflow: hidden; */ +/* transition: max-height 0.5s ease-out, opacity 0.5s ease-out; */ +/* opacity: 0; */ +/* } */ + +/* .violation-sample-container.visible { */ +/* max-height: 1000px; /\* Arbitrary large value to ensure full height *\/ */ +/* opacity: 1; */ +/* } */ diff --git a/docs/tailwind.config.js b/docs/tailwind.config.js new file mode 100644 index 000000000..361db4b0b --- /dev/null +++ b/docs/tailwind.config.js @@ -0,0 +1,21 @@ +/** @type {import('tailwindcss').Config} */ +module.exports = { + content: [ + "./app/**/*.{js,ts,jsx,tsx,mdx}", + "./pages/**/*.{js,ts,jsx,tsx,mdx}", + "./components/**/*.{js,ts,jsx,tsx,mdx}", + ], + theme: { + colors: { + primary: { + 600: '#9d6ce5', + } + }, + extend: { + colors: { + 'quint-purple': '#9d6ce5', + }, + } + }, + plugins: [], +}; diff --git a/docs/theme.config.jsx b/docs/theme.config.jsx new file mode 100644 index 000000000..89300eccb --- /dev/null +++ b/docs/theme.config.jsx @@ -0,0 +1,88 @@ +import { useTheme, useConfig } from 'nextra-theme-docs' +import { useRouter } from 'next/router'; + +export default { + logo: () => { + const { resolvedTheme } = useTheme() + if (resolvedTheme == "dark") { + return ( + Quint + ); + } else { + return ( + Quint + ); + } + }, + docsRepositoryBase: 'https://github.com/informalsystems/quint', + project: { + link: 'https://github.com/informalsystems/quint' + }, + chat: { + link: 'https://t.me/quint_lang', + icon: ( + + + + + ) + }, + head: function useHead() { + const { title } = useConfig() + return ( + <> + + + + + + + + + + + + + + + + + ) + }, + footer: { + text: ( +
+

+ © {new Date().getFullYear()} Informal Systems. +

+
+ ) + }, + primaryHue: { dark: 264, light: 264 }, + primarySaturation: { dark: 52, light: 90 }, + sidebar: { + defaultMenuCollapseLevel: 1 + }, + useNextSeoProps() { + const { asPath } = useRouter(); + return { + titleTemplate: asPath !== '/' ? '%s – Quint' : 'Quint', + }; + } +} diff --git a/docs/tsconfig.json b/docs/tsconfig.json new file mode 100644 index 000000000..d55eb3081 --- /dev/null +++ b/docs/tsconfig.json @@ -0,0 +1,28 @@ +{ + "compilerOptions": { + "lib": [ + "dom", + "dom.iterable", + "esnext" + ], + "allowJs": true, + "skipLibCheck": true, + "strict": false, + "noEmit": true, + "incremental": true, + "module": "esnext", + "esModuleInterop": true, + "moduleResolution": "node", + "resolveJsonModule": true, + "isolatedModules": true, + "jsx": "preserve" + }, + "include": [ + "next-env.d.ts", + "**/*.ts", + "**/*.tsx" + ], + "exclude": [ + "node_modules" + ] +} diff --git a/examples/.scripts/run-example.sh b/examples/.scripts/run-example.sh index e64273133..e8f7753e8 100755 --- a/examples/.scripts/run-example.sh +++ b/examples/.scripts/run-example.sh @@ -34,6 +34,9 @@ result () { "$file" =~ ^cryptography/ || "$file" =~ ^spells/ || "$file" == "solidity/SimpleAuction/SimpleAuction.qnt" || + "$file" == "tutorials/integers.qnt" || + "$file" == "tutorials/sets.qnt" || + "$file" == "tutorials/booleans.qnt" || "$file" == "cosmos/ics20/base.qnt" || "$file" == "cosmos/bank/bank.qnt" ) ]] ; then printf "N/A[^nostatemachine]"; return diff --git a/examples/README.md b/examples/README.md index 0abecf829..5b525cbc8 100644 --- a/examples/README.md +++ b/examples/README.md @@ -92,7 +92,6 @@ listed without any additional command line arguments. | [language-features/tuples.qnt](./language-features/tuples.qnt) | :white_check_mark: | :white_check_mark: | :white_check_mark: | :white_check_mark: | | [puzzles/prisoners/prisoners.qnt](./puzzles/prisoners/prisoners.qnt) | :white_check_mark: | :white_check_mark: | :white_check_mark: | :white_check_mark: | | [puzzles/river/river.qnt](./puzzles/river/river.qnt) | :white_check_mark: | :white_check_mark: | :white_check_mark: | :white_check_mark: | -| [solidity/Coin/coin.qnt](./solidity/Coin/coin.qnt) | :white_check_mark: | :white_check_mark: | :white_check_mark: | :white_check_mark: | | [solidity/ERC20/erc20.qnt](./solidity/ERC20/erc20.qnt) | :white_check_mark: | :white_check_mark: | :white_check_mark: | :white_check_mark: | | [solidity/GradualPonzi/gradualPonzi.qnt](./solidity/GradualPonzi/gradualPonzi.qnt) | :white_check_mark: | :white_check_mark: | :white_check_mark: | :white_check_mark: | | [solidity/icse23-fig7/lottery.qnt](./solidity/icse23-fig7/lottery.qnt) | :white_check_mark: | :white_check_mark: | :white_check_mark: | :x:https://github.com/informalsystems/quint/issues/1285 | @@ -102,6 +101,12 @@ listed without any additional command line arguments. | [spells/BoundedUInt.qnt](./spells/BoundedUInt.qnt) | :white_check_mark: | :white_check_mark: | :white_check_mark: | N/A[^nostatemachine] | | [spells/commonSpells.qnt](./spells/commonSpells.qnt) | :white_check_mark: | :white_check_mark: | :white_check_mark: | N/A[^nostatemachine] | | [spells/rareSpells.qnt](./spells/rareSpells.qnt) | :white_check_mark: | :white_check_mark: | :white_check_mark: | N/A[^nostatemachine] | +| [tutorials/booleans.qnt](./tutorials/booleans.qnt) | :white_check_mark: | :white_check_mark: | :white_check_mark: | N/A[^nostatemachine] | +| [tutorials/coin.qnt](./tutorials/coin.qnt) | :white_check_mark: | :white_check_mark: | :white_check_mark: | :white_check_mark: | +| [tutorials/hello.qnt](./tutorials/hello.qnt) | :white_check_mark: | :white_check_mark: | :white_check_mark: | :white_check_mark: | +| [tutorials/integers.qnt](./tutorials/integers.qnt) | :white_check_mark: | :white_check_mark: | :white_check_mark: | N/A[^nostatemachine] | +| [tutorials/repl/kettle.qnt](./tutorials/repl/kettle.qnt) | :white_check_mark: | :white_check_mark: | :white_check_mark: | :white_check_mark: | +| [tutorials/sets.qnt](./tutorials/sets.qnt) | :white_check_mark: | :white_check_mark: | :white_check_mark: | N/A[^nostatemachine] | | [verification/defaultOpNames.qnt](./verification/defaultOpNames.qnt) | :white_check_mark: | :white_check_mark: | :white_check_mark: | :white_check_mark: | [^nostatemachine]: This specification does not define a state machine. diff --git a/examples/solidity/Coin/README.md b/examples/solidity/Coin/README.md deleted file mode 100644 index cd1502d6e..000000000 --- a/examples/solidity/Coin/README.md +++ /dev/null @@ -1,4 +0,0 @@ -This example is covered in detail in the tutorial 3: - - - [Quint spec](https://github.com/informalsystems/quint/blob/main/tutorials/lesson3-anatomy/coin.qnt) - - [Tutorial 3](https://github.com/informalsystems/quint/blob/main/tutorials/lesson3-anatomy/coin.md) diff --git a/examples/solidity/Coin/coin.qnt b/examples/solidity/Coin/coin.qnt deleted file mode 120000 index 09aa39f62..000000000 --- a/examples/solidity/Coin/coin.qnt +++ /dev/null @@ -1 +0,0 @@ -../../../tutorials/lesson3-anatomy/coin.qnt \ No newline at end of file diff --git a/examples/tutorials/README.md b/examples/tutorials/README.md new file mode 100644 index 000000000..805f7e3ec --- /dev/null +++ b/examples/tutorials/README.md @@ -0,0 +1,3 @@ +# Tutorial examples + +Examples from the [Lessons](https://quint-lang.org/docs/lessons) diff --git a/examples/tutorials/booleans.qnt b/examples/tutorials/booleans.qnt new file mode 100644 index 000000000..21171c857 --- /dev/null +++ b/examples/tutorials/booleans.qnt @@ -0,0 +1,65 @@ +/* + If you do not know how to start this lesson, see: + https://github.com/informalsystems/quint/tree/main/tutorials + */ +module booleansLesson { + + // false is a built-in constant + pure val myFalse = false + + // true is a built-in constant too + pure val myTrue = true + + // Boolean negation, which is written as `!x` in some languages + pure def myNot(x) = not(x) + + // you can compare Booleans for equality + pure def myEq(x, y) = x == y + + // you can compare Booleans for inequality + pure def myNeq(x, y) = x != y + + // you can also write negation like that + pure def myNot2(x) = x.not() + + // Boolean "and", which is written as `x && y` in some languages + pure def myAnd(x, y) = x and y + + // You can also write Boolean "and" like that in the OOP form + pure def myAnd2(x, y) = x.and(y) + + // We can apply "and" to more than two arguments + pure def myAnd3(x, y, z) = and(x, y, z) + + /// When your expressions get bigger, you can stack them in `and { ... }` + pure def myAnd4(x, y, z) = and { + x, + y, + z + } + + // Boolean "or", which is written as `x || y` in some languages. + pure def myOr(x, y) = x or y + + /// You can also write Boolean "or" like that in the OOP form + pure def myOr2(x, y) = x.or(y) + + /// We can apply "or" to more than two arguments + pure def myOr3(x, y, z) = or(x, y, z) + + /// When your expressions get bigger, you can stack them in `or { ... }` + pure def myOr4(x, y, z) = or { + x, + y, + z + } + + /// Boolean implication. + /// This operator is equivalent to `not(x).or(y)` as well as to `if (x) y else true`. + pure def myImplies(x, y) = x implies y + + /// Boolean equivalence. + /// It is equivalent to x == y, but this operator requires the arguments + /// to have the Boolean type. + pure def myIff(x, y) = x iff y +} diff --git a/examples/tutorials/coin.qnt b/examples/tutorials/coin.qnt new file mode 100644 index 000000000..36aef5448 --- /dev/null +++ b/examples/tutorials/coin.qnt @@ -0,0 +1,203 @@ +/** + * Coin contract from the Solidity examples [1]. It is a nice starting + * example for Solidity. It is a nice starting example for Quint too. + * + * This contract introduces a custom coin. Although Solidity contracts + * may easily call other contracts, our protocol specification is + * not easily composable with other protocol specifications. It is possible to + * make it composable, but it would introduce additional code (following simple + * idioms), which we want to avoid in this introductory example. + * + * As we focus on the protocol idea (in Quint), not the implementation (in Solidity), + * we omit events and error codes. + * + * [1]: https://docs.soliditylang.org/en/v0.8.17/introduction-to-smart-contracts.html#subcurrency-example + * + * Igor Konnov, Informal Systems, 2023 + */ +module coin { + + // TYPE DEFINITIONS + + // encode addresses as string literals + type Addr = str + + // We declare a Solidity uint (256 bit) as an alias of Quint int. + // Note that these UInt values are still integers, which can get + // arbitrarily large. Hence, we have to take care of overflows. + // In the future, Quint will have a standard library for that. + type UInt = int + + // FUNCTIONAL LAYER: + // Values and functions that are state-independent + + // the maximal value a Solidity UInt can carry + pure val MAX_UINT = 2^256 - 1 + + // does a big integer represent a Solidity UInt? + pure def isUInt(i: int): bool = (0 <= i and i <= MAX_UINT) + + // STATE MACHINE: + // State-dependent definitions and actions + + // Currently, we fix the set of all addresses to a small set. + // In the future, we will be using the commented-out constant declaration. + // For now, we are using simple aliases instead of actual Ethereum addresses. + //const ADDR: Set[Addr] + pure val ADDR = Set("null", "alice", "bob", "charlie", "eve") + + // the minter's address + var minter: Addr + // the balances in the subcurrency tokens + var balances: Addr -> UInt + // a handy definition to query the whole state in REPL at once + val state = { minter: minter, balances: balances } + + // a helper function to make preconditions similar to Solidity + def require(cond: bool): bool = cond + + // compute the total supply of coins over all balances + val totalSupply = ADDR.fold(0, (sum, a) => sum + balances.get(a)) + + // state initialization + action init: bool = { + // since the contract can be initialized by an arbitrary message sender, + // we choose the sender from the set of all addresses non-deterministically. + nondet sender = oneOf(ADDR) + all { + minter' = sender, + balances' = ADDR.mapBy(a => 0) + } + } + + // Sends an amount of newly created coins to an address. + // Can only be called by the minter, that is, the contract creator. + // Note that we have to add `sender` as an action parameter, + // which is accessed implicitly via `msg.sender` in Solidity. + action mint(sender: Addr, receiver: Addr, amount: UInt): bool = all { + require(sender == minter), + val newBal = balances.get(receiver) + amount + all { + // Solidity does the overflow check under the hood. + // We have to add it ourselves. + require(isUInt(newBal)), + // update the balances and keep the minter address + balances' = balances.set(receiver, newBal), + minter' = minter, + } + } + + // Sends an amount of existing coins from any caller (sender) + // to a receiver's address. + // Note that we have to add `sender` as an action parameter, + // which is accessed via implicit `msg.sender` in Solidity. + action send(sender: Addr, receiver: Addr, amount: UInt): bool = all { + require(not(amount > balances.get(sender))), + if (sender == receiver) { + balances' = balances + } else { + val newSenderBal = balances.get(sender) - amount + val newReceiverBal = balances.get(receiver) + amount + all { + // Again, Solidity does an automatic overflow test. + // We do it ourselves. + require(isUInt(newSenderBal)), + require(isUInt(newReceiverBal)), + balances' = + balances + .set(sender, newSenderBal) + .set(receiver, newReceiverBal) + } + }, + // keep the minter unchanged + minter' = minter, + } + + // All possible behaviors of the protocol in one action. + action step: bool = { + nondet sender = oneOf(ADDR) + nondet receiver = oneOf(ADDR) + nondet amount = 0.to(MAX_UINT).oneOf() + // execute one of the available actions/methods + any { + mint(sender, receiver, amount), + send(sender, receiver, amount), + } + } + + // INVARIANTS AND TEMPORAL PROPERTIES + + // One of the simplest properties is that all balances are within the uint range. + // While it is automatically guaranteed by Solidity, our specification is using + // big integers. Hence, it makes sense to check the range. + val balancesRangeInv: bool = + ADDR.forall(a => isUInt(balances.get(a))) + + // It is desirable that the total supply of tokens fits into UInt. + // Otherwise, a blockchain implementation or the user interface + // may run into an unexpected overflow when adding up the balances. + val totalSupplyDoesNotOverflowInv: bool = { + isUInt(totalSupply) + } + + // The temporal property that says the following: + // Assume that we want to check the temporal property `NoSupplyOverflow` + // for every initial state. Then we have to check for every initial state + // that it never produces a computation that ends in a state violating + // `totalSupplyDoesNotOverflowInv`. In general, temporal properties may be + // checked against any kinds of states, not only initial ones. + temporal NoSupplyOverflow: bool = always(totalSupplyDoesNotOverflowInv) + + // TESTS + + // send should not work without minting + run sendWithoutMintTest = { + init.then(send(minter, "bob", 5)) + .fail() + } + + // `mint`, then `send` + run mintSendTest = { + init.then(mint(minter, "bob", 10)) + .then(send("bob", "eve", 4)) + .then(all { + assert(balances.get("bob") == 6), + assert(balances.get("eve") == 4), + minter' = minter, + balances' = balances, + }) + } + + // Mint some coins for Eve and Bob. + // Test that Eve can always send coins to Bob, + // if she has enough on her balance. + // This test may fail sometimes. Do you see why? + // If not, execute it multiple times in REPL, until it fails. + run mintTwiceThenSendError = { + // non-deterministically pick some amounts to mint and send + nondet mintEve = 0.to(MAX_UINT).oneOf() + nondet mintBob = 0.to(MAX_UINT).oneOf() + nondet eveToBob = 0.to(MAX_UINT).oneOf() + // execute a fixed sequence `init`, `mint`, `mint`, `send` + init.then(mint(minter, "eve", mintEve)) + .then(mint(minter, "bob", mintBob)) + .then( + if (eveToBob <= balances.get("eve")) { + // if Eve has enough tokens, send to Bob should pass + send("eve", "bob", eveToBob) + } else { + // otherwise, just ignore the test + all { minter' = minter, balances' = balances } + } + ) + } + + // to run the random simulator for 10000 executions, each up to 10 actions, + // execute the following in the command line: + // + // $ quint run --invariant totalSupplyDoesNotOverflowInv coin.qnt + + + +} + diff --git a/examples/tutorials/hello.qnt b/examples/tutorials/hello.qnt new file mode 100644 index 000000000..d4f7e93bd --- /dev/null +++ b/examples/tutorials/hello.qnt @@ -0,0 +1,46 @@ +/* + If you do not know how to start this lesson, see: + https://github.com/informalsystems/quint/tree/main/tutorials + */ +module hello { + + // the state variable to keep the output by the computer + var consoleOutput: str + + // the state variable to keep the out read by the user + var readByUser: str + + // initialize the state machine that captures the protocol + action init = all { + consoleOutput' = "", + readByUser' = "" + } + + // write "Hello, world!" in consoleOutput of the state machine, + // if the console output is clean + action write = all { + consoleOutput == "", + consoleOutput' = "Hello, world!", + readByUser' = readByUser, + } + + // read the message from `consoleOutput` into `readByUser`, + // if the console output is not clean + action read = all { + consoleOutput != "", + readByUser' = consoleOutput, + consoleOutput' = consoleOutput, + } + + // execute a single step of the state machine: + // it may be `read` or `write`, whatever is available + action step = any { + write, + read + } + + // a simple test that demonstrates an interaction between + // the computer and the user + run writeReadTest = init.then(write).then(read) +} + diff --git a/examples/tutorials/integers.qnt b/examples/tutorials/integers.qnt new file mode 100644 index 000000000..869d273ca --- /dev/null +++ b/examples/tutorials/integers.qnt @@ -0,0 +1,58 @@ +/* + If you do not know how to start this lesson, see: + https://github.com/informalsystems/quint/tree/main/tutorials + */ +module integersLesson { + + // 0 is an integer literal + pure val int0 = 0 + + // 2 is an integer literal + pure val int1 = 2 + + // -3 is an integer literal + pure val negative1 = -3 + + // i^j is the integer exponentiation, that is, + // `i` multiplied by itself `j - 1` times + pure def myPow(i, j) = i^j + + // i + j is the integer addition + pure def myAdd(i, j) = i + j + + // i - j is the integer subtraction + pure def mySub(i, j) = i - j + + // i * j is the integer multiplication + pure def myMul(i, j) = i * j + + // i / j is the integer division + pure def myDiv(i, j) = i / j + + // i % j is the integer remainder + pure def myMod(i, j) = i % j + + + // `i > j` is true if and only if `i` is greater than `j` + pure def myGreaterThan(i, j) = i > j + + // `i >= j` is true if and only if `i` is greater than `j`, or equal to `j` + pure def myGreaterThanOrEqual(i, j) = i >= j + + // `i < j` is true if and only if `i` is less than `j` + pure def myLessThan(i, j) = i < j + + // `i <= j` is true if and only if `i` is less than `j`, or equal to `j` + pure def myLessThanOrEqual(i, j) = i <= j + + // `i == j` is true if and only if `i` equals to `j` + pure def myEquals(i, j) = i == j + + // `i != j` is true if and only if `i` is not equal to `j` + pure def myNotEqual(i, j) = i != j + + + // -i is the integer negation + pure def myUnaryMinus(i) = -i +} + diff --git a/examples/tutorials/repl/.envrc b/examples/tutorials/repl/.envrc new file mode 100644 index 000000000..0a722d7d2 --- /dev/null +++ b/examples/tutorials/repl/.envrc @@ -0,0 +1 @@ +use nix; diff --git a/examples/tutorials/repl/Makefile b/examples/tutorials/repl/Makefile new file mode 100644 index 000000000..f5c8cb921 --- /dev/null +++ b/examples/tutorials/repl/Makefile @@ -0,0 +1,15 @@ +## +# Quint REPL tutorials +# +# @file +# @version 0.1 + +./repl/kettle.qnt: ../../../docs/pages/docs/repl.md + lmt $^ + cat replTest.txt |\ + egrep '^(>>>|\.\.\.) ' | sed 's/^>>> //g' | sed 's/^\.\.\. //g' \ + >replTestIn.txt + cat replTest.txt |\ + egrep -v '^(>>>|\.\.\.) ' >replTestOut.txt + +# end diff --git a/examples/tutorials/repl/default.nix b/examples/tutorials/repl/default.nix new file mode 100644 index 000000000..640dc91b5 --- /dev/null +++ b/examples/tutorials/repl/default.nix @@ -0,0 +1,9 @@ +with (import { }); +pkgs.mkShell { + name = "quint-documentation-env"; + buildInputs = [ pkgs.go ]; + shellHook = '' + go install github.com/driusan/lmt@latest + export PATH=$PATH:$(go env GOPATH)/bin + ''; +} diff --git a/tutorials/repl/kettle.qnt b/examples/tutorials/repl/kettle.qnt similarity index 61% rename from tutorials/repl/kettle.qnt rename to examples/tutorials/repl/kettle.qnt index a29c537e4..030719cb0 100644 --- a/tutorials/repl/kettle.qnt +++ b/examples/tutorials/repl/kettle.qnt @@ -1,3 +1,4 @@ +// @mainModule kettle // -*- mode: Bluespec; -*- // The example from the REPL tutorial module kettle { @@ -92,3 +93,107 @@ module kettle { } } +/*! 1 + 3 !*/ + +/*! Set(1, 2, 3).map(i => i * 2) !*/ + +/*! fahrenheit(freezingTemperature) !*/ + +/*! fahrenheit(boilingTemperature) !*/ + +/*! 0.to(100).exists(celsius => fahrenheit(celsius) == celsius) !*/ + +/*! (-100).to(100).exists(celsius => fahrenheit(celsius) == celsius) !*/ + +/*! veryCold !*/ + +/*! veryHot !*/ + +/*! temperature !*/ + +/*! init !*/ + +/*! temperature !*/ + +/*! heatingOn !*/ + +/*! kettleState !*/ + +/*! pressButton !*/ + +/*! kettleState !*/ + +/*! pressButton !*/ + +/*! failover !*/ + +/*! temperature !*/ + +/*! all { temperature' = 100, heatingOn' = true, beeping' = false } !*/ + +/*! failover !*/ + +/*! heatingOn !*/ + +/*! temperature !*/ + +/*! beeping !*/ + +/*! init !*/ + +/*! pressButton !*/ + +/*! kettleState !*/ + +/*! heat !*/ + +/*! temperature !*/ + +/*! heat !*/ + +/*! temperature !*/ + +/*! heat !*/ + +/*! temperature !*/ + +/*! init !*/ + +/*! pressButton !*/ + +/*! heat !*/ + +/*! depressButton !*/ + +/*! kettleState !*/ + +/*! all { heatingOn' = true, temperature' = 100, beeping' = false } !*/ + +/*! depressButton !*/ + +/*! kettleState !*/ + +/*! all { heatingOn' = true, temperature' = 100, beeping' = false } !*/ + +/*! failover !*/ + +/*! kettleState !*/ + +/*! all { heatingOn' = true, temperature' = 100, beeping' = false } !*/ + +/*! any { +depressButton, +failover, +} !*/ + +/*! init !*/ + +/*! step !*/ + +/*! step !*/ + +/*! step !*/ + +/*! step !*/ + +/*! initNondet !*/ diff --git a/examples/tutorials/repl/replTest.txt b/examples/tutorials/repl/replTest.txt new file mode 100644 index 000000000..0df38629b --- /dev/null +++ b/examples/tutorials/repl/replTest.txt @@ -0,0 +1,115 @@ +true + +>>> 1 + 3 +4 +>>> Set(1, 2, 3).map(i => i * 2) +Set(2, 4, 6) +>>> fahrenheit(freezingTemperature) +32 +>>> fahrenheit(boilingTemperature) +212 +>>> 0.to(100).exists(celsius => fahrenheit(celsius) == celsius) +false +>>> (-100).to(100).exists(celsius => fahrenheit(celsius) == celsius) +true +>>> veryCold +-40 +>>> veryHot +104 +>>> temperature +runtime error: error: [QNT502] Variable temperature is not set + var temperature: int + ^^^^^^^^^^^^^^^^^^^^ + + +>>> init +true +>>> temperature +20 +>>> heatingOn +false +>>> kettleState +{ beeping: false, heatingOn: false, temperature: 20 } +>>> pressButton +true +>>> kettleState +{ beeping: false, heatingOn: true, temperature: 20 } +>>> pressButton +false +>>> failover +false +>>> temperature +20 +>>> all { temperature' = 100, heatingOn' = true, beeping' = false } +true +>>> failover +true +>>> heatingOn +false +>>> temperature +100 +>>> beeping +true +>>> init +true +>>> pressButton +true +>>> kettleState +{ beeping: false, heatingOn: true, temperature: 20 } +>>> heat +true +>>> temperature +21 +>>> heat +true +>>> temperature +22 +>>> heat +true +>>> temperature +23 +>>> init +true +>>> pressButton +true +>>> heat +true +>>> depressButton +true +>>> kettleState +{ beeping: false, heatingOn: false, temperature: 21 } +>>> all { heatingOn' = true, temperature' = 100, beeping' = false } +true +>>> depressButton +true +>>> kettleState +{ beeping: false, heatingOn: false, temperature: 100 } +>>> all { heatingOn' = true, temperature' = 100, beeping' = false } +true +>>> failover +true +>>> kettleState +{ beeping: true, heatingOn: false, temperature: 100 } +>>> all { heatingOn' = true, temperature' = 100, beeping' = false } +true +>>> any { +... depressButton, +... failover, +... } +... +true +>>> init +true +>>> step +true +>>> step +true +>>> step +true +>>> step +true +>>> initNondet +true +>>> .save kettle.qnt +Session saved to: kettle.qnt + diff --git a/tutorials/repl/replTestIn.txt b/examples/tutorials/repl/replTestIn.txt similarity index 100% rename from tutorials/repl/replTestIn.txt rename to examples/tutorials/repl/replTestIn.txt diff --git a/tutorials/repl/replTestOut.txt b/examples/tutorials/repl/replTestOut.txt similarity index 99% rename from tutorials/repl/replTestOut.txt rename to examples/tutorials/repl/replTestOut.txt index 03709b620..f70e297b7 100644 --- a/tutorials/repl/replTestOut.txt +++ b/examples/tutorials/repl/replTestOut.txt @@ -1,3 +1,5 @@ +true + 4 Set(2, 4, 6) 32 diff --git a/examples/tutorials/sets.qnt b/examples/tutorials/sets.qnt new file mode 100644 index 000000000..13f776b95 --- /dev/null +++ b/examples/tutorials/sets.qnt @@ -0,0 +1,123 @@ +/** + * A tutorial on sets. + * + * Igor Konnov, Informal Systems, 2023 + */ +module sets { + // We represent swap pairs as tuples of two strings + type Pair = (str, str) + + // the first element of a pair + pure def fst(pair: Pair): str = pair._1 + + // the second element of a pair + pure def snd(pair: Pair): str = pair._2 + + // all available swap pairs as a set of tuples + pure val availablePairs = Set( + ("BTC", "USDC"), ("BTC", "USDT"), ("ETH", "USDC"), ("ETH", "USDT"), + ("EVMOS", "USDC"), ("EVMOS", "WETH"), ("ETH", "WETH"), ("ATOM", "EVMOS"), + ("ATOM", "JUNO"), ("ATOM", "OSMO"), ("EVMOS", "JUNO"), ("ATOM", "JUNO"), + ("EVMOS", "OSMO") + ) + + pure val hasAtomJuno = ("ATOM", "JUNO").in(availablePairs) + + pure val hasEvmosEth = availablePairs.contains(("EVMOS", "ETH")) + + pure val hasAtom = availablePairs.exists(p => p._1 == "ATOM") + + // is SCRT missing from the pairs? + pure val missingScrt = + availablePairs.forall(p => p._1 != "SCRT" and p._2 != "SCRT") + + // swap pairs as two-element sets (unordered pairs) + pure val availableUnorderedPairs = + availablePairs.map(p => Set(p._1, p._2)) + + pure val hasAtomRight = availableUnorderedPairs.exists(p => "ATOM".in(p)) + + // all unordered pairs that contain ATOM at one end + pure val atomPairs = availableUnorderedPairs.filter(p => p.contains("ATOM")) + + // the number of swap pairs + pure val howMany = availableUnorderedPairs.size() + + // get all coins that appear in pairs + pure def coinsInPairs(pairs: Set[Set[str]]): Set[str] = pairs.flatten() + + // all coins in our swap pairs + pure val coins = coinsInPairs(availableUnorderedPairs) + + // an example of `someCoins` in `buyableVia1Swap` + pure def someCoinsExample = Set("ATOM", "ETH", "USDT") + + // which coins can be bought by swapping a coin from `someCoins` once? + pure def buyableVia1Swap(someCoins) = + availableUnorderedPairs + .filter(p => p.intersect(someCoins).size() == 1) + .flatten() + .exclude(someCoins) + + // which coins can be bought by swapping a coin from `someCoins` exactly twice? + pure def buyableVia2Swaps(someCoins) = + someCoins + .buyableVia1Swap() + .buyableVia1Swap() + + // which coins can be bought by swapping a coin from `someCoins` twice? + pure def buyableVia1or2Swaps(someCoins) = + buyableVia1Swap(someCoins) + .union(buyableVia2Swaps(someCoins)) + + // which coins can we buy via n swaps when starting from `someCoins` + pure def buyableNSwaps(someCoins, n) = + 1.to(n).fold(someCoins, + (prevCoins, i) => buyableVia1Swap(prevCoins)) + + // inSet.exists(myFun) can be expressed via `fold`, when `inSet` is finite + pure def myExists(inSet: Set[a], pred: a => bool): bool = + inSet.fold(false, (result, elem) => result or pred(elem)) + + // inSet.forall(myFun) can be expressed via `fold`, when `inSet` is finite + pure def myForall(inSet: Set[a], pred: a => bool): bool = + inSet.fold(true, (result, elem) => result and pred(elem)) + + // inSet.filter(myFun) can be expressed via `fold`, when `inSet` is finite + pure def myFilter(inSet: Set[a], pred: a => bool): Set[a] = + inSet.fold(Set(), + (result, elem) => + if (pred(elem)) result.union(Set(elem)) else result) + + // inSet.map(myFun) can be expressed via `fold`, when `inSet` is finite + pure def myMap(inSet: Set[a], mapper: a => b): Set[b] = + inSet.fold(Set(), + (result, elem) => result.union(Set(mapper(elem)))) + + // flatten(inSet) can be expressed via `fold`, when `inSet` is finite + pure def myFlatten(inSet: Set[Set[a]]): Set[a] = + inSet.fold(Set(), (result, elem) => result.union(elem) ) + + // all combinations of unordered pairs that have exactly 4 pairs + pure val quads = + availableUnorderedPairs + .powerset() + .filter(pairs => pairs.size() == 4) + + // Do pairs form a cycle? If they do, then for every participating coin, + // there are exactly two pairs that have this coin in common. + pure def isCycle(pairs: Set[Set[str]]): bool = { + pure def appearsTwice(coin) = { + pairs.filter(ps => coin.in(ps)).size() == 2 + } + // for every coin that appears in `pairs`, + // there are exactly two pairs that cointain it + coinsInPairs(pairs).forall(appearsTwice) + } + + // all pair cycles of length 4 + pure val cycles4 = quads.filter(isCycle) + + +} + diff --git a/quint/cli-tests.md b/quint/cli-tests.md index 1687b37e5..c37746f0e 100644 --- a/quint/cli-tests.md +++ b/quint/cli-tests.md @@ -182,7 +182,7 @@ This example was pointing to Paxos. Now it does not typecheck. ### OK on typecheck coin - quint typecheck ../examples/solidity/Coin/coin.qnt + quint typecheck ../examples/tutorials/coin.qnt ### OK on test SimpleAuction.qnt diff --git a/quint/io-cli-tests.md b/quint/io-cli-tests.md index f6c0d5d7e..7697d5228 100644 --- a/quint/io-cli-tests.md +++ b/quint/io-cli-tests.md @@ -520,7 +520,7 @@ This is a regression test for #648. ``` cat <&1 \ + | quint -r ../examples/tutorials/coin.qnt::coin 2>&1 \ | tail -n +3 init balances @@ -541,7 +541,7 @@ The command `run` finds an overflow in Coin. ``` output=$(quint run --max-steps=5 --seed=0x1e352e160ffa12 --invariant=totalSupplyDoesNotOverflowInv \ - ../examples/solidity/Coin/coin.qnt 2>&1) + ../examples/tutorials/coin.qnt 2>&1) exit_code=$? echo "$output" | sed -e 's/([0-9]*ms)/(duration)/g' -e 's#^.*coin.qnt# HOME/coin.qnt#g' exit $exit_code @@ -603,7 +603,7 @@ The command `run` finds an overflow in Coin and shows the operator calls. output=$(quint run --max-steps=5 --seed=0x1786e678d45fe0 \ --invariant=totalSupplyDoesNotOverflowInv \ --verbosity=3 \ - ../examples/solidity/Coin/coin.qnt 2>&1) + ../examples/tutorials/coin.qnt 2>&1) exit_code=$? echo "$output" | sed -e 's/([0-9]*ms)/(duration)/g' -e 's#^.*coin.qnt# HOME/coin.qnt#g' exit $exit_code @@ -736,7 +736,7 @@ error: Invariant violated ``` quint run --out-itf=out-itf-example.itf.json --max-steps=5 --seed=123 \ --invariant=totalSupplyDoesNotOverflowInv \ - ../examples/solidity/Coin/coin.qnt + ../examples/tutorials/coin.qnt cat out-itf-example.itf.json | jq '.states[0]."balances"."#map"[0]' rm out-itf-example.itf.json ``` @@ -757,7 +757,7 @@ rm out-itf-example.itf.json ``` quint run --out-itf=out-itf-mbt-example.itf.json --max-steps=5 --seed=123 \ --invariant=totalSupplyDoesNotOverflowInv --mbt\ - ../examples/solidity/Coin/coin.qnt + ../examples/tutorials/coin.qnt cat out-itf-mbt-example.itf.json | jq '.states[1]' rm out-itf-mbt-example.itf.json ``` @@ -845,7 +845,7 @@ rm out-itf-mbt-example.itf.json ``` -quint run --out-itf=out-itf-example.itf.json --max-steps=5 --seed=123 ../examples/solidity/Coin/coin.qnt +quint run --out-itf=out-itf-example.itf.json --max-steps=5 --seed=123 ../examples/tutorials/coin.qnt cat out-itf-example.itf.json | jq '.states[0]."balances"."#map"[0]' rm out-itf-example.itf.json ``` @@ -864,7 +864,7 @@ rm out-itf-example.itf.json ``` -quint run --out-itf=out-itf-example.itf.json --n-traces=3 --max-steps=5 --seed=123 ../examples/solidity/Coin/coin.qnt +quint run --out-itf=out-itf-example.itf.json --n-traces=3 --max-steps=5 --seed=123 ../examples/tutorials/coin.qnt cat out-itf-example0.itf.json | jq '.["#meta"].status' rm out-itf-example*.itf.json ``` @@ -878,7 +878,7 @@ rm out-itf-example*.itf.json ``` -quint run --out-itf=out-itf-example.itf.json --n-traces=3 --max-steps=5 --seed=123 ../examples/solidity/Coin/coin.qnt \ +quint run --out-itf=out-itf-example.itf.json --n-traces=3 --max-steps=5 --seed=123 ../examples/tutorials/coin.qnt \ --invariant=totalSupplyDoesNotOverflowInv cat out-itf-example0.itf.json | jq '.["#meta"].status' cat out-itf-example1.itf.json | jq '.["#meta"].status' @@ -900,7 +900,7 @@ TODO: output states after fix: https://github.com/informalsystems/quint/issues/2 ``` output=$(quint test --output='coin_{#}_{}.itf.json' \ - ../examples/solidity/Coin/coin.qnt) + ../examples/tutorials/coin.qnt) exit_code=$? echo "$output" | sed -e 's/([0-9]*ms)/(duration)/g' -e 's#^.*coin.qnt# HOME/coin.qnt#g' cat coin_0_sendWithoutMintTest.itf.json | jq '.states' @@ -988,9 +988,12 @@ The REPL tutorial is reproducible in REPL. ``` +cd ../examples/tutorials/repl/ +make quint -q -r \ - ../tutorials/repl/kettle.qnt::kettle <../tutorials/repl/replTestIn.txt \ - | diff - ../tutorials/repl/replTestOut.txt + kettle.qnt::kettle < replTestIn.txt \ + | diff - replTestOut.txt +cd - > /dev/null ``` ### test --verbosity=3 outputs a trace @@ -999,7 +1002,7 @@ quint -q -r \ ``` output=$(quint test --seed=0x1cce8452305113 --match=mintTwiceThenSendError \ - --verbosity=3 ../examples/solidity/Coin/coin.qnt) + --verbosity=3 ../examples/tutorials/coin.qnt) exit_code=$? echo "$output" | sed -e 's/([0-9]*ms)/(duration)/g' -e 's#^.*coin.qnt# HOME/coin.qnt#g' exit $exit_code @@ -1068,7 +1071,7 @@ send( ``` -output=$(quint test --seed=NotANumber ../examples/solidity/Coin/coin.qnt) +output=$(quint test --seed=NotANumber ../examples/tutorials/coin.qnt) exit_code=$? echo "$output" | sed -e 's/([0-9]*ms)/(duration)/g' -e 's#^.*coin.qnt# HOME/coin.qnt#g' exit $exit_code @@ -1084,7 +1087,7 @@ error: --seed must be a big integer, found: NotANumber ``` -output=$(quint run --seed=NotANumber ../examples/solidity/Coin/coin.qnt) +output=$(quint run --seed=NotANumber ../examples/tutorials/coin.qnt) exit_code=$? echo "$output" | sed -e 's/([0-9]*ms)/(duration)/g' -e 's#^.*coin.qnt# HOME/coin.qnt#g' exit $exit_code diff --git a/quint/package.json b/quint/package.json index 50b1891bc..35b8aeceb 100644 --- a/quint/package.json +++ b/quint/package.json @@ -2,13 +2,7 @@ "name": "@informalsystems/quint", "version": "0.21.0", "description": "Core tool for the Quint specification language", - "keywords": [ - "temporal", - "logic", - "formal", - "specification", - "verification" - ], + "keywords": ["temporal", "logic", "formal", "specification", "verification"], "homepage": "https://github.com/informalsystems/quint", "bugs": "https://github.com/informalsystems/quint/issues", "license": "Apache 2.0", @@ -35,11 +29,7 @@ "bin": { "quint": "dist/src/cli.js" }, - "files": [ - "README.md", - "dist/**/*", - "test/**/*.ts" - ], + "files": ["README.md", "dist/**/*", "test/**/*.ts"], "engines": { "node": "18 - 20" }, @@ -81,7 +71,7 @@ "apalache-dist": "txm apalache-dist-tests.md", "generate": "npm run antlr && npm run compile && npm link && npm run api-docs && npm run update-fixtures", "antlr": "antlr4ts -visitor ./src/generated/Quint.g4 && antlr4ts -visitor ./src/generated/Effect.g4", - "api-docs": "quint docs ./src/builtin.qnt > ../doc/builtin.md", + "api-docs": "quint docs ./src/builtin.qnt > ../docs/pages/docs/builtin.md", "update-fixtures": "./scripts/update-fixtures.sh", "format-check": "npx prettier --check '**/*.ts' && npx eslint '**/*.ts'", "format": "npx prettier --write '**/*.ts' && npx eslint --fix '**/*.ts'", diff --git a/quint/src/builtin.qnt b/quint/src/builtin.qnt index 35f68fde6..88de4150d 100644 --- a/quint/src/builtin.qnt +++ b/quint/src/builtin.qnt @@ -32,7 +32,7 @@ module builtin { /// /// ### Examples /// - /// ``` + /// ```quint /// assert(iff(true, true)) /// assert(iff(false, false)) /// assert(not(iff(true, false))) @@ -46,7 +46,7 @@ module builtin { /// /// ### Examples /// - /// ``` + /// ```quint /// assert(true.implies(true)) /// assert(false.implies(false)) /// assert(not(true.implies(false))) @@ -65,7 +65,7 @@ module builtin { /// /// ### Examples /// - /// ``` + /// ```quint /// assert(Set(1, 2, 3).exists(n => n == 2)) /// assert(not(Set(1, 2, 3).exists(n => n == 4))) /// ``` @@ -77,7 +77,7 @@ module builtin { /// /// ### Examples /// - /// ``` + /// ```quint /// assert(Set(1, 2, 3).forall(n => n > 0)) /// assert(not(Set(1, 2, 3).forall(n => n > 1))) /// ``` @@ -91,7 +91,7 @@ module builtin { /// /// ### Examples /// - /// ``` + /// ```quint /// assert(1.in(Set(1, 2, 3))) /// assert(not(4.in(Set(1, 2, 3)))) /// ``` @@ -105,7 +105,7 @@ module builtin { /// /// ### Examples /// - /// ``` + /// ```quint /// assert(Set(1, 2, 3).contains(1)) /// assert(not(Set(1, 2, 3).contains(4))) /// ``` @@ -117,7 +117,7 @@ module builtin { /// /// ### Examples /// - /// ``` + /// ```quint /// assert(Set(1, 2, 3).union(Set(2, 3, 4)) == Set(1, 2, 3, 4)) /// ``` pure def union(s1, s2): (Set[a], Set[a]) => Set[a] @@ -128,7 +128,7 @@ module builtin { /// /// ### Examples /// - /// ``` + /// ```quint /// assert(Set(1, 2, 3).intersect(Set(2, 3, 4)) == Set(2, 3)) /// ``` pure def intersect(s1, s2): (Set[a], Set[a]) => Set[a] @@ -139,7 +139,7 @@ module builtin { /// /// ### Examples /// - /// ``` + /// ```quint /// assert(Set(1, 2, 3).exclude(Set(2, 3, 4)) == Set(1)) /// ``` pure def exclude(s1, s2): (Set[a], Set[a]) => Set[a] @@ -148,7 +148,7 @@ module builtin { /// /// ### Examples /// - /// ``` + /// ```quint /// assert(Set(1, 2, 3).subseteq(Set(1, 2, 3, 4))) /// assert(not(Set(1, 2, 3).subseteq(Set(1, 2)))) /// ``` @@ -158,7 +158,7 @@ module builtin { /// /// ### Examples /// - /// ``` + /// ```quint /// assert(Set(1, 2, 3).filter(n => n > 1) == Set(2, 3)) /// ``` pure def filter(s, p): (Set[a], (a) => bool) => Set[a] @@ -169,7 +169,7 @@ module builtin { /// /// ### Examples /// - /// ``` + /// ```quint /// assert(Set(1, 2, 3).map(n => n > 1) == Set(false, true, true)) /// ``` pure def map(s, f): (Set[a], (a) => b) => Set[b] @@ -183,7 +183,7 @@ module builtin { /// /// ### Examples /// - /// ``` + /// ```quint /// val sum = Set(1, 2, 3, 4).fold(0, (x, y) => x + y) /// assert(sum == 10) /// val mul = Set(1, 2, 3, 4).fold(1, (x, y) => x * y) @@ -196,7 +196,7 @@ module builtin { /// /// ### Examples /// - /// ``` + /// ```quint /// assert(Set(1, 2).powerset() == Set( /// Set(), /// Set(1), @@ -210,7 +210,7 @@ module builtin { /// /// ### Examples /// - /// ``` + /// ```quint /// assert(Set(Set(1, 2), Set(3, 4)).flatten() == Set(1, 2, 3, 4)) /// ``` pure def flatten(s): (Set[Set[a]]) => Set[a] @@ -222,7 +222,7 @@ module builtin { /// /// ### Examples /// - /// ``` + /// ```quint /// assert(Set(1, 2).allLists().contains([])) /// assert(Set(1, 2).allLists().contains([1, 1, 1, 1, 2, 1])) /// ``` @@ -230,7 +230,7 @@ module builtin { /// `s.allListsUpTo(l)` is the set of all lists of elements in `s` with length <= `l` /// - /// ``` + /// ```quint /// assert(Set(1, 2).allListsUpTo(1) == Set([], [1], [2])) /// assert(Set(1).allListsUpTo(2) == Set([], [1], [1, 1])) /// ``` @@ -240,7 +240,7 @@ module builtin { /// /// ### Examples /// - /// ``` + /// ```quint /// assert(Set(1, 2, 3).chooseSome() == 1) /// assert(Set(1, 2, 3).filter(x => x > 2).chooseSome() == 3) /// ``` @@ -250,7 +250,7 @@ module builtin { /// /// ### Examples /// - /// ``` + /// ```quint /// nondet x = oneOf(Set(1, 2, 3)) /// assert(x.in(Set(1, 2, 3))) /// ``` @@ -260,7 +260,7 @@ module builtin { /// /// ### Examples /// - /// ``` + /// ```quint /// assert(Set(1, 2, 3).isFinite()) /// assert(!Nat.isFinite()) /// ``` @@ -270,7 +270,7 @@ module builtin { /// /// ### Examples /// - /// ``` + /// ```quint /// assert(Set(1, 2, 3).size() == 3) /// ``` pure def size(s): (Set[a]) => int @@ -281,7 +281,7 @@ module builtin { /// /// ### Examples /// - /// ``` + /// ```quint /// pure val m = Map(1 -> true, 2 -> false) /// assert(m.get(1) == true) /// ``` @@ -291,7 +291,7 @@ module builtin { /// /// ### Examples /// - /// ``` + /// ```quint /// pure val m = Map(1 -> true, 2 -> false) /// assert(m.keys() == Set(1, 2)) /// ``` @@ -301,7 +301,7 @@ module builtin { /// /// ### Examples /// - /// ``` + /// ```quint /// pure val m = Set(1, 2, 3).mapBy(x => x * x) /// assert(m == Map(1 -> 1, 2 -> 4, 3 -> 9)) /// ``` @@ -312,7 +312,7 @@ module builtin { /// /// ### Examples /// - /// ``` + /// ```quint /// pure val m = Set((1, true), (2, false)).setToMap() /// assert(m == Map(1 -> true, 2 -> false)) /// ``` @@ -322,7 +322,7 @@ module builtin { /// /// ### Examples /// - /// ``` + /// ```quint /// pure val s = Set(1, 2).setOfMaps(set(true, false)) /// assert(s == Set( /// Map(1 -> true, 2 -> true), @@ -339,7 +339,7 @@ module builtin { /// /// ### Examples /// - /// ``` + /// ```quint /// pure val m = Map(1 -> true, 2 -> false) /// pure val m2 = m.set(2, true) /// assert(m == Map(1 -> true, 2 -> false)) @@ -353,7 +353,7 @@ module builtin { /// /// ### Examples /// - /// ``` + /// ```quint /// pure val m = Map(1 -> true, 2 -> false) /// pure val m2 = m.setBy(2, x => !x) /// assert(m == Map(1 -> true, 2 -> false)) @@ -365,7 +365,7 @@ module builtin { /// /// ### Examples /// - /// ``` + /// ```quint /// pure val m = Map(1 -> true, 2 -> false) /// pure val m2 = m.put(2, true) /// pure val m3 = m.put(3, true) @@ -379,7 +379,7 @@ module builtin { /// /// ### Examples /// - /// ``` + /// ```quint /// assert(List(1, 2, 3).append(4) == List(1, 2, 3, 4)) /// ``` pure def append(l, e): (List[a], a) => List[a] @@ -388,7 +388,7 @@ module builtin { /// /// ### Examples /// - /// ``` + /// ```quint /// assert(List(1, 2, 3).append(List(4, 5, 6)) == List(1, 2, 3, 4, 5, 6)) /// ``` pure def concat(l1, l2): (List[a], List[a]) => List[a] @@ -399,7 +399,7 @@ module builtin { /// /// ### Examples /// - /// ``` + /// ```quint /// assert(List(1, 2, 3).head() == 1) /// ``` pure def head(l): (List[a]) => a @@ -410,7 +410,7 @@ module builtin { /// /// ### Examples /// - /// ``` + /// ```quint /// assert(List(1, 2, 3).tail() == List(2, 3)) /// ``` pure def tail(l): (List[a]) => List[a] @@ -419,7 +419,7 @@ module builtin { /// /// ### Examples /// - /// ``` + /// ```quint /// assert(List(1, 2, 3).length() == 3) /// ``` pure def length(l): (List[a]) => int @@ -430,7 +430,7 @@ module builtin { /// /// ### Examples /// - /// ``` + /// ```quint /// assert(List(1, 2, 3).nth(1) == 2) /// ``` pure def nth(l, i): (List[a], int) => a @@ -439,7 +439,7 @@ module builtin { /// /// ### Examples /// - /// ``` + /// ```quint /// assert(List(1, 2, 3).indices() == Set(0, 1, 2)) /// ``` pure def indices(l): (List[a]) => Set[int] @@ -450,7 +450,7 @@ module builtin { /// /// ### Examples /// - /// ``` + /// ```quint /// assert(List(1, 2, 3).replaceAt(1, 4) == List(1, 4, 3)) /// ``` pure def replaceAt(l, i, e): (List[a], int, a) => List[a] @@ -467,7 +467,7 @@ module builtin { /// /// ### Examples /// - /// ``` + /// ```quint /// assert(List(1, 2, 3, 4, 5).slice(1, 3) == List(2, 3)) /// ``` pure def slice(l, i, j): (List[a], int, int) => List[a] @@ -480,7 +480,7 @@ module builtin { /// /// ### Examples /// - /// ``` + /// ```quint /// assert(range(1, 3) == List(1, 2)) /// ``` pure def range(i, j): (int, int) => List[int] @@ -491,7 +491,7 @@ module builtin { /// /// ### Examples /// - /// ``` + /// ```quint /// assert(List(1, 2, 3).select(x -> x % 2 == 0) == List(2)) /// ``` pure def select(l, p): (List[a], (a) => bool) => List[a] @@ -503,7 +503,7 @@ module builtin { /// /// ### Examples /// - /// ``` + /// ```quint /// pure val sum = List(1, 2, 3, 4).foldl(0, (x, y) => x + y) /// assert(sum == 10) /// pure val l = List(1, 2, 3, 4).foldl(List(), (l, e) => l.append(e)) @@ -574,7 +574,7 @@ module builtin { /// /// ### Examples /// - /// ``` + /// ```quint /// assert(1.to(3) == Set(1, 2, 3)) /// ``` pure def to(i, j): (int, int) => Set[int] @@ -583,7 +583,7 @@ module builtin { /// /// ### Examples /// - /// ``` + /// ```quint /// var x: int /// action Init = x' = 0 /// action Next = x' = x + 1 @@ -597,7 +597,7 @@ module builtin { /// /// ### Examples /// - /// ``` + /// ```quint /// var x: int /// action Init = x' = 0 /// action Next = x' = x + 1 @@ -609,7 +609,7 @@ module builtin { /// /// ### Examples /// - /// ``` + /// ```quint /// var x: int /// action Init = x' = 0 /// action Next = x' = x + 1 @@ -626,7 +626,7 @@ module builtin { /// /// ### Examples /// - /// ``` + /// ```quint /// var x: int /// action Init = x' = 0 /// action Next = x' = x + 1 @@ -643,7 +643,7 @@ module builtin { /// /// ### Examples /// - /// ``` + /// ```quint /// var x: int /// action Init = x' = 0 /// action Next = any { @@ -662,7 +662,7 @@ module builtin { /// /// ### Examples /// - /// ``` + /// ```quint /// var x: int /// action Init = x' = 0 /// action Under10 = all { @@ -679,7 +679,7 @@ module builtin { /// }) /// ``` /// - /// ``` + /// ```quint /// var x: int /// action Init = x' = 0 /// action impossible = { @@ -704,7 +704,7 @@ module builtin { /// /// ### Examples /// - /// ``` + /// ```quint /// var x: int /// action Init = x' = 0 /// action Next = any { @@ -729,7 +729,7 @@ module builtin { /// /// ### Examples /// - /// ``` + /// ```quint /// var x: int /// action Init = x' = 0 /// action cycleTo1 = all { x == 0, x' = 1 }, @@ -757,7 +757,7 @@ module builtin { /// /// ### Examples /// - /// ``` + /// ```quint /// var x: int /// action Init = x' = 0 /// // Next defines all transitions from a number to its successor. @@ -773,12 +773,12 @@ module builtin { /// /// ### Examples /// - /// ``` + /// ```quint /// pure val m = if (1 == 2) 3 else 4 /// assert(m == 4) /// ``` /// - /// ``` + /// ```quint /// var x: int /// action a = if (x == 0) x' = 3 else x' = 4 /// run test = (x' = 0).then(a).then(assert(x == 3)) @@ -795,7 +795,7 @@ module builtin { /// /// ### Examples /// - /// ``` + /// ```quint /// var x: int /// run test = (x' = 1).then(x' = 2).then(x' = 3).then(assert(x == 3)) /// ``` @@ -812,7 +812,7 @@ module builtin { /// /// ### Examples /// - /// ``` + /// ```quint /// var n: int /// run expectConditionOkTest = (n' = 0).then(n' = 3).expect(n == 3) /// run expectConditionFailsTest = (n' = 0).then(n' = 3).expect(n == 4) @@ -834,11 +834,11 @@ module builtin { /// /// ### Examples /// - /// ``` + /// ```quint /// var x: int /// run test = (x' = 0).then(3.reps(i => x' = x + 1)).then(assert(x == 3)) /// ``` - action reps(n, A): (int, int => bool) => bool + action reps(n, A): (int, int => bool) => bool /// `a.fail()` evaluates to `true` if and only if action `a` evaluates to `false`. /// @@ -851,12 +851,12 @@ module builtin { /// /// ### Examples /// - /// ``` + /// ```quint /// var x: int /// run test = (x' = 0).then(3.reps(x' = x + 1)).then(assert(x == 3)) - /// ``` + /// ```quint /// - /// ``` + /// ```quint /// var x: int /// action Init = x' = 0 /// action Next = x' = x + 1 @@ -873,7 +873,7 @@ module builtin { /// /// ### Examples /// - /// ``` + /// ```quint /// var x: int /// >>> (x' = 0).then(3.reps(i => x' = q::debug("new x:", x + 1))) /// > new x: 1 @@ -891,10 +891,10 @@ module builtin { // /// // /// ### Examples // /// - // /// ``` + // /// ```quint // /// pure val l = List(1, 2, 3) // /// assert(l == [ 1, 2, 3 ]) - // /// ``` + // /// ```quint // pure def List(a*): (a*) => List[a] // /// `Set(e0, ..., en)` is a set of values `e0`, ..., `en`. @@ -903,10 +903,10 @@ module builtin { // /// // /// ### Examples // /// - // /// ``` + // /// ```quint // /// pure val s = Set(1, 2, 3) // /// assert(s.contains(2)) - // /// ``` + // /// ```quint // pure def Set(e*): (a*) => Set[a] // /// `Map(k0 -> v0, ..., kn -> vn)` is a map that maps `k0` to `v0`, ..., and `kn` to `vn`. @@ -918,7 +918,7 @@ module builtin { // /// ``` // /// pure val m = Map("a" -> 1, "b" -> 2) // /// assert(m.get("a") == 1) - // /// ``` + // /// ```quint // pure def Map((k -> v)*): ((a, b)* => a -> b // /// `Rec((f0, v0), ..., (fn, vn))` is a record that has fields `f0`, ..., `fn` with values `v0`, ..., `vn`. @@ -929,10 +929,10 @@ module builtin { // /// // /// ### Examples // /// - // /// ``` + // /// ```quint // /// pure val r = { x: 1, y: 2 } // /// assert(r.x == 1) - // /// ``` + // /// ```quint // pure def Rec((f0, v0), ..., (fn, vn)): ((str, a0), ..., (str, an)) => { f0:, a0, ..., fn:, an } // /// `r.field(f)` is the value of the field `f` in the record `r`. @@ -943,21 +943,21 @@ module builtin { // /// // /// ### Examples // /// - // /// ``` + // /// ```quint // /// pure val r = { x: 1, y: 2 } // /// assert(r.field("x") == 1) // /// assert(r.y == 1) - // /// ``` + // /// ```quint // pure def field(r, f): ({ f: a | t }, str) => a // /// `r.fieldNames()` is the set of field names in the record `r`. // /// // /// ### Examples // /// - // /// ``` + // /// ```quint // /// pure val r = { x: 1, y: 2 } // /// assert(r.fieldNames() == Set("x", "y")) - // /// ``` + // /// ```quint // pure def fieldNames(r): ({ f0: t0, ..., fn: tn }) => Set[str] // /// `r.with(f, v)` is a record that is the same as `r` except that the field `f` has value `v`. @@ -966,10 +966,10 @@ module builtin { // /// // /// ### Examples // /// - // /// ``` + // /// ```quint // /// pure val r = { x: 1, y: 2 } // /// assert(r.with("x", 3) == { x: 3, y: 2 }) - // /// ``` + // /// ```quint // pure def with(r, f, v): ({ f0: t0 | r }, str, t0) => { f0: t0 | r } // /// `Tup(e0, ..., en)` is a tuple of values `e0`, ..., `en`. @@ -980,11 +980,11 @@ module builtin { // /// // /// ### Examples // /// - // /// ``` + // /// ```quint // /// pure val t = Tup(1, 2, 3) // /// assert(t == (1, 2, 3)) // /// assert(t._1 == 1) - // /// ``` + // /// ```quint // pure def Tup(e0, ..., en): (a0, ..., an) => (a0, ..., an) // /// `t.item(i)` is the `i`th element of the tuple `t`. @@ -995,11 +995,11 @@ module builtin { // /// // /// ### Examples // /// - // /// ``` + // /// ```quint // /// pure val t = (1, 2, 3) // /// assert(t.item(1) == 1) // /// assert(t._2 == 2) - // /// ``` + // /// ```quint // pure def item(t, i): ((a0, ..., an), int) => ai // /// `tuples(s0, ..., sn)` is the set of all tuples `(t0, ..., tn)` such that `ti` is in `si`. @@ -1008,10 +1008,10 @@ module builtin { // /// // /// ### Examples // /// - // /// ``` + // /// ```quint // /// pure val s = tuples(Set(1, 2), Set(3, 4)) // /// assert(s == Set((1, 3), (1, 4), (2, 3), (2, 4))) - // /// ``` + // /// ```quint // pure def tuples(a*): (Set[a0], ..., Set[an]) => Set[(a0, ..., an)] // /// `variant("Label", expr)` is a value of type `T` when @@ -1022,12 +1022,12 @@ module builtin { // /// // /// ### Examples // /// - // /// ``` + // /// ```quint // /// type T = A(int) | B | C(str) // /// assert(variant("A", 42) == A(42)) // /// assert(variant("B", Rec()) == B) // /// assert(variant("C", "foo") == C("foo")) - // /// ``` + // /// ```quint // /// // /// Note that this is an internal built-in and users should prefer the generated // /// data constructor matching the label of a variant. E.g., in our example above, @@ -1047,12 +1047,12 @@ module builtin { // /// // /// ### Examples // /// - // /// ``` + // /// ```quint // /// type T = A(int) | B | C(str) // /// val result = matchVariant(A(42), "A", (x) => x, "B", (_) => 0, "C", (_) => 1) // /// assert(result == 42) // /// assert(result == match A(42) { A(x) => x | B => 0 | C(_) => 1 }) - // /// ``` + // /// ```quint // /// // /// Note that this is an internal built-in and users should prefer the // /// `match` syntax on the right hand side of the `==` above. @@ -1066,10 +1066,10 @@ module builtin { // /// // /// ### Examples // /// - // /// ``` + // /// ```quint // /// assert(and(1 == 1, true)) // /// assert(not(and(1 == 1, true, false))) - // /// ``` + // /// ```quint // pure def and(a*): (bool*) => bool // /// `actionAll(a0, ..., an)` is true when all `ai` are true. `ai` can be actions. @@ -1080,13 +1080,13 @@ module builtin { // /// // /// ### Examples // /// - // /// ``` + // /// ```quint // /// var x: int // /// var y: int // /// run test = (all { x' = 0, y' = 0 }) // /// .then(all { x' = 1, y' = 2 }) // /// .then(assert(x == 1 && y == 2)) - // /// ``` + // /// ```quint // pure def actionAll(a*): (bool*) => bool // /// `or(a0, ..., an)` is true when at least one `ai` is true. @@ -1097,10 +1097,10 @@ module builtin { // /// // /// ### Examples // /// - // /// ``` + // /// ```quint // /// assert(or(1 == 1, false)) // /// assert(not(or(1 == 2, false, false))) - // /// ``` + // /// ```quint // pure def or(a*): (bool*) => bool // /// `actionAny(a0, ..., an)` is true when at least one `ai` is true. `ai` can be actions. @@ -1111,9 +1111,9 @@ module builtin { // /// // /// ### Examples // /// - // /// ``` + // /// ```quint // /// var x: int // /// run test = (x' = 0).then(any { x' = 1, x' = 2 }).then(assert(or { x == 1, x == 2 })) - // /// ``` + // /// ```quint // pure def actionAny(a*): (bool*) => bool } diff --git a/quint/src/docs.ts b/quint/src/docs.ts index 7c2365c75..2201a807d 100644 --- a/quint/src/docs.ts +++ b/quint/src/docs.ts @@ -20,8 +20,10 @@ import { QuintModule, isDef } from './ir/quintIr' * A documentation entry for a definition, compatible with LSP responses for signature help */ export interface DocumentationEntry { + /* The definition's name */ + name: string /* The mode and type signature of the definition */ - label: string + signature: string /* The documentation string in markdown for the definition, if present */ documentation?: string } @@ -37,7 +39,8 @@ export function produceDocs(quintModule: QuintModule): Map { const result = builtinDocs(idGen) assert.deepEqual(result.get('Bool'), { - label: 'pure val Bool: Set[bool]', + name: 'Bool', + signature: 'pure val Bool: Set[bool]', documentation: dedent( `The set of all booleans | diff --git a/quint/test/docs.test.ts b/quint/test/docs.test.ts index 9ce58e892..716833327 100644 --- a/quint/test/docs.test.ts +++ b/quint/test/docs.test.ts @@ -19,7 +19,8 @@ describe('produceDocs', () => { [...docs.values()], [ { - label: 'val foo', + name: 'foo', + signature: 'val foo', documentation: 'This is a docstring for foo', }, ] @@ -29,13 +30,16 @@ describe('produceDocs', () => { describe('toMarkdown', () => { const doc = { - label: 'val foo', + name: 'foo', + signature: 'val foo', documentation: 'This is a docstring for foo', } it('produces markdown out of a documentation entry', () => { const expectedMarkdown = dedent( - `## \`val foo\` + `## foo + | + |Signature: \`val foo\` | |This is a docstring for foo` ) diff --git a/vscode/quint-vscode/server/src/complete.ts b/vscode/quint-vscode/server/src/complete.ts index cbeb7c855..c78c052b8 100644 --- a/vscode/quint-vscode/server/src/complete.ts +++ b/vscode/quint-vscode/server/src/complete.ts @@ -187,7 +187,7 @@ function builtinCompletionsWithDocs( return { label: op.name, kind: CompletionItemKind.Function, - detail: docs?.label, + detail: docs?.signature, documentation: md(docs?.documentation), } }) diff --git a/vscode/quint-vscode/server/src/hover.ts b/vscode/quint-vscode/server/src/hover.ts index 6b2e6fd96..d0844038d 100644 --- a/vscode/quint-vscode/server/src/hover.ts +++ b/vscode/quint-vscode/server/src/hover.ts @@ -176,7 +176,7 @@ function documentationHover( return [] } - let hoverText = ['```quint', signature.label, '```', ''] + let hoverText = ['```quint', signature.signature, '```', ''] if (signature.documentation) { hoverText.push(signature.documentation) } diff --git a/vscode/quint-vscode/server/src/server.ts b/vscode/quint-vscode/server/src/server.ts index e41212d9a..420b2ed49 100644 --- a/vscode/quint-vscode/server/src/server.ts +++ b/vscode/quint-vscode/server/src/server.ts @@ -156,8 +156,12 @@ export class QuintLanguageServer { } const signatureWithMarkupKind = signature?.documentation - ? { ...signature, documentation: { kind: 'markdown', value: signature.documentation } as MarkupContent } - : signature + ? { + name: signature.name, + label: signature.signature, + documentation: { kind: 'markdown', value: signature.documentation } as MarkupContent, + } + : { name: signature.name, label: signature.signature } return { signatures: [signatureWithMarkupKind],