diff --git a/.github/workflows/impress-frontend.yml b/.github/workflows/impress-frontend.yml index 60c35696..8e0ac0b6 100644 --- a/.github/workflows/impress-frontend.yml +++ b/.github/workflows/impress-frontend.yml @@ -19,7 +19,7 @@ jobs: - name: Setup Node.js uses: actions/setup-node@v4 with: - node-version: "18.x" + node-version: "20.x" - name: Restore the frontend cache uses: actions/cache@v4 @@ -46,6 +46,11 @@ jobs: - name: Checkout repository uses: actions/checkout@v4 + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version: "20.x" + - name: Restore the frontend cache uses: actions/cache@v4 id: front-node_modules @@ -54,7 +59,7 @@ jobs: key: front-node_modules-${{ hashFiles('src/frontend/**/yarn.lock') }} - name: Test App - run: cd src/frontend/ && yarn app:test + run: cd src/frontend/ && yarn test lint-front: runs-on: ubuntu-latest diff --git a/src/frontend/package.json b/src/frontend/package.json index 1dddbd3f..680d355a 100644 --- a/src/frontend/package.json +++ b/src/frontend/package.json @@ -13,16 +13,19 @@ "APP_IMPRESS": "yarn workspace app-impress", "APP_E2E": "yarn workspace app-e2e", "I18N": "yarn workspace packages-i18n", + "COLLABORATION_SERVER": "yarn workspace server-y-provider", "app:dev": "yarn APP_IMPRESS run dev", "app:start": "yarn APP_IMPRESS run start", "app:build": "yarn APP_IMPRESS run build", "app:test": "yarn APP_IMPRESS run test", "ci:build": "yarn APP_IMPRESS run build:ci", "e2e:test": "yarn APP_E2E run test", - "lint": "yarn APP_IMPRESS run lint && yarn APP_E2E run lint && yarn workspace eslint-config-impress run lint && yarn I18N run lint", + "lint": "yarn APP_IMPRESS run lint && yarn APP_E2E run lint && yarn workspace eslint-config-impress run lint && yarn I18N run lint && yarn COLLABORATION_SERVER run lint", "i18n:extract": "yarn I18N run extract-translation", "i18n:deploy": "yarn I18N run format-deploy && yarn APP_IMPRESS prettier", - "i18n:test": "yarn I18N run test" + "i18n:test": "yarn I18N run test", + "test": "yarn server:test && yarn app:test", + "server:test": "yarn COLLABORATION_SERVER run test" }, "resolutions": { "@blocknote/core": "0.20.0", diff --git a/src/frontend/servers/y-provider/.eslintrc.cjs b/src/frontend/servers/y-provider/.eslintrc.cjs index 41f13b7c..33a3b049 100644 --- a/src/frontend/servers/y-provider/.eslintrc.cjs +++ b/src/frontend/servers/y-provider/.eslintrc.cjs @@ -1,6 +1,6 @@ module.exports = { root: true, - extends: ['impress/next'], + extends: ['impress/jest', 'impress/next'], parserOptions: { tsconfigRootDir: __dirname, project: ['./tsconfig.json'], diff --git a/src/frontend/servers/y-provider/__tests__/server.test.ts b/src/frontend/servers/y-provider/__tests__/server.test.ts new file mode 100644 index 00000000..523a7e25 --- /dev/null +++ b/src/frontend/servers/y-provider/__tests__/server.test.ts @@ -0,0 +1,234 @@ +import { + HocuspocusProvider, + HocuspocusProviderWebsocket, +} from '@hocuspocus/provider'; +import request from 'supertest'; +import WebSocket from 'ws'; + +const port = 5555; +const portWS = 6666; +const origin = 'http://localhost:3000'; + +jest.mock('../src/env', () => { + return { + PORT: port, + COLLABORATION_SERVER_ORIGIN: origin, + COLLABORATION_SERVER_SECRET: 'test-secret-api-key', + }; +}); + +console.error = jest.fn(); + +import { promiseDone } from '../src/helpers'; +import { hocuspocusServer, initServer } from '../src/server'; // Adjust the path to your server file + +const { app, server } = initServer(); + +describe('Server Tests', () => { + beforeAll(async () => { + await hocuspocusServer.configure({ port: portWS }).listen(); + }); + + afterAll(() => { + server.close(); + void hocuspocusServer.destroy(); + }); + + test('Ping Pong', async () => { + const response = await request(app as any).get('/ping'); + + expect(response.status).toBe(200); + expect(response.body.message).toBe('pong'); + }); + + test('POST /collaboration/api/reset-connections?room=[ROOM_ID] invalid origin', async () => { + const response = await request(app as any) + .post('/collaboration/api/reset-connections/?room=test-room') + .set('Origin', 'http://invalid-origin.com') + .send({ document_id: 'test-document' }); + + expect(response.status).toBe(403); + expect(response.body.error).toBe('CORS policy violation: Invalid Origin'); + }); + + test('POST /collaboration/api/reset-connections?room=[ROOM_ID] with incorrect API key should return 403', async () => { + const response = await request(app as any) + .post('/collaboration/api/reset-connections/?room=test-room') + .set('Origin', origin) + .set('Authorization', 'wrong-api-key'); + + expect(response.status).toBe(403); + expect(response.body.error).toBe('Forbidden: Invalid API Key'); + }); + + test('POST /collaboration/api/reset-connections?room=[ROOM_ID] failed if room not indicated', async () => { + const response = await request(app as any) + .post('/collaboration/api/reset-connections/') + .set('Origin', origin) + .set('Authorization', 'test-secret-api-key') + .send({ document_id: 'test-document' }); + + expect(response.status).toBe(400); + expect(response.body.error).toBe('Room name not provided'); + }); + + test('POST /collaboration/api/reset-connections?room=[ROOM_ID] with correct API key should reset connections', async () => { + // eslint-disable-next-line jest/unbound-method + const { closeConnections } = hocuspocusServer; + const mockHandleConnection = jest.fn(); + (hocuspocusServer.closeConnections as jest.Mock) = mockHandleConnection; + + const response = await request(app as any) + .post('/collaboration/api/reset-connections?room=test-room') + .set('Origin', origin) + .set('Authorization', 'test-secret-api-key'); + + expect(response.status).toBe(200); + expect(response.body.message).toBe('Connections reset'); + + expect(mockHandleConnection).toHaveBeenCalled(); + mockHandleConnection.mockClear(); + hocuspocusServer.closeConnections = closeConnections; + }); + + ['/collaboration/api/anything/', '/', '/anything'].forEach((path) => { + test(`"${path}" endpoint should be forbidden`, async () => { + const response = await request(app as any).post(path); + + expect(response.status).toBe(403); + expect(response.body.error).toBe('Forbidden'); + }); + }); + + test('WebSocket connection with correct API key can connect', () => { + const { promise, done } = promiseDone(); + + // eslint-disable-next-line jest/unbound-method + const { handleConnection } = hocuspocusServer; + const mockHandleConnection = jest.fn(); + (hocuspocusServer.handleConnection as jest.Mock) = mockHandleConnection; + + const clientWS = new WebSocket( + `ws://localhost:${port}/collaboration/ws/?room=test-room`, + { + headers: { + authorization: 'test-secret-api-key', + Origin: origin, + }, + }, + ); + + clientWS.on('open', () => { + expect(mockHandleConnection).toHaveBeenCalled(); + clientWS.close(); + mockHandleConnection.mockClear(); + hocuspocusServer.handleConnection = handleConnection; + done(); + }); + + return promise; + }); + + test('WebSocket connection with bad origin should be closed', () => { + const { promise, done } = promiseDone(); + + const ws = new WebSocket( + `ws://localhost:${port}/collaboration/ws/?room=test-room`, + { + headers: { + Origin: 'http://bad-origin.com', + }, + }, + ); + + ws.onclose = () => { + expect(ws.readyState).toBe(ws.CLOSED); + done(); + }; + + return promise; + }); + + test('WebSocket connection with incorrect API key should be closed', () => { + const { promise, done } = promiseDone(); + const ws = new WebSocket( + `ws://localhost:${port}/collaboration/ws/?room=test-room`, + { + headers: { + Authorization: 'wrong-api-key', + Origin: origin, + }, + }, + ); + + ws.onclose = () => { + expect(ws.readyState).toBe(ws.CLOSED); + done(); + }; + + return promise; + }); + + test('WebSocket connection not allowed if room not matching provider name', () => { + const { promise, done } = promiseDone(); + + const wsHocus = new HocuspocusProviderWebsocket({ + url: `ws://localhost:${portWS}/?room=my-test`, + WebSocketPolyfill: WebSocket, + maxAttempts: 1, + quiet: true, + }); + + const provider = new HocuspocusProvider({ + websocketProvider: wsHocus, + name: 'hocuspocus-test', + broadcast: false, + quiet: true, + preserveConnection: false, + onClose: (data) => { + wsHocus.stopConnectionAttempt(); + expect(data.event.reason).toBe('Forbidden'); + wsHocus.webSocket?.close(); + wsHocus.disconnect(); + provider.destroy(); + wsHocus.destroy(); + done(); + }, + }); + + return promise; + }); + + test('WebSocket connection read-only', () => { + const { promise, done } = promiseDone(); + + const wsHocus = new HocuspocusProviderWebsocket({ + url: `ws://localhost:${portWS}/?room=hocuspocus-test`, + WebSocketPolyfill: WebSocket, + }); + + const provider = new HocuspocusProvider({ + websocketProvider: wsHocus, + name: 'hocuspocus-test', + broadcast: false, + quiet: true, + onConnect: () => { + void hocuspocusServer + .openDirectConnection('hocuspocus-test') + .then((connection) => { + connection.document?.getConnections().forEach((connection) => { + expect(connection.readOnly).toBe(true); + }); + + void connection.disconnect(); + }); + + provider.destroy(); + wsHocus.destroy(); + done(); + }, + }); + + return promise; + }); +}); diff --git a/src/frontend/servers/y-provider/jest.config.js b/src/frontend/servers/y-provider/jest.config.js new file mode 100644 index 00000000..e964e0b0 --- /dev/null +++ b/src/frontend/servers/y-provider/jest.config.js @@ -0,0 +1,11 @@ +var config = { + rootDir: './__tests__', + testEnvironment: 'node', + transform: { + '^.+\\.(ts)$': 'ts-jest', + }, + moduleNameMapper: { + '^@/(.*)$': '/../src/$1', + }, +}; +export default config; diff --git a/src/frontend/servers/y-provider/package.json b/src/frontend/servers/y-provider/package.json index d043d702..08586eb7 100644 --- a/src/frontend/servers/y-provider/package.json +++ b/src/frontend/servers/y-provider/package.json @@ -6,10 +6,11 @@ "license": "MIT", "type": "module", "scripts": { - "build": "tsc -p ./src", + "build": "tsc -p tsconfig.build.json && tsc-alias", "dev": "nodemon --config nodemon.json", - "start": "node ./dist/server.js", - "lint": "eslint . --ext .ts" + "start": "node ./dist/start-server.js", + "lint": "eslint . --ext .ts", + "test": "jest" }, "engines": { "node": ">=18" @@ -21,13 +22,21 @@ "y-protocols": "1.0.6" }, "devDependencies": { + "@hocuspocus/provider": "2.14.0", "@types/express": "5.0.0", "@types/express-ws": "3.0.5", + "@types/jest": "29.5.14", "@types/node": "*", + "@types/supertest": "6.0.2", + "@types/ws": "8.5.13", "eslint-config-impress": "*", + "jest": "29.7.0", "nodemon": "3.1.7", + "supertest": "7.0.0", "ts-jest": "29.2.5", "ts-node": "10.9.2", - "typescript": "*" + "tsc-alias": "1.8.10", + "typescript": "*", + "ws": "8.18.0" } } diff --git a/src/frontend/servers/y-provider/src/helpers.ts b/src/frontend/servers/y-provider/src/helpers.ts new file mode 100644 index 00000000..c23b8368 --- /dev/null +++ b/src/frontend/servers/y-provider/src/helpers.ts @@ -0,0 +1,8 @@ +export const promiseDone = () => { + let done: (value: void | PromiseLike) => void = () => {}; + const promise = new Promise((resolve) => { + done = resolve; + }); + + return { done, promise }; +}; diff --git a/src/frontend/servers/y-provider/tsconfig.build.json b/src/frontend/servers/y-provider/tsconfig.build.json new file mode 100644 index 00000000..c14ab509 --- /dev/null +++ b/src/frontend/servers/y-provider/tsconfig.build.json @@ -0,0 +1,7 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "rootDir": "./src", + }, + "exclude": ["node_modules", "dist", "__tests__"], +} diff --git a/src/frontend/servers/y-provider/tsconfig.json b/src/frontend/servers/y-provider/tsconfig.json index b0cbd99c..120bda63 100644 --- a/src/frontend/servers/y-provider/tsconfig.json +++ b/src/frontend/servers/y-provider/tsconfig.json @@ -13,6 +13,13 @@ "jsx": "preserve", "incremental": false, "outDir": "./dist", + "paths": { + "@/*": ["./src/*"] + } + }, + "tsc-alias": { + "resolveFullPaths": true, + "verbose": false }, "include": ["**/*.ts"], "exclude": ["node_modules"] diff --git a/src/frontend/yarn.lock b/src/frontend/yarn.lock index 1b282914..d1525d01 100644 --- a/src/frontend/yarn.lock +++ b/src/frontend/yarn.lock @@ -4515,6 +4515,11 @@ dependencies: "@types/node" "*" +"@types/cookiejar@^2.1.5": + version "2.1.5" + resolved "https://registry.yarnpkg.com/@types/cookiejar/-/cookiejar-2.1.5.tgz#14a3e83fa641beb169a2dd8422d91c3c345a9a78" + integrity sha512-he+DHOWReW0nghN24E1WUqM0efK4kI9oTqDm6XmK8ZPe2djZ90BSNdGnIyCLzCPw7/pogPlGbzI2wHGGmi4O/Q== + "@types/debug@^4.0.0": version "4.1.12" resolved "https://registry.yarnpkg.com/@types/debug/-/debug-4.1.12.tgz#a155f21690871953410df4b6b6f53187f0500917" @@ -4696,6 +4701,11 @@ resolved "https://registry.yarnpkg.com/@types/mdurl/-/mdurl-2.0.0.tgz#d43878b5b20222682163ae6f897b20447233bdfd" integrity sha512-RGdgjQUZba5p6QEFAVx2OGb8rQDL/cPRG7GiedRzMcJ1tYnUANBncjbSB1NRGwbvjcPeikRABz2nshyPk1bhWg== +"@types/methods@^1.1.4": + version "1.1.4" + resolved "https://registry.yarnpkg.com/@types/methods/-/methods-1.1.4.tgz#d3b7ac30ac47c91054ea951ce9eed07b1051e547" + integrity sha512-ymXWVrDiCxTBE3+RIrrP533E70eA+9qu7zdWoHuOmGujkYtzf4HQF96b8nwHLqhuf4ykX61IGRIB38CC6/sImQ== + "@types/mime@^1": version "1.3.5" resolved "https://registry.yarnpkg.com/@types/mime/-/mime-1.3.5.tgz#1ef302e01cf7d2b5a0fa526790c9123bf1d06690" @@ -4846,6 +4856,24 @@ resolved "https://registry.yarnpkg.com/@types/stylis/-/stylis-4.2.5.tgz#1daa6456f40959d06157698a653a9ab0a70281df" integrity sha512-1Xve+NMN7FWjY14vLoY5tL3BVEQ/n42YLwaqJIPYhotZ9uBHt87VceMwWQpzmdEt2TNXIorIFG+YeCUUW7RInw== +"@types/superagent@^8.1.0": + version "8.1.9" + resolved "https://registry.yarnpkg.com/@types/superagent/-/superagent-8.1.9.tgz#28bfe4658e469838ed0bf66d898354bcab21f49f" + integrity sha512-pTVjI73witn+9ILmoJdajHGW2jkSaOzhiFYF1Rd3EQ94kymLqB9PjD9ISg7WaALC7+dCHT0FGe9T2LktLq/3GQ== + dependencies: + "@types/cookiejar" "^2.1.5" + "@types/methods" "^1.1.4" + "@types/node" "*" + form-data "^4.0.0" + +"@types/supertest@6.0.2": + version "6.0.2" + resolved "https://registry.yarnpkg.com/@types/supertest/-/supertest-6.0.2.tgz#2af1c466456aaf82c7c6106c6b5cbd73a5e86588" + integrity sha512-137ypx2lk/wTQbW6An6safu9hXmajAifU/s7szAHLN/FeIm5w7yR0Wkl9fdJMRSHwOn4HLAI0DaB2TOORuhPDg== + dependencies: + "@types/methods" "^1.1.4" + "@types/superagent" "^8.1.0" + "@types/symlink-or-copy@^1.2.0": version "1.2.2" resolved "https://registry.yarnpkg.com/@types/symlink-or-copy/-/symlink-or-copy-1.2.2.tgz#51b1c00b516a5774ada5d611e65eb123f988ef8d" @@ -4883,7 +4911,7 @@ resolved "https://registry.yarnpkg.com/@types/use-sync-external-store/-/use-sync-external-store-0.0.6.tgz#60be8d21baab8c305132eb9cb912ed497852aadc" integrity sha512-zFDAD+tlpf2r4asuHEj0XH6pY6i0g5NeAHPn+15wk3BV6JA69eERFXC1gyGThDkVa1zCyKr5jox1+2LbV/AMLg== -"@types/ws@*": +"@types/ws@*", "@types/ws@8.5.13": version "8.5.13" resolved "https://registry.yarnpkg.com/@types/ws/-/ws-8.5.13.tgz#6414c280875e2691d0d1e080b05addbf5cb91e20" integrity sha512-osM/gWBTPKgHV8XkTunnegTRIsvF6owmf5w+JtAfOw472dptdm0dlGv4xCt6GwQRcC2XVOvvRE/0bAoQcL2QkA== @@ -5361,6 +5389,11 @@ arraybuffer.prototype.slice@^1.0.3: is-array-buffer "^3.0.4" is-shared-array-buffer "^1.0.2" +asap@^2.0.0: + version "2.0.6" + resolved "https://registry.yarnpkg.com/asap/-/asap-2.0.6.tgz#e50347611d7e690943208bbdafebcbc2fb866d46" + integrity sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA== + ast-types-flow@^0.0.8: version "0.0.8" resolved "https://registry.yarnpkg.com/ast-types-flow/-/ast-types-flow-0.0.8.tgz#0a85e1c92695769ac13a428bb653e7538bea27d6" @@ -5924,6 +5957,11 @@ commander@^7.2.0: resolved "https://registry.yarnpkg.com/commander/-/commander-7.2.0.tgz#a36cb57d0b501ce108e4d20559a150a391d97ab7" integrity sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw== +commander@^9.0.0: + version "9.5.0" + resolved "https://registry.yarnpkg.com/commander/-/commander-9.5.0.tgz#bc08d1eb5cedf7ccb797a96199d41c7bc3e60d30" + integrity sha512-KRs7WVDKg86PWiuAqhDrAQnTXZKraVcCc6vFdL14qrZ/DcWwuRo7VoiYXalXO7S5GKpqYiVEwCbgFDfxNHKJBQ== + commander@~12.1.0: version "12.1.0" resolved "https://registry.yarnpkg.com/commander/-/commander-12.1.0.tgz#01423b36f501259fdaac4d0e4d60c96c991585d3" @@ -5939,6 +5977,11 @@ commondir@^1.0.1: resolved "https://registry.yarnpkg.com/commondir/-/commondir-1.0.1.tgz#ddd800da0c66127393cca5950ea968a3aaf1253b" integrity sha512-W9pAhw0ja1Edb5GVdIF1mjZw/ASI0AlShXM83UUGe2DVr5TdAPEA1OA8m/g8zWp9x6On7gqufY+FatDbC3MDQg== +component-emitter@^1.3.0: + version "1.3.1" + resolved "https://registry.yarnpkg.com/component-emitter/-/component-emitter-1.3.1.tgz#ef1d5796f7d93f135ee6fb684340b26403c97d17" + integrity sha512-T0+barUSQRTUQASh8bx02dl+DhF54GtIDY13Y3m9oWTklKbb3Wv974meRpeZ3lp1JpLVECWWNHC4vaG2XHXouQ== + compute-scroll-into-view@^3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/compute-scroll-into-view/-/compute-scroll-into-view-3.1.0.tgz#753f11d972596558d8fe7c6bcbc8497690ab4c87" @@ -5988,6 +6031,11 @@ cookie@0.7.1: resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.7.1.tgz#2f73c42142d5d5cf71310a74fc4ae61670e5dbc9" integrity sha512-6DnInpx7SJ2AK3+CTUE/ZM0vWTUboZCegxhC2xiIydHR9jNuTAASBrfEpHhiGOZw/nX51bHt6YQl8jsGo4y/0w== +cookiejar@^2.1.4: + version "2.1.4" + resolved "https://registry.yarnpkg.com/cookiejar/-/cookiejar-2.1.4.tgz#ee669c1fea2cf42dc31585469d193fef0d65771b" + integrity sha512-LDx6oHrK+PhzLKJU9j5S7/Y3jM/mUHvD/DeI1WQmJn652iPC5Y4TBzC9l+5OMOXlyTTA+SmVUPm0HQUwpD5Jqw== + core-js-compat@^3.38.0, core-js-compat@^3.38.1: version "3.39.0" resolved "https://registry.yarnpkg.com/core-js-compat/-/core-js-compat-3.39.0.tgz#b12dccb495f2601dc860bdbe7b4e3ffa8ba63f61" @@ -6347,6 +6395,14 @@ devlop@^1.0.0: dependencies: dequal "^2.0.0" +dezalgo@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/dezalgo/-/dezalgo-1.0.4.tgz#751235260469084c132157dfa857f386d4c33d81" + integrity sha512-rXSP0bf+5n0Qonsb+SVVfNfIsimO4HEtmnIpPHY8Q1UCzKlQrDMfdobr8nJOOsRgWCyMRqeSBQzmWUMq7zvVig== + dependencies: + asap "^2.0.0" + wrappy "1" + diff-sequences@^29.6.3: version "29.6.3" resolved "https://registry.yarnpkg.com/diff-sequences/-/diff-sequences-29.6.3.tgz#4deaf894d11407c51efc8418012f9e70b84ea921" @@ -7166,6 +7222,11 @@ fast-levenshtein@^2.0.6: resolved "https://registry.yarnpkg.com/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz#3d8a5c66883a16a30ca8643e851f19baa7797917" integrity sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw== +fast-safe-stringify@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/fast-safe-stringify/-/fast-safe-stringify-2.1.1.tgz#c406a83b6e70d9e35ce3b30a81141df30aeba884" + integrity sha512-W+KJc2dmILlPplD/H4K9l9LcAHAfPtP6BY84uVLXQ6Evcz9Lcg33Y2z1IVblT6xdY54PXYVHEv+0Wpq8Io6zkA== + fast-uri@^3.0.1: version "3.0.3" resolved "https://registry.yarnpkg.com/fast-uri/-/fast-uri-3.0.3.tgz#892a1c91802d5d7860de728f18608a0573142241" @@ -7316,6 +7377,15 @@ form-data@^4.0.0: combined-stream "^1.0.8" mime-types "^2.1.12" +formidable@^3.5.1: + version "3.5.2" + resolved "https://registry.yarnpkg.com/formidable/-/formidable-3.5.2.tgz#207c33fecdecb22044c82ba59d0c63a12fb81d77" + integrity sha512-Jqc1btCy3QzRbJaICGwKcBfGWuLADRerLzDqi2NwSt/UkXLsHJw2TVResiaoBufHVHy9aSgClOHCeJsSsFLTbg== + dependencies: + dezalgo "^1.0.4" + hexoid "^2.0.0" + once "^1.4.0" + forwarded@0.2.0: version "0.2.0" resolved "https://registry.yarnpkg.com/forwarded/-/forwarded-0.2.0.tgz#2269936428aad4c15c7ebe9779a84bf0b2a81811" @@ -7567,7 +7637,7 @@ globalthis@^1.0.4: define-properties "^1.2.1" gopd "^1.0.1" -globby@^11.1.0: +globby@^11.0.4, globby@^11.1.0: version "11.1.0" resolved "https://registry.yarnpkg.com/globby/-/globby-11.1.0.tgz#bd4be98bb042f83d796f7e3811991fbe82a0d34b" integrity sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g== @@ -7920,6 +7990,11 @@ heimdalljs@^0.2.6: dependencies: rsvp "~3.2.1" +hexoid@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/hexoid/-/hexoid-2.0.0.tgz#fb36c740ebbf364403fa1ec0c7efd268460ec5b9" + integrity sha512-qlspKUK7IlSQv2o+5I7yhUd7TxlOG2Vr5LTa3ve2XSNVKAL/n/u/7KLvKmFNimomDIKvZFXWHv0T12mv7rT8Aw== + hoist-non-react-statics@^3.3.1, hoist-non-react-statics@^3.3.2: version "3.3.2" resolved "https://registry.yarnpkg.com/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz#ece0acaf71d62c2969c2ec59feff42a4b1a85b45" @@ -9563,7 +9638,7 @@ merge2@^1.3.0, merge2@^1.4.1: resolved "https://registry.yarnpkg.com/merge2/-/merge2-1.4.1.tgz#4368892f885e907455a6fd7dc55c0c9d404990ae" integrity sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg== -methods@~1.1.2: +methods@^1.1.2, methods@~1.1.2: version "1.1.2" resolved "https://registry.yarnpkg.com/methods/-/methods-1.1.2.tgz#5529a4d67654134edcc5266656835b0f851afcee" integrity sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w== @@ -9898,6 +9973,11 @@ mime@1.6.0: resolved "https://registry.yarnpkg.com/mime/-/mime-1.6.0.tgz#32cd9e5c64553bd58d19a568af452acff04981b1" integrity sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg== +mime@2.6.0: + version "2.6.0" + resolved "https://registry.yarnpkg.com/mime/-/mime-2.6.0.tgz#a2a682a95cd4d0cb1d6257e28f83da7e35800367" + integrity sha512-USPkMeET31rOMiarsBNIHZKLGgvKc/LrjofAnBlOttf5ajRvqiRA8QsenbcooctK6d6Ts6aqZXBA+XbkKthiQg== + mimic-fn@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-2.1.0.tgz#7ed2c2ccccaf84d3ffcb7a69b57711fc2083401b" @@ -9976,6 +10056,11 @@ ms@2.1.3, ms@^2.1.1, ms@^2.1.3: resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2" integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA== +mylas@^2.1.9: + version "2.1.13" + resolved "https://registry.yarnpkg.com/mylas/-/mylas-2.1.13.tgz#1e23b37d58fdcc76e15d8a5ed23f9ae9fc0cbdf4" + integrity sha512-+MrqnJRtxdF+xngFfUUkIMQrUUL0KsxbADUkn23Z/4ibGg192Q+z+CQyiYwvWTsYjJygmMR8+w3ZDa98Zh6ESg== + nanoid@^3.3.6, nanoid@^3.3.7: version "3.3.8" resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.3.8.tgz#b1be3030bee36aaff18bacb375e5cce521684baf" @@ -10428,6 +10513,13 @@ playwright@1.49.0: optionalDependencies: fsevents "2.3.2" +plimit-lit@^1.2.6: + version "1.6.1" + resolved "https://registry.yarnpkg.com/plimit-lit/-/plimit-lit-1.6.1.tgz#a34594671b31ee8e93c72d505dfb6852eb72374a" + integrity sha512-B7+VDyb8Tl6oMJT9oSO2CW8XC/T4UcJGrwOVoNGwOQsQYhlpfajmrMj5xeejqaASq3V/EqThyOeATEOMuSEXiA== + dependencies: + queue-lit "^1.5.1" + possible-typed-array-names@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/possible-typed-array-names/-/possible-typed-array-names-1.0.0.tgz#89bb63c6fada2c3e90adc4a647beeeb39cc7bf8f" @@ -10815,6 +10907,13 @@ qs@6.13.0: dependencies: side-channel "^1.0.6" +qs@^6.11.0: + version "6.13.1" + resolved "https://registry.yarnpkg.com/qs/-/qs-6.13.1.tgz#3ce5fc72bd3a8171b85c99b93c65dd20b7d1b16e" + integrity sha512-EJPeIn0CYrGu+hli1xilKAPXODtJ12T0sP63Ijx2/khC2JtuaN3JyNIpvmnkmaEtha9ocbG4A4cMcr+TvqvwQg== + dependencies: + side-channel "^1.0.6" + querystring@^0.2.0: version "0.2.1" resolved "https://registry.yarnpkg.com/querystring/-/querystring-0.2.1.tgz#40d77615bb09d16902a85c3e38aa8b5ed761c2dd" @@ -10825,6 +10924,11 @@ querystringify@^2.1.1: resolved "https://registry.yarnpkg.com/querystringify/-/querystringify-2.2.0.tgz#3345941b4153cb9d082d8eee4cda2016a9aef7f6" integrity sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ== +queue-lit@^1.5.1: + version "1.5.2" + resolved "https://registry.yarnpkg.com/queue-lit/-/queue-lit-1.5.2.tgz#83c24d4f4764802377b05a6e5c73017caf3f8747" + integrity sha512-tLc36IOPeMAubu8BkW8YDBV+WyIgKlYU7zUNs0J5Vk9skSZ4JfGlPOqplP0aHdfv7HL0B2Pg6nwiq60Qc6M2Hw== + queue-microtask@^1.2.2: version "1.2.3" resolved "https://registry.yarnpkg.com/queue-microtask/-/queue-microtask-1.2.3.tgz#4929228bbc724dfac43e0efb058caf7b6cfb6243" @@ -12204,6 +12308,29 @@ stylis@4.3.2: resolved "https://registry.yarnpkg.com/stylis/-/stylis-4.3.2.tgz#8f76b70777dd53eb669c6f58c997bf0a9972e444" integrity sha512-bhtUjWd/z6ltJiQwg0dUfxEJ+W+jdqQd8TbWLWyeIJHlnsqmGLRFFd8e5mA0AZi/zx90smXRlN66YMTcaSFifg== +superagent@^9.0.1: + version "9.0.2" + resolved "https://registry.yarnpkg.com/superagent/-/superagent-9.0.2.tgz#a18799473fc57557289d6b63960610e358bdebc1" + integrity sha512-xuW7dzkUpcJq7QnhOsnNUgtYp3xRwpt2F7abdRYIpCsAt0hhUqia0EdxyXZQQpNmGtsCzYHryaKSV3q3GJnq7w== + dependencies: + component-emitter "^1.3.0" + cookiejar "^2.1.4" + debug "^4.3.4" + fast-safe-stringify "^2.1.1" + form-data "^4.0.0" + formidable "^3.5.1" + methods "^1.1.2" + mime "2.6.0" + qs "^6.11.0" + +supertest@7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/supertest/-/supertest-7.0.0.tgz#cac53b3d6872a0b317980b2b0cfa820f09cd7634" + integrity sha512-qlsr7fIC0lSddmA3tzojvzubYxvlGtzumcdHgPwbFWMISQwL22MhM2Y3LNt+6w9Yyx7559VW5ab70dgphm8qQA== + dependencies: + methods "^1.1.2" + superagent "^9.0.1" + supports-color@^5.5.0: version "5.5.0" resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.5.0.tgz#e2e69a44ac8772f78a1ec0b35b689df6530efc8f" @@ -12515,6 +12642,18 @@ ts-node@10.9.2: v8-compile-cache-lib "^3.0.1" yn "3.1.1" +tsc-alias@1.8.10: + version "1.8.10" + resolved "https://registry.yarnpkg.com/tsc-alias/-/tsc-alias-1.8.10.tgz#279f9bf0dd8bc10fb27820393d4881db5a303938" + integrity sha512-Ibv4KAWfFkFdKJxnWfVtdOmB0Zi1RJVxcbPGiCDsFpCQSsmpWyuzHG3rQyI5YkobWwxFPEyQfu1hdo4qLG2zPw== + dependencies: + chokidar "^3.5.3" + commander "^9.0.0" + globby "^11.0.4" + mylas "^2.1.9" + normalize-path "^3.0.0" + plimit-lit "^1.2.6" + tsconfig-paths@^3.15.0: version "3.15.0" resolved "https://registry.yarnpkg.com/tsconfig-paths/-/tsconfig-paths-3.15.0.tgz#5299ec605e55b1abb23ec939ef15edaf483070d4" @@ -13505,16 +13644,16 @@ write-file-atomic@^5.0.1: imurmurhash "^0.1.4" signal-exit "^4.0.1" +ws@8.18.0, ws@^8.11.0, ws@^8.17.1, ws@^8.18.0, ws@^8.5.0: + version "8.18.0" + resolved "https://registry.yarnpkg.com/ws/-/ws-8.18.0.tgz#0d7505a6eafe2b0e712d232b42279f53bc289bbc" + integrity sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw== + ws@^7.4.6: version "7.5.10" resolved "https://registry.yarnpkg.com/ws/-/ws-7.5.10.tgz#58b5c20dc281633f6c19113f39b349bd8bd558d9" integrity sha512-+dbF1tHwZpXcbOJdVOkzLDxZP1ailvSxM6ZweXTegylPny803bFhA+vqBYw4s31NSAk4S2Qz+AKXK9a4wkdjcQ== -ws@^8.11.0, ws@^8.17.1, ws@^8.18.0, ws@^8.5.0: - version "8.18.0" - resolved "https://registry.yarnpkg.com/ws/-/ws-8.18.0.tgz#0d7505a6eafe2b0e712d232b42279f53bc289bbc" - integrity sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw== - xml-name-validator@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/xml-name-validator/-/xml-name-validator-4.0.0.tgz#79a006e2e63149a8600f15430f0a4725d1524835"