diff --git a/nextjs/next.config.mjs b/nextjs/next.config.mjs
index fb98a23..81aa05b 100644
--- a/nextjs/next.config.mjs
+++ b/nextjs/next.config.mjs
@@ -1,7 +1,5 @@
/** @type {import('next').NextConfig} */
-
-const nextConfig = {
-};
+const nextConfig = {};
export default nextConfig;
diff --git a/nextjs/package.json b/nextjs/package.json
index a1af495..1009acb 100644
--- a/nextjs/package.json
+++ b/nextjs/package.json
@@ -1,11 +1,13 @@
{
- "name": "canvas-mangement",
+ "name": "canvas-management",
"version": "0.1.0",
"private": true,
"scripts": {
"dev": "next dev",
+ "socket": "node src/websocket.js",
"build": "next build",
"start": "next start",
+ "socketProd": "NODE_ENV=production node src/websocket.js",
"lint": "tsc && next lint",
"test": "vitest"
},
@@ -15,11 +17,16 @@
"@trpc/client": "11.0.0-rc.608",
"@trpc/react-query": "11.0.0-rc.608",
"@trpc/server": "11.0.0-rc.608",
+ "@types/ws": "^8.5.13",
+ "chokidar": "^4.0.1",
"jsdom": "^25.0.0",
"next": "^15.0.2",
"react": "^18",
"react-dom": "^18",
+ "socket.io": "^4.8.1",
+ "socket.io-client": "^4.8.1",
"superjson": "^2.2.1",
+ "ws": "^8.18.0",
"zod": "^3.23.8"
},
"devDependencies": {
diff --git a/nextjs/pnpm-lock.yaml b/nextjs/pnpm-lock.yaml
index 95950bb..7040af8 100644
--- a/nextjs/pnpm-lock.yaml
+++ b/nextjs/pnpm-lock.yaml
@@ -23,6 +23,12 @@ importers:
'@trpc/server':
specifier: 11.0.0-rc.608
version: 11.0.0-rc.608
+ '@types/ws':
+ specifier: ^8.5.13
+ version: 8.5.13
+ chokidar:
+ specifier: ^4.0.1
+ version: 4.0.1
jsdom:
specifier: ^25.0.0
version: 25.0.1
@@ -35,9 +41,18 @@ importers:
react-dom:
specifier: ^18
version: 18.3.1(react@18.3.1)
+ socket.io:
+ specifier: ^4.8.1
+ version: 4.8.1
+ socket.io-client:
+ specifier: ^4.8.1
+ version: 4.8.1
superjson:
specifier: ^2.2.1
version: 2.2.1
+ ws:
+ specifier: ^8.18.0
+ version: 8.18.0
zod:
specifier: ^3.23.8
version: 3.23.8
@@ -92,7 +107,7 @@ importers:
version: 2.4.1(csstype@3.1.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
tailwindcss:
specifier: ^3.4.1
- version: 3.4.14
+ version: 3.4.14(ts-node@10.9.2(@types/node@22.9.0)(typescript@5.6.3))
typescript:
specifier: ^5
version: 5.6.3
@@ -196,6 +211,10 @@ packages:
resolution: {integrity: sha512-Z/yiTPj+lDVnF7lWeKCIJzaIkI0vYO87dMpZ4bg4TDrFe4XXLFWL1TbXU27gBP3QccxV9mZICCrnjnYlJjXHOA==}
engines: {node: '>=6.9.0'}
+ '@cspotcode/source-map-support@0.8.1':
+ resolution: {integrity: sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==}
+ engines: {node: '>=12'}
+
'@emnapi/runtime@1.3.1':
resolution: {integrity: sha512-kEBmG8KyqtxJZv+ygbEim+KCGtIq1fC22Ms3S4ziXmYKm8uyoLX0MHONVKwp+9opg390VaKRNt4a7A9NwmpNhw==}
@@ -495,6 +514,9 @@ packages:
'@jridgewell/trace-mapping@0.3.25':
resolution: {integrity: sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==}
+ '@jridgewell/trace-mapping@0.3.9':
+ resolution: {integrity: sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==}
+
'@monaco-editor/loader@1.4.0':
resolution: {integrity: sha512-00ioBig0x642hytVspPl7DbQyaSWRaolYie/UFNjoTdvoKPzo6xrXLhTk9ixgIKcLH5b5vDOjVNiGyY+uDCUlg==}
peerDependencies:
@@ -680,6 +702,9 @@ packages:
'@rushstack/eslint-patch@1.10.4':
resolution: {integrity: sha512-WJgX9nzTqknM393q1QJDJmoW28kUfEnybeTfVNcNAPnIx210RXm2DiXiHzfNPJNIUUb1tJnz/l4QGtJ30PgWmA==}
+ '@socket.io/component-emitter@3.1.2':
+ resolution: {integrity: sha512-9BCxFwvbGg/RsZK9tjXd8s4UcwR0MWeFQ1XEKIQVVvAGJyINdrqKMcTRyLoK8Rse1GjzLV9cwjWV1olXRWEXVA==}
+
'@swc/counter@0.1.3':
resolution: {integrity: sha512-e2BR4lsJkkRlKZ/qCHPw9ZaSxc0MVUd7gtbtaB7aMvHeJVYe8sOB8DBZkP2DtISHGSku9sCK6T6cnY0CtXrOCQ==}
@@ -730,6 +755,18 @@ packages:
'@trpc/server@11.0.0-rc.608':
resolution: {integrity: sha512-+qxeyWlS+1zlTbekG7C+khDGZn788kLJG+ufO8mH/kgbRC+/a0OZSk7tyQI2Pf+UWeKc6KzrTi7TRBOOEBBE1A==}
+ '@tsconfig/node10@1.0.11':
+ resolution: {integrity: sha512-DcRjDCujK/kCk/cUe8Xz8ZSpm8mS3mNNpta+jGCA6USEDfktlNvm1+IuZ9eTcDbNk41BHwpHHeW+N1lKCz4zOw==}
+
+ '@tsconfig/node12@1.0.11':
+ resolution: {integrity: sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==}
+
+ '@tsconfig/node14@1.0.3':
+ resolution: {integrity: sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==}
+
+ '@tsconfig/node16@1.0.4':
+ resolution: {integrity: sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==}
+
'@types/aria-query@5.0.4':
resolution: {integrity: sha512-rfT93uj5s0PRL7EzccGMs3brplhcrghnDoV26NqKhCAS1hVo+WdNsPvE/yb6ilfr5hi2MEk6d5EWJTKdxg8jVw==}
@@ -745,6 +782,12 @@ packages:
'@types/babel__traverse@7.20.6':
resolution: {integrity: sha512-r1bzfrm0tomOI8g1SzvCaQHo6Lcv6zu0EA+W2kHrt8dyrHQxGzBBL4kdkzIS+jBMV+EYcMAEAqXqYaLJq5rOZg==}
+ '@types/cookie@0.4.1':
+ resolution: {integrity: sha512-XW/Aa8APYr6jSVVA1y/DEIZX0/GMKLEVekNG727R8cs56ahETkRAy/3DR7+fJyh7oUgGwNQaRfXCun0+KbWY7Q==}
+
+ '@types/cors@2.8.17':
+ resolution: {integrity: sha512-8CGDvrBj1zgo2qE+oS3pOCyYNqCPryMWY2bGfwA0dcfopWGgxs+78df0Rs3rc9THP4JkOhLsAa+15VdpAqkcUA==}
+
'@types/dompurify@3.0.5':
resolution: {integrity: sha512-1Wg0g3BtQF7sSb27fJQAKck1HECM6zV1EB66j8JH9i3LCjYabJa0FSdiSgsD5K/RbrsR0SiraKacLB+T8ZVYAg==}
@@ -769,6 +812,9 @@ packages:
'@types/trusted-types@2.0.7':
resolution: {integrity: sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw==}
+ '@types/ws@8.5.13':
+ resolution: {integrity: sha512-osM/gWBTPKgHV8XkTunnegTRIsvF6owmf5w+JtAfOw472dptdm0dlGv4xCt6GwQRcC2XVOvvRE/0bAoQcL2QkA==}
+
'@typescript-eslint/eslint-plugin@8.13.0':
resolution: {integrity: sha512-nQtBLiZYMUPkclSeC3id+x4uVd1SGtHuElTxL++SfP47jR0zfkZBJHc+gL4qPsgTuypz0k8Y2GheaDYn6Gy3rg==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
@@ -864,11 +910,19 @@ packages:
'@vitest/utils@2.1.4':
resolution: {integrity: sha512-MXDnZn0Awl2S86PSNIim5PWXgIAx8CIkzu35mBdSApUip6RFOGXBCf3YFyeEu8n1IHk4bWD46DeYFu9mQlFIRg==}
+ accepts@1.3.8:
+ resolution: {integrity: sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==}
+ engines: {node: '>= 0.6'}
+
acorn-jsx@5.3.2:
resolution: {integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==}
peerDependencies:
acorn: ^6.0.0 || ^7.0.0 || ^8.0.0
+ acorn-walk@8.3.4:
+ resolution: {integrity: sha512-ueEepnujpqee2o5aIYnvHU6C0A42MNdsIDeqy5BydrkuC5R1ZuUFnm27EeFJGoEHJQgn3uleRvmTXaJgfXbt4g==}
+ engines: {node: '>=0.4.0'}
+
acorn@8.14.0:
resolution: {integrity: sha512-cl669nCJTZBsL97OF4kUQm5g5hC2uihk0NxY3WENAC0TYdILVkAyHymAntgxGkl7K+t0cXIrH5siy5S4XkFycA==}
engines: {node: '>=0.4.0'}
@@ -908,6 +962,9 @@ packages:
resolution: {integrity: sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==}
engines: {node: '>= 8'}
+ arg@4.1.3:
+ resolution: {integrity: sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==}
+
arg@5.0.2:
resolution: {integrity: sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==}
@@ -981,6 +1038,10 @@ packages:
balanced-match@1.0.2:
resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==}
+ base64id@2.0.0:
+ resolution: {integrity: sha512-lGe34o6EHj9y3Kts9R4ZYs/Gr+6N7MCaMlIFA3F1R2O5/m7K06AxfSeO5530PEERE6/WyEg3lsuyw4GHlPZHog==}
+ engines: {node: ^4.5.0 || >= 5.9}
+
binary-extensions@2.3.0:
resolution: {integrity: sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==}
engines: {node: '>=8'}
@@ -1039,6 +1100,10 @@ packages:
resolution: {integrity: sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==}
engines: {node: '>= 8.10.0'}
+ chokidar@4.0.1:
+ resolution: {integrity: sha512-n8enUVCED/KVRQlab1hr3MVpcVMvxtZjmEa956u+4YijlmQED223XMSYj2tLuKvr4jcCTzNNMpQDUer72MMmzA==}
+ engines: {node: '>= 14.16.0'}
+
client-only@0.0.1:
resolution: {integrity: sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA==}
@@ -1070,10 +1135,21 @@ packages:
convert-source-map@2.0.0:
resolution: {integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==}
+ cookie@0.7.2:
+ resolution: {integrity: sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==}
+ engines: {node: '>= 0.6'}
+
copy-anything@3.0.5:
resolution: {integrity: sha512-yCEafptTtb4bk7GLEQoM8KVJpxAfdBJYaXyzQEgQQQgYrZiDp8SJmGKlYza6CYjEDNstAdNdKA3UuoULlEbS6w==}
engines: {node: '>=12.13'}
+ cors@2.8.5:
+ resolution: {integrity: sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==}
+ engines: {node: '>= 0.10'}
+
+ create-require@1.1.1:
+ resolution: {integrity: sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==}
+
cross-spawn@7.0.5:
resolution: {integrity: sha512-ZVJrKKYunU38/76t0RMOulHOnUcbU9GbpWKAOZ0mhjr7CX6FVrH+4FrAapSOekrgFQ3f/8gwMEuIft0aKq6Hug==}
engines: {node: '>= 8'}
@@ -1159,6 +1235,10 @@ packages:
didyoumean@1.2.2:
resolution: {integrity: sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw==}
+ diff@4.0.2:
+ resolution: {integrity: sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==}
+ engines: {node: '>=0.3.1'}
+
dlv@1.1.3:
resolution: {integrity: sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==}
@@ -1188,6 +1268,17 @@ packages:
emoji-regex@9.2.2:
resolution: {integrity: sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==}
+ engine.io-client@6.6.2:
+ resolution: {integrity: sha512-TAr+NKeoVTjEVW8P3iHguO1LO6RlUz9O5Y8o7EY0fU+gY1NYqas7NN3slpFtbXEsLMHk0h90fJMfKjRkQ0qUIw==}
+
+ engine.io-parser@5.2.3:
+ resolution: {integrity: sha512-HqD3yTBfnBxIrbnM1DoD6Pcq8NECnh8d4As1Qgh0z5Gg3jRRIqijury0CL3ghu/edArpUYiYqQiDUQBIs4np3Q==}
+ engines: {node: '>=10.0.0'}
+
+ engine.io@6.6.2:
+ resolution: {integrity: sha512-gmNvsYi9C8iErnZdVcJnvCpSKbWTt1E8+JZo8b+daLninywUWi5NQ5STSHZ9rFjFO7imNcvb8Pc5pe/wMR5xEw==}
+ engines: {node: '>=10.2.0'}
+
enhanced-resolve@5.17.1:
resolution: {integrity: sha512-LMHl3dXhTcfv8gM4kEzIUeTQ+7fpdA0l2tUf34BddXPkz2A5xJ5L/Pchd5BL6rdccM9QGvu0sWZzK1Z1t4wwyg==}
engines: {node: '>=10.13.0'}
@@ -1797,6 +1888,9 @@ packages:
magic-string@0.30.12:
resolution: {integrity: sha512-Ea8I3sQMVXr8JhN4z+H/d8zwo+tYDgHE9+5G4Wnrwhs0gaK9fXTKx0Tw5Xwsd/bCPTTZNRAdpyzvoeORe9LYpw==}
+ make-error@1.3.6:
+ resolution: {integrity: sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==}
+
marked@14.1.4:
resolution: {integrity: sha512-vkVZ8ONmUdPnjCKc5uTRvmkRbx4EAi2OkTOXmfTDhZz3OFqMNBM1oTTWwTr4HY4uAEojhzPf+Fy8F1DWa3Sndg==}
engines: {node: '>= 18'}
@@ -1849,6 +1943,10 @@ packages:
natural-compare@1.4.0:
resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==}
+ negotiator@0.6.3:
+ resolution: {integrity: sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==}
+ engines: {node: '>= 0.6'}
+
next@15.0.2:
resolution: {integrity: sha512-rxIWHcAu4gGSDmwsELXacqAPUk+j8dV/A9cDF5fsiCMpkBDYkO2AEaL1dfD+nNmDiU6QMCFN8Q30VEKapT9UHQ==}
engines: {node: '>=18.18.0'}
@@ -2090,6 +2188,10 @@ packages:
resolution: {integrity: sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==}
engines: {node: '>=8.10.0'}
+ readdirp@4.0.2:
+ resolution: {integrity: sha512-yDMz9g+VaZkqBYS/ozoBJwaBhTbZo3UNYQHNRw1D3UFQB8oHB4uS/tAODO+ZLjGWmUbKnIlOWO+aaIiAxrUWHA==}
+ engines: {node: '>= 14.16.0'}
+
reflect.getprototypeof@1.0.6:
resolution: {integrity: sha512-fmfw4XgoDke3kdI6h4xcUz1dG8uaiv5q9gcEwLS4Pnth2kxT+GZ7YehS1JTMGBQmtV7Y4GFGbs2re2NqhdozUg==}
engines: {node: '>= 0.4'}
@@ -2197,6 +2299,21 @@ packages:
simple-swizzle@0.2.2:
resolution: {integrity: sha512-JA//kQgZtbuY83m+xT+tXJkmJncGMTFT+C+g2h2R9uxkYIrE2yy9sgmcLhCnw57/WSD+Eh3J97FPEDFnbXnDUg==}
+ socket.io-adapter@2.5.5:
+ resolution: {integrity: sha512-eLDQas5dzPgOWCk9GuuJC2lBqItuhKI4uxGgo9aIV7MYbk2h9Q6uULEh8WBzThoI7l+qU9Ast9fVUmkqPP9wYg==}
+
+ socket.io-client@4.8.1:
+ resolution: {integrity: sha512-hJVXfu3E28NmzGk8o1sHhN3om52tRvwYeidbj7xKy2eIIse5IoKX3USlS6Tqt3BHAtflLIkCQBkzVrEEfWUyYQ==}
+ engines: {node: '>=10.0.0'}
+
+ socket.io-parser@4.2.4:
+ resolution: {integrity: sha512-/GbIKmo8ioc+NIWIhwdecY0ge+qVBSMdgxGygevmdHj24bsfgtCmcUUcQ5ZzcylGFHsN3k4HB4Cgkl96KVnuew==}
+ engines: {node: '>=10.0.0'}
+
+ socket.io@4.8.1:
+ resolution: {integrity: sha512-oZ7iUCxph8WYRHHcjBEc9unw3adt5CmSNlppj/5Q4k2RIrhl8Z5yY2Xr4j9zj0+wzVZ0bxmYoGSzKJnRl6A4yg==}
+ engines: {node: '>=10.2.0'}
+
source-map-js@1.2.1:
resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==}
engines: {node: '>=0.10.0'}
@@ -2358,6 +2475,20 @@ packages:
ts-interface-checker@0.1.13:
resolution: {integrity: sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==}
+ ts-node@10.9.2:
+ resolution: {integrity: sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ==}
+ hasBin: true
+ peerDependencies:
+ '@swc/core': '>=1.2.50'
+ '@swc/wasm': '>=1.2.50'
+ '@types/node': '*'
+ typescript: '>=2.7'
+ peerDependenciesMeta:
+ '@swc/core':
+ optional: true
+ '@swc/wasm':
+ optional: true
+
tsconfig-paths@3.15.0:
resolution: {integrity: sha512-2Ac2RgzDe/cn48GvOe3M+o82pEFewD3UPbyoUHHdKasHwJKjds4fLXWf/Ux5kATBKN20oaFGu+jbElp1pos0mg==}
@@ -2411,6 +2542,13 @@ packages:
util-deprecate@1.0.2:
resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==}
+ v8-compile-cache-lib@3.0.1:
+ resolution: {integrity: sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==}
+
+ vary@1.1.2:
+ resolution: {integrity: sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==}
+ engines: {node: '>= 0.8'}
+
vite-node@2.1.4:
resolution: {integrity: sha512-kqa9v+oi4HwkG6g8ufRnb5AeplcRw8jUF6/7/Qz1qRQOXHImG8YnLbB+LLszENwFnoBl9xIf9nVdCFzNd7GQEg==}
engines: {node: ^18.0.0 || >=20.0.0}
@@ -2532,6 +2670,18 @@ packages:
wrappy@1.0.2:
resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==}
+ ws@8.17.1:
+ resolution: {integrity: sha512-6XQFvXTkbfUOZOKKILFG1PDK2NDQs4azKQl26T0YS5CxqWLgXajbPZ+h4gZekJyRqFU8pvnbAbbs/3TgRPy+GQ==}
+ 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
+
ws@8.18.0:
resolution: {integrity: sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw==}
engines: {node: '>=10.0.0'}
@@ -2551,6 +2701,10 @@ packages:
xmlchars@2.2.0:
resolution: {integrity: sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==}
+ xmlhttprequest-ssl@2.1.2:
+ resolution: {integrity: sha512-TEU+nJVUUnA4CYJFLvK5X9AOeH4KvDvhIfm0vV1GaQRtchnG0hgK5p8hw/xjv8cunWYCsiPCSDzObPyhEwq3KQ==}
+ engines: {node: '>=0.4.0'}
+
yallist@3.1.1:
resolution: {integrity: sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==}
@@ -2559,6 +2713,10 @@ packages:
engines: {node: '>= 14'}
hasBin: true
+ yn@3.1.1:
+ resolution: {integrity: sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==}
+ engines: {node: '>=6'}
+
yocto-queue@0.1.0:
resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==}
engines: {node: '>=10'}
@@ -2689,6 +2847,11 @@ snapshots:
'@babel/helper-string-parser': 7.25.9
'@babel/helper-validator-identifier': 7.25.9
+ '@cspotcode/source-map-support@0.8.1':
+ dependencies:
+ '@jridgewell/trace-mapping': 0.3.9
+ optional: true
+
'@emnapi/runtime@1.3.1':
dependencies:
tslib: 2.8.1
@@ -2899,6 +3062,12 @@ snapshots:
'@jridgewell/resolve-uri': 3.1.2
'@jridgewell/sourcemap-codec': 1.5.0
+ '@jridgewell/trace-mapping@0.3.9':
+ dependencies:
+ '@jridgewell/resolve-uri': 3.1.2
+ '@jridgewell/sourcemap-codec': 1.5.0
+ optional: true
+
'@monaco-editor/loader@1.4.0(monaco-editor@0.52.0)':
dependencies:
monaco-editor: 0.52.0
@@ -3018,6 +3187,8 @@ snapshots:
'@rushstack/eslint-patch@1.10.4': {}
+ '@socket.io/component-emitter@3.1.2': {}
+
'@swc/counter@0.1.3': {}
'@swc/helpers@0.5.13':
@@ -3066,6 +3237,18 @@ snapshots:
'@trpc/server@11.0.0-rc.608': {}
+ '@tsconfig/node10@1.0.11':
+ optional: true
+
+ '@tsconfig/node12@1.0.11':
+ optional: true
+
+ '@tsconfig/node14@1.0.3':
+ optional: true
+
+ '@tsconfig/node16@1.0.4':
+ optional: true
+
'@types/aria-query@5.0.4': {}
'@types/babel__core@7.20.5':
@@ -3089,6 +3272,12 @@ snapshots:
dependencies:
'@babel/types': 7.26.0
+ '@types/cookie@0.4.1': {}
+
+ '@types/cors@2.8.17':
+ dependencies:
+ '@types/node': 22.9.0
+
'@types/dompurify@3.0.5':
dependencies:
'@types/trusted-types': 2.0.7
@@ -3114,6 +3303,10 @@ snapshots:
'@types/trusted-types@2.0.7': {}
+ '@types/ws@8.5.13':
+ dependencies:
+ '@types/node': 22.9.0
+
'@typescript-eslint/eslint-plugin@8.13.0(@typescript-eslint/parser@8.13.0(eslint@8.57.1)(typescript@5.6.3))(eslint@8.57.1)(typescript@5.6.3)':
dependencies:
'@eslint-community/regexpp': 4.12.1
@@ -3248,10 +3441,20 @@ snapshots:
loupe: 3.1.2
tinyrainbow: 1.2.0
+ accepts@1.3.8:
+ dependencies:
+ mime-types: 2.1.35
+ negotiator: 0.6.3
+
acorn-jsx@5.3.2(acorn@8.14.0):
dependencies:
acorn: 8.14.0
+ acorn-walk@8.3.4:
+ dependencies:
+ acorn: 8.14.0
+ optional: true
+
acorn@8.14.0: {}
agent-base@7.1.1:
@@ -3286,6 +3489,9 @@ snapshots:
normalize-path: 3.0.0
picomatch: 2.3.1
+ arg@4.1.3:
+ optional: true
+
arg@5.0.2: {}
argparse@2.0.1: {}
@@ -3385,6 +3591,8 @@ snapshots:
balanced-match@1.0.2: {}
+ base64id@2.0.0: {}
+
binary-extensions@2.3.0: {}
brace-expansion@1.1.11:
@@ -3454,6 +3662,10 @@ snapshots:
optionalDependencies:
fsevents: 2.3.3
+ chokidar@4.0.1:
+ dependencies:
+ readdirp: 4.0.2
+
client-only@0.0.1: {}
color-convert@2.0.1:
@@ -3484,10 +3696,20 @@ snapshots:
convert-source-map@2.0.0: {}
+ cookie@0.7.2: {}
+
copy-anything@3.0.5:
dependencies:
is-what: 4.1.16
+ cors@2.8.5:
+ dependencies:
+ object-assign: 4.1.1
+ vary: 1.1.2
+
+ create-require@1.1.1:
+ optional: true
+
cross-spawn@7.0.5:
dependencies:
path-key: 3.1.1
@@ -3562,6 +3784,9 @@ snapshots:
didyoumean@1.2.2: {}
+ diff@4.0.2:
+ optional: true
+
dlv@1.1.3: {}
doctrine@2.1.0:
@@ -3584,6 +3809,37 @@ snapshots:
emoji-regex@9.2.2: {}
+ engine.io-client@6.6.2:
+ dependencies:
+ '@socket.io/component-emitter': 3.1.2
+ debug: 4.3.7
+ engine.io-parser: 5.2.3
+ ws: 8.17.1
+ xmlhttprequest-ssl: 2.1.2
+ transitivePeerDependencies:
+ - bufferutil
+ - supports-color
+ - utf-8-validate
+
+ engine.io-parser@5.2.3: {}
+
+ engine.io@6.6.2:
+ dependencies:
+ '@types/cookie': 0.4.1
+ '@types/cors': 2.8.17
+ '@types/node': 22.9.0
+ accepts: 1.3.8
+ base64id: 2.0.0
+ cookie: 0.7.2
+ cors: 2.8.5
+ debug: 4.3.7
+ engine.io-parser: 5.2.3
+ ws: 8.17.1
+ transitivePeerDependencies:
+ - bufferutil
+ - supports-color
+ - utf-8-validate
+
enhanced-resolve@5.17.1:
dependencies:
graceful-fs: 4.2.11
@@ -4379,6 +4635,9 @@ snapshots:
dependencies:
'@jridgewell/sourcemap-codec': 1.5.0
+ make-error@1.3.6:
+ optional: true
+
marked@14.1.4: {}
merge2@1.4.1: {}
@@ -4420,6 +4679,8 @@ snapshots:
natural-compare@1.4.0: {}
+ negotiator@0.6.3: {}
+
next@15.0.2(@babel/core@7.26.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1):
dependencies:
'@next/env': 15.0.2
@@ -4561,12 +4822,13 @@ snapshots:
camelcase-css: 2.0.1
postcss: 8.4.47
- postcss-load-config@4.0.2(postcss@8.4.47):
+ postcss-load-config@4.0.2(postcss@8.4.47)(ts-node@10.9.2(@types/node@22.9.0)(typescript@5.6.3)):
dependencies:
lilconfig: 3.1.2
yaml: 2.6.0
optionalDependencies:
postcss: 8.4.47
+ ts-node: 10.9.2(@types/node@22.9.0)(typescript@5.6.3)
postcss-nested@6.2.0(postcss@8.4.47):
dependencies:
@@ -4649,6 +4911,8 @@ snapshots:
dependencies:
picomatch: 2.3.1
+ readdirp@4.0.2: {}
+
reflect.getprototypeof@1.0.6:
dependencies:
call-bind: 1.0.7
@@ -4812,6 +5076,47 @@ snapshots:
is-arrayish: 0.3.2
optional: true
+ socket.io-adapter@2.5.5:
+ dependencies:
+ debug: 4.3.7
+ ws: 8.17.1
+ transitivePeerDependencies:
+ - bufferutil
+ - supports-color
+ - utf-8-validate
+
+ socket.io-client@4.8.1:
+ dependencies:
+ '@socket.io/component-emitter': 3.1.2
+ debug: 4.3.7
+ engine.io-client: 6.6.2
+ socket.io-parser: 4.2.4
+ transitivePeerDependencies:
+ - bufferutil
+ - supports-color
+ - utf-8-validate
+
+ socket.io-parser@4.2.4:
+ dependencies:
+ '@socket.io/component-emitter': 3.1.2
+ debug: 4.3.7
+ transitivePeerDependencies:
+ - supports-color
+
+ socket.io@4.8.1:
+ dependencies:
+ accepts: 1.3.8
+ base64id: 2.0.0
+ cors: 2.8.5
+ debug: 4.3.7
+ engine.io: 6.6.2
+ socket.io-adapter: 2.5.5
+ socket.io-parser: 4.2.4
+ transitivePeerDependencies:
+ - bufferutil
+ - supports-color
+ - utf-8-validate
+
source-map-js@1.2.1: {}
stackback@0.0.2: {}
@@ -4920,7 +5225,7 @@ snapshots:
symbol-tree@3.2.4: {}
- tailwindcss@3.4.14:
+ tailwindcss@3.4.14(ts-node@10.9.2(@types/node@22.9.0)(typescript@5.6.3)):
dependencies:
'@alloc/quick-lru': 5.2.0
arg: 5.0.2
@@ -4939,7 +5244,7 @@ snapshots:
postcss: 8.4.47
postcss-import: 15.1.0(postcss@8.4.47)
postcss-js: 4.0.1(postcss@8.4.47)
- postcss-load-config: 4.0.2(postcss@8.4.47)
+ postcss-load-config: 4.0.2(postcss@8.4.47)(ts-node@10.9.2(@types/node@22.9.0)(typescript@5.6.3))
postcss-nested: 6.2.0(postcss@8.4.47)
postcss-selector-parser: 6.1.2
resolve: 1.22.8
@@ -4993,6 +5298,25 @@ snapshots:
ts-interface-checker@0.1.13: {}
+ ts-node@10.9.2(@types/node@22.9.0)(typescript@5.6.3):
+ dependencies:
+ '@cspotcode/source-map-support': 0.8.1
+ '@tsconfig/node10': 1.0.11
+ '@tsconfig/node12': 1.0.11
+ '@tsconfig/node14': 1.0.3
+ '@tsconfig/node16': 1.0.4
+ '@types/node': 22.9.0
+ acorn: 8.14.0
+ acorn-walk: 8.3.4
+ arg: 4.1.3
+ create-require: 1.1.1
+ diff: 4.0.2
+ make-error: 1.3.6
+ typescript: 5.6.3
+ v8-compile-cache-lib: 3.0.1
+ yn: 3.1.1
+ optional: true
+
tsconfig-paths@3.15.0:
dependencies:
'@types/json5': 0.0.29
@@ -5063,6 +5387,11 @@ snapshots:
util-deprecate@1.0.2: {}
+ v8-compile-cache-lib@3.0.1:
+ optional: true
+
+ vary@1.1.2: {}
+
vite-node@2.1.4(@types/node@22.9.0):
dependencies:
cac: 6.7.14
@@ -5205,16 +5534,23 @@ snapshots:
wrappy@1.0.2: {}
+ ws@8.17.1: {}
+
ws@8.18.0: {}
xml-name-validator@5.0.0: {}
xmlchars@2.2.0: {}
+ xmlhttprequest-ssl@2.1.2: {}
+
yallist@3.1.1: {}
yaml@2.6.0: {}
+ yn@3.1.1:
+ optional: true
+
yocto-queue@0.1.0: {}
zod@3.23.8: {}
diff --git a/nextjs/src/app/course/[courseName]/settings/HolidayConfig.tsx b/nextjs/src/app/course/[courseName]/settings/HolidayConfig.tsx
index edbd6b4..1fd352a 100644
--- a/nextjs/src/app/course/[courseName]/settings/HolidayConfig.tsx
+++ b/nextjs/src/app/course/[courseName]/settings/HolidayConfig.tsx
@@ -22,19 +22,19 @@ laborDay:
- 9/1/2024`;
export const holidaysAreEqual = (
- obj1: {
+ holidays1: {
name: string;
days: string[];
}[],
- obj2: {
+ holidays2: {
name: string;
days: string[];
}[]
): boolean => {
- if (obj1.length !== obj2.length) return false;
+ if (holidays1.length !== holidays2.length) return false;
- const sortedObj1 = [...obj1].sort((a, b) => a.name.localeCompare(b.name));
- const sortedObj2 = [...obj2].sort((a, b) => a.name.localeCompare(b.name));
+ const sortedObj1 = [...holidays1].sort((a, b) => a.name.localeCompare(b.name));
+ const sortedObj2 = [...holidays2].sort((a, b) => a.name.localeCompare(b.name));
for (let i = 0; i < sortedObj1.length; i++) {
const holiday1 = sortedObj1[i];
@@ -64,7 +64,6 @@ export default function HolidayConfig() {
}
function InnerHolidayConfig() {
const [settings] = useLocalCourseSettingsQuery();
- console.log(settings.holidays);
const updateSettings = useUpdateLocalCourseSettingsMutation();
const [rawText, setRawText] = useState(holidaysToString(settings.holidays));
diff --git a/nextjs/src/app/layout.tsx b/nextjs/src/app/layout.tsx
index bd6be97..e6e59cb 100644
--- a/nextjs/src/app/layout.tsx
+++ b/nextjs/src/app/layout.tsx
@@ -9,6 +9,7 @@ import { trpcAppRouter } from "@/services/trpc/router/app";
import { createTrpcContext } from "@/services/trpc/context";
import superjson from "superjson";
import { fileStorageService } from "@/services/fileStorage/fileStorageService";
+import { ClientCacheInvalidation } from "./realtime/ClientCacheInvalidation";
export const dynamic = "force-dynamic";
export const metadata: Metadata = {
@@ -28,7 +29,10 @@ export default async function RootLayout({
- {children}
+
+
+ {children}
+
diff --git a/nextjs/src/app/realtime/ClientCacheInvalidation.tsx b/nextjs/src/app/realtime/ClientCacheInvalidation.tsx
new file mode 100644
index 0000000..accfa53
--- /dev/null
+++ b/nextjs/src/app/realtime/ClientCacheInvalidation.tsx
@@ -0,0 +1,39 @@
+"use client";
+
+import React, { useEffect } from 'react';
+import { io, Socket } from 'socket.io-client';
+
+interface ServerToClientEvents {
+ message: (data: string) => void;
+ fileChanged: (filePath: string) => void;
+}
+
+interface ClientToServerEvents {
+ sendMessage: (data: string) => void;
+}
+
+const socket: Socket = io('/');
+
+export function ClientCacheInvalidation() {
+ useEffect(() => {
+ socket.on('message', (data) => {
+ console.log('Received message:', data);
+ });
+
+ socket.on('fileChanged', (filePath) => {
+ console.log('File changed:', filePath);
+ });
+
+ socket.on('connect_error', (error) => {
+ console.error('Connection error:', error);
+ });
+
+ return () => {
+ socket.off('message');
+ socket.off('fileChanged');
+ socket.off('connect_error');
+ };
+ }, []);
+
+ return <>>;
+}
diff --git a/nextjs/src/hooks/localCourse/assignmentHooks.ts b/nextjs/src/hooks/localCourse/assignmentHooks.ts
index 807a33d..e7894f8 100644
--- a/nextjs/src/hooks/localCourse/assignmentHooks.ts
+++ b/nextjs/src/hooks/localCourse/assignmentHooks.ts
@@ -16,7 +16,6 @@ export const useAssignmentQuery = (
export const useAssignmentNamesQuery = (moduleName: string) => {
const { courseName } = useCourseContext();
- console.log("rendering all assignments query");
return trpc.assignment.getAllAssignments.useSuspenseQuery(
{
moduleName,
diff --git a/nextjs/src/websocket.js b/nextjs/src/websocket.js
new file mode 100644
index 0000000..179efde
--- /dev/null
+++ b/nextjs/src/websocket.js
@@ -0,0 +1,55 @@
+import { createServer } from "node:http";
+import next from "next";
+import { Server } from "socket.io";
+import chokidar from "chokidar";
+import path from "path";
+
+const dev = process.env.NODE_ENV !== "production";
+const hostname = "localhost";
+const port = 3000;
+
+// when using middleware `hostname` and `port` must be provided below
+const app = next({ dev, hostname, port });
+const handler = app.getRequestHandler();
+
+const folderToWatch = path.join(process.cwd(), "./storage");
+console.log("watching folder", folderToWatch);
+
+app.prepare().then(() => {
+ const httpServer = createServer(handler);
+
+ const io = new Server(httpServer);
+ const watcher = chokidar.watch(folderToWatch, { persistent: true });
+
+ io.on("connection", (socket) => {
+ console.log("websocket connection created");
+
+ // Define a change event handler
+ const changeHandler = (filePath) => {
+ const relativePath = filePath.replace(folderToWatch, "")
+ console.log(`Sending file changed websocket message: ${relativePath}`);
+
+
+
+ socket.emit("fileChanged", `File changed: ${filePath}`);
+ };
+
+ // Attach the change event handler
+ watcher.on("change", changeHandler);
+
+ // Clean up the change event handler when the client disconnects
+ socket.on("disconnect", () => {
+ console.log("Client disconnected");
+ watcher.off("change", changeHandler); // Remove the event listener
+ });
+ });
+
+ httpServer
+ .once("error", (err) => {
+ console.error(err);
+ process.exit(1);
+ })
+ .listen(port, () => {
+ console.log(`> Ready on http://${hostname}:${port}`);
+ });
+});
diff --git a/nextjs/tsconfig.json b/nextjs/tsconfig.json
index d4ae335..9251dd5 100644
--- a/nextjs/tsconfig.json
+++ b/nextjs/tsconfig.json
@@ -34,7 +34,7 @@
"**/*.tsx",
".next/types/**/*.ts",
"src/app/course/[courseName]/modules/[moduleName]/quiz/[quizName]/EditQuiztsx"
- ],
+, "next.config.mjs", "src/websocket.js" ],
"exclude": [
"node_modules"
]