├── .gitignore ├── .hcignore ├── README.md ├── agent1.keystore ├── agent2.keystore ├── app.json ├── cli ├── Cargo.lock ├── Cargo.toml └── src │ └── main.rs ├── conductor-config.toml ├── demo-moves.txt ├── dist └── generic-game.dna.json ├── generic-game-diagram.pdf ├── test ├── checkers.js ├── helpers.js ├── index.js ├── matchmaking.js ├── package-lock.json ├── package.json └── tictactoe.js └── zomes └── main ├── code ├── .hcbuild ├── Cargo.lock ├── Cargo.toml └── src │ ├── game.rs │ ├── game_move.rs │ ├── lib.rs │ ├── matchmaking.rs │ ├── tictactoe │ ├── mod.rs │ ├── moves.rs │ ├── state.rs │ └── validation.rs │ └── your-game │ ├── mod.rs │ ├── moves.rs │ ├── state.rs │ └── validation.rs └── zome.json /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | target/ 3 | .hc 4 | .cargo/ 5 | -------------------------------------------------------------------------------- /.hcignore: -------------------------------------------------------------------------------- 1 | dist 2 | test 3 | README.md -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Holochain Generic Game 2 | 3 | This is a generic game framework packaged in a Holochain application! 4 | 5 | **The main challenge for the September 2019 devcamp is to built your own game using this game framework.** 6 | 7 | ## Overview 8 | 9 | Previous read: [Fundamentals of Games on Holochain](https://hackmd.io/@FqBkpkUfTSKcADA4DAqhqw/S1gB6kOiE) 10 | 11 | The generic game framework is tuned for the types of games that Holochain is good for. In the game framework, all the work of creating entries and linking them is already done for you. You only need to "fill in the blanks" to implement the behaviour of your game. 12 | 13 | You will be building your game as the devcamp progresses - take it step by step. 14 | 15 | Overview of the zome files: 16 | 17 | - `lib.rs`, `game.rs`, `game_move.rs`, `matchmaking.rs`: these files are the heart of the generic game framework. In this devcamp, we won't be editing these files, although you can look at them or experiment if you're curious. 18 | - `tictactoe` folder: each folder constitues a game. You can use it as a reference game implementation when you are implementing your own. 19 | - `your-game` folder: this is the folder that you will be editing. It contains todos that will guide you through the process. 20 | 21 | ## ✍️ First Challenge - Play a game with yourself 22 | 23 | The first task lets you try out running a holochain instance locally with two agents to play a game of tic-tac-toe. Before you begin make sure you have the holochain conductor `holochain` available on your path. 24 | 25 | ### 1. Start the conductor 26 | 27 | From the repo root directory run the following command 28 | 29 | ``` 30 | holochain -c ./conductor-config.toml 31 | ``` 32 | 33 | It might take a few seconds to unlock the keystore but you should see something like the following: 34 | 35 | ``` 36 | Using config path: ./conductor-config.toml 37 | Unlocking agent keys: 38 | Unlocking key for agent 'test_agent1': 39 | Reading keystore from ./agent1.keystore 40 | Unlocking key for agent 'test_agent2': 41 | Reading keystore from ./agent2.keystore 42 | 2019-05-17 15:55:37 ThreadId(1):conductor: starting signal loop 43 | Reading DNA from ./dist/generic-game.dna.json 44 | Failed to load instance instance1 from storage: ErrorGeneric("State could not be loaded due to NoneError") 45 | Initializing new chain... 46 | ... 47 | ``` 48 | and then a whole lot of colored debug output. Scanning the debug you should be able to see indication that the conductor is: 49 | 50 | - unlocking the keystores 51 | - loading the game DNA from file 52 | - creating a local chain for each agent 53 | - validating the agents first two local chain entries (the `DNA` entry and the `agent` entry) 54 | 55 | These are the essential tasks for a holochain instance that is starting for the first time. 56 | 57 | ### v2. Start the CLI 58 | 59 | To keep things simple we will be interacting with our Holochain conductor using a command line interface that connects via HTTP. Make sure you keep the conductor running and in a new terminal window run the following: 60 | 61 | ``` 62 | cd cli 63 | cargo run http://localhost:3001 instance1 64 | ``` 65 | 66 | This will build the CLI and then run it. This is instructing the CLI to connect to a holochain instance running on localhost port 3001 with the instance id `instance1`. After it builds you should see the following: 67 | 68 | ``` 69 | ###################################################################### 70 | CLI interface for games written using the Holochain Generic Game framework. 71 | Enter "help" for a list of commands. 72 | Use "create_game " or "join_game " to start or join a game. 73 | Press Ctrl-D or enter "quit" to exit. 74 | ###################################################################### 75 | 76 | 77 | Your agent address is "HcScjcgKqXC5pmfvka9DmtEJwVr548yd86UPtJGGoue9ynuikuRTN7oE5zcjgbi" 78 | 79 | Send this to other players so they can invite you to a game. 80 | 81 | 82 | No game> 83 | ``` 84 | 85 | 86 | If you see this it means you are now successfully connected to the holochain instance and can participate as this agent. Be sure to test out the commands to see what you can do. 87 | 88 | You can't play a game with one agent so open up ~another~ terminal window and connect to the conductor on the port/instance where the second agent is running: 89 | ``` 90 | cd cli 91 | cargo run http://localhost:3002 instance2 92 | ``` 93 | 94 | ### 3. Play a game 95 | 96 | Now the tricky part, to play a game of tic-tac-toe with yourself! Keep the conductor running and both windows with the CLI. We'll refer to one of them as Agent A and the other as Agent B. 97 | 98 | Agent A will be the one to create the game. Copy the agent address from Agent B and run the following command in **Agent A**: 99 | ``` 100 | new_game HcScidPSdAT43q9qirJwt5rHJYjjsvougV3jgSBwdJujszw3bBu5Mktr74Rgnea 101 | ``` 102 | 103 | This should create a new game and show the following output: 104 | ``` 105 | Non-creator must make the first move 106 | 107 | x 0 1 2 108 | y 109 | 0 | | | | 110 | 1 | | | | 111 | 2 | | | | 112 | 113 | QmTNHtXZye7vz3d4LQz5zgHvk1wvxbsBHcstorDWQxshfZ> 114 | ``` 115 | 116 | We have just commit our first entry in the local chain and DHT! A `Game` entry was created with Agent B as the opponent and shared to the DHT. The hash at the bottom of the screen is the hash/address of the Game entry in the DHT. This is the unique identifier we can use to join this game (Note this will be different for everyone as our Game entry includes a timestamp). 117 | 118 | Copy the game address and run the following in **Agent B** 119 | ``` 120 | join_game QmTNHtXZye7vz3d4LQz5zgHvk1wvxbsBHcstorDWQxshfZ 121 | ``` 122 | 123 | If the Game entry was successfully shared in the previous step Agent B shoud now see: 124 | 125 | ``` 126 | Setting current game hash to QmTNHtXZye7vz3d4LQz5zgHvk1wvxbsBHcstorDWQxshfZ 127 | 128 | Non-creator must make the first move 129 | 130 | x 0 1 2 131 | y 132 | 0 | | | | 133 | 1 | | | | 134 | 2 | | | | 135 | 136 | 137 | ``` 138 | 139 | and as the non-creator we are allowed to make the first move. To see the available moves in this game you can run the `moves` command 140 | 141 | ``` 142 | QmTNHtXZye7vz3d4LQz5zgHvk1wvxbsBHcstorDWQxshfZ> moves 143 | The valid moves are: 144 | - {"Place":{"pos":{"x":0,"y":0}}} 145 | ``` 146 | 147 | Lets try making a move. Make sure you are Agent B and run 148 | 149 | ``` 150 | make_move {"Place":{"pos":{"x":0,"y":0}}} 151 | ``` 152 | 153 | and you should get: 154 | 155 | ``` 156 | making move: "{\"Place\":{\"pos\":{\"x\":0,\"y\":0}}}" 157 | Move cast successfully 158 | Waiting for gossip... 159 | OK! 160 | 161 | It is your opponents turn 162 | 163 | x 0 1 2 164 | y 165 | 0 |X| | | 166 | 1 | | | | 167 | 2 | | | | 168 | 169 | ``` 170 | 171 | Thats it! Now you know how it works you can play out the rest of the game. Make sure you test what happens if you try to make an invalid move. 172 | 173 | ## ✍️ Implement your own game 174 | 175 | Here you need to make an important decision on what game you will be implementing: 176 | 177 | - **Simple checkers game**: this is the main option. This is the game we will be implementing with all the group during the devcamp. Also, we already have reference implementation of the checkers game you can find in the other branch of this repository if you're stuck. 178 | - **Design and implement your own game** (advanced rust programmer): feeling adventurous? You can build another kind of game, from idea to design to implementation. Exciting! As devcamp mentors, we'll help all we can, though we can't provide the same type of resources of the checkers option. 179 | 180 | You already known which game you will be implementing? Good! You can begin these steps: 181 | 182 | 1. Rename the folder `your-game` to, well, to your game name. 183 | 2. Look for `DEVCAMP TODO` in all the files inside that folder. You should see all the "fill in the blank" spots you will be implementing. These comments contain examples, hints and references to help you. 184 | 185 | **Note**: as your game won't be completely ready to compile until the last `TODO` is completed, we recommend using the code completion and error highlighting of an IDE. 186 | 187 | ### ✍️ Exercises after Session 3 - 12/09 188 | 189 | Describe the moves of your game. 190 | 191 | 1. Implement `DEVCAMP TODO #1`. 192 | 2. Implement `DEVCAMP TODO #2`. 193 | 194 | ### ✍️ Exercises after Session 4 - 13/09 195 | 196 | Describe your game state, how it evolves and how it is displayed. 197 | 198 | 1. Implement `DEVCAMP TODO #3`. 199 | 2. Implement `DEVCAMP TODO #4`. 200 | 3. Implement `DEVCAMP TODO #5`. 201 | 4. Implement `DEVCAMP TODO #6`. 202 | 203 | ### ✍️ Exercises after Session 5 - 14/09 204 | 205 | Describe whether a move is valid or not depending on the current game state. 206 | 207 | 1. Implement `DEVCAMP TODO #7`. 208 | 2. Implement `DEVCAMP TODO #8`. 209 | 210 | ### ✍️ Build and run 211 | 212 | After completing all the steps above, your game will be ready to be compiled and executed. Hooray! 213 | 214 | If you have opted to make the checkers game, you can use the [holochain-games-ui](https://github.com/holochain-devcamp/holochain-games-ui) to play with your game. 215 | 216 | In any case, you can use the CLI included in this repo to play any game you have implemented. Refer to step 1 of this guide to know how to use the CLI. -------------------------------------------------------------------------------- /agent1.keystore: -------------------------------------------------------------------------------- 1 | {"passphrase_check":"eyJzYWx0IjpbNzUsODIsMTk5LDE0NCwxODQsMzcsMTUxLDQ3LDEyNiwyMzAsMjQ0LDE3OCwxMTQsMTI4LDE3NSwxMjhdLCJub25jZSI6Wzg2LDQ2LDIwNywyNTIsODEsMTM0LDIyLDE1MywyMjEsNzUsMjAyLDExOSwyNywxNjAsMjQzLDMxLDkyLDczLDIyOCwxMjgsMzYsMTkxLDIxNCwxNl0sImNpcGhlciI6WzEwOCw4NCwxNiw0OCwxNjcsMTUyLDcxLDE4NiwxNDUsMTk3LDEyNywyMDksMTIwLDI0OCwxNTEsMTkxLDYwLDExNywzNCwyMDcsOTUsMTcwLDE4MCwxMCw2OSwxNDYsNTksMTg1LDUsMjEyLDIxLDI0LDI0MywxMTUsMjE1LDE3MiwxNTMsMTUwLDE4Nyw5OSwxMDksMjIzLDExNCwxNzAsMTA5LDE0NywxODgsMTg2LDIxNCwyMTIsMjM0LDIxOCwyMCwyMDcsNjgsMTY1XX0=","secrets":{"primary_keybundle:enc_key":{"blob_type":"EncryptingKey","seed_type":"Mock","hint":"","data":"eyJzYWx0IjpbMjQ5LDIyOCw1MCwyNDcsNDQsMTI4LDkyLDEyNiwxNSw1MSwyMjIsMTA0LDIyNCwzNCwyMzQsMTNdLCJub25jZSI6WzMwLDIwMywxMzYsMTMsNjAsMTEsMTAsNTMsMTI2LDUyLDE5OCwxOTcsNDYsNDksMSwyMzUsMTksMTcsMTUyLDEzLDEzMiwzNCw5MSwxOTRdLCJjaXBoZXIiOlsyMzYsMTMsMTMzLDE2NSw4OCw1LDc3LDU3LDE5NSw5NywxMjUsNCw0OSwxNTgsMzIsMiwxNDAsMjI1LDIyNywxNTQsMTA0LDE3NSwyMCwxNTcsNjEsMzAsMSw3MCwyLDE5MiwxMTIsMzcsMTI2LDE5MCw5NSwzNiwxMjksNTIsMzEsMjQ5LDE2NywyMzYsNDUsNTQsOTYsMzIsMjIwLDIwLDE5OSwyMiwyMDAsMTc0LDI0MCwyMjAsMjI3LDYyLDIxMiwyNTMsNjUsMTkwLDE1MSwxODUsNTQsMTQwLDc3LDc0LDE4MCwyMyw2NywxMjEsMTQ2LDc1LDEzOSwxNTQsMTgyLDE4NSwyNTQsODcsMTQzLDI0OSwxMzMsNSwxODIsODYsMTUsMTc3LDc5LDE3MF19"},"primary_keybundle:sign_key":{"blob_type":"SigningKey","seed_type":"Mock","hint":"","data":"eyJzYWx0IjpbNTEsODMsMTU3LDI0OSw2MSwxMzYsMjUwLDQ5LDE0MiwxNCw2MywxNzgsMjI1LDIxMSwxNTAsNjJdLCJub25jZSI6WzE4MCwxMzQsMjQ0LDIxOCwxMzQsMSwyNTEsMTkyLDIyOSwzNywxNzksMjQzLDEwMiwyMTcsMTQsMTY4LDEyOCwxNTcsMTIsMjEyLDgyLDEzNSwxNjUsMjI2XSwiY2lwaGVyIjpbMTMyLDE4Nyw0NiwyMDgsODksMTI5LDIzNywxNDQsNTEsMTg5LDE3OCw3LDkyLDYxLDE2NSwzNSwxNTEsMjI1LDExMiwxNzcsMjM0LDE4OSwxNjQsMjAxLDEyMywxMTYsMTEsMjEsMTQ0LDIwNSwyMzksMTc0LDIxNSwxNDIsMTE3LDg2LDIxNCwyNyw5NiwyMDAsMTQyLDE5MywyOSwyMzIsMjEzLDY3LDYwLDg3LDEzNiwyNTQsNDAsNjcsMTYwLDIzLDkyLDIyOCw3MywxOTksMTUzLDE4OCwyNDgsMjI5LDkwLDExLDUyLDkyLDE2NCwyMjMsMTkxLDEyNSwxMTMsNzgsMjE5LDYxLDc1LDIyNCwxNTgsNTgsMzgsMzMsMjE5LDI0LDE2NSwxNzIsMTkxLDUsMTk0LDcsMjEzLDMxLDI1NSwxMTEsMTY0LDMzLDMxLDIxMywzMywxNDAsOSwxMTAsMTA4LDYxLDIxOSwxNDIsMTgwLDEwOCwyNTIsNjIsMTU0LDE3NywxMDIsNCwyMDYsMTc2LDIyNCwxMywxMTYsMTU3LDk3LDk5XX0="},"root_seed":{"blob_type":"Seed","seed_type":"OneShot","hint":"","data":"eyJzYWx0IjpbMTE2LDI3LDE1MSw4MywxMzQsNzIsMTYyLDE2NSwxNTQsMjA0LDE1NCwxNTksMTkyLDEzNCwxMzYsMjQyXSwibm9uY2UiOlsyMzIsNDAsMjQyLDIwNCwyMjksMTk5LDMwLDIxMiwxNTUsOTQsMTY1LDYsMTczLDIzNSw5Niw3MCwxODEsNjQsMzYsNiwxMTQsMTI3LDE3NSwxMzhdLCJjaXBoZXIiOlsxNTIsMTI3LDM0LDE4OSw1NCwxNjQsNTAsMjA4LDYzLDkxLDY4LDIzNSwzNiwxODcsNzMsMzksMTQ4LDE2LDEyOSwxMzIsMTkxLDExNiwyMTIsMjExLDE0NywxNzEsMjM2LDE2MCw3NCwxNjQsMTksMjQwLDI1NSwzOSwxOTUsNDcsMjIsMTQwLDc5LDIzNCwyMTUsMTMxLDIxNywyMDQsMTg2LDU1LDI0OSwxNjldfQ=="}}} -------------------------------------------------------------------------------- /agent2.keystore: -------------------------------------------------------------------------------- 1 | {"passphrase_check":"eyJzYWx0IjpbMjA2LDE2Nyw3OSw1OSw1NiwxNzgsMTQ2LDM0LDI0MywyMjYsMTAzLDI3LDQyLDE1OSwxNjMsMjI3XSwibm9uY2UiOlsxNzAsNzAsMTY1LDE3Niw5MiwxNjEsMTE0LDM2LDEzMSw0Miw3NSwzNSwxNTgsMjUxLDIzNiwxNTYsMjExLDE1OCwxNDQsMTAxLDYsMjM4LDE1MywxMjVdLCJjaXBoZXIiOlsyNTEsMTA4LDczLDEyNCwxODUsNzgsMjA0LDkwLDExMyw3NywyMDksMTc4LDEzLDQ0LDE1OSw0OSwxLDE5NiwxMTAsNTQsMTU1LDExOSw5Nyw1MSwxMDcsMTUwLDE2LDI4LDc4LDE3MiwyMTEsMTM5LDk1LDg3LDEzNywxNDUsNjgsNTUsNjEsNzcsMTcsMTUyLDE1NywyNiwxNTcsMjMzLDI0OCwyMjEsODksMTIxLDkyLDkwLDEzNCwyNiwyMzksNzFdfQ==","secrets":{"primary_keybundle:enc_key":{"blob_type":"EncryptingKey","seed_type":"Mock","hint":"","data":"eyJzYWx0IjpbOTgsMTE1LDIzNSwxMzksMTM5LDIyNywxOTYsMTY4LDIxNSwxOTIsMTg5LDI1MywxMDksMjAsNDgsMTQwXSwibm9uY2UiOlsyMzEsMjMsMzAsMTg4LDI1MCwxMCwyMjgsMTMxLDcsMTgsMjQ0LDEwLDgxLDEyNCw4OCwxMzQsMTM1LDE3OCw0OSwyLDE0MywzNSwyNDAsMTUzXSwiY2lwaGVyIjpbMzYsMjQ1LDI0MCwxNzUsMjA4LDgwLDY4LDE3MiwxNzgsMjEsOTksNjcsMjAwLDI0NCw5NywyMTQsMTA1LDMxLDcsMTIxLDE2NSwyMTksMjQ5LDE5OSwxNTYsNDksNDksMTkzLDE1LDk4LDE4LDIwNiwyMDMsMzEsMTE1LDEyMSwxMzEsMTcxLDM2LDIzMiw0NCwxMjcsMTExLDIwMiwyMDIsMjUzLDIyNiwyMywyMzAsMTc0LDQxLDE0LDE3NSw2NywyNCwyMzAsMjIwLDY0LDIxNiw1MSwxNjMsOTksMjI2LDM2LDI0NywzMSwxODMsMTkxLDIzNywzOCwyMDEsMjIyLDE0MywyMDYsMTQ5LDEwMCwxNjQsMTQ3LDExMiwyMzYsMTI5LDIzLDI1NSwzLDEzMCw5MiwyMzEsOTNdfQ=="},"primary_keybundle:sign_key":{"blob_type":"SigningKey","seed_type":"Mock","hint":"","data":"eyJzYWx0IjpbMTAwLDcsMzgsOTQsMjUsMjI3LDE3Myw3MCwxMzMsOTMsMjE3LDksMjUwLDE4NSwxNjEsMjM2XSwibm9uY2UiOlsxNDcsMTU0LDE4OCwxNSwyNDMsMTAxLDQwLDExMCwxOSwxMDQsMjUzLDMsMjE1LDEyOSw0NiwxMDQsMTcsMTQ4LDIxNiwzOSw3LDYwLDIwNiwyMDRdLCJjaXBoZXIiOlsyNDYsNDMsMTgsOTcsMTI4LDMwLDE3OCw4MCwxMzUsMTQ4LDIzNCwxMiwzMSwxNDIsMTc2LDU5LDk5LDI0NSwyMDUsMTQwLDE1NCwyMTksMjA5LDQ5LDE0OCwxMiwxOTUsOTEsMTEyLDIwOCwxMDAsMTc1LDIyMiw2NywxNzAsMzEsMjA0LDUwLDIxMSw0NSwxNyw4NSw0NSw1NSwyNiwzMCwxMzAsNDQsNCw1NiwxMyw5MywzOSwyMzksMTM5LDIyNywyNTAsNywyNTQsNzMsMTQ5LDE1NiwyMDEsMTA3LDc5LDEyNCwyMzIsMCwxNzMsMjEwLDM0LDEsMTIzLDE0MywyNTIsMzgsMTEzLDEyOSwyMTAsMTU2LDIzNSw2Nyw2LDc0LDQ5LDI1MywxNSwyMzMsMTA2LDE0MiwyMjEsNjksMTU3LDI0Nyw5NiwxNzEsMTk5LDE4LDE3LDI0OCwxMzEsMjMyLDkwLDg5LDY4LDEwMiwxODMsMzksMjEwLDI1MCw0NywyMDcsMjMyLDE4NSwzNiwxOTgsMjMsMyw0LDEwOV19"},"root_seed":{"blob_type":"Seed","seed_type":"OneShot","hint":"","data":"eyJzYWx0IjpbOTAsMywxNTgsMTg4LDE2OCwyMTUsMTEwLDQ5LDEyMiwyMjAsNzYsMjE1LDE4OCwxMDcsMTAyLDc5XSwibm9uY2UiOls1NywxMSwxOTEsMTIsOTYsMTg2LDYxLDI0OSwxMDEsMTM0LDEyNywyNDAsMjQ1LDI1NSwxMDIsMTk1LDE1OSwyNTIsNjEsMzAsNTksMTA5LDE1OCwxMTddLCJjaXBoZXIiOlsyNDAsMjQ2LDE0LDE2NywxMTMsMjA5LDY0LDYsMjI5LDYzLDE4Niw5NiwxOTcsNDIsMTMzLDE1MCw0NSw0MSw0OCwyMjcsMjU1LDE3MywyNDIsNzgsOTgsMTc2LDE1OCwyMCwxMjEsOTYsMTc0LDE5NywyMjUsNTQsMTA4LDI0LDcsNDAsMSwxNiwxMDUsOCwyMywxMDcsMjQsMjI4LDExNSwxODddfQ=="}}} -------------------------------------------------------------------------------- /app.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Holochain App Name", 3 | "description": "A Holochain app", 4 | "authors": [ 5 | { 6 | "identifier": "Author Name ", 7 | "public_key_source": "", 8 | "signature": "" 9 | } 10 | ], 11 | "version": "0.1.0", 12 | "dht": {}, 13 | "properties": null 14 | } -------------------------------------------------------------------------------- /cli/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "cli" 3 | version = "0.1.0" 4 | authors = ["willem "] 5 | edition = "2018" 6 | 7 | [dependencies] 8 | structopt = "0.2.15" 9 | reqwest = "0.9.16" 10 | serde_json = "1.0.39" 11 | linefeed = "0.6.0" 12 | -------------------------------------------------------------------------------- /cli/src/main.rs: -------------------------------------------------------------------------------- 1 | use std::io; 2 | use std::iter::repeat; 3 | use std::time::{self, SystemTime, UNIX_EPOCH}; 4 | use std::thread; 5 | use serde_json::json; 6 | use structopt::StructOpt; 7 | use linefeed::{Interface, ReadResult}; 8 | 9 | #[derive(Debug, StructOpt)] 10 | #[structopt(name = "example", about = "An example of StructOpt usage.")] 11 | struct Cli { 12 | /// This is the instance ID in the conductor that is running the game on the given port (e.g gameInstance) 13 | instance: String, 14 | /// Url to connect to the running conductor HTTP port (default: http://localhost:3000) 15 | #[structopt(default_value = "http://localhost:3000")] 16 | url: reqwest::Url, 17 | } 18 | 19 | static COMMANDS: &[(&str, &str)] = &[ 20 | ("help", "Displays this the help page"), 21 | ("join_game", "Set the game to make moves against, usage: join_game "), 22 | ("new_game", "Create a new game to play with an opponent, usage: new_game "), 23 | ("moves", "Display the set of moves this game supports"), 24 | ("make_move", "Make a move in this game, usage: make_move "), 25 | 26 | ("create_proposal", "Publicly publish that you are looking for someone to play with. Usage: post_propoal "), 27 | ("accept_proposal", "Accept a propsal. This will start a new game. Usage: accept_proposal "), 28 | ("get_proposals", "Get all of the public proposals that are current"), 29 | ("check_responses", "Given a proposal hash find the responses. Usage: check_responses "), 30 | ("remove_proposal", "Remove a proposal that you authored given its hash. Usage: remove_proposal "), 31 | 32 | ("exit", "Exit this CLI. Holochain will persist state so games can be resumed later."), 33 | ]; 34 | 35 | fn main() -> io::Result<()> { 36 | let cli = Cli::from_args(); 37 | 38 | // create the functions required for playing the game 39 | let whoami = holochain_call_generator(cli.url.clone(), cli.instance.clone(), "main".into(), "whoami".into()); 40 | let valid_moves = holochain_call_generator(cli.url.clone(), cli.instance.clone(), "main".into(), "get_valid_moves".into()); 41 | let make_move = holochain_call_generator(cli.url.clone(), cli.instance.clone(), "main".into(), "make_move".into()); 42 | let create_game = holochain_call_generator(cli.url.clone(), cli.instance.clone(), "main".into(), "create_game".into()); 43 | let render_game = holochain_call_generator(cli.url.clone(), cli.instance.clone(), "main".into(), "render_state".into()); 44 | 45 | // matchmaking funcs 46 | let create_proposal = holochain_call_generator(cli.url.clone(), cli.instance.clone(), "main".into(), "create_proposal".into()); 47 | let get_proposals = holochain_call_generator(cli.url.clone(), cli.instance.clone(), "main".into(), "get_proposals".into()); 48 | let accept_proposal = holochain_call_generator(cli.url.clone(), cli.instance.clone(), "main".into(), "accept_proposal".into()); 49 | let check_responses = holochain_call_generator(cli.url.clone(), cli.instance.clone(), "main".into(), "check_responses".into()); 50 | let _remove_proposal = holochain_call_generator(cli.url.clone(), cli.instance.clone(), "main".into(), "remove_proposal".into()); 51 | 52 | 53 | let interface = Interface::new("Holochain generic game")?; 54 | 55 | println!(""); 56 | println!(""); 57 | println!("{}", repeat('#').take(70).collect::()); 58 | println!("CLI interface for games written using the Holochain Generic Game framework."); 59 | println!("Enter \"help\" for a list of commands."); 60 | println!("Use \"create_game \" or \"join_game \" to start or join a game."); 61 | println!("Press Ctrl-D or enter \"quit\" to exit."); 62 | println!("{}", repeat('#').take(70).collect::()); 63 | println!(""); 64 | println!(""); 65 | 66 | match whoami(json!({})) { 67 | Ok(agent_addr) => { 68 | println!("Your agent address is {}\n\nSend this to other players so they can invite you to a game.", agent_addr); 69 | }, 70 | Err(_e) => { 71 | println!("No holochain instance named {} running on {}. Check the conductor is running and the instanceId in the conductor config is correct.", cli.instance, cli.url); 72 | return Ok(()); 73 | } 74 | } 75 | 76 | println!(""); 77 | println!(""); 78 | 79 | interface.set_prompt("No game> ")?; 80 | 81 | let mut current_game: Option = None; 82 | 83 | while let ReadResult::Input(line) = interface.read_line()? { 84 | 85 | if !line.trim().is_empty() { 86 | interface.add_history_unique(line.clone()); 87 | } 88 | 89 | let (cmd, args) = split_first_word(&line); 90 | 91 | let result: Result<(), String> = match cmd { 92 | "help" => { 93 | println!("Holochain generic game commands:"); 94 | println!(); 95 | for &(cmd, help) in COMMANDS { 96 | println!(" {:15} - {}", cmd, help); 97 | println!(); 98 | } 99 | println!(); 100 | Ok(()) 101 | } 102 | "join_game" => { 103 | if is_hash(args) { 104 | println!("Setting current game hash to {}", args); 105 | current_game = Some(args.into()); 106 | Ok(()) 107 | } else { 108 | Err("argument must be a valid address".into()) 109 | } 110 | } 111 | "new_game" => { 112 | if is_agent_addr(args) { 113 | let result = create_game(json!({ 114 | "opponent": args, 115 | "timestamp": current_timestamp() 116 | })); 117 | result.map(|result| { 118 | current_game = result.as_str().map(|s| s.to_string()); 119 | }) 120 | } else { 121 | Err("argument must be valid agent address of an opponent.".into()) 122 | } 123 | } 124 | "moves" => { 125 | valid_moves(json!({})).map(|result| { 126 | println!("The valid moves are:"); 127 | result.as_array().unwrap() 128 | .iter() 129 | .for_each(|elem| { 130 | println!("- {}", elem); 131 | }); 132 | println!(); 133 | }) 134 | }, 135 | "make_move" => { 136 | if let Some(current_game) = current_game.clone() { 137 | let move_json: serde_json::Value = serde_json::from_str(args).unwrap_or(serde_json::Value::Null); 138 | println!("making move: {:?}", args); 139 | make_move(json!({ 140 | "new_move": { 141 | "game": current_game, 142 | "move_type": move_json, 143 | "timestamp": current_timestamp() 144 | } 145 | })).map(|_| { 146 | println!("Move cast successfully"); 147 | println!("Waiting for gossip..."); 148 | // wait a bit so it displays correctly 149 | thread::sleep(time::Duration::from_millis(4000)); 150 | println!("OK!") 151 | }) 152 | } 153 | else { 154 | Err("No game set to make moves on. use the \"join_game\" command.".into()) 155 | } 156 | }, 157 | "create_proposal" => { 158 | println!("creating proposal with message {:?}", args); 159 | let result = create_proposal(json!({"message": args})); 160 | println!("Create result: {:?}", result); 161 | Ok(()) 162 | }, 163 | "get_proposals" => { 164 | let result = get_proposals(json!({})).unwrap(); 165 | println!("Current game proposals: \n"); 166 | result.as_array().unwrap().iter().for_each(|r| { 167 | println!("[{}] : {{ Agent: {}, Message: {} }}", r["address"].as_str().unwrap(), r["entry"]["agent"], r["entry"]["message"]); 168 | }); 169 | println!("\n"); 170 | Ok(()) 171 | }, 172 | "accept_proposal" => { 173 | accept_proposal(json!({"proposal_addr": args, "created_at": current_timestamp()})).map(|game_addr| { 174 | println!("Proposal accepted. Game created with address: {}", game_addr); 175 | current_game = Some(game_addr.as_str().unwrap().into()); 176 | }) 177 | }, 178 | "check_responses" => { 179 | let result = check_responses(json!({"proposal_addr": args})).unwrap(); 180 | println!("Proposal has the following responses: \n"); 181 | result.as_array().unwrap().iter().for_each(|response| { 182 | println!("[{}] : Agent: {}", response["address"], response["entry"]["player_1"]); 183 | }); 184 | println!("use \"join_game\" with any of the listed addresses to join: \n"); 185 | Ok(()) 186 | }, 187 | "remove_proposal" => { 188 | println!("NOT IMPLEMENTED - this has been disabled until deletion but is fixed."); 189 | Ok(()) 190 | // remove_proposal(json!({"proposal_addr": args})).map(|_| { 191 | // println!("Proposal successfully marked as deleted\n"); 192 | // }) 193 | }, 194 | "exit" => { 195 | if let Some(current_game) = current_game.clone() { 196 | println!("You can resume this game at a later date by using:\n\"join_game {}\"", current_game); 197 | } 198 | println!("Bye!"); 199 | break 200 | } 201 | _ => { 202 | Err("Invalid command!".into()) 203 | } 204 | }; 205 | 206 | if let Err(e) = result { 207 | println!("Error: {}", e) 208 | } 209 | 210 | if let Some(current_game_string) = current_game.clone() { 211 | interface.set_prompt(&format!("{}> ", current_game_string))?; 212 | match render_game(json!({"game_address": current_game_string.clone()})) { 213 | Ok(render_result) => { 214 | println!("{}", render_result.as_str().unwrap()); 215 | }, 216 | Err(_e) => { 217 | println!("No game is currently visible with that address."); 218 | current_game = None; 219 | } 220 | } 221 | } 222 | } 223 | Ok(()) 224 | } 225 | 226 | 227 | /** 228 | * Returns functions to make calls to a particular zome function on a url 229 | */ 230 | fn holochain_call_generator( 231 | url: reqwest::Url, 232 | instance: String, 233 | zome: String, 234 | func: String, 235 | ) -> Box Result> { 236 | 237 | let client = reqwest::Client::new(); 238 | 239 | let make_rpc_call = move |params| { 240 | json!({ 241 | "jsonrpc": "2.0", 242 | "id": 0, 243 | "method": "call", 244 | "params": { 245 | "instance_id": instance, 246 | "zome": zome, 247 | "function": func, 248 | "args": params 249 | } 250 | }) 251 | }; 252 | 253 | Box::new(move |params| { 254 | let call_result: serde_json::Value = client.post(url.clone()) 255 | .json(&make_rpc_call(params)) 256 | .send().map_err(|e| e.to_string())? 257 | .json() 258 | .map(|r: serde_json::Value| { 259 | r["result"].clone() 260 | }) 261 | .map(|s| serde_json::from_str( 262 | s.as_str().expect(&format!("Holochain did not return a string result: {}", s)) 263 | ).expect(&format!("Holochain did not return a valid stringified JSON result: {}", s))) 264 | .map_err(|e| e.to_string())?; 265 | 266 | // deal with the json encoded holochain error responses 267 | if let Some(inner_result) = call_result.get("Ok") { 268 | Ok(inner_result.clone()) 269 | } else { 270 | Err(call_result["Err"].to_string()) 271 | } 272 | }) 273 | 274 | } 275 | 276 | /*=============================== 277 | = Helpers = 278 | ===============================*/ 279 | 280 | fn split_first_word(s: &str) -> (&str, &str) { 281 | let s = s.trim(); 282 | 283 | match s.find(|ch: char| ch.is_whitespace()) { 284 | Some(pos) => (&s[..pos], s[pos..].trim_start()), 285 | None => (s, "") 286 | } 287 | } 288 | 289 | fn is_hash(s: &str) -> bool { 290 | s.starts_with("Qm") && s.len() == 46 291 | } 292 | 293 | fn is_agent_addr(s: &str) -> bool { 294 | s.starts_with("Hc") && s.len() == 63 295 | } 296 | 297 | fn current_timestamp() -> u32 { 298 | SystemTime::now().duration_since(UNIX_EPOCH).unwrap().as_secs() as u32 299 | } 300 | 301 | /*===== End of Helpers ======*/ 302 | -------------------------------------------------------------------------------- /conductor-config.toml: -------------------------------------------------------------------------------- 1 | [[dnas]] 2 | id = "game-dna" 3 | file = "./dist/generic-game.dna.json" 4 | hash = "QmUnxGv8p8PG71AWh88sNztxndyKK9WUiy94S29RU7jhg7" 5 | 6 | 7 | [[agents]] 8 | id = "agent1" 9 | name = "agent1" 10 | public_address = "HcScjcgKqXC5pmfvka9DmtEJwVr548yd86UPtJGGoue9ynuikuRTN7oE5zcjgbi" 11 | keystore_file = "./agent1.keystore" 12 | 13 | [[agents]] 14 | id = "agent2" 15 | name = "agent2" 16 | public_address = "HcScidPSdAT43q9qirJwt5rHJYjjsvougV3jgSBwdJujszw3bBu5Mktr74Rgnea" 17 | keystore_file = "./agent2.keystore" 18 | 19 | 20 | [[instances]] 21 | id = "instance1" 22 | dna = "game-dna" 23 | agent = "agent1" 24 | [instances.storage] 25 | type = "memory" 26 | path = "tmp-storage" 27 | 28 | [[instances]] 29 | id = "instance2" 30 | dna = "game-dna" 31 | agent = "agent2" 32 | [instances.storage] 33 | type = "memory" 34 | path = "tmp-storage" 35 | 36 | 37 | [[interfaces]] 38 | id = "http-interface1" 39 | [interfaces.driver] 40 | type = "http" 41 | port = 3000 42 | [[interfaces.instances]] 43 | id = "instance1" 44 | [[interfaces.instances]] 45 | id = "instance2" 46 | -------------------------------------------------------------------------------- /demo-moves.txt: -------------------------------------------------------------------------------- 1 | ####### setup ######## 2 | 3 | # start conductor (in own terminal) 4 | $> nix-shell https://github.com/holochain/holonix/tarball/0.0.22 5 | nix-shell> holochain -c ./conductor-config.toml 6 | 7 | # connect CLI as agent A (in own terminal, requires Rust install) 8 | A> cd cli 9 | A> cargo run instance1 10 | 11 | # collect CLI as agent B (in own terminal) 12 | B> cd cli 13 | B> cargo run instance2 14 | 15 | 16 | ####### Playing a game ######## 17 | 18 | # create a game and invite agent B (copy agent address of B) 19 | A> new_game HcScidPSdAT43q9qirJwt5rHJYjjsvougV3jgSBwdJujszw3bBu5Mktr74Rgnea 20 | 21 | # accept the game (copy hash from created game) 22 | B> join_game QmSdUBkztZqU5EPoLpoaRqzLMEkHr7HvbBRWsfKqwQ1eA8 23 | 24 | # list the moves and make a first move 25 | B> moves 26 | B> make_move {"MovePiece":{"from":{"x":1,"y":5},"to":{"x":2,"y":4}}} 27 | 28 | # show B can't move now because it is not their turn 29 | B> make_move {"MovePiece":{"from":{"x":3,"y":5},"to":{"x":4,"y":4}}} 30 | 31 | # Make move as A 32 | 33 | A> make_move {"MovePiece":{"from":{"x":4,"y":2},"to":{"x":3,"y":3}}} 34 | 35 | # Make invalid move as B to show validation 36 | 37 | B> make_move {"MovePiece":{"from":{"x":2,"y":4},"to":{"x":3,"y":3}}} 38 | 39 | # make correct move as B, hop to take piece 40 | B> make_move {"MovePiece":{"from":{"x":2,"y":4},"to":{"x":4,"y":2}}} 41 | 42 | ## You can kind of freestyle it from here. Note there are no win conditions programmed in yet so although you can all the pieces the game never ends -------------------------------------------------------------------------------- /generic-game-diagram.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/holochain-devcamp/generic-game/98d548a5fb8334683db9158cf6b63c3a79cd4121/generic-game-diagram.pdf -------------------------------------------------------------------------------- /test/checkers.js: -------------------------------------------------------------------------------- 1 | const {results, lastResult, makeMove, createGame, renderState, getState} = require('./helpers') 2 | 3 | module.exports = (scenario) => { 4 | scenario("Can create a new game of checkers and make a move", async (s, t, { alice, bob }) => { 5 | 6 | const whoamiResult = await alice.callSync("main", "whoami", {}) 7 | console.log(whoamiResult) 8 | t.equal(whoamiResult.Ok.length, 63) 9 | 10 | let game_address = await createGame(alice, bob); 11 | 12 | // agent 2 must go first 13 | await makeMove(bob, { 14 | game: game_address, 15 | timestamp: 0, 16 | move_type: {MovePiece: { from: {x: 1, y: 5}, to: {x: 0, y: 4} }}, 17 | }) 18 | t.notEqual(lastResult().Ok, undefined, "Bob made the first move") 19 | 20 | await renderState(alice, game_address) 21 | 22 | await makeMove(alice, { 23 | game: game_address, 24 | timestamp: 1, 25 | move_type: {MovePiece: { from: {x: 0, y: 2}, to: {x: 1, y: 3} }}, 26 | }) 27 | console.log(lastResult()) 28 | t.notEqual(lastResult().Ok, undefined, "Alice made the second move") 29 | 30 | await renderState(alice, game_address) 31 | 32 | await makeMove(bob, { 33 | game: game_address, 34 | timestamp: 2, 35 | move_type: {MovePiece: { from: {x: 5, y: 5}, to: {x: 6, y: 4} }}, 36 | }) 37 | t.notEqual(lastResult().Ok, undefined, "Bob made the third move") 38 | 39 | let state = await getState(alice, game_address) 40 | 41 | t.equal(state.Ok.moves.length, 3, "There were three moves in the game") 42 | 43 | // both agents should see the same game state 44 | t.deepEqual(await getState(bob, game_address), await getState(alice, game_address), "Alice and Bob both see the same game state") 45 | 46 | 47 | // finally print all the outputs 48 | results.forEach((result, i) => { 49 | console.log(`${i}: ${JSON.stringify(result, null, 2)}\n`) 50 | }) 51 | 52 | }) 53 | } 54 | -------------------------------------------------------------------------------- /test/helpers.js: -------------------------------------------------------------------------------- 1 | 2 | /** 3 | * Collection of functions to abstract the Holochain calls 4 | * and make the testing code cleaner. 5 | */ 6 | 7 | let results = [] 8 | 9 | module.exports = { 10 | results: results, 11 | lastResult: (back=0) => results[results.length-1-back], 12 | makeMove: async (agent, game_move) => { 13 | const result = await agent.callSync("main", "make_move", { new_move: game_move }) 14 | results.push(result) 15 | return result 16 | }, 17 | createGame: async (agent, opponent) => { 18 | const result = await agent.callSync("main", "create_game", { opponent: opponent.agentId, timestamp: 0 }) 19 | results.push(result) 20 | return result.Ok 21 | }, 22 | renderState: async (agent, game_address) => { 23 | const result = await agent.callSync("main", "render_state", { game_address }) 24 | console.log(result.Ok) 25 | }, 26 | getState: async (agent, game_address) => { 27 | const result = await agent.callSync("main", "get_state", { game_address }) 28 | results.push(result) 29 | return result 30 | }, 31 | } 32 | -------------------------------------------------------------------------------- /test/index.js: -------------------------------------------------------------------------------- 1 | const path = require('path') 2 | const tape = require('tape') 3 | 4 | const { Diorama, tapeExecutor, backwardCompatibilityMiddleware } = require('@holochain/diorama') 5 | 6 | process.on('unhandledRejection', error => { 7 | // Will print "unhandledRejection err is not defined" 8 | console.error('got unhandledRejection:', error); 9 | }); 10 | 11 | const dnaPath = path.join(__dirname, "../dist/generic-game.dna.json") 12 | const dna = Diorama.dna(dnaPath, 'generic-game') 13 | 14 | const diorama = new Diorama({ 15 | instances: { 16 | alice: dna, 17 | bob: dna, 18 | }, 19 | bridges: [], 20 | debugLog: false, 21 | executor: tapeExecutor(require('tape')), 22 | middleware: backwardCompatibilityMiddleware, 23 | }) 24 | 25 | // uncomment one of these 26 | // require('./tictactoe')(diorama.registerScenario) 27 | require('./checkers')(diorama.registerScenario) 28 | 29 | // test the matchmaking 30 | // require('./matchmaking')(diorama.registerScenario) 31 | 32 | 33 | diorama.run() 34 | -------------------------------------------------------------------------------- /test/matchmaking.js: -------------------------------------------------------------------------------- 1 | 2 | module.exports = (scenario)=> { 3 | scenario("Bob can accept Alices proposal, create a game and Alice can see the game", async (s, t, { alice, bob }) => { 4 | const addr = await alice.callSync("main", "create_proposal", {message : "sup"}) 5 | t.equal(addr.Ok.length, 46, "Proposal was created successfully") 6 | 7 | const proposals = await bob.callSync("main", "get_proposals", {}) 8 | console.log(proposals) 9 | t.equal(proposals.Ok.length, 1, "Bob could retrieve Alices Proposal") 10 | 11 | const acceptance = await bob.callSync("main", "accept_proposal", { proposal_addr: proposals.Ok[0].address, created_at: 0 }) 12 | t.notEqual(acceptance.Ok, undefined, "Bob could accept the proposal by creating a game") // check it returned Ok 13 | 14 | const games = await bob.callSync("main", "check_responses", { proposal_addr: proposals.Ok[0].address }) 15 | t.deepEqual( 16 | games.Ok, 17 | [{ 18 | entry: { 19 | player_1: bob.agentId, 20 | player_2: alice.agentId, 21 | created_at: 0 22 | }, 23 | address: games.Ok[0].address 24 | }], 25 | "The game was created as expected" 26 | ) 27 | }) 28 | } 29 | -------------------------------------------------------------------------------- /test/package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "requires": true, 3 | "lockfileVersion": 1, 4 | "dependencies": { 5 | "101": { 6 | "version": "1.6.3", 7 | "resolved": "https://registry.npmjs.org/101/-/101-1.6.3.tgz", 8 | "integrity": "sha512-4dmQ45yY0Dx24Qxp+zAsNLlMF6tteCyfVzgbulvSyC7tCyd3V8sW76sS0tHq8NpcbXfWTKasfyfzU1Kd86oKzw==", 9 | "requires": { 10 | "clone": "^1.0.2", 11 | "deep-eql": "^0.1.3", 12 | "keypather": "^1.10.2" 13 | } 14 | }, 15 | "@babel/runtime": { 16 | "version": "7.4.5", 17 | "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.4.5.tgz", 18 | "integrity": "sha512-TuI4qpWZP6lGOGIuGWtp9sPluqYICmbk8T/1vpSysqJxRPkudh/ofFWyqdcMsDf2s7KvDL4/YHgKyvcS3g9CJQ==", 19 | "requires": { 20 | "regenerator-runtime": "^0.13.2" 21 | } 22 | }, 23 | "@holochain/diorama": { 24 | "version": "0.1.2", 25 | "resolved": "https://registry.npmjs.org/@holochain/diorama/-/diorama-0.1.2.tgz", 26 | "integrity": "sha512-xku1Vhu8zQIaKG+QA0X8KaR3S8iqxmnw4/zId6A6wXJQNONyVKIR6YwVeRSIl9tppXkTaFPDcb4j86mGjBT9AQ==", 27 | "requires": { 28 | "@holochain/hachiko": "^0.1.0-rc4", 29 | "@holochain/hc-web-client": "^0.5.0", 30 | "colors": "^1.3.3", 31 | "del": "^4.1.1", 32 | "get-port": "^5.0.0", 33 | "winston": "^3.2.1", 34 | "winston-null": "^2.0.0" 35 | } 36 | }, 37 | "@holochain/hachiko": { 38 | "version": "0.1.0", 39 | "resolved": "https://registry.npmjs.org/@holochain/hachiko/-/hachiko-0.1.0.tgz", 40 | "integrity": "sha512-r4dFGALyGjl5kUGDwmdOsEdUcz+DU0oDI3NsxRWO3/Ve3/ditXYosc7LRqj3TGqmEp3FBIEkJLt/nRPtvFgEhA==", 41 | "requires": { 42 | "colors": "^1.3.3", 43 | "lodash": "^4.17.11", 44 | "winston": "^3.2.1", 45 | "winston-null": "^2.0.0" 46 | } 47 | }, 48 | "@holochain/hc-web-client": { 49 | "version": "0.5.0", 50 | "resolved": "https://registry.npmjs.org/@holochain/hc-web-client/-/hc-web-client-0.5.0.tgz", 51 | "integrity": "sha512-p+Q3rRg+ODfW2w65SteeiPL3Do43rWztioC1V05tiPvzDaupUUMtf7mXOoLwYFoqYnHYvbMsTBPIh7RpSt907w==", 52 | "requires": { 53 | "isomorphic-fetch": "^2.2.1", 54 | "rpc-websockets": "^4.3.3" 55 | } 56 | }, 57 | "@types/events": { 58 | "version": "3.0.0", 59 | "resolved": "https://registry.npmjs.org/@types/events/-/events-3.0.0.tgz", 60 | "integrity": "sha512-EaObqwIvayI5a8dCzhFrjKzVwKLxjoG9T6Ppd5CEo07LRKfQ8Yokw54r5+Wq7FaBQ+yXRvQAYPrHwya1/UFt9g==" 61 | }, 62 | "@types/glob": { 63 | "version": "7.1.1", 64 | "resolved": "https://registry.npmjs.org/@types/glob/-/glob-7.1.1.tgz", 65 | "integrity": "sha512-1Bh06cbWJUHMC97acuD6UMG29nMt0Aqz1vF3guLfG+kHHJhy3AyohZFFxYk2f7Q1SQIrNwvncxAE0N/9s70F2w==", 66 | "requires": { 67 | "@types/events": "*", 68 | "@types/minimatch": "*", 69 | "@types/node": "*" 70 | } 71 | }, 72 | "@types/minimatch": { 73 | "version": "3.0.3", 74 | "resolved": "https://registry.npmjs.org/@types/minimatch/-/minimatch-3.0.3.tgz", 75 | "integrity": "sha512-tHq6qdbT9U1IRSGf14CL0pUlULksvY9OZ+5eEgl1N7t+OA3tGvNpxJCzuKQlsNgCVwbAs670L1vcVQi8j9HjnA==" 76 | }, 77 | "@types/node": { 78 | "version": "12.0.8", 79 | "resolved": "https://registry.npmjs.org/@types/node/-/node-12.0.8.tgz", 80 | "integrity": "sha512-b8bbUOTwzIY3V5vDTY1fIJ+ePKDUBqt2hC2woVGotdQQhG/2Sh62HOKHrT7ab+VerXAcPyAiTEipPu/FsreUtg==" 81 | }, 82 | "array-union": { 83 | "version": "1.0.2", 84 | "resolved": "https://registry.npmjs.org/array-union/-/array-union-1.0.2.tgz", 85 | "integrity": "sha1-mjRBDk9OPaI96jdb5b5w8kd47Dk=", 86 | "requires": { 87 | "array-uniq": "^1.0.1" 88 | } 89 | }, 90 | "array-uniq": { 91 | "version": "1.0.3", 92 | "resolved": "https://registry.npmjs.org/array-uniq/-/array-uniq-1.0.3.tgz", 93 | "integrity": "sha1-r2rId6Jcx/dOBYiUdThY39sk/bY=" 94 | }, 95 | "assert-args": { 96 | "version": "1.2.1", 97 | "resolved": "https://registry.npmjs.org/assert-args/-/assert-args-1.2.1.tgz", 98 | "integrity": "sha1-QEEDoUUqMv53iYgR5U5ZCoqTc70=", 99 | "requires": { 100 | "101": "^1.2.0", 101 | "compound-subject": "0.0.1", 102 | "debug": "^2.2.0", 103 | "get-prototype-of": "0.0.0", 104 | "is-capitalized": "^1.0.0", 105 | "is-class": "0.0.4" 106 | } 107 | }, 108 | "async": { 109 | "version": "2.6.2", 110 | "resolved": "https://registry.npmjs.org/async/-/async-2.6.2.tgz", 111 | "integrity": "sha512-H1qVYh1MYhEEFLsP97cVKqCGo7KfCyTt6uEWqsTBr9SO84oK9Uwbyd/yCW+6rKJLHksBNUVWZDAjfS+Ccx0Bbg==", 112 | "requires": { 113 | "lodash": "^4.17.11" 114 | } 115 | }, 116 | "async-limiter": { 117 | "version": "1.0.0", 118 | "resolved": "https://registry.npmjs.org/async-limiter/-/async-limiter-1.0.0.tgz", 119 | "integrity": "sha512-jp/uFnooOiO+L211eZOoSyzpOITMXx1rBITauYykG3BRYPu8h0UcxsPNB04RR5vo4Tyz3+ay17tR6JVf9qzYWg==" 120 | }, 121 | "babel-runtime": { 122 | "version": "6.26.0", 123 | "resolved": "https://registry.npmjs.org/babel-runtime/-/babel-runtime-6.26.0.tgz", 124 | "integrity": "sha1-llxwWGaOgrVde/4E/yM3vItWR/4=", 125 | "requires": { 126 | "core-js": "^2.4.0", 127 | "regenerator-runtime": "^0.11.0" 128 | }, 129 | "dependencies": { 130 | "regenerator-runtime": { 131 | "version": "0.11.1", 132 | "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.11.1.tgz", 133 | "integrity": "sha512-MguG95oij0fC3QV3URf4V2SDYGJhJnJGqvIIgdECeODCT98wSWDAJ94SSuVpYQUoTcGUIL6L4yNB7j1DFFHSBg==" 134 | } 135 | } 136 | }, 137 | "balanced-match": { 138 | "version": "1.0.0", 139 | "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", 140 | "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=" 141 | }, 142 | "brace-expansion": { 143 | "version": "1.1.11", 144 | "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", 145 | "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", 146 | "requires": { 147 | "balanced-match": "^1.0.0", 148 | "concat-map": "0.0.1" 149 | } 150 | }, 151 | "circular-json": { 152 | "version": "0.5.9", 153 | "resolved": "https://registry.npmjs.org/circular-json/-/circular-json-0.5.9.tgz", 154 | "integrity": "sha512-4ivwqHpIFJZBuhN3g/pEcdbnGUywkBblloGbkglyloVjjR3uT6tieI89MVOfbP2tHX5sgb01FuLgAOzebNlJNQ==" 155 | }, 156 | "clone": { 157 | "version": "1.0.4", 158 | "resolved": "https://registry.npmjs.org/clone/-/clone-1.0.4.tgz", 159 | "integrity": "sha1-2jCcwmPfFZlMaIypAheco8fNfH4=" 160 | }, 161 | "color": { 162 | "version": "3.0.0", 163 | "resolved": "https://registry.npmjs.org/color/-/color-3.0.0.tgz", 164 | "integrity": "sha512-jCpd5+s0s0t7p3pHQKpnJ0TpQKKdleP71LWcA0aqiljpiuAkOSUFN/dyH8ZwF0hRmFlrIuRhufds1QyEP9EB+w==", 165 | "requires": { 166 | "color-convert": "^1.9.1", 167 | "color-string": "^1.5.2" 168 | } 169 | }, 170 | "color-convert": { 171 | "version": "1.9.3", 172 | "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", 173 | "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", 174 | "requires": { 175 | "color-name": "1.1.3" 176 | } 177 | }, 178 | "color-name": { 179 | "version": "1.1.3", 180 | "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", 181 | "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=" 182 | }, 183 | "color-string": { 184 | "version": "1.5.3", 185 | "resolved": "https://registry.npmjs.org/color-string/-/color-string-1.5.3.tgz", 186 | "integrity": "sha512-dC2C5qeWoYkxki5UAXapdjqO672AM4vZuPGRQfO8b5HKuKGBbKWpITyDYN7TOFKvRW7kOgAn3746clDBMDJyQw==", 187 | "requires": { 188 | "color-name": "^1.0.0", 189 | "simple-swizzle": "^0.2.2" 190 | } 191 | }, 192 | "colornames": { 193 | "version": "1.1.1", 194 | "resolved": "https://registry.npmjs.org/colornames/-/colornames-1.1.1.tgz", 195 | "integrity": "sha1-+IiQMGhcfE/54qVZ9Qd+t2qBb5Y=" 196 | }, 197 | "colors": { 198 | "version": "1.3.3", 199 | "resolved": "https://registry.npmjs.org/colors/-/colors-1.3.3.tgz", 200 | "integrity": "sha512-mmGt/1pZqYRjMxB1axhTo16/snVZ5krrKkcmMeVKxzECMMXoCgnvTPp10QgHfcbQZw8Dq2jMNG6je4JlWU0gWg==" 201 | }, 202 | "colorspace": { 203 | "version": "1.1.2", 204 | "resolved": "https://registry.npmjs.org/colorspace/-/colorspace-1.1.2.tgz", 205 | "integrity": "sha512-vt+OoIP2d76xLhjwbBaucYlNSpPsrJWPlBTtwCpQKIu6/CSMutyzX93O/Do0qzpH3YoHEes8YEFXyZ797rEhzQ==", 206 | "requires": { 207 | "color": "3.0.x", 208 | "text-hex": "1.0.x" 209 | } 210 | }, 211 | "compound-subject": { 212 | "version": "0.0.1", 213 | "resolved": "https://registry.npmjs.org/compound-subject/-/compound-subject-0.0.1.tgz", 214 | "integrity": "sha1-JxVUaYoVrmCLHfyv0wt7oeqJLEs=" 215 | }, 216 | "concat-map": { 217 | "version": "0.0.1", 218 | "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", 219 | "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=" 220 | }, 221 | "core-js": { 222 | "version": "2.6.9", 223 | "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.6.9.tgz", 224 | "integrity": "sha512-HOpZf6eXmnl7la+cUdMnLvUxKNqLUzJvgIziQ0DiF3JwSImNphIqdGqzj6hIKyX04MmV0poclQ7+wjWvxQyR2A==" 225 | }, 226 | "core-util-is": { 227 | "version": "1.0.2", 228 | "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", 229 | "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=" 230 | }, 231 | "cycle": { 232 | "version": "1.0.3", 233 | "resolved": "https://registry.npmjs.org/cycle/-/cycle-1.0.3.tgz", 234 | "integrity": "sha1-IegLK+hYD5i0aPN5QwZisEbDStI=" 235 | }, 236 | "debug": { 237 | "version": "2.6.9", 238 | "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", 239 | "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", 240 | "requires": { 241 | "ms": "2.0.0" 242 | }, 243 | "dependencies": { 244 | "ms": { 245 | "version": "2.0.0", 246 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", 247 | "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" 248 | } 249 | } 250 | }, 251 | "deep-eql": { 252 | "version": "0.1.3", 253 | "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-0.1.3.tgz", 254 | "integrity": "sha1-71WKyrjeJSBs1xOQbXTlaTDrafI=", 255 | "requires": { 256 | "type-detect": "0.1.1" 257 | } 258 | }, 259 | "deep-equal": { 260 | "version": "0.1.2", 261 | "resolved": "https://registry.npmjs.org/deep-equal/-/deep-equal-0.1.2.tgz", 262 | "integrity": "sha1-skbCuApXCkfBG+HZvRBw7IeLh84=" 263 | }, 264 | "define-properties": { 265 | "version": "1.1.3", 266 | "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz", 267 | "integrity": "sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ==", 268 | "requires": { 269 | "object-keys": "^1.0.12" 270 | }, 271 | "dependencies": { 272 | "object-keys": { 273 | "version": "1.1.1", 274 | "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", 275 | "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==" 276 | } 277 | } 278 | }, 279 | "defined": { 280 | "version": "0.0.0", 281 | "resolved": "https://registry.npmjs.org/defined/-/defined-0.0.0.tgz", 282 | "integrity": "sha1-817qfXBekzuvE7LwOz+D2SFAOz4=" 283 | }, 284 | "del": { 285 | "version": "4.1.1", 286 | "resolved": "https://registry.npmjs.org/del/-/del-4.1.1.tgz", 287 | "integrity": "sha512-QwGuEUouP2kVwQenAsOof5Fv8K9t3D8Ca8NxcXKrIpEHjTXK5J2nXLdP+ALI1cgv8wj7KuwBhTwBkOZSJKM5XQ==", 288 | "requires": { 289 | "@types/glob": "^7.1.1", 290 | "globby": "^6.1.0", 291 | "is-path-cwd": "^2.0.0", 292 | "is-path-in-cwd": "^2.0.0", 293 | "p-map": "^2.0.0", 294 | "pify": "^4.0.1", 295 | "rimraf": "^2.6.3" 296 | } 297 | }, 298 | "diagnostics": { 299 | "version": "1.1.1", 300 | "resolved": "https://registry.npmjs.org/diagnostics/-/diagnostics-1.1.1.tgz", 301 | "integrity": "sha512-8wn1PmdunLJ9Tqbx+Fx/ZEuHfJf4NKSN2ZBj7SJC/OWRWha843+WsTjqMe1B5E3p28jqBlp+mJ2fPVxPyNgYKQ==", 302 | "requires": { 303 | "colorspace": "1.1.x", 304 | "enabled": "1.0.x", 305 | "kuler": "1.0.x" 306 | } 307 | }, 308 | "duplexer": { 309 | "version": "0.1.1", 310 | "resolved": "https://registry.npmjs.org/duplexer/-/duplexer-0.1.1.tgz", 311 | "integrity": "sha1-rOb/gIwc5mtX0ev5eXessCM0z8E=" 312 | }, 313 | "enabled": { 314 | "version": "1.0.2", 315 | "resolved": "https://registry.npmjs.org/enabled/-/enabled-1.0.2.tgz", 316 | "integrity": "sha1-ll9lE9LC0cX0ZStkouM5ZGf8L5M=", 317 | "requires": { 318 | "env-variable": "0.0.x" 319 | } 320 | }, 321 | "encoding": { 322 | "version": "0.1.12", 323 | "resolved": "https://registry.npmjs.org/encoding/-/encoding-0.1.12.tgz", 324 | "integrity": "sha1-U4tm8+5izRq1HsMjgp0flIDHS+s=", 325 | "requires": { 326 | "iconv-lite": "~0.4.13" 327 | } 328 | }, 329 | "env-variable": { 330 | "version": "0.0.5", 331 | "resolved": "https://registry.npmjs.org/env-variable/-/env-variable-0.0.5.tgz", 332 | "integrity": "sha512-zoB603vQReOFvTg5xMl9I1P2PnHsHQQKTEowsKKD7nseUfJq6UWzK+4YtlWUO1nhiQUxe6XMkk+JleSZD1NZFA==" 333 | }, 334 | "es-abstract": { 335 | "version": "1.13.0", 336 | "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.13.0.tgz", 337 | "integrity": "sha512-vDZfg/ykNxQVwup/8E1BZhVzFfBxs9NqMzGcvIJrqg5k2/5Za2bWo40dK2J1pgLngZ7c+Shh8lwYtLGyrwPutg==", 338 | "requires": { 339 | "es-to-primitive": "^1.2.0", 340 | "function-bind": "^1.1.1", 341 | "has": "^1.0.3", 342 | "is-callable": "^1.1.4", 343 | "is-regex": "^1.0.4", 344 | "object-keys": "^1.0.12" 345 | }, 346 | "dependencies": { 347 | "object-keys": { 348 | "version": "1.1.1", 349 | "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", 350 | "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==" 351 | } 352 | } 353 | }, 354 | "es-to-primitive": { 355 | "version": "1.2.0", 356 | "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.0.tgz", 357 | "integrity": "sha512-qZryBOJjV//LaxLTV6UC//WewneB3LcXOL9NP++ozKVXsIIIpm/2c13UDiD9Jp2eThsecw9m3jPqDwTyobcdbg==", 358 | "requires": { 359 | "is-callable": "^1.1.4", 360 | "is-date-object": "^1.0.1", 361 | "is-symbol": "^1.0.2" 362 | } 363 | }, 364 | "eventemitter3": { 365 | "version": "3.1.2", 366 | "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-3.1.2.tgz", 367 | "integrity": "sha512-tvtQIeLVHjDkJYnzf2dgVMxfuSGJeM/7UCG17TT4EumTfNtF+0nebF/4zWOIkCreAbtNqhGEboB6BWrwqNaw4Q==" 368 | }, 369 | "fast-safe-stringify": { 370 | "version": "2.0.6", 371 | "resolved": "https://registry.npmjs.org/fast-safe-stringify/-/fast-safe-stringify-2.0.6.tgz", 372 | "integrity": "sha512-q8BZ89jjc+mz08rSxROs8VsrBBcn1SIw1kq9NjolL509tkABRk9io01RAjSaEv1Xb2uFLt8VtRiZbGp5H8iDtg==" 373 | }, 374 | "faucet": { 375 | "version": "0.0.1", 376 | "resolved": "https://registry.npmjs.org/faucet/-/faucet-0.0.1.tgz", 377 | "integrity": "sha1-WX3PHSGJosBiMhtZHo8VHtIDnZw=", 378 | "requires": { 379 | "defined": "0.0.0", 380 | "duplexer": "~0.1.1", 381 | "minimist": "0.0.5", 382 | "sprintf": "~0.1.3", 383 | "tap-parser": "~0.4.0", 384 | "tape": "~2.3.2", 385 | "through2": "~0.2.3" 386 | }, 387 | "dependencies": { 388 | "tape": { 389 | "version": "2.3.3", 390 | "resolved": "https://registry.npmjs.org/tape/-/tape-2.3.3.tgz", 391 | "integrity": "sha1-Lnzgox3wn41oUWZKcYQuDKUFevc=", 392 | "requires": { 393 | "deep-equal": "~0.1.0", 394 | "defined": "~0.0.0", 395 | "inherits": "~2.0.1", 396 | "jsonify": "~0.0.0", 397 | "resumer": "~0.0.0", 398 | "through": "~2.3.4" 399 | } 400 | } 401 | } 402 | }, 403 | "fecha": { 404 | "version": "2.3.3", 405 | "resolved": "https://registry.npmjs.org/fecha/-/fecha-2.3.3.tgz", 406 | "integrity": "sha512-lUGBnIamTAwk4znq5BcqsDaxSmZ9nDVJaij6NvRt/Tg4R69gERA+otPKbS86ROw9nxVMw2/mp1fnaiWqbs6Sdg==" 407 | }, 408 | "for-each": { 409 | "version": "0.3.3", 410 | "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.3.tgz", 411 | "integrity": "sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==", 412 | "requires": { 413 | "is-callable": "^1.1.3" 414 | } 415 | }, 416 | "fs.realpath": { 417 | "version": "1.0.0", 418 | "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", 419 | "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=" 420 | }, 421 | "function-bind": { 422 | "version": "1.1.1", 423 | "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", 424 | "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==" 425 | }, 426 | "get-port": { 427 | "version": "5.0.0", 428 | "resolved": "https://registry.npmjs.org/get-port/-/get-port-5.0.0.tgz", 429 | "integrity": "sha512-imzMU0FjsZqNa6BqOjbbW6w5BivHIuQKopjpPqcnx0AVHJQKCxK1O+Ab3OrVXhrekqfVMjwA9ZYu062R+KcIsQ==", 430 | "requires": { 431 | "type-fest": "^0.3.0" 432 | } 433 | }, 434 | "get-prototype-of": { 435 | "version": "0.0.0", 436 | "resolved": "https://registry.npmjs.org/get-prototype-of/-/get-prototype-of-0.0.0.tgz", 437 | "integrity": "sha1-mHcr0QcW0W3rSzIlFsRp78oorEQ=" 438 | }, 439 | "glob": { 440 | "version": "7.1.4", 441 | "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.4.tgz", 442 | "integrity": "sha512-hkLPepehmnKk41pUGm3sYxoFs/umurYfYJCerbXEyFIWcAzvpipAgVkBqqT9RBKMGjnq6kMuyYwha6csxbiM1A==", 443 | "requires": { 444 | "fs.realpath": "^1.0.0", 445 | "inflight": "^1.0.4", 446 | "inherits": "2", 447 | "minimatch": "^3.0.4", 448 | "once": "^1.3.0", 449 | "path-is-absolute": "^1.0.0" 450 | } 451 | }, 452 | "globby": { 453 | "version": "6.1.0", 454 | "resolved": "https://registry.npmjs.org/globby/-/globby-6.1.0.tgz", 455 | "integrity": "sha1-9abXDoOV4hyFj7BInWTfAkJNUGw=", 456 | "requires": { 457 | "array-union": "^1.0.1", 458 | "glob": "^7.0.3", 459 | "object-assign": "^4.0.1", 460 | "pify": "^2.0.0", 461 | "pinkie-promise": "^2.0.0" 462 | }, 463 | "dependencies": { 464 | "pify": { 465 | "version": "2.3.0", 466 | "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", 467 | "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=" 468 | } 469 | } 470 | }, 471 | "has": { 472 | "version": "1.0.3", 473 | "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", 474 | "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", 475 | "requires": { 476 | "function-bind": "^1.1.1" 477 | } 478 | }, 479 | "has-symbols": { 480 | "version": "1.0.0", 481 | "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.0.tgz", 482 | "integrity": "sha1-uhqPGvKg/DllD1yFA2dwQSIGO0Q=" 483 | }, 484 | "iconv-lite": { 485 | "version": "0.4.24", 486 | "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", 487 | "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", 488 | "requires": { 489 | "safer-buffer": ">= 2.1.2 < 3" 490 | } 491 | }, 492 | "inflight": { 493 | "version": "1.0.6", 494 | "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", 495 | "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", 496 | "requires": { 497 | "once": "^1.3.0", 498 | "wrappy": "1" 499 | } 500 | }, 501 | "inherits": { 502 | "version": "2.0.4", 503 | "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", 504 | "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" 505 | }, 506 | "is-arrayish": { 507 | "version": "0.3.2", 508 | "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.3.2.tgz", 509 | "integrity": "sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==" 510 | }, 511 | "is-callable": { 512 | "version": "1.1.4", 513 | "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.1.4.tgz", 514 | "integrity": "sha512-r5p9sxJjYnArLjObpjA4xu5EKI3CuKHkJXMhT7kwbpUyIFD1n5PMAsoPvWnvtZiNz7LjkYDRZhd7FlI0eMijEA==" 515 | }, 516 | "is-capitalized": { 517 | "version": "1.0.0", 518 | "resolved": "https://registry.npmjs.org/is-capitalized/-/is-capitalized-1.0.0.tgz", 519 | "integrity": "sha1-TIRktNkdPk7rRIid0s2PGwrEwTY=" 520 | }, 521 | "is-class": { 522 | "version": "0.0.4", 523 | "resolved": "https://registry.npmjs.org/is-class/-/is-class-0.0.4.tgz", 524 | "integrity": "sha1-4FdFFwW7NOOePjNZjJOpg3KWtzY=" 525 | }, 526 | "is-date-object": { 527 | "version": "1.0.1", 528 | "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.1.tgz", 529 | "integrity": "sha1-mqIOtq7rv/d/vTPnTKAbM1gdOhY=" 530 | }, 531 | "is-path-cwd": { 532 | "version": "2.1.0", 533 | "resolved": "https://registry.npmjs.org/is-path-cwd/-/is-path-cwd-2.1.0.tgz", 534 | "integrity": "sha512-Sc5j3/YnM8tDeyCsVeKlm/0p95075DyLmDEIkSgQ7mXkrOX+uTCtmQFm0CYzVyJwcCCmO3k8qfJt17SxQwB5Zw==" 535 | }, 536 | "is-path-in-cwd": { 537 | "version": "2.1.0", 538 | "resolved": "https://registry.npmjs.org/is-path-in-cwd/-/is-path-in-cwd-2.1.0.tgz", 539 | "integrity": "sha512-rNocXHgipO+rvnP6dk3zI20RpOtrAM/kzbB258Uw5BWr3TpXi861yzjo16Dn4hUox07iw5AyeMLHWsujkjzvRQ==", 540 | "requires": { 541 | "is-path-inside": "^2.1.0" 542 | } 543 | }, 544 | "is-path-inside": { 545 | "version": "2.1.0", 546 | "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-2.1.0.tgz", 547 | "integrity": "sha512-wiyhTzfDWsvwAW53OBWF5zuvaOGlZ6PwYxAbPVDhpm+gM09xKQGjBq/8uYN12aDvMxnAnq3dxTyoSoRNmg5YFg==", 548 | "requires": { 549 | "path-is-inside": "^1.0.2" 550 | } 551 | }, 552 | "is-regex": { 553 | "version": "1.0.4", 554 | "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.0.4.tgz", 555 | "integrity": "sha1-VRdIm1RwkbCTDglWVM7SXul+lJE=", 556 | "requires": { 557 | "has": "^1.0.1" 558 | } 559 | }, 560 | "is-stream": { 561 | "version": "1.1.0", 562 | "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz", 563 | "integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ=" 564 | }, 565 | "is-symbol": { 566 | "version": "1.0.2", 567 | "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.2.tgz", 568 | "integrity": "sha512-HS8bZ9ox60yCJLH9snBpIwv9pYUAkcuLhSA1oero1UB5y9aiQpRA8y2ex945AOtCZL1lJDeIk3G5LthswI46Lw==", 569 | "requires": { 570 | "has-symbols": "^1.0.0" 571 | } 572 | }, 573 | "isarray": { 574 | "version": "1.0.0", 575 | "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", 576 | "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" 577 | }, 578 | "isomorphic-fetch": { 579 | "version": "2.2.1", 580 | "resolved": "https://registry.npmjs.org/isomorphic-fetch/-/isomorphic-fetch-2.2.1.tgz", 581 | "integrity": "sha1-YRrhrPFPXoH3KVB0coGf6XM1WKk=", 582 | "requires": { 583 | "node-fetch": "^1.0.1", 584 | "whatwg-fetch": ">=0.10.0" 585 | } 586 | }, 587 | "json3": { 588 | "version": "3.3.3", 589 | "resolved": "https://registry.npmjs.org/json3/-/json3-3.3.3.tgz", 590 | "integrity": "sha512-c7/8mbUsKigAbLkD5B010BK4D9LZm7A1pNItkEwiUZRpIN66exu/e7YQWysGun+TRKaJp8MhemM+VkfWv42aCA==" 591 | }, 592 | "jsonify": { 593 | "version": "0.0.0", 594 | "resolved": "https://registry.npmjs.org/jsonify/-/jsonify-0.0.0.tgz", 595 | "integrity": "sha1-LHS27kHZPKUbe1qu6PUDYx0lKnM=" 596 | }, 597 | "keypather": { 598 | "version": "1.10.2", 599 | "resolved": "https://registry.npmjs.org/keypather/-/keypather-1.10.2.tgz", 600 | "integrity": "sha1-4ESWMtSz5RbyHMAUznxWRP3c5hQ=", 601 | "requires": { 602 | "101": "^1.0.0" 603 | } 604 | }, 605 | "kuler": { 606 | "version": "1.0.1", 607 | "resolved": "https://registry.npmjs.org/kuler/-/kuler-1.0.1.tgz", 608 | "integrity": "sha512-J9nVUucG1p/skKul6DU3PUZrhs0LPulNaeUOox0IyXDi8S4CztTHs1gQphhuZmzXG7VOQSf6NJfKuzteQLv9gQ==", 609 | "requires": { 610 | "colornames": "^1.1.1" 611 | } 612 | }, 613 | "lodash": { 614 | "version": "4.17.11", 615 | "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.11.tgz", 616 | "integrity": "sha512-cQKh8igo5QUhZ7lg38DYWAxMvjSAKG0A8wGSVimP07SIUEK2UO+arSRKbRZWtelMtN5V0Hkwh5ryOto/SshYIg==" 617 | }, 618 | "logform": { 619 | "version": "2.1.2", 620 | "resolved": "https://registry.npmjs.org/logform/-/logform-2.1.2.tgz", 621 | "integrity": "sha512-+lZh4OpERDBLqjiwDLpAWNQu6KMjnlXH2ByZwCuSqVPJletw0kTWJf5CgSNAUKn1KUkv3m2cUz/LK8zyEy7wzQ==", 622 | "requires": { 623 | "colors": "^1.2.1", 624 | "fast-safe-stringify": "^2.0.4", 625 | "fecha": "^2.3.3", 626 | "ms": "^2.1.1", 627 | "triple-beam": "^1.3.0" 628 | } 629 | }, 630 | "minimatch": { 631 | "version": "3.0.4", 632 | "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", 633 | "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", 634 | "requires": { 635 | "brace-expansion": "^1.1.7" 636 | } 637 | }, 638 | "minimist": { 639 | "version": "0.0.5", 640 | "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.5.tgz", 641 | "integrity": "sha1-16oye87PUY+RBqxrjwA/o7zqhWY=" 642 | }, 643 | "ms": { 644 | "version": "2.1.2", 645 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", 646 | "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" 647 | }, 648 | "nan": { 649 | "version": "2.14.0", 650 | "resolved": "https://registry.npmjs.org/nan/-/nan-2.14.0.tgz", 651 | "integrity": "sha512-INOFj37C7k3AfaNTtX8RhsTw7qRy7eLET14cROi9+5HAVbbHuIWUHEauBv5qT4Av2tWasiTY1Jw6puUNqRJXQg==" 652 | }, 653 | "node-fetch": { 654 | "version": "1.7.3", 655 | "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-1.7.3.tgz", 656 | "integrity": "sha512-NhZ4CsKx7cYm2vSrBAr2PvFOe6sWDf0UYLRqA6svUYg7+/TSfVAu49jYC4BvQ4Sms9SZgdqGBgroqfDhJdTyKQ==", 657 | "requires": { 658 | "encoding": "^0.1.11", 659 | "is-stream": "^1.0.1" 660 | } 661 | }, 662 | "object-assign": { 663 | "version": "4.1.1", 664 | "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", 665 | "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=" 666 | }, 667 | "object-inspect": { 668 | "version": "1.6.0", 669 | "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.6.0.tgz", 670 | "integrity": "sha512-GJzfBZ6DgDAmnuaM3104jR4s1Myxr3Y3zfIyN4z3UdqN69oSRacNK8UhnobDdC+7J2AHCjGwxQubNJfE70SXXQ==" 671 | }, 672 | "object-keys": { 673 | "version": "0.4.0", 674 | "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-0.4.0.tgz", 675 | "integrity": "sha1-KKaq50KN0sOpLz2V8hM13SBOAzY=" 676 | }, 677 | "once": { 678 | "version": "1.4.0", 679 | "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", 680 | "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", 681 | "requires": { 682 | "wrappy": "1" 683 | } 684 | }, 685 | "one-time": { 686 | "version": "0.0.4", 687 | "resolved": "https://registry.npmjs.org/one-time/-/one-time-0.0.4.tgz", 688 | "integrity": "sha1-+M33eISCb+Tf+T46nMN7HkSAdC4=" 689 | }, 690 | "p-map": { 691 | "version": "2.1.0", 692 | "resolved": "https://registry.npmjs.org/p-map/-/p-map-2.1.0.tgz", 693 | "integrity": "sha512-y3b8Kpd8OAN444hxfBbFfj1FY/RjtTd8tzYwhUqNYXx0fXx2iX4maP4Qr6qhIKbQXI02wTLAda4fYUbDagTUFw==" 694 | }, 695 | "path-is-absolute": { 696 | "version": "1.0.1", 697 | "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", 698 | "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=" 699 | }, 700 | "path-is-inside": { 701 | "version": "1.0.2", 702 | "resolved": "https://registry.npmjs.org/path-is-inside/-/path-is-inside-1.0.2.tgz", 703 | "integrity": "sha1-NlQX3t5EQw0cEa9hAn+s8HS9/FM=" 704 | }, 705 | "path-parse": { 706 | "version": "1.0.6", 707 | "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.6.tgz", 708 | "integrity": "sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw==" 709 | }, 710 | "pify": { 711 | "version": "4.0.1", 712 | "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz", 713 | "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==" 714 | }, 715 | "pinkie": { 716 | "version": "2.0.4", 717 | "resolved": "https://registry.npmjs.org/pinkie/-/pinkie-2.0.4.tgz", 718 | "integrity": "sha1-clVrgM+g1IqXToDnckjoDtT3+HA=" 719 | }, 720 | "pinkie-promise": { 721 | "version": "2.0.1", 722 | "resolved": "https://registry.npmjs.org/pinkie-promise/-/pinkie-promise-2.0.1.tgz", 723 | "integrity": "sha1-ITXW36ejWMBprJsXh3YogihFD/o=", 724 | "requires": { 725 | "pinkie": "^2.0.0" 726 | } 727 | }, 728 | "process-nextick-args": { 729 | "version": "2.0.1", 730 | "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", 731 | "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==" 732 | }, 733 | "readable-stream": { 734 | "version": "3.4.0", 735 | "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.4.0.tgz", 736 | "integrity": "sha512-jItXPLmrSR8jmTRmRWJXCnGJsfy85mB3Wd/uINMXA65yrnFo0cPClFIUWzo2najVNSl+mx7/4W8ttlLWJe99pQ==", 737 | "requires": { 738 | "inherits": "^2.0.3", 739 | "string_decoder": "^1.1.1", 740 | "util-deprecate": "^1.0.1" 741 | } 742 | }, 743 | "regenerator-runtime": { 744 | "version": "0.13.2", 745 | "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.2.tgz", 746 | "integrity": "sha512-S/TQAZJO+D3m9xeN1WTI8dLKBBiRgXBlTJvbWjCThHWZj9EvHK70Ff50/tYj2J/fvBY6JtFVwRuazHN2E7M9BA==" 747 | }, 748 | "resolve": { 749 | "version": "1.10.1", 750 | "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.10.1.tgz", 751 | "integrity": "sha512-KuIe4mf++td/eFb6wkaPbMDnP6kObCaEtIDuHOUED6MNUo4K670KZUHuuvYPZDxNF0WVLw49n06M2m2dXphEzA==", 752 | "requires": { 753 | "path-parse": "^1.0.6" 754 | } 755 | }, 756 | "resumer": { 757 | "version": "0.0.0", 758 | "resolved": "https://registry.npmjs.org/resumer/-/resumer-0.0.0.tgz", 759 | "integrity": "sha1-8ej0YeQGS6Oegq883CqMiT0HZ1k=", 760 | "requires": { 761 | "through": "~2.3.4" 762 | } 763 | }, 764 | "rimraf": { 765 | "version": "2.6.3", 766 | "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.3.tgz", 767 | "integrity": "sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA==", 768 | "requires": { 769 | "glob": "^7.1.3" 770 | } 771 | }, 772 | "rpc-websockets": { 773 | "version": "4.4.0", 774 | "resolved": "https://registry.npmjs.org/rpc-websockets/-/rpc-websockets-4.4.0.tgz", 775 | "integrity": "sha512-ddbA5VTc/E8xG3UTfOIiz6j7nY7iNE/XZEeLvfi/iMtw1MODU6H+T1yd07MRu2vzh/aeP8zDsmPpz16XLUcWAg==", 776 | "requires": { 777 | "@babel/runtime": "^7.4.5", 778 | "assert-args": "^1.2.1", 779 | "babel-runtime": "^6.26.0", 780 | "circular-json": "^0.5.9", 781 | "eventemitter3": "^3.1.2", 782 | "uuid": "^3.3.2", 783 | "ws": "^5.2.2" 784 | } 785 | }, 786 | "safe-buffer": { 787 | "version": "5.1.2", 788 | "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", 789 | "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" 790 | }, 791 | "safer-buffer": { 792 | "version": "2.1.2", 793 | "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", 794 | "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" 795 | }, 796 | "semver": { 797 | "version": "5.7.0", 798 | "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.0.tgz", 799 | "integrity": "sha512-Ya52jSX2u7QKghxeoFGpLwCtGlt7j0oY9DYb5apt9nPlJ42ID+ulTXESnt/qAQcoSERyZ5sl3LDIOw0nAn/5DA==" 800 | }, 801 | "simple-swizzle": { 802 | "version": "0.2.2", 803 | "resolved": "https://registry.npmjs.org/simple-swizzle/-/simple-swizzle-0.2.2.tgz", 804 | "integrity": "sha1-pNprY1/8zMoz9w0Xy5JZLeleVXo=", 805 | "requires": { 806 | "is-arrayish": "^0.3.1" 807 | } 808 | }, 809 | "sleep": { 810 | "version": "5.2.4", 811 | "resolved": "https://registry.npmjs.org/sleep/-/sleep-5.2.4.tgz", 812 | "integrity": "sha512-SoltvxayTifWOgOGD6CTh+djcp5TaOa/zdbaA38wEH1ahF2azmiLOh8CPt6ExHf0pAJAsA9OCHTS7zK24Ym4yA==", 813 | "requires": { 814 | "nan": ">=2.12.1" 815 | } 816 | }, 817 | "sprintf": { 818 | "version": "0.1.5", 819 | "resolved": "https://registry.npmjs.org/sprintf/-/sprintf-0.1.5.tgz", 820 | "integrity": "sha1-j4PjmpMXwaUCy324BQ5Rxnn27c8=" 821 | }, 822 | "stack-trace": { 823 | "version": "0.0.10", 824 | "resolved": "https://registry.npmjs.org/stack-trace/-/stack-trace-0.0.10.tgz", 825 | "integrity": "sha1-VHxws0fo0ytOEI6hoqFZ5f3eGcA=" 826 | }, 827 | "string.prototype.trim": { 828 | "version": "1.1.2", 829 | "resolved": "https://registry.npmjs.org/string.prototype.trim/-/string.prototype.trim-1.1.2.tgz", 830 | "integrity": "sha1-0E3iyJ4Tf019IG8Ia17S+ua+jOo=", 831 | "requires": { 832 | "define-properties": "^1.1.2", 833 | "es-abstract": "^1.5.0", 834 | "function-bind": "^1.0.2" 835 | } 836 | }, 837 | "string_decoder": { 838 | "version": "1.2.0", 839 | "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.2.0.tgz", 840 | "integrity": "sha512-6YqyX6ZWEYguAxgZzHGL7SsCeGx3V2TtOTqZz1xSTSWnqsbWwbptafNyvf/ACquZUXV3DANr5BDIwNYe1mN42w==", 841 | "requires": { 842 | "safe-buffer": "~5.1.0" 843 | } 844 | }, 845 | "tap-parser": { 846 | "version": "0.4.3", 847 | "resolved": "https://registry.npmjs.org/tap-parser/-/tap-parser-0.4.3.tgz", 848 | "integrity": "sha1-pOrhkMENdsehEZIf84u+TVjwnuo=", 849 | "requires": { 850 | "inherits": "~2.0.1", 851 | "readable-stream": "~1.1.11" 852 | }, 853 | "dependencies": { 854 | "isarray": { 855 | "version": "0.0.1", 856 | "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", 857 | "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=" 858 | }, 859 | "readable-stream": { 860 | "version": "1.1.14", 861 | "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.1.14.tgz", 862 | "integrity": "sha1-fPTFTvZI44EwhMY23SB54WbAgdk=", 863 | "requires": { 864 | "core-util-is": "~1.0.0", 865 | "inherits": "~2.0.1", 866 | "isarray": "0.0.1", 867 | "string_decoder": "~0.10.x" 868 | } 869 | }, 870 | "string_decoder": { 871 | "version": "0.10.31", 872 | "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", 873 | "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=" 874 | } 875 | } 876 | }, 877 | "tape": { 878 | "version": "4.10.2", 879 | "resolved": "https://registry.npmjs.org/tape/-/tape-4.10.2.tgz", 880 | "integrity": "sha512-mgl23h7W2yuk3N85FOYrin2OvThTYWdwbk6XQ1pr2PMJieyW2FM/4Bu/+kD/wecb3aZ0Enm+Syinyq467OPq2w==", 881 | "requires": { 882 | "deep-equal": "~1.0.1", 883 | "defined": "~1.0.0", 884 | "for-each": "~0.3.3", 885 | "function-bind": "~1.1.1", 886 | "glob": "~7.1.4", 887 | "has": "~1.0.3", 888 | "inherits": "~2.0.3", 889 | "minimist": "~1.2.0", 890 | "object-inspect": "~1.6.0", 891 | "resolve": "~1.10.1", 892 | "resumer": "~0.0.0", 893 | "string.prototype.trim": "~1.1.2", 894 | "through": "~2.3.8" 895 | }, 896 | "dependencies": { 897 | "deep-equal": { 898 | "version": "1.0.1", 899 | "resolved": "https://registry.npmjs.org/deep-equal/-/deep-equal-1.0.1.tgz", 900 | "integrity": "sha1-9dJgKStmDghO/0zbyfCK0yR0SLU=" 901 | }, 902 | "defined": { 903 | "version": "1.0.0", 904 | "resolved": "https://registry.npmjs.org/defined/-/defined-1.0.0.tgz", 905 | "integrity": "sha1-yY2bzvdWdBiOEQlpFRGZ45sfppM=" 906 | }, 907 | "minimist": { 908 | "version": "1.2.0", 909 | "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", 910 | "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=" 911 | } 912 | } 913 | }, 914 | "text-hex": { 915 | "version": "1.0.0", 916 | "resolved": "https://registry.npmjs.org/text-hex/-/text-hex-1.0.0.tgz", 917 | "integrity": "sha512-uuVGNWzgJ4yhRaNSiubPY7OjISw4sw4E5Uv0wbjp+OzcbmVU/rsT8ujgcXJhn9ypzsgr5vlzpPqP+MBBKcGvbg==" 918 | }, 919 | "through": { 920 | "version": "2.3.8", 921 | "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", 922 | "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=" 923 | }, 924 | "through2": { 925 | "version": "0.2.3", 926 | "resolved": "https://registry.npmjs.org/through2/-/through2-0.2.3.tgz", 927 | "integrity": "sha1-6zKE2k6jEbbMis42U3SKUqvyWj8=", 928 | "requires": { 929 | "readable-stream": "~1.1.9", 930 | "xtend": "~2.1.1" 931 | }, 932 | "dependencies": { 933 | "isarray": { 934 | "version": "0.0.1", 935 | "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", 936 | "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=" 937 | }, 938 | "readable-stream": { 939 | "version": "1.1.14", 940 | "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.1.14.tgz", 941 | "integrity": "sha1-fPTFTvZI44EwhMY23SB54WbAgdk=", 942 | "requires": { 943 | "core-util-is": "~1.0.0", 944 | "inherits": "~2.0.1", 945 | "isarray": "0.0.1", 946 | "string_decoder": "~0.10.x" 947 | } 948 | }, 949 | "string_decoder": { 950 | "version": "0.10.31", 951 | "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", 952 | "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=" 953 | } 954 | } 955 | }, 956 | "triple-beam": { 957 | "version": "1.3.0", 958 | "resolved": "https://registry.npmjs.org/triple-beam/-/triple-beam-1.3.0.tgz", 959 | "integrity": "sha512-XrHUvV5HpdLmIj4uVMxHggLbFSZYIn7HEWsqePZcI50pco+MPqJ50wMGY794X7AOOhxOBAjbkqfAbEe/QMp2Lw==" 960 | }, 961 | "type-detect": { 962 | "version": "0.1.1", 963 | "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-0.1.1.tgz", 964 | "integrity": "sha1-C6XsKohWQORw6k6FBZcZANrFiCI=" 965 | }, 966 | "type-fest": { 967 | "version": "0.3.1", 968 | "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.3.1.tgz", 969 | "integrity": "sha512-cUGJnCdr4STbePCgqNFbpVNCepa+kAVohJs1sLhxzdH+gnEoOd8VhbYa7pD3zZYGiURWM2xzEII3fQcRizDkYQ==" 970 | }, 971 | "util-deprecate": { 972 | "version": "1.0.2", 973 | "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", 974 | "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=" 975 | }, 976 | "uuid": { 977 | "version": "3.3.2", 978 | "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.3.2.tgz", 979 | "integrity": "sha512-yXJmeNaw3DnnKAOKJE51sL/ZaYfWJRl1pK9dr19YFCu0ObS231AB1/LbqTKRAQ5kw8A90rA6fr4riOUpTZvQZA==" 980 | }, 981 | "whatwg-fetch": { 982 | "version": "3.0.0", 983 | "resolved": "https://registry.npmjs.org/whatwg-fetch/-/whatwg-fetch-3.0.0.tgz", 984 | "integrity": "sha512-9GSJUgz1D4MfyKU7KRqwOjXCXTqWdFNvEr7eUBYchQiVc744mqK/MzXPNR2WsPkmkOa4ywfg8C2n8h+13Bey1Q==" 985 | }, 986 | "winston": { 987 | "version": "3.2.1", 988 | "resolved": "https://registry.npmjs.org/winston/-/winston-3.2.1.tgz", 989 | "integrity": "sha512-zU6vgnS9dAWCEKg/QYigd6cgMVVNwyTzKs81XZtTFuRwJOcDdBg7AU0mXVyNbs7O5RH2zdv+BdNZUlx7mXPuOw==", 990 | "requires": { 991 | "async": "^2.6.1", 992 | "diagnostics": "^1.1.1", 993 | "is-stream": "^1.1.0", 994 | "logform": "^2.1.1", 995 | "one-time": "0.0.4", 996 | "readable-stream": "^3.1.1", 997 | "stack-trace": "0.0.x", 998 | "triple-beam": "^1.3.0", 999 | "winston-transport": "^4.3.0" 1000 | } 1001 | }, 1002 | "winston-compat": { 1003 | "version": "0.1.4", 1004 | "resolved": "https://registry.npmjs.org/winston-compat/-/winston-compat-0.1.4.tgz", 1005 | "integrity": "sha512-mMEfFsSm6GmkFF+f4/0UJtG4N1vSaczGmXLVJYmS/+u2zUaIPcw2ZRuwUg2TvVBjswgiraN+vNnAG8z4fRUZ4w==", 1006 | "requires": { 1007 | "cycle": "~1.0.3", 1008 | "logform": "^1.6.0", 1009 | "triple-beam": "^1.2.0" 1010 | }, 1011 | "dependencies": { 1012 | "logform": { 1013 | "version": "1.10.0", 1014 | "resolved": "https://registry.npmjs.org/logform/-/logform-1.10.0.tgz", 1015 | "integrity": "sha512-em5ojIhU18fIMOw/333mD+ZLE2fis0EzXl1ZwHx4iQzmpQi6odNiY/t+ITNr33JZhT9/KEaH+UPIipr6a9EjWg==", 1016 | "requires": { 1017 | "colors": "^1.2.1", 1018 | "fast-safe-stringify": "^2.0.4", 1019 | "fecha": "^2.3.3", 1020 | "ms": "^2.1.1", 1021 | "triple-beam": "^1.2.0" 1022 | } 1023 | } 1024 | } 1025 | }, 1026 | "winston-null": { 1027 | "version": "2.0.0", 1028 | "resolved": "https://registry.npmjs.org/winston-null/-/winston-null-2.0.0.tgz", 1029 | "integrity": "sha512-uS5tJB5OkLWOoc3I7/LsWUfTIa5Du38XSviHf/b0TINK659Np9368FJwTt15UoZQYUQVxLpM06lxk2dKET22Xw==", 1030 | "requires": { 1031 | "semver": "^5.6.0", 1032 | "winston-compat": "^0.1.4", 1033 | "winston-transport": "^4.2.0" 1034 | } 1035 | }, 1036 | "winston-transport": { 1037 | "version": "4.3.0", 1038 | "resolved": "https://registry.npmjs.org/winston-transport/-/winston-transport-4.3.0.tgz", 1039 | "integrity": "sha512-B2wPuwUi3vhzn/51Uukcao4dIduEiPOcOt9HJ3QeaXgkJ5Z7UwpBzxS4ZGNHtrxrUvTwemsQiSys0ihOf8Mp1A==", 1040 | "requires": { 1041 | "readable-stream": "^2.3.6", 1042 | "triple-beam": "^1.2.0" 1043 | }, 1044 | "dependencies": { 1045 | "readable-stream": { 1046 | "version": "2.3.6", 1047 | "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", 1048 | "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", 1049 | "requires": { 1050 | "core-util-is": "~1.0.0", 1051 | "inherits": "~2.0.3", 1052 | "isarray": "~1.0.0", 1053 | "process-nextick-args": "~2.0.0", 1054 | "safe-buffer": "~5.1.1", 1055 | "string_decoder": "~1.1.1", 1056 | "util-deprecate": "~1.0.1" 1057 | } 1058 | }, 1059 | "string_decoder": { 1060 | "version": "1.1.1", 1061 | "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", 1062 | "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", 1063 | "requires": { 1064 | "safe-buffer": "~5.1.0" 1065 | } 1066 | } 1067 | } 1068 | }, 1069 | "wrappy": { 1070 | "version": "1.0.2", 1071 | "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", 1072 | "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" 1073 | }, 1074 | "ws": { 1075 | "version": "5.2.2", 1076 | "resolved": "https://registry.npmjs.org/ws/-/ws-5.2.2.tgz", 1077 | "integrity": "sha512-jaHFD6PFv6UgoIVda6qZllptQsMlDEJkTQcybzzXDYM1XO9Y8em691FGMPmM46WGyLU4z9KMgQN+qrux/nhlHA==", 1078 | "requires": { 1079 | "async-limiter": "~1.0.0" 1080 | } 1081 | }, 1082 | "xtend": { 1083 | "version": "2.1.2", 1084 | "resolved": "https://registry.npmjs.org/xtend/-/xtend-2.1.2.tgz", 1085 | "integrity": "sha1-bv7MKk2tjmlixJAbM3znuoe10os=", 1086 | "requires": { 1087 | "object-keys": "~0.4.0" 1088 | } 1089 | } 1090 | } 1091 | } 1092 | -------------------------------------------------------------------------------- /test/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "devDependencies": {}, 3 | "dependencies": { 4 | "@holochain/diorama": "^0.1.1", 5 | "faucet": "0.0.1", 6 | "json3": "*", 7 | "sleep": "^5.2.3", 8 | "tape": "^4.9.1" 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /test/tictactoe.js: -------------------------------------------------------------------------------- 1 | const {results, lastResult, makeMove, createGame, renderState, getState} = require('./helpers') 2 | 3 | module.exports = (scenario) => { 4 | scenario("Can create a new game of tictactoe and make a move", async (s, t, { alice, bob }) => { 5 | 6 | let game_address = await createGame(alice, bob); 7 | 8 | // agent 2 must go first 9 | await makeMove(bob, { 10 | game: game_address, 11 | timestamp: 0, 12 | move_type: {Place: { pos: { x: 0, y: 0 } } }, 13 | }) 14 | t.notEqual(lastResult().Ok, undefined, "Bob made the first move") 15 | 16 | await renderState(alice, game_address) 17 | 18 | await makeMove(alice, { 19 | game: game_address, 20 | timestamp: 1, 21 | move_type: {Place: { pos: { x: 1, y: 1 } } }, 22 | }) 23 | t.notEqual(lastResult().Ok, undefined, "Alice made the second move") 24 | 25 | await renderState(alice, game_address) 26 | 27 | await makeMove(bob, { 28 | game: game_address, 29 | timestamp: 2, 30 | move_type: {Place: { pos: { x: 1, y: 0 } } }, 31 | }) 32 | t.notEqual(lastResult().Ok, undefined, "Bob made the third move") 33 | 34 | let state = await getState(alice, game_address) 35 | 36 | t.equal(state.Ok.moves.length, 3, "There were three moves in the game") 37 | 38 | // both agents should see the same game state 39 | t.deepEqual(await getState(bob, game_address), await getState(alice, game_address), "Alice and Bob both see the same game state") 40 | 41 | 42 | // finally print all the outputs 43 | results.forEach((result, i) => { 44 | console.log(`${i}: ${JSON.stringify(result, null, 2)}\n`) 45 | }) 46 | 47 | }) 48 | } 49 | -------------------------------------------------------------------------------- /zomes/main/code/.hcbuild: -------------------------------------------------------------------------------- 1 | { 2 | "steps": [ 3 | { 4 | "command": "cargo", 5 | "arguments": [ 6 | "build", 7 | "--release", 8 | "--target=wasm32-unknown-unknown", 9 | "--target-dir=/tmp/holochain/target" 10 | ] 11 | }, 12 | { 13 | "command": "wasm-gc", 14 | "arguments": ["/tmp/holochain/target/wasm32-unknown-unknown/release/main.wasm"] 15 | }, 16 | { 17 | "command": "wasm-opt", 18 | "arguments": [ 19 | "-Oz", 20 | "--vacuum", 21 | "/tmp/holochain/target/wasm32-unknown-unknown/release/main.wasm" 22 | ] 23 | }, 24 | { 25 | "command": "wasm2wat", 26 | "arguments": [ 27 | "/tmp/holochain/target/wasm32-unknown-unknown/release/main.wasm", 28 | "-o", 29 | "/tmp/holochain/target/wasm32-unknown-unknown/release/main.wat" 30 | ] 31 | }, 32 | { 33 | "command": "wat2wasm", 34 | "arguments": [ 35 | "/tmp/holochain/target/wasm32-unknown-unknown/release/main.wat", 36 | "-o", 37 | "/tmp/holochain/target/wasm32-unknown-unknown/release/main.wasm" 38 | ] 39 | } 40 | ], 41 | "artifact": "/tmp/holochain/target/wasm32-unknown-unknown/release/main.wasm" 42 | } 43 | -------------------------------------------------------------------------------- /zomes/main/code/Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | [[package]] 4 | name = "aho-corasick" 5 | version = "0.6.10" 6 | source = "registry+https://github.com/rust-lang/crates.io-index" 7 | dependencies = [ 8 | "memchr 2.2.0 (registry+https://github.com/rust-lang/crates.io-index)", 9 | ] 10 | 11 | [[package]] 12 | name = "ansi_term" 13 | version = "0.11.0" 14 | source = "registry+https://github.com/rust-lang/crates.io-index" 15 | dependencies = [ 16 | "winapi 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", 17 | ] 18 | 19 | [[package]] 20 | name = "arrayref" 21 | version = "0.3.5" 22 | source = "registry+https://github.com/rust-lang/crates.io-index" 23 | 24 | [[package]] 25 | name = "autocfg" 26 | version = "0.1.4" 27 | source = "registry+https://github.com/rust-lang/crates.io-index" 28 | 29 | [[package]] 30 | name = "base64" 31 | version = "0.10.1" 32 | source = "registry+https://github.com/rust-lang/crates.io-index" 33 | dependencies = [ 34 | "byteorder 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)", 35 | ] 36 | 37 | [[package]] 38 | name = "bitflags" 39 | version = "1.0.4" 40 | source = "registry+https://github.com/rust-lang/crates.io-index" 41 | 42 | [[package]] 43 | name = "block-buffer" 44 | version = "0.3.3" 45 | source = "registry+https://github.com/rust-lang/crates.io-index" 46 | dependencies = [ 47 | "arrayref 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", 48 | "byte-tools 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", 49 | ] 50 | 51 | [[package]] 52 | name = "byte-tools" 53 | version = "0.2.0" 54 | source = "registry+https://github.com/rust-lang/crates.io-index" 55 | 56 | [[package]] 57 | name = "byteorder" 58 | version = "1.3.1" 59 | source = "registry+https://github.com/rust-lang/crates.io-index" 60 | 61 | [[package]] 62 | name = "cfg-if" 63 | version = "0.1.9" 64 | source = "registry+https://github.com/rust-lang/crates.io-index" 65 | 66 | [[package]] 67 | name = "chrono" 68 | version = "0.4.6" 69 | source = "registry+https://github.com/rust-lang/crates.io-index" 70 | dependencies = [ 71 | "num-integer 0.1.41 (registry+https://github.com/rust-lang/crates.io-index)", 72 | "num-traits 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", 73 | "time 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)", 74 | ] 75 | 76 | [[package]] 77 | name = "cloudabi" 78 | version = "0.0.3" 79 | source = "registry+https://github.com/rust-lang/crates.io-index" 80 | dependencies = [ 81 | "bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)", 82 | ] 83 | 84 | [[package]] 85 | name = "crossbeam-channel" 86 | version = "0.3.8" 87 | source = "registry+https://github.com/rust-lang/crates.io-index" 88 | dependencies = [ 89 | "crossbeam-utils 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)", 90 | "smallvec 0.6.10 (registry+https://github.com/rust-lang/crates.io-index)", 91 | ] 92 | 93 | [[package]] 94 | name = "crossbeam-utils" 95 | version = "0.6.5" 96 | source = "registry+https://github.com/rust-lang/crates.io-index" 97 | dependencies = [ 98 | "cfg-if 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", 99 | "lazy_static 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", 100 | ] 101 | 102 | [[package]] 103 | name = "crunchy" 104 | version = "0.1.6" 105 | source = "registry+https://github.com/rust-lang/crates.io-index" 106 | 107 | [[package]] 108 | name = "ctor" 109 | version = "0.1.9" 110 | source = "registry+https://github.com/rust-lang/crates.io-index" 111 | dependencies = [ 112 | "quote 0.6.11 (registry+https://github.com/rust-lang/crates.io-index)", 113 | "syn 0.15.31 (registry+https://github.com/rust-lang/crates.io-index)", 114 | ] 115 | 116 | [[package]] 117 | name = "difference" 118 | version = "2.0.0" 119 | source = "registry+https://github.com/rust-lang/crates.io-index" 120 | 121 | [[package]] 122 | name = "digest" 123 | version = "0.7.6" 124 | source = "registry+https://github.com/rust-lang/crates.io-index" 125 | dependencies = [ 126 | "generic-array 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", 127 | ] 128 | 129 | [[package]] 130 | name = "either" 131 | version = "1.5.2" 132 | source = "registry+https://github.com/rust-lang/crates.io-index" 133 | 134 | [[package]] 135 | name = "fake-simd" 136 | version = "0.1.2" 137 | source = "registry+https://github.com/rust-lang/crates.io-index" 138 | 139 | [[package]] 140 | name = "fuchsia-cprng" 141 | version = "0.1.1" 142 | source = "registry+https://github.com/rust-lang/crates.io-index" 143 | 144 | [[package]] 145 | name = "futures-channel-preview" 146 | version = "0.3.0-alpha.16" 147 | source = "registry+https://github.com/rust-lang/crates.io-index" 148 | dependencies = [ 149 | "futures-core-preview 0.3.0-alpha.16 (registry+https://github.com/rust-lang/crates.io-index)", 150 | ] 151 | 152 | [[package]] 153 | name = "futures-core-preview" 154 | version = "0.3.0-alpha.16" 155 | source = "registry+https://github.com/rust-lang/crates.io-index" 156 | 157 | [[package]] 158 | name = "futures-executor-preview" 159 | version = "0.3.0-alpha.16" 160 | source = "registry+https://github.com/rust-lang/crates.io-index" 161 | dependencies = [ 162 | "futures-channel-preview 0.3.0-alpha.16 (registry+https://github.com/rust-lang/crates.io-index)", 163 | "futures-core-preview 0.3.0-alpha.16 (registry+https://github.com/rust-lang/crates.io-index)", 164 | "futures-util-preview 0.3.0-alpha.16 (registry+https://github.com/rust-lang/crates.io-index)", 165 | "num_cpus 1.10.0 (registry+https://github.com/rust-lang/crates.io-index)", 166 | "pin-utils 0.1.0-alpha.4 (registry+https://github.com/rust-lang/crates.io-index)", 167 | ] 168 | 169 | [[package]] 170 | name = "futures-io-preview" 171 | version = "0.3.0-alpha.16" 172 | source = "registry+https://github.com/rust-lang/crates.io-index" 173 | dependencies = [ 174 | "futures-core-preview 0.3.0-alpha.16 (registry+https://github.com/rust-lang/crates.io-index)", 175 | ] 176 | 177 | [[package]] 178 | name = "futures-preview" 179 | version = "0.3.0-alpha.16" 180 | source = "registry+https://github.com/rust-lang/crates.io-index" 181 | dependencies = [ 182 | "futures-channel-preview 0.3.0-alpha.16 (registry+https://github.com/rust-lang/crates.io-index)", 183 | "futures-core-preview 0.3.0-alpha.16 (registry+https://github.com/rust-lang/crates.io-index)", 184 | "futures-executor-preview 0.3.0-alpha.16 (registry+https://github.com/rust-lang/crates.io-index)", 185 | "futures-io-preview 0.3.0-alpha.16 (registry+https://github.com/rust-lang/crates.io-index)", 186 | "futures-sink-preview 0.3.0-alpha.16 (registry+https://github.com/rust-lang/crates.io-index)", 187 | "futures-util-preview 0.3.0-alpha.16 (registry+https://github.com/rust-lang/crates.io-index)", 188 | ] 189 | 190 | [[package]] 191 | name = "futures-sink-preview" 192 | version = "0.3.0-alpha.16" 193 | source = "registry+https://github.com/rust-lang/crates.io-index" 194 | dependencies = [ 195 | "futures-channel-preview 0.3.0-alpha.16 (registry+https://github.com/rust-lang/crates.io-index)", 196 | "futures-core-preview 0.3.0-alpha.16 (registry+https://github.com/rust-lang/crates.io-index)", 197 | ] 198 | 199 | [[package]] 200 | name = "futures-util-preview" 201 | version = "0.3.0-alpha.16" 202 | source = "registry+https://github.com/rust-lang/crates.io-index" 203 | dependencies = [ 204 | "futures-channel-preview 0.3.0-alpha.16 (registry+https://github.com/rust-lang/crates.io-index)", 205 | "futures-core-preview 0.3.0-alpha.16 (registry+https://github.com/rust-lang/crates.io-index)", 206 | "futures-io-preview 0.3.0-alpha.16 (registry+https://github.com/rust-lang/crates.io-index)", 207 | "futures-sink-preview 0.3.0-alpha.16 (registry+https://github.com/rust-lang/crates.io-index)", 208 | "memchr 2.2.0 (registry+https://github.com/rust-lang/crates.io-index)", 209 | "pin-utils 0.1.0-alpha.4 (registry+https://github.com/rust-lang/crates.io-index)", 210 | "slab 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", 211 | ] 212 | 213 | [[package]] 214 | name = "generic-array" 215 | version = "0.9.0" 216 | source = "registry+https://github.com/rust-lang/crates.io-index" 217 | dependencies = [ 218 | "typenum 1.10.0 (registry+https://github.com/rust-lang/crates.io-index)", 219 | ] 220 | 221 | [[package]] 222 | name = "hcid" 223 | version = "0.0.6" 224 | source = "registry+https://github.com/rust-lang/crates.io-index" 225 | dependencies = [ 226 | "reed-solomon 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", 227 | ] 228 | 229 | [[package]] 230 | name = "hdk" 231 | version = "0.0.25-alpha1" 232 | source = "git+https://github.com/holochain/holochain-rust?tag=0.0.25-alpha1#1327abe513af3f5206e87d6b479c4262c1b3011e" 233 | dependencies = [ 234 | "bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)", 235 | "holochain_core_types 0.0.25-alpha1 (git+https://github.com/holochain/holochain-rust?tag=0.0.25-alpha1)", 236 | "holochain_json_api 0.0.15 (registry+https://github.com/rust-lang/crates.io-index)", 237 | "holochain_json_derive 0.0.15 (registry+https://github.com/rust-lang/crates.io-index)", 238 | "holochain_persistence_api 0.0.6 (registry+https://github.com/rust-lang/crates.io-index)", 239 | "holochain_wasm_utils 0.0.25-alpha1 (git+https://github.com/holochain/holochain-rust?tag=0.0.25-alpha1)", 240 | "lazy_static 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", 241 | "pretty_assertions 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)", 242 | "serde 1.0.89 (registry+https://github.com/rust-lang/crates.io-index)", 243 | "serde_derive 1.0.89 (registry+https://github.com/rust-lang/crates.io-index)", 244 | "serde_json 1.0.39 (registry+https://github.com/rust-lang/crates.io-index)", 245 | ] 246 | 247 | [[package]] 248 | name = "hdk-proc-macros" 249 | version = "0.0.25-alpha1" 250 | source = "git+https://github.com/holochain/holochain-rust?tag=0.0.25-alpha1#1327abe513af3f5206e87d6b479c4262c1b3011e" 251 | dependencies = [ 252 | "hdk 0.0.25-alpha1 (git+https://github.com/holochain/holochain-rust?tag=0.0.25-alpha1)", 253 | "proc-macro2 0.4.27 (registry+https://github.com/rust-lang/crates.io-index)", 254 | "quote 0.6.11 (registry+https://github.com/rust-lang/crates.io-index)", 255 | "syn 0.15.31 (registry+https://github.com/rust-lang/crates.io-index)", 256 | ] 257 | 258 | [[package]] 259 | name = "holochain_core_types" 260 | version = "0.0.25-alpha1" 261 | source = "git+https://github.com/holochain/holochain-rust?tag=0.0.25-alpha1#1327abe513af3f5206e87d6b479c4262c1b3011e" 262 | dependencies = [ 263 | "arrayref 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", 264 | "base64 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)", 265 | "chrono 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", 266 | "crossbeam-channel 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", 267 | "futures-channel-preview 0.3.0-alpha.16 (registry+https://github.com/rust-lang/crates.io-index)", 268 | "futures-core-preview 0.3.0-alpha.16 (registry+https://github.com/rust-lang/crates.io-index)", 269 | "futures-executor-preview 0.3.0-alpha.16 (registry+https://github.com/rust-lang/crates.io-index)", 270 | "futures-io-preview 0.3.0-alpha.16 (registry+https://github.com/rust-lang/crates.io-index)", 271 | "futures-preview 0.3.0-alpha.16 (registry+https://github.com/rust-lang/crates.io-index)", 272 | "futures-sink-preview 0.3.0-alpha.16 (registry+https://github.com/rust-lang/crates.io-index)", 273 | "futures-util-preview 0.3.0-alpha.16 (registry+https://github.com/rust-lang/crates.io-index)", 274 | "hcid 0.0.6 (registry+https://github.com/rust-lang/crates.io-index)", 275 | "holochain_json_api 0.0.15 (registry+https://github.com/rust-lang/crates.io-index)", 276 | "holochain_json_derive 0.0.15 (registry+https://github.com/rust-lang/crates.io-index)", 277 | "holochain_persistence_api 0.0.6 (registry+https://github.com/rust-lang/crates.io-index)", 278 | "lazy_static 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", 279 | "lib3h_crypto_api 0.0.8 (registry+https://github.com/rust-lang/crates.io-index)", 280 | "multihash 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", 281 | "objekt 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", 282 | "regex 1.1.2 (registry+https://github.com/rust-lang/crates.io-index)", 283 | "rust-base58 0.0.4 (registry+https://github.com/rust-lang/crates.io-index)", 284 | "serde 1.0.89 (registry+https://github.com/rust-lang/crates.io-index)", 285 | "serde_derive 1.0.89 (registry+https://github.com/rust-lang/crates.io-index)", 286 | "serde_json 1.0.39 (registry+https://github.com/rust-lang/crates.io-index)", 287 | "shrinkwraprs 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", 288 | "snowflake 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", 289 | "uuid 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)", 290 | "wasmi 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)", 291 | ] 292 | 293 | [[package]] 294 | name = "holochain_json_api" 295 | version = "0.0.15" 296 | source = "registry+https://github.com/rust-lang/crates.io-index" 297 | dependencies = [ 298 | "arrayref 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", 299 | "base64 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)", 300 | "chrono 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", 301 | "futures-channel-preview 0.3.0-alpha.16 (registry+https://github.com/rust-lang/crates.io-index)", 302 | "futures-core-preview 0.3.0-alpha.16 (registry+https://github.com/rust-lang/crates.io-index)", 303 | "futures-executor-preview 0.3.0-alpha.16 (registry+https://github.com/rust-lang/crates.io-index)", 304 | "futures-io-preview 0.3.0-alpha.16 (registry+https://github.com/rust-lang/crates.io-index)", 305 | "futures-preview 0.3.0-alpha.16 (registry+https://github.com/rust-lang/crates.io-index)", 306 | "futures-sink-preview 0.3.0-alpha.16 (registry+https://github.com/rust-lang/crates.io-index)", 307 | "futures-util-preview 0.3.0-alpha.16 (registry+https://github.com/rust-lang/crates.io-index)", 308 | "hcid 0.0.6 (registry+https://github.com/rust-lang/crates.io-index)", 309 | "holochain_json_derive 0.0.15 (registry+https://github.com/rust-lang/crates.io-index)", 310 | "multihash 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", 311 | "objekt 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", 312 | "rust-base58 0.0.4 (registry+https://github.com/rust-lang/crates.io-index)", 313 | "serde 1.0.89 (registry+https://github.com/rust-lang/crates.io-index)", 314 | "serde_derive 1.0.89 (registry+https://github.com/rust-lang/crates.io-index)", 315 | "serde_json 1.0.39 (registry+https://github.com/rust-lang/crates.io-index)", 316 | "shrinkwraprs 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", 317 | "uuid 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)", 318 | ] 319 | 320 | [[package]] 321 | name = "holochain_json_derive" 322 | version = "0.0.1-alpha2" 323 | source = "registry+https://github.com/rust-lang/crates.io-index" 324 | dependencies = [ 325 | "quote 0.6.11 (registry+https://github.com/rust-lang/crates.io-index)", 326 | "serde 1.0.89 (registry+https://github.com/rust-lang/crates.io-index)", 327 | "serde_json 1.0.39 (registry+https://github.com/rust-lang/crates.io-index)", 328 | "syn 0.15.31 (registry+https://github.com/rust-lang/crates.io-index)", 329 | ] 330 | 331 | [[package]] 332 | name = "holochain_json_derive" 333 | version = "0.0.15" 334 | source = "registry+https://github.com/rust-lang/crates.io-index" 335 | dependencies = [ 336 | "quote 0.6.11 (registry+https://github.com/rust-lang/crates.io-index)", 337 | "serde 1.0.89 (registry+https://github.com/rust-lang/crates.io-index)", 338 | "serde_json 1.0.39 (registry+https://github.com/rust-lang/crates.io-index)", 339 | "syn 0.15.31 (registry+https://github.com/rust-lang/crates.io-index)", 340 | ] 341 | 342 | [[package]] 343 | name = "holochain_persistence_api" 344 | version = "0.0.6" 345 | source = "registry+https://github.com/rust-lang/crates.io-index" 346 | dependencies = [ 347 | "arrayref 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", 348 | "base64 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)", 349 | "chrono 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", 350 | "futures-channel-preview 0.3.0-alpha.16 (registry+https://github.com/rust-lang/crates.io-index)", 351 | "futures-core-preview 0.3.0-alpha.16 (registry+https://github.com/rust-lang/crates.io-index)", 352 | "futures-executor-preview 0.3.0-alpha.16 (registry+https://github.com/rust-lang/crates.io-index)", 353 | "futures-io-preview 0.3.0-alpha.16 (registry+https://github.com/rust-lang/crates.io-index)", 354 | "futures-preview 0.3.0-alpha.16 (registry+https://github.com/rust-lang/crates.io-index)", 355 | "futures-sink-preview 0.3.0-alpha.16 (registry+https://github.com/rust-lang/crates.io-index)", 356 | "futures-util-preview 0.3.0-alpha.16 (registry+https://github.com/rust-lang/crates.io-index)", 357 | "hcid 0.0.6 (registry+https://github.com/rust-lang/crates.io-index)", 358 | "holochain_json_api 0.0.15 (registry+https://github.com/rust-lang/crates.io-index)", 359 | "holochain_json_derive 0.0.15 (registry+https://github.com/rust-lang/crates.io-index)", 360 | "lazy_static 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", 361 | "multihash 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", 362 | "objekt 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", 363 | "regex 1.1.2 (registry+https://github.com/rust-lang/crates.io-index)", 364 | "rust-base58 0.0.4 (registry+https://github.com/rust-lang/crates.io-index)", 365 | "serde 1.0.89 (registry+https://github.com/rust-lang/crates.io-index)", 366 | "serde_derive 1.0.89 (registry+https://github.com/rust-lang/crates.io-index)", 367 | "serde_json 1.0.39 (registry+https://github.com/rust-lang/crates.io-index)", 368 | "shrinkwraprs 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", 369 | "uuid 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)", 370 | ] 371 | 372 | [[package]] 373 | name = "holochain_wasm_utils" 374 | version = "0.0.25-alpha1" 375 | source = "git+https://github.com/holochain/holochain-rust?tag=0.0.25-alpha1#1327abe513af3f5206e87d6b479c4262c1b3011e" 376 | dependencies = [ 377 | "holochain_core_types 0.0.25-alpha1 (git+https://github.com/holochain/holochain-rust?tag=0.0.25-alpha1)", 378 | "holochain_json_api 0.0.15 (registry+https://github.com/rust-lang/crates.io-index)", 379 | "holochain_json_derive 0.0.15 (registry+https://github.com/rust-lang/crates.io-index)", 380 | "holochain_persistence_api 0.0.6 (registry+https://github.com/rust-lang/crates.io-index)", 381 | "serde 1.0.89 (registry+https://github.com/rust-lang/crates.io-index)", 382 | "serde_derive 1.0.89 (registry+https://github.com/rust-lang/crates.io-index)", 383 | "serde_json 1.0.39 (registry+https://github.com/rust-lang/crates.io-index)", 384 | ] 385 | 386 | [[package]] 387 | name = "indexmap" 388 | version = "1.0.2" 389 | source = "registry+https://github.com/rust-lang/crates.io-index" 390 | 391 | [[package]] 392 | name = "itertools" 393 | version = "0.7.11" 394 | source = "registry+https://github.com/rust-lang/crates.io-index" 395 | dependencies = [ 396 | "either 1.5.2 (registry+https://github.com/rust-lang/crates.io-index)", 397 | ] 398 | 399 | [[package]] 400 | name = "itoa" 401 | version = "0.4.4" 402 | source = "registry+https://github.com/rust-lang/crates.io-index" 403 | 404 | [[package]] 405 | name = "lazy_static" 406 | version = "1.2.0" 407 | source = "registry+https://github.com/rust-lang/crates.io-index" 408 | 409 | [[package]] 410 | name = "lib3h_crypto_api" 411 | version = "0.0.8" 412 | source = "registry+https://github.com/rust-lang/crates.io-index" 413 | dependencies = [ 414 | "serde 1.0.89 (registry+https://github.com/rust-lang/crates.io-index)", 415 | "serde_derive 1.0.89 (registry+https://github.com/rust-lang/crates.io-index)", 416 | "zeroize 0.9.2 (registry+https://github.com/rust-lang/crates.io-index)", 417 | ] 418 | 419 | [[package]] 420 | name = "libc" 421 | version = "0.2.58" 422 | source = "registry+https://github.com/rust-lang/crates.io-index" 423 | 424 | [[package]] 425 | name = "main" 426 | version = "0.1.0" 427 | dependencies = [ 428 | "hdk 0.0.25-alpha1 (git+https://github.com/holochain/holochain-rust?tag=0.0.25-alpha1)", 429 | "hdk-proc-macros 0.0.25-alpha1 (git+https://github.com/holochain/holochain-rust?tag=0.0.25-alpha1)", 430 | "holochain_json_derive 0.0.1-alpha2 (registry+https://github.com/rust-lang/crates.io-index)", 431 | "holochain_wasm_utils 0.0.25-alpha1 (git+https://github.com/holochain/holochain-rust?tag=0.0.25-alpha1)", 432 | "serde 1.0.89 (registry+https://github.com/rust-lang/crates.io-index)", 433 | "serde_derive 1.0.89 (registry+https://github.com/rust-lang/crates.io-index)", 434 | "serde_json 1.0.39 (registry+https://github.com/rust-lang/crates.io-index)", 435 | ] 436 | 437 | [[package]] 438 | name = "memchr" 439 | version = "2.2.0" 440 | source = "registry+https://github.com/rust-lang/crates.io-index" 441 | 442 | [[package]] 443 | name = "memory_units" 444 | version = "0.3.0" 445 | source = "registry+https://github.com/rust-lang/crates.io-index" 446 | 447 | [[package]] 448 | name = "multihash" 449 | version = "0.8.0" 450 | source = "registry+https://github.com/rust-lang/crates.io-index" 451 | dependencies = [ 452 | "sha1 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", 453 | "sha2 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)", 454 | "tiny-keccak 1.4.2 (registry+https://github.com/rust-lang/crates.io-index)", 455 | ] 456 | 457 | [[package]] 458 | name = "num" 459 | version = "0.2.0" 460 | source = "registry+https://github.com/rust-lang/crates.io-index" 461 | dependencies = [ 462 | "num-bigint 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", 463 | "num-complex 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", 464 | "num-integer 0.1.41 (registry+https://github.com/rust-lang/crates.io-index)", 465 | "num-iter 0.1.39 (registry+https://github.com/rust-lang/crates.io-index)", 466 | "num-rational 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", 467 | "num-traits 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", 468 | ] 469 | 470 | [[package]] 471 | name = "num-bigint" 472 | version = "0.2.2" 473 | source = "registry+https://github.com/rust-lang/crates.io-index" 474 | dependencies = [ 475 | "num-integer 0.1.41 (registry+https://github.com/rust-lang/crates.io-index)", 476 | "num-traits 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", 477 | ] 478 | 479 | [[package]] 480 | name = "num-complex" 481 | version = "0.2.1" 482 | source = "registry+https://github.com/rust-lang/crates.io-index" 483 | dependencies = [ 484 | "num-traits 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", 485 | ] 486 | 487 | [[package]] 488 | name = "num-integer" 489 | version = "0.1.41" 490 | source = "registry+https://github.com/rust-lang/crates.io-index" 491 | dependencies = [ 492 | "autocfg 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", 493 | "num-traits 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", 494 | ] 495 | 496 | [[package]] 497 | name = "num-iter" 498 | version = "0.1.39" 499 | source = "registry+https://github.com/rust-lang/crates.io-index" 500 | dependencies = [ 501 | "autocfg 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", 502 | "num-integer 0.1.41 (registry+https://github.com/rust-lang/crates.io-index)", 503 | "num-traits 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", 504 | ] 505 | 506 | [[package]] 507 | name = "num-rational" 508 | version = "0.2.1" 509 | source = "registry+https://github.com/rust-lang/crates.io-index" 510 | dependencies = [ 511 | "num-bigint 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", 512 | "num-integer 0.1.41 (registry+https://github.com/rust-lang/crates.io-index)", 513 | "num-traits 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", 514 | ] 515 | 516 | [[package]] 517 | name = "num-traits" 518 | version = "0.2.8" 519 | source = "registry+https://github.com/rust-lang/crates.io-index" 520 | dependencies = [ 521 | "autocfg 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", 522 | ] 523 | 524 | [[package]] 525 | name = "num_cpus" 526 | version = "1.10.0" 527 | source = "registry+https://github.com/rust-lang/crates.io-index" 528 | dependencies = [ 529 | "libc 0.2.58 (registry+https://github.com/rust-lang/crates.io-index)", 530 | ] 531 | 532 | [[package]] 533 | name = "objekt" 534 | version = "0.1.2" 535 | source = "registry+https://github.com/rust-lang/crates.io-index" 536 | 537 | [[package]] 538 | name = "output_vt100" 539 | version = "0.1.2" 540 | source = "registry+https://github.com/rust-lang/crates.io-index" 541 | dependencies = [ 542 | "winapi 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", 543 | ] 544 | 545 | [[package]] 546 | name = "parity-wasm" 547 | version = "0.31.3" 548 | source = "registry+https://github.com/rust-lang/crates.io-index" 549 | dependencies = [ 550 | "byteorder 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)", 551 | ] 552 | 553 | [[package]] 554 | name = "pin-utils" 555 | version = "0.1.0-alpha.4" 556 | source = "registry+https://github.com/rust-lang/crates.io-index" 557 | 558 | [[package]] 559 | name = "pretty_assertions" 560 | version = "0.6.1" 561 | source = "registry+https://github.com/rust-lang/crates.io-index" 562 | dependencies = [ 563 | "ansi_term 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", 564 | "ctor 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", 565 | "difference 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)", 566 | "output_vt100 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", 567 | ] 568 | 569 | [[package]] 570 | name = "proc-macro2" 571 | version = "0.2.3" 572 | source = "registry+https://github.com/rust-lang/crates.io-index" 573 | dependencies = [ 574 | "unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", 575 | ] 576 | 577 | [[package]] 578 | name = "proc-macro2" 579 | version = "0.4.27" 580 | source = "registry+https://github.com/rust-lang/crates.io-index" 581 | dependencies = [ 582 | "unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", 583 | ] 584 | 585 | [[package]] 586 | name = "quote" 587 | version = "0.4.2" 588 | source = "registry+https://github.com/rust-lang/crates.io-index" 589 | dependencies = [ 590 | "proc-macro2 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", 591 | ] 592 | 593 | [[package]] 594 | name = "quote" 595 | version = "0.6.11" 596 | source = "registry+https://github.com/rust-lang/crates.io-index" 597 | dependencies = [ 598 | "proc-macro2 0.4.27 (registry+https://github.com/rust-lang/crates.io-index)", 599 | ] 600 | 601 | [[package]] 602 | name = "rand" 603 | version = "0.5.6" 604 | source = "registry+https://github.com/rust-lang/crates.io-index" 605 | dependencies = [ 606 | "cloudabi 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)", 607 | "fuchsia-cprng 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", 608 | "libc 0.2.58 (registry+https://github.com/rust-lang/crates.io-index)", 609 | "rand_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", 610 | "winapi 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", 611 | ] 612 | 613 | [[package]] 614 | name = "rand_core" 615 | version = "0.3.1" 616 | source = "registry+https://github.com/rust-lang/crates.io-index" 617 | dependencies = [ 618 | "rand_core 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", 619 | ] 620 | 621 | [[package]] 622 | name = "rand_core" 623 | version = "0.4.0" 624 | source = "registry+https://github.com/rust-lang/crates.io-index" 625 | 626 | [[package]] 627 | name = "redox_syscall" 628 | version = "0.1.54" 629 | source = "registry+https://github.com/rust-lang/crates.io-index" 630 | 631 | [[package]] 632 | name = "reed-solomon" 633 | version = "0.2.1" 634 | source = "registry+https://github.com/rust-lang/crates.io-index" 635 | 636 | [[package]] 637 | name = "regex" 638 | version = "1.1.2" 639 | source = "registry+https://github.com/rust-lang/crates.io-index" 640 | dependencies = [ 641 | "aho-corasick 0.6.10 (registry+https://github.com/rust-lang/crates.io-index)", 642 | "memchr 2.2.0 (registry+https://github.com/rust-lang/crates.io-index)", 643 | "regex-syntax 0.6.6 (registry+https://github.com/rust-lang/crates.io-index)", 644 | "thread_local 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", 645 | "utf8-ranges 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", 646 | ] 647 | 648 | [[package]] 649 | name = "regex-syntax" 650 | version = "0.6.6" 651 | source = "registry+https://github.com/rust-lang/crates.io-index" 652 | dependencies = [ 653 | "ucd-util 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", 654 | ] 655 | 656 | [[package]] 657 | name = "rust-base58" 658 | version = "0.0.4" 659 | source = "registry+https://github.com/rust-lang/crates.io-index" 660 | dependencies = [ 661 | "num 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", 662 | ] 663 | 664 | [[package]] 665 | name = "ryu" 666 | version = "0.2.8" 667 | source = "registry+https://github.com/rust-lang/crates.io-index" 668 | 669 | [[package]] 670 | name = "serde" 671 | version = "1.0.89" 672 | source = "registry+https://github.com/rust-lang/crates.io-index" 673 | 674 | [[package]] 675 | name = "serde_derive" 676 | version = "1.0.89" 677 | source = "registry+https://github.com/rust-lang/crates.io-index" 678 | dependencies = [ 679 | "proc-macro2 0.4.27 (registry+https://github.com/rust-lang/crates.io-index)", 680 | "quote 0.6.11 (registry+https://github.com/rust-lang/crates.io-index)", 681 | "syn 0.15.31 (registry+https://github.com/rust-lang/crates.io-index)", 682 | ] 683 | 684 | [[package]] 685 | name = "serde_json" 686 | version = "1.0.39" 687 | source = "registry+https://github.com/rust-lang/crates.io-index" 688 | dependencies = [ 689 | "indexmap 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", 690 | "itoa 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)", 691 | "ryu 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", 692 | "serde 1.0.89 (registry+https://github.com/rust-lang/crates.io-index)", 693 | ] 694 | 695 | [[package]] 696 | name = "sha1" 697 | version = "0.5.0" 698 | source = "registry+https://github.com/rust-lang/crates.io-index" 699 | 700 | [[package]] 701 | name = "sha2" 702 | version = "0.7.1" 703 | source = "registry+https://github.com/rust-lang/crates.io-index" 704 | dependencies = [ 705 | "block-buffer 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", 706 | "byte-tools 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", 707 | "digest 0.7.6 (registry+https://github.com/rust-lang/crates.io-index)", 708 | "fake-simd 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", 709 | ] 710 | 711 | [[package]] 712 | name = "shrinkwraprs" 713 | version = "0.2.1" 714 | source = "registry+https://github.com/rust-lang/crates.io-index" 715 | dependencies = [ 716 | "bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)", 717 | "itertools 0.7.11 (registry+https://github.com/rust-lang/crates.io-index)", 718 | "quote 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", 719 | "syn 0.12.15 (registry+https://github.com/rust-lang/crates.io-index)", 720 | ] 721 | 722 | [[package]] 723 | name = "slab" 724 | version = "0.4.2" 725 | source = "registry+https://github.com/rust-lang/crates.io-index" 726 | 727 | [[package]] 728 | name = "smallvec" 729 | version = "0.6.10" 730 | source = "registry+https://github.com/rust-lang/crates.io-index" 731 | 732 | [[package]] 733 | name = "snowflake" 734 | version = "1.3.0" 735 | source = "registry+https://github.com/rust-lang/crates.io-index" 736 | 737 | [[package]] 738 | name = "syn" 739 | version = "0.12.15" 740 | source = "registry+https://github.com/rust-lang/crates.io-index" 741 | dependencies = [ 742 | "proc-macro2 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", 743 | "quote 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", 744 | "unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", 745 | ] 746 | 747 | [[package]] 748 | name = "syn" 749 | version = "0.15.31" 750 | source = "registry+https://github.com/rust-lang/crates.io-index" 751 | dependencies = [ 752 | "proc-macro2 0.4.27 (registry+https://github.com/rust-lang/crates.io-index)", 753 | "quote 0.6.11 (registry+https://github.com/rust-lang/crates.io-index)", 754 | "unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", 755 | ] 756 | 757 | [[package]] 758 | name = "synstructure" 759 | version = "0.10.2" 760 | source = "registry+https://github.com/rust-lang/crates.io-index" 761 | dependencies = [ 762 | "proc-macro2 0.4.27 (registry+https://github.com/rust-lang/crates.io-index)", 763 | "quote 0.6.11 (registry+https://github.com/rust-lang/crates.io-index)", 764 | "syn 0.15.31 (registry+https://github.com/rust-lang/crates.io-index)", 765 | "unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", 766 | ] 767 | 768 | [[package]] 769 | name = "thread_local" 770 | version = "0.3.6" 771 | source = "registry+https://github.com/rust-lang/crates.io-index" 772 | dependencies = [ 773 | "lazy_static 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", 774 | ] 775 | 776 | [[package]] 777 | name = "time" 778 | version = "0.1.42" 779 | source = "registry+https://github.com/rust-lang/crates.io-index" 780 | dependencies = [ 781 | "libc 0.2.58 (registry+https://github.com/rust-lang/crates.io-index)", 782 | "redox_syscall 0.1.54 (registry+https://github.com/rust-lang/crates.io-index)", 783 | "winapi 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", 784 | ] 785 | 786 | [[package]] 787 | name = "tiny-keccak" 788 | version = "1.4.2" 789 | source = "registry+https://github.com/rust-lang/crates.io-index" 790 | dependencies = [ 791 | "crunchy 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", 792 | ] 793 | 794 | [[package]] 795 | name = "typenum" 796 | version = "1.10.0" 797 | source = "registry+https://github.com/rust-lang/crates.io-index" 798 | 799 | [[package]] 800 | name = "ucd-util" 801 | version = "0.1.3" 802 | source = "registry+https://github.com/rust-lang/crates.io-index" 803 | 804 | [[package]] 805 | name = "unicode-xid" 806 | version = "0.1.0" 807 | source = "registry+https://github.com/rust-lang/crates.io-index" 808 | 809 | [[package]] 810 | name = "utf8-ranges" 811 | version = "1.0.2" 812 | source = "registry+https://github.com/rust-lang/crates.io-index" 813 | 814 | [[package]] 815 | name = "uuid" 816 | version = "0.7.1" 817 | source = "registry+https://github.com/rust-lang/crates.io-index" 818 | dependencies = [ 819 | "rand 0.5.6 (registry+https://github.com/rust-lang/crates.io-index)", 820 | ] 821 | 822 | [[package]] 823 | name = "wasmi" 824 | version = "0.4.4" 825 | source = "registry+https://github.com/rust-lang/crates.io-index" 826 | dependencies = [ 827 | "memory_units 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", 828 | "parity-wasm 0.31.3 (registry+https://github.com/rust-lang/crates.io-index)", 829 | ] 830 | 831 | [[package]] 832 | name = "winapi" 833 | version = "0.3.7" 834 | source = "registry+https://github.com/rust-lang/crates.io-index" 835 | dependencies = [ 836 | "winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", 837 | "winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", 838 | ] 839 | 840 | [[package]] 841 | name = "winapi-i686-pc-windows-gnu" 842 | version = "0.4.0" 843 | source = "registry+https://github.com/rust-lang/crates.io-index" 844 | 845 | [[package]] 846 | name = "winapi-x86_64-pc-windows-gnu" 847 | version = "0.4.0" 848 | source = "registry+https://github.com/rust-lang/crates.io-index" 849 | 850 | [[package]] 851 | name = "zeroize" 852 | version = "0.9.2" 853 | source = "registry+https://github.com/rust-lang/crates.io-index" 854 | dependencies = [ 855 | "zeroize_derive 0.9.3 (registry+https://github.com/rust-lang/crates.io-index)", 856 | ] 857 | 858 | [[package]] 859 | name = "zeroize_derive" 860 | version = "0.9.3" 861 | source = "registry+https://github.com/rust-lang/crates.io-index" 862 | dependencies = [ 863 | "proc-macro2 0.4.27 (registry+https://github.com/rust-lang/crates.io-index)", 864 | "quote 0.6.11 (registry+https://github.com/rust-lang/crates.io-index)", 865 | "syn 0.15.31 (registry+https://github.com/rust-lang/crates.io-index)", 866 | "synstructure 0.10.2 (registry+https://github.com/rust-lang/crates.io-index)", 867 | ] 868 | 869 | [metadata] 870 | "checksum aho-corasick 0.6.10 (registry+https://github.com/rust-lang/crates.io-index)" = "81ce3d38065e618af2d7b77e10c5ad9a069859b4be3c2250f674af3840d9c8a5" 871 | "checksum ansi_term 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ee49baf6cb617b853aa8d93bf420db2383fab46d314482ca2803b40d5fde979b" 872 | "checksum arrayref 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)" = "0d382e583f07208808f6b1249e60848879ba3543f57c32277bf52d69c2f0f0ee" 873 | "checksum autocfg 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "0e49efa51329a5fd37e7c79db4621af617cd4e3e5bc224939808d076077077bf" 874 | "checksum base64 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)" = "0b25d992356d2eb0ed82172f5248873db5560c4721f564b13cb5193bda5e668e" 875 | "checksum bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "228047a76f468627ca71776ecdebd732a3423081fcf5125585bcd7c49886ce12" 876 | "checksum block-buffer 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "a076c298b9ecdb530ed9d967e74a6027d6a7478924520acddcddc24c1c8ab3ab" 877 | "checksum byte-tools 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "560c32574a12a89ecd91f5e742165893f86e3ab98d21f8ea548658eb9eef5f40" 878 | "checksum byteorder 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "a019b10a2a7cdeb292db131fc8113e57ea2a908f6e7894b0c3c671893b65dbeb" 879 | "checksum cfg-if 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)" = "b486ce3ccf7ffd79fdeb678eac06a9e6c09fc88d33836340becb8fffe87c5e33" 880 | "checksum chrono 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)" = "45912881121cb26fad7c38c17ba7daa18764771836b34fab7d3fbd93ed633878" 881 | "checksum cloudabi 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "ddfc5b9aa5d4507acaf872de71051dfd0e309860e88966e1051e462a077aac4f" 882 | "checksum crossbeam-channel 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)" = "0f0ed1a4de2235cabda8558ff5840bffb97fcb64c97827f354a451307df5f72b" 883 | "checksum crossbeam-utils 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)" = "f8306fcef4a7b563b76b7dd949ca48f52bc1141aa067d2ea09565f3e2652aa5c" 884 | "checksum crunchy 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "a2f4a431c5c9f662e1200b7c7f02c34e91361150e382089a8f2dec3ba680cbda" 885 | "checksum ctor 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)" = "3b4c17619643c1252b5f690084b82639dd7fac141c57c8e77a00e0148132092c" 886 | "checksum difference 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "524cbf6897b527295dff137cec09ecf3a05f4fddffd7dfcd1585403449e74198" 887 | "checksum digest 0.7.6 (registry+https://github.com/rust-lang/crates.io-index)" = "03b072242a8cbaf9c145665af9d250c59af3b958f83ed6824e13533cf76d5b90" 888 | "checksum either 1.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "5527cfe0d098f36e3f8839852688e63c8fff1c90b2b405aef730615f9a7bcf7b" 889 | "checksum fake-simd 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "e88a8acf291dafb59c2d96e8f59828f3838bb1a70398823ade51a84de6a6deed" 890 | "checksum fuchsia-cprng 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "a06f77d526c1a601b7c4cdd98f54b5eaabffc14d5f2f0296febdc7f357c6d3ba" 891 | "checksum futures-channel-preview 0.3.0-alpha.16 (registry+https://github.com/rust-lang/crates.io-index)" = "4cd523712fc272e9b714669165a2832debee5a5b7e409bfccdc7c0d5cd0cf07a" 892 | "checksum futures-core-preview 0.3.0-alpha.16 (registry+https://github.com/rust-lang/crates.io-index)" = "719770f328642b657b849856bb5a607db9538dd5bb3000122e5ead55d0a58c36" 893 | "checksum futures-executor-preview 0.3.0-alpha.16 (registry+https://github.com/rust-lang/crates.io-index)" = "315dc58c908535d059576a329b86cd185933433382cfcd394fb2fa353330de03" 894 | "checksum futures-io-preview 0.3.0-alpha.16 (registry+https://github.com/rust-lang/crates.io-index)" = "cca0bf7a1f39c9d32b797b0def93d5932aa71796236aad6b549bac6f7df159a3" 895 | "checksum futures-preview 0.3.0-alpha.16 (registry+https://github.com/rust-lang/crates.io-index)" = "fcfeac5f016a4b5835bb93eb7961f50a64f0e001207562703d9ddf4109d7b263" 896 | "checksum futures-sink-preview 0.3.0-alpha.16 (registry+https://github.com/rust-lang/crates.io-index)" = "49dcfdacd6b5974ca0b9b78bc38ffd1071da0206179735c3df82e279f5b784e4" 897 | "checksum futures-util-preview 0.3.0-alpha.16 (registry+https://github.com/rust-lang/crates.io-index)" = "f7a0451b9c5047c2b9ab93425ffd0793165511e93c04b977cd45fbd41c6e34b2" 898 | "checksum generic-array 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ef25c5683767570c2bbd7deba372926a55eaae9982d7726ee2a1050239d45b9d" 899 | "checksum hcid 0.0.6 (registry+https://github.com/rust-lang/crates.io-index)" = "9e5ea27f6b17df2ded5dcfc492ecd0db719d00b144dbaaf2df1658a7e38cfd2e" 900 | "checksum hdk 0.0.25-alpha1 (git+https://github.com/holochain/holochain-rust?tag=0.0.25-alpha1)" = "" 901 | "checksum hdk-proc-macros 0.0.25-alpha1 (git+https://github.com/holochain/holochain-rust?tag=0.0.25-alpha1)" = "" 902 | "checksum holochain_core_types 0.0.25-alpha1 (git+https://github.com/holochain/holochain-rust?tag=0.0.25-alpha1)" = "" 903 | "checksum holochain_json_api 0.0.15 (registry+https://github.com/rust-lang/crates.io-index)" = "4b2ad76558f8be0b295202089987899b2cfd8c715b90275dcda490dab16be1a8" 904 | "checksum holochain_json_derive 0.0.1-alpha2 (registry+https://github.com/rust-lang/crates.io-index)" = "57d7eb950d0154f032b0cbabdcb9f5d4bd10795b4606cf9df8330642896a2bbd" 905 | "checksum holochain_json_derive 0.0.15 (registry+https://github.com/rust-lang/crates.io-index)" = "64c62a8b6d5fc20b7c4d0256c1ab5a611d1fa2064798b64d3f17521ec602bc1d" 906 | "checksum holochain_persistence_api 0.0.6 (registry+https://github.com/rust-lang/crates.io-index)" = "824c5fcae141934ac87e61f7113659db31ff45d5305771c92cafbed1163ff2a2" 907 | "checksum holochain_wasm_utils 0.0.25-alpha1 (git+https://github.com/holochain/holochain-rust?tag=0.0.25-alpha1)" = "" 908 | "checksum indexmap 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7e81a7c05f79578dbc15793d8b619db9ba32b4577003ef3af1a91c416798c58d" 909 | "checksum itertools 0.7.11 (registry+https://github.com/rust-lang/crates.io-index)" = "0d47946d458e94a1b7bcabbf6521ea7c037062c81f534615abcad76e84d4970d" 910 | "checksum itoa 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)" = "501266b7edd0174f8530248f87f99c88fbe60ca4ef3dd486835b8d8d53136f7f" 911 | "checksum lazy_static 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "a374c89b9db55895453a74c1e38861d9deec0b01b405a82516e9d5de4820dea1" 912 | "checksum lib3h_crypto_api 0.0.8 (registry+https://github.com/rust-lang/crates.io-index)" = "b324f2f7b665bccfce47a7422d467fbc2cbd62f1571339c879c4c19174534b5d" 913 | "checksum libc 0.2.58 (registry+https://github.com/rust-lang/crates.io-index)" = "6281b86796ba5e4366000be6e9e18bf35580adf9e63fbe2294aadb587613a319" 914 | "checksum memchr 2.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "2efc7bc57c883d4a4d6e3246905283d8dae951bb3bd32f49d6ef297f546e1c39" 915 | "checksum memory_units 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "71d96e3f3c0b6325d8ccd83c33b28acb183edcb6c67938ba104ec546854b0882" 916 | "checksum multihash 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c62469025f45dee2464ef9fc845f4683c543993792c1993e7d903c17a4546b74" 917 | "checksum num 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "cf4825417e1e1406b3782a8ce92f4d53f26ec055e3622e1881ca8e9f5f9e08db" 918 | "checksum num-bigint 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "57450397855d951f1a41305e54851b1a7b8f5d2e349543a02a2effe25459f718" 919 | "checksum num-complex 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "107b9be86cd2481930688277b675b0114578227f034674726605b8a482d8baf8" 920 | "checksum num-integer 0.1.41 (registry+https://github.com/rust-lang/crates.io-index)" = "b85e541ef8255f6cf42bbfe4ef361305c6c135d10919ecc26126c4e5ae94bc09" 921 | "checksum num-iter 0.1.39 (registry+https://github.com/rust-lang/crates.io-index)" = "76bd5272412d173d6bf9afdf98db8612bbabc9a7a830b7bfc9c188911716132e" 922 | "checksum num-rational 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "4e96f040177bb3da242b5b1ecf3f54b5d5af3efbbfb18608977a5d2767b22f10" 923 | "checksum num-traits 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)" = "6ba9a427cfca2be13aa6f6403b0b7e7368fe982bfa16fccc450ce74c46cd9b32" 924 | "checksum num_cpus 1.10.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1a23f0ed30a54abaa0c7e83b1d2d87ada7c3c23078d1d87815af3e3b6385fbba" 925 | "checksum objekt 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "2069a3ae3dad97a4ae47754e8f47e5d2f1fd32ab7ad8a84bb31d051faa59cc3c" 926 | "checksum output_vt100 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "53cdc5b785b7a58c5aad8216b3dfa114df64b0b06ae6e1501cef91df2fbdf8f9" 927 | "checksum parity-wasm 0.31.3 (registry+https://github.com/rust-lang/crates.io-index)" = "511379a8194230c2395d2f5fa627a5a7e108a9f976656ce723ae68fca4097bfc" 928 | "checksum pin-utils 0.1.0-alpha.4 (registry+https://github.com/rust-lang/crates.io-index)" = "5894c618ce612a3fa23881b152b608bafb8c56cfc22f434a3ba3120b40f7b587" 929 | "checksum pretty_assertions 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)" = "3f81e1644e1b54f5a68959a29aa86cde704219254669da328ecfdf6a1f09d427" 930 | "checksum proc-macro2 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "cd07deb3c6d1d9ff827999c7f9b04cdfd66b1b17ae508e14fe47b620f2282ae0" 931 | "checksum proc-macro2 0.4.27 (registry+https://github.com/rust-lang/crates.io-index)" = "4d317f9caece796be1980837fd5cb3dfec5613ebdb04ad0956deea83ce168915" 932 | "checksum quote 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "1eca14c727ad12702eb4b6bfb5a232287dcf8385cb8ca83a3eeaf6519c44c408" 933 | "checksum quote 0.6.11 (registry+https://github.com/rust-lang/crates.io-index)" = "cdd8e04bd9c52e0342b406469d494fcb033be4bdbe5c606016defbb1681411e1" 934 | "checksum rand 0.5.6 (registry+https://github.com/rust-lang/crates.io-index)" = "c618c47cd3ebd209790115ab837de41425723956ad3ce2e6a7f09890947cacb9" 935 | "checksum rand_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "7a6fdeb83b075e8266dcc8762c22776f6877a63111121f5f8c7411e5be7eed4b" 936 | "checksum rand_core 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d0e7a549d590831370895ab7ba4ea0c1b6b011d106b5ff2da6eee112615e6dc0" 937 | "checksum redox_syscall 0.1.54 (registry+https://github.com/rust-lang/crates.io-index)" = "12229c14a0f65c4f1cb046a3b52047cdd9da1f4b30f8a39c5063c8bae515e252" 938 | "checksum reed-solomon 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "13de68c877a77f35885442ac72c8beb7c2f0b09380c43b734b9d63d1db69ee54" 939 | "checksum regex 1.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "53ee8cfdddb2e0291adfb9f13d31d3bbe0a03c9a402c01b1e24188d86c35b24f" 940 | "checksum regex-syntax 0.6.6 (registry+https://github.com/rust-lang/crates.io-index)" = "dcfd8681eebe297b81d98498869d4aae052137651ad7b96822f09ceb690d0a96" 941 | "checksum rust-base58 0.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "b313b91fcdc6719ad41fa2dad2b7e810b03833fae4bf911950e15529a5f04439" 942 | "checksum ryu 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)" = "b96a9549dc8d48f2c283938303c4b5a77aa29bfbc5b54b084fb1630408899a8f" 943 | "checksum serde 1.0.89 (registry+https://github.com/rust-lang/crates.io-index)" = "92514fb95f900c9b5126e32d020f5c6d40564c27a5ea6d1d7d9f157a96623560" 944 | "checksum serde_derive 1.0.89 (registry+https://github.com/rust-lang/crates.io-index)" = "bb6eabf4b5914e88e24eea240bb7c9f9a2cbc1bbbe8d961d381975ec3c6b806c" 945 | "checksum serde_json 1.0.39 (registry+https://github.com/rust-lang/crates.io-index)" = "5a23aa71d4a4d43fdbfaac00eff68ba8a06a51759a89ac3304323e800c4dd40d" 946 | "checksum sha1 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "171698ce4ec7cbb93babeb3190021b4d72e96ccb98e33d277ae4ea959d6f2d9e" 947 | "checksum sha2 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)" = "9eb6be24e4c23a84d7184280d2722f7f2731fcdd4a9d886efbfe4413e4847ea0" 948 | "checksum shrinkwraprs 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "7d5f047b90b2ca2d1526ff73d67cba61f86f4cf9a8afddc99dd96702ded8e684" 949 | "checksum slab 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "c111b5bd5695e56cffe5129854aa230b39c93a305372fdbb2668ca2394eea9f8" 950 | "checksum smallvec 0.6.10 (registry+https://github.com/rust-lang/crates.io-index)" = "ab606a9c5e214920bb66c458cd7be8ef094f813f20fe77a54cc7dbfff220d4b7" 951 | "checksum snowflake 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "27207bb65232eda1f588cf46db2fee75c0808d557f6b3cf19a75f5d6d7c94df1" 952 | "checksum syn 0.12.15 (registry+https://github.com/rust-lang/crates.io-index)" = "c97c05b8ebc34ddd6b967994d5c6e9852fa92f8b82b3858c39451f97346dcce5" 953 | "checksum syn 0.15.31 (registry+https://github.com/rust-lang/crates.io-index)" = "d2b4cfac95805274c6afdb12d8f770fa2d27c045953e7b630a81801953699a9a" 954 | "checksum synstructure 0.10.2 (registry+https://github.com/rust-lang/crates.io-index)" = "02353edf96d6e4dc81aea2d8490a7e9db177bf8acb0e951c24940bf866cb313f" 955 | "checksum thread_local 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)" = "c6b53e329000edc2b34dbe8545fd20e55a333362d0a321909685a19bd28c3f1b" 956 | "checksum time 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)" = "db8dcfca086c1143c9270ac42a2bbd8a7ee477b78ac8e45b19abfb0cbede4b6f" 957 | "checksum tiny-keccak 1.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "e9175261fbdb60781fcd388a4d6cc7e14764a2b629a7ad94abb439aed223a44f" 958 | "checksum typenum 1.10.0 (registry+https://github.com/rust-lang/crates.io-index)" = "612d636f949607bdf9b123b4a6f6d966dedf3ff669f7f045890d3a4a73948169" 959 | "checksum ucd-util 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "535c204ee4d8434478593480b8f86ab45ec9aae0e83c568ca81abf0fd0e88f86" 960 | "checksum unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "fc72304796d0818e357ead4e000d19c9c174ab23dc11093ac919054d20a6a7fc" 961 | "checksum utf8-ranges 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "796f7e48bef87609f7ade7e06495a87d5cd06c7866e6a5cbfceffc558a243737" 962 | "checksum uuid 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)" = "dab5c5526c5caa3d106653401a267fed923e7046f35895ffcb5ca42db64942e6" 963 | "checksum wasmi 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)" = "f6a891b45c79e9f96fb66cc84a057211ef9cd2e5e8d093f3dbbd480e146a8758" 964 | "checksum winapi 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)" = "f10e386af2b13e47c89e7236a7a14a086791a2b88ebad6df9bf42040195cf770" 965 | "checksum winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" 966 | "checksum winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" 967 | "checksum zeroize 0.9.2 (registry+https://github.com/rust-lang/crates.io-index)" = "4177936c03b5a349c1b8e4509c46add268e66bc66fe92663729fa0570fe4f213" 968 | "checksum zeroize_derive 0.9.3 (registry+https://github.com/rust-lang/crates.io-index)" = "080616bd0e31f36095288bb0acdf1f78ef02c2fa15527d7e993f2a6c7591643e" 969 | -------------------------------------------------------------------------------- /zomes/main/code/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "main" 3 | version = "0.1.0" 4 | authors = ["willem "] 5 | edition = "2018" 6 | 7 | [dependencies] 8 | serde = "=1.0.89" 9 | serde_json = { version = "=1.0.39", features = ["preserve_order"] } 10 | serde_derive = "=1.0.89" 11 | hdk = { git = "https://github.com/holochain/holochain-rust", tag = "0.0.25-alpha1" } 12 | hdk-proc-macros = { git = "https://github.com/holochain/holochain-rust", tag = "0.0.25-alpha1" } 13 | holochain_wasm_utils = { git = "https://github.com/holochain/holochain-rust", tag = "0.0.25-alpha1" } 14 | holochain_json_derive = { version = "0.0.1-alpha2" } 15 | 16 | [lib] 17 | path = "src/lib.rs" 18 | crate-type = ["cdylib"] 19 | -------------------------------------------------------------------------------- /zomes/main/code/src/game.rs: -------------------------------------------------------------------------------- 1 | use std::convert::TryFrom; 2 | use hdk::{ 3 | utils, 4 | entry_definition::ValidatingEntryType, 5 | error::{ZomeApiResult, ZomeApiError}, 6 | holochain_persistence_api::{ 7 | cas::content::{AddressableContent, Address}, 8 | }, 9 | holochain_json_api::{ 10 | error::JsonError, json::JsonString, 11 | }, 12 | holochain_core_types::{ 13 | dna::entry_types::Sharing, 14 | validation::EntryValidationData, 15 | entry::Entry, 16 | link::LinkMatch, 17 | } 18 | }; 19 | 20 | use crate::game_move::Move; 21 | use crate::GameState; 22 | 23 | #[derive(Clone, Debug, Serialize, Deserialize, DefaultJson)] 24 | pub struct Game { 25 | pub player_1: Address, 26 | pub player_2: Address, 27 | pub created_at: u32, 28 | } 29 | 30 | /*===================================== 31 | = DHT Functions = 32 | =====================================*/ 33 | 34 | /// Traverse the linked list rooted at a game to find all the moves 35 | pub fn get_moves(game_address: &Address) -> ZomeApiResult> { 36 | match hdk::get_links(game_address, LinkMatch::Any, LinkMatch::Any)?.addresses().into_iter().next() { 37 | Some(first_move) => { 38 | let mut move_addresses = vec![first_move]; 39 | let mut more = true; 40 | while more { 41 | more = match hdk::get_links(move_addresses.last().unwrap(), LinkMatch::Any, LinkMatch::Any)?.addresses().into_iter().next() { 42 | Some(addr) => { 43 | move_addresses.push(addr.clone()); 44 | true 45 | }, 46 | None => { 47 | false 48 | }, 49 | } 50 | } 51 | let moves: Vec = move_addresses.iter().map(|addr| { 52 | let move_entry = hdk::get_entry(addr).unwrap().unwrap(); 53 | if let Entry::App(_, move_struct) = move_entry { 54 | Move::try_from(move_struct).expect("Entry at address is type other than Move") 55 | } else { 56 | panic!("Not an app entry!") 57 | } 58 | }).collect(); 59 | Ok(moves) 60 | }, 61 | None => { 62 | Ok(Vec::new()) 63 | } 64 | } 65 | } 66 | 67 | pub fn get_state(game_address: &Address) -> ZomeApiResult { 68 | let moves = get_moves(game_address)?; 69 | let game = get_game(game_address)?; 70 | let new_state = moves.iter().fold(GameState::initial(), |state, new_move| state.evolve(game.clone(), new_move)); 71 | Ok(new_state) 72 | } 73 | 74 | pub fn get_game(game_address: &Address) -> ZomeApiResult { 75 | utils::get_as_type(game_address.to_owned()) 76 | } 77 | 78 | /*===== End of DHT Functions ======*/ 79 | 80 | 81 | 82 | /*============================================= 83 | = Local chain functions = 84 | =============================================*/ 85 | 86 | pub fn get_game_local_chain(local_chain: Vec, game_address: &Address) -> ZomeApiResult { 87 | local_chain 88 | .iter() 89 | .filter(|entry| { 90 | entry.address() == game_address.to_owned() 91 | }) 92 | .filter_map(|entry| { 93 | if let Entry::App(_, entry_data) = entry { 94 | Some(Game::try_from(entry_data.clone()).unwrap()) 95 | } else { 96 | None 97 | } 98 | }) 99 | .next() 100 | .ok_or(ZomeApiError::HashNotFound) 101 | } 102 | 103 | pub fn get_moves_local_chain(local_chain: Vec, game_address: &Address) -> ZomeApiResult> { 104 | Ok(local_chain 105 | .iter() 106 | .filter_map(|entry| { 107 | if let Entry::App(entry_type, entry_data) = entry { 108 | if entry_type.to_string() == "move" { 109 | Some(Move::try_from(entry_data.clone()).unwrap()) 110 | } else { 111 | None 112 | } 113 | } else { 114 | None 115 | } 116 | }) 117 | .filter(|game_move| { 118 | game_move.game == game_address.to_owned() 119 | }) 120 | .rev() 121 | .collect()) 122 | } 123 | 124 | pub fn get_state_local_chain(local_chain: Vec, game_address: &Address) -> ZomeApiResult { 125 | let moves = get_moves_local_chain(local_chain.clone(), game_address)?; 126 | let game = get_game_local_chain(local_chain, game_address)?; 127 | let new_state = moves.iter().fold(GameState::initial(), move |state, new_move| state.evolve(game.clone(), new_move)); 128 | Ok(new_state) 129 | } 130 | 131 | 132 | /*===== End of Local chain functions ======*/ 133 | 134 | 135 | 136 | 137 | pub fn definition() -> ValidatingEntryType { 138 | entry!( 139 | name: "game", 140 | description: "Represents an occurence of a game between several agents", 141 | sharing: Sharing::Public, 142 | validation_package: || { 143 | hdk::ValidationPackageDefinition::Entry 144 | }, 145 | 146 | validation: | validation_data: hdk::EntryValidationData| { 147 | match validation_data { 148 | EntryValidationData::Create{entry, validation_data: _} => { 149 | let game = entry as Game; 150 | if game.player_1 == game.player_2 { 151 | return Err("Player 1 and Player 2 must be different agents.".into()) 152 | } 153 | Ok(()) 154 | }, 155 | _ => { 156 | Err("Cannot modify or delete a game".into()) 157 | } 158 | } 159 | } 160 | ) 161 | } 162 | -------------------------------------------------------------------------------- /zomes/main/code/src/game_move.rs: -------------------------------------------------------------------------------- 1 | use hdk::{ 2 | entry_definition::ValidatingEntryType, 3 | holochain_persistence_api::{ 4 | cas::content::{Address}, 5 | }, 6 | holochain_json_api::{ 7 | error::JsonError, json::JsonString, 8 | }, 9 | holochain_core_types::{ 10 | dna::entry_types::Sharing, 11 | validation::EntryValidationData, 12 | entry::Entry, 13 | } 14 | }; 15 | 16 | use crate::MoveType; 17 | use crate::game::{get_game_local_chain, get_state_local_chain}; 18 | 19 | 20 | #[derive(Clone, Debug, Serialize, Deserialize, DefaultJson)] 21 | pub struct MoveInput { 22 | pub game: Address, 23 | pub move_type: MoveType, 24 | pub timestamp: u32, 25 | } 26 | 27 | 28 | #[derive(Clone, Debug, Serialize, Deserialize, DefaultJson, PartialEq)] 29 | pub struct Move { 30 | pub game: Address, 31 | pub author: Address, 32 | pub move_type: MoveType, 33 | pub previous_move: Address, 34 | pub timestamp: u32, 35 | } 36 | 37 | pub fn definition() -> ValidatingEntryType { 38 | entry!( 39 | name: "move", 40 | description: "A move by an agent in an game", 41 | sharing: Sharing::Public, 42 | validation_package: || { 43 | hdk::ValidationPackageDefinition::ChainFull 44 | }, 45 | 46 | validation: | validation_data: hdk::EntryValidationData| { 47 | match validation_data { 48 | EntryValidationData::Create{entry, validation_data} => { 49 | let mut local_chain = validation_data.package.source_chain_entries 50 | .ok_or("Could not retrieve source chain")?; 51 | hdk::debug(format!("{:?}", local_chain))?; 52 | 53 | // load the game and game state 54 | let _new_move = Move::from(entry); 55 | 56 | // Sometimes the validating entry is already in the chain when validation runs, 57 | // To make our state reduction work correctly this must be removed 58 | local_chain.remove_item(&Entry::App("move".into() , _new_move.clone().into())); 59 | 60 | let state = get_state_local_chain(local_chain.clone(), &_new_move.game) 61 | .map_err(|_| "Could not load state during validation")?; 62 | let game = get_game_local_chain(local_chain, &_new_move.game) 63 | .map_err(|_| "Could not load game during validation")?; 64 | 65 | _new_move.is_valid(game, state) 66 | }, 67 | _ => { 68 | Err("Cannot modify or delete a move".into()) 69 | } 70 | } 71 | }, 72 | 73 | links: [ 74 | from!( 75 | "game", 76 | link_type: "game->move", 77 | validation_package: || { 78 | hdk::ValidationPackageDefinition::Entry 79 | }, 80 | validation: | _validation_data: hdk::LinkValidationData| { 81 | Ok(()) 82 | } 83 | ), 84 | from!( 85 | "move", 86 | link_type: "move->move", 87 | validation_package: || { 88 | hdk::ValidationPackageDefinition::Entry 89 | }, 90 | validation: | _validation_data: hdk::LinkValidationData| { 91 | Ok(()) 92 | } 93 | ) 94 | ] 95 | ) 96 | } 97 | -------------------------------------------------------------------------------- /zomes/main/code/src/lib.rs: -------------------------------------------------------------------------------- 1 | #![feature(vec_remove_item, proc_macro_hygiene)] 2 | #[macro_use] 3 | extern crate hdk; 4 | extern crate serde; 5 | #[macro_use] 6 | extern crate serde_derive; 7 | extern crate serde_json; 8 | #[macro_use] 9 | extern crate holochain_json_derive; 10 | 11 | extern crate hdk_proc_macros; 12 | use hdk_proc_macros::zome; 13 | 14 | use hdk::{ 15 | AGENT_ADDRESS, 16 | entry_definition::ValidatingEntryType, 17 | error::ZomeApiResult, 18 | holochain_persistence_api::{ 19 | cas::content::{ 20 | Address, 21 | AddressableContent, 22 | }, 23 | }, 24 | holochain_core_types::{ 25 | entry::Entry, 26 | }, 27 | }; 28 | 29 | /** 30 | * DEVCAMP TODO #8: 31 | * Replace the tictactoe game import with your own game imports 32 | */ 33 | mod tictactoe; 34 | pub use tictactoe::{ 35 | GameState, 36 | MoveType, 37 | }; 38 | 39 | 40 | mod game; 41 | mod game_move; 42 | mod matchmaking; 43 | 44 | use game::Game; 45 | use game_move::{Move, MoveInput}; 46 | use matchmaking::{GameProposal, GetResponse}; 47 | 48 | #[zome] 49 | pub mod main { 50 | 51 | #[init] 52 | pub fn init() { 53 | Ok(()) 54 | } 55 | 56 | #[validate_agent] 57 | pub fn validate_agent(validation_data: EntryValidationData) { 58 | Ok(()) 59 | } 60 | 61 | /*========================================= 62 | = Entry Definitions = 63 | =========================================*/ 64 | 65 | #[entry_def] 66 | fn game_entry_def() -> ValidatingEntryType { 67 | game::definition() 68 | } 69 | 70 | #[entry_def] 71 | fn game_move_entry_def() -> ValidatingEntryType { 72 | game_move::definition() 73 | } 74 | 75 | #[entry_def] 76 | fn game_proposal_def() -> ValidatingEntryType { 77 | matchmaking::game_proposal_def() 78 | } 79 | 80 | #[entry_def] 81 | fn anchor_def() -> ValidatingEntryType { 82 | matchmaking::anchor_def() 83 | } 84 | 85 | /*===== End of Entry Definitions ======*/ 86 | 87 | 88 | /*====================================== 89 | = Zome functions = 90 | ======================================*/ 91 | 92 | #[zome_fn("hc_public")] 93 | fn create_game(opponent: Address, timestamp: u32) -> ZomeApiResult
{ 94 | let new_game = Game { 95 | player_1: AGENT_ADDRESS.to_string().into(), 96 | player_2: opponent, 97 | created_at: timestamp, 98 | }; 99 | let game_entry = Entry::App( 100 | "game".into(), 101 | new_game.into(), 102 | ); 103 | hdk::commit_entry(&game_entry) 104 | } 105 | 106 | #[zome_fn("hc_public")] 107 | fn make_move(new_move: MoveInput) -> ZomeApiResult<()> { 108 | // get all the moves from the DHT by following the hash chain 109 | let published_moves = game::get_moves(&new_move.game)?; 110 | 111 | // get all moves in this agents local chain 112 | let chain_moves = hdk::query("move".into(), 0, 0)?; 113 | 114 | // Update this agents local chain to match the game state 115 | let base_address = match published_moves.clone().last() { 116 | Some(last_move) => { // add any moves NOT found in the DHT to this agents local chain 117 | for _move in &published_moves { 118 | let move_entry = Entry::App("move".into(), _move.into()); 119 | if !chain_moves.contains(&move_entry.address()) { 120 | hdk::commit_entry(&move_entry)?; 121 | } 122 | } 123 | Entry::App("move".into(), last_move.into()).address() 124 | 125 | } 126 | None => { // no moves have been made so commit the Game to local chain 127 | let game = game::get_game(&new_move.game)?; 128 | let game_entry = Entry::App("game".into(), game.into()); 129 | hdk::commit_entry(&game_entry)? 130 | } 131 | }; 132 | 133 | let new_move = Move { 134 | game: new_move.game, 135 | author: AGENT_ADDRESS.to_string().into(), 136 | move_type: new_move.move_type, 137 | previous_move: base_address.clone(), 138 | timestamp: new_move.timestamp, 139 | }; 140 | let move_entry = Entry::App( 141 | "move".into(), 142 | new_move.into(), 143 | ); 144 | let move_address = hdk::commit_entry(&move_entry)?; 145 | 146 | match published_moves.last() { 147 | Some(_) => { 148 | // base is a move 149 | hdk::link_entries(&base_address, &move_address, "move->move", "")?; 150 | } 151 | None => { 152 | // base is a game 153 | hdk::link_entries(&base_address, &move_address, "game->move", "")?; 154 | } 155 | } 156 | 157 | Ok(()) 158 | } 159 | 160 | #[zome_fn("hc_public")] 161 | fn get_game_hash(opponent: Address, timestamp: u32) -> ZomeApiResult
{ 162 | let new_game = Game { 163 | player_1: opponent, 164 | player_2: AGENT_ADDRESS.to_string().into(), 165 | created_at: timestamp, 166 | }; 167 | Ok(Entry::App( 168 | "game".into(), 169 | new_game.into(), 170 | ).address()) 171 | } 172 | 173 | #[zome_fn("hc_public")] 174 | fn get_state(game_address: Address) -> ZomeApiResult { 175 | game::get_state(&game_address) 176 | } 177 | 178 | #[zome_fn("hc_public")] 179 | fn render_state(game_address: Address) -> ZomeApiResult { 180 | Ok(game::get_state(&game_address)?.render()) 181 | } 182 | 183 | #[zome_fn("hc_public")] 184 | fn get_valid_moves() -> ZomeApiResult> { 185 | Ok(MoveType::describe()) 186 | } 187 | 188 | #[zome_fn("hc_public")] 189 | fn whoami() -> ZomeApiResult
{ 190 | Ok(AGENT_ADDRESS.to_string().into()) 191 | } 192 | 193 | #[zome_fn("hc_public")] 194 | fn create_proposal(message: String) -> ZomeApiResult
{ 195 | matchmaking::handle_create_proposal(message) 196 | } 197 | 198 | #[zome_fn("hc_public")] 199 | fn get_proposals() -> ZomeApiResult>> { 200 | matchmaking::handle_get_proposals() 201 | } 202 | 203 | #[zome_fn("hc_public")] 204 | fn accept_proposal(proposal_addr: Address, created_at: u32) -> ZomeApiResult
{ 205 | matchmaking::handle_accept_proposal(proposal_addr, created_at) 206 | } 207 | 208 | #[zome_fn("hc_public")] 209 | fn check_responses(proposal_addr: Address) -> ZomeApiResult>> { 210 | matchmaking::handle_check_responses(proposal_addr) 211 | } 212 | 213 | #[zome_fn("hc_public")] 214 | fn remove_proposal(proposal_addr: Address) -> ZomeApiResult
{ 215 | matchmaking::handle_remove_proposal(proposal_addr) 216 | } 217 | /*===== End of Zome functions ======*/ 218 | } 219 | -------------------------------------------------------------------------------- /zomes/main/code/src/matchmaking.rs: -------------------------------------------------------------------------------- 1 | use hdk::{ 2 | AGENT_ADDRESS, 3 | entry_definition::ValidatingEntryType, 4 | error::ZomeApiResult, 5 | holochain_persistence_api::{ 6 | cas::content::{AddressableContent, Address}, 7 | }, 8 | holochain_json_api::{ 9 | error::JsonError, json::{JsonString, default_to_json}, 10 | }, 11 | holochain_core_types::{ 12 | dna::entry_types::Sharing, 13 | validation::EntryValidationData, 14 | entry::Entry, 15 | link::LinkMatch, 16 | } 17 | }; 18 | 19 | use serde::Serialize; 20 | use std::fmt::Debug; 21 | 22 | use crate::game::Game; 23 | 24 | #[derive(Serialize, Deserialize, Debug, DefaultJson, Clone)] 25 | pub struct GameProposal { 26 | pub agent: Address, 27 | pub message: String, 28 | } 29 | 30 | #[derive(Serialize, Deserialize, Debug, Clone)] 31 | pub struct GetResponse { 32 | pub entry: T, 33 | pub address: Address 34 | } 35 | 36 | impl + Debug + Serialize> From> for JsonString { 37 | fn from(u: GetResponse) -> JsonString { 38 | default_to_json(u) 39 | } 40 | } 41 | 42 | pub fn handle_create_proposal(message: String) -> ZomeApiResult
{ 43 | 44 | // create the data as a struct 45 | let game_proposal_data = GameProposal { 46 | agent: AGENT_ADDRESS.to_string().into(), 47 | message, 48 | }; 49 | 50 | // create an entry 51 | let entry = Entry::App( 52 | "game_proposal".into(), 53 | game_proposal_data.into(), 54 | ); 55 | 56 | // commit the entry. '?' means return immedietly on error 57 | let proposal_address = hdk::commit_entry(&entry)?; 58 | 59 | // create an anchor entry and commit. 60 | // The native type is string so we can skip the first step 61 | let anchor_entry = Entry::App( 62 | "anchor".into(), 63 | "game_proposals".into(), 64 | ); 65 | let anchor_address = hdk::commit_entry(&anchor_entry)?; 66 | 67 | // finally link them together 68 | hdk::link_entries( 69 | &anchor_address, 70 | &proposal_address, 71 | "has_proposal", // the link type, defined on the base entry 72 | "" // the tag which is not used in this example 73 | )?; 74 | 75 | // return the proposal address 76 | Ok(proposal_address) 77 | } 78 | 79 | pub fn handle_get_proposals() -> ZomeApiResult>> { 80 | // define the anchor entry again and compute its hash 81 | let anchor_address = Entry::App( 82 | "anchor".into(), 83 | "game_proposals".into() 84 | ).address(); 85 | 86 | Ok( 87 | hdk::utils::get_links_and_load_type( 88 | &anchor_address, 89 | LinkMatch::Exactly("has_proposal"), // the link type to match 90 | LinkMatch::Any 91 | )?.into_iter().map(|proposal: GameProposal| { 92 | let address = Entry::App("game_proposal".into(), proposal.clone().into()).address(); 93 | GetResponse{entry: proposal, address} 94 | }).collect() 95 | ) 96 | } 97 | 98 | pub fn handle_accept_proposal(proposal_addr: Address, created_at: u32) -> ZomeApiResult
{ 99 | // this will early return error if it doesn't exist 100 | let proposal: GameProposal = hdk::utils::get_as_type(proposal_addr.clone())?; 101 | 102 | // create the new game 103 | let game = Game { 104 | player_1: AGENT_ADDRESS.to_string().into(), 105 | player_2: proposal.agent, 106 | created_at, 107 | }; 108 | let game_entry = Entry::App( 109 | "game".into(), 110 | game.into() 111 | ); 112 | let game_addr = hdk::commit_entry(&game_entry)?; 113 | 114 | // link to the proposal 115 | hdk::link_entries( 116 | &proposal_addr, 117 | &game_addr, 118 | "from_proposal", 119 | "" 120 | )?; 121 | Ok(game_addr) 122 | } 123 | 124 | pub fn handle_check_responses(proposal_addr: Address) -> ZomeApiResult>> { 125 | Ok( 126 | hdk::utils::get_links_and_load_type(&proposal_addr, LinkMatch::Exactly("from_proposal".into()), LinkMatch::Any)? 127 | .into_iter().map(|game: Game| { 128 | let address = Entry::App("game".into(), game.clone().into()).address(); 129 | GetResponse{entry: game, address} 130 | }).collect() 131 | ) 132 | } 133 | 134 | pub fn handle_remove_proposal(proposal_addr: Address) -> ZomeApiResult
{ 135 | hdk::remove_entry(&proposal_addr) 136 | } 137 | 138 | pub fn game_proposal_def() -> ValidatingEntryType { 139 | entry!( 140 | // we will need to use this name when creating an entry later 141 | name: "game_proposal", 142 | description: "Represents an agent advertizing they wish to play a game at this time", 143 | // Public sharing means this entry goes to the local chain *and* DHT 144 | sharing: Sharing::Public, 145 | validation_package: || { 146 | // This defines the data required for the validation callback. 147 | // In this case it is just the entry data itself 148 | hdk::ValidationPackageDefinition::Entry 149 | }, 150 | validation: | validation_data: hdk::EntryValidationData| { 151 | match validation_data { 152 | // only match if the entry is being created (not modified or deleted) 153 | EntryValidationData::Create{ entry, validation_data } => { 154 | let game_proposal = GameProposal::from(entry); 155 | if validation_data.sources().contains(&game_proposal.agent) { 156 | Ok(()) 157 | } else { 158 | Err("Cannot author a proposal from another agent".into()) 159 | } 160 | 161 | }, 162 | EntryValidationData::Delete{..} => { // should update to only the author can delete 163 | Ok(()) 164 | }, 165 | _ => { 166 | Err("Cannot modify, only create and delete".into()) 167 | } 168 | } 169 | }, 170 | links: [ 171 | to!( 172 | "game", 173 | link_type: "from_proposal", 174 | validation_package: || { hdk::ValidationPackageDefinition::Entry }, 175 | validation: | _validation_data: hdk::LinkValidationData| { 176 | Ok(()) 177 | } 178 | ) 179 | ] 180 | ) 181 | } 182 | 183 | pub fn anchor_def() -> ValidatingEntryType { 184 | entry!( 185 | name: "anchor", 186 | description: "Central known location to link from", 187 | sharing: Sharing::Public, 188 | validation_package: || { hdk::ValidationPackageDefinition::Entry }, 189 | validation: | _validation_data: hdk::EntryValidationData| { 190 | Ok(()) 191 | }, 192 | links: [ 193 | to!( 194 | "game_proposal", // this must match exactly the target entry type 195 | link_type: "has_proposal", // must use this when creating the link 196 | validation_package: || { 197 | hdk::ValidationPackageDefinition::Entry 198 | }, 199 | validation: | _validation_data: hdk::LinkValidationData| { 200 | Ok(()) 201 | } 202 | ) 203 | ] 204 | ) 205 | } -------------------------------------------------------------------------------- /zomes/main/code/src/tictactoe/mod.rs: -------------------------------------------------------------------------------- 1 | /** 2 | * All of this code is specific to the game Checkers 3 | * By changing the moves, state, reducer and validation rules you can implement you own game. 4 | */ 5 | 6 | pub mod state; 7 | pub mod validation; 8 | pub mod moves; 9 | 10 | pub use self::{ 11 | state::{ 12 | GameState, 13 | }, 14 | moves::{ 15 | MoveType, 16 | }, 17 | }; 18 | -------------------------------------------------------------------------------- /zomes/main/code/src/tictactoe/moves.rs: -------------------------------------------------------------------------------- 1 | use hdk::holochain_json_api::{ 2 | error::JsonError, json::JsonString, 3 | }; 4 | 5 | 6 | /** 7 | * 8 | * The MoveType enum defines all the types of moves that are valid in your game and the 9 | * data they carry. In Checkers you can move a piece (MovePiece) from a location to another location. 10 | * 11 | */ 12 | 13 | #[derive(Clone, Debug, Serialize, Deserialize, DefaultJson, PartialEq)] 14 | pub enum MoveType { 15 | Place { 16 | pos: Piece, 17 | }, 18 | } 19 | 20 | impl MoveType { 21 | pub fn describe() -> Vec { 22 | vec![ 23 | MoveType::Place{pos: Piece{x: 0, y: 0}} 24 | // add the other variants here to add descriptors 25 | ] 26 | } 27 | } 28 | 29 | #[derive(Clone, Debug, Serialize, Deserialize, DefaultJson, PartialEq)] 30 | pub struct Piece { 31 | pub x: usize, 32 | pub y: usize, 33 | } 34 | -------------------------------------------------------------------------------- /zomes/main/code/src/tictactoe/state.rs: -------------------------------------------------------------------------------- 1 | use hdk::holochain_json_api::{ 2 | error::JsonError, json::JsonString, 3 | }; 4 | use hdk::AGENT_ADDRESS; 5 | 6 | use crate::game_move::Move; 7 | use crate::game::Game; 8 | use super::{ 9 | moves::Piece, 10 | MoveType, 11 | validation::{Player, get_current_player}, 12 | }; 13 | 14 | pub const BOARD_SIZE: usize = 3; 15 | pub const PLAYER_1_MARK: char = 'O'; 16 | pub const PLAYER_2_MARK: char = 'X'; //player 2 / Xs go first 17 | pub const EMPTY_SPACE: char = ' '; 18 | 19 | /** 20 | * 21 | * As a game author you get to decide what the State object of your game looks like. 22 | * Most of the time you want it to include all of the previous moves as well. 23 | * 24 | * To customize the game state implement your own GameState struct. This must have a function called `initial()` 25 | * which returns the initial state. 26 | * 27 | */ 28 | 29 | 30 | #[derive(Clone, Debug, Serialize, Deserialize, DefaultJson)] 31 | pub struct GameState { 32 | pub moves: Vec, 33 | pub player_1: PlayerState, 34 | pub player_2: PlayerState, 35 | } 36 | 37 | #[derive(Clone, Debug, Serialize, Deserialize, DefaultJson)] 38 | pub struct PlayerState { 39 | pub pieces: Vec, 40 | pub resigned: bool, 41 | pub winner: bool, 42 | } 43 | 44 | impl PlayerState { 45 | pub fn initial() -> Self { 46 | PlayerState { 47 | pieces: Vec::new(), 48 | resigned: false, 49 | winner: false, 50 | } 51 | } 52 | } 53 | 54 | impl GameState { 55 | pub fn initial() -> Self { 56 | GameState { 57 | moves: Vec::new(), 58 | player_1: PlayerState::initial(), 59 | player_2: PlayerState::initial(), 60 | } 61 | } 62 | 63 | pub fn render(&self) -> String { 64 | let mut disp = "\n".to_string(); 65 | 66 | if let Some(last_move) = self.moves.last() { 67 | if last_move.author.to_string() == AGENT_ADDRESS.to_string() { 68 | disp.push_str("It is your opponents turn \n"); 69 | } else { 70 | disp.push_str("It is your turn \n"); 71 | } 72 | } else { 73 | disp.push_str("Non-creator must make the first move \n"); 74 | } 75 | disp.push('\n'); 76 | disp.push_str(" x 0 1 2\ny\n"); 77 | let board = board_sparse_to_dense(self); 78 | for y in 0..BOARD_SIZE { 79 | disp.push_str(&format!("{} |", y)); 80 | for x in 0..BOARD_SIZE { 81 | let c = match board[x][y] { 82 | 1 => PLAYER_1_MARK, 83 | 2 => PLAYER_2_MARK, 84 | _ => EMPTY_SPACE, 85 | }; 86 | disp.push_str(&format!("{}|", c)); 87 | } 88 | disp.push('\n'); 89 | } 90 | 91 | if self.player_1.resigned { 92 | disp.push_str(&format!("Game over: Player 1 has resigned!\n")); 93 | } else if self.player_2.resigned { 94 | disp.push_str(&format!("Game over: Player 2 has resigned!\n")); 95 | } else if self.player_1.winner { 96 | disp.push_str(&format!("Game over: Player 1 is the winner!\n")); 97 | } else if self.player_2.winner { 98 | disp.push_str(&format!("Game over: Player 2 is the winner!\n")); 99 | } 100 | disp 101 | } 102 | 103 | pub fn evolve(&self, game: Game, next_move: &Move) -> Self { 104 | let current_player = get_current_player(&game, &next_move.author).unwrap(); 105 | 106 | match &next_move.move_type { 107 | MoveType::Place{pos} => { 108 | let mut board = board_sparse_to_dense(&self); 109 | let mut moves = self.moves.clone(); 110 | moves.push(next_move.to_owned()); 111 | 112 | // make the move by adding a new piece at the position 113 | board[pos.x][pos.y] = match current_player { Player::Player1 => 1, Player::Player2 => 2}; 114 | 115 | // check if this resulted in a player victory 116 | let mut diag_down = 0; 117 | let mut diag_up = 0; 118 | let mut across = [0; 3]; 119 | let mut down = [0; 3]; 120 | for x in 0..BOARD_SIZE { 121 | for y in 0..BOARD_SIZE { 122 | let delta = match board[x][y] {1 => 1, 2 => -1, _ => 0}; 123 | down[x] += delta; 124 | across[y] += delta; 125 | // diag down e.g. \ 126 | if x == y { 127 | diag_down += delta; 128 | } //diag up e.g. / 129 | else if x == (BOARD_SIZE - 1 - y) { 130 | diag_up += delta; 131 | } 132 | } 133 | } 134 | let player_1_victory = across.iter().any(|e| *e == (BOARD_SIZE as i32)) 135 | || down.iter().any(|e| *e == (BOARD_SIZE as i32)); 136 | || diag_down == (BOARD_SIZE as i32); 137 | || diag_up == (BOARD_SIZE as i32); 138 | 139 | let player_2_victory = across.iter().any(|e| *e == (-1*BOARD_SIZE as i32)) 140 | || down.iter().any(|e| *e == (-1*BOARD_SIZE as i32)); 141 | || diag_down == (-1*BOARD_SIZE as i32); 142 | || diag_up == (-1*BOARD_SIZE as i32); 143 | 144 | let (player_1_pieces, player_2_pieces) = board_dense_to_sparse(board); 145 | 146 | GameState{ 147 | player_1: PlayerState { 148 | pieces: player_1_pieces, 149 | resigned: false, 150 | winner: player_1_victory, 151 | }, 152 | player_2: PlayerState { 153 | pieces: player_2_pieces, 154 | resigned: false, 155 | winner: player_2_victory, 156 | }, 157 | moves, 158 | ..self.clone() 159 | } 160 | } 161 | } 162 | } 163 | 164 | } 165 | 166 | /*======================================== 167 | = Helper functions = 168 | ========================================*/ 169 | 170 | pub fn board_sparse_to_dense(state: &GameState)-> [[u8; 8]; 8] { 171 | let mut board = [[0u8; 8]; 8]; 172 | state.player_1.pieces.iter().for_each(|piece| { 173 | board[piece.x][piece.y] = 1; 174 | }); 175 | state.player_2.pieces.iter().for_each(|piece| { 176 | board[piece.x][piece.y] = 2; 177 | }); 178 | board 179 | } 180 | 181 | pub fn board_dense_to_sparse(board: [[u8; 8]; 8]) -> (Vec, Vec) { 182 | let mut player_1_pieces = Vec::new(); 183 | let mut player_2_pieces = Vec::new(); 184 | board.iter().enumerate().for_each(|(x, row)| { 185 | row.iter().enumerate().for_each(|(y, square)| { 186 | if *square == 1 { 187 | player_1_pieces.push(Piece{x , y}); 188 | } else if *square == 2 { 189 | player_2_pieces.push(Piece{x , y}); 190 | } 191 | }) 192 | }); 193 | (player_1_pieces, player_2_pieces) 194 | } 195 | 196 | /*===== End of Helper functions ======*/ 197 | -------------------------------------------------------------------------------- /zomes/main/code/src/tictactoe/validation.rs: -------------------------------------------------------------------------------- 1 | use hdk::holochain_persistence_api::{ 2 | cas::content::Address, 3 | }; 4 | 5 | use crate::game::Game; 6 | use crate::game_move::Move; 7 | use super::{ 8 | GameState, 9 | moves::Piece, 10 | MoveType, 11 | state::{board_sparse_to_dense, BOARD_SIZE}, 12 | }; 13 | 14 | 15 | /** 16 | * 17 | * To implement your own custom rule validation all you need to do is re-implement the function `is_valid` on `Move` 18 | * 19 | * This function takes the current game and the game state (which includes all the existing moves) 20 | * and determines if a new candidate move is valid. Typically this will involve first matching on the move type 21 | * and then determining if the move is valid. 22 | * 23 | * It function must return Ok(()) if a move is valid and Err("Some error string") for an invalid move. 24 | * It is useful to provide descriptive error strings as these can be visible to the end user. 25 | * 26 | */ 27 | 28 | 29 | impl Move { 30 | pub fn is_valid(&self, game: Game, game_state: GameState) -> Result<(), String> { 31 | hdk::debug(format!("{:?}", game_state)).unwrap(); 32 | // let current_player = get_current_player(&game, &self.author)?; 33 | match &self.move_type { 34 | MoveType::Place{pos} => { 35 | is_players_turn(self.author.clone(), game, &game_state)?; 36 | pos.is_in_bounds()?; 37 | pos.is_empty(&game_state)?; 38 | hdk::debug("Validation Success!").unwrap(); 39 | Ok(()) } 40 | } 41 | } 42 | } 43 | 44 | 45 | /*======================================== 46 | = Helper functions = 47 | ========================================*/ 48 | 49 | pub enum Player{ 50 | Player1, 51 | Player2, 52 | } 53 | 54 | pub fn get_current_player(game: &Game, player_addr: &Address) -> Result { 55 | match (player_addr == &game.player_1, player_addr == &game.player_2) { 56 | (true, true) => return Err("Player cannot play themselves".into()), 57 | (true, false) => Ok(Player::Player1), 58 | (false, true) => Ok(Player::Player2), 59 | (false, false) => return Err("Player is not part of this game!".into()), 60 | } 61 | } 62 | 63 | 64 | fn is_players_turn(player: Address, game: Game, game_state: &GameState) -> Result<(), String> { 65 | let moves = &game_state.moves; 66 | match moves.last() { 67 | Some(last_move) => { 68 | if last_move.author == player { 69 | Err("It is not this players turn.".into()) 70 | } else { 71 | Ok(()) 72 | } 73 | }, 74 | None => { 75 | // by convention player 2 makes the first move thus accepting the invitation to play 76 | if game.player_2 == player { 77 | Ok(()) 78 | } else { 79 | Err("Player 2 must make the first move.".into()) 80 | } 81 | }, 82 | } 83 | } 84 | 85 | impl Piece { 86 | fn is_in_bounds(&self) -> Result<(), String> { 87 | if self.x < BOARD_SIZE 88 | && self.y < BOARD_SIZE // no need to check > 0 as usize is always positive 89 | { 90 | Ok(()) 91 | } else { 92 | Err("Position is not in bounds".to_string()) 93 | } 94 | } 95 | 96 | fn is_empty(&self, game_state: &GameState) -> Result<(), String> { 97 | match board_sparse_to_dense(game_state)[self.x][self.y] == 0 { 98 | true => Ok(()), 99 | false => Err("A piece already exists at that position.".to_string()) 100 | } 101 | } 102 | } 103 | 104 | /*===== End of Helper functions ======*/ 105 | 106 | -------------------------------------------------------------------------------- /zomes/main/code/src/your-game/mod.rs: -------------------------------------------------------------------------------- 1 | /** 2 | * All of this code is specific to the game Checkers 3 | * By changing the moves, state, reducer and validation rules you can implement you own game. 4 | */ 5 | 6 | pub mod state; 7 | pub mod validation; 8 | pub mod moves; 9 | 10 | pub use self::{ 11 | state::{ 12 | GameState, 13 | }, 14 | moves::{ 15 | MoveType, 16 | }, 17 | }; 18 | -------------------------------------------------------------------------------- /zomes/main/code/src/your-game/moves.rs: -------------------------------------------------------------------------------- 1 | use hdk::holochain_json_api::{ 2 | error::JsonError, json::JsonString, 3 | }; 4 | 5 | /** 6 | * 7 | * The MoveType enum defines all the types of moves that are valid in your game and the 8 | * data they carry. In Checkers you can move a piece (MovePiece) from a location to another location. 9 | * 10 | */ 11 | 12 | #[derive(Clone, Debug, Serialize, Deserialize, DefaultJson, PartialEq)] 13 | pub enum MoveType { 14 | /** 15 | * DEVCAMP TODO #1: 16 | * Input all possible move types of your game 17 | * 18 | * Hint: Enum variants can be structs, and thus have parameters 19 | * References: https://doc.rust-lang.org/rust-by-example/custom_types/enum.html 20 | * Examples: 21 | * RollDice { number: u32 } 22 | * GuessMovie(String) 23 | */ 24 | } 25 | 26 | impl MoveType { 27 | pub fn describe() -> Vec { 28 | /** 29 | * DEVCAMP TODO #2: 30 | * Return a list containing each one of the MoveType enum variantss 31 | * This is only a helper list function for the CLI to be able to output the list of possible moves 32 | * 33 | * Hint: use the vec![] macro 34 | * References: https://doc.rust-lang.org/1.3.0/std/macro.vec!.html 35 | */ 36 | } 37 | } -------------------------------------------------------------------------------- /zomes/main/code/src/your-game/state.rs: -------------------------------------------------------------------------------- 1 | use hdk::holochain_json_api::{ 2 | error::JsonError, json::JsonString, 3 | }; 4 | use hdk::AGENT_ADDRESS; 5 | 6 | use crate::game_move::Move; 7 | use crate::game::Game; 8 | use super::{ 9 | MoveType, 10 | // Usually you would import structs and types defined in the 'moves' file 11 | }; 12 | 13 | /** 14 | * 15 | * As a game author you get to decide what the State object of your game looks like. 16 | * Most of the time you want it to include all of the previous moves as well. 17 | * 18 | * To customize the game state implement your own GameState struct. This must have a function called `initial()` 19 | * which returns the initial state. 20 | * 21 | */ 22 | 23 | 24 | #[derive(Clone, Debug, Serialize, Deserialize, DefaultJson)] 25 | pub struct GameState { 26 | /** 27 | * DEVCAMP TODO #3: 28 | * Implement struct that determines the state of your game 29 | * 30 | * Hint: you can define other helper structs and reuse the Move struct to store the state 31 | * References: https://doc.rust-lang.org/rust-by-example/custom_types/structs.html 32 | */ 33 | } 34 | 35 | /** 36 | * Example of a possible GameState 37 | * 38 | * #[derive(Clone, Debug, Serialize, Deserialize, DefaultJson)] 39 | * pub struct GameState { 40 | * pub moves: Vec, 41 | * pub player_1: PlayerState, 42 | * pub player_2: PlayerState, 43 | * } 44 | * 45 | * #[derive(Clone, Debug, Serialize, Deserialize, DefaultJson)] 46 | * pub struct PlayerState { 47 | * pub pieces: Vec, 48 | * pub resigned: bool, 49 | * pub winner: bool, 50 | * } 51 | */ 52 | 53 | 54 | 55 | impl GameState { 56 | pub fn initial() -> Self { 57 | /** 58 | * DEVCAMP TODO #4: 59 | * Return the initial game state 60 | * 61 | * Hint: this function will always return a static value, that 62 | * Example: 63 | * GameState { 64 | * moves: Vec::new(), 65 | * player_1: PlayerState { 66 | * pieces: Vec::new(), 67 | * resigned: false, 68 | * winner: false, 69 | * }, 70 | * player_2: PlayerState { 71 | * pieces: Vec::new(), 72 | * resigned: false, 73 | * winner: false, 74 | * }, 75 | * } 76 | */ 77 | } 78 | 79 | pub fn render(&self) -> String { 80 | /** 81 | * DEVCAMP TODO #5: 82 | * Return a string rendering the state of the game, so that the CLI can render it in the terminal 83 | * 84 | * Hint: useful snippets: 85 | * let mut disp = "\n".to_string(); 86 | * disp.push_str(" x 0 1 2 3 4 5 6 7\n"); 87 | * References: https://doc.rust-lang.org/rust-by-example/std/str.html 88 | */ 89 | } 90 | 91 | pub fn evolve(&self, game: Game, next_move: &Move) -> Self { 92 | /** 93 | * DEVCAMP TODO #6: 94 | * Return the new game state resulting from applying the next_move to the current state of the game 95 | * You can assume that next_move is valid 96 | * 97 | * Hints: 98 | * - This is similar to a Redux reducer, a function that given a state and a new action, 99 | * returns the next state 100 | * - It can be really helpful to use the helper functions below to transform your state before and after 101 | * you apply the next_move changes to it. For example, the function could be structured like this: 102 | * * Transform GameState to a matrix of all pieces 103 | * * Apply move to the matrix 104 | * * Transform matrix to GameState 105 | * References: https://www.joshmcguigan.com/blog/array-initialization-rust/ 106 | */ 107 | } 108 | } 109 | 110 | /*======================================== 111 | = Helper functions = 112 | 113 | // This function transforms the game state from a vector of pieces to a matrix where each 114 | // position represents the location of the piece 115 | 116 | // Visual representation: 117 | // From: { player1: [{x: 0, y: 4}, {x: 6, y: 3}], player2: [{x: 6, y: 4}, {x: 5, y: 6}] 118 | // To: 119 | // [ 120 | // [0, 0, 0, 0, 1, 0, 0, 0], 121 | // [0, 0, 0, 0, 0, 0, 0, 0], 122 | // [0, 0, 0, 0, 0, 0, 0, 0], 123 | // [0, 0, 0, 0, 0, 0, 0, 0], 124 | // [0, 0, 0, 0, 0, 0, 0, 0], 125 | // [0, 0, 0, 0, 0, 0, 2, 0], 126 | // [0, 0, 0, 1, 2, 0, 0, 0], 127 | // [0, 0, 0, 0, 0, 0, 0, 0], 128 | //] 129 | 130 | pub fn board_sparse_to_dense(state: &GameState)-> [[u8; 8]; 8] { 131 | let mut board = [[0u8; 8]; 8]; 132 | state.player_1.pieces.iter().for_each(|piece| { 133 | board[piece.x][piece.y] = 1; 134 | }); 135 | state.player_2.pieces.iter().for_each(|piece| { 136 | board[piece.x][piece.y] = 2; 137 | }); 138 | board 139 | } 140 | 141 | // This function is the inverse of the function above, 142 | // takes a matrix in the same format of above and transforms it to a tuple of two vectors of pieces, 143 | // that can directly be saved in the entry 144 | 145 | pub fn board_dense_to_sparse(board: [[u8; 8]; 8]) -> (Vec, Vec) { 146 | let mut player_1_pieces = Vec::new(); 147 | let mut player_2_pieces = Vec::new(); 148 | board.iter().enumerate().for_each(|(x, row)| { 149 | row.iter().enumerate().for_each(|(y, square)| { 150 | if *square == 1 { 151 | player_1_pieces.push(Piece{x , y}); 152 | } else if *square == 2 { 153 | player_2_pieces.push(Piece{x , y}); 154 | } 155 | }) 156 | }); 157 | (player_1_pieces, player_2_pieces) 158 | } 159 | ========================================*/ 160 | -------------------------------------------------------------------------------- /zomes/main/code/src/your-game/validation.rs: -------------------------------------------------------------------------------- 1 | use hdk::holochain_persistence_api::{ 2 | cas::content::Address, 3 | }; 4 | 5 | use crate::game::Game; 6 | use crate::game_move::Move; 7 | use super::{ 8 | GameState, 9 | MoveType, 10 | }; 11 | 12 | 13 | /** 14 | * 15 | * To implement your own custom rule validation all you need to do is re-implement the function `is_valid` on `Move` 16 | * 17 | * This function takes the current game and the game state (which includes all the existing moves) 18 | * and determines if a new candidate move is valid. Typically this will involve first matching on the move type 19 | * and then determining if the move is valid. 20 | * 21 | * It function must return Ok(()) if a move is valid and Err("Some error string") for an invalid move. 22 | * It is useful to provide descriptive error strings as these can be visible to the end user. 23 | * 24 | */ 25 | 26 | 27 | impl Move { 28 | pub fn is_valid(&self, game: Game, game_state: GameState) -> Result<(), String> { 29 | /** 30 | * DEVCAMP TODO #7: 31 | * Return Ok() if the self move is valid, or Err("Error message".into()) otherwise 32 | * 33 | * Hint: 34 | * - You can use the standard '?' rust notation to call helper functions and 35 | * return their error messages upwards (akin to 'throw Exception' in other languages) 36 | * - Usually you should make global move checks first ('is it the turn of the author of the move?'), 37 | * and then match the 'self.move_type' enum and check the validity of each variant 38 | * References: https://doc.rust-lang.org/edition-guide/rust-2018/error-handling-and-panics/the-question-mark-operator-for-easier-error-handling.html 39 | */ 40 | } 41 | } 42 | 43 | -------------------------------------------------------------------------------- /zomes/main/zome.json: -------------------------------------------------------------------------------- 1 | { 2 | "description": "The main App" 3 | } --------------------------------------------------------------------------------