diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index d9b1bede2..21276ae4d 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -124,6 +124,12 @@ importers: src: dependencies: + '@fastify/express': + specifier: ^2.3.0 + version: 2.3.0 + '@fastify/static': + specifier: ^7.0.1 + version: 7.0.1 async: specifier: ^3.2.5 version: 3.2.5 @@ -160,6 +166,12 @@ importers: fast-deep-equal: specifier: ^3.1.3 version: 3.1.3 + fastify: + specifier: ^4.26.2 + version: 4.26.2 + fastify-socket.io: + specifier: ^5.0.0 + version: 5.0.0(fastify@4.26.2) find-root: specifier: 1.1.0 version: 1.1.0 @@ -199,6 +211,12 @@ importers: openapi-backend: specifier: ^5.10.6 version: 5.10.6 + pino: + specifier: ^8.19.0 + version: 8.19.0 + pino-pretty: + specifier: ^10.3.1 + version: 10.3.1 proxy-addr: specifier: ^2.0.7 version: 2.0.7 @@ -211,6 +229,9 @@ importers: rehype-minify-whitespace: specifier: ^6.0.0 version: 6.0.0 + require-from-url: + specifier: ^3.1.3 + version: 3.1.3 resolve: specifier: 1.22.8 version: 1.22.8 @@ -785,6 +806,65 @@ packages: - supports-color dev: false + /@fastify/accept-negotiator@1.1.0: + resolution: {integrity: sha512-OIHZrb2ImZ7XG85HXOONLcJWGosv7sIvM2ifAPQVhg9Lv7qdmMBNVaai4QTdyuaqbKM5eO6sLSQOYI7wEQeCJQ==} + engines: {node: '>=14'} + dev: false + + /@fastify/ajv-compiler@3.5.0: + resolution: {integrity: sha512-ebbEtlI7dxXF5ziNdr05mOY8NnDiPB1XvAlLHctRt/Rc+C3LCOVW5imUVX+mhvUhnNzmPBHewUkOFgGlCxgdAA==} + dependencies: + ajv: 8.12.0 + ajv-formats: 2.1.1 + fast-uri: 2.3.0 + dev: false + + /@fastify/error@3.4.1: + resolution: {integrity: sha512-wWSvph+29GR783IhmvdwWnN4bUxTD01Vm5Xad4i7i1VuAOItLvbPAb69sb0IQ2N57yprvhNIwAP5B6xfKTmjmQ==} + dev: false + + /@fastify/express@2.3.0: + resolution: {integrity: sha512-jvvjlPPCfJsSHfF6tQDyARJ3+c3xXiqcxVZu6bi3xMWCWB3fl07vrjFDeaqnwqKhLZ9+m6cog5dw7gIMKEsTnQ==} + dependencies: + express: 4.18.3 + fastify-plugin: 4.5.1 + transitivePeerDependencies: + - supports-color + dev: false + + /@fastify/fast-json-stringify-compiler@4.3.0: + resolution: {integrity: sha512-aZAXGYo6m22Fk1zZzEUKBvut/CIIQe/BapEORnxiD5Qr0kPHqqI69NtEMCme74h+at72sPhbkb4ZrLd1W3KRLA==} + dependencies: + fast-json-stringify: 5.13.0 + dev: false + + /@fastify/merge-json-schemas@0.1.1: + resolution: {integrity: sha512-fERDVz7topgNjtXsJTTW1JKLy0rhuLRcquYqNR9rF7OcVpCa2OVW49ZPDIhaRRCaUuvVxI+N416xUoF76HNSXA==} + dependencies: + fast-deep-equal: 3.1.3 + dev: false + + /@fastify/send@2.1.0: + resolution: {integrity: sha512-yNYiY6sDkexoJR0D8IDy3aRP3+L4wdqCpvx5WP+VtEU58sn7USmKynBzDQex5X42Zzvw2gNzzYgP90UfWShLFA==} + dependencies: + '@lukeed/ms': 2.0.2 + escape-html: 1.0.3 + fast-decode-uri-component: 1.0.1 + http-errors: 2.0.0 + mime: 3.0.0 + dev: false + + /@fastify/static@7.0.1: + resolution: {integrity: sha512-i1p/nELMknAisNfnjo7yhfoUOdKzA+n92QaMirv2NkZrJ1Wl12v2nyTYlDwPN8XoStMBAnRK/Kx6zKmfrXUPXw==} + dependencies: + '@fastify/accept-negotiator': 1.1.0 + '@fastify/send': 2.1.0 + content-disposition: 0.5.4 + fastify-plugin: 4.5.1 + fastq: 1.17.1 + glob: 10.3.10 + dev: false + /@humanwhocodes/config-array@0.11.14: resolution: {integrity: sha512-3T8LkOmg45BV5FICb15QQMsyUSWrQ8AygVfC7ZG32zOalnqrilm018ZVCw0eapXux8FtA33q8PSRSstjee3jSg==} engines: {node: '>=10.10.0'} @@ -805,6 +885,18 @@ packages: resolution: {integrity: sha512-6EwiSjwWYP7pTckG6I5eyFANjPhmPjUX9JRLUSfNPC7FX7zK9gyZAfUEaECL6ALTpGX5AjnBq3C9XmVWPitNpw==} dev: true + /@isaacs/cliui@8.0.2: + resolution: {integrity: sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==} + engines: {node: '>=12'} + dependencies: + string-width: 5.1.2 + string-width-cjs: /string-width@4.2.3 + strip-ansi: 7.1.0 + strip-ansi-cjs: /strip-ansi@6.0.1 + wrap-ansi: 8.1.0 + wrap-ansi-cjs: /wrap-ansi@7.0.0 + dev: false + /@jridgewell/gen-mapping@0.3.3: resolution: {integrity: sha512-HLhSWOLRi875zjjMG/r+Nv0oCW8umGb0BgEhyX3dDX3egwZtB8PqLnjz3yedt8R5StBrzcg4aBpnh8UA9D1BoQ==} engines: {node: '>=6.0.0'} @@ -862,6 +954,11 @@ packages: resolution: {integrity: sha512-4JQNk+3mVzK3xh2rqd6RB4J46qUR19azEHBneZyTZM+c456qOrbbM/5xcR8huNCCcbVt7+UmizG6GuUvPvKUYg==} dev: false + /@lukeed/ms@2.0.2: + resolution: {integrity: sha512-9I2Zn6+NJLfaGoz9jN3lpwDgAYvfGeNYdbAIjJOqzs4Tpc+VU3Jqq4IofSUBKajiDS8k9fZIg18/z13mpk1bsA==} + engines: {node: '>=8'} + dev: false + /@nodelib/fs.scandir@2.1.5: resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==} engines: {node: '>= 8'} @@ -883,6 +980,13 @@ packages: fastq: 1.17.1 dev: true + /@pkgjs/parseargs@0.11.0: + resolution: {integrity: sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==} + engines: {node: '>=14'} + requiresBuild: true + dev: false + optional: true + /@playwright/test@1.42.1: resolution: {integrity: sha512-Gq9rmS54mjBL/7/MvBaNOBwbfnh7beHvS6oS4srqXFcQHpQCV1+c8JXWE8VLPyRDhgS3H8x8A7hztqI9VnwrAQ==} engines: {node: '>=16'} @@ -2179,6 +2283,17 @@ packages: - '@swc/helpers' dev: true + /abort-controller@3.0.0: + resolution: {integrity: sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==} + engines: {node: '>=6.5'} + dependencies: + event-target-shim: 5.0.1 + dev: false + + /abstract-logging@2.0.1: + resolution: {integrity: sha512-2BjRTZxTPvheOvGbBslFSYOUkr+SjPtOnrLP33f+VIWLzezQpZcqVg7ja3L4dBXmzzgwT+a029jRx5PCi3JuiA==} + dev: false + /accepts@1.3.8: resolution: {integrity: sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==} engines: {node: '>= 0.6'} @@ -2209,13 +2324,8 @@ packages: - supports-color dev: false - /ajv-formats@2.1.1(ajv@8.12.0): + /ajv-formats@2.1.1: resolution: {integrity: sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==} - peerDependencies: - ajv: ^8.0.0 - peerDependenciesMeta: - ajv: - optional: true dependencies: ajv: 8.12.0 dev: false @@ -2246,7 +2356,11 @@ packages: /ansi-regex@5.0.1: resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==} engines: {node: '>=8'} - dev: true + + /ansi-regex@6.0.1: + resolution: {integrity: sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==} + engines: {node: '>=12'} + dev: false /ansi-styles@3.2.1: resolution: {integrity: sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==} @@ -2261,6 +2375,11 @@ packages: dependencies: color-convert: 2.0.1 + /ansi-styles@6.2.1: + resolution: {integrity: sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==} + engines: {node: '>=12'} + dev: false + /anymatch@3.1.3: resolution: {integrity: sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==} engines: {node: '>= 8'} @@ -2269,6 +2388,10 @@ packages: picomatch: 2.3.1 dev: true + /archy@1.0.0: + resolution: {integrity: sha512-Xg+9RwCg/0p32teKdGMPTPnVXKD0w3DfHnFTficozsAgsvq2XenPJq/MYpzzQ/v8zrOyJn6Ds39VA4JIDwFfqw==} + dev: false + /argparse@2.0.1: resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==} @@ -2372,6 +2495,11 @@ packages: /asynckit@0.4.0: resolution: {integrity: sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==} + /atomic-sleep@1.0.0: + resolution: {integrity: sha512-kNOjDqAh7px0XWNI+4QbzoiR/nTkHAWNud2uvnJquD1/x5a7EQZMJT0AczqK0Qn67oY/TTQ1LbUKajZpp3I9tQ==} + engines: {node: '>=8.0.0'} + dev: false + /available-typed-arrays@1.0.7: resolution: {integrity: sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==} engines: {node: '>= 0.4'} @@ -2379,6 +2507,17 @@ packages: possible-typed-array-names: 1.0.0 dev: true + /avvio@8.3.0: + resolution: {integrity: sha512-VBVH0jubFr9LdFASy/vNtm5giTrnbVquWBhT0fyizuNK2rQ7e7ONU2plZQWUNqtE1EmxFEb+kbSkFRkstiaS9Q==} + dependencies: + '@fastify/error': 3.4.1 + archy: 1.0.0 + debug: 4.3.4(supports-color@8.1.1) + fastq: 1.17.1 + transitivePeerDependencies: + - supports-color + dev: false + /axios@1.6.8: resolution: {integrity: sha512-v/ZHtJDU39mDpyBoFVkETcd/uNdxrWRrg3bKpOKzXFA6Bvqopts6ALSMU3y6ijYxbw2B+wPrIv46egTzJXCLGQ==} dependencies: @@ -2396,6 +2535,10 @@ packages: /balanced-match@1.0.2: resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} + /base64-js@1.5.1: + resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==} + dev: false + /base64id@2.0.0: resolution: {integrity: sha512-lGe34o6EHj9y3Kts9R4ZYs/Gr+6N7MCaMlIFA3F1R2O5/m7K06AxfSeO5530PEERE6/WyEg3lsuyw4GHlPZHog==} engines: {node: ^4.5.0 || >= 5.9} @@ -2471,6 +2614,13 @@ packages: resolution: {integrity: sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==} dev: false + /buffer@6.0.3: + resolution: {integrity: sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==} + dependencies: + base64-js: 1.5.1 + ieee754: 1.2.1 + dev: false + /builtin-modules@3.3.0: resolution: {integrity: sha512-zhaCDicdLuWN5UbN5IMnFqNMhNfo919sH85y2/ea+5Yg9TsTkeZxpL+JLbp6cgYFS4sRLp3YV4S6yDuqVWHYOw==} engines: {node: '>=6'} @@ -2592,6 +2742,10 @@ packages: /color-name@1.1.4: resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==} + /colorette@2.0.20: + resolution: {integrity: sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==} + dev: false + /combined-stream@1.0.8: resolution: {integrity: sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==} engines: {node: '>= 0.8'} @@ -2655,6 +2809,11 @@ packages: engines: {node: '>= 0.6'} dev: false + /cookie@0.6.0: + resolution: {integrity: sha512-U71cyTamuh1CRNCfpGY6to28lxvNwPG4Guz/EVjgf3Jmzv0vlDp1atT9eS5dDjMYHucpHbWns6Lwf3BKz6svdw==} + engines: {node: '>= 0.6'} + dev: false + /cookiejar@2.1.4: resolution: {integrity: sha512-LDx6oHrK+PhzLKJU9j5S7/Y3jM/mUHvD/DeI1WQmJn652iPC5Y4TBzC9l+5OMOXlyTTA+SmVUPm0HQUwpD5Jqw==} @@ -2714,6 +2873,10 @@ packages: engines: {node: '>=4.0'} dev: false + /dateformat@4.6.3: + resolution: {integrity: sha512-2P0p0pFGzHS5EMnhdxQi7aJN+iMheud0UhG4dlE1DLAlvL8JHjJJTX/CSm4JXwV0Ka5nGk3zC5mcb5bUQUxxMA==} + dev: false + /debug@2.6.9: resolution: {integrity: sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==} peerDependencies: @@ -2855,6 +3018,10 @@ packages: tslib: 2.6.2 dev: true + /eastasianwidth@0.2.0: + resolution: {integrity: sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==} + dev: false + /ee-first@1.1.1: resolution: {integrity: sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==} dev: false @@ -2873,13 +3040,22 @@ packages: /emoji-regex@8.0.0: resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==} - dev: true + + /emoji-regex@9.2.2: + resolution: {integrity: sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==} + dev: false /encodeurl@1.0.2: resolution: {integrity: sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==} engines: {node: '>= 0.8'} dev: false + /end-of-stream@1.4.4: + resolution: {integrity: sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==} + dependencies: + once: 1.4.0 + dev: false + /engine.io-client@6.5.3: resolution: {integrity: sha512-9Z0qLB0NIisTRt1DZ/8U2k12RJn8yls/nXMZLn+/N8hANT3TcYjKFKcwbw5zFQiN4NTde3TSY9zb79e1ij6j9Q==} dependencies: @@ -3470,6 +3646,16 @@ packages: mime: 1.6.0 dev: false + /event-target-shim@5.0.1: + resolution: {integrity: sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==} + engines: {node: '>=6'} + dev: false + + /events@3.3.0: + resolution: {integrity: sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==} + engines: {node: '>=0.8.x'} + dev: false + /express-rate-limit@7.2.0(express@4.18.3): resolution: {integrity: sha512-T7nul1t4TNyfZMJ7pKRKkdeVJWa2CqB8NA1P8BwYaoDI5QSBZARv5oMS43J7b7I5P+4asjVXjb7ONuwDKucahg==} engines: {node: '>= 16'} @@ -3522,6 +3708,18 @@ packages: resolution: {integrity: sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==} dev: false + /fast-content-type-parse@1.1.0: + resolution: {integrity: sha512-fBHHqSTFLVnR61C+gltJuE5GkVQMV0S2nqUO8TJ+5Z3qAKG8vAx4FKai1s5jq/inV1+sREynIWSuQ6HgoSXpDQ==} + dev: false + + /fast-copy@3.0.2: + resolution: {integrity: sha512-dl0O9Vhju8IrcLndv2eU4ldt1ftXMqqfgN4H1cpmGV7P6jeB9FwpN9a2c8DPGE1Ys88rNUJVYDHq73CGAGOPfQ==} + dev: false + + /fast-decode-uri-component@1.0.1: + resolution: {integrity: sha512-WKgKWg5eUxvRZGwW8FvfbaH7AXSh2cL+3j5fMGzUMCxWBJ3dV3a7Wz8y2f/uQ0e3B6WmodD3oS54jTQ9HVTIIg==} + dev: false + /fast-deep-equal@3.1.3: resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==} @@ -3540,18 +3738,82 @@ packages: resolution: {integrity: sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==} dev: true + /fast-json-stringify@5.13.0: + resolution: {integrity: sha512-XjTDWKHP3GoMQUOfnjYUbqeHeEt+PvYgvBdG2fRSmYaORILbSr8xTJvZX+w1YSAP5pw2NwKrGRmQleYueZEoxw==} + dependencies: + '@fastify/merge-json-schemas': 0.1.1 + ajv: 8.12.0 + ajv-formats: 2.1.1 + fast-deep-equal: 3.1.3 + fast-uri: 2.3.0 + json-schema-ref-resolver: 1.0.1 + rfdc: 1.3.1 + dev: false + /fast-levenshtein@2.0.6: resolution: {integrity: sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==} dev: true + /fast-querystring@1.1.2: + resolution: {integrity: sha512-g6KuKWmFXc0fID8WWH0jit4g0AGBoJhCkJMb1RmbsSEUNvQ+ZC8D6CUZ+GtF8nMzSPXnhiePyyqqipzNNEnHjg==} + dependencies: + fast-decode-uri-component: 1.0.1 + dev: false + + /fast-redact@3.4.0: + resolution: {integrity: sha512-2gwPvyna0zwBdxKnng1suu/dTL5s8XEy2ZqH8mwDUwJdDkV8w5kp+JV26mupdK68HmPMbm6yjW9m7/Ys/BHEHg==} + engines: {node: '>=6'} + dev: false + /fast-safe-stringify@2.1.1: resolution: {integrity: sha512-W+KJc2dmILlPplD/H4K9l9LcAHAfPtP6BY84uVLXQ6Evcz9Lcg33Y2z1IVblT6xdY54PXYVHEv+0Wpq8Io6zkA==} + /fast-uri@2.3.0: + resolution: {integrity: sha512-eel5UKGn369gGEWOqBShmFJWfq/xSJvsgDzgLYC845GneayWvXBf0lJCBn5qTABfewy1ZDPoaR5OZCP+kssfuw==} + dev: false + + /fastify-plugin@4.5.1: + resolution: {integrity: sha512-stRHYGeuqpEZTL1Ef0Ovr2ltazUT9g844X5z/zEBFLG8RYlpDiOCIG+ATvYEp+/zmc7sN29mcIMp8gvYplYPIQ==} + dev: false + + /fastify-socket.io@5.0.0(fastify@4.26.2): + resolution: {integrity: sha512-BQKGLVSsLn9s4IXLWjwZRGTNuFbXt5bPY2qeXJUFYFtu/0BxLqcE2WMOrWUZdFWy12kuEvXC76ObsilQKeSfqg==} + requiresBuild: true + peerDependencies: + fastify: 4.x.x + dependencies: + fastify: 4.26.2 + fastify-plugin: 4.5.1 + tslib: 2.6.2 + dev: false + + /fastify@4.26.2: + resolution: {integrity: sha512-90pjTuPGrfVKtdpLeLzND5nyC4woXZN5VadiNQCicj/iJU4viNHKhsAnb7jmv1vu2IzkLXyBiCzdWuzeXgQ5Ug==} + dependencies: + '@fastify/ajv-compiler': 3.5.0 + '@fastify/error': 3.4.1 + '@fastify/fast-json-stringify-compiler': 4.3.0 + abstract-logging: 2.0.1 + avvio: 8.3.0 + fast-content-type-parse: 1.1.0 + fast-json-stringify: 5.13.0 + find-my-way: 8.1.0 + light-my-request: 5.12.0 + pino: 8.19.0 + process-warning: 3.0.0 + proxy-addr: 2.0.7 + rfdc: 1.3.1 + secure-json-parse: 2.7.0 + semver: 7.6.0 + toad-cache: 3.7.0 + transitivePeerDependencies: + - supports-color + dev: false + /fastq@1.17.1: resolution: {integrity: sha512-sRVD3lWVIXWg6By68ZN7vho9a1pQcN/WBFaAAsDDFzlJjvoGx0P8z7V1t72grFJfJhu3YPZBuu25f7Kaw2jN1w==} dependencies: reusify: 1.0.4 - dev: true /file-entry-cache@6.0.1: resolution: {integrity: sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==} @@ -3588,6 +3850,15 @@ packages: - supports-color dev: false + /find-my-way@8.1.0: + resolution: {integrity: sha512-41QwjCGcVTODUmLLqTMeoHeiozbMXYMAE1CKFiDyi9zVZ2Vjh0yz3MF0WQZoIb+cmzP/XlbFjlF2NtJmvZHznA==} + engines: {node: '>=14'} + dependencies: + fast-deep-equal: 3.1.3 + fast-querystring: 1.1.2 + safe-regex2: 2.0.0 + dev: false + /find-root@1.1.0: resolution: {integrity: sha512-NKfW6bec6GfKc0SGx1e07QZY9PE99u0Bft/0rzSD5k3sO/vwkVUpDUKVm5Gpp5Ue3YfShPFTX2070tDs5kB9Ng==} dev: false @@ -3633,6 +3904,14 @@ packages: is-callable: 1.2.7 dev: true + /foreground-child@3.1.1: + resolution: {integrity: sha512-TMKDUnIte6bfb5nWv7V/caI169OHgvwjb7V4WkeUvbQQdjr5rWKqHFiKWb/fcOwB+CzBT+qbWjvj+DVwRskpIg==} + engines: {node: '>=14'} + dependencies: + cross-spawn: 7.0.3 + signal-exit: 4.1.0 + dev: false + /form-data@4.0.0: resolution: {integrity: sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==} engines: {node: '>= 6'} @@ -3790,6 +4069,18 @@ packages: is-glob: 4.0.3 dev: true + /glob@10.3.10: + resolution: {integrity: sha512-fa46+tv1Ak0UPK1TOy/pZrIybNNt4HCv7SDzwyfiOZkvZLEbjsZkJBPtDHVshZjbecAoAGSC20MjLDG/qr679g==} + engines: {node: '>=16 || 14 >=14.17'} + hasBin: true + dependencies: + foreground-child: 3.1.1 + jackspeak: 2.3.6 + minimatch: 9.0.3 + minipass: 5.0.0 + path-scurry: 1.10.1 + dev: false + /glob@7.2.3: resolution: {integrity: sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==} dependencies: @@ -4005,6 +4296,10 @@ packages: hasBin: true dev: true + /help-me@5.0.0: + resolution: {integrity: sha512-7xgomUX6ADmcYzFik0HzAxh/73YlKR9bmFzf51CZwR+b6YtzU2m0u49hQCqV6SvlqIqsaxovfwdvbnsw3b/zpg==} + dev: false + /hexoid@1.0.0: resolution: {integrity: sha512-QFLV0taWQOZtvIRIAdBChesmogZrtuXvVWsFHZTk2SU+anspqZ2vMnoLg7IE1+Uk16N19APic1BuF8bC8c2m5g==} engines: {node: '>=8'} @@ -4083,6 +4378,10 @@ packages: safer-buffer: 2.1.2 dev: false + /ieee754@1.2.1: + resolution: {integrity: sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==} + dev: false + /ignore@5.3.1: resolution: {integrity: sha512-5Fytz/IraMjqpwfd34ke28PTVMjZjJG2MPn5t7OE4eUCUNf8BAa7b5WUS9/Qvr6mwOQS7Mk6vdsMno5he+T8Xw==} engines: {node: '>= 4'} @@ -4196,7 +4495,6 @@ packages: /is-fullwidth-code-point@3.0.0: resolution: {integrity: sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==} engines: {node: '>=8'} - dev: true /is-glob@4.0.3: resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==} @@ -4303,6 +4601,15 @@ packages: /isexe@2.0.0: resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} + /jackspeak@2.3.6: + resolution: {integrity: sha512-N3yCS/NegsOBokc8GAdM8UcmfsKiSS8cipheD/nivzr700H+nsMOxJjQnvwOcRYVuFkdH0wGUvW2WbXGmrZGbQ==} + engines: {node: '>=14'} + dependencies: + '@isaacs/cliui': 8.0.2 + optionalDependencies: + '@pkgjs/parseargs': 0.11.0 + dev: false + /jake@10.8.7: resolution: {integrity: sha512-ZDi3aP+fG/LchyBzUM804VjddnwfSfsdeYkwt8NcbKRvo4rFkjhs456iLFn3k2ZUWvNe4i48WACDbza8fhq2+w==} engines: {node: '>=10'} @@ -4314,6 +4621,11 @@ packages: minimatch: 3.1.2 dev: false + /joycon@3.1.1: + resolution: {integrity: sha512-34wB/Y7MW7bzjKRjUKTa46I2Z7eV62Rkhva+KkopW7Qvv/OSWBqvkSY7vusOPrNuZcUG3tApvdVgNB8POj3SPw==} + engines: {node: '>=10'} + dev: false + /js-cookie@3.0.5: resolution: {integrity: sha512-cEiJEAEoIbWfCZYKWhVwFuvPX1gETRYPw6LlaTKoxD3s2AkXzkCjnp6h0V77ozyqj0jakteJ4YqDJT830+lVGw==} engines: {node: '>=14'} @@ -4379,6 +4691,12 @@ packages: resolution: {integrity: sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==} dev: true + /json-schema-ref-resolver@1.0.1: + resolution: {integrity: sha512-EJAj1pgHc1hxF6vo2Z3s69fMjO1INq6eGHXZ8Z6wCQeldCuwxGK9Sxf4/cScGn3FZubCVUehfWtcDM/PLteCQw==} + dependencies: + fast-deep-equal: 3.1.3 + dev: false + /json-schema-traverse@0.4.1: resolution: {integrity: sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==} dev: true @@ -4456,6 +4774,14 @@ packages: type-check: 0.4.0 dev: true + /light-my-request@5.12.0: + resolution: {integrity: sha512-P526OX6E7aeCIfw/9UyJNsAISfcFETghysaWHQAlQYayynShT08MOj4c6fBCvTWBrHXSvqBAKDp3amUPSCQI4w==} + dependencies: + cookie: 0.6.0 + process-warning: 3.0.0 + set-cookie-parser: 2.6.0 + dev: false + /lines-and-columns@1.2.4: resolution: {integrity: sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==} dev: true @@ -4544,6 +4870,11 @@ packages: tslib: 2.6.2 dev: true + /lru-cache@10.2.0: + resolution: {integrity: sha512-2bIM8x+VAf6JT4bKAljS1qUWgMsqZRPGJS6FSahIMPVvctcNhyVp7AJu7quxOW9jwkryBReKZY5tY5JYv2n/7Q==} + engines: {node: 14 || >=16.14} + dev: false + /lru-cache@5.1.1: resolution: {integrity: sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==} dependencies: @@ -4660,6 +4991,12 @@ packages: engines: {node: '>=4.0.0'} hasBin: true + /mime@3.0.0: + resolution: {integrity: sha512-jSCU7/VB1loIWBZe14aEYHU/+1UMEHoaO7qxCOVJOw9GgH72VAWppxNcjU+x9a2k3GSIBXNKxXQFqRvvZ7vr3A==} + engines: {node: '>=10.0.0'} + hasBin: true + dev: false + /minimatch@3.1.2: resolution: {integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==} dependencies: @@ -4684,11 +5021,9 @@ packages: engines: {node: '>=16 || 14 >=14.17'} dependencies: brace-expansion: 2.0.1 - dev: true /minimist@1.2.8: resolution: {integrity: sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==} - dev: true /minipass@3.3.6: resolution: {integrity: sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==} @@ -4891,6 +5226,11 @@ packages: resolution: {integrity: sha512-9gRK4+sRWzeN6AOewNBTLXir7Zl/i3GB6Yl26gK4flxz8BXVpD3kt8amREmWNb0mxYOGDotvE5a4N+PtGGKdkg==} dev: false + /on-exit-leak-free@2.1.2: + resolution: {integrity: sha512-0eJJY6hXLGf1udHwfNftBqH+g73EU4B504nZeKpz1sYRKafAghwxEJunB2O7rDZkL4PGfsMVnTXZ2EjibbqcsA==} + engines: {node: '>=14.0.0'} + dev: false + /on-finished@2.4.1: resolution: {integrity: sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==} engines: {node: '>= 0.8'} @@ -4936,7 +5276,7 @@ packages: resolution: {integrity: sha512-xTHOmxU/VQGUgo7Cm0jhwbklOKobXby+/237EG967+3TQEYJztMgX9Q5UE2taZKwyKPUq0j11dngpGjUuxz1hQ==} dependencies: ajv: 8.12.0 - ajv-formats: 2.1.1(ajv@8.12.0) + ajv-formats: 2.1.1 lodash.merge: 4.6.2 openapi-types: 12.1.3 dev: false @@ -5019,6 +5359,14 @@ packages: /path-parse@1.0.7: resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==} + /path-scurry@1.10.1: + resolution: {integrity: sha512-MkhCqzzBEpPvxxQ71Md0b1Kk51W01lrYvlMzSUaIzNsODdd7mqhiimSZlr+VegAz5Z6Vzt9Xg2ttE//XBhH3EQ==} + engines: {node: '>=16 || 14 >=14.17'} + dependencies: + lru-cache: 10.2.0 + minipass: 5.0.0 + dev: false + /path-to-regexp@0.1.7: resolution: {integrity: sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ==} dev: false @@ -5041,6 +5389,54 @@ packages: engines: {node: '>=8.6'} dev: true + /pino-abstract-transport@1.1.0: + resolution: {integrity: sha512-lsleG3/2a/JIWUtf9Q5gUNErBqwIu1tUKTT3dUzaf5DySw9ra1wcqKjJjLX1VTY64Wk1eEOYsVGSaGfCK85ekA==} + dependencies: + readable-stream: 4.5.2 + split2: 4.2.0 + dev: false + + /pino-pretty@10.3.1: + resolution: {integrity: sha512-az8JbIYeN/1iLj2t0jR9DV48/LQ3RC6hZPpapKPkb84Q+yTidMCpgWxIT3N0flnBDilyBQ1luWNpOeJptjdp/g==} + hasBin: true + dependencies: + colorette: 2.0.20 + dateformat: 4.6.3 + fast-copy: 3.0.2 + fast-safe-stringify: 2.1.1 + help-me: 5.0.0 + joycon: 3.1.1 + minimist: 1.2.8 + on-exit-leak-free: 2.1.2 + pino-abstract-transport: 1.1.0 + pump: 3.0.0 + readable-stream: 4.5.2 + secure-json-parse: 2.7.0 + sonic-boom: 3.8.0 + strip-json-comments: 3.1.1 + dev: false + + /pino-std-serializers@6.2.2: + resolution: {integrity: sha512-cHjPPsE+vhj/tnhCy/wiMh3M3z3h/j15zHQX+S9GkTBgqJuTuJzYJ4gUyACLhDaJ7kk9ba9iRDmbH2tJU03OiA==} + dev: false + + /pino@8.19.0: + resolution: {integrity: sha512-oswmokxkav9bADfJ2ifrvfHUwad6MLp73Uat0IkQWY3iAw5xTRoznXbXksZs8oaOUMpmhVWD+PZogNzllWpJaA==} + hasBin: true + dependencies: + atomic-sleep: 1.0.0 + fast-redact: 3.4.0 + on-exit-leak-free: 2.1.2 + pino-abstract-transport: 1.1.0 + pino-std-serializers: 6.2.2 + process-warning: 3.0.0 + quick-format-unescaped: 4.0.4 + real-require: 0.2.0 + safe-stable-stringify: 2.4.3 + sonic-boom: 3.8.0 + thread-stream: 2.4.1 + dev: false + /playwright-core@1.42.1: resolution: {integrity: sha512-mxz6zclokgrke9p1vtdy/COWBH+eOZgYUVVU34C73M+4j4HLlQJHtfcqiqqxpP0o8HhMkflvfbquLX5dg6wlfA==} engines: {node: '>=16'} @@ -5076,6 +5472,15 @@ packages: engines: {node: '>= 0.8.0'} dev: true + /process-warning@3.0.0: + resolution: {integrity: sha512-mqn0kFRl0EoqhnL0GQ0veqFHyIN1yig9RHh/InzORTUiZHFRAur+aMtRkELNwGs9aNwKS6tg/An4NYBPGwvtzQ==} + dev: false + + /process@0.11.10: + resolution: {integrity: sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==} + engines: {node: '>= 0.6.0'} + dev: false + /promise@1.3.0: resolution: {integrity: sha512-R9WrbTF3EPkVtWjp7B7umQGVndpsi+rsDAfrR4xAALQpFLa/+2OriecLhawxzvii2gd9+DZFwROWDuUUaqS5yA==} dependencies: @@ -5102,6 +5507,13 @@ packages: resolution: {integrity: sha512-E/ZsdU4HLs/68gYzgGTkMicWTLPdAftJLfJFlLUAAKZGkStNU72sZjT66SnMDVOfOWY/YAoiD7Jxa9iHvngcag==} dev: false + /pump@3.0.0: + resolution: {integrity: sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==} + dependencies: + end-of-stream: 1.4.4 + once: 1.4.0 + dev: false + /punycode@2.3.1: resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==} engines: {node: '>=6'} @@ -5127,6 +5539,10 @@ packages: resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==} dev: true + /quick-format-unescaped@4.0.4: + resolution: {integrity: sha512-tYC1Q1hgyRuHgloV/YXs2w15unPVh8qfu/qCTfhTYamaw7fyhumKa2yGpdSo87vY32rIclj+4fWYQXUMs9EHvg==} + dev: false + /rambda@7.5.0: resolution: {integrity: sha512-y/M9weqWAH4iopRd7EHDEQQvpFPHj1AA3oHozE9tfITHUtTR7Z9PSlIRRG2l1GuW7sefC1cXFfIcF+cgnShdBA==} dev: true @@ -5282,6 +5698,17 @@ packages: loose-envify: 1.4.0 dev: true + /readable-stream@4.5.2: + resolution: {integrity: sha512-yjavECdqeZ3GLXNgRXgeQEdz9fvDDkNKyHnbHRFtOr7/LcfgBcmct7t/ET+HaCTqfh06OzoAxrkN/IfjJBVe+g==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + dependencies: + abort-controller: 3.0.0 + buffer: 6.0.3 + events: 3.3.0 + process: 0.11.10 + string_decoder: 1.3.0 + dev: false + /readdirp@3.6.0: resolution: {integrity: sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==} engines: {node: '>=8.10.0'} @@ -5289,6 +5716,11 @@ packages: picomatch: 2.3.1 dev: true + /real-require@0.2.0: + resolution: {integrity: sha512-57frrGM/OCTLqLOAh0mhVA9VBMHd+9U7Zb2THMGdBUoZVOtGbJzjxsYGDJ3A9AYYCP4hn6y1TVbaOfzWtm5GFg==} + engines: {node: '>= 12.13.0'} + dev: false + /regenerator-runtime@0.14.1: resolution: {integrity: sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==} dev: true @@ -5348,6 +5780,11 @@ packages: engines: {node: '>=0.10.0'} dev: false + /require-from-url@3.1.3: + resolution: {integrity: sha512-SWYVQr6rZMumhsE0MGL3caGtBNDBPQRm7JV4fsxb8Nc+LR42QkmLPP56P+Y9jncZLNrrk4SpE/Ozaf8Jo3ialA==} + engines: {node: '>=8.11.1'} + dev: false + /requires-port@1.0.0: resolution: {integrity: sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==} dev: false @@ -5368,10 +5805,14 @@ packages: path-parse: 1.0.7 supports-preserve-symlinks-flag: 1.0.0 + /ret@0.2.2: + resolution: {integrity: sha512-M0b3YWQs7R3Z917WRQy1HHA7Ba7D8hvZg6UE5mLykJxQVE2ju0IXbGlaHPPlkY+WN7wFP+wUMXmBFA0aV6vYGQ==} + engines: {node: '>=4'} + dev: false + /reusify@1.0.4: resolution: {integrity: sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==} engines: {iojs: '>=1.0.0', node: '>=0.10.0'} - dev: true /rfdc@1.3.1: resolution: {integrity: sha512-r5a3l5HzYlIC68TpmYKlxWjmOP6wiPJ1vWv2HeLhNsRZMrCkxeqxiHlQ21oXmQ4F3SiryXBHhAD7JZqvOJjFmg==} @@ -5439,6 +5880,17 @@ packages: is-regex: 1.1.4 dev: true + /safe-regex2@2.0.0: + resolution: {integrity: sha512-PaUSFsUaNNuKwkBijoAPHAK6/eM6VirvyPWlZ7BAQy4D+hCvh4B6lIG+nPdhbFfIbP+gTGBcrdsOaUs0F+ZBOQ==} + dependencies: + ret: 0.2.2 + dev: false + + /safe-stable-stringify@2.4.3: + resolution: {integrity: sha512-e2bDA2WJT0wxseVd4lsDP4+3ONX6HpMXQa1ZhFQ7SU+GjvORCmShbCMltrtIDfkYhVHrOcPtj+KhmDBdPdZD1g==} + engines: {node: '>=10'} + dev: false + /safer-buffer@2.1.2: resolution: {integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==} dev: false @@ -5456,6 +5908,10 @@ packages: loose-envify: 1.4.0 dev: true + /secure-json-parse@2.7.0: + resolution: {integrity: sha512-6aU+Rwsezw7VR8/nyvKTx8QpWH9FrcYiXXlqC4z5d5XQBDRqtbfsRjnwGyqbi3gddNtWHuEk9OANUotL26qKUw==} + dev: false + /security@1.0.0: resolution: {integrity: sha512-5qfoAgfRWS1sUn+fUJtdbbqM1BD/LoQGa+smPTDjf9OqHyuJqi6ewtbYL0+V1S1RaU6OCOCMWGZocIfz2YK4uw==} dev: false @@ -5513,7 +5969,6 @@ packages: /set-cookie-parser@2.6.0: resolution: {integrity: sha512-RVnVQxTXuerk653XfuliOxBP81Sf0+qfQE73LIYKcyMYHG94AuH0kgrQpRDuTZnSmjpysHmzxJXKNfa6PjFhyQ==} - dev: true /set-function-length@1.2.1: resolution: {integrity: sha512-j4t6ccc+VsKwYHso+kElc5neZpjtq9EnRICFZtWyBsLojhmeF/ZBd/elqm22WJh/BziDe/SBiOeAt0m2mfLD0g==} @@ -5563,6 +6018,11 @@ packages: resolution: {integrity: sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==} dev: false + /signal-exit@4.1.0: + resolution: {integrity: sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==} + engines: {node: '>=14'} + dev: false + /sinon@17.0.1: resolution: {integrity: sha512-wmwE19Lie0MLT+ZYNpDymasPHUKTaZHUH/pKEubRXIzySv9Atnlw+BUMGCzWgV7b7wO+Hw6f1TEOr0IUnmU8/g==} dependencies: @@ -5650,6 +6110,12 @@ packages: - utf-8-validate dev: false + /sonic-boom@3.8.0: + resolution: {integrity: sha512-ybz6OYOUjoQQCQ/i4LU8kaToD8ACtYP+Cj5qd2AO36bwbdewxWJ3ArmJ2cr6AvxlL2o0PqnCcPGUgkILbfkaCA==} + dependencies: + atomic-sleep: 1.0.0 + dev: false + /source-map-js@1.0.2: resolution: {integrity: sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==} engines: {node: '>=0.10.0'} @@ -5675,6 +6141,11 @@ packages: resolution: {integrity: sha512-ELtFtxc3r5we5GZfe6Fi0BFFxIi2M6BY1YEntBscKRDD3zx4JVHqx2VnTRSQu1BixCYSTH3MTjKd4esI2R7EgQ==} dev: true + /split2@4.2.0: + resolution: {integrity: sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==} + engines: {node: '>= 10.x'} + dev: false + /statuses@2.0.1: resolution: {integrity: sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==} engines: {node: '>= 0.8'} @@ -5698,7 +6169,15 @@ packages: emoji-regex: 8.0.0 is-fullwidth-code-point: 3.0.0 strip-ansi: 6.0.1 - dev: true + + /string-width@5.1.2: + resolution: {integrity: sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==} + engines: {node: '>=12'} + dependencies: + eastasianwidth: 0.2.0 + emoji-regex: 9.2.2 + strip-ansi: 7.1.0 + dev: false /string.prototype.trim@1.2.8: resolution: {integrity: sha512-lfjY4HcixfQXOfaqCvcBuOIapyaroTXhbkfJN3gcB1OtyupngWK4sEET9Knd0cXd28kTUqu/kHoV4HKSJdnjiQ==} @@ -5725,6 +6204,12 @@ packages: es-abstract: 1.22.4 dev: true + /string_decoder@1.3.0: + resolution: {integrity: sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==} + dependencies: + safe-buffer: 5.2.1 + dev: false + /stringify-entities@4.0.3: resolution: {integrity: sha512-BP9nNHMhhfcMbiuQKCqMjhDP5yBCAxsPu4pHFFzJ6Alo9dZgY4VLDPutXqIjpRiMoKdp7Av85Gr73Q5uH9k7+g==} dependencies: @@ -5737,7 +6222,13 @@ packages: engines: {node: '>=8'} dependencies: ansi-regex: 5.0.1 - dev: true + + /strip-ansi@7.1.0: + resolution: {integrity: sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==} + engines: {node: '>=12'} + dependencies: + ansi-regex: 6.0.1 + dev: false /strip-bom@3.0.0: resolution: {integrity: sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==} @@ -5747,7 +6238,6 @@ packages: /strip-json-comments@3.1.1: resolution: {integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==} engines: {node: '>=8'} - dev: true /superagent@8.1.2: resolution: {integrity: sha512-6WTxW1EB6yCxV5VFOIPQruWGHqc3yI7hEmZK6h+pyk69Lk/Ut7rLUY6W/ONF2MjBuGjvmMiIpsrVJ2vjrHlslA==} @@ -5843,6 +6333,12 @@ packages: resolution: {integrity: sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==} dev: true + /thread-stream@2.4.1: + resolution: {integrity: sha512-d/Ex2iWd1whipbT681JmTINKw0ZwOUBZm7+Gjs64DHuX34mmw8vJL2bFAaNacaW72zYiTJxSHi5abUuOi5nsfg==} + dependencies: + real-require: 0.2.0 + dev: false + /threads@1.7.0: resolution: {integrity: sha512-Mx5NBSHX3sQYR6iI9VYbgHKBLisyB+xROCBGjjWm1O9wb9vfLxdaGtmT/KCjUqMsSNW6nERzCW3T6H43LqjDZQ==} dependencies: @@ -5880,6 +6376,11 @@ packages: is-number: 7.0.0 dev: true + /toad-cache@3.7.0: + resolution: {integrity: sha512-/m8M+2BJUpoJdgAHoG+baCwBT+tf2VraSfkBgl0Y00qIWt41DJ8R5B8nsEw0I58YwF5IZH6z24/2TobDKnqSWw==} + engines: {node: '>=12'} + dev: false + /toidentifier@1.0.1: resolution: {integrity: sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==} engines: {node: '>=0.6'} @@ -5938,7 +6439,6 @@ packages: /tslib@2.6.2: resolution: {integrity: sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==} - dev: true /tsutils@3.21.0(typescript@5.4.2): resolution: {integrity: sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==} @@ -6380,7 +6880,15 @@ packages: ansi-styles: 4.3.0 string-width: 4.2.3 strip-ansi: 6.0.1 - dev: true + + /wrap-ansi@8.1.0: + resolution: {integrity: sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==} + engines: {node: '>=12'} + dependencies: + ansi-styles: 6.2.1 + string-width: 5.1.2 + strip-ansi: 7.1.0 + dev: false /wrappy@1.0.2: resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==} diff --git a/src/node/hooks/express.ts b/src/node/hooks/express.ts index 29da71ac3..093eedab2 100644 --- a/src/node/hooks/express.ts +++ b/src/node/hooks/express.ts @@ -7,7 +7,6 @@ import _ from 'underscore'; // @ts-ignore import cookieParser from 'cookie-parser'; import events from 'events'; -import express from 'express'; // @ts-ignore import expressSession from 'express-session'; import fs from 'fs'; @@ -18,7 +17,7 @@ const settings = require('../utils/Settings'); const stats = require('../stats') import util from 'util'; const webaccess = require('./express/webaccess'); - +import Fastify from 'fastify'; import SecretRotator from '../security/SecretRotator'; let secretRotator: SecretRotator|null = null; @@ -100,7 +99,21 @@ exports.createServer = async () => { exports.restartServer = async () => { await closeServer(); - const app = express(); // New syntax for express v3 + console.log('Starting Etherpad...'); + const fastify = Fastify({ + logger: { + level: 'error', + transport: { + target: 'pino-pretty', + options: { + translateTime: 'HH:MM:ss Z', + ignore: 'pid,hostname', + }, + } + } + }); + await fastify.register(require('@fastify/express')) + if (settings.ssl) { console.log('SSL -- enabled'); @@ -119,142 +132,140 @@ exports.restartServer = async () => { options.ca.push(fs.readFileSync(caFileName)); } } - - const https = require('https'); - exports.server = https.createServer(options, app); - } else { - const http = require('http'); - exports.server = http.createServer(app); } - app.use((req, res, next) => { - // res.header("X-Frame-Options", "deny"); // breaks embedded pads - if (settings.ssl) { - // we use SSL - res.header('Strict-Transport-Security', 'max-age=31536000; includeSubDomains'); - } + exports.server = fastify.server - // Stop IE going into compatability mode - // https://github.com/ether/etherpad-lite/issues/2547 - res.header('X-UA-Compatible', 'IE=Edge,chrome=1'); + fastify.use((req, res, next) => { + // res.header("X-Frame-Options", "deny"); // breaks embedded pads + if (settings.ssl) { + // we use SSL + res.header('Strict-Transport-Security', 'max-age=31536000; includeSubDomains'); + } - // Enable a strong referrer policy. Same-origin won't drop Referers when - // loading local resources, but it will drop them when loading foreign resources. - // It's still a last bastion of referrer security. External URLs should be - // already marked with rel="noreferer" and user-generated content pages are already - // marked with - // https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Referrer-Policy - // https://github.com/ether/etherpad-lite/pull/3636 - res.header('Referrer-Policy', 'same-origin'); + // Stop IE going into compatability mode + // https://github.com/ether/etherpad-lite/issues/2547 + res.header('X-UA-Compatible', 'IE=Edge,chrome=1'); - // send git version in the Server response header if exposeVersion is true. - if (settings.exposeVersion) { - res.header('Server', serverName); - } + // Enable a strong referrer policy. Same-origin won't drop Referers when + // loading local resources, but it will drop them when loading foreign resources. + // It's still a last bastion of referrer security. External URLs should be + // already marked with rel="noreferer" and user-generated content pages are already + // marked with + // https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Referrer-Policy + // https://github.com/ether/etherpad-lite/pull/3636 + res.header('Referrer-Policy', 'same-origin'); - next(); - }); + // send git version in the Server response header if exposeVersion is true. + if (settings.exposeVersion) { + res.header('Server', serverName); + } - if (settings.trustProxy) { - /* - * If 'trust proxy' === true, the client’s IP address in req.ip will be the - * left-most entry in the X-Forwarded-* header. - * - * Source: https://expressjs.com/en/guide/behind-proxies.html - */ - app.enable('trust proxy'); - } - - // Measure response time - app.use((req, res, next) => { - const stopWatch = stats.timer('httpRequests').start(); - const sendFn = res.send.bind(res); - res.send = (...args) => { stopWatch.end(); return sendFn(...args); }; - next(); - }); - - // If the log level specified in the config file is WARN or ERROR the application server never - // starts listening to requests as reported in issue #158. Not installing the log4js connect - // logger when the log level has a higher severity than INFO since it would not log at that level - // anyway. - if (!(settings.loglevel === 'WARN' && settings.loglevel === 'ERROR')) { - app.use(log4js.connectLogger(logger, { - level: log4js.levels.DEBUG.levelStr, - format: ':status, :method :url', - })); - } - - const {keyRotationInterval, sessionLifetime} = settings.cookie; - let secret = settings.sessionKey; - if (keyRotationInterval && sessionLifetime) { - secretRotator = new SecretRotator( - 'expressSessionSecrets', keyRotationInterval, sessionLifetime, settings.sessionKey); - await secretRotator.start(); - secret = secretRotator.secrets; - } - if (!secret) throw new Error('missing cookie signing secret'); - - app.use(cookieParser(secret, {})); - - sessionStore = new SessionStore(settings.cookie.sessionRefreshInterval); - exports.sessionMiddleware = expressSession({ - propagateTouch: true, - rolling: true, - secret, - store: sessionStore, - resave: false, - saveUninitialized: false, - // Set the cookie name to a javascript identifier compatible string. Makes code handling it - // cleaner :) - name: 'express_sid', - cookie: { - maxAge: sessionLifetime || null, // Convert 0 to null. - sameSite: settings.cookie.sameSite, - - // The automatic express-session mechanism for determining if the application is being served - // over ssl is similar to the one used for setting the language cookie, which check if one of - // these conditions is true: - // - // 1. we are directly serving the nodejs application over SSL, using the "ssl" options in - // settings.json - // - // 2. we are serving the nodejs application in plaintext, but we are using a reverse proxy - // that terminates SSL for us. In this case, the user has to set trustProxy = true in - // settings.json, and the information wheter the application is over SSL or not will be - // extracted from the X-Forwarded-Proto HTTP header - // - // Please note that this will not be compatible with applications being served over http and - // https at the same time. - // - // reference: https://github.com/expressjs/session/blob/v1.17.0/README.md#cookiesecure - secure: 'auto', - }, - }); - - // Give plugins an opportunity to install handlers/middleware before the express-session - // middleware. This allows plugins to avoid creating an express-session record in the database - // when it is not needed (e.g., public static content). - await hooks.aCallAll('expressPreSession', {app}); - app.use(exports.sessionMiddleware); - - app.use(webaccess.checkAccess); - - await Promise.all([ - hooks.aCallAll('expressConfigure', {app}), - hooks.aCallAll('expressCreateServer', {app, server: exports.server}), - ]); - exports.server.on('connection', (socket:Socket) => { - sockets.add(socket); - socketsEvents.emit('updated'); - socket.on('close', () => { - sockets.delete(socket); - socketsEvents.emit('updated'); + next(); }); - }); - await util.promisify(exports.server.listen).bind(exports.server)(settings.port, settings.ip); + + if (settings.trustProxy) { + /* + * If 'trust proxy' === true, the client’s IP address in req.ip will be the + * left-most entry in the X-Forwarded-* header. + * + * Source: https://expressjs.com/en/guide/behind-proxies.html + */ + fastify.enable('trust proxy'); + } + + // Measure response time + fastify.use((req, res, next) => { + const stopWatch = stats.timer('httpRequests').start(); + const sendFn = res.send.bind(res); + res.send = (...args) => { + stopWatch.end(); + return sendFn(...args); + }; + next(); + }); + + // If the log level specified in the config file is WARN or ERROR the application server never + // starts listening to requests as reported in issue #158. Not installing the log4js connect + // logger when the log level has a higher severity than INFO since it would not log at that level + // anyway. + if (!(settings.loglevel === 'WARN' && settings.loglevel === 'ERROR')) { + } + + const {keyRotationInterval, sessionLifetime} = settings.cookie; + let secret = settings.sessionKey; + if (keyRotationInterval && sessionLifetime) { + secretRotator = new SecretRotator( + 'expressSessionSecrets', keyRotationInterval, sessionLifetime, settings.sessionKey); + await secretRotator.start(); + secret = secretRotator.secrets; + } + if (!secret) throw new Error('missing cookie signing secret'); + + fastify.use(cookieParser(secret, {})); + + sessionStore = new SessionStore(settings.cookie.sessionRefreshInterval); + exports.sessionMiddleware = expressSession({ + propagateTouch: true, + rolling: true, + secret, + store: sessionStore, + resave: false, + saveUninitialized: false, + // Set the cookie name to a javascript identifier compatible string. Makes code handling it + // cleaner :) + name: 'express_sid', + cookie: { + maxAge: sessionLifetime || null, // Convert 0 to null. + sameSite: settings.cookie.sameSite, + + // The automatic express-session mechanism for determining if the application is being served + // over ssl is similar to the one used for setting the language cookie, which check if one of + // these conditions is true: + // + // 1. we are directly serving the nodejs application over SSL, using the "ssl" options in + // settings.json + // + // 2. we are serving the nodejs application in plaintext, but we are using a reverse proxy + // that terminates SSL for us. In this case, the user has to set trustProxy = true in + // settings.json, and the information wheter the application is over SSL or not will be + // extracted from the X-Forwarded-Proto HTTP header + // + // Please note that this will not be compatible with applications being served over http and + // https at the same time. + // + // reference: https://github.com/expressjs/session/blob/v1.17.0/README.md#cookiesecure + secure: 'auto', + }, + }); + + // Give plugins an opportunity to install handlers/middleware before the express-session + // middleware. This allows plugins to avoid creating an express-session record in the database + // when it is not needed (e.g., public static content). + await hooks.aCallAll('expressPreSession', {app:fastify}); + fastify.use(exports.sessionMiddleware); + + fastify.use(webaccess.checkAccess); + + await Promise.all([ + hooks.aCallAll('expressConfigure', {app: fastify}), + hooks.aCallAll('expressCreateServer', {app:fastify, server: fastify}), + ]); + exports.server.on('connection', (socket: Socket) => { + sockets.add(socket); + socketsEvents.emit('updated'); + socket.on('close', () => { + sockets.delete(socket); + socketsEvents.emit('updated'); + }); + }); + // Run the server! + await fastify.listen({ + port: settings.port, + }) startTime.setValue(Date.now()); - logger.info('HTTP server listening for connections'); -}; + logger.info('HTTP server listening for connections'); +} exports.shutdown = async (hookName:string, context: any) => { await closeServer(); diff --git a/src/node/hooks/express/importexport.ts b/src/node/hooks/express/importexport.ts index 898606e49..1cc6bcdf4 100644 --- a/src/node/hooks/express/importexport.ts +++ b/src/node/hooks/express/importexport.ts @@ -24,9 +24,8 @@ exports.expressCreateServer = (hookName:string, args:ArgsExpressType, cb:Functio }, }); - // handle export requests - args.app.use('/p/:pad/:rev?/export/:type', limiter); - args.app.get('/p/:pad/:rev?/export/:type', (req:any, res:any, next:Function) => { + + const handleImport = (req:any, res:any, next:Function) => { (async () => { const types = ['pdf', 'doc', 'txt', 'html', 'odt', 'etherpad']; // send a 404 if we don't support this filetype @@ -38,12 +37,12 @@ exports.expressCreateServer = (hookName:string, args:ArgsExpressType, cb:Functio if (settings.exportAvailable() === 'no' && ['odt', 'pdf', 'doc'].indexOf(req.params.type) !== -1) { console.error(`Impossible to export pad "${req.params.pad}" in ${req.params.type} format.` + - ' There is no converter configured'); + ' There is no converter configured'); // ACHTUNG: do not include req.params.type in res.send() because there is // no HTML escaping and it would lead to an XSS res.send('This export is not enabled at this Etherpad instance. Set the path to Abiword' + - ' or soffice (LibreOffice) in settings.json to enable this feature'); + ' or soffice (LibreOffice) in settings.json to enable this feature'); return; } @@ -68,22 +67,26 @@ exports.expressCreateServer = (hookName:string, args:ArgsExpressType, cb:Functio await exportHandler.doExport(req, res, padId, readOnlyId, req.params.type); } })().catch((err) => next(err || new Error(err))); - }); + }; + + // handle export requests + args.app.use('/p/:pad/export/:type', limiter); + args.app.use('/p/:pad/:rev/export/:type', limiter); + + args.app.get('/p/:pad/:rev/export/:type', handleImport); + args.app.get('/p/:pad/export/:type', handleImport); // handle import requests args.app.use('/p/:pad/import', limiter); - args.app.post('/p/:pad/import', (req:any, res:any, next:Function) => { - (async () => { - // @ts-ignore - const {session: {user} = {}} = req; - const {accessStatus, authorID: authorId} = await securityManager.checkAccess( - req.params.pad, req.cookies.sessionID, req.cookies.token, user); - if (accessStatus !== 'grant' || !webaccess.userCanModify(req.params.pad, req)) { - return res.status(403).send('Forbidden'); - } - await importHandler.doImport(req, res, req.params.pad, authorId); - })().catch((err) => next(err || new Error(err))); - }); + args.app.post('/p/:pad/import', async (req: any, res: any, next: Function) => { + const {session: {user} = {}} = req; + const {accessStatus, authorID: authorId} = await securityManager.checkAccess( + req.params.pad, req.cookies.sessionID, req.cookies.token, user); + if (accessStatus !== 'grant' || !webaccess.userCanModify(req.params.pad, req)) { + return res.status(403).send('Forbidden'); + } + await importHandler.doImport(req, res, req.params.pad, authorId); + }) return cb(); }; diff --git a/src/node/hooks/express/padurlsanitize.ts b/src/node/hooks/express/padurlsanitize.ts index 8679bcfe3..3e8a084f8 100644 --- a/src/node/hooks/express/padurlsanitize.ts +++ b/src/node/hooks/express/padurlsanitize.ts @@ -6,27 +6,5 @@ const padManager = require('../../db/PadManager'); exports.expressCreateServer = (hookName:string, args:ArgsExpressType, cb:Function) => { // redirects browser to the pad's sanitized url if needed. otherwise, renders the html - args.app.param('pad', (req:any, res:any, next:Function, padId:string) => { - (async () => { - // ensure the padname is valid and the url doesn't end with a / - if (!padManager.isValidPadId(padId) || /\/$/.test(req.url)) { - res.status(404).send('Such a padname is forbidden'); - return; - } - - const sanitizedPadId = await padManager.sanitizePadId(padId); - - if (sanitizedPadId === padId) { - // the pad id was fine, so just render it - next(); - } else { - // the pad id was sanitized, so we redirect to the sanitized version - const realURL = - encodeURIComponent(sanitizedPadId) + new URL(req.url, 'http://invalid.invalid').search; - res.header('Location', realURL); - res.status(302).send(`You should be redirected to ${realURL}`); - } - })().catch((err) => next(err || new Error(err))); - }); return cb(); }; diff --git a/src/node/hooks/express/socketio.ts b/src/node/hooks/express/socketio.ts index bbdec1c1c..c7f59b038 100644 --- a/src/node/hooks/express/socketio.ts +++ b/src/node/hooks/express/socketio.ts @@ -70,7 +70,7 @@ export const expressCreateServer = (hookName:string, args:ArgsExpressType, cb:Fu // there shouldn't be a browser that isn't compatible to all // transports in this list at once // e.g. XHR is disabled in IE by default, so in IE it should use jsonp-polling - io = new Server(args.server,{ + io = new Server(args.server.server,{ transports: settings.socketTransportProtocols, cookie: false, maxHttpBufferSize: settings.socketIo.maxHttpBufferSize, diff --git a/src/node/hooks/express/specialpages.ts b/src/node/hooks/express/specialpages.ts index 85a23479f..9812a682f 100644 --- a/src/node/hooks/express/specialpages.ts +++ b/src/node/hooks/express/specialpages.ts @@ -22,61 +22,59 @@ exports.expressPreSession = async (hookName:string, {app}:any) => { }); app.get('/stats', (req:any, res:any) => { - res.json(require('../../stats').toJSON()); + require('../../stats'); }); app.get('/javascript', (req:any, res:any) => { - res.send(eejs.require('ep_etherpad-lite/templates/javascript.html', {req})); + res.type('text/html').send(eejs.require('ep_etherpad-lite/templates/javascript.html', {req})) }); app.get('/robots.txt', (req:any, res:any) => { let filePath = path.join(settings.root, 'src', 'static', 'skins', settings.skinName, 'robots.txt'); - res.sendFile(filePath, (err:any) => { - // there is no custom robots.txt, send the default robots.txt which dissallows all - if (err) { - filePath = path.join(settings.root, 'src', 'static', 'robots.txt'); - res.sendFile(filePath); - } - }); + fs.stat(filePath, (err:any, stats:any) => { + if (err) { + filePath = path.join(settings.root, 'src', 'static', 'robots.txt'); + } + + const buffer = fs.readFileSync(filePath) // sync just for DEMO + res.type('text/plain').send(buffer, {req}) + + }) }); - app.get('/favicon.ico', (req:any, res:any, next:Function) => { - (async () => { - /* - If this is a url we simply redirect to that one. - */ - if (settings.favicon && settings.favicon.startsWith('http')) { - res.redirect(settings.favicon); - res.send(); - return; - } + app.get('/favicon.ico', async (req: any, res: any) => { + /* + If this is a url we simply redirect to that one. + */ + if (settings.favicon && settings.favicon.startsWith('http')) { + res.redirect(settings.favicon); + res.send(); + return; + } - - const fns = [ - ...(settings.favicon ? [path.resolve(settings.root, settings.favicon)] : []), - path.join(settings.root, 'src', 'static', 'skins', settings.skinName, 'favicon.ico'), - path.join(settings.root, 'src', 'static', 'favicon.ico'), - ]; - for (const fn of fns) { - try { - await fsp.access(fn, fs.constants.R_OK); - } catch (err) { - continue; - } - res.setHeader('Cache-Control', `public, max-age=${settings.maxAge}`); - await util.promisify(res.sendFile.bind(res))(fn); - return; + const fns = [ + ...(settings.favicon ? [path.resolve(settings.root, settings.favicon)] : []), + path.join(settings.root, 'src', 'static', 'skins', settings.skinName, 'favicon.ico'), + path.join(settings.root, 'src', 'static', 'favicon.ico'), + ]; + for (const fn of fns) { + try { + await fsp.access(fn, fs.constants.R_OK); + } catch (err) { + continue; } - next(); - })().catch((err) => next(err || new Error(err))); + res.header('Cache-Control', `public, max-age=${settings.maxAge}`); + return fs.readFileSync(fn) + } }); }; exports.expressCreateServer = (hookName:string, args:any, cb:Function) => { // serve index.html under / args.app.get('/', (req:any, res:any) => { - res.send(eejs.require('ep_etherpad-lite/templates/index.html', {req})); + res.type('text/html').send(eejs.require('ep_etherpad-lite/templates/index.html', {req})) + //res.send(eejs.require('ep_etherpad-lite/templates/index.html', {req})); }); // serve pad.html under /p @@ -91,11 +89,11 @@ exports.expressCreateServer = (hookName:string, args:any, cb:Function) => { // can be removed when require-kernel is dropped res.header('Feature-Policy', 'sync-xhr \'self\''); - res.send(eejs.require('ep_etherpad-lite/templates/pad.html', { + res.type('text/html').send(eejs.require('ep_etherpad-lite/templates/pad.html', { req, toolbar, isReadOnly, - })); + }), {req}) }); // serve timeslider.html under /p/$padname/timeslider @@ -103,11 +101,8 @@ exports.expressCreateServer = (hookName:string, args:any, cb:Function) => { hooks.callAll('padInitToolbar', { toolbar, }); - - res.send(eejs.require('ep_etherpad-lite/templates/timeslider.html', { - req, - toolbar, - })); + res.type('text/html').send(eejs.require('ep_etherpad-lite/templates/pad.html', + {req, toolbar})); }); // The client occasionally polls this endpoint to get an updated expiration for the express_sid diff --git a/src/node/hooks/express/static.ts b/src/node/hooks/express/static.ts index 1d9ba01e9..fc581ef46 100644 --- a/src/node/hooks/express/static.ts +++ b/src/node/hooks/express/static.ts @@ -36,12 +36,40 @@ exports.expressPreSession = async (hookName:string, {app}:any) => { // Cache both minified and static. const assetCache = new CachingMiddleware(); // Cache static assets - app.all(/\/js\/(.*)/, assetCache.handle.bind(assetCache)); - app.all(/\/css\/(.*)/, assetCache.handle.bind(assetCache)); + + + app.get('/static/js/require-kernel.js', (req:any, res:any) => { + res.header('Content-Type', 'application/javascript; charset=utf-8'); + res.header('Cache-Control', `public, max-age=${settings.maxAge}`); + const file = fs.readFile(path.join(settings.root, 'src/static/js/require-kernel.js'), 'utf8'); + res.send(); + }) + + app.route({ + method: ['GET', 'POST', 'PUT', 'DELETE', 'PATCH', 'HEAD', 'OPTIONS'], + url: '/js/*', + handler: assetCache.handle.bind(assetCache) + }); + + app.route({ + method: ['GET', 'POST', 'PUT', 'DELETE', 'PATCH', 'HEAD', 'OPTIONS'], + url: '/css/*', + handler: assetCache.handle.bind(assetCache) + }); + + /*app.register(require('@fastify/static'), { + root: path.join(settings.root, 'src', 'static'), + prefix: '/static/', // optional: default '/' + constraints: {} // optional: default {} + })*/ // Minify will serve static files compressed (minify enabled). It also has // file-specific hacks for ace/require-kernel/etc. - app.all('/static/:filename(*)', minify.minify); + app.route({ + method: ['GET', 'POST', 'PUT', 'DELETE', 'PATCH', 'HEAD', 'OPTIONS'], + url: '/static/*', + handler: minify.minify + }); // Setup middleware that will package JavaScript files served by minify for // CommonJS loader on the client-side. @@ -73,9 +101,9 @@ exports.expressPreSession = async (hookName:string, {app}:any) => { // @ts-ignore delete clientPlugins[name].package; } - res.setHeader('Content-Type', 'application/json; charset=utf-8'); - res.setHeader('Cache-Control', `public, max-age=${settings.maxAge}`); - res.write(JSON.stringify({plugins: clientPlugins, parts: clientParts})); - res.end(); + + res.header('Content-Type', 'application/json; charset=utf-8') + res.header('Cache-Control', `public, max-age=${settings.maxAge}`) + return {plugins: clientPlugins, parts: clientParts}; }); }; diff --git a/src/node/hooks/i18n.ts b/src/node/hooks/i18n.ts index 500f1f887..74a6db99e 100644 --- a/src/node/hooks/i18n.ts +++ b/src/node/hooks/i18n.ts @@ -133,8 +133,8 @@ exports.expressPreSession = async (hookName:string, {app}:any) => { // works with /locale/en and /locale/en.json requests const locale = req.params.locale.split('.')[0]; if (Object.prototype.hasOwnProperty.call(exports.availableLangs, locale)) { - res.setHeader('Cache-Control', `public, max-age=${settings.maxAge}`); - res.setHeader('Content-Type', 'application/json; charset=utf-8'); + res.header('Cache-Control', `public, max-age=${settings.maxAge}`); + res.header('Content-Type', 'application/json; charset=utf-8'); res.send(`{"${locale}":${JSON.stringify(locales[locale])}}`); } else { res.status(404).send('Language not available'); @@ -142,8 +142,8 @@ exports.expressPreSession = async (hookName:string, {app}:any) => { }); app.get('/locales.json', (req: any, res:any) => { - res.setHeader('Cache-Control', `public, max-age=${settings.maxAge}`); - res.setHeader('Content-Type', 'application/json; charset=utf-8'); + res.header('Cache-Control', `public, max-age=${settings.maxAge}`); + res.header('Content-Type', 'application/json; charset=utf-8'); res.send(localeIndex); }); }; diff --git a/src/node/utils/ImportHtml.ts b/src/node/utils/ImportHtml.ts index 57d9cbe4f..7d76b47c0 100644 --- a/src/node/utils/ImportHtml.ts +++ b/src/node/utils/ImportHtml.ts @@ -15,13 +15,11 @@ * limitations under the License. */ -import log4js from 'log4js'; const Changeset = require('../../static/js/Changeset'); const contentcollector = require('../../static/js/contentcollector'); import jsdom from 'jsdom'; import {PadType} from "../types/PadType"; -const apiLogger = log4js.getLogger('ImportHtml'); let processor:any; exports.setPadHTML = async (pad: PadType, html:string, authorId = '') => { @@ -38,8 +36,8 @@ exports.setPadHTML = async (pad: PadType, html:string, authorId = '') => { // below the last line of an import document.body.appendChild(document.createElement('p')); - apiLogger.debug('html:'); - apiLogger.debug(html); + console.debug('html:'); + console.debug(html); // Convert a dom tree into a list of lines and attribute liens // using the content collector object @@ -48,24 +46,24 @@ exports.setPadHTML = async (pad: PadType, html:string, authorId = '') => { // we use a try here because if the HTML is bad it will blow up cc.collectContent(document.body); } catch (err: any) { - apiLogger.warn(`Error processing HTML: ${err.stack || err}`); + console.warn(`Error processing HTML: ${err.stack || err}`); throw err; } const result = cc.finish(); - apiLogger.debug('Lines:'); + console.debug('Lines:'); let i; for (i = 0; i < result.lines.length; i++) { - apiLogger.debug(`Line ${i + 1} text: ${result.lines[i]}`); - apiLogger.debug(`Line ${i + 1} attributes: ${result.lineAttribs[i]}`); + console.debug(`Line ${i + 1} text: ${result.lines[i]}`); + console.debug(`Line ${i + 1} attributes: ${result.lineAttribs[i]}`); } // Get the new plain text and its attributes const newText = result.lines.join('\n'); - apiLogger.debug('newText:'); - apiLogger.debug(newText); + console.debug('newText:'); + console.debug(newText); const newAttribs = `${result.lineAttribs.join('|1+1')}|1+1`; // create a new changeset with a helper builder object @@ -88,7 +86,7 @@ exports.setPadHTML = async (pad: PadType, html:string, authorId = '') => { // the changeset is ready! const theChangeset = builder.toString(); - apiLogger.debug(`The changeset: ${theChangeset}`); + console.debug(`The changeset: ${theChangeset}`); await pad.setText('\n', authorId); await pad.appendRevision(theChangeset, authorId); }; diff --git a/src/node/utils/Minify.js b/src/node/utils/Minify.js index 2e8a2d960..18f07c204 100644 --- a/src/node/utils/Minify.js +++ b/src/node/utils/Minify.js @@ -126,9 +126,7 @@ const minify = async (req, res) => { filename = sanitizePathname(filename); } catch (err) { logger.error(`sanitization of pathname "${filename}" failed: ${err.stack || err}`); - res.writeHead(404, {}); - res.end(); - return; + return res.status(404).send(); } // Backward compatibility for plugins that require() files from old paths. @@ -174,12 +172,12 @@ const minify = async (req, res) => { const [date, exists] = await statFile(filename, 3); if (date) { date.setMilliseconds(0); - res.setHeader('last-modified', date.toUTCString()); - res.setHeader('date', (new Date()).toUTCString()); + res.header('last-modified', date.toUTCString()); + res.header('date', (new Date()).toUTCString()); if (settings.maxAge !== undefined) { const expiresDate = new Date(Date.now() + settings.maxAge * 1000); - res.setHeader('expires', expiresDate.toUTCString()); - res.setHeader('cache-control', `max-age=${settings.maxAge}`); + res.header('expires', expiresDate.toUTCString()); + res.header('cache-control', `max-age=${settings.maxAge}`); } } @@ -317,7 +315,7 @@ const getFile = async (filename) => { return await fs.readFile(path.resolve(ROOT_DIR, filename)); }; -exports.minify = (req, res, next) => minify(req, res).catch((err) => next(err || new Error(err))); +exports.minify = (req, res, next) => minify(req, res); exports.requestURIs = requestURIs; diff --git a/src/node/utils/Settings.ts b/src/node/utils/Settings.ts index 8f1e058ea..43f9920e6 100644 --- a/src/node/utils/Settings.ts +++ b/src/node/utils/Settings.ts @@ -28,7 +28,6 @@ */ const absolutePaths = require('./AbsolutePaths'); -const deepEqual = require('fast-deep-equal/es6'); const fs = require('fs'); const os = require('os'); const path = require('path'); diff --git a/src/node/utils/caching_middleware.ts b/src/node/utils/caching_middleware.ts index 74712871c..e82e64048 100644 --- a/src/node/utils/caching_middleware.ts +++ b/src/node/utils/caching_middleware.ts @@ -157,14 +157,14 @@ export default class CachingMiddleware { } const _headers:Headers = {}; - oldRes.setHeader = res.setHeader; - res.setHeader = (key: string, value: string) => { + oldRes.header = res.header; + res.header = (key: string, value: string) => { // Don't set cookies, see issue #707 if (key.toLowerCase() === 'set-cookie') return; _headers[key.toLowerCase()] = value; // @ts-ignore - oldRes.setHeader.call(res, key, value); + oldRes.header.call(res, key, value); }; oldRes.writeHead = res.writeHead; @@ -175,7 +175,7 @@ export default class CachingMiddleware { let buffer = ''; Object.keys(headers || {}).forEach((key) => { - res.setHeader(key, headers[key]); + res.header(key, headers[key]); }); headers = _headers; diff --git a/src/node/utils/sanitizePathname.ts b/src/node/utils/sanitizePathname.ts index 2932b913d..e7d2cd417 100644 --- a/src/node/utils/sanitizePathname.ts +++ b/src/node/utils/sanitizePathname.ts @@ -1,10 +1,11 @@ 'use strict'; -const path = require('path'); +import path from 'path'; // Normalizes p and ensures that it is a relative path that does not reach outside. See // https://nvd.nist.gov/vuln/detail/CVE-2015-3297 for additional context. module.exports = (p: string, pathApi = path) => { + console.log('p:', p); // The documentation for path.normalize() says that it resolves '..' and '.' segments. The word // "resolve" implies that it examines the filesystem to resolve symbolic links, so 'a/../b' might // not be the same thing as 'b'. Most path normalization functions from other libraries (e.g., diff --git a/src/package.json b/src/package.json index 339b97d7f..7811fcc4d 100644 --- a/src/package.json +++ b/src/package.json @@ -30,6 +30,8 @@ } ], "dependencies": { + "@fastify/express": "^2.3.0", + "@fastify/static": "^7.0.1", "async": "^3.2.5", "axios": "^1.6.8", "clean-css": "^5.3.3", @@ -42,6 +44,8 @@ "express-rate-limit": "^7.2.0", "express-session": "npm:@etherpad/express-session@^1.18.2", "fast-deep-equal": "^3.1.3", + "fastify": "^4.26.2", + "fastify-socket.io": "^5.0.0", "find-root": "1.1.0", "formidable": "^3.5.1", "http-errors": "^2.0.0", @@ -55,10 +59,13 @@ "measured-core": "^2.0.0", "mime-types": "^2.1.35", "openapi-backend": "^5.10.6", + "pino": "^8.19.0", + "pino-pretty": "^10.3.1", "proxy-addr": "^2.0.7", "rate-limiter-flexible": "^5.0.0", "rehype": "^13.0.1", "rehype-minify-whitespace": "^6.0.0", + "require-from-url": "^3.1.3", "resolve": "1.22.8", "security": "1.0.0", "semver": "^7.6.0", @@ -79,15 +86,16 @@ "etherpad-lite": "node/server.ts" }, "devDependencies": { + "@playwright/test": "^1.42.1", "@types/async": "^3.2.24", "@types/express": "^4.17.21", "@types/http-errors": "^2.0.4", "@types/jsdom": "^21.1.6", "@types/mocha": "^10.0.6", "@types/node": "^20.11.28", + "@types/semver": "^7.5.8", "@types/sinon": "^17.0.3", "@types/supertest": "^6.0.2", - "@types/semver": "^7.5.8", "@types/underscore": "^1.11.15", "eslint": "^8.57.0", "eslint-config-etherpad": "^3.0.22", @@ -96,7 +104,6 @@ "mocha-froth": "^0.2.10", "nodeify": "^1.0.1", "openapi-schema-validation": "^0.4.2", - "@playwright/test": "^1.42.1", "set-cookie-parser": "^2.6.0", "sinon": "^17.0.1", "split-grid": "^1.0.11", diff --git a/src/static/js/pad.js b/src/static/js/pad.js index df409372a..b9f08ba6a 100644 --- a/src/static/js/pad.js +++ b/src/static/js/pad.js @@ -23,12 +23,13 @@ */ let socket; +const requireFromUrl = require('require-from-url/sync'); // These jQuery things should create local references, but for now `require()` // assigns to the global `$` and augments it with plugins. -require('./vendors/jquery'); -require('./vendors/farbtastic'); -require('./vendors/gritter'); +requireFromUrl('./vendors/jquery'); +requireFromUrl('./vendors/farbtastic'); +requireFromUrl('./vendors/gritter'); const Cookies = require('./pad_utils').Cookies; const chat = require('./chat').chat; diff --git a/src/static/js/pluginfw/installer.ts b/src/static/js/pluginfw/installer.ts index 973bdd56f..242e3e525 100644 --- a/src/static/js/pluginfw/installer.ts +++ b/src/static/js/pluginfw/installer.ts @@ -1,6 +1,5 @@ 'use strict'; -import log4js from "log4js"; import axios, {AxiosResponse} from "axios"; import {PackageData, PackageInfo} from "../../../node/types/PackageInfo"; @@ -17,7 +16,6 @@ const settings = require('../../../node/utils/Settings'); import {LinkInstaller} from "./LinkInstaller"; const {findEtherpadRoot} = require('../../../node/utils/AbsolutePaths'); -const logger = log4js.getLogger('plugins'); export const pluginInstallPath = path.join(settings.root, 'src','plugin_packages'); export const node_modules = path.join(findEtherpadRoot(),'src', 'node_modules'); @@ -51,7 +49,7 @@ const wrapTaskCb = (cb:Function|null) => { }; const migratePluginsFromNodeModules = async () => { - logger.info('start migration of plugins in node_modules'); + console.info('start migration of plugins in node_modules'); // Notes: // * Do not pass `--prod` otherwise `npm ls` will fail if there is no `package.json`. // * The `--no-production` flag is required (or the `NODE_ENV` environment variable must be @@ -75,7 +73,7 @@ const migratePluginsFromNodeModules = async () => { }; export const checkForMigration = async () => { - logger.info('check installed plugins for migration'); + console.info('check installed plugins for migration'); // Initialize linkInstaller await linkInstaller.init() @@ -101,15 +99,15 @@ export const checkForMigration = async () => { const moduleName = path.basename(file); try { await fs.access(path.join(node_modules, moduleName), fs.constants.F_OK); - logger.debug(`plugin ${moduleName} already exists in node_modules`); + console.debug(`plugin ${moduleName} already exists in node_modules`); } catch (err) { // Create symlink to node_modules - logger.debug(`create symlink for ${file} to ${path.join(node_modules,moduleName)}`) + console.debug(`create symlink for ${file} to ${path.join(node_modules,moduleName)}`) await fs.symlink(path.join(pluginInstallPath,file), path.join(node_modules,moduleName), 'dir') } } }).catch(()=>{ - logger.debug('plugin directory does not exist'); + console.debug('plugin directory does not exist'); }) const fileContent = await fs.readFile(installedPluginsPath); const installedPlugins = JSON.parse(fileContent.toString()); @@ -137,19 +135,19 @@ const persistInstalledPlugins = async () => { export const uninstall = async (pluginName: string, cb:Function|null = null) => { cb = wrapTaskCb(cb); - logger.info(`Uninstalling plugin ${pluginName}...`); + console.info(`Uninstalling plugin ${pluginName}...`); await linkInstaller.uninstallPlugin(pluginName); - logger.info(`Successfully uninstalled plugin ${pluginName}`); + console.info(`Successfully uninstalled plugin ${pluginName}`); await hooks.aCallAll('pluginUninstall', {pluginName}); cb(null); }; export const install = async (pluginName: string, cb:Function|null = null) => { cb = wrapTaskCb(cb); - logger.info(`Installing plugin ${pluginName}...`); + console.info(`Installing plugin ${pluginName}...`); await linkInstaller.installPlugin(pluginName); - logger.info(`Successfully installed plugin ${pluginName}`); + console.info(`Successfully installed plugin ${pluginName}`); await hooks.aCallAll('pluginInstall', {pluginName}); cb(null); }; @@ -195,7 +193,7 @@ export const search = (searchTerm: string, maxCacheAge: number) => getAvailableP !~results[pluginName].description.toLowerCase().indexOf(searchTerm)) ) { if (typeof results[pluginName].description === 'undefined') { - logger.debug(`plugin without Description: ${results[pluginName].name}`); + console.debug(`plugin without Description: ${results[pluginName].name}`); } continue;