start playing with websockets

This commit is contained in:
dylan 2024-04-03 20:29:27 -07:00
parent df5423b294
commit f9e21db529
11 changed files with 554 additions and 152 deletions

View File

@ -9,9 +9,10 @@
## Next
- [x] Fix css bug on chromium
- [ ] Add user page to show all projects
- [x] Add user page to show all projects
## Later
- [ ] GPIO shared within room
- [ ] Update pico console handle
- [ ] Add readme prop to picobook.json (figure out best name) and display it in React
- [ ] Multiplayer support

420
package-lock.json generated
View File

@ -13,12 +13,13 @@
"@fastify/cookie": "^9.0.4",
"@fastify/secure-session": "^7.1.0",
"@fastify/static": "^6.10.2",
"@fastify/websocket": "^10.0.1",
"@firebox/components": "^0.1.5",
"@firebox/tsutil": "^0.1.2",
"@sinclair/typebox": "^0.31.5",
"dotenv": "^16.4.5",
"execa": "^8.0.1",
"fastify": "^4.22.0",
"fastify": "^4.26.2",
"isomorphic-git": "^1.25.6",
"react-pico-8": "^4.1.0",
"react-router-dom": "^6.18.0",
@ -32,6 +33,7 @@
"@types/react": "^18.2.21",
"@types/react-dom": "^18.2.7",
"@types/uuid": "^9.0.7",
"@types/ws": "^8.5.10",
"esbuild": "^0.19.2",
"nodemon": "^3.0.1",
"react": "^18.2.0",
@ -955,9 +957,9 @@
"integrity": "sha512-J8TOSBq3SoZbDhM9+R/u77hP93gz/rajSA+K2kGyijPpORPWUXHUpTaleoj+92As0S9uPRP7Oi8IqMf0u+ro6A=="
},
"node_modules/@fastify/error": {
"version": "3.3.0",
"resolved": "https://registry.npmjs.org/@fastify/error/-/error-3.3.0.tgz",
"integrity": "sha512-dj7vjIn1Ar8sVXj2yAXiMNCJDmS9MQ9XMlIecX2dIzzhjSHCyKo4DdXjXMs7wKW2kj6yvVRSpuQjOZ3YLrh56w=="
"version": "3.4.1",
"resolved": "https://registry.npmjs.org/@fastify/error/-/error-3.4.1.tgz",
"integrity": "sha512-wWSvph+29GR783IhmvdwWnN4bUxTD01Vm5Xad4i7i1VuAOItLvbPAb69sb0IQ2N57yprvhNIwAP5B6xfKTmjmQ=="
},
"node_modules/@fastify/fast-json-stringify-compiler": {
"version": "4.3.0",
@ -1081,6 +1083,16 @@
"node": "^12.22.0 || ^14.17.0 || >=16.0.0"
}
},
"node_modules/@fastify/websocket": {
"version": "10.0.1",
"resolved": "https://registry.npmjs.org/@fastify/websocket/-/websocket-10.0.1.tgz",
"integrity": "sha512-8/pQIxTPRD8U94aILTeJ+2O3el/r19+Ej5z1O1mXlqplsUH7KzCjAI0sgd5DM/NoPjAi5qLFNIjgM5+9/rGSNw==",
"dependencies": {
"duplexify": "^4.1.2",
"fastify-plugin": "^4.0.0",
"ws": "^8.0.0"
}
},
"node_modules/@firebox/components": {
"version": "0.1.5",
"resolved": "https://nodepack.playbox.link/@firebox/components/-/components-0.1.5.tgz",
@ -1233,6 +1245,15 @@
"integrity": "sha512-jg+97EGIcY9AGHJJRaaPVgetKDsrTgbRjQ5Msgjh/DQKEFl0DtyRr/VCOyD1T2R1MNeWPK/u7JoGhlDZnKBAfA==",
"dev": true
},
"node_modules/@types/ws": {
"version": "8.5.10",
"resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.5.10.tgz",
"integrity": "sha512-vmQSUcfalpIq0R9q7uTo2lXs6eGIpt9wtnLdMv9LVpIjCA/+ufZRozlVoVelIYixx1ugCBKDhn89vnsEGOCx9A==",
"dev": true,
"dependencies": {
"@types/node": "*"
}
},
"node_modules/abbrev": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz",
@ -1394,13 +1415,14 @@
}
},
"node_modules/avvio": {
"version": "8.2.1",
"resolved": "https://registry.npmjs.org/avvio/-/avvio-8.2.1.tgz",
"integrity": "sha512-TAlMYvOuwGyLK3PfBb5WKBXZmXz2fVCgv23d6zZFdle/q3gPjmxBaeuC0pY0Dzs5PWMSgfqqEZkrye19GlDTgw==",
"version": "8.3.0",
"resolved": "https://registry.npmjs.org/avvio/-/avvio-8.3.0.tgz",
"integrity": "sha512-VBVH0jubFr9LdFASy/vNtm5giTrnbVquWBhT0fyizuNK2rQ7e7ONU2plZQWUNqtE1EmxFEb+kbSkFRkstiaS9Q==",
"dependencies": {
"@fastify/error": "^3.3.0",
"archy": "^1.0.0",
"debug": "^4.0.0",
"fastq": "^1.6.1"
"fastq": "^1.17.1"
}
},
"node_modules/babel-plugin-macros": {
@ -1859,12 +1881,31 @@
"url": "https://dotenvx.com"
}
},
"node_modules/duplexify": {
"version": "4.1.3",
"resolved": "https://registry.npmjs.org/duplexify/-/duplexify-4.1.3.tgz",
"integrity": "sha512-M3BmBhwJRZsSx38lZyhE53Csddgzl5R7xGJNk7CVddZD6CcmwMCH8J+7AprIrQKH7TonKxaCjcv27Qmf+sQ+oA==",
"dependencies": {
"end-of-stream": "^1.4.1",
"inherits": "^2.0.3",
"readable-stream": "^3.1.1",
"stream-shift": "^1.0.2"
}
},
"node_modules/emoji-regex": {
"version": "8.0.0",
"resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
"integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==",
"dev": true
},
"node_modules/end-of-stream": {
"version": "1.4.4",
"resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz",
"integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==",
"dependencies": {
"once": "^1.4.0"
}
},
"node_modules/error-ex": {
"version": "1.3.2",
"resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz",
@ -2033,9 +2074,9 @@
}
},
"node_modules/fast-content-type-parse": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/fast-content-type-parse/-/fast-content-type-parse-1.0.0.tgz",
"integrity": "sha512-Xbc4XcysUXcsP5aHUU7Nq3OwvHq97C+WnbkeIefpeYLX+ryzFJlU6OStFJhs6Ol0LkUGpcK+wL0JwfM+FCU5IA=="
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/fast-content-type-parse/-/fast-content-type-parse-1.1.0.tgz",
"integrity": "sha512-fBHHqSTFLVnR61C+gltJuE5GkVQMV0S2nqUO8TJ+5Z3qAKG8vAx4FKai1s5jq/inV1+sREynIWSuQ6HgoSXpDQ=="
},
"node_modules/fast-decode-uri-component": {
"version": "1.0.1",
@ -2069,9 +2110,9 @@
}
},
"node_modules/fast-redact": {
"version": "3.3.0",
"resolved": "https://registry.npmjs.org/fast-redact/-/fast-redact-3.3.0.tgz",
"integrity": "sha512-6T5V1QK1u4oF+ATxs1lWUmlEk6P2T9HqJG3e2DnHOdVgZy2rFJBoEnrIedcTXlkAHU/zKC+7KETJ+KGGKwxgMQ==",
"version": "3.5.0",
"resolved": "https://registry.npmjs.org/fast-redact/-/fast-redact-3.5.0.tgz",
"integrity": "sha512-dwsoQlS7h9hMeYUq1W++23NDcBLV4KqONnITDV9DjfS3q1SgDGVrBdvvTLUotWtPSD7asWDV9/CmsZPy8Hf70A==",
"engines": {
"node": ">=6"
}
@ -2082,26 +2123,36 @@
"integrity": "sha512-cIusKBIt/R/oI6z/1nyfe2FvGKVTohVRfvkOhvx0nCEW+xf5NoCXjAHcWp93uOUBchzYcsvPlrapAdX1uW+YGg=="
},
"node_modules/fastify": {
"version": "4.22.0",
"resolved": "https://registry.npmjs.org/fastify/-/fastify-4.22.0.tgz",
"integrity": "sha512-HLoBmetdQ6zaJohKW6jzUww8NnwHzkbIbUEyAzM+Nnf7cZVSXRuUV+6b2/xLmu6GGkruIFJ/bIQoKWYRx4wnAQ==",
"version": "4.26.2",
"resolved": "https://registry.npmjs.org/fastify/-/fastify-4.26.2.tgz",
"integrity": "sha512-90pjTuPGrfVKtdpLeLzND5nyC4woXZN5VadiNQCicj/iJU4viNHKhsAnb7jmv1vu2IzkLXyBiCzdWuzeXgQ5Ug==",
"funding": [
{
"type": "github",
"url": "https://github.com/sponsors/fastify"
},
{
"type": "opencollective",
"url": "https://opencollective.com/fastify"
}
],
"dependencies": {
"@fastify/ajv-compiler": "^3.5.0",
"@fastify/error": "^3.2.0",
"@fastify/error": "^3.4.0",
"@fastify/fast-json-stringify-compiler": "^4.3.0",
"abstract-logging": "^2.0.1",
"avvio": "^8.2.1",
"fast-content-type-parse": "^1.0.0",
"fast-json-stringify": "^5.7.0",
"find-my-way": "^7.6.0",
"light-my-request": "^5.9.1",
"pino": "^8.12.0",
"process-warning": "^2.2.0",
"avvio": "^8.3.0",
"fast-content-type-parse": "^1.1.0",
"fast-json-stringify": "^5.8.0",
"find-my-way": "^8.0.0",
"light-my-request": "^5.11.0",
"pino": "^8.17.0",
"process-warning": "^3.0.0",
"proxy-addr": "^2.0.7",
"rfdc": "^1.3.0",
"secure-json-parse": "^2.5.0",
"semver": "^7.5.0",
"tiny-lru": "^11.0.1"
"secure-json-parse": "^2.7.0",
"semver": "^7.5.4",
"toad-cache": "^3.3.0"
}
},
"node_modules/fastify-plugin": {
@ -2110,9 +2161,9 @@
"integrity": "sha512-stRHYGeuqpEZTL1Ef0Ovr2ltazUT9g844X5z/zEBFLG8RYlpDiOCIG+ATvYEp+/zmc7sN29mcIMp8gvYplYPIQ=="
},
"node_modules/fastq": {
"version": "1.15.0",
"resolved": "https://registry.npmjs.org/fastq/-/fastq-1.15.0.tgz",
"integrity": "sha512-wBrocU2LCXXa+lWBt8RoIRD89Fi8OdABODa/kEnyeyjS5aZO5/GNvI5sEINADqP/h8M29UHTHUb53sUu5Ihqdw==",
"version": "1.17.1",
"resolved": "https://registry.npmjs.org/fastq/-/fastq-1.17.1.tgz",
"integrity": "sha512-sRVD3lWVIXWg6By68ZN7vho9a1pQcN/WBFaAAsDDFzlJjvoGx0P8z7V1t72grFJfJhu3YPZBuu25f7Kaw2jN1w==",
"dependencies": {
"reusify": "^1.0.4"
}
@ -2158,9 +2209,9 @@
}
},
"node_modules/find-my-way": {
"version": "7.6.2",
"resolved": "https://registry.npmjs.org/find-my-way/-/find-my-way-7.6.2.tgz",
"integrity": "sha512-0OjHn1b1nCX3eVbm9ByeEHiscPYiHLfhei1wOUU9qffQkk98wE0Lo8VrVYfSGMgnSnDh86DxedduAnBf4nwUEw==",
"version": "8.1.0",
"resolved": "https://registry.npmjs.org/find-my-way/-/find-my-way-8.1.0.tgz",
"integrity": "sha512-41QwjCGcVTODUmLLqTMeoHeiozbMXYMAE1CKFiDyi9zVZ2Vjh0yz3MF0WQZoIb+cmzP/XlbFjlF2NtJmvZHznA==",
"dependencies": {
"fast-deep-equal": "^3.1.3",
"fast-querystring": "^1.0.0",
@ -2945,15 +2996,23 @@
"integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug=="
},
"node_modules/light-my-request": {
"version": "5.10.0",
"resolved": "https://registry.npmjs.org/light-my-request/-/light-my-request-5.10.0.tgz",
"integrity": "sha512-ZU2D9GmAcOUculTTdH9/zryej6n8TzT+fNGdNtm6SDp5MMMpHrJJkvAdE3c6d8d2chE9i+a//dS9CWZtisknqA==",
"version": "5.12.0",
"resolved": "https://registry.npmjs.org/light-my-request/-/light-my-request-5.12.0.tgz",
"integrity": "sha512-P526OX6E7aeCIfw/9UyJNsAISfcFETghysaWHQAlQYayynShT08MOj4c6fBCvTWBrHXSvqBAKDp3amUPSCQI4w==",
"dependencies": {
"cookie": "^0.5.0",
"process-warning": "^2.0.0",
"cookie": "^0.6.0",
"process-warning": "^3.0.0",
"set-cookie-parser": "^2.4.1"
}
},
"node_modules/light-my-request/node_modules/cookie": {
"version": "0.6.0",
"resolved": "https://registry.npmjs.org/cookie/-/cookie-0.6.0.tgz",
"integrity": "sha512-U71cyTamuh1CRNCfpGY6to28lxvNwPG4Guz/EVjgf3Jmzv0vlDp1atT9eS5dDjMYHucpHbWns6Lwf3BKz6svdw==",
"engines": {
"node": ">= 0.6"
}
},
"node_modules/lines-and-columns": {
"version": "1.2.4",
"resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz",
@ -3332,9 +3391,12 @@
}
},
"node_modules/on-exit-leak-free": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/on-exit-leak-free/-/on-exit-leak-free-2.1.0.tgz",
"integrity": "sha512-VuCaZZAjReZ3vUwgOB8LxAosIurDiAW0s13rI1YwmaP++jvcxP77AWoQvenZebpCA2m8WC1/EosPYPMjnRAp/w=="
"version": "2.1.2",
"resolved": "https://registry.npmjs.org/on-exit-leak-free/-/on-exit-leak-free-2.1.2.tgz",
"integrity": "sha512-0eJJY6hXLGf1udHwfNftBqH+g73EU4B504nZeKpz1sYRKafAghwxEJunB2O7rDZkL4PGfsMVnTXZ2EjibbqcsA==",
"engines": {
"node": ">=14.0.0"
}
},
"node_modules/once": {
"version": "1.4.0",
@ -3673,20 +3735,20 @@
}
},
"node_modules/pino": {
"version": "8.15.0",
"resolved": "https://registry.npmjs.org/pino/-/pino-8.15.0.tgz",
"integrity": "sha512-olUADJByk4twxccmAxb1RiGKOSvddHugCV3wkqjyv+3Sooa2KLrmXrKEWOKi0XPCLasRR5jBXxioE1jxUa4KzQ==",
"version": "8.19.0",
"resolved": "https://registry.npmjs.org/pino/-/pino-8.19.0.tgz",
"integrity": "sha512-oswmokxkav9bADfJ2ifrvfHUwad6MLp73Uat0IkQWY3iAw5xTRoznXbXksZs8oaOUMpmhVWD+PZogNzllWpJaA==",
"dependencies": {
"atomic-sleep": "^1.0.0",
"fast-redact": "^3.1.1",
"on-exit-leak-free": "^2.1.0",
"pino-abstract-transport": "v1.0.0",
"pino-abstract-transport": "v1.1.0",
"pino-std-serializers": "^6.0.0",
"process-warning": "^2.0.0",
"process-warning": "^3.0.0",
"quick-format-unescaped": "^4.0.3",
"real-require": "^0.2.0",
"safe-stable-stringify": "^2.3.1",
"sonic-boom": "^3.1.0",
"sonic-boom": "^3.7.0",
"thread-stream": "^2.0.0"
},
"bin": {
@ -3694,9 +3756,9 @@
}
},
"node_modules/pino-abstract-transport": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/pino-abstract-transport/-/pino-abstract-transport-1.0.0.tgz",
"integrity": "sha512-c7vo5OpW4wIS42hUVcT5REsL8ZljsUfBjqV/e2sFxmFEFZiq1XLUp5EYLtuDH6PEHq9W1egWqRbnLUP5FuZmOA==",
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/pino-abstract-transport/-/pino-abstract-transport-1.1.0.tgz",
"integrity": "sha512-lsleG3/2a/JIWUtf9Q5gUNErBqwIu1tUKTT3dUzaf5DySw9ra1wcqKjJjLX1VTY64Wk1eEOYsVGSaGfCK85ekA==",
"dependencies": {
"readable-stream": "^4.0.0",
"split2": "^4.0.0"
@ -3726,9 +3788,9 @@
}
},
"node_modules/pino-abstract-transport/node_modules/readable-stream": {
"version": "4.4.2",
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-4.4.2.tgz",
"integrity": "sha512-Lk/fICSyIhodxy1IDK2HazkeGjSmezAWX2egdtJnYhtzKEsBPJowlI6F6LPb5tqIQILrMbx22S5o3GuJavPusA==",
"version": "4.5.2",
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-4.5.2.tgz",
"integrity": "sha512-yjavECdqeZ3GLXNgRXgeQEdz9fvDDkNKyHnbHRFtOr7/LcfgBcmct7t/ET+HaCTqfh06OzoAxrkN/IfjJBVe+g==",
"dependencies": {
"abort-controller": "^3.0.0",
"buffer": "^6.0.3",
@ -3798,9 +3860,9 @@
}
},
"node_modules/process-warning": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/process-warning/-/process-warning-2.2.0.tgz",
"integrity": "sha512-/1WZ8+VQjR6avWOgHeEPd7SDQmFQ1B5mC1eRXsCm5TarlNmx/wCsa5GEaxGm05BORRtyG/Ex/3xq3TuRvq57qg=="
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/process-warning/-/process-warning-3.0.0.tgz",
"integrity": "sha512-mqn0kFRl0EoqhnL0GQ0veqFHyIN1yig9RHh/InzORTUiZHFRAur+aMtRkELNwGs9aNwKS6tg/An4NYBPGwvtzQ=="
},
"node_modules/proxy-addr": {
"version": "2.0.7",
@ -4252,9 +4314,9 @@
}
},
"node_modules/sonic-boom": {
"version": "3.3.0",
"resolved": "https://registry.npmjs.org/sonic-boom/-/sonic-boom-3.3.0.tgz",
"integrity": "sha512-LYxp34KlZ1a2Jb8ZQgFCK3niIHzibdwtwNUWKg0qQRzsDoJ3Gfgkf8KdBTFU3SkejDEIlWwnSnpVdOZIhFMl/g==",
"version": "3.8.0",
"resolved": "https://registry.npmjs.org/sonic-boom/-/sonic-boom-3.8.0.tgz",
"integrity": "sha512-ybz6OYOUjoQQCQ/i4LU8kaToD8ACtYP+Cj5qd2AO36bwbdewxWJ3ArmJ2cr6AvxlL2o0PqnCcPGUgkILbfkaCA==",
"dependencies": {
"atomic-sleep": "^1.0.0"
}
@ -4287,6 +4349,11 @@
"node": ">= 0.4"
}
},
"node_modules/stream-shift": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/stream-shift/-/stream-shift-1.0.3.tgz",
"integrity": "sha512-76ORR0DO1o1hlKwTbi/DM3EXWGf3ZJYO8cXX5RJwnul2DEg2oyoZyjLNoQM8WsvZiFKCRfC1O0J7iCvie3RZmQ=="
},
"node_modules/string_decoder": {
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz",
@ -4398,21 +4465,13 @@
}
},
"node_modules/thread-stream": {
"version": "2.4.0",
"resolved": "https://registry.npmjs.org/thread-stream/-/thread-stream-2.4.0.tgz",
"integrity": "sha512-xZYtOtmnA63zj04Q+F9bdEay5r47bvpo1CaNqsKi7TpoJHcotUez8Fkfo2RJWpW91lnnaApdpRbVwCWsy+ifcw==",
"version": "2.4.1",
"resolved": "https://registry.npmjs.org/thread-stream/-/thread-stream-2.4.1.tgz",
"integrity": "sha512-d/Ex2iWd1whipbT681JmTINKw0ZwOUBZm7+Gjs64DHuX34mmw8vJL2bFAaNacaW72zYiTJxSHi5abUuOi5nsfg==",
"dependencies": {
"real-require": "^0.2.0"
}
},
"node_modules/tiny-lru": {
"version": "11.0.1",
"resolved": "https://registry.npmjs.org/tiny-lru/-/tiny-lru-11.0.1.tgz",
"integrity": "sha512-iNgFugVuQgBKrqeO/mpiTTgmBsTP0WL6yeuLfLs/Ctf0pI/ixGqIRm8sDCwMcXGe9WWvt2sGXI5mNqZbValmJg==",
"engines": {
"node": ">=12"
}
},
"node_modules/tmp": {
"version": "0.0.33",
"resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz",
@ -4445,6 +4504,14 @@
"node": ">=8.0"
}
},
"node_modules/toad-cache": {
"version": "3.7.0",
"resolved": "https://registry.npmjs.org/toad-cache/-/toad-cache-3.7.0.tgz",
"integrity": "sha512-/m8M+2BJUpoJdgAHoG+baCwBT+tf2VraSfkBgl0Y00qIWt41DJ8R5B8nsEw0I58YwF5IZH6z24/2TobDKnqSWw==",
"engines": {
"node": ">=12"
}
},
"node_modules/toidentifier": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz",
@ -4683,6 +4750,26 @@
"resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
"integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ=="
},
"node_modules/ws": {
"version": "8.16.0",
"resolved": "https://registry.npmjs.org/ws/-/ws-8.16.0.tgz",
"integrity": "sha512-HS0c//TP7Ina87TfiPUz1rQzMhHrl/SG2guqRcTOIUYD2q8uhUdNHZYJUaQ8aTGPzCh+c6oawMKW35nFl1dxyQ==",
"engines": {
"node": ">=10.0.0"
},
"peerDependencies": {
"bufferutil": "^4.0.1",
"utf-8-validate": ">=5.0.2"
},
"peerDependenciesMeta": {
"bufferutil": {
"optional": true
},
"utf-8-validate": {
"optional": true
}
}
},
"node_modules/xtend": {
"version": "4.0.2",
"resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz",
@ -5342,9 +5429,9 @@
"integrity": "sha512-J8TOSBq3SoZbDhM9+R/u77hP93gz/rajSA+K2kGyijPpORPWUXHUpTaleoj+92As0S9uPRP7Oi8IqMf0u+ro6A=="
},
"@fastify/error": {
"version": "3.3.0",
"resolved": "https://registry.npmjs.org/@fastify/error/-/error-3.3.0.tgz",
"integrity": "sha512-dj7vjIn1Ar8sVXj2yAXiMNCJDmS9MQ9XMlIecX2dIzzhjSHCyKo4DdXjXMs7wKW2kj6yvVRSpuQjOZ3YLrh56w=="
"version": "3.4.1",
"resolved": "https://registry.npmjs.org/@fastify/error/-/error-3.4.1.tgz",
"integrity": "sha512-wWSvph+29GR783IhmvdwWnN4bUxTD01Vm5Xad4i7i1VuAOItLvbPAb69sb0IQ2N57yprvhNIwAP5B6xfKTmjmQ=="
},
"@fastify/fast-json-stringify-compiler": {
"version": "4.3.0",
@ -5441,6 +5528,16 @@
}
}
},
"@fastify/websocket": {
"version": "10.0.1",
"resolved": "https://registry.npmjs.org/@fastify/websocket/-/websocket-10.0.1.tgz",
"integrity": "sha512-8/pQIxTPRD8U94aILTeJ+2O3el/r19+Ej5z1O1mXlqplsUH7KzCjAI0sgd5DM/NoPjAi5qLFNIjgM5+9/rGSNw==",
"requires": {
"duplexify": "^4.1.2",
"fastify-plugin": "^4.0.0",
"ws": "^8.0.0"
}
},
"@firebox/components": {
"version": "0.1.5",
"resolved": "https://nodepack.playbox.link/@firebox/components/-/components-0.1.5.tgz",
@ -5571,6 +5668,15 @@
"integrity": "sha512-jg+97EGIcY9AGHJJRaaPVgetKDsrTgbRjQ5Msgjh/DQKEFl0DtyRr/VCOyD1T2R1MNeWPK/u7JoGhlDZnKBAfA==",
"dev": true
},
"@types/ws": {
"version": "8.5.10",
"resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.5.10.tgz",
"integrity": "sha512-vmQSUcfalpIq0R9q7uTo2lXs6eGIpt9wtnLdMv9LVpIjCA/+ufZRozlVoVelIYixx1ugCBKDhn89vnsEGOCx9A==",
"dev": true,
"requires": {
"@types/node": "*"
}
},
"abbrev": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz",
@ -5690,13 +5796,14 @@
"dev": true
},
"avvio": {
"version": "8.2.1",
"resolved": "https://registry.npmjs.org/avvio/-/avvio-8.2.1.tgz",
"integrity": "sha512-TAlMYvOuwGyLK3PfBb5WKBXZmXz2fVCgv23d6zZFdle/q3gPjmxBaeuC0pY0Dzs5PWMSgfqqEZkrye19GlDTgw==",
"version": "8.3.0",
"resolved": "https://registry.npmjs.org/avvio/-/avvio-8.3.0.tgz",
"integrity": "sha512-VBVH0jubFr9LdFASy/vNtm5giTrnbVquWBhT0fyizuNK2rQ7e7ONU2plZQWUNqtE1EmxFEb+kbSkFRkstiaS9Q==",
"requires": {
"@fastify/error": "^3.3.0",
"archy": "^1.0.0",
"debug": "^4.0.0",
"fastq": "^1.6.1"
"fastq": "^1.17.1"
}
},
"babel-plugin-macros": {
@ -6018,12 +6125,31 @@
"resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.4.5.tgz",
"integrity": "sha512-ZmdL2rui+eB2YwhsWzjInR8LldtZHGDoQ1ugH85ppHKwpUHL7j7rN0Ti9NCnGiQbhaZ11FpR+7ao1dNsmduNUg=="
},
"duplexify": {
"version": "4.1.3",
"resolved": "https://registry.npmjs.org/duplexify/-/duplexify-4.1.3.tgz",
"integrity": "sha512-M3BmBhwJRZsSx38lZyhE53Csddgzl5R7xGJNk7CVddZD6CcmwMCH8J+7AprIrQKH7TonKxaCjcv27Qmf+sQ+oA==",
"requires": {
"end-of-stream": "^1.4.1",
"inherits": "^2.0.3",
"readable-stream": "^3.1.1",
"stream-shift": "^1.0.2"
}
},
"emoji-regex": {
"version": "8.0.0",
"resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
"integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==",
"dev": true
},
"end-of-stream": {
"version": "1.4.4",
"resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz",
"integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==",
"requires": {
"once": "^1.4.0"
}
},
"error-ex": {
"version": "1.3.2",
"resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz",
@ -6148,9 +6274,9 @@
}
},
"fast-content-type-parse": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/fast-content-type-parse/-/fast-content-type-parse-1.0.0.tgz",
"integrity": "sha512-Xbc4XcysUXcsP5aHUU7Nq3OwvHq97C+WnbkeIefpeYLX+ryzFJlU6OStFJhs6Ol0LkUGpcK+wL0JwfM+FCU5IA=="
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/fast-content-type-parse/-/fast-content-type-parse-1.1.0.tgz",
"integrity": "sha512-fBHHqSTFLVnR61C+gltJuE5GkVQMV0S2nqUO8TJ+5Z3qAKG8vAx4FKai1s5jq/inV1+sREynIWSuQ6HgoSXpDQ=="
},
"fast-decode-uri-component": {
"version": "1.0.1",
@ -6184,9 +6310,9 @@
}
},
"fast-redact": {
"version": "3.3.0",
"resolved": "https://registry.npmjs.org/fast-redact/-/fast-redact-3.3.0.tgz",
"integrity": "sha512-6T5V1QK1u4oF+ATxs1lWUmlEk6P2T9HqJG3e2DnHOdVgZy2rFJBoEnrIedcTXlkAHU/zKC+7KETJ+KGGKwxgMQ=="
"version": "3.5.0",
"resolved": "https://registry.npmjs.org/fast-redact/-/fast-redact-3.5.0.tgz",
"integrity": "sha512-dwsoQlS7h9hMeYUq1W++23NDcBLV4KqONnITDV9DjfS3q1SgDGVrBdvvTLUotWtPSD7asWDV9/CmsZPy8Hf70A=="
},
"fast-uri": {
"version": "2.2.0",
@ -6194,26 +6320,26 @@
"integrity": "sha512-cIusKBIt/R/oI6z/1nyfe2FvGKVTohVRfvkOhvx0nCEW+xf5NoCXjAHcWp93uOUBchzYcsvPlrapAdX1uW+YGg=="
},
"fastify": {
"version": "4.22.0",
"resolved": "https://registry.npmjs.org/fastify/-/fastify-4.22.0.tgz",
"integrity": "sha512-HLoBmetdQ6zaJohKW6jzUww8NnwHzkbIbUEyAzM+Nnf7cZVSXRuUV+6b2/xLmu6GGkruIFJ/bIQoKWYRx4wnAQ==",
"version": "4.26.2",
"resolved": "https://registry.npmjs.org/fastify/-/fastify-4.26.2.tgz",
"integrity": "sha512-90pjTuPGrfVKtdpLeLzND5nyC4woXZN5VadiNQCicj/iJU4viNHKhsAnb7jmv1vu2IzkLXyBiCzdWuzeXgQ5Ug==",
"requires": {
"@fastify/ajv-compiler": "^3.5.0",
"@fastify/error": "^3.2.0",
"@fastify/error": "^3.4.0",
"@fastify/fast-json-stringify-compiler": "^4.3.0",
"abstract-logging": "^2.0.1",
"avvio": "^8.2.1",
"fast-content-type-parse": "^1.0.0",
"fast-json-stringify": "^5.7.0",
"find-my-way": "^7.6.0",
"light-my-request": "^5.9.1",
"pino": "^8.12.0",
"process-warning": "^2.2.0",
"avvio": "^8.3.0",
"fast-content-type-parse": "^1.1.0",
"fast-json-stringify": "^5.8.0",
"find-my-way": "^8.0.0",
"light-my-request": "^5.11.0",
"pino": "^8.17.0",
"process-warning": "^3.0.0",
"proxy-addr": "^2.0.7",
"rfdc": "^1.3.0",
"secure-json-parse": "^2.5.0",
"semver": "^7.5.0",
"tiny-lru": "^11.0.1"
"secure-json-parse": "^2.7.0",
"semver": "^7.5.4",
"toad-cache": "^3.3.0"
}
},
"fastify-plugin": {
@ -6222,9 +6348,9 @@
"integrity": "sha512-stRHYGeuqpEZTL1Ef0Ovr2ltazUT9g844X5z/zEBFLG8RYlpDiOCIG+ATvYEp+/zmc7sN29mcIMp8gvYplYPIQ=="
},
"fastq": {
"version": "1.15.0",
"resolved": "https://registry.npmjs.org/fastq/-/fastq-1.15.0.tgz",
"integrity": "sha512-wBrocU2LCXXa+lWBt8RoIRD89Fi8OdABODa/kEnyeyjS5aZO5/GNvI5sEINADqP/h8M29UHTHUb53sUu5Ihqdw==",
"version": "1.17.1",
"resolved": "https://registry.npmjs.org/fastq/-/fastq-1.17.1.tgz",
"integrity": "sha512-sRVD3lWVIXWg6By68ZN7vho9a1pQcN/WBFaAAsDDFzlJjvoGx0P8z7V1t72grFJfJhu3YPZBuu25f7Kaw2jN1w==",
"requires": {
"reusify": "^1.0.4"
}
@ -6257,9 +6383,9 @@
}
},
"find-my-way": {
"version": "7.6.2",
"resolved": "https://registry.npmjs.org/find-my-way/-/find-my-way-7.6.2.tgz",
"integrity": "sha512-0OjHn1b1nCX3eVbm9ByeEHiscPYiHLfhei1wOUU9qffQkk98wE0Lo8VrVYfSGMgnSnDh86DxedduAnBf4nwUEw==",
"version": "8.1.0",
"resolved": "https://registry.npmjs.org/find-my-way/-/find-my-way-8.1.0.tgz",
"integrity": "sha512-41QwjCGcVTODUmLLqTMeoHeiozbMXYMAE1CKFiDyi9zVZ2Vjh0yz3MF0WQZoIb+cmzP/XlbFjlF2NtJmvZHznA==",
"requires": {
"fast-deep-equal": "^3.1.3",
"fast-querystring": "^1.0.0",
@ -6812,13 +6938,20 @@
"integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug=="
},
"light-my-request": {
"version": "5.10.0",
"resolved": "https://registry.npmjs.org/light-my-request/-/light-my-request-5.10.0.tgz",
"integrity": "sha512-ZU2D9GmAcOUculTTdH9/zryej6n8TzT+fNGdNtm6SDp5MMMpHrJJkvAdE3c6d8d2chE9i+a//dS9CWZtisknqA==",
"version": "5.12.0",
"resolved": "https://registry.npmjs.org/light-my-request/-/light-my-request-5.12.0.tgz",
"integrity": "sha512-P526OX6E7aeCIfw/9UyJNsAISfcFETghysaWHQAlQYayynShT08MOj4c6fBCvTWBrHXSvqBAKDp3amUPSCQI4w==",
"requires": {
"cookie": "^0.5.0",
"process-warning": "^2.0.0",
"cookie": "^0.6.0",
"process-warning": "^3.0.0",
"set-cookie-parser": "^2.4.1"
},
"dependencies": {
"cookie": {
"version": "0.6.0",
"resolved": "https://registry.npmjs.org/cookie/-/cookie-0.6.0.tgz",
"integrity": "sha512-U71cyTamuh1CRNCfpGY6to28lxvNwPG4Guz/EVjgf3Jmzv0vlDp1atT9eS5dDjMYHucpHbWns6Lwf3BKz6svdw=="
}
}
},
"lines-and-columns": {
@ -7085,9 +7218,9 @@
}
},
"on-exit-leak-free": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/on-exit-leak-free/-/on-exit-leak-free-2.1.0.tgz",
"integrity": "sha512-VuCaZZAjReZ3vUwgOB8LxAosIurDiAW0s13rI1YwmaP++jvcxP77AWoQvenZebpCA2m8WC1/EosPYPMjnRAp/w=="
"version": "2.1.2",
"resolved": "https://registry.npmjs.org/on-exit-leak-free/-/on-exit-leak-free-2.1.2.tgz",
"integrity": "sha512-0eJJY6hXLGf1udHwfNftBqH+g73EU4B504nZeKpz1sYRKafAghwxEJunB2O7rDZkL4PGfsMVnTXZ2EjibbqcsA=="
},
"once": {
"version": "1.4.0",
@ -7330,27 +7463,27 @@
"integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g=="
},
"pino": {
"version": "8.15.0",
"resolved": "https://registry.npmjs.org/pino/-/pino-8.15.0.tgz",
"integrity": "sha512-olUADJByk4twxccmAxb1RiGKOSvddHugCV3wkqjyv+3Sooa2KLrmXrKEWOKi0XPCLasRR5jBXxioE1jxUa4KzQ==",
"version": "8.19.0",
"resolved": "https://registry.npmjs.org/pino/-/pino-8.19.0.tgz",
"integrity": "sha512-oswmokxkav9bADfJ2ifrvfHUwad6MLp73Uat0IkQWY3iAw5xTRoznXbXksZs8oaOUMpmhVWD+PZogNzllWpJaA==",
"requires": {
"atomic-sleep": "^1.0.0",
"fast-redact": "^3.1.1",
"on-exit-leak-free": "^2.1.0",
"pino-abstract-transport": "v1.0.0",
"pino-abstract-transport": "v1.1.0",
"pino-std-serializers": "^6.0.0",
"process-warning": "^2.0.0",
"process-warning": "^3.0.0",
"quick-format-unescaped": "^4.0.3",
"real-require": "^0.2.0",
"safe-stable-stringify": "^2.3.1",
"sonic-boom": "^3.1.0",
"sonic-boom": "^3.7.0",
"thread-stream": "^2.0.0"
}
},
"pino-abstract-transport": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/pino-abstract-transport/-/pino-abstract-transport-1.0.0.tgz",
"integrity": "sha512-c7vo5OpW4wIS42hUVcT5REsL8ZljsUfBjqV/e2sFxmFEFZiq1XLUp5EYLtuDH6PEHq9W1egWqRbnLUP5FuZmOA==",
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/pino-abstract-transport/-/pino-abstract-transport-1.1.0.tgz",
"integrity": "sha512-lsleG3/2a/JIWUtf9Q5gUNErBqwIu1tUKTT3dUzaf5DySw9ra1wcqKjJjLX1VTY64Wk1eEOYsVGSaGfCK85ekA==",
"requires": {
"readable-stream": "^4.0.0",
"split2": "^4.0.0"
@ -7366,9 +7499,9 @@
}
},
"readable-stream": {
"version": "4.4.2",
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-4.4.2.tgz",
"integrity": "sha512-Lk/fICSyIhodxy1IDK2HazkeGjSmezAWX2egdtJnYhtzKEsBPJowlI6F6LPb5tqIQILrMbx22S5o3GuJavPusA==",
"version": "4.5.2",
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-4.5.2.tgz",
"integrity": "sha512-yjavECdqeZ3GLXNgRXgeQEdz9fvDDkNKyHnbHRFtOr7/LcfgBcmct7t/ET+HaCTqfh06OzoAxrkN/IfjJBVe+g==",
"requires": {
"abort-controller": "^3.0.0",
"buffer": "^6.0.3",
@ -7419,9 +7552,9 @@
"integrity": "sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A=="
},
"process-warning": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/process-warning/-/process-warning-2.2.0.tgz",
"integrity": "sha512-/1WZ8+VQjR6avWOgHeEPd7SDQmFQ1B5mC1eRXsCm5TarlNmx/wCsa5GEaxGm05BORRtyG/Ex/3xq3TuRvq57qg=="
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/process-warning/-/process-warning-3.0.0.tgz",
"integrity": "sha512-mqn0kFRl0EoqhnL0GQ0veqFHyIN1yig9RHh/InzORTUiZHFRAur+aMtRkELNwGs9aNwKS6tg/An4NYBPGwvtzQ=="
},
"proxy-addr": {
"version": "2.0.7",
@ -7734,9 +7867,9 @@
}
},
"sonic-boom": {
"version": "3.3.0",
"resolved": "https://registry.npmjs.org/sonic-boom/-/sonic-boom-3.3.0.tgz",
"integrity": "sha512-LYxp34KlZ1a2Jb8ZQgFCK3niIHzibdwtwNUWKg0qQRzsDoJ3Gfgkf8KdBTFU3SkejDEIlWwnSnpVdOZIhFMl/g==",
"version": "3.8.0",
"resolved": "https://registry.npmjs.org/sonic-boom/-/sonic-boom-3.8.0.tgz",
"integrity": "sha512-ybz6OYOUjoQQCQ/i4LU8kaToD8ACtYP+Cj5qd2AO36bwbdewxWJ3ArmJ2cr6AvxlL2o0PqnCcPGUgkILbfkaCA==",
"requires": {
"atomic-sleep": "^1.0.0"
}
@ -7760,6 +7893,11 @@
"internal-slot": "^1.0.4"
}
},
"stream-shift": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/stream-shift/-/stream-shift-1.0.3.tgz",
"integrity": "sha512-76ORR0DO1o1hlKwTbi/DM3EXWGf3ZJYO8cXX5RJwnul2DEg2oyoZyjLNoQM8WsvZiFKCRfC1O0J7iCvie3RZmQ=="
},
"string_decoder": {
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz",
@ -7840,18 +7978,13 @@
}
},
"thread-stream": {
"version": "2.4.0",
"resolved": "https://registry.npmjs.org/thread-stream/-/thread-stream-2.4.0.tgz",
"integrity": "sha512-xZYtOtmnA63zj04Q+F9bdEay5r47bvpo1CaNqsKi7TpoJHcotUez8Fkfo2RJWpW91lnnaApdpRbVwCWsy+ifcw==",
"version": "2.4.1",
"resolved": "https://registry.npmjs.org/thread-stream/-/thread-stream-2.4.1.tgz",
"integrity": "sha512-d/Ex2iWd1whipbT681JmTINKw0ZwOUBZm7+Gjs64DHuX34mmw8vJL2bFAaNacaW72zYiTJxSHi5abUuOi5nsfg==",
"requires": {
"real-require": "^0.2.0"
}
},
"tiny-lru": {
"version": "11.0.1",
"resolved": "https://registry.npmjs.org/tiny-lru/-/tiny-lru-11.0.1.tgz",
"integrity": "sha512-iNgFugVuQgBKrqeO/mpiTTgmBsTP0WL6yeuLfLs/Ctf0pI/ixGqIRm8sDCwMcXGe9WWvt2sGXI5mNqZbValmJg=="
},
"tmp": {
"version": "0.0.33",
"resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz",
@ -7875,6 +8008,11 @@
"is-number": "^7.0.0"
}
},
"toad-cache": {
"version": "3.7.0",
"resolved": "https://registry.npmjs.org/toad-cache/-/toad-cache-3.7.0.tgz",
"integrity": "sha512-/m8M+2BJUpoJdgAHoG+baCwBT+tf2VraSfkBgl0Y00qIWt41DJ8R5B8nsEw0I58YwF5IZH6z24/2TobDKnqSWw=="
},
"toidentifier": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz",
@ -8051,6 +8189,12 @@
"resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
"integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ=="
},
"ws": {
"version": "8.16.0",
"resolved": "https://registry.npmjs.org/ws/-/ws-8.16.0.tgz",
"integrity": "sha512-HS0c//TP7Ina87TfiPUz1rQzMhHrl/SG2guqRcTOIUYD2q8uhUdNHZYJUaQ8aTGPzCh+c6oawMKW35nFl1dxyQ==",
"requires": {}
},
"xtend": {
"version": "4.0.2",
"resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz",

View File

@ -29,12 +29,13 @@
"@fastify/cookie": "^9.0.4",
"@fastify/secure-session": "^7.1.0",
"@fastify/static": "^6.10.2",
"@fastify/websocket": "^10.0.1",
"@firebox/components": "^0.1.5",
"@firebox/tsutil": "^0.1.2",
"@sinclair/typebox": "^0.31.5",
"dotenv": "^16.4.5",
"execa": "^8.0.1",
"fastify": "^4.22.0",
"fastify": "^4.26.2",
"isomorphic-git": "^1.25.6",
"react-pico-8": "^4.1.0",
"react-router-dom": "^6.18.0",
@ -48,6 +49,7 @@
"@types/react": "^18.2.21",
"@types/react-dom": "^18.2.7",
"@types/uuid": "^9.0.7",
"@types/ws": "^8.5.10",
"esbuild": "^0.19.2",
"nodemon": "^3.0.1",
"react": "^18.2.0",

View File

@ -1,8 +1,9 @@
import { Link, useParams } from "react-router-dom"
import { Link, useParams, useSearchParams } from "react-router-dom"
import { Pico8Console } from "./pico8-client/Pico8Console";
import { useEffect, useState } from "react";
import { DbRelease } from "../server/dbal/dbal";
import { css } from "@emotion/css";
import { useWebsocket } from "./hooks/useWebsocket";
type Info = {
release: DbRelease | null;
@ -11,7 +12,15 @@ type Info = {
export const GamePage = () => {
const {author, slug} = useParams();
// const [searchParams, setSearchParams] = useSearchParams();
const [searchParams, setSearchParams] = useSearchParams();
const room = searchParams.get('room');
const socket = useWebsocket({
url: `/api/ws/room?room=${room}`,
// url: "wss://echo.websocket.org",
onMessage({message}) {
console.log('message', message);
}
})
// const version = searchParams.get('v');
const [v, setVersion] = useState<string | null>(null);
const [info, setInfo] = useState<Info | null>(null);
@ -87,6 +96,11 @@ export const GamePage = () => {
</select>
</div>
</div>
<div>
<button onClick={() => {
room && socket.sendMessage({room, type: "hello", name: "world"});
}}>Websocket</button>
</div>
{/* <div>
<p>This is a paragraph about this game. It is a cool game. And a cool website to play it on. It automagically connects from GitHub.</p>
</div> */}

View File

@ -0,0 +1,46 @@
import { useCallback, useEffect, useRef, useState } from "react";
export const useWebsocket = (props: {url: string; onMessage: (stuff: {socket: WebSocket; message: unknown}) => void}) => {
const {url, onMessage} = props;
const onMessageRef = useRef(onMessage);
const ws = useRef<WebSocket | null>(null);
onMessageRef.current = onMessage;
useEffect(() => {
const webSocket = new WebSocket(url);
webSocket.addEventListener("open", () => {
console.log("WebSocket is open now.");
});
webSocket.addEventListener("message", (event: any) => {
onMessageRef.current({socket: webSocket, message: JSON.parse(event.data)});
});
webSocket.addEventListener("error", (event) => {
console.log("WebSocket error: ", event);
});
webSocket.addEventListener("close", () => {
console.log("WebSocket is closed now.");
ws.current = null;
});
ws.current = webSocket;
return () => {
webSocket.close();
};
}, [url]);
const sendMessage = useCallback((message: unknown) => {
if (ws.current && ws.current.readyState === WebSocket.OPEN) {
ws.current.send(JSON.stringify(message));
} else {
console.error("WebSocket is not open. Message not sent.");
}
}, []);
return {sendMessage};
};

74
src/server/api/room.ts Normal file
View File

@ -0,0 +1,74 @@
import { Type } from "@sinclair/typebox";
import { FirRouteOptions, FirWebsocketHandler, FirWebsocketInput } from "../util/routewrap.js";
import { WebSocket } from "@fastify/websocket";
const method = "GET";
const url = "/api/ws/room";
const payloadT = Type.Any();
type Room = {
name: string;
sockets: WebSocket[];
}
let rooms: Room[] = [];
const websocket = {
onOpen({socket, req}) {
const {room: roomName} = req.query as any;
let room = rooms.find(r => r.name === roomName);
if (!room) {
console.log("creating room", roomName);
room = {
name: roomName,
sockets: [],
};
rooms.push(room);
}
if (!room.sockets.includes(socket)) {
console.log("adding socket to room", roomName);
room.sockets.push(socket);
}
console.log('rooms', rooms);
},
onClose({socket, req}) {
const {room: roomName} = req.query as any;
const room = rooms.find(r => r.name === roomName);
if (room) {
room.sockets = room.sockets.filter(sock => sock !== socket);
if (room.sockets.length === 0) {
rooms = rooms.filter(r => r !== room);
}
}
console.log('rooms', rooms);
},
onMessage({socket, payload}) {
const {room: roomName} = payload as any;
let room = rooms.find(r => r.name === roomName);
if (!room) {
console.log("creating room", roomName);
room = {
name: roomName,
sockets: [],
};
rooms.push(room);
}
if (!room.sockets.includes(socket)) {
console.log("adding socket to room", roomName);
room.sockets.push(socket);
}
console.log("replying to everyone in room", roomName);
room.sockets.forEach(sock => {
sock.send(JSON.stringify(payload));
});
console.log('rooms', rooms);
},
} as const satisfies FirWebsocketHandler;
export default {
method,
url,
payloadT,
websocket,
} as const satisfies FirRouteOptions<typeof payloadT>;

View File

@ -111,7 +111,6 @@ export const getAuthorGames = async (where: {
export const insertRelease = async (props: {manifest: PicobookManifest, carts: {name: string; rom: number[]}[]}) => {
const {manifest, carts} = props;
// console.log('carts', JSON.stringify(carts));
const {id: slug, author, repo, version} = manifest;
const id = uuidv4();
const now = new Date();

43
src/server/file.ts Normal file
View File

@ -0,0 +1,43 @@
// Import the framework and instantiate it
import Fastify from 'fastify'
import {fastifyWebsocket} from '@fastify/websocket';
const server = Fastify({
logger: true
});
server.register(fastifyWebsocket);
server.get("/api/ws/room", { websocket: true }, function wsHandler (socket) {
console.log("Client connected!");
console.log(socket);
socket.on('upgrade', () => {
console.log('upgraded');
});
socket.on('message', message => {
console.log('Message from server:', message);
socket.send(`Echo: ${message}`);
});
socket.on('close', () => {
console.log('WebSocket connection closed');
});
socket.on('error', error => {
console.error('WebSocket error:', error);
});
});
// Run the server!
try {
// Note: host needs to be 0.0.0.0 rather than omitted or localhost, otherwise
// it always returns an empty reply when used inside docker...
// See: https://github.com/fastify/fastify/issues/935
await server.listen({ port: parseInt(process.env["PORT"] ?? "3000"), host: "0.0.0.0" })
} catch (err) {
server.log.error(err)
process.exit(1)
}

View File

@ -1,8 +1,9 @@
// Import the framework and instantiate it
import Fastify from 'fastify'
import fastifyStatic from '@fastify/static'
import {fastifyWebsocket} from '@fastify/websocket';
import { routeList } from "./routelist.ts";
import { route } from "./util/routewrap.ts";
import { attachRoute } from "./util/routewrap.ts";
import { git } from './util/git.ts';
import path from "path";
import {fileURLToPath} from 'url';
@ -17,13 +18,15 @@ const server = Fastify({
logger: true
});
server.register(fastifyWebsocket);
server.register(fastifyStatic, {
root: new URL('public', import.meta.url).toString().slice("file://".length),
prefix: '/',
});
routeList.forEach(firRoute => {
server.route(route(firRoute));
attachRoute(server, firRoute);
});
server.setNotFoundHandler((req, res) => {

View File

@ -2,6 +2,7 @@ import echo from "./api/echo.ts";
import getAuthor from "./api/getAuthor.ts";
import getRelease from "./api/getRelease.ts";
import release from "./api/release.ts";
import room from "./api/room.ts";
import webhook from "./api/webhook.ts";
export const routeList = [
@ -10,6 +11,7 @@ export const routeList = [
release,
getRelease,
getAuthor,
room,
];
export type RouteList = typeof routeList;

View File

@ -1,7 +1,10 @@
import { Static, TSchema } from "@sinclair/typebox";
import { Value } from "@sinclair/typebox/value";
import { HTTPMethods } from "fastify"
import { FastifyInstance, FastifyRequest, HTTPMethods } from "fastify"
import { RouteOptions } from "fastify/types/route.js";
import { type WebSocket } from "@fastify/websocket";
type WebsocketConnection = Parameters<Defined<RouteOptions["wsHandler"]>>[0];
type URLString = string;
@ -9,22 +12,94 @@ export type FirRouteInput<TPayloadSchema extends TSchema> = {
payload: Static<TPayloadSchema>,
}
export type FirWebsocketInput<TPayloadSchema extends TSchema> = {
socket: WebsocketConnection,
req: FastifyRequest,
payload: Static<TPayloadSchema>,
}
export type FirWebsocketHandler<TIn extends TSchema = TSchema> = {
onMessage?(input: FirWebsocketInput<TIn>): void,
onOpen?(input: {socket: WebSocket, req: FastifyRequest}): void,
onClose?(input: {socket: WebSocket, req: FastifyRequest}): void,
onError?(input: {socket: WebSocket, req: FastifyRequest, error: unknown}): void,
};
export type FirRouteOptions<TIn extends TSchema = TSchema, TOut extends TSchema = TSchema> = {
method: HTTPMethods,
url: URLString,
payloadT: TIn,
responseT?: TOut,
} & ({
handler: (input: FirRouteInput<TIn>) => Static<TOut> | Promise<Static<TOut>>,
}
} | {
websocket: FirWebsocketHandler<TIn>,
})
export const route = <TIn extends TSchema, TOut extends TSchema>(routeOptions: FirRouteOptions<TIn, TOut>): RouteOptions => {
type Defined<T> = T extends undefined ? never : T;
export const attachRoute = <TIn extends TSchema, TOut extends TSchema>(server: FastifyInstance, routeOptions: FirRouteOptions<TIn, TOut>) => {
const {
method,
url,
payloadT,
handler,
} = routeOptions;
if ("websocket" in routeOptions) {
console.log('SETTING UP WS');
const {websocket} = routeOptions;
server.register(async function(fastify: FastifyInstance) {
fastify.get('/api/ws/room', { websocket: true }, (socket: WebSocket, req: FastifyRequest) => {
websocket.onOpen && websocket.onOpen({socket, req});
socket.on('message', (message: any) => {
const payload = JSON.parse(message.toString());
if (Value.Check(payloadT, payload)) {
websocket.onMessage && websocket.onMessage({socket, payload, req});
} else {
throw new Error("Payload wrong shape.");
}
});
socket.on('close', () => {
websocket.onClose && websocket.onClose({socket, req});
});
socket.on('error', (error: any) => {
websocket.onError && websocket.onError({socket, error, req});
});
})
});
return;
// const {websocket} = routeOptions;
// const augmentedWsHandler = (conn: Parameters<Defined<RouteOptions["wsHandler"]>>[0]) => {
// console.log('HELLO');
// conn.on("message", (message) => {
// const payload = JSON.parse(message.toString());
// if (Value.Check(payloadT, payload)) {
// websocket({socket: conn, payload});
// } else {
// throw new Error("Payload wrong shape.");
// }
// });
// }
// return {
// method: 'GET', // WebSocket upgrades are GET requests
// url,
// // websocket: true,
// wsHandler: augmentedWsHandler,
// handler: (...args) => {
// console.log('socket!');
// const socket = args[0].socket.on("message", () => {
// console.log("connected!");
// })
// },
// // handler: (request, reply) => {
// // reply.code(405).send({ message: 'Method Not Allowed' }); // Handle non-WebSocket requests
// // }
// }
}
const {handler} = routeOptions;
const augmentedHandler = (request: Parameters<RouteOptions["handler"]>[0]) => {
const {
body,
@ -34,14 +109,13 @@ export const route = <TIn extends TSchema, TOut extends TSchema>(routeOptions: F
if (Value.Check(payloadT, payload)) {
return handler({payload});
} else {
throw new Error("Payload wrong shape.");
}
}
return {
server.route({
method,
url,
handler: augmentedHandler,
}
});
}