diff --git a/deploy/https/nginx.backend.conf b/deploy/https/nginx.backend.conf index c1b7975da..3eaa6d1e3 100644 --- a/deploy/https/nginx.backend.conf +++ b/deploy/https/nginx.backend.conf @@ -1,8 +1,9 @@ # 이제는 거의 동일 worker_processes auto; +# 4096개의 연결로 변경 events { - worker_connections 1024; + worker_connections 4096; } http { @@ -19,30 +20,84 @@ http { server main-backend-app-3:8080 max_fails=3 fail_timeout=10s; } + # websocket용 upstream + upstream main_backend_ws { + # load balancer를 hash로 해서 바뀌지 않도록 해야 한다. + hash $arg_room_code consistent; + + server main-backend-app-1:8080 max_fails=3 fail_timeout=10s; + server main-backend-app-2:8080 max_fails=3 fail_timeout=10s; + server main-backend-app-3:8080 max_fails=3 fail_timeout=10s; + } + server { listen 80; server_name _; - # 마찬가지로 실제 배포 환경에서도 sse 연결이 가능하도록 하는 것이 중요하다. - location ~ ^/api/cards/sse$ { + location = /api/ws { + return 301 /api/ws/; + } + + location ^~ /api/ws/ { + if ($arg_room_code = "") { return 400; } + + proxy_pass http://main_backend_ws; + + # 전역적으로 설정했지만 안전성을 위해서 한번 더 + proxy_http_version 1.1; + + # 웹소켓 업그레이드 + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection "upgrade"; + + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + proxy_set_header X-Forwarded-Host $host; + + # 연결 높이기 + proxy_read_timeout 3600s; + proxy_send_timeout 3600s; + + # 실시간 성을 위해서 추가 + proxy_buffering off; + + # 이건 업스트림 변경을 일으키지 않게 한다. + proxy_next_upstream off; + } + + # sse를 사용하는 경우 버퍼링을 꺼주고 오래 연결이 가능하도록 해주어야 한다. -> 지금은 사용하지 않지만 마지막에 sse가 있으면 이를 이용하도록 할 예정이다. + location ^~ /api/sse/ { proxy_pass http://main_backend_app; proxy_http_version 1.1; proxy_set_header Connection ""; + proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; + # buffering 꺼주기 -> 실시간으로 바로 보낼 수 있도록 하기 위함이다. proxy_buffering off; proxy_cache off; add_header X-Accel-Buffering no; + # 오랫동안 연결되도록 하기 1시간 proxy_read_timeout 3600s; proxy_send_timeout 3600s; + # 응답을 메모리/디스크에 쌓지 않고 바로 전달하도록 한다. proxy_request_buffering off; + chunked_transfer_encoding on; + + # 스트리밍에 경우 업스트림을 교체하지 않는다. proxy_next_upstream off; + + # sse가 캐시되지 않도록 명시 + add_header Cache-Control "no-cache, no-store"; + gzip off; } diff --git a/deploy/https/nginx.gateway.conf b/deploy/https/nginx.gateway.conf index f83335f9f..8eb9d5062 100644 --- a/deploy/https/nginx.gateway.conf +++ b/deploy/https/nginx.gateway.conf @@ -29,28 +29,55 @@ server { proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; - # keep alive 사용을 위한 체크 - proxy_http_version 1.1; - proxy_set_header Connection ""; - # 443에도 certbot을 확인할 수 있도록 수정 location /.well-known/acme-challenge/ { root /var/www/certbot; } # Docker에 dns 서버 - resolver 127.0.0.11 ipv6=off; + # 최대 10초까지만 같은 이름을 쓰고 2초 까지 기다린다 + resolver 127.0.0.11 ipv6=off valid=10s; + resolver_timeout 2s; + + set $backend_upstream http://main-backend-nginx; - # test와 마찬가지로 sse 연결에 대해서 설정을 해두는 것이 중요하다. - location ~ ^/api/cards/sse$ { - proxy_pass http://main_backend_app; + # websocket 연결 설정 ( test 서버와 거의 동일 ) + location = /api/ws { + return 301 /api/ws/; + } + location ^~ /api/ws/ { + proxy_pass $backend_upstream; + + # 전역적으로 설정했지만 안전성을 위해서 한번 더 proxy_http_version 1.1; - proxy_set_header Connection ""; + + # 웹소켓 업그레이드 + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection "upgrade"; + proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; + proxy_set_header X-Forwarded-Host $host; + + # 연결 높이기 + proxy_read_timeout 3600s; + proxy_send_timeout 3600s; + + # 실시간 성을 위해서 추가 + proxy_buffering off; + } + + + # test와 마찬가지로 sse 연결에 대해서 설정을 해두는 것이 중요하다. -> 지금은 사용하지 않지만 마지막에 sse가 있으면 이를 이용하도록 할 예정이다. + location ^~ /api/sse/ { + proxy_pass $backend_upstream; + + # keep alive 설정 + proxy_http_version 1.1; + proxy_set_header Connection ""; proxy_buffering off; proxy_cache off; @@ -60,13 +87,20 @@ server { proxy_send_timeout 3600s; proxy_request_buffering off; - proxy_next_upstream off; gzip off; + add_header Cache-Control "no-cache, no-store"; + + # 응답을 한번에 다 보내는게 아닌 조각 단위로 계속 나누어서 보내도록 설정 + chunked_transfer_encoding on; } # backend로 연결 location /api/ { - proxy_pass http://main-backend-nginx; + proxy_pass $backend_upstream; + + # keep alive 사용을 위한 체크 + proxy_http_version 1.1; + proxy_set_header Connection ""; # gateway도 설정을 안하면 api가 기다리든 말든 끊어버리니 api gateway도 설정을 해주는 것이 중요하다. -> 나중에 업로드 api만 따로 변경 client_max_body_size 10m; @@ -75,16 +109,21 @@ server { proxy_read_timeout 1m; } + set $frontend_upstream http://frontend:3000; + # NEXT에 캐싱서버 -> NEXT는 기본적으로 압축하는 로직이 존재한다. location /_next/static/ { - proxy_pass http://frontend:3000; + proxy_pass $frontend_upstream; expires 1y; add_header Cache-Control "public, immutable"; access_log off; } location / { - proxy_pass http://frontend:3000; + proxy_pass $frontend_upstream; + + proxy_http_version 1.1; + proxy_set_header Connection ""; # 연결 지연시간 설정 proxy_connect_timeout 3s; diff --git a/deploy/test/docker-compose.yml b/deploy/test/docker-compose.yml index 1f2bec57f..7ccc1e06f 100644 --- a/deploy/test/docker-compose.yml +++ b/deploy/test/docker-compose.yml @@ -39,6 +39,19 @@ services: - clobby-network restart: unless-stopped + # sfu 서버 -> 현재 sfu는 이걸로 가고 위에 로드밸런싱을 갖춘 아키텍처는 나중에 저장 로직으로 사용하면 된다. ( 배포는 또 다 분리할거니까 괜찮을 거다. ) + sfu: + container_name: sfu + image: "${MAIN_BACKEND_IMG}:latest" + env_file: + - ./.env.backend + networks: + - clobby-network + # 보안 주의 + ports: + - "40000-41999:40000-41999/udp" + - "40000-41999:40000-41999/tcp" + # frontend 빌드 frontend: container_name: frontend diff --git a/deploy/test/nginx.backend.conf b/deploy/test/nginx.backend.conf index a80c0e04a..3c453ca83 100644 --- a/deploy/test/nginx.backend.conf +++ b/deploy/test/nginx.backend.conf @@ -1,9 +1,9 @@ # 동시에 처리 가능한 프로세스 수 설정 worker_processes auto; -# 하나의 process는 1024개의 동시 연결이다. +# 하나의 process는 4096개의 동시 연결이다. events { - worker_connections 1024; + worker_connections 4096; } http { @@ -12,7 +12,7 @@ http { # load balancer least_conn; - # backend 헬스 체크 추가 + # backend 헬스 체크 추가 ( pool의 크기 ) keepalive 64; # 10초 동안 3번의 연결실패가 일어나면 그 앱은 닫는다. @@ -22,16 +22,67 @@ http { server main-backend-app-3:8080 max_fails=3 fail_timeout=10s; } + # websocket용 upstream 추가 + upstream main_backend_ws { + # sfu 서버로 사용하게 되어서 주석 처리 ( 나중에 websocket은 이걸 사용할 수 있다. ) + # # load_balancer 떄문에 바뀌지 않도록 이를 수정해준다. ( room_id를 기반으로 앱을 매핑 ) + # # consistent를 이용해서 서버가 늘거나 삭제되도 대부분은 그대로 서버를 유지하도록 한다. + # hash $arg_room_code consistent; + + # server main-backend-app-1:8080 max_fails=3 fail_timeout=10s; + # server main-backend-app-2:8080 max_fails=3 fail_timeout=10s; + # server main-backend-app-3:8080 max_fails=3 fail_timeout=10s; + server sfu:8080; + keepalive 64; + } + server { listen 80; server_name _; - # sse를 사용하는 경우 버퍼링을 꺼주고 오래 연결이 가능하도록 해주어야 한다. - location ~ ^/api/cards/sse$ { + # 웹소켓 관련 추가 + location = /api/ws { + return 301 /api/ws/; + } + + location ^~ /api/ws/ { + # room_id가 없으면 에러를 발생시켜야 한다. + # nginx는 $arg_<파라미터이름>으로 매핑을 시킨다. + # if ($arg_room_code = "") { return 400; } + + proxy_pass http://main_backend_ws; + + # 전역적으로 설정했지만 안전성을 위해서 한번 더 + proxy_http_version 1.1; + + # 웹소켓 업그레이드 + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection "upgrade"; + + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + proxy_set_header X-Forwarded-Host $host; + + # 연결 높이기 + proxy_read_timeout 3600s; + proxy_send_timeout 3600s; + + # 실시간 성을 위해서 추가 + proxy_buffering off; + + # 이건 업스트림 변경을 일으키지 않게 한다. + proxy_next_upstream off; + } + + # sse를 사용하는 경우 버퍼링을 꺼주고 오래 연결이 가능하도록 해주어야 한다. -> 지금은 사용하지 않지만 마지막에 sse가 있으면 이를 이용하도록 할 예정이다. + location ^~ /api/sse/ { proxy_pass http://main_backend_app; proxy_http_version 1.1; proxy_set_header Connection ""; + proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; @@ -48,10 +99,14 @@ http { # 응답을 메모리/디스크에 쌓지 않고 바로 전달하도록 한다. proxy_request_buffering off; + chunked_transfer_encoding on; # 스트리밍에 경우 업스트림을 교체하지 않는다. proxy_next_upstream off; + # sse가 캐시되지 않도록 명시 + add_header Cache-Control "no-cache"; + gzip off; } diff --git a/deploy/test/nginx.gateway.conf b/deploy/test/nginx.gateway.conf index 31f3f7623..aa43ee25c 100644 --- a/deploy/test/nginx.gateway.conf +++ b/deploy/test/nginx.gateway.conf @@ -12,23 +12,56 @@ server { # application/javascript text/javascript application/rss+xml # image/svg+xml; - # Docker Dns로 TTL 주기마다 DNS를 재조회 한다. - resolver 127.0.0.11 ipv6=off; + # Docker Dns로 TTL 주기마다 DNS를 재조회 한다. ( 10초 마다 확인하기 ) + resolver 127.0.0.11 ipv6=off valid=10s; + resolver_timeout 2s; - - # keep alive 설정 + # http 1.1 사용 proxy_http_version 1.1; - proxy_set_header Connection ""; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; - # sse를 위한 수정 - gateway도 수정해 줘야 백엔드에서 받을 수 있다. ( 백엔드 쪽이 살아있어도 gateway가 죽으면 의미가 없기에 ) - location ~ ^/api/cards/sse$ { - proxy_pass http://main-backend-nginx; + # 백엔드도 DNS를 항상 조회하게 해야 한다. + set $backend_upstream http://main-backend-nginx; + + # 웹소켓 관련 추가 + # 리다이렉트도 추가 + location = /api/ws { + return 301 /api/ws/; + } + + location ^~ /api/ws/ { + proxy_pass $backend_upstream; + # 전역적으로 설정했지만 안전성을 위해서 한번 더 + proxy_http_version 1.1; + + # 웹소켓 업그레이드 + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection "upgrade"; + + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + proxy_set_header X-Forwarded-Host $host; + + # 연결 높이기 + proxy_read_timeout 3600s; + proxy_send_timeout 3600s; + + # 실시간 성을 위해서 추가 + proxy_buffering off; + } + + # sse를 사용을 위한 부분 + location ^~ /api/sse/ { + proxy_pass $backend_upstream; + + # keep alive 설정 proxy_http_version 1.1; proxy_set_header Connection ""; @@ -41,13 +74,19 @@ server { proxy_request_buffering off; gzip off; + add_header Cache-Control "no-cache"; + # 응답을 한번에 다 보내는게 아닌 조각 단위로 계속 나누어서 보내도록 설정 + chunked_transfer_encoding on; } # backend 프록시 전달 - location /api/ { + location /api/ { # Dns 탐색 - proxy_pass http://main-backend-nginx; + proxy_pass $backend_upstream; + + # keep alive 설정 + proxy_set_header Connection ""; client_max_body_size 10m; proxy_connect_timeout 5s; @@ -56,10 +95,13 @@ server { } + # 항상 DNS를 재조회 하도록 해서 안정성을 높인다. + set $frontend_upstream http://frontend:3000; + # NEXT용 static caching # NEXT는 자체적으로 해시화가 있어서 장기 캐싱에도 안전하다고 합니다. location /_next/static/ { - proxy_pass http://frontend:3000; + proxy_pass $frontend_upstream; expires 1y; add_header Cache-Control "public, immutable"; access_log off; @@ -67,7 +109,10 @@ server { # 나머지는 frontend로 전달 location / { - proxy_pass http://frontend:3000; + proxy_pass $frontend_upstream; + + # keep alive 설정 + proxy_set_header Connection ""; # frontend는 연결 시간을 짧게 잡아서 유저가 오류를 빨리 찾아낼 수 있도록 설정합니다. proxy_connect_timeout 3s; diff --git a/rep/main_backend/Dockerfile b/rep/main_backend/Dockerfile index 3be8e45f8..3e8dfebd6 100644 --- a/rep/main_backend/Dockerfile +++ b/rep/main_backend/Dockerfile @@ -5,6 +5,11 @@ FROM node:22-slim AS base WORKDIR /app +# python3, make, g++ mediasoup에게 필요한 패키지 추가 +RUN apt-get update && apt-get install -y --no-install-recommends \ + python3 make g++ pkg-config git \ + && rm -rf /var/lib/apt/lists/* + # pnpm && yarn과 같은 패키지 매니저 사용 허용 & pnpm 설치 RUN corepack enable \ && corepack prepare pnpm@10.0.0 --activate @@ -20,22 +25,29 @@ COPY package.json pnpm-lock.yaml ./ RUN --mount=type=cache,id=pnpm-store-backend,target=/root/.local/share/pnpm/store \ pnpm install --frozen-lockfile +# ci에서 mediasoup 부분은 한번더 install해서 해결해보아야 한다. +RUN pnpm rebuild mediasoup + COPY . . -# 빌드 +# 빌드 후 prod만 남기기 RUN pnpm build +RUN pnpm prune --prod # 실제 실행 -> 이것만 남음 -FROM base AS runner +FROM node:22-slim AS runner -# 필요한 의존성 가져오기 -COPY package.json pnpm-lock.yaml ./ +# 현재 문제가 발생하는 pnpm i를 (mediasoup문제) 해결하기 위해서 이를 주석처리하고 안전하게 build한 것을 사용 +# # 필요한 의존성 가져오기 +# COPY package.json pnpm-lock.yaml ./ -# 따로 설치해서 실행이 되도록 하기 -> 기존 캐시 최대한 유지 -RUN --mount=type=cache,id=pnpm-store-backend,target=/root/.local/share/pnpm/store \ - pnpm install --prod --frozen-lockfile +# # 따로 설치해서 실행이 되도록 하기 -> 기존 캐시 최대한 유지 +# RUN --mount=type=cache,id=pnpm-store-backend,target=/root/.local/share/pnpm/store \ +# pnpm install --prod --frozen-lockfile -# 빌드 패키지에서 빌드 결과물 가져오기 +# 빌드 패키지에서 실행에 필요한 빌드 결과물 가져오기 +COPY --from=build /app/package.json ./package.json +COPY --from=build /app/node_modules ./node_modules COPY --from=build /app/dist ./dist # 실행 관련 diff --git a/rep/main_backend/package.json b/rep/main_backend/package.json index 3b5bbee1b..bb44ed44d 100644 --- a/rep/main_backend/package.json +++ b/rep/main_backend/package.json @@ -23,13 +23,13 @@ "@aws-sdk/s3-request-presigner": "^3.957.0", "@nestjs/apollo": "^13.2.3", "@nestjs/axios": "^4.0.1", - "@nestjs/common": "^10.0.0", + "@nestjs/common": "^10", "@nestjs/config": "^4.0.2", - "@nestjs/core": "^10.0.0", + "@nestjs/core": "^10", "@nestjs/graphql": "^13.2.3", "@nestjs/platform-express": "^10.0.0", - "@nestjs/platform-socket.io": "^11.1.11", - "@nestjs/websockets": "^11.1.11", + "@nestjs/platform-socket.io": "^10.4.21", + "@nestjs/websockets": "^10.4.21", "@socket.io/redis-adapter": "^8.3.0", "argon2": "^0.44.0", "axios": "^1.13.2", @@ -41,6 +41,7 @@ "graphql": "^16.12.0", "graphql-type-json": "^0.3.2", "jose": "^6.1.3", + "mediasoup": "^3.19.14", "mysql2": "^3.15.3", "redis": "^5.10.0", "reflect-metadata": "^0.1.13", @@ -90,5 +91,11 @@ ], "coverageDirectory": "../coverage", "testEnvironment": "node" + }, + "pnpm": { + "onlyBuiltDependencies": [ + "mediasoup", + "argon2" + ] } } diff --git a/rep/main_backend/pnpm-lock.yaml b/rep/main_backend/pnpm-lock.yaml index 75d58fb55..f32354e70 100644 --- a/rep/main_backend/pnpm-lock.yaml +++ b/rep/main_backend/pnpm-lock.yaml @@ -13,40 +13,40 @@ importers: version: 5.2.0(graphql@16.12.0) '@as-integrations/express5': specifier: ^1.1.2 - version: 1.1.2(@apollo/server@5.2.0(graphql@16.12.0))(express@4.21.2) + version: 1.1.2(@apollo/server@5.2.0(graphql@16.12.0))(express@4.22.1) '@aws-sdk/client-s3': specifier: ^3.957.0 - version: 3.957.0 + version: 3.965.0 '@aws-sdk/s3-request-presigner': specifier: ^3.957.0 - version: 3.957.0 + version: 3.965.0 '@nestjs/apollo': specifier: ^13.2.3 - version: 13.2.3(@apollo/server@5.2.0(graphql@16.12.0))(@as-integrations/express5@1.1.2(@apollo/server@5.2.0(graphql@16.12.0))(express@4.21.2))(@nestjs/common@10.4.20(class-transformer@0.5.1)(class-validator@0.14.3)(reflect-metadata@0.1.14)(rxjs@7.8.2))(@nestjs/core@10.4.20)(@nestjs/graphql@13.2.3(@nestjs/common@10.4.20(class-transformer@0.5.1)(class-validator@0.14.3)(reflect-metadata@0.1.14)(rxjs@7.8.2))(@nestjs/core@10.4.20)(class-transformer@0.5.1)(class-validator@0.14.3)(graphql@16.12.0)(reflect-metadata@0.1.14))(graphql@16.12.0) + version: 13.2.3(@apollo/server@5.2.0(graphql@16.12.0))(@as-integrations/express5@1.1.2(@apollo/server@5.2.0(graphql@16.12.0))(express@4.22.1))(@nestjs/common@10.4.21(class-transformer@0.5.1)(class-validator@0.14.3)(reflect-metadata@0.1.14)(rxjs@7.8.2))(@nestjs/core@10.4.21)(@nestjs/graphql@13.2.3(@nestjs/common@10.4.21(class-transformer@0.5.1)(class-validator@0.14.3)(reflect-metadata@0.1.14)(rxjs@7.8.2))(@nestjs/core@10.4.21)(class-transformer@0.5.1)(class-validator@0.14.3)(graphql@16.12.0)(reflect-metadata@0.1.14))(graphql@16.12.0) '@nestjs/axios': specifier: ^4.0.1 - version: 4.0.1(@nestjs/common@10.4.20(class-transformer@0.5.1)(class-validator@0.14.3)(reflect-metadata@0.1.14)(rxjs@7.8.2))(axios@1.13.2)(rxjs@7.8.2) + version: 4.0.1(@nestjs/common@10.4.21(class-transformer@0.5.1)(class-validator@0.14.3)(reflect-metadata@0.1.14)(rxjs@7.8.2))(axios@1.13.2)(rxjs@7.8.2) '@nestjs/common': - specifier: ^10.0.0 - version: 10.4.20(class-transformer@0.5.1)(class-validator@0.14.3)(reflect-metadata@0.1.14)(rxjs@7.8.2) + specifier: ^10 + version: 10.4.21(class-transformer@0.5.1)(class-validator@0.14.3)(reflect-metadata@0.1.14)(rxjs@7.8.2) '@nestjs/config': specifier: ^4.0.2 - version: 4.0.2(@nestjs/common@10.4.20(class-transformer@0.5.1)(class-validator@0.14.3)(reflect-metadata@0.1.14)(rxjs@7.8.2))(rxjs@7.8.2) + version: 4.0.2(@nestjs/common@10.4.21(class-transformer@0.5.1)(class-validator@0.14.3)(reflect-metadata@0.1.14)(rxjs@7.8.2))(rxjs@7.8.2) '@nestjs/core': - specifier: ^10.0.0 - version: 10.4.20(@nestjs/common@10.4.20(class-transformer@0.5.1)(class-validator@0.14.3)(reflect-metadata@0.1.14)(rxjs@7.8.2))(@nestjs/platform-express@10.4.20)(@nestjs/websockets@11.1.11)(reflect-metadata@0.1.14)(rxjs@7.8.2) + specifier: ^10 + version: 10.4.21(@nestjs/common@10.4.21(class-transformer@0.5.1)(class-validator@0.14.3)(reflect-metadata@0.1.14)(rxjs@7.8.2))(@nestjs/platform-express@10.4.21)(@nestjs/websockets@10.4.21)(reflect-metadata@0.1.14)(rxjs@7.8.2) '@nestjs/graphql': specifier: ^13.2.3 - version: 13.2.3(@nestjs/common@10.4.20(class-transformer@0.5.1)(class-validator@0.14.3)(reflect-metadata@0.1.14)(rxjs@7.8.2))(@nestjs/core@10.4.20)(class-transformer@0.5.1)(class-validator@0.14.3)(graphql@16.12.0)(reflect-metadata@0.1.14) + version: 13.2.3(@nestjs/common@10.4.21(class-transformer@0.5.1)(class-validator@0.14.3)(reflect-metadata@0.1.14)(rxjs@7.8.2))(@nestjs/core@10.4.21)(class-transformer@0.5.1)(class-validator@0.14.3)(graphql@16.12.0)(reflect-metadata@0.1.14) '@nestjs/platform-express': specifier: ^10.0.0 - version: 10.4.20(@nestjs/common@10.4.20(class-transformer@0.5.1)(class-validator@0.14.3)(reflect-metadata@0.1.14)(rxjs@7.8.2))(@nestjs/core@10.4.20) + version: 10.4.21(@nestjs/common@10.4.21(class-transformer@0.5.1)(class-validator@0.14.3)(reflect-metadata@0.1.14)(rxjs@7.8.2))(@nestjs/core@10.4.21) '@nestjs/platform-socket.io': - specifier: ^11.1.11 - version: 11.1.11(@nestjs/common@10.4.20(class-transformer@0.5.1)(class-validator@0.14.3)(reflect-metadata@0.1.14)(rxjs@7.8.2))(@nestjs/websockets@11.1.11)(rxjs@7.8.2) + specifier: ^10.4.21 + version: 10.4.21(@nestjs/common@10.4.21(class-transformer@0.5.1)(class-validator@0.14.3)(reflect-metadata@0.1.14)(rxjs@7.8.2))(@nestjs/websockets@10.4.21)(rxjs@7.8.2) '@nestjs/websockets': - specifier: ^11.1.11 - version: 11.1.11(@nestjs/common@10.4.20(class-transformer@0.5.1)(class-validator@0.14.3)(reflect-metadata@0.1.14)(rxjs@7.8.2))(@nestjs/core@10.4.20)(@nestjs/platform-socket.io@11.1.11)(reflect-metadata@0.1.14)(rxjs@7.8.2) + specifier: ^10.4.21 + version: 10.4.21(@nestjs/common@10.4.21(class-transformer@0.5.1)(class-validator@0.14.3)(reflect-metadata@0.1.14)(rxjs@7.8.2))(@nestjs/core@10.4.21)(@nestjs/platform-socket.io@10.4.21)(reflect-metadata@0.1.14)(rxjs@7.8.2) '@socket.io/redis-adapter': specifier: ^8.3.0 version: 8.3.0(socket.io-adapter@2.5.6) @@ -80,6 +80,9 @@ importers: jose: specifier: ^6.1.3 version: 6.1.3 + mediasoup: + specifier: ^3.19.14 + version: 3.19.14 mysql2: specifier: ^3.15.3 version: 3.16.0 @@ -107,7 +110,7 @@ importers: version: 10.2.3(chokidar@3.6.0)(typescript@5.9.3) '@nestjs/testing': specifier: ^10.0.0 - version: 10.4.20(@nestjs/common@10.4.20(class-transformer@0.5.1)(class-validator@0.14.3)(reflect-metadata@0.1.14)(rxjs@7.8.2))(@nestjs/core@10.4.20)(@nestjs/platform-express@10.4.20) + version: 10.4.21(@nestjs/common@10.4.21(class-transformer@0.5.1)(class-validator@0.14.3)(reflect-metadata@0.1.14)(rxjs@7.8.2))(@nestjs/core@10.4.21)(@nestjs/platform-express@10.4.21) '@types/cookie-parser': specifier: ^1.4.10 version: 1.4.10(@types/express@4.17.25) @@ -125,10 +128,10 @@ importers: version: 2.0.16 '@typescript-eslint/eslint-plugin': specifier: ^8.50.0 - version: 8.50.0(@typescript-eslint/parser@8.50.0(eslint@8.57.1)(typescript@5.9.3))(eslint@8.57.1)(typescript@5.9.3) + version: 8.52.0(@typescript-eslint/parser@8.52.0(eslint@8.57.1)(typescript@5.9.3))(eslint@8.57.1)(typescript@5.9.3) '@typescript-eslint/parser': specifier: ^8.50.0 - version: 8.50.0(eslint@8.57.1)(typescript@5.9.3) + version: 8.52.0(eslint@8.57.1)(typescript@5.9.3) eslint: specifier: ^8.42.0 version: 8.57.1 @@ -140,7 +143,7 @@ importers: version: 4.4.4(eslint-plugin-import@2.32.0)(eslint@8.57.1) eslint-plugin-import: specifier: ^2.32.0 - version: 2.32.0(@typescript-eslint/parser@8.50.0(eslint@8.57.1)(typescript@5.9.3))(eslint-import-resolver-typescript@4.4.4)(eslint@8.57.1) + version: 2.32.0(@typescript-eslint/parser@8.52.0(eslint@8.57.1)(typescript@5.9.3))(eslint-import-resolver-typescript@4.4.4)(eslint@8.57.1) eslint-plugin-prettier: specifier: ^5.0.0 version: 5.5.4(@types/eslint@9.6.1)(eslint-config-prettier@9.1.2(eslint@8.57.1))(eslint@8.57.1)(prettier@3.7.4) @@ -315,139 +318,139 @@ packages: '@aws-crypto/util@5.2.0': resolution: {integrity: sha512-4RkU9EsI6ZpBve5fseQlGNUWKMa1RLPQ1dnjnQoe07ldfIzcsGb5hC5W0Dm7u423KWzawlrpbjXBrXCEv9zazQ==} - '@aws-sdk/client-s3@3.957.0': - resolution: {integrity: sha512-UvKQ9CpqzJxMjSjFpBaeWFAnR2sQChQH2jJEhsCFkAvrz1KDrU7sjOqUAPUH+ytsP5xghx1AZhfK2rVE/+JS0Q==} + '@aws-sdk/client-s3@3.965.0': + resolution: {integrity: sha512-BTeaaU1iK0BfatTCrtYjNkIHCoZH256qOI18l9bK4z6mVOgpHkYN4RvOu+NnKgyX58n+HWfOuhtKUD4OE33Vdw==} engines: {node: '>=18.0.0'} - '@aws-sdk/client-sso@3.957.0': - resolution: {integrity: sha512-iRdRjd+IpOogqRPt8iNRcg30J53z4rRfMviGwpKgsEa/fx3inCUPOuca3Ap7ZDES0atnEg3KGSJ3V/NQiEJ4BA==} + '@aws-sdk/client-sso@3.965.0': + resolution: {integrity: sha512-iv2tr+n4aZ+nPUFFvG00hISPuEd4DU+1/Q8rPAYKXsM+vEPJ2nAnP5duUOa2fbOLIUCRxX3dcQaQaghVHDHzQw==} engines: {node: '>=18.0.0'} - '@aws-sdk/core@3.957.0': - resolution: {integrity: sha512-DrZgDnF1lQZv75a52nFWs6MExihJF2GZB6ETZRqr6jMwhrk2kbJPUtvgbifwcL7AYmVqHQDJBrR/MqkwwFCpiw==} + '@aws-sdk/core@3.965.0': + resolution: {integrity: sha512-aq9BhQxdHit8UUJ9C0im9TtuKeK0pT6NXmNJxMTCFeStI7GG7ImIsSislg3BZTIifVg1P6VLdzMyz9de85iutQ==} engines: {node: '>=18.0.0'} - '@aws-sdk/crc64-nvme@3.957.0': - resolution: {integrity: sha512-qSwSfI+qBU9HDsd6/4fM9faCxYJx2yDuHtj+NVOQ6XYDWQzFab/hUdwuKZ77Pi6goLF1pBZhJ2azaC2w7LbnTA==} + '@aws-sdk/crc64-nvme@3.965.0': + resolution: {integrity: sha512-9FbIyJ/Zz1AdEIrb0+Pn7wRi+F/0Y566ooepg0hDyHUzRV3ZXKjOlu3wJH3YwTz2UkdwQmldfUos2yDJps7RyA==} engines: {node: '>=18.0.0'} - '@aws-sdk/credential-provider-env@3.957.0': - resolution: {integrity: sha512-475mkhGaWCr+Z52fOOVb/q2VHuNvqEDixlYIkeaO6xJ6t9qR0wpLt4hOQaR6zR1wfZV0SlE7d8RErdYq/PByog==} + '@aws-sdk/credential-provider-env@3.965.0': + resolution: {integrity: sha512-mdGnaIjMxTIjsb70dEj3VsWPWpoq1V5MWzBSfJq2H8zgMBXjn6d5/qHP8HMf53l9PrsgqzMpXGv3Av549A2x1g==} engines: {node: '>=18.0.0'} - '@aws-sdk/credential-provider-http@3.957.0': - resolution: {integrity: sha512-8dS55QHRxXgJlHkEYaCGZIhieCs9NU1HU1BcqQ4RfUdSsfRdxxktqUKgCnBnOOn0oD3PPA8cQOCAVgIyRb3Rfw==} + '@aws-sdk/credential-provider-http@3.965.0': + resolution: {integrity: sha512-YuGQel9EgA/z25oeLM+GYYQS750+8AESvr7ZEmVnRPL0sg+K3DmGqdv+9gFjFd0UkLjTlC/jtbP2cuY6UcPiHQ==} engines: {node: '>=18.0.0'} - '@aws-sdk/credential-provider-ini@3.957.0': - resolution: {integrity: sha512-YuoZmIeE91YIeUfihh8SiSu546KtTvU+4rG5SaL30U9+nGq6P11GRRgqF0ANUyRseLC9ONHt+utar4gbO3++og==} + '@aws-sdk/credential-provider-ini@3.965.0': + resolution: {integrity: sha512-xRo72Prer5s0xYVSCxCymVIRSqrVlevK5cmU0GWq9yJtaBNpnx02jwdJg80t/Ni7pgbkQyFWRMcq38c1tc6M/w==} engines: {node: '>=18.0.0'} - '@aws-sdk/credential-provider-login@3.957.0': - resolution: {integrity: sha512-XcD5NEQDWYk8B4gs89bkwf2d+DNF8oS2NR5RoHJEbX4l8KErVATUjpEYVn6/rAFEktungxlYTnQ5wh0cIQvP5w==} + '@aws-sdk/credential-provider-login@3.965.0': + resolution: {integrity: sha512-43/H8Qku8LHyugbhLo8kjD+eauhybCeVkmrnvWl8bXNHJP7xi1jCdtBQJKKJqiIHZws4MOEwkji8kFdAVRCe6g==} engines: {node: '>=18.0.0'} - '@aws-sdk/credential-provider-node@3.957.0': - resolution: {integrity: sha512-b9FT/7BQcJ001w+3JbTiJXfxHrWvPb7zDvvC1i1FKcNOvyCt3BGu04n4nO/b71a3iBnbfBXI89hCIZQsuLcEgw==} + '@aws-sdk/credential-provider-node@3.965.0': + resolution: {integrity: sha512-cRxmMHF+Zh2lkkkEVduKl+8OQdtg/DhYA69+/7SPSQURlgyjFQGlRQ58B7q8abuNlrGT3sV+UzeOylZpJbV61Q==} engines: {node: '>=18.0.0'} - '@aws-sdk/credential-provider-process@3.957.0': - resolution: {integrity: sha512-/KIz9kadwbeLy6SKvT79W81Y+hb/8LMDyeloA2zhouE28hmne+hLn0wNCQXAAupFFlYOAtZR2NTBs7HBAReJlg==} + '@aws-sdk/credential-provider-process@3.965.0': + resolution: {integrity: sha512-gmkPmdiR0yxnTzLPDb7rwrDhGuCUjtgnj8qWP+m0gSz/W43rR4jRPVEf6DUX2iC+ImQhxo3NFhuB3V42Kzo3TQ==} engines: {node: '>=18.0.0'} - '@aws-sdk/credential-provider-sso@3.957.0': - resolution: {integrity: sha512-gTLPJFOkGtn3tVGglRhCar2oOobK1YctZRAT8nfJr17uaSRoAP46zIIHNYBZZUMqImb0qAHD9Ugm+Zd9sIqxyA==} + '@aws-sdk/credential-provider-sso@3.965.0': + resolution: {integrity: sha512-N01AYvtCqG3Wo/s/LvYt19ity18/FqggiXT+elAs3X9Om/Wfx+hw9G+i7jaDmy+/xewmv8AdQ2SK5Q30dXw/Fw==} engines: {node: '>=18.0.0'} - '@aws-sdk/credential-provider-web-identity@3.957.0': - resolution: {integrity: sha512-x17xMeD7c+rKEsWachGIMifACqkugskrETWz18QDWismFcrmUuOcZu5rUa8s9y1pnITLKUQ1xU/qDLPH52jLlA==} + '@aws-sdk/credential-provider-web-identity@3.965.0': + resolution: {integrity: sha512-T4gMZ2JzXnfxe1oTD+EDGLSxFfk1+WkLZdiHXEMZp8bFI1swP/3YyDFXI+Ib9Uq1JhnAmrCXtOnkicKEhDkdhQ==} engines: {node: '>=18.0.0'} - '@aws-sdk/middleware-bucket-endpoint@3.957.0': - resolution: {integrity: sha512-iczcn/QRIBSpvsdAS/rbzmoBpleX1JBjXvCynMbDceVLBIcVrwT1hXECrhtIC2cjh4HaLo9ClAbiOiWuqt+6MA==} + '@aws-sdk/middleware-bucket-endpoint@3.965.0': + resolution: {integrity: sha512-gbdv3Dl8l8xmg4oH60fXvfDyTxfx28w5/Hxdymx3vurM07tAyd4qld8zEXejnSpraTo45QcHRtk5auELIMfeag==} engines: {node: '>=18.0.0'} - '@aws-sdk/middleware-expect-continue@3.957.0': - resolution: {integrity: sha512-AlbK3OeVNwZZil0wlClgeI/ISlOt/SPUxBsIns876IFaVu/Pj3DgImnYhpcJuFRek4r4XM51xzIaGQXM6GDHGg==} + '@aws-sdk/middleware-expect-continue@3.965.0': + resolution: {integrity: sha512-UBxVytsmhEmFwkBnt+aV0eAJ7uc+ouNokCqMBrQ7Oc5A77qhlcHfOgXIKz2SxqsiYTsDq+a0lWFM/XpyRWraqA==} engines: {node: '>=18.0.0'} - '@aws-sdk/middleware-flexible-checksums@3.957.0': - resolution: {integrity: sha512-iJpeVR5V8se1hl2pt+k8bF/e9JO4KWgPCMjg8BtRspNtKIUGy7j6msYvbDixaKZaF2Veg9+HoYcOhwnZumjXSA==} + '@aws-sdk/middleware-flexible-checksums@3.965.0': + resolution: {integrity: sha512-5rzEW08trcpHMe6jkQyYc4PL1KG/H7BbnySFSzhih+r/gktQEiE36sb1BNf7av9I0Vk2Ccmt7wocB5PIT7GDkQ==} engines: {node: '>=18.0.0'} - '@aws-sdk/middleware-host-header@3.957.0': - resolution: {integrity: sha512-BBgKawVyfQZglEkNTuBBdC3azlyqNXsvvN4jPkWAiNYcY0x1BasaJFl+7u/HisfULstryweJq/dAvIZIxzlZaA==} + '@aws-sdk/middleware-host-header@3.965.0': + resolution: {integrity: sha512-SfpSYqoPOAmdb3DBsnNsZ0vix+1VAtkUkzXM79JL3R5IfacpyKE2zytOgVAQx/FjhhlpSTwuXd+LRhUEVb3MaA==} engines: {node: '>=18.0.0'} - '@aws-sdk/middleware-location-constraint@3.957.0': - resolution: {integrity: sha512-y8/W7TOQpmDJg/fPYlqAhwA4+I15LrS7TwgUEoxogtkD8gfur9wFMRLT8LCyc9o4NMEcAnK50hSb4+wB0qv6tQ==} + '@aws-sdk/middleware-location-constraint@3.965.0': + resolution: {integrity: sha512-07T1rwAarQs33mVg5U28AsSdLB5JUXu9yBTBmspFGajKVsEahIyntf53j9mAXF1N2KR0bNdP0J4A0kst4t43UQ==} engines: {node: '>=18.0.0'} - '@aws-sdk/middleware-logger@3.957.0': - resolution: {integrity: sha512-w1qfKrSKHf9b5a8O76yQ1t69u6NWuBjr5kBX+jRWFx/5mu6RLpqERXRpVJxfosbep7k3B+DSB5tZMZ82GKcJtQ==} + '@aws-sdk/middleware-logger@3.965.0': + resolution: {integrity: sha512-gjUvJRZT1bUABKewnvkj51LAynFrfz2h5DYAg5/2F4Utx6UOGByTSr9Rq8JCLbURvvzAbCtcMkkIJRxw+8Zuzw==} engines: {node: '>=18.0.0'} - '@aws-sdk/middleware-recursion-detection@3.957.0': - resolution: {integrity: sha512-D2H/WoxhAZNYX+IjkKTdOhOkWQaK0jjJrDBj56hKjU5c9ltQiaX/1PqJ4dfjHntEshJfu0w+E6XJ+/6A6ILBBA==} + '@aws-sdk/middleware-recursion-detection@3.965.0': + resolution: {integrity: sha512-6dvD+18Ni14KCRu+tfEoNxq1sIGVp9tvoZDZ7aMvpnA7mDXuRLrOjRQ/TAZqXwr9ENKVGyxcPl0cRK8jk1YWjA==} engines: {node: '>=18.0.0'} - '@aws-sdk/middleware-sdk-s3@3.957.0': - resolution: {integrity: sha512-5B2qY2nR2LYpxoQP0xUum5A1UNvH2JQpLHDH1nWFNF/XetV7ipFHksMxPNhtJJ6ARaWhQIDXfOUj0jcnkJxXUg==} + '@aws-sdk/middleware-sdk-s3@3.965.0': + resolution: {integrity: sha512-dXEgnojaaVRl+OlOx35mg3rYEbfffIN4X6tLmIfDnaKz0hMaDMvsE9jJXb/vBvokbdO1sVB27/2FEM4ttLSLnw==} engines: {node: '>=18.0.0'} - '@aws-sdk/middleware-ssec@3.957.0': - resolution: {integrity: sha512-qwkmrK0lizdjNt5qxl4tHYfASh8DFpHXM1iDVo+qHe+zuslfMqQEGRkzxS8tJq/I+8F0c6v3IKOveKJAfIvfqQ==} + '@aws-sdk/middleware-ssec@3.965.0': + resolution: {integrity: sha512-dke++CTw26y+a2D1DdVuZ4+2TkgItdx6TeuE0zOl4lsqXGvTBUG4eaIZalt7ZOAW5ys2pbDOk1bPuh4opoD3pQ==} engines: {node: '>=18.0.0'} - '@aws-sdk/middleware-user-agent@3.957.0': - resolution: {integrity: sha512-50vcHu96XakQnIvlKJ1UoltrFODjsq2KvtTgHiPFteUS884lQnK5VC/8xd1Msz/1ONpLMzdCVproCQqhDTtMPQ==} + '@aws-sdk/middleware-user-agent@3.965.0': + resolution: {integrity: sha512-RBEYVGgu/WeAt+H/qLrGc+t8LqAUkbyvh3wBfTiuAD+uBcWsKnvnB1iSBX75FearC0fmoxzXRUc0PMxMdqpjJQ==} engines: {node: '>=18.0.0'} - '@aws-sdk/nested-clients@3.957.0': - resolution: {integrity: sha512-PZUFtaUTSZWO+mbgQGWSiwz3EqedsuKNb7Xoxjzh5rfJE352DD4/jScQEhVPxvdLw62IK9b5UDu5kZlxzBs9Ow==} + '@aws-sdk/nested-clients@3.965.0': + resolution: {integrity: sha512-muNVUjUEU+/KLFrLzQ8PMXyw4+a/MP6t4GIvwLtyx/kH0rpSy5s0YmqacMXheuIe6F/5QT8uksXGNAQenitkGQ==} engines: {node: '>=18.0.0'} - '@aws-sdk/region-config-resolver@3.957.0': - resolution: {integrity: sha512-V8iY3blh8l2iaOqXWW88HbkY5jDoWjH56jonprG/cpyqqCnprvpMUZWPWYJoI8rHRf2bqzZeql1slxG6EnKI7A==} + '@aws-sdk/region-config-resolver@3.965.0': + resolution: {integrity: sha512-RoMhu9ly2B0coxn8ctXosPP2WmDD0MkQlZGLjoYHQUOCBmty5qmCxOqBmBDa6wbWbB8xKtMQ/4VXloQOgzjHXg==} engines: {node: '>=18.0.0'} - '@aws-sdk/s3-request-presigner@3.957.0': - resolution: {integrity: sha512-8U3c4Bmq1W0nQBZ1zbXQmbr4AH2iQIChRXy+fhrwjHHWPL3ek/2gDQR/cAiDAtlvXVIjWMqLJ1Ku45dGhOIbaw==} + '@aws-sdk/s3-request-presigner@3.965.0': + resolution: {integrity: sha512-aQ9vvXjeoQsAaRHS18l8doY+E/6mmNMSDMU6eJsSUDgvgGRMHhsKjiVh7DJGbZRRogdrES4KAfx6raIB4kBz5Q==} engines: {node: '>=18.0.0'} - '@aws-sdk/signature-v4-multi-region@3.957.0': - resolution: {integrity: sha512-t6UfP1xMUigMMzHcb7vaZcjv7dA2DQkk9C/OAP1dKyrE0vb4lFGDaTApi17GN6Km9zFxJthEMUbBc7DL0hq1Bg==} + '@aws-sdk/signature-v4-multi-region@3.965.0': + resolution: {integrity: sha512-hgbAThbsUrWtNpFBQxzXevIfd5Qgr4TLbXY1AIbmpSX9fPVC114pdieRMpopJ0fYaJ7v5/blTiS6wzVdXleZ/w==} engines: {node: '>=18.0.0'} - '@aws-sdk/token-providers@3.957.0': - resolution: {integrity: sha512-oSwo3BZ6gcvhjTg036V0UQmtENUeNwfCU35iDckX961CdI1alQ3TKRWLzKrwvXCbrOx+bZsuA1PHsTbNhI/+Fw==} + '@aws-sdk/token-providers@3.965.0': + resolution: {integrity: sha512-aR0qxg0b8flkXJVE+CM1gzo7uJ57md50z2eyCwofC0QIz5Y0P7/7vvb9/dmUQt6eT9XRN5iRcUqq2IVxVDvJOw==} engines: {node: '>=18.0.0'} - '@aws-sdk/types@3.957.0': - resolution: {integrity: sha512-wzWC2Nrt859ABk6UCAVY/WYEbAd7FjkdrQL6m24+tfmWYDNRByTJ9uOgU/kw9zqLCAwb//CPvrJdhqjTznWXAg==} + '@aws-sdk/types@3.965.0': + resolution: {integrity: sha512-jvodoJdMavvg8faN7co58vVJRO5MVep4JFPRzUNCzpJ98BDqWDk/ad045aMJcmxkLzYLS2UAnUmqjJ/tUPNlzQ==} engines: {node: '>=18.0.0'} - '@aws-sdk/util-arn-parser@3.957.0': - resolution: {integrity: sha512-Aj6m+AyrhWyg8YQ4LDPg2/gIfGHCEcoQdBt5DeSFogN5k9mmJPOJ+IAmNSWmWRjpOxEy6eY813RNDI6qS97M0g==} + '@aws-sdk/util-arn-parser@3.965.0': + resolution: {integrity: sha512-bNGKr5Tct28jGLkL8xIkGu7swpDgBpkTVbGaofhzr/X80iclbOv656RGxhMpDvmc4S9UuQnqLRXyceNFNF2V7Q==} engines: {node: '>=18.0.0'} - '@aws-sdk/util-endpoints@3.957.0': - resolution: {integrity: sha512-xwF9K24mZSxcxKS3UKQFeX/dPYkEps9wF1b+MGON7EvnbcucrJGyQyK1v1xFPn1aqXkBTFi+SZaMRx5E5YCVFw==} + '@aws-sdk/util-endpoints@3.965.0': + resolution: {integrity: sha512-WqSCB0XIsGUwZWvrYkuoofi2vzoVHqyeJ2kN+WyoOsxPLTiQSBIoqm/01R/qJvoxwK/gOOF7su9i84Vw2NQQpQ==} engines: {node: '>=18.0.0'} - '@aws-sdk/util-format-url@3.957.0': - resolution: {integrity: sha512-Yyo/tlc0iGFGTPPkuxub1uRAv6XrnVnvSNjslZh5jIYA8GZoeEFPgJa3Qdu0GUS/YwoK8GOLnnaL9h/eH5LDJQ==} + '@aws-sdk/util-format-url@3.965.0': + resolution: {integrity: sha512-KiplV4xYGXdNCcz5eRP8WfAejT5EkE2gQxC4IY6WsuxYprzQKsnGaAzEQ+giR5GgQLIRBkPaWT0xHEYkMiCQ1Q==} engines: {node: '>=18.0.0'} - '@aws-sdk/util-locate-window@3.957.0': - resolution: {integrity: sha512-nhmgKHnNV9K+i9daumaIz8JTLsIIML9PE/HUks5liyrjUzenjW/aHoc7WJ9/Td/gPZtayxFnXQSJRb/fDlBuJw==} + '@aws-sdk/util-locate-window@3.965.0': + resolution: {integrity: sha512-9LJFand4bIoOjOF4x3wx0UZYiFZRo4oUauxQSiEX2dVg+5qeBOJSjp2SeWykIE6+6frCZ5wvWm2fGLK8D32aJw==} engines: {node: '>=18.0.0'} - '@aws-sdk/util-user-agent-browser@3.957.0': - resolution: {integrity: sha512-exueuwxef0lUJRnGaVkNSC674eAiWU07ORhxBnevFFZEKisln+09Qrtw823iyv5I1N8T+wKfh95xvtWQrNKNQw==} + '@aws-sdk/util-user-agent-browser@3.965.0': + resolution: {integrity: sha512-Xiza/zMntQGpkd2dETQeAK8So1pg5+STTzpcdGWxj5q0jGO5ayjqT/q1Q7BrsX5KIr6PvRkl9/V7lLCv04wGjQ==} - '@aws-sdk/util-user-agent-node@3.957.0': - resolution: {integrity: sha512-ycbYCwqXk4gJGp0Oxkzf2KBeeGBdTxz559D41NJP8FlzSej1Gh7Rk40Zo6AyTfsNWkrl/kVi1t937OIzC5t+9Q==} + '@aws-sdk/util-user-agent-node@3.965.0': + resolution: {integrity: sha512-kokIHUfNT3/P55E4fUJJrFHuuA9BbjFKUIxoLrd3UaRfdafT0ScRfg2eaZie6arf60EuhlUIZH0yALxttMEjxQ==} engines: {node: '>=18.0.0'} peerDependencies: aws-crt: '>=1.0.0' @@ -455,12 +458,12 @@ packages: aws-crt: optional: true - '@aws-sdk/xml-builder@3.957.0': - resolution: {integrity: sha512-Ai5iiQqS8kJ5PjzMhWcLKN0G2yasAkvpnPlq2EnqlIMdB48HsizElt62qcktdxp4neRMyGkFq4NzgmDbXnhRiA==} + '@aws-sdk/xml-builder@3.965.0': + resolution: {integrity: sha512-Tcod25/BTupraQwtb+Q+GX8bmEZfxIFjjJ/AvkhUZsZlkPeVluzq1uu3Oeqf145DCdMjzLIN6vab5MrykbDP+g==} engines: {node: '>=18.0.0'} - '@aws/lambda-invoke-store@0.2.2': - resolution: {integrity: sha512-C0NBLsIqzDIae8HFw9YIrIBsbc0xTiOtt7fAukGPnqQ/+zZNaq+4jhuccltK0QuWHBnNm/a6kLIRA6GFiM10eg==} + '@aws/lambda-invoke-store@0.2.3': + resolution: {integrity: sha512-oLvsaPMTBejkkmHhjf09xTgk71mOqyr/409NKhRIL08If7AhVfUsJhVsx386uJaqNd42v9kWamQ9lFbkoC2dYw==} engines: {node: '>=18.0.0'} '@babel/code-frame@7.27.1': @@ -628,8 +631,8 @@ packages: '@bcoe/v8-coverage@0.2.3': resolution: {integrity: sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==} - '@borewit/text-codec@0.1.1': - resolution: {integrity: sha512-5L/uBxmjaCIX5h8Z+uu+kA9BQLkc/Wl06UGR5ajNRxu+/XjonB5i8JpgFMrPj3LXTCPA0pv8yxUvbUi+QthGGA==} + '@borewit/text-codec@0.2.1': + resolution: {integrity: sha512-k7vvKPbf7J2fZ5klGRD9AeKfUvojuZIQ3BT5u7Jfv+puwXkUBUT5PVyMDfJZpy30CBDXGMgw7fguK/lpOMBvgw==} '@colors/colors@1.5.0': resolution: {integrity: sha512-ooWCrlZP11i8GImSjTHYHLkvFDP48nS4+204nGb1RiX/WXYHmJA2III9/e2DWVabCESdW7hBAEzHRqUn9OUVvQ==} @@ -639,11 +642,11 @@ packages: resolution: {integrity: sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==} engines: {node: '>=12'} - '@emnapi/core@1.7.1': - resolution: {integrity: sha512-o1uhUASyo921r2XtHYOHy7gdkGLge8ghBEQHMWmyJFoXlpU58kIrhhN3w26lpQb6dspetweapMn2CSNwQ8I4wg==} + '@emnapi/core@1.8.1': + resolution: {integrity: sha512-AvT9QFpxK0Zd8J0jopedNm+w/2fIzvtPKPjqyw9jwvBaReTTqPBk9Hixaz7KbjimP+QNz605/XnjFcDAL2pqBg==} - '@emnapi/runtime@1.7.1': - resolution: {integrity: sha512-PVtJr5CmLwYAU9PZDMITZoR5iAOShYREoR45EyyLrbntV50mdePTgUn4AmOw90Ifcj+x2kRjdzr1HP3RrNiHGA==} + '@emnapi/runtime@1.8.1': + resolution: {integrity: sha512-mehfKSMWjjNol8659Z8KxEMrdSJDDot5SXMq00dM8BN4o+CLNXQ0xH2V7EchNHV4RmbZLmmPdEaXZc5H2FXmDg==} '@emnapi/wasi-threads@1.1.0': resolution: {integrity: sha512-WI0DdZ8xFSbgMjR1sFsKABJ/C5OnRrjT06JXbZKexJGrDuPTzZdDYfFlsgcCXCyf+suG5QU2e/y1Wo2V/OapLQ==} @@ -651,8 +654,8 @@ packages: '@epic-web/invariant@1.0.0': resolution: {integrity: sha512-lrTPqgvfFQtR/eY/qkIzp98OGdNJu0m5ji3q/nJI8v3SXkRKEnWiOxMmbvcSoAIzv/cGiuvRy57k4suKQSAdwA==} - '@eslint-community/eslint-utils@4.9.0': - resolution: {integrity: sha512-ayVFHdtZ+hsq1t2Dy24wCmGXGe4q9Gu3smhLYALJrr473ZH27MsnSL+LKUlimp4BWJqMDMLmPpx/Q9R3OAlL4g==} + '@eslint-community/eslint-utils@4.9.1': + resolution: {integrity: sha512-phrYmNiYppR7znFEdqgfWHXR6NCkZEK7hwWDHZUjit/2/U0r6XvkDl0SYnoM51Hq7FhCGdLDT6zxCCOY1hexsQ==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} peerDependencies: eslint: ^6.0.0 || ^7.0.0 || >=8.0.0 @@ -675,18 +678,36 @@ packages: peerDependencies: graphql: ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0 + '@graphql-tools/merge@9.1.7': + resolution: {integrity: sha512-Y5E1vTbTabvcXbkakdFUt4zUIzB1fyaEnVmIWN0l0GMed2gdD01TpZWLUm4RNAxpturvolrb24oGLQrBbPLSoQ==} + engines: {node: '>=16.0.0'} + peerDependencies: + graphql: ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0 + '@graphql-tools/schema@10.0.30': resolution: {integrity: sha512-yPXU17uM/LR90t92yYQqn9mAJNOVZJc0nQtYeZyZeQZeQjwIGlTubvvoDL0fFVk+wZzs4YQOgds2NwSA4npodA==} engines: {node: '>=16.0.0'} peerDependencies: graphql: ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0 + '@graphql-tools/schema@10.0.31': + resolution: {integrity: sha512-ZewRgWhXef6weZ0WiP7/MV47HXiuFbFpiDUVLQl6mgXsWSsGELKFxQsyUCBos60Qqy1JEFAIu3Ns6GGYjGkqkQ==} + engines: {node: '>=16.0.0'} + peerDependencies: + graphql: ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0 + '@graphql-tools/utils@10.11.0': resolution: {integrity: sha512-iBFR9GXIs0gCD+yc3hoNswViL1O5josI33dUqiNStFI/MHLCEPduasceAcazRH77YONKNiviHBV8f7OgcT4o2Q==} engines: {node: '>=16.0.0'} peerDependencies: graphql: ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0 + '@graphql-tools/utils@11.0.0': + resolution: {integrity: sha512-bM1HeZdXA2C3LSIeLOnH/bcqSgbQgKEDrjxODjqi3y58xai2TkNrtYcQSoWzGbt9VMN1dORGjR7Vem8SPnUFQA==} + engines: {node: '>=16.0.0'} + peerDependencies: + graphql: ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0 + '@graphql-typed-document-node/core@3.2.0': resolution: {integrity: sha512-mB9oAsNCm9aM3/SOv4YtBMqZbYj10R7dkq8byBqxGY/ncFwhf2oQzMV+LCRlWoDSEBJ3COiR1yeDvMtsoOsuFQ==} peerDependencies: @@ -709,6 +730,10 @@ packages: resolution: {integrity: sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==} engines: {node: '>=12'} + '@isaacs/fs-minipass@4.0.1': + resolution: {integrity: sha512-wgm9Ehl2jpeqP3zw/7mo3kRHFp5MEDhqAdwy1fTGkHAwnkGOVsgpvQhL8B5n1qlb01jV3n/bI0ZfZp5lWA1k4w==} + engines: {node: '>=18.0.0'} + '@istanbuljs/load-nyc-config@1.1.0': resolution: {integrity: sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ==} engines: {node: '>=8'} @@ -858,8 +883,8 @@ packages: '@swc/core': optional: true - '@nestjs/common@10.4.20': - resolution: {integrity: sha512-hxJxZF7jcKGuUzM9EYbuES80Z/36piJbiqmPy86mk8qOn5gglFebBTvcx7PWVbRNSb4gngASYnefBj/Y2HAzpQ==} + '@nestjs/common@10.4.21': + resolution: {integrity: sha512-2nabPCrq6HAc6PlZQsdDaV16ur7rs8Z8SH/rewS0SqbrvV6hgC/D5IPjVt4NvX7UjWKapqq+bymicuiZjP5WlQ==} peerDependencies: class-transformer: '*' class-validator: '*' @@ -877,8 +902,8 @@ packages: '@nestjs/common': ^10.0.0 || ^11.0.0 rxjs: ^7.1.0 - '@nestjs/core@10.4.20': - resolution: {integrity: sha512-kRdtyKA3+Tu70N3RQ4JgmO1E3LzAMs/eppj7SfjabC7TgqNWoS4RLhWl4BqmsNVmjj6D5jgfPVtHtgYkU3AfpQ==} + '@nestjs/core@10.4.21': + resolution: {integrity: sha512-MhiSGplB4TkadceA7opn/NaZmJhwYYNdB8nS8I29nLNx3vU+8aGHBiueZgcphEVDETZJSfc2VA5Mn/FC3JcsrA==} peerDependencies: '@nestjs/common': ^10.0.0 '@nestjs/microservices': ^10.0.0 @@ -928,17 +953,17 @@ packages: class-validator: optional: true - '@nestjs/platform-express@10.4.20': - resolution: {integrity: sha512-rh97mX3rimyf4xLMLHuTOBKe6UD8LOJ14VlJ1F/PTd6C6ZK9Ak6EHuJvdaGcSFQhd3ZMBh3I6CuujKGW9pNdIg==} + '@nestjs/platform-express@10.4.21': + resolution: {integrity: sha512-xIsa4h+oKf4zrHpTWN2i0gYkGaXewDqv4+KCatI1+aWoZKScFdoI82MFfuzq+z/EBpnVP2ABGqvPJWu+ZhKYvQ==} peerDependencies: '@nestjs/common': ^10.0.0 '@nestjs/core': ^10.0.0 - '@nestjs/platform-socket.io@11.1.11': - resolution: {integrity: sha512-0z6pLg9CuTXtz7q2lRZoPOU94DN28OTa39f4cQrlZysKA6QrKM7w7z6xqb4g32qjF+LQHFNRmMJtE/pLrxBaig==} + '@nestjs/platform-socket.io@10.4.21': + resolution: {integrity: sha512-3nIwx0Bi7I4yTrmqLi2rEnk2PsGyLoA8JanP7TGyoUazs4QelWeIkcxDlgiD7WphJAv0BsLCNkfnfS/3zOFfSA==} peerDependencies: - '@nestjs/common': ^11.0.0 - '@nestjs/websockets': ^11.0.0 + '@nestjs/common': ^10.0.0 + '@nestjs/websockets': ^10.0.0 rxjs: ^7.1.0 '@nestjs/schematics@10.2.3': @@ -946,8 +971,8 @@ packages: peerDependencies: typescript: '>=4.8.2' - '@nestjs/testing@10.4.20': - resolution: {integrity: sha512-nMkRDukDKskdPruM6EsgMq7yJua+CPZM6I6FrLP8yXw8BiVSPv9Nm0CtcGGwt3kgZF9hfxKjGqLjsvVBsv6Vfw==} + '@nestjs/testing@10.4.21': + resolution: {integrity: sha512-mQyJvrJ4mA9nukx+zXafh0iLtbGmwalnGWdoTih6cKtANGewIVsqgfSpuxwzyR4d42uc5jqgBulEZszmUFQ/5A==} peerDependencies: '@nestjs/common': ^10.0.0 '@nestjs/core': ^10.0.0 @@ -959,12 +984,12 @@ packages: '@nestjs/platform-express': optional: true - '@nestjs/websockets@11.1.11': - resolution: {integrity: sha512-apuP7C/gtMBIYNgA8IWt75GTZeWya5JQCnrLZFcOu+IZt00j9Xd/Bm7hbj/Qr/JVoM/7q6c/4p4oOZtBGx4aeA==} + '@nestjs/websockets@10.4.21': + resolution: {integrity: sha512-LOAF7q2c9Aa7mpSzVwCNBb7DzXfXfjUTtqKxz0xaIefHUw+y1Uhpxw39zXa3ki1SlsitNVdEohIazfRXcrLDJQ==} peerDependencies: - '@nestjs/common': ^11.0.0 - '@nestjs/core': ^11.0.0 - '@nestjs/platform-socket.io': ^11.0.0 + '@nestjs/common': ^10.0.0 + '@nestjs/core': ^10.0.0 + '@nestjs/platform-socket.io': ^10.0.0 reflect-metadata: ^0.1.12 || ^0.2.0 rxjs: ^7.1.0 peerDependenciesMeta: @@ -1093,8 +1118,8 @@ packages: resolution: {integrity: sha512-HAGoUAFYsUkoSckuKbCPayECeMim8pOu+yLy1zOxt1sifzEbrsRpYa+mKcMdiHKMeiqOibyPG0sFJnmaV/OGEg==} engines: {node: '>=18.0.0'} - '@smithy/core@3.20.0': - resolution: {integrity: sha512-WsSHCPq/neD5G/MkK4csLI5Y5Pkd9c1NMfpYEKeghSGaD4Ja1qLIohRQf2D5c1Uy5aXp76DeKHkzWZ9KAlHroQ==} + '@smithy/core@3.20.1': + resolution: {integrity: sha512-wOboSEdQ85dbKAJ0zL+wQ6b0HTSBRhtGa0PYKysQXkRg+vK0tdCRRVruiFM2QMprkOQwSYOnwF4og96PAaEGag==} engines: {node: '>=18.0.0'} '@smithy/credential-provider-imds@4.2.7': @@ -1157,12 +1182,12 @@ packages: resolution: {integrity: sha512-GszfBfCcvt7kIbJ41LuNa5f0wvQCHhnGx/aDaZJCCT05Ld6x6U2s0xsc/0mBFONBZjQJp2U/0uSJ178OXOwbhg==} engines: {node: '>=18.0.0'} - '@smithy/middleware-endpoint@4.4.1': - resolution: {integrity: sha512-gpLspUAoe6f1M6H0u4cVuFzxZBrsGZmjx2O9SigurTx4PbntYa4AJ+o0G0oGm1L2oSX6oBhcGHwrfJHup2JnJg==} + '@smithy/middleware-endpoint@4.4.2': + resolution: {integrity: sha512-mqpAdux0BNmZu/SqkFhQEnod4fX23xxTvU2LUpmKp0JpSI+kPYCiHJMmzREr8yxbNxKL2/DU1UZm9i++ayU+2g==} engines: {node: '>=18.0.0'} - '@smithy/middleware-retry@4.4.17': - resolution: {integrity: sha512-MqbXK6Y9uq17h+4r0ogu/sBT6V/rdV+5NvYL7ZV444BKfQygYe8wAhDrVXagVebN6w2RE0Fm245l69mOsPGZzg==} + '@smithy/middleware-retry@4.4.18': + resolution: {integrity: sha512-E5hulijA59nBk/zvcwVMaS7FG7Y4l6hWA9vrW018r+8kiZef4/ETQaPI4oY+3zsy9f6KqDv3c4VKtO4DwwgpCg==} engines: {node: '>=18.0.0'} '@smithy/middleware-serde@4.2.8': @@ -1209,8 +1234,8 @@ packages: resolution: {integrity: sha512-9oNUlqBlFZFOSdxgImA6X5GFuzE7V2H7VG/7E70cdLhidFbdtvxxt81EHgykGK5vq5D3FafH//X+Oy31j3CKOg==} engines: {node: '>=18.0.0'} - '@smithy/smithy-client@4.10.2': - resolution: {integrity: sha512-D5z79xQWpgrGpAHb054Fn2CCTQZpog7JELbVQ6XAvXs5MNKWf28U9gzSBlJkOyMl9LA1TZEjRtwvGXfP0Sl90g==} + '@smithy/smithy-client@4.10.3': + resolution: {integrity: sha512-EfECiO/0fAfb590LBnUe7rI5ux7XfquQ8LBzTe7gxw0j9QW/q8UT/EHWHlxV/+jhQ3+Ssga9uUYXCQgImGMbNg==} engines: {node: '>=18.0.0'} '@smithy/types@4.11.0': @@ -1245,12 +1270,12 @@ packages: resolution: {integrity: sha512-YEjpl6XJ36FTKmD+kRJJWYvrHeUvm5ykaUS5xK+6oXffQPHeEM4/nXlZPe+Wu0lsgRUcNZiliYNh/y7q9c2y6Q==} engines: {node: '>=18.0.0'} - '@smithy/util-defaults-mode-browser@4.3.16': - resolution: {integrity: sha512-/eiSP3mzY3TsvUOYMeL4EqUX6fgUOj2eUOU4rMMgVbq67TiRLyxT7Xsjxq0bW3OwuzK009qOwF0L2OgJqperAQ==} + '@smithy/util-defaults-mode-browser@4.3.17': + resolution: {integrity: sha512-dwN4GmivYF1QphnP3xJESXKtHvkkvKHSZI8GrSKMVoENVSKW2cFPRYC4ZgstYjUHdR3zwaDkIaTDIp26JuY7Cw==} engines: {node: '>=18.0.0'} - '@smithy/util-defaults-mode-node@4.2.19': - resolution: {integrity: sha512-3a4+4mhf6VycEJyHIQLypRbiwG6aJvbQAeRAVXydMmfweEPnLLabRbdyo/Pjw8Rew9vjsh5WCdhmDaHkQnhhhA==} + '@smithy/util-defaults-mode-node@4.2.20': + resolution: {integrity: sha512-VD/I4AEhF1lpB3B//pmOIMBNLMrtdMXwy9yCOfa2QkJGDr63vH3RqPbSAKzoGMov3iryCxTXCxSsyGmEB8PDpg==} engines: {node: '>=18.0.0'} '@smithy/util-endpoints@3.2.7': @@ -1374,6 +1399,9 @@ packages: '@types/http-errors@2.0.5': resolution: {integrity: sha512-r8Tayk8HJnX0FztbZN7oVqGccWgw98T/0neJphO91KkmOzug1KkofZURD4UaD5uH8AqcFLfdPErnBod0u71/qg==} + '@types/ini@4.1.1': + resolution: {integrity: sha512-MIyNUZipBTbyUNnhvuXJTY7B6qNI78meck9Jbv3wk0OgNwRyOOVEKDutAkOs1snB/tx0FafyR6/SN4Ps0hZPeg==} + '@types/istanbul-lib-coverage@2.0.6': resolution: {integrity: sha512-2QF/t/auWm0lsy8XtKVPG19v3sSOQlJe/YHZgfjb/KBBHOGSV+J2q/S671rcq9uTBrLAXmZpqJiaQbMT+zNU1w==} @@ -1437,63 +1465,63 @@ packages: '@types/yargs@17.0.35': resolution: {integrity: sha512-qUHkeCyQFxMXg79wQfTtfndEC+N9ZZg76HJftDJp+qH2tV7Gj4OJi7l+PiWwJ+pWtW8GwSmqsDj/oymhrTWXjg==} - '@typescript-eslint/eslint-plugin@8.50.0': - resolution: {integrity: sha512-O7QnmOXYKVtPrfYzMolrCTfkezCJS9+ljLdKW/+DCvRsc3UAz+sbH6Xcsv7p30+0OwUbeWfUDAQE0vpabZ3QLg==} + '@typescript-eslint/eslint-plugin@8.52.0': + resolution: {integrity: sha512-okqtOgqu2qmZJ5iN4TWlgfF171dZmx2FzdOv2K/ixL2LZWDStL8+JgQerI2sa8eAEfoydG9+0V96m7V+P8yE1Q==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: - '@typescript-eslint/parser': ^8.50.0 + '@typescript-eslint/parser': ^8.52.0 eslint: ^8.57.0 || ^9.0.0 typescript: '>=4.8.4 <6.0.0' - '@typescript-eslint/parser@8.50.0': - resolution: {integrity: sha512-6/cmF2piao+f6wSxUsJLZjck7OQsYyRtcOZS02k7XINSNlz93v6emM8WutDQSXnroG2xwYlEVHJI+cPA7CPM3Q==} + '@typescript-eslint/parser@8.52.0': + resolution: {integrity: sha512-iIACsx8pxRnguSYhHiMn2PvhvfpopO9FXHyn1mG5txZIsAaB6F0KwbFnUQN3KCiG3Jcuad/Cao2FAs1Wp7vAyg==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: eslint: ^8.57.0 || ^9.0.0 typescript: '>=4.8.4 <6.0.0' - '@typescript-eslint/project-service@8.50.0': - resolution: {integrity: sha512-Cg/nQcL1BcoTijEWyx4mkVC56r8dj44bFDvBdygifuS20f3OZCHmFbjF34DPSi07kwlFvqfv/xOLnJ5DquxSGQ==} + '@typescript-eslint/project-service@8.52.0': + resolution: {integrity: sha512-xD0MfdSdEmeFa3OmVqonHi+Cciab96ls1UhIF/qX/O/gPu5KXD0bY9lu33jj04fjzrXHcuvjBcBC+D3SNSadaw==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: typescript: '>=4.8.4 <6.0.0' - '@typescript-eslint/scope-manager@8.50.0': - resolution: {integrity: sha512-xCwfuCZjhIqy7+HKxBLrDVT5q/iq7XBVBXLn57RTIIpelLtEIZHXAF/Upa3+gaCpeV1NNS5Z9A+ID6jn50VD4A==} + '@typescript-eslint/scope-manager@8.52.0': + resolution: {integrity: sha512-ixxqmmCcc1Nf8S0mS0TkJ/3LKcC8mruYJPOU6Ia2F/zUUR4pApW7LzrpU3JmtePbRUTes9bEqRc1Gg4iyRnDzA==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@typescript-eslint/tsconfig-utils@8.50.0': - resolution: {integrity: sha512-vxd3G/ybKTSlm31MOA96gqvrRGv9RJ7LGtZCn2Vrc5htA0zCDvcMqUkifcjrWNNKXHUU3WCkYOzzVSFBd0wa2w==} + '@typescript-eslint/tsconfig-utils@8.52.0': + resolution: {integrity: sha512-jl+8fzr/SdzdxWJznq5nvoI7qn2tNYV/ZBAEcaFMVXf+K6jmXvAFrgo/+5rxgnL152f//pDEAYAhhBAZGrVfwg==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: typescript: '>=4.8.4 <6.0.0' - '@typescript-eslint/type-utils@8.50.0': - resolution: {integrity: sha512-7OciHT2lKCewR0mFoBrvZJ4AXTMe/sYOe87289WAViOocEmDjjv8MvIOT2XESuKj9jp8u3SZYUSh89QA4S1kQw==} + '@typescript-eslint/type-utils@8.52.0': + resolution: {integrity: sha512-JD3wKBRWglYRQkAtsyGz1AewDu3mTc7NtRjR/ceTyGoPqmdS5oCdx/oZMWD5Zuqmo6/MpsYs0wp6axNt88/2EQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: eslint: ^8.57.0 || ^9.0.0 typescript: '>=4.8.4 <6.0.0' - '@typescript-eslint/types@8.50.0': - resolution: {integrity: sha512-iX1mgmGrXdANhhITbpp2QQM2fGehBse9LbTf0sidWK6yg/NE+uhV5dfU1g6EYPlcReYmkE9QLPq/2irKAmtS9w==} + '@typescript-eslint/types@8.52.0': + resolution: {integrity: sha512-LWQV1V4q9V4cT4H5JCIx3481iIFxH1UkVk+ZkGGAV1ZGcjGI9IoFOfg3O6ywz8QqCDEp7Inlg6kovMofsNRaGg==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@typescript-eslint/typescript-estree@8.50.0': - resolution: {integrity: sha512-W7SVAGBR/IX7zm1t70Yujpbk+zdPq/u4soeFSknWFdXIFuWsBGBOUu/Tn/I6KHSKvSh91OiMuaSnYp3mtPt5IQ==} + '@typescript-eslint/typescript-estree@8.52.0': + resolution: {integrity: sha512-XP3LClsCc0FsTK5/frGjolyADTh3QmsLp6nKd476xNI9CsSsLnmn4f0jrzNoAulmxlmNIpeXuHYeEQv61Q6qeQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: typescript: '>=4.8.4 <6.0.0' - '@typescript-eslint/utils@8.50.0': - resolution: {integrity: sha512-87KgUXET09CRjGCi2Ejxy3PULXna63/bMYv72tCAlDJC3Yqwln0HiFJ3VJMst2+mEtNtZu5oFvX4qJGjKsnAgg==} + '@typescript-eslint/utils@8.52.0': + resolution: {integrity: sha512-wYndVMWkweqHpEpwPhwqE2lnD2DxC6WVLupU/DOt/0/v+/+iQbbzO3jOHjmBMnhu0DgLULvOaU4h4pwHYi2oRQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: eslint: ^8.57.0 || ^9.0.0 typescript: '>=4.8.4 <6.0.0' - '@typescript-eslint/visitor-keys@8.50.0': - resolution: {integrity: sha512-Xzmnb58+Db78gT/CCj/PVCvK+zxbnsw6F+O1oheYszJbBSdEjVhQi3C/Xttzxgi/GLmpvOggRs1RFpiJ8+c34Q==} + '@typescript-eslint/visitor-keys@8.52.0': + resolution: {integrity: sha512-ink3/Zofus34nmBsPjow63FP5M7IGff0RKAgqR6+CFpdk22M7aLwC9gOcLGYqr7MczLPzZVERW9hRog3O4n1sQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} '@ungap/structured-clone@1.3.0': @@ -1834,8 +1862,8 @@ packages: resolution: {integrity: sha512-lGe34o6EHj9y3Kts9R4ZYs/Gr+6N7MCaMlIFA3F1R2O5/m7K06AxfSeO5530PEERE6/WyEg3lsuyw4GHlPZHog==} engines: {node: ^4.5.0 || >= 5.9} - baseline-browser-mapping@2.9.8: - resolution: {integrity: sha512-Y1fOuNDowLfgKOypdc9SPABfoWXuZHBOyCS4cD52IeZBhr4Md6CLLs6atcxVrzRmQ06E7hSlm5bHHApPKR/byA==} + baseline-browser-mapping@2.9.13: + resolution: {integrity: sha512-WhtvB2NG2wjr04+h77sg3klAIwrgOqnjS49GGudnUPGFFgg7G17y7Qecqp+2Dr5kUDxNRBca0SK7cG8JwzkWDQ==} hasBin: true binary-extensions@2.3.0: @@ -1849,8 +1877,8 @@ packages: resolution: {integrity: sha512-7rAxByjUMqQ3/bHJy7D6OGXvx/MMc4IqBn/X0fcM1QUcAItpZrBEYhWGem+tzXH90c+G01ypMcYJBO9Y30203g==} engines: {node: '>= 0.8', npm: 1.2.8000 || >= 1.4.16} - body-parser@2.2.1: - resolution: {integrity: sha512-nfDwkulwiZYQIGwxdy0RUmowMhKcFVcYXUU7m4QlKYim1rUtg83xm2yjZ40QjDuc291AJjjeSc9b++AWHSgSHw==} + body-parser@2.2.2: + resolution: {integrity: sha512-oP5VkATKlNwcgvxi0vM0p/D3n2C3EReYVX+DNYs5TjZFn/oQt2j+4sVJtSMr18pdRr8wjTcBl6LoV+FUwzPmNA==} engines: {node: '>=18'} bowser@2.13.1: @@ -1916,8 +1944,8 @@ packages: resolution: {integrity: sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==} engines: {node: '>=10'} - caniuse-lite@1.0.30001760: - resolution: {integrity: sha512-7AAMPcueWELt1p3mi13HR/LHH0TJLT11cnwDJEs3xA4+CK/PLKeO9Kl1oru24htkyUKtkGCvAx4ohB0Ttry8Dw==} + caniuse-lite@1.0.30001763: + resolution: {integrity: sha512-mh/dGtq56uN98LlNX9qdbKnzINhX0QzhiWBFEkFfsFO4QyCvL8YegrJAazCwXIeqkIob8BlZPGM3xdnY+sgmvQ==} chalk@4.1.2: resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==} @@ -1942,6 +1970,10 @@ packages: resolution: {integrity: sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==} engines: {node: '>= 14.16.0'} + chownr@3.0.0: + resolution: {integrity: sha512-+IxzY9BZOQd/XuYPRmrvEVjF/nqj5kgT4kEq7VofrDoM1MxoRjEWkrCC3EtLi59TVawxTAn+orJwFQcrqEN1+g==} + engines: {node: '>=18'} + chrome-trace-event@1.0.4: resolution: {integrity: sha512-rNjApaLzuwaOTjCiT8lSDdGN1APCiqkChLMJxJPWLunPAt5fy8xgU9/jNOchV84wfIxrA0lRQB7oCT8jrn/wrQ==} engines: {node: '>=6.0'} @@ -2051,9 +2083,8 @@ packages: cookie-signature@1.0.6: resolution: {integrity: sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==} - cookie@0.7.1: - resolution: {integrity: sha512-6DnInpx7SJ2AK3+CTUE/ZM0vWTUboZCegxhC2xiIydHR9jNuTAASBrfEpHhiGOZw/nX51bHt6YQl8jsGo4y/0w==} - engines: {node: '>= 0.6'} + cookie-signature@1.0.7: + resolution: {integrity: sha512-NXdYc3dLr47pBkpUCHtKSwIOQXLVn8dZEuywboCOJY/osA0wFSLlSawr3KN8qXJEyX66FcONTH8EIlVuK0yyFA==} cookie@0.7.2: resolution: {integrity: sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==} @@ -2106,6 +2137,10 @@ packages: cssfilter@0.0.10: resolution: {integrity: sha512-FAaLDaplstoRsDR8XGYH51znUN0UY7nMc6Z9/fvE8EXGwvJE9hu7W2vHwx1+bd6gCYnln9nLbzxFTrcO9YQDZw==} + data-uri-to-buffer@4.0.1: + resolution: {integrity: sha512-0R9ikRb668HB7QDxT1vkpuUBtqc53YyAwMwGeUFKRojY/NWKvdZ+9UYtRfGmhqNbRkTSVpMbmyhXipFFv2cb/A==} + engines: {node: '>= 12'} + data-view-buffer@1.0.2: resolution: {integrity: sha512-EmKO5V3OLXh1rtK2wgXRansaK1/mtVdTUEiEI0W8RkvgT05kfxaH29PliLnpLP73yYO6142Q72QNa8Wx/A5CqQ==} engines: {node: '>= 0.4'} @@ -2152,8 +2187,8 @@ packages: supports-color: optional: true - dedent@1.7.0: - resolution: {integrity: sha512-HGFtf8yhuhGhqO07SV79tRp+br4MnbdjeVxotpn1QBl30pcLLCQjX5b2295ll0fv8RKDKsmWYrl05usHM9CewQ==} + dedent@1.7.1: + resolution: {integrity: sha512-9JmrhGZpOlEgOLdQgSm0zxFaYoQon408V1v49aqTWuXENVlnCuY9JBZcXZiCsZQWDjTm5Qf/nIvAy77mXDAjEg==} peerDependencies: babel-plugin-macros: ^3.1.0 peerDependenciesMeta: @@ -2252,10 +2287,6 @@ packages: emoji-regex@9.2.2: resolution: {integrity: sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==} - encodeurl@1.0.2: - resolution: {integrity: sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==} - engines: {node: '>= 0.8'} - encodeurl@2.0.0: resolution: {integrity: sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==} engines: {node: '>= 0.8'} @@ -2432,8 +2463,8 @@ packages: engines: {node: '>=4'} hasBin: true - esquery@1.6.0: - resolution: {integrity: sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==} + esquery@1.7.0: + resolution: {integrity: sha512-Ap6G0WQwcU/LHsvLwON1fAQX9Zp0A2Y6Y/cJBl9r/JbW90Zyg4/zbG6zzKa2OTALELarYHmKu0GhpM5EO+7T0g==} engines: {node: '>=0.10'} esrecurse@4.3.0: @@ -2475,8 +2506,8 @@ packages: resolution: {integrity: sha512-2Zks0hf1VLFYI1kbh0I5jP3KHHyCHpkfyHBzsSXRFgl/Bg9mWYfMW8oD+PdMPlEwy5HNsR9JutYy6pMeOh61nw==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - express@4.21.2: - resolution: {integrity: sha512-28HqgMZAmih1Czt9ny7qr6ek2qddF4FclbMzwhCREB6OFfH+rXAnuNCwo1/wFvrtbgsQDb4kSbX9de9lFbrXnA==} + express@4.22.1: + resolution: {integrity: sha512-F2X8g9P1X7uCPZMA3MVf9wcTqlyNp7IhH5qPCI0izhaOIYXaW9L535tGA3qmjRzpH+bZczqq7hVKxTR4NWnu+g==} engines: {node: '>= 0.10.0'} external-editor@3.1.0: @@ -2509,8 +2540,8 @@ packages: resolution: {integrity: sha512-pfX9uG9Ki0yekDHx2SiuRIyFdyAr1kMIMitPvb0YBo8SUfKvia7w7FIyd/l6av85pFYRhZscS75MwMnbvY+hcQ==} hasBin: true - fastq@1.19.1: - resolution: {integrity: sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ==} + fastq@1.20.1: + resolution: {integrity: sha512-GGToxJ/w1x32s/D2EKND7kTil4n8OVk/9mycTc4VDza13lOvpUZTGX3mFSCtV9ksdGBVzvsyAVLM6mHFThxXxw==} fb-watchman@2.0.2: resolution: {integrity: sha512-p5161BqbuCaSnB8jIbzQHOlpgsPmK5rJVDfDKO91Axs5NC1uu3HRQm6wt9cd9/+GtQQIO53JdGXXoyDpTAsgYA==} @@ -2524,6 +2555,10 @@ packages: picomatch: optional: true + fetch-blob@3.2.0: + resolution: {integrity: sha512-7yAQpD2UMJzLi1Dqv7qFYnPbaPx7ZfFK6PiIxQ4PfkGPyNyl2Ugx+a/umUonmKqjhM4DnfbMvdX6otXq83soQQ==} + engines: {node: ^12.20 || >= 14.13} + fflate@0.8.2: resolution: {integrity: sha512-cPJU47OaAoCbg0pBvzsgpTPhmhqI5eJjh/JIu8tPj5q+T7iLvW/JAYUqmE7KOB4R1ZyEhzBaIQpQpardBF5z8A==} @@ -2543,8 +2578,8 @@ packages: resolution: {integrity: sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==} engines: {node: '>=8'} - finalhandler@1.3.1: - resolution: {integrity: sha512-6BN9trH7bp3qvnrRyzsBz+g3lZxTNZTbVO2EV1CS0WIcDbawYVdYvGflME/9QP0h0pYlCDBCTjYa9nZzMDpyxQ==} + finalhandler@1.3.2: + resolution: {integrity: sha512-aA4RyPcd3badbdABGDuTXCMTtOneUCAYH/gxoYRTZlIJdF0YPWuGqiAsIrhNnnqdXGswYk6dGujem4w80UJFhg==} engines: {node: '>= 0.8'} finalhandler@2.1.1: @@ -2563,6 +2598,9 @@ packages: resolution: {integrity: sha512-CYcENa+FtcUKLmhhqyctpclsq7QF38pKjZHsGNiSQF5r4FtoKDWabFDl3hzaEQMvT1LHEysw5twgLvpYYb4vbw==} engines: {node: ^10.12.0 || >=12.0.0} + flatbuffers@25.9.23: + resolution: {integrity: sha512-MI1qs7Lo4Syw0EOzUl0xjs2lsoeqFku44KpngfIduHBYvzm8h2+7K8YMQh1JtVVVrUvhLpNwqVi4DERegUJhPQ==} + flatted@3.3.3: resolution: {integrity: sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg==} @@ -2594,6 +2632,10 @@ packages: resolution: {integrity: sha512-8RipRLol37bNs2bhoV67fiTEvdTrbMUYcFTiy3+wuuOnUog2QBHCZWXDRijWQfAkhBj2Uf5UnVaiWwA5vdd82w==} engines: {node: '>= 6'} + formdata-polyfill@4.0.10: + resolution: {integrity: sha512-buewHzMvYL29jdeQTVILecSaZKnt/RJWjoZCF5OW60Z67/GmSLBkOFM7qh1PI3zFNtJbaZL5eQu1vLfazOwj4g==} + engines: {node: '>=12.20.0'} + formidable@2.1.5: resolution: {integrity: sha512-Oz5Hwvwak/DCaXVVUtPn4oLMLLy1CdclLKO1LFgU7XzDpVMUU5UjlSLpGMocyQNNk8F6IJW9M/YdooSn2MRI+Q==} @@ -2739,6 +2781,10 @@ packages: resolution: {integrity: sha512-DKKrynuQRne0PNpEbzuEdHlYOMksHSUI8Zc9Unei5gTsMNA2/vMpoMz/yKba50pejK56qj98qM0SjYxAKi13gQ==} engines: {node: ^12.22.0 || ^14.16.0 || ^16.0.0 || >=17.0.0} + h264-profile-level-id@2.3.2: + resolution: {integrity: sha512-hnq1UDlw7WGJV6GCr/g7wnkHYUjdAY2bis9rgn2JqSdQS2WfVvnt1ZE9g8nTguracodf5LLKZOwURsDN49YtBQ==} + engines: {node: '>=20'} + handlebars@4.7.8: resolution: {integrity: sha512-vafaFqs8MZkRrSX7sFVUdo3ap/eNiLnb4IakshzvP56X5Nr1iGKAIqdX6tMlm6HcNRIkr6AxO5jFEoJzzpT8aQ==} engines: {node: '>=0.4.7'} @@ -2794,8 +2840,8 @@ packages: resolution: {integrity: sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==} engines: {node: '>=0.10.0'} - iconv-lite@0.7.1: - resolution: {integrity: sha512-2Tth85cXwGFHfvRgZWszZSvdo+0Xsqmw8k8ZwxScfcBneNUraK+dxRxRm24nszx80Y0TVio8kKLt5sLE7ZCLlw==} + iconv-lite@0.7.2: + resolution: {integrity: sha512-im9DjEDQ55s9fL4EYzOAv0yMqmMBSZp6G0VvFyTMPKWxiSBHUj9NW/qqLmXUwXrrM7AvqSlTCfvqRb0cM8yYqw==} engines: {node: '>=0.10.0'} ieee754@1.2.1: @@ -2829,6 +2875,10 @@ packages: inherits@2.0.4: resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==} + ini@6.0.0: + resolution: {integrity: sha512-IBTdIkzZNOpqm7q3dRqJvMaldXjDHWkEDfrwGEQTs5eaQMWV+djAhR+wahyNNMAa+qpbDUhBMVt4ZKNwpPm7xQ==} + engines: {node: ^20.17.0 || >=22.9.0} + inquirer@8.2.6: resolution: {integrity: sha512-M1WuAmb7pn9zdFRtQYk26ZBoY043Sse0wVDdk4Bppr+JOXyQYybdtvK+l9wUibhtjdjvtoiNy8tk+EgsYIUqKg==} engines: {node: '>=12.0.0'} @@ -3222,8 +3272,8 @@ packages: resolution: {integrity: sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==} engines: {node: '>= 0.8.0'} - libphonenumber-js@1.12.31: - resolution: {integrity: sha512-Z3IhgVgrqO1S5xPYM3K5XwbkDasU67/Vys4heW+lfSBALcUZjeIIzI8zCLifY+OCzSq+fpDdywMDa7z+4srJPQ==} + libphonenumber-js@1.12.33: + resolution: {integrity: sha512-r9kw4OA6oDO4dPXkOrXTkArQAafIKAU71hChInV4FxZ69dxCfbwQGDPzqR5/vea94wU705/3AZroEbSoeVWrQw==} lines-and-columns@1.2.4: resolution: {integrity: sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==} @@ -3310,6 +3360,10 @@ packages: resolution: {integrity: sha512-aisnrDP4GNe06UcKFnV5bfMNPBUw4jsLGaWwWfnH3v02GnBuXX2MCVn5RbrWo0j3pczUilYblq7fQ7Nw2t5XKw==} engines: {node: '>= 0.8'} + mediasoup@3.19.14: + resolution: {integrity: sha512-8ESNnyvxD5XgMSORMR258T/RIcL6e0/eDOTeHM4GG4dezJOdgwdbjBRWkfL9f4oHKtpar2NgbIPEqg62uWhoXg==} + engines: {node: '>=22'} + memfs@3.5.3: resolution: {integrity: sha512-UERzLsxzllchadvbPs5aolHh65ISpKpM+ccLbOJ8/vvpBKmAWf+la7dXFy7Mr0ySHbdHrFv5kGFCUHHe6GFEmw==} engines: {node: '>= 4.0.0'} @@ -3376,6 +3430,10 @@ packages: resolution: {integrity: sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==} engines: {node: '>=16 || 14 >=14.17'} + minizlib@3.1.0: + resolution: {integrity: sha512-KZxYo1BUkWD2TVFLr0MQoM8vUUigWD3LlD83a/75BqC+4qE0Hb1Vo5v1FgcfaNXvfXzr+5EhQ6ing/CaBijTlw==} + engines: {node: '>= 18'} + mkdirp@0.5.6: resolution: {integrity: sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==} hasBin: true @@ -3431,6 +3489,11 @@ packages: resolution: {integrity: sha512-/bRZty2mXUIFY/xU5HLvveNHlswNJej+RnxBjOMkidWfwZzgTbPG1E3K5TOxRLOR+5hX7bSofy8yf1hZevMS8A==} engines: {node: ^18 || ^20 || >= 21} + node-domexception@1.0.0: + resolution: {integrity: sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==} + engines: {node: '>=10.5.0'} + deprecated: Use your platform's native DOMException instead + node-emoji@1.11.0: resolution: {integrity: sha512-wo2DpQkQp7Sjm2A0cq+sN7EHKO6Sl0ctXeBdFZrL9T9+UywORbufTcTZxom8YqpLQt/FqNMUkOpkZrJVYSKD3A==} @@ -3443,6 +3506,10 @@ packages: encoding: optional: true + node-fetch@3.3.2: + resolution: {integrity: sha512-dRB78srN/l6gqWulah9SrxeYnxeddIG30+GOqK/9OlLVyLg3HPnr6SqOWTWOXKRwC2eGYCkZ59NNuSgvSrpgOA==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + node-gyp-build@4.8.4: resolution: {integrity: sha512-LA4ZjwlnUblHVgq0oBF3Jl/6h/Nvs5fzBLwdEF4nuxnFdsfajde4WfxtJr3CaiH+F6ewcIB/q4jQ4UzPyid+CQ==} hasBin: true @@ -3622,8 +3689,8 @@ packages: resolution: {integrity: sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==} engines: {node: '>= 0.8.0'} - prettier-linter-helpers@1.0.0: - resolution: {integrity: sha512-GbK2cP9nraSSUF9N2XwUwqfzlAFlMNYYl+ShE/V+H8a9uNl/oUqB1w2EL54Jh0OlyRSd8RfWYJ3coVS4TROP2w==} + prettier-linter-helpers@1.0.1: + resolution: {integrity: sha512-SxToR7P8Y2lWmv/kTzVLC1t/GDI2WGjMwNhLLE9qtH8Q13C+aEmuRlzDst4Up4s0Wc8sF2M+J57iB3cMLqftfg==} engines: {node: '>=6.0.0'} prettier@3.7.4: @@ -3657,8 +3724,8 @@ packages: resolution: {integrity: sha512-+38qI9SOr8tfZ4QmJNplMUxqjbe7LKvvZgWdExBOmd+egZTtjLB67Gu0HRX3u/XOq7UU2Nx6nsjvS16Z9uwfpg==} engines: {node: '>=0.6'} - qs@6.14.0: - resolution: {integrity: sha512-YWWTjgABSKcvs/nWBi9PycY/JiPJqOD4JA6o9Sej2AtvSGarXxKC3OQSk4pAarbdQlKAh5D4FCQkJNkW+GAn3w==} + qs@6.14.1: + resolution: {integrity: sha512-4EK3+xJl8Ts67nLYNwqw/dsFVnCf+qR7RgXSK9jEEm9unao3njwMDdmsdvoKBKHzxd7tCYz5e5M+SnMjdtXGQQ==} engines: {node: '>=0.6'} queue-microtask@1.2.3: @@ -3814,8 +3881,8 @@ packages: engines: {node: '>=10'} hasBin: true - send@0.19.0: - resolution: {integrity: sha512-dW41u5VfLXu8SJh5bwRmyYUbAoSB3c9uQh6L8h/KtsFREPWpbX1lrljJo186Jc4nmci/sGUZ9a0a0J2zgfq2hw==} + send@0.19.2: + resolution: {integrity: sha512-VMbMxbDeehAxpOtWJXlcUS5E8iXh6QmN+BkRX1GARS3wRaXEEgzCcB10gTQazO42tpNIya8xIyNx8fll1OFPrg==} engines: {node: '>= 0.8.0'} seq-queue@0.0.5: @@ -3824,8 +3891,8 @@ packages: serialize-javascript@6.0.2: resolution: {integrity: sha512-Saa1xPByTTq2gdeFZYLLo+RFE35NHZkAbqZeWNd3BpzppeVisAqpDjcp8dyf6uIvEqJRd46jemmyA4iFIeVk8g==} - serve-static@1.16.2: - resolution: {integrity: sha512-VqpjJZKadQB/PEbEwvFdO43Ax5dFBZ2UECszz8bQ7pi7wt//PWe1P6MN7eCnjsatYtBT6EuiClbjSWP2WrIoTw==} + serve-static@1.16.3: + resolution: {integrity: sha512-x0RTqQel6g5SY7Lg6ZreMmsOzncHFU7nhnRWkKgWuMTu5NN0DR5oruckMqRvacAN9d5w6ARnRBXl9xhDCgfMeA==} engines: {node: '>= 0.8.0'} set-function-length@1.2.2: @@ -3893,6 +3960,10 @@ packages: resolution: {integrity: sha512-bPMmpy/5WWKHea5Y/jYAP6k74A+hvmRCQaJuJB6I/ML5JZq/KfNieUVo/3Mh7SAqn7TyFdIo6wqYHInG1MU1bQ==} engines: {node: '>=10.0.0'} + socket.io@4.8.1: + resolution: {integrity: sha512-oZ7iUCxph8WYRHHcjBEc9unw3adt5CmSNlppj/5Q4k2RIrhl8Z5yY2Xr4j9zj0+wzVZ0bxmYoGSzKJnRl6A4yg==} + engines: {node: '>=10.2.0'} + socket.io@4.8.3: resolution: {integrity: sha512-2Dd78bqzzjE6KPkD5fHZmDAKRNe3J15q+YHDrIsy9WEkqttc7GY+kT9OBLSMaPbQaEd0x1BjcmtMtXkfpc+T5A==} engines: {node: '>=10.2.0'} @@ -4020,6 +4091,10 @@ packages: engines: {node: '>=6.4.0'} deprecated: Please upgrade to supertest v7.1.3+, see release notes at https://github.com/forwardemail/supertest/releases/tag/v7.1.3 - maintenance is supported by Forward Email @ https://forwardemail.net + supports-color@10.2.2: + resolution: {integrity: sha512-SS+jx45GF1QjgEXQx4NJZV9ImqmO2NPz5FNsIHrsDjh2YsHnawpan7SNQ1o8NuhrbHZy9AZhIoCUiCeaW/C80g==} + engines: {node: '>=18'} + supports-color@7.2.0: resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==} engines: {node: '>=8'} @@ -4048,6 +4123,10 @@ packages: resolution: {integrity: sha512-g9ljZiwki/LfxmQADO3dEY1CbpmXT5Hm2fJ+QaGKwSXUylMybePR7/67YW7jOrrvjEgL1Fmz5kzyAjWVWLlucg==} engines: {node: '>=6'} + tar@7.5.2: + resolution: {integrity: sha512-7NyxrTE4Anh8km8iEy7o0QYPs+0JKBTj5ZaqHg6B39erLg0qYXN3BijtShwbsNSvQ+LN75+KV+C4QR/f6Gwnpg==} + engines: {node: '>=18'} + terser-webpack-plugin@5.3.16: resolution: {integrity: sha512-h9oBFCWrq78NyWWVcSwZarJkZ01c2AyGrzs1crmHZO3QUg9D61Wu4NPjBy69n7JqylFF5y+CsUZYmYEIZ3mR+Q==} engines: {node: '>= 10.13.0'} @@ -4102,8 +4181,8 @@ packages: resolution: {integrity: sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==} engines: {node: '>=0.6'} - token-types@6.1.1: - resolution: {integrity: sha512-kh9LVIWH5CnL63Ipf0jhlBIy0UsrMj/NJDfpsy1SqOXlLKEVyXXYrnFxFT1yOOYVGBSApeVnjPw/sBz5BfEjAQ==} + token-types@6.1.2: + resolution: {integrity: sha512-dRXchy+C0IgK8WPC6xvCHFRIWYUbqqdEIKPaKo/AcTUNzwLTK6AH7RjdLWsEZcAN/TBdtfUw3PYEgPr5VPr6ww==} engines: {node: '>=14.16'} tr46@0.0.3: @@ -4113,8 +4192,8 @@ packages: resolution: {integrity: sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==} hasBin: true - ts-api-utils@2.1.0: - resolution: {integrity: sha512-CUgTZL1irw8u29bzrOD/nH85jqyc74D6SshFgujOIA7osm2Rz7dYH77agkx7H4FBNxDq7Cjf+IjaX/8zwFW+ZQ==} + ts-api-utils@2.4.0: + resolution: {integrity: sha512-3TaVTaAv2gTiMB35i3FiGJaRfwb3Pyn/j3m/bfAvGe8FB7CF6u+LMYqYlDh7reQf7UNvoTvdfAqHGmPGOSsPmA==} engines: {node: '>=18.12'} peerDependencies: typescript: '>=4.8.4' @@ -4304,8 +4383,8 @@ packages: resolution: {integrity: sha512-kiGUalWN+rgBJ/1OHZsBtU4rXZOfj/7rKQxULKlIzwzQSvMJUUNgPwJEEh7gU6xEVxC0ahoOBvN2YI8GH6FNgA==} engines: {node: '>=10.12.0'} - validator@13.15.23: - resolution: {integrity: sha512-4yoz1kEWqUjzi5zsPbAS/903QXSYp0UOtHsPpp7p9rHAw/W+dkInskAE386Fat3oKRROwO98d9ZB0G4cObgUyw==} + validator@13.15.26: + resolution: {integrity: sha512-spH26xU080ydGggxRyR1Yhcbgx+j3y5jbNXk/8L+iRvdIEQ4uTRH2Sgf2dokud6Q4oAtsbNvJ1Ft+9xmm6IZcA==} engines: {node: '>= 0.10'} vary@1.1.2: @@ -4315,13 +4394,17 @@ packages: walker@1.0.8: resolution: {integrity: sha512-ts/8E8l5b7kY0vlWLewOkDXMmPdLcVV4GmOQLyxuSswIJsweeFZtAsMF7k1Nszz+TYBQrlYRmzOnr398y1JemQ==} - watchpack@2.4.4: - resolution: {integrity: sha512-c5EGNOiyxxV5qmTtAB7rbiXxi1ooX1pQKMLX/MIabJjRA0SJBQOjKF+KSVfHkr9U1cADPon0mRiVe/riyaiDUA==} + watchpack@2.5.0: + resolution: {integrity: sha512-e6vZvY6xboSwLz2GD36c16+O/2Z6fKvIf4pOXptw2rY9MVwE/TXc6RGqxD3I3x0a28lwBY7DE+76uTPSsBrrCA==} engines: {node: '>=10.13.0'} wcwidth@1.0.1: resolution: {integrity: sha512-XHPEwS0q6TaxcvG85+8EYkbiCux2XtWG2mkc47Ng2A77BQu9+DqIOJldST4HgPkuea7dvKSj5VgX3P1d4rW8Tg==} + web-streams-polyfill@3.3.3: + resolution: {integrity: sha512-d2JWLCivmZYTSIoge9MsgFCZrt571BikcWGYkjC1khllbTeDlGqZ2D8vD8E/lJa8WGWbb7Plm8/XJYV7IJHZZw==} + engines: {node: '>= 8'} + webidl-conversions@3.0.1: resolution: {integrity: sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==} @@ -4437,6 +4520,10 @@ packages: yallist@3.1.1: resolution: {integrity: sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==} + yallist@5.0.0: + resolution: {integrity: sha512-YgvUTfwqyc7UXVMrB+SImsVYSmTS8X/tSrtdNZMImM+n7+QTriRXyXim0mBrTXNeqzVF0KWGgHPeiyViFFrNDw==} + engines: {node: '>=18'} + yargs-parser@21.1.1: resolution: {integrity: sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==} engines: {node: '>=12'} @@ -4531,9 +4618,9 @@ snapshots: '@apollo/utils.logger': 3.0.0 '@apollo/utils.usagereporting': 2.1.0(graphql@16.12.0) '@apollo/utils.withrequired': 3.0.0 - '@graphql-tools/schema': 10.0.30(graphql@16.12.0) + '@graphql-tools/schema': 10.0.31(graphql@16.12.0) async-retry: 1.3.3 - body-parser: 2.2.1 + body-parser: 2.2.2 cors: 2.8.5 finalhandler: 2.1.1 graphql: 16.12.0 @@ -4602,29 +4689,29 @@ snapshots: dependencies: xss: 1.0.15 - '@as-integrations/express5@1.1.2(@apollo/server@5.2.0(graphql@16.12.0))(express@4.21.2)': + '@as-integrations/express5@1.1.2(@apollo/server@5.2.0(graphql@16.12.0))(express@4.22.1)': dependencies: '@apollo/server': 5.2.0(graphql@16.12.0) - express: 4.21.2 + express: 4.22.1 '@aws-crypto/crc32@5.2.0': dependencies: '@aws-crypto/util': 5.2.0 - '@aws-sdk/types': 3.957.0 + '@aws-sdk/types': 3.965.0 tslib: 2.8.1 '@aws-crypto/crc32c@5.2.0': dependencies: '@aws-crypto/util': 5.2.0 - '@aws-sdk/types': 3.957.0 + '@aws-sdk/types': 3.965.0 tslib: 2.8.1 '@aws-crypto/sha1-browser@5.2.0': dependencies: '@aws-crypto/supports-web-crypto': 5.2.0 '@aws-crypto/util': 5.2.0 - '@aws-sdk/types': 3.957.0 - '@aws-sdk/util-locate-window': 3.957.0 + '@aws-sdk/types': 3.965.0 + '@aws-sdk/util-locate-window': 3.965.0 '@smithy/util-utf8': 2.3.0 tslib: 2.8.1 @@ -4633,15 +4720,15 @@ snapshots: '@aws-crypto/sha256-js': 5.2.0 '@aws-crypto/supports-web-crypto': 5.2.0 '@aws-crypto/util': 5.2.0 - '@aws-sdk/types': 3.957.0 - '@aws-sdk/util-locate-window': 3.957.0 + '@aws-sdk/types': 3.965.0 + '@aws-sdk/util-locate-window': 3.965.0 '@smithy/util-utf8': 2.3.0 tslib: 2.8.1 '@aws-crypto/sha256-js@5.2.0': dependencies: '@aws-crypto/util': 5.2.0 - '@aws-sdk/types': 3.957.0 + '@aws-sdk/types': 3.965.0 tslib: 2.8.1 '@aws-crypto/supports-web-crypto@5.2.0': @@ -4650,35 +4737,35 @@ snapshots: '@aws-crypto/util@5.2.0': dependencies: - '@aws-sdk/types': 3.957.0 + '@aws-sdk/types': 3.965.0 '@smithy/util-utf8': 2.3.0 tslib: 2.8.1 - '@aws-sdk/client-s3@3.957.0': + '@aws-sdk/client-s3@3.965.0': dependencies: '@aws-crypto/sha1-browser': 5.2.0 '@aws-crypto/sha256-browser': 5.2.0 '@aws-crypto/sha256-js': 5.2.0 - '@aws-sdk/core': 3.957.0 - '@aws-sdk/credential-provider-node': 3.957.0 - '@aws-sdk/middleware-bucket-endpoint': 3.957.0 - '@aws-sdk/middleware-expect-continue': 3.957.0 - '@aws-sdk/middleware-flexible-checksums': 3.957.0 - '@aws-sdk/middleware-host-header': 3.957.0 - '@aws-sdk/middleware-location-constraint': 3.957.0 - '@aws-sdk/middleware-logger': 3.957.0 - '@aws-sdk/middleware-recursion-detection': 3.957.0 - '@aws-sdk/middleware-sdk-s3': 3.957.0 - '@aws-sdk/middleware-ssec': 3.957.0 - '@aws-sdk/middleware-user-agent': 3.957.0 - '@aws-sdk/region-config-resolver': 3.957.0 - '@aws-sdk/signature-v4-multi-region': 3.957.0 - '@aws-sdk/types': 3.957.0 - '@aws-sdk/util-endpoints': 3.957.0 - '@aws-sdk/util-user-agent-browser': 3.957.0 - '@aws-sdk/util-user-agent-node': 3.957.0 + '@aws-sdk/core': 3.965.0 + '@aws-sdk/credential-provider-node': 3.965.0 + '@aws-sdk/middleware-bucket-endpoint': 3.965.0 + '@aws-sdk/middleware-expect-continue': 3.965.0 + '@aws-sdk/middleware-flexible-checksums': 3.965.0 + '@aws-sdk/middleware-host-header': 3.965.0 + '@aws-sdk/middleware-location-constraint': 3.965.0 + '@aws-sdk/middleware-logger': 3.965.0 + '@aws-sdk/middleware-recursion-detection': 3.965.0 + '@aws-sdk/middleware-sdk-s3': 3.965.0 + '@aws-sdk/middleware-ssec': 3.965.0 + '@aws-sdk/middleware-user-agent': 3.965.0 + '@aws-sdk/region-config-resolver': 3.965.0 + '@aws-sdk/signature-v4-multi-region': 3.965.0 + '@aws-sdk/types': 3.965.0 + '@aws-sdk/util-endpoints': 3.965.0 + '@aws-sdk/util-user-agent-browser': 3.965.0 + '@aws-sdk/util-user-agent-node': 3.965.0 '@smithy/config-resolver': 4.4.5 - '@smithy/core': 3.20.0 + '@smithy/core': 3.20.1 '@smithy/eventstream-serde-browser': 4.2.7 '@smithy/eventstream-serde-config-resolver': 4.3.7 '@smithy/eventstream-serde-node': 4.2.7 @@ -4689,21 +4776,21 @@ snapshots: '@smithy/invalid-dependency': 4.2.7 '@smithy/md5-js': 4.2.7 '@smithy/middleware-content-length': 4.2.7 - '@smithy/middleware-endpoint': 4.4.1 - '@smithy/middleware-retry': 4.4.17 + '@smithy/middleware-endpoint': 4.4.2 + '@smithy/middleware-retry': 4.4.18 '@smithy/middleware-serde': 4.2.8 '@smithy/middleware-stack': 4.2.7 '@smithy/node-config-provider': 4.3.7 '@smithy/node-http-handler': 4.4.7 '@smithy/protocol-http': 5.3.7 - '@smithy/smithy-client': 4.10.2 + '@smithy/smithy-client': 4.10.3 '@smithy/types': 4.11.0 '@smithy/url-parser': 4.2.7 '@smithy/util-base64': 4.3.0 '@smithy/util-body-length-browser': 4.2.0 '@smithy/util-body-length-node': 4.2.1 - '@smithy/util-defaults-mode-browser': 4.3.16 - '@smithy/util-defaults-mode-node': 4.2.19 + '@smithy/util-defaults-mode-browser': 4.3.17 + '@smithy/util-defaults-mode-node': 4.2.20 '@smithy/util-endpoints': 3.2.7 '@smithy/util-middleware': 4.2.7 '@smithy/util-retry': 4.2.7 @@ -4714,41 +4801,41 @@ snapshots: transitivePeerDependencies: - aws-crt - '@aws-sdk/client-sso@3.957.0': + '@aws-sdk/client-sso@3.965.0': dependencies: '@aws-crypto/sha256-browser': 5.2.0 '@aws-crypto/sha256-js': 5.2.0 - '@aws-sdk/core': 3.957.0 - '@aws-sdk/middleware-host-header': 3.957.0 - '@aws-sdk/middleware-logger': 3.957.0 - '@aws-sdk/middleware-recursion-detection': 3.957.0 - '@aws-sdk/middleware-user-agent': 3.957.0 - '@aws-sdk/region-config-resolver': 3.957.0 - '@aws-sdk/types': 3.957.0 - '@aws-sdk/util-endpoints': 3.957.0 - '@aws-sdk/util-user-agent-browser': 3.957.0 - '@aws-sdk/util-user-agent-node': 3.957.0 + '@aws-sdk/core': 3.965.0 + '@aws-sdk/middleware-host-header': 3.965.0 + '@aws-sdk/middleware-logger': 3.965.0 + '@aws-sdk/middleware-recursion-detection': 3.965.0 + '@aws-sdk/middleware-user-agent': 3.965.0 + '@aws-sdk/region-config-resolver': 3.965.0 + '@aws-sdk/types': 3.965.0 + '@aws-sdk/util-endpoints': 3.965.0 + '@aws-sdk/util-user-agent-browser': 3.965.0 + '@aws-sdk/util-user-agent-node': 3.965.0 '@smithy/config-resolver': 4.4.5 - '@smithy/core': 3.20.0 + '@smithy/core': 3.20.1 '@smithy/fetch-http-handler': 5.3.8 '@smithy/hash-node': 4.2.7 '@smithy/invalid-dependency': 4.2.7 '@smithy/middleware-content-length': 4.2.7 - '@smithy/middleware-endpoint': 4.4.1 - '@smithy/middleware-retry': 4.4.17 + '@smithy/middleware-endpoint': 4.4.2 + '@smithy/middleware-retry': 4.4.18 '@smithy/middleware-serde': 4.2.8 '@smithy/middleware-stack': 4.2.7 '@smithy/node-config-provider': 4.3.7 '@smithy/node-http-handler': 4.4.7 '@smithy/protocol-http': 5.3.7 - '@smithy/smithy-client': 4.10.2 + '@smithy/smithy-client': 4.10.3 '@smithy/types': 4.11.0 '@smithy/url-parser': 4.2.7 '@smithy/util-base64': 4.3.0 '@smithy/util-body-length-browser': 4.2.0 '@smithy/util-body-length-node': 4.2.1 - '@smithy/util-defaults-mode-browser': 4.3.16 - '@smithy/util-defaults-mode-node': 4.2.19 + '@smithy/util-defaults-mode-browser': 4.3.17 + '@smithy/util-defaults-mode-node': 4.2.20 '@smithy/util-endpoints': 3.2.7 '@smithy/util-middleware': 4.2.7 '@smithy/util-retry': 4.2.7 @@ -4757,59 +4844,59 @@ snapshots: transitivePeerDependencies: - aws-crt - '@aws-sdk/core@3.957.0': + '@aws-sdk/core@3.965.0': dependencies: - '@aws-sdk/types': 3.957.0 - '@aws-sdk/xml-builder': 3.957.0 - '@smithy/core': 3.20.0 + '@aws-sdk/types': 3.965.0 + '@aws-sdk/xml-builder': 3.965.0 + '@smithy/core': 3.20.1 '@smithy/node-config-provider': 4.3.7 '@smithy/property-provider': 4.2.7 '@smithy/protocol-http': 5.3.7 '@smithy/signature-v4': 5.3.7 - '@smithy/smithy-client': 4.10.2 + '@smithy/smithy-client': 4.10.3 '@smithy/types': 4.11.0 '@smithy/util-base64': 4.3.0 '@smithy/util-middleware': 4.2.7 '@smithy/util-utf8': 4.2.0 tslib: 2.8.1 - '@aws-sdk/crc64-nvme@3.957.0': + '@aws-sdk/crc64-nvme@3.965.0': dependencies: '@smithy/types': 4.11.0 tslib: 2.8.1 - '@aws-sdk/credential-provider-env@3.957.0': + '@aws-sdk/credential-provider-env@3.965.0': dependencies: - '@aws-sdk/core': 3.957.0 - '@aws-sdk/types': 3.957.0 + '@aws-sdk/core': 3.965.0 + '@aws-sdk/types': 3.965.0 '@smithy/property-provider': 4.2.7 '@smithy/types': 4.11.0 tslib: 2.8.1 - '@aws-sdk/credential-provider-http@3.957.0': + '@aws-sdk/credential-provider-http@3.965.0': dependencies: - '@aws-sdk/core': 3.957.0 - '@aws-sdk/types': 3.957.0 + '@aws-sdk/core': 3.965.0 + '@aws-sdk/types': 3.965.0 '@smithy/fetch-http-handler': 5.3.8 '@smithy/node-http-handler': 4.4.7 '@smithy/property-provider': 4.2.7 '@smithy/protocol-http': 5.3.7 - '@smithy/smithy-client': 4.10.2 + '@smithy/smithy-client': 4.10.3 '@smithy/types': 4.11.0 '@smithy/util-stream': 4.5.8 tslib: 2.8.1 - '@aws-sdk/credential-provider-ini@3.957.0': - dependencies: - '@aws-sdk/core': 3.957.0 - '@aws-sdk/credential-provider-env': 3.957.0 - '@aws-sdk/credential-provider-http': 3.957.0 - '@aws-sdk/credential-provider-login': 3.957.0 - '@aws-sdk/credential-provider-process': 3.957.0 - '@aws-sdk/credential-provider-sso': 3.957.0 - '@aws-sdk/credential-provider-web-identity': 3.957.0 - '@aws-sdk/nested-clients': 3.957.0 - '@aws-sdk/types': 3.957.0 + '@aws-sdk/credential-provider-ini@3.965.0': + dependencies: + '@aws-sdk/core': 3.965.0 + '@aws-sdk/credential-provider-env': 3.965.0 + '@aws-sdk/credential-provider-http': 3.965.0 + '@aws-sdk/credential-provider-login': 3.965.0 + '@aws-sdk/credential-provider-process': 3.965.0 + '@aws-sdk/credential-provider-sso': 3.965.0 + '@aws-sdk/credential-provider-web-identity': 3.965.0 + '@aws-sdk/nested-clients': 3.965.0 + '@aws-sdk/types': 3.965.0 '@smithy/credential-provider-imds': 4.2.7 '@smithy/property-provider': 4.2.7 '@smithy/shared-ini-file-loader': 4.4.2 @@ -4818,11 +4905,11 @@ snapshots: transitivePeerDependencies: - aws-crt - '@aws-sdk/credential-provider-login@3.957.0': + '@aws-sdk/credential-provider-login@3.965.0': dependencies: - '@aws-sdk/core': 3.957.0 - '@aws-sdk/nested-clients': 3.957.0 - '@aws-sdk/types': 3.957.0 + '@aws-sdk/core': 3.965.0 + '@aws-sdk/nested-clients': 3.965.0 + '@aws-sdk/types': 3.965.0 '@smithy/property-provider': 4.2.7 '@smithy/protocol-http': 5.3.7 '@smithy/shared-ini-file-loader': 4.4.2 @@ -4831,15 +4918,15 @@ snapshots: transitivePeerDependencies: - aws-crt - '@aws-sdk/credential-provider-node@3.957.0': + '@aws-sdk/credential-provider-node@3.965.0': dependencies: - '@aws-sdk/credential-provider-env': 3.957.0 - '@aws-sdk/credential-provider-http': 3.957.0 - '@aws-sdk/credential-provider-ini': 3.957.0 - '@aws-sdk/credential-provider-process': 3.957.0 - '@aws-sdk/credential-provider-sso': 3.957.0 - '@aws-sdk/credential-provider-web-identity': 3.957.0 - '@aws-sdk/types': 3.957.0 + '@aws-sdk/credential-provider-env': 3.965.0 + '@aws-sdk/credential-provider-http': 3.965.0 + '@aws-sdk/credential-provider-ini': 3.965.0 + '@aws-sdk/credential-provider-process': 3.965.0 + '@aws-sdk/credential-provider-sso': 3.965.0 + '@aws-sdk/credential-provider-web-identity': 3.965.0 + '@aws-sdk/types': 3.965.0 '@smithy/credential-provider-imds': 4.2.7 '@smithy/property-provider': 4.2.7 '@smithy/shared-ini-file-loader': 4.4.2 @@ -4848,21 +4935,21 @@ snapshots: transitivePeerDependencies: - aws-crt - '@aws-sdk/credential-provider-process@3.957.0': + '@aws-sdk/credential-provider-process@3.965.0': dependencies: - '@aws-sdk/core': 3.957.0 - '@aws-sdk/types': 3.957.0 + '@aws-sdk/core': 3.965.0 + '@aws-sdk/types': 3.965.0 '@smithy/property-provider': 4.2.7 '@smithy/shared-ini-file-loader': 4.4.2 '@smithy/types': 4.11.0 tslib: 2.8.1 - '@aws-sdk/credential-provider-sso@3.957.0': + '@aws-sdk/credential-provider-sso@3.965.0': dependencies: - '@aws-sdk/client-sso': 3.957.0 - '@aws-sdk/core': 3.957.0 - '@aws-sdk/token-providers': 3.957.0 - '@aws-sdk/types': 3.957.0 + '@aws-sdk/client-sso': 3.965.0 + '@aws-sdk/core': 3.965.0 + '@aws-sdk/token-providers': 3.965.0 + '@aws-sdk/types': 3.965.0 '@smithy/property-provider': 4.2.7 '@smithy/shared-ini-file-loader': 4.4.2 '@smithy/types': 4.11.0 @@ -4870,11 +4957,11 @@ snapshots: transitivePeerDependencies: - aws-crt - '@aws-sdk/credential-provider-web-identity@3.957.0': + '@aws-sdk/credential-provider-web-identity@3.965.0': dependencies: - '@aws-sdk/core': 3.957.0 - '@aws-sdk/nested-clients': 3.957.0 - '@aws-sdk/types': 3.957.0 + '@aws-sdk/core': 3.965.0 + '@aws-sdk/nested-clients': 3.965.0 + '@aws-sdk/types': 3.965.0 '@smithy/property-provider': 4.2.7 '@smithy/shared-ini-file-loader': 4.4.2 '@smithy/types': 4.11.0 @@ -4882,31 +4969,31 @@ snapshots: transitivePeerDependencies: - aws-crt - '@aws-sdk/middleware-bucket-endpoint@3.957.0': + '@aws-sdk/middleware-bucket-endpoint@3.965.0': dependencies: - '@aws-sdk/types': 3.957.0 - '@aws-sdk/util-arn-parser': 3.957.0 + '@aws-sdk/types': 3.965.0 + '@aws-sdk/util-arn-parser': 3.965.0 '@smithy/node-config-provider': 4.3.7 '@smithy/protocol-http': 5.3.7 '@smithy/types': 4.11.0 '@smithy/util-config-provider': 4.2.0 tslib: 2.8.1 - '@aws-sdk/middleware-expect-continue@3.957.0': + '@aws-sdk/middleware-expect-continue@3.965.0': dependencies: - '@aws-sdk/types': 3.957.0 + '@aws-sdk/types': 3.965.0 '@smithy/protocol-http': 5.3.7 '@smithy/types': 4.11.0 tslib: 2.8.1 - '@aws-sdk/middleware-flexible-checksums@3.957.0': + '@aws-sdk/middleware-flexible-checksums@3.965.0': dependencies: '@aws-crypto/crc32': 5.2.0 '@aws-crypto/crc32c': 5.2.0 '@aws-crypto/util': 5.2.0 - '@aws-sdk/core': 3.957.0 - '@aws-sdk/crc64-nvme': 3.957.0 - '@aws-sdk/types': 3.957.0 + '@aws-sdk/core': 3.965.0 + '@aws-sdk/crc64-nvme': 3.965.0 + '@aws-sdk/types': 3.965.0 '@smithy/is-array-buffer': 4.2.0 '@smithy/node-config-provider': 4.3.7 '@smithy/protocol-http': 5.3.7 @@ -4916,43 +5003,43 @@ snapshots: '@smithy/util-utf8': 4.2.0 tslib: 2.8.1 - '@aws-sdk/middleware-host-header@3.957.0': + '@aws-sdk/middleware-host-header@3.965.0': dependencies: - '@aws-sdk/types': 3.957.0 + '@aws-sdk/types': 3.965.0 '@smithy/protocol-http': 5.3.7 '@smithy/types': 4.11.0 tslib: 2.8.1 - '@aws-sdk/middleware-location-constraint@3.957.0': + '@aws-sdk/middleware-location-constraint@3.965.0': dependencies: - '@aws-sdk/types': 3.957.0 + '@aws-sdk/types': 3.965.0 '@smithy/types': 4.11.0 tslib: 2.8.1 - '@aws-sdk/middleware-logger@3.957.0': + '@aws-sdk/middleware-logger@3.965.0': dependencies: - '@aws-sdk/types': 3.957.0 + '@aws-sdk/types': 3.965.0 '@smithy/types': 4.11.0 tslib: 2.8.1 - '@aws-sdk/middleware-recursion-detection@3.957.0': + '@aws-sdk/middleware-recursion-detection@3.965.0': dependencies: - '@aws-sdk/types': 3.957.0 - '@aws/lambda-invoke-store': 0.2.2 + '@aws-sdk/types': 3.965.0 + '@aws/lambda-invoke-store': 0.2.3 '@smithy/protocol-http': 5.3.7 '@smithy/types': 4.11.0 tslib: 2.8.1 - '@aws-sdk/middleware-sdk-s3@3.957.0': + '@aws-sdk/middleware-sdk-s3@3.965.0': dependencies: - '@aws-sdk/core': 3.957.0 - '@aws-sdk/types': 3.957.0 - '@aws-sdk/util-arn-parser': 3.957.0 - '@smithy/core': 3.20.0 + '@aws-sdk/core': 3.965.0 + '@aws-sdk/types': 3.965.0 + '@aws-sdk/util-arn-parser': 3.965.0 + '@smithy/core': 3.20.1 '@smithy/node-config-provider': 4.3.7 '@smithy/protocol-http': 5.3.7 '@smithy/signature-v4': 5.3.7 - '@smithy/smithy-client': 4.10.2 + '@smithy/smithy-client': 4.10.3 '@smithy/types': 4.11.0 '@smithy/util-config-provider': 4.2.0 '@smithy/util-middleware': 4.2.7 @@ -4960,57 +5047,57 @@ snapshots: '@smithy/util-utf8': 4.2.0 tslib: 2.8.1 - '@aws-sdk/middleware-ssec@3.957.0': + '@aws-sdk/middleware-ssec@3.965.0': dependencies: - '@aws-sdk/types': 3.957.0 + '@aws-sdk/types': 3.965.0 '@smithy/types': 4.11.0 tslib: 2.8.1 - '@aws-sdk/middleware-user-agent@3.957.0': + '@aws-sdk/middleware-user-agent@3.965.0': dependencies: - '@aws-sdk/core': 3.957.0 - '@aws-sdk/types': 3.957.0 - '@aws-sdk/util-endpoints': 3.957.0 - '@smithy/core': 3.20.0 + '@aws-sdk/core': 3.965.0 + '@aws-sdk/types': 3.965.0 + '@aws-sdk/util-endpoints': 3.965.0 + '@smithy/core': 3.20.1 '@smithy/protocol-http': 5.3.7 '@smithy/types': 4.11.0 tslib: 2.8.1 - '@aws-sdk/nested-clients@3.957.0': + '@aws-sdk/nested-clients@3.965.0': dependencies: '@aws-crypto/sha256-browser': 5.2.0 '@aws-crypto/sha256-js': 5.2.0 - '@aws-sdk/core': 3.957.0 - '@aws-sdk/middleware-host-header': 3.957.0 - '@aws-sdk/middleware-logger': 3.957.0 - '@aws-sdk/middleware-recursion-detection': 3.957.0 - '@aws-sdk/middleware-user-agent': 3.957.0 - '@aws-sdk/region-config-resolver': 3.957.0 - '@aws-sdk/types': 3.957.0 - '@aws-sdk/util-endpoints': 3.957.0 - '@aws-sdk/util-user-agent-browser': 3.957.0 - '@aws-sdk/util-user-agent-node': 3.957.0 + '@aws-sdk/core': 3.965.0 + '@aws-sdk/middleware-host-header': 3.965.0 + '@aws-sdk/middleware-logger': 3.965.0 + '@aws-sdk/middleware-recursion-detection': 3.965.0 + '@aws-sdk/middleware-user-agent': 3.965.0 + '@aws-sdk/region-config-resolver': 3.965.0 + '@aws-sdk/types': 3.965.0 + '@aws-sdk/util-endpoints': 3.965.0 + '@aws-sdk/util-user-agent-browser': 3.965.0 + '@aws-sdk/util-user-agent-node': 3.965.0 '@smithy/config-resolver': 4.4.5 - '@smithy/core': 3.20.0 + '@smithy/core': 3.20.1 '@smithy/fetch-http-handler': 5.3.8 '@smithy/hash-node': 4.2.7 '@smithy/invalid-dependency': 4.2.7 '@smithy/middleware-content-length': 4.2.7 - '@smithy/middleware-endpoint': 4.4.1 - '@smithy/middleware-retry': 4.4.17 + '@smithy/middleware-endpoint': 4.4.2 + '@smithy/middleware-retry': 4.4.18 '@smithy/middleware-serde': 4.2.8 '@smithy/middleware-stack': 4.2.7 '@smithy/node-config-provider': 4.3.7 '@smithy/node-http-handler': 4.4.7 '@smithy/protocol-http': 5.3.7 - '@smithy/smithy-client': 4.10.2 + '@smithy/smithy-client': 4.10.3 '@smithy/types': 4.11.0 '@smithy/url-parser': 4.2.7 '@smithy/util-base64': 4.3.0 '@smithy/util-body-length-browser': 4.2.0 '@smithy/util-body-length-node': 4.2.1 - '@smithy/util-defaults-mode-browser': 4.3.16 - '@smithy/util-defaults-mode-node': 4.2.19 + '@smithy/util-defaults-mode-browser': 4.3.17 + '@smithy/util-defaults-mode-node': 4.2.20 '@smithy/util-endpoints': 3.2.7 '@smithy/util-middleware': 4.2.7 '@smithy/util-retry': 4.2.7 @@ -5019,39 +5106,39 @@ snapshots: transitivePeerDependencies: - aws-crt - '@aws-sdk/region-config-resolver@3.957.0': + '@aws-sdk/region-config-resolver@3.965.0': dependencies: - '@aws-sdk/types': 3.957.0 + '@aws-sdk/types': 3.965.0 '@smithy/config-resolver': 4.4.5 '@smithy/node-config-provider': 4.3.7 '@smithy/types': 4.11.0 tslib: 2.8.1 - '@aws-sdk/s3-request-presigner@3.957.0': + '@aws-sdk/s3-request-presigner@3.965.0': dependencies: - '@aws-sdk/signature-v4-multi-region': 3.957.0 - '@aws-sdk/types': 3.957.0 - '@aws-sdk/util-format-url': 3.957.0 - '@smithy/middleware-endpoint': 4.4.1 + '@aws-sdk/signature-v4-multi-region': 3.965.0 + '@aws-sdk/types': 3.965.0 + '@aws-sdk/util-format-url': 3.965.0 + '@smithy/middleware-endpoint': 4.4.2 '@smithy/protocol-http': 5.3.7 - '@smithy/smithy-client': 4.10.2 + '@smithy/smithy-client': 4.10.3 '@smithy/types': 4.11.0 tslib: 2.8.1 - '@aws-sdk/signature-v4-multi-region@3.957.0': + '@aws-sdk/signature-v4-multi-region@3.965.0': dependencies: - '@aws-sdk/middleware-sdk-s3': 3.957.0 - '@aws-sdk/types': 3.957.0 + '@aws-sdk/middleware-sdk-s3': 3.965.0 + '@aws-sdk/types': 3.965.0 '@smithy/protocol-http': 5.3.7 '@smithy/signature-v4': 5.3.7 '@smithy/types': 4.11.0 tslib: 2.8.1 - '@aws-sdk/token-providers@3.957.0': + '@aws-sdk/token-providers@3.965.0': dependencies: - '@aws-sdk/core': 3.957.0 - '@aws-sdk/nested-clients': 3.957.0 - '@aws-sdk/types': 3.957.0 + '@aws-sdk/core': 3.965.0 + '@aws-sdk/nested-clients': 3.965.0 + '@aws-sdk/types': 3.965.0 '@smithy/property-provider': 4.2.7 '@smithy/shared-ini-file-loader': 4.4.2 '@smithy/types': 4.11.0 @@ -5059,56 +5146,56 @@ snapshots: transitivePeerDependencies: - aws-crt - '@aws-sdk/types@3.957.0': + '@aws-sdk/types@3.965.0': dependencies: '@smithy/types': 4.11.0 tslib: 2.8.1 - '@aws-sdk/util-arn-parser@3.957.0': + '@aws-sdk/util-arn-parser@3.965.0': dependencies: tslib: 2.8.1 - '@aws-sdk/util-endpoints@3.957.0': + '@aws-sdk/util-endpoints@3.965.0': dependencies: - '@aws-sdk/types': 3.957.0 + '@aws-sdk/types': 3.965.0 '@smithy/types': 4.11.0 '@smithy/url-parser': 4.2.7 '@smithy/util-endpoints': 3.2.7 tslib: 2.8.1 - '@aws-sdk/util-format-url@3.957.0': + '@aws-sdk/util-format-url@3.965.0': dependencies: - '@aws-sdk/types': 3.957.0 + '@aws-sdk/types': 3.965.0 '@smithy/querystring-builder': 4.2.7 '@smithy/types': 4.11.0 tslib: 2.8.1 - '@aws-sdk/util-locate-window@3.957.0': + '@aws-sdk/util-locate-window@3.965.0': dependencies: tslib: 2.8.1 - '@aws-sdk/util-user-agent-browser@3.957.0': + '@aws-sdk/util-user-agent-browser@3.965.0': dependencies: - '@aws-sdk/types': 3.957.0 + '@aws-sdk/types': 3.965.0 '@smithy/types': 4.11.0 bowser: 2.13.1 tslib: 2.8.1 - '@aws-sdk/util-user-agent-node@3.957.0': + '@aws-sdk/util-user-agent-node@3.965.0': dependencies: - '@aws-sdk/middleware-user-agent': 3.957.0 - '@aws-sdk/types': 3.957.0 + '@aws-sdk/middleware-user-agent': 3.965.0 + '@aws-sdk/types': 3.965.0 '@smithy/node-config-provider': 4.3.7 '@smithy/types': 4.11.0 tslib: 2.8.1 - '@aws-sdk/xml-builder@3.957.0': + '@aws-sdk/xml-builder@3.965.0': dependencies: '@smithy/types': 4.11.0 fast-xml-parser: 5.2.5 tslib: 2.8.1 - '@aws/lambda-invoke-store@0.2.2': {} + '@aws/lambda-invoke-store@0.2.3': {} '@babel/code-frame@7.27.1': dependencies: @@ -5131,7 +5218,7 @@ snapshots: '@babel/types': 7.28.5 '@jridgewell/remapping': 2.3.5 convert-source-map: 2.0.0 - debug: 4.4.3 + debug: 4.4.3(supports-color@10.2.2) gensync: 1.0.0-beta.2 json5: 2.2.3 semver: 6.3.1 @@ -5288,7 +5375,7 @@ snapshots: '@babel/parser': 7.28.5 '@babel/template': 7.27.2 '@babel/types': 7.28.5 - debug: 4.4.3 + debug: 4.4.3(supports-color@10.2.2) transitivePeerDependencies: - supports-color @@ -5299,7 +5386,7 @@ snapshots: '@bcoe/v8-coverage@0.2.3': {} - '@borewit/text-codec@0.1.1': {} + '@borewit/text-codec@0.2.1': {} '@colors/colors@1.5.0': optional: true @@ -5308,13 +5395,13 @@ snapshots: dependencies: '@jridgewell/trace-mapping': 0.3.9 - '@emnapi/core@1.7.1': + '@emnapi/core@1.8.1': dependencies: '@emnapi/wasi-threads': 1.1.0 tslib: 2.8.1 optional: true - '@emnapi/runtime@1.7.1': + '@emnapi/runtime@1.8.1': dependencies: tslib: 2.8.1 optional: true @@ -5326,7 +5413,7 @@ snapshots: '@epic-web/invariant@1.0.0': {} - '@eslint-community/eslint-utils@4.9.0(eslint@8.57.1)': + '@eslint-community/eslint-utils@4.9.1(eslint@8.57.1)': dependencies: eslint: 8.57.1 eslint-visitor-keys: 3.4.3 @@ -5336,7 +5423,7 @@ snapshots: '@eslint/eslintrc@2.1.4': dependencies: ajv: 6.12.6 - debug: 4.4.3 + debug: 4.4.3(supports-color@10.2.2) espree: 9.6.1 globals: 13.24.0 ignore: 5.3.2 @@ -5355,6 +5442,12 @@ snapshots: graphql: 16.12.0 tslib: 2.8.1 + '@graphql-tools/merge@9.1.7(graphql@16.12.0)': + dependencies: + '@graphql-tools/utils': 11.0.0(graphql@16.12.0) + graphql: 16.12.0 + tslib: 2.8.1 + '@graphql-tools/schema@10.0.30(graphql@16.12.0)': dependencies: '@graphql-tools/merge': 9.1.6(graphql@16.12.0) @@ -5362,6 +5455,13 @@ snapshots: graphql: 16.12.0 tslib: 2.8.1 + '@graphql-tools/schema@10.0.31(graphql@16.12.0)': + dependencies: + '@graphql-tools/merge': 9.1.7(graphql@16.12.0) + '@graphql-tools/utils': 11.0.0(graphql@16.12.0) + graphql: 16.12.0 + tslib: 2.8.1 + '@graphql-tools/utils@10.11.0(graphql@16.12.0)': dependencies: '@graphql-typed-document-node/core': 3.2.0(graphql@16.12.0) @@ -5370,6 +5470,14 @@ snapshots: graphql: 16.12.0 tslib: 2.8.1 + '@graphql-tools/utils@11.0.0(graphql@16.12.0)': + dependencies: + '@graphql-typed-document-node/core': 3.2.0(graphql@16.12.0) + '@whatwg-node/promise-helpers': 1.3.2 + cross-inspect: 1.0.1 + graphql: 16.12.0 + tslib: 2.8.1 + '@graphql-typed-document-node/core@3.2.0(graphql@16.12.0)': dependencies: graphql: 16.12.0 @@ -5377,7 +5485,7 @@ snapshots: '@humanwhocodes/config-array@0.13.0': dependencies: '@humanwhocodes/object-schema': 2.0.3 - debug: 4.4.3 + debug: 4.4.3(supports-color@10.2.2) minimatch: 3.1.2 transitivePeerDependencies: - supports-color @@ -5395,6 +5503,10 @@ snapshots: wrap-ansi: 8.1.0 wrap-ansi-cjs: wrap-ansi@7.0.0 + '@isaacs/fs-minipass@4.0.1': + dependencies: + minipass: 7.1.2 + '@istanbuljs/load-nyc-config@1.1.0': dependencies: camelcase: 5.3.1 @@ -5604,28 +5716,28 @@ snapshots: '@napi-rs/wasm-runtime@0.2.12': dependencies: - '@emnapi/core': 1.7.1 - '@emnapi/runtime': 1.7.1 + '@emnapi/core': 1.8.1 + '@emnapi/runtime': 1.8.1 '@tybys/wasm-util': 0.10.1 optional: true - '@nestjs/apollo@13.2.3(@apollo/server@5.2.0(graphql@16.12.0))(@as-integrations/express5@1.1.2(@apollo/server@5.2.0(graphql@16.12.0))(express@4.21.2))(@nestjs/common@10.4.20(class-transformer@0.5.1)(class-validator@0.14.3)(reflect-metadata@0.1.14)(rxjs@7.8.2))(@nestjs/core@10.4.20)(@nestjs/graphql@13.2.3(@nestjs/common@10.4.20(class-transformer@0.5.1)(class-validator@0.14.3)(reflect-metadata@0.1.14)(rxjs@7.8.2))(@nestjs/core@10.4.20)(class-transformer@0.5.1)(class-validator@0.14.3)(graphql@16.12.0)(reflect-metadata@0.1.14))(graphql@16.12.0)': + '@nestjs/apollo@13.2.3(@apollo/server@5.2.0(graphql@16.12.0))(@as-integrations/express5@1.1.2(@apollo/server@5.2.0(graphql@16.12.0))(express@4.22.1))(@nestjs/common@10.4.21(class-transformer@0.5.1)(class-validator@0.14.3)(reflect-metadata@0.1.14)(rxjs@7.8.2))(@nestjs/core@10.4.21)(@nestjs/graphql@13.2.3(@nestjs/common@10.4.21(class-transformer@0.5.1)(class-validator@0.14.3)(reflect-metadata@0.1.14)(rxjs@7.8.2))(@nestjs/core@10.4.21)(class-transformer@0.5.1)(class-validator@0.14.3)(graphql@16.12.0)(reflect-metadata@0.1.14))(graphql@16.12.0)': dependencies: '@apollo/server': 5.2.0(graphql@16.12.0) '@apollo/server-plugin-landing-page-graphql-playground': 4.0.1(@apollo/server@5.2.0(graphql@16.12.0)) - '@nestjs/common': 10.4.20(class-transformer@0.5.1)(class-validator@0.14.3)(reflect-metadata@0.1.14)(rxjs@7.8.2) - '@nestjs/core': 10.4.20(@nestjs/common@10.4.20(class-transformer@0.5.1)(class-validator@0.14.3)(reflect-metadata@0.1.14)(rxjs@7.8.2))(@nestjs/platform-express@10.4.20)(@nestjs/websockets@11.1.11)(reflect-metadata@0.1.14)(rxjs@7.8.2) - '@nestjs/graphql': 13.2.3(@nestjs/common@10.4.20(class-transformer@0.5.1)(class-validator@0.14.3)(reflect-metadata@0.1.14)(rxjs@7.8.2))(@nestjs/core@10.4.20)(class-transformer@0.5.1)(class-validator@0.14.3)(graphql@16.12.0)(reflect-metadata@0.1.14) + '@nestjs/common': 10.4.21(class-transformer@0.5.1)(class-validator@0.14.3)(reflect-metadata@0.1.14)(rxjs@7.8.2) + '@nestjs/core': 10.4.21(@nestjs/common@10.4.21(class-transformer@0.5.1)(class-validator@0.14.3)(reflect-metadata@0.1.14)(rxjs@7.8.2))(@nestjs/platform-express@10.4.21)(@nestjs/websockets@10.4.21)(reflect-metadata@0.1.14)(rxjs@7.8.2) + '@nestjs/graphql': 13.2.3(@nestjs/common@10.4.21(class-transformer@0.5.1)(class-validator@0.14.3)(reflect-metadata@0.1.14)(rxjs@7.8.2))(@nestjs/core@10.4.21)(class-transformer@0.5.1)(class-validator@0.14.3)(graphql@16.12.0)(reflect-metadata@0.1.14) graphql: 16.12.0 iterall: 1.3.0 lodash.omit: 4.5.0 tslib: 2.8.1 optionalDependencies: - '@as-integrations/express5': 1.1.2(@apollo/server@5.2.0(graphql@16.12.0))(express@4.21.2) + '@as-integrations/express5': 1.1.2(@apollo/server@5.2.0(graphql@16.12.0))(express@4.22.1) - '@nestjs/axios@4.0.1(@nestjs/common@10.4.20(class-transformer@0.5.1)(class-validator@0.14.3)(reflect-metadata@0.1.14)(rxjs@7.8.2))(axios@1.13.2)(rxjs@7.8.2)': + '@nestjs/axios@4.0.1(@nestjs/common@10.4.21(class-transformer@0.5.1)(class-validator@0.14.3)(reflect-metadata@0.1.14)(rxjs@7.8.2))(axios@1.13.2)(rxjs@7.8.2)': dependencies: - '@nestjs/common': 10.4.20(class-transformer@0.5.1)(class-validator@0.14.3)(reflect-metadata@0.1.14)(rxjs@7.8.2) + '@nestjs/common': 10.4.21(class-transformer@0.5.1)(class-validator@0.14.3)(reflect-metadata@0.1.14)(rxjs@7.8.2) axios: 1.13.2 rxjs: 7.8.2 @@ -5655,7 +5767,7 @@ snapshots: - uglify-js - webpack-cli - '@nestjs/common@10.4.20(class-transformer@0.5.1)(class-validator@0.14.3)(reflect-metadata@0.1.14)(rxjs@7.8.2)': + '@nestjs/common@10.4.21(class-transformer@0.5.1)(class-validator@0.14.3)(reflect-metadata@0.1.14)(rxjs@7.8.2)': dependencies: file-type: 20.4.1 iterare: 1.2.1 @@ -5669,17 +5781,17 @@ snapshots: transitivePeerDependencies: - supports-color - '@nestjs/config@4.0.2(@nestjs/common@10.4.20(class-transformer@0.5.1)(class-validator@0.14.3)(reflect-metadata@0.1.14)(rxjs@7.8.2))(rxjs@7.8.2)': + '@nestjs/config@4.0.2(@nestjs/common@10.4.21(class-transformer@0.5.1)(class-validator@0.14.3)(reflect-metadata@0.1.14)(rxjs@7.8.2))(rxjs@7.8.2)': dependencies: - '@nestjs/common': 10.4.20(class-transformer@0.5.1)(class-validator@0.14.3)(reflect-metadata@0.1.14)(rxjs@7.8.2) + '@nestjs/common': 10.4.21(class-transformer@0.5.1)(class-validator@0.14.3)(reflect-metadata@0.1.14)(rxjs@7.8.2) dotenv: 16.4.7 dotenv-expand: 12.0.1 lodash: 4.17.21 rxjs: 7.8.2 - '@nestjs/core@10.4.20(@nestjs/common@10.4.20(class-transformer@0.5.1)(class-validator@0.14.3)(reflect-metadata@0.1.14)(rxjs@7.8.2))(@nestjs/platform-express@10.4.20)(@nestjs/websockets@11.1.11)(reflect-metadata@0.1.14)(rxjs@7.8.2)': + '@nestjs/core@10.4.21(@nestjs/common@10.4.21(class-transformer@0.5.1)(class-validator@0.14.3)(reflect-metadata@0.1.14)(rxjs@7.8.2))(@nestjs/platform-express@10.4.21)(@nestjs/websockets@10.4.21)(reflect-metadata@0.1.14)(rxjs@7.8.2)': dependencies: - '@nestjs/common': 10.4.20(class-transformer@0.5.1)(class-validator@0.14.3)(reflect-metadata@0.1.14)(rxjs@7.8.2) + '@nestjs/common': 10.4.21(class-transformer@0.5.1)(class-validator@0.14.3)(reflect-metadata@0.1.14)(rxjs@7.8.2) '@nuxtjs/opencollective': 0.3.2 fast-safe-stringify: 2.1.1 iterare: 1.2.1 @@ -5689,19 +5801,19 @@ snapshots: tslib: 2.8.1 uid: 2.0.2 optionalDependencies: - '@nestjs/platform-express': 10.4.20(@nestjs/common@10.4.20(class-transformer@0.5.1)(class-validator@0.14.3)(reflect-metadata@0.1.14)(rxjs@7.8.2))(@nestjs/core@10.4.20) - '@nestjs/websockets': 11.1.11(@nestjs/common@10.4.20(class-transformer@0.5.1)(class-validator@0.14.3)(reflect-metadata@0.1.14)(rxjs@7.8.2))(@nestjs/core@10.4.20)(@nestjs/platform-socket.io@11.1.11)(reflect-metadata@0.1.14)(rxjs@7.8.2) + '@nestjs/platform-express': 10.4.21(@nestjs/common@10.4.21(class-transformer@0.5.1)(class-validator@0.14.3)(reflect-metadata@0.1.14)(rxjs@7.8.2))(@nestjs/core@10.4.21) + '@nestjs/websockets': 10.4.21(@nestjs/common@10.4.21(class-transformer@0.5.1)(class-validator@0.14.3)(reflect-metadata@0.1.14)(rxjs@7.8.2))(@nestjs/core@10.4.21)(@nestjs/platform-socket.io@10.4.21)(reflect-metadata@0.1.14)(rxjs@7.8.2) transitivePeerDependencies: - encoding - '@nestjs/graphql@13.2.3(@nestjs/common@10.4.20(class-transformer@0.5.1)(class-validator@0.14.3)(reflect-metadata@0.1.14)(rxjs@7.8.2))(@nestjs/core@10.4.20)(class-transformer@0.5.1)(class-validator@0.14.3)(graphql@16.12.0)(reflect-metadata@0.1.14)': + '@nestjs/graphql@13.2.3(@nestjs/common@10.4.21(class-transformer@0.5.1)(class-validator@0.14.3)(reflect-metadata@0.1.14)(rxjs@7.8.2))(@nestjs/core@10.4.21)(class-transformer@0.5.1)(class-validator@0.14.3)(graphql@16.12.0)(reflect-metadata@0.1.14)': dependencies: '@graphql-tools/merge': 9.1.6(graphql@16.12.0) '@graphql-tools/schema': 10.0.30(graphql@16.12.0) '@graphql-tools/utils': 10.11.0(graphql@16.12.0) - '@nestjs/common': 10.4.20(class-transformer@0.5.1)(class-validator@0.14.3)(reflect-metadata@0.1.14)(rxjs@7.8.2) - '@nestjs/core': 10.4.20(@nestjs/common@10.4.20(class-transformer@0.5.1)(class-validator@0.14.3)(reflect-metadata@0.1.14)(rxjs@7.8.2))(@nestjs/platform-express@10.4.20)(@nestjs/websockets@11.1.11)(reflect-metadata@0.1.14)(rxjs@7.8.2) - '@nestjs/mapped-types': 2.1.0(@nestjs/common@10.4.20(class-transformer@0.5.1)(class-validator@0.14.3)(reflect-metadata@0.1.14)(rxjs@7.8.2))(class-transformer@0.5.1)(class-validator@0.14.3)(reflect-metadata@0.1.14) + '@nestjs/common': 10.4.21(class-transformer@0.5.1)(class-validator@0.14.3)(reflect-metadata@0.1.14)(rxjs@7.8.2) + '@nestjs/core': 10.4.21(@nestjs/common@10.4.21(class-transformer@0.5.1)(class-validator@0.14.3)(reflect-metadata@0.1.14)(rxjs@7.8.2))(@nestjs/platform-express@10.4.21)(@nestjs/websockets@10.4.21)(reflect-metadata@0.1.14)(rxjs@7.8.2) + '@nestjs/mapped-types': 2.1.0(@nestjs/common@10.4.21(class-transformer@0.5.1)(class-validator@0.14.3)(reflect-metadata@0.1.14)(rxjs@7.8.2))(class-transformer@0.5.1)(class-validator@0.14.3)(reflect-metadata@0.1.14) chokidar: 4.0.3 fast-glob: 3.3.3 graphql: 16.12.0 @@ -5723,32 +5835,32 @@ snapshots: - uWebSockets.js - utf-8-validate - '@nestjs/mapped-types@2.1.0(@nestjs/common@10.4.20(class-transformer@0.5.1)(class-validator@0.14.3)(reflect-metadata@0.1.14)(rxjs@7.8.2))(class-transformer@0.5.1)(class-validator@0.14.3)(reflect-metadata@0.1.14)': + '@nestjs/mapped-types@2.1.0(@nestjs/common@10.4.21(class-transformer@0.5.1)(class-validator@0.14.3)(reflect-metadata@0.1.14)(rxjs@7.8.2))(class-transformer@0.5.1)(class-validator@0.14.3)(reflect-metadata@0.1.14)': dependencies: - '@nestjs/common': 10.4.20(class-transformer@0.5.1)(class-validator@0.14.3)(reflect-metadata@0.1.14)(rxjs@7.8.2) + '@nestjs/common': 10.4.21(class-transformer@0.5.1)(class-validator@0.14.3)(reflect-metadata@0.1.14)(rxjs@7.8.2) reflect-metadata: 0.1.14 optionalDependencies: class-transformer: 0.5.1 class-validator: 0.14.3 - '@nestjs/platform-express@10.4.20(@nestjs/common@10.4.20(class-transformer@0.5.1)(class-validator@0.14.3)(reflect-metadata@0.1.14)(rxjs@7.8.2))(@nestjs/core@10.4.20)': + '@nestjs/platform-express@10.4.21(@nestjs/common@10.4.21(class-transformer@0.5.1)(class-validator@0.14.3)(reflect-metadata@0.1.14)(rxjs@7.8.2))(@nestjs/core@10.4.21)': dependencies: - '@nestjs/common': 10.4.20(class-transformer@0.5.1)(class-validator@0.14.3)(reflect-metadata@0.1.14)(rxjs@7.8.2) - '@nestjs/core': 10.4.20(@nestjs/common@10.4.20(class-transformer@0.5.1)(class-validator@0.14.3)(reflect-metadata@0.1.14)(rxjs@7.8.2))(@nestjs/platform-express@10.4.20)(@nestjs/websockets@11.1.11)(reflect-metadata@0.1.14)(rxjs@7.8.2) + '@nestjs/common': 10.4.21(class-transformer@0.5.1)(class-validator@0.14.3)(reflect-metadata@0.1.14)(rxjs@7.8.2) + '@nestjs/core': 10.4.21(@nestjs/common@10.4.21(class-transformer@0.5.1)(class-validator@0.14.3)(reflect-metadata@0.1.14)(rxjs@7.8.2))(@nestjs/platform-express@10.4.21)(@nestjs/websockets@10.4.21)(reflect-metadata@0.1.14)(rxjs@7.8.2) body-parser: 1.20.3 cors: 2.8.5 - express: 4.21.2 + express: 4.22.1 multer: 2.0.2 tslib: 2.8.1 transitivePeerDependencies: - supports-color - '@nestjs/platform-socket.io@11.1.11(@nestjs/common@10.4.20(class-transformer@0.5.1)(class-validator@0.14.3)(reflect-metadata@0.1.14)(rxjs@7.8.2))(@nestjs/websockets@11.1.11)(rxjs@7.8.2)': + '@nestjs/platform-socket.io@10.4.21(@nestjs/common@10.4.21(class-transformer@0.5.1)(class-validator@0.14.3)(reflect-metadata@0.1.14)(rxjs@7.8.2))(@nestjs/websockets@10.4.21)(rxjs@7.8.2)': dependencies: - '@nestjs/common': 10.4.20(class-transformer@0.5.1)(class-validator@0.14.3)(reflect-metadata@0.1.14)(rxjs@7.8.2) - '@nestjs/websockets': 11.1.11(@nestjs/common@10.4.20(class-transformer@0.5.1)(class-validator@0.14.3)(reflect-metadata@0.1.14)(rxjs@7.8.2))(@nestjs/core@10.4.20)(@nestjs/platform-socket.io@11.1.11)(reflect-metadata@0.1.14)(rxjs@7.8.2) + '@nestjs/common': 10.4.21(class-transformer@0.5.1)(class-validator@0.14.3)(reflect-metadata@0.1.14)(rxjs@7.8.2) + '@nestjs/websockets': 10.4.21(@nestjs/common@10.4.21(class-transformer@0.5.1)(class-validator@0.14.3)(reflect-metadata@0.1.14)(rxjs@7.8.2))(@nestjs/core@10.4.21)(@nestjs/platform-socket.io@10.4.21)(reflect-metadata@0.1.14)(rxjs@7.8.2) rxjs: 7.8.2 - socket.io: 4.8.3 + socket.io: 4.8.1 tslib: 2.8.1 transitivePeerDependencies: - bufferutil @@ -5777,25 +5889,25 @@ snapshots: transitivePeerDependencies: - chokidar - '@nestjs/testing@10.4.20(@nestjs/common@10.4.20(class-transformer@0.5.1)(class-validator@0.14.3)(reflect-metadata@0.1.14)(rxjs@7.8.2))(@nestjs/core@10.4.20)(@nestjs/platform-express@10.4.20)': + '@nestjs/testing@10.4.21(@nestjs/common@10.4.21(class-transformer@0.5.1)(class-validator@0.14.3)(reflect-metadata@0.1.14)(rxjs@7.8.2))(@nestjs/core@10.4.21)(@nestjs/platform-express@10.4.21)': dependencies: - '@nestjs/common': 10.4.20(class-transformer@0.5.1)(class-validator@0.14.3)(reflect-metadata@0.1.14)(rxjs@7.8.2) - '@nestjs/core': 10.4.20(@nestjs/common@10.4.20(class-transformer@0.5.1)(class-validator@0.14.3)(reflect-metadata@0.1.14)(rxjs@7.8.2))(@nestjs/platform-express@10.4.20)(@nestjs/websockets@11.1.11)(reflect-metadata@0.1.14)(rxjs@7.8.2) + '@nestjs/common': 10.4.21(class-transformer@0.5.1)(class-validator@0.14.3)(reflect-metadata@0.1.14)(rxjs@7.8.2) + '@nestjs/core': 10.4.21(@nestjs/common@10.4.21(class-transformer@0.5.1)(class-validator@0.14.3)(reflect-metadata@0.1.14)(rxjs@7.8.2))(@nestjs/platform-express@10.4.21)(@nestjs/websockets@10.4.21)(reflect-metadata@0.1.14)(rxjs@7.8.2) tslib: 2.8.1 optionalDependencies: - '@nestjs/platform-express': 10.4.20(@nestjs/common@10.4.20(class-transformer@0.5.1)(class-validator@0.14.3)(reflect-metadata@0.1.14)(rxjs@7.8.2))(@nestjs/core@10.4.20) + '@nestjs/platform-express': 10.4.21(@nestjs/common@10.4.21(class-transformer@0.5.1)(class-validator@0.14.3)(reflect-metadata@0.1.14)(rxjs@7.8.2))(@nestjs/core@10.4.21) - '@nestjs/websockets@11.1.11(@nestjs/common@10.4.20(class-transformer@0.5.1)(class-validator@0.14.3)(reflect-metadata@0.1.14)(rxjs@7.8.2))(@nestjs/core@10.4.20)(@nestjs/platform-socket.io@11.1.11)(reflect-metadata@0.1.14)(rxjs@7.8.2)': + '@nestjs/websockets@10.4.21(@nestjs/common@10.4.21(class-transformer@0.5.1)(class-validator@0.14.3)(reflect-metadata@0.1.14)(rxjs@7.8.2))(@nestjs/core@10.4.21)(@nestjs/platform-socket.io@10.4.21)(reflect-metadata@0.1.14)(rxjs@7.8.2)': dependencies: - '@nestjs/common': 10.4.20(class-transformer@0.5.1)(class-validator@0.14.3)(reflect-metadata@0.1.14)(rxjs@7.8.2) - '@nestjs/core': 10.4.20(@nestjs/common@10.4.20(class-transformer@0.5.1)(class-validator@0.14.3)(reflect-metadata@0.1.14)(rxjs@7.8.2))(@nestjs/platform-express@10.4.20)(@nestjs/websockets@11.1.11)(reflect-metadata@0.1.14)(rxjs@7.8.2) + '@nestjs/common': 10.4.21(class-transformer@0.5.1)(class-validator@0.14.3)(reflect-metadata@0.1.14)(rxjs@7.8.2) + '@nestjs/core': 10.4.21(@nestjs/common@10.4.21(class-transformer@0.5.1)(class-validator@0.14.3)(reflect-metadata@0.1.14)(rxjs@7.8.2))(@nestjs/platform-express@10.4.21)(@nestjs/websockets@10.4.21)(reflect-metadata@0.1.14)(rxjs@7.8.2) iterare: 1.2.1 object-hash: 3.0.0 reflect-metadata: 0.1.14 rxjs: 7.8.2 tslib: 2.8.1 optionalDependencies: - '@nestjs/platform-socket.io': 11.1.11(@nestjs/common@10.4.20(class-transformer@0.5.1)(class-validator@0.14.3)(reflect-metadata@0.1.14)(rxjs@7.8.2))(@nestjs/websockets@11.1.11)(rxjs@7.8.2) + '@nestjs/platform-socket.io': 10.4.21(@nestjs/common@10.4.21(class-transformer@0.5.1)(class-validator@0.14.3)(reflect-metadata@0.1.14)(rxjs@7.8.2))(@nestjs/websockets@10.4.21)(rxjs@7.8.2) '@noble/hashes@1.8.0': {} @@ -5809,7 +5921,7 @@ snapshots: '@nodelib/fs.walk@1.2.8': dependencies: '@nodelib/fs.scandir': 2.1.5 - fastq: 1.19.1 + fastq: 1.20.1 '@nuxtjs/opencollective@0.3.2': dependencies: @@ -5908,7 +6020,7 @@ snapshots: '@smithy/util-middleware': 4.2.7 tslib: 2.8.1 - '@smithy/core@3.20.0': + '@smithy/core@3.20.1': dependencies: '@smithy/middleware-serde': 4.2.8 '@smithy/protocol-http': 5.3.7 @@ -6012,9 +6124,9 @@ snapshots: '@smithy/types': 4.11.0 tslib: 2.8.1 - '@smithy/middleware-endpoint@4.4.1': + '@smithy/middleware-endpoint@4.4.2': dependencies: - '@smithy/core': 3.20.0 + '@smithy/core': 3.20.1 '@smithy/middleware-serde': 4.2.8 '@smithy/node-config-provider': 4.3.7 '@smithy/shared-ini-file-loader': 4.4.2 @@ -6023,12 +6135,12 @@ snapshots: '@smithy/util-middleware': 4.2.7 tslib: 2.8.1 - '@smithy/middleware-retry@4.4.17': + '@smithy/middleware-retry@4.4.18': dependencies: '@smithy/node-config-provider': 4.3.7 '@smithy/protocol-http': 5.3.7 '@smithy/service-error-classification': 4.2.7 - '@smithy/smithy-client': 4.10.2 + '@smithy/smithy-client': 4.10.3 '@smithy/types': 4.11.0 '@smithy/util-middleware': 4.2.7 '@smithy/util-retry': 4.2.7 @@ -6102,10 +6214,10 @@ snapshots: '@smithy/util-utf8': 4.2.0 tslib: 2.8.1 - '@smithy/smithy-client@4.10.2': + '@smithy/smithy-client@4.10.3': dependencies: - '@smithy/core': 3.20.0 - '@smithy/middleware-endpoint': 4.4.1 + '@smithy/core': 3.20.1 + '@smithy/middleware-endpoint': 4.4.2 '@smithy/middleware-stack': 4.2.7 '@smithy/protocol-http': 5.3.7 '@smithy/types': 4.11.0 @@ -6150,20 +6262,20 @@ snapshots: dependencies: tslib: 2.8.1 - '@smithy/util-defaults-mode-browser@4.3.16': + '@smithy/util-defaults-mode-browser@4.3.17': dependencies: '@smithy/property-provider': 4.2.7 - '@smithy/smithy-client': 4.10.2 + '@smithy/smithy-client': 4.10.3 '@smithy/types': 4.11.0 tslib: 2.8.1 - '@smithy/util-defaults-mode-node@4.2.19': + '@smithy/util-defaults-mode-node@4.2.20': dependencies: '@smithy/config-resolver': 4.4.5 '@smithy/credential-provider-imds': 4.2.7 '@smithy/node-config-provider': 4.3.7 '@smithy/property-provider': 4.2.7 - '@smithy/smithy-client': 4.10.2 + '@smithy/smithy-client': 4.10.3 '@smithy/types': 4.11.0 tslib: 2.8.1 @@ -6236,9 +6348,9 @@ snapshots: '@tokenizer/inflate@0.2.7': dependencies: - debug: 4.4.3 + debug: 4.4.3(supports-color@10.2.2) fflate: 0.8.2 - token-types: 6.1.1 + token-types: 6.1.2 transitivePeerDependencies: - supports-color @@ -6329,6 +6441,8 @@ snapshots: '@types/http-errors@2.0.5': {} + '@types/ini@4.1.1': {} + '@types/istanbul-lib-coverage@2.0.6': {} '@types/istanbul-lib-report@3.0.3': @@ -6398,95 +6512,95 @@ snapshots: dependencies: '@types/yargs-parser': 21.0.3 - '@typescript-eslint/eslint-plugin@8.50.0(@typescript-eslint/parser@8.50.0(eslint@8.57.1)(typescript@5.9.3))(eslint@8.57.1)(typescript@5.9.3)': + '@typescript-eslint/eslint-plugin@8.52.0(@typescript-eslint/parser@8.52.0(eslint@8.57.1)(typescript@5.9.3))(eslint@8.57.1)(typescript@5.9.3)': dependencies: '@eslint-community/regexpp': 4.12.2 - '@typescript-eslint/parser': 8.50.0(eslint@8.57.1)(typescript@5.9.3) - '@typescript-eslint/scope-manager': 8.50.0 - '@typescript-eslint/type-utils': 8.50.0(eslint@8.57.1)(typescript@5.9.3) - '@typescript-eslint/utils': 8.50.0(eslint@8.57.1)(typescript@5.9.3) - '@typescript-eslint/visitor-keys': 8.50.0 + '@typescript-eslint/parser': 8.52.0(eslint@8.57.1)(typescript@5.9.3) + '@typescript-eslint/scope-manager': 8.52.0 + '@typescript-eslint/type-utils': 8.52.0(eslint@8.57.1)(typescript@5.9.3) + '@typescript-eslint/utils': 8.52.0(eslint@8.57.1)(typescript@5.9.3) + '@typescript-eslint/visitor-keys': 8.52.0 eslint: 8.57.1 ignore: 7.0.5 natural-compare: 1.4.0 - ts-api-utils: 2.1.0(typescript@5.9.3) + ts-api-utils: 2.4.0(typescript@5.9.3) typescript: 5.9.3 transitivePeerDependencies: - supports-color - '@typescript-eslint/parser@8.50.0(eslint@8.57.1)(typescript@5.9.3)': + '@typescript-eslint/parser@8.52.0(eslint@8.57.1)(typescript@5.9.3)': dependencies: - '@typescript-eslint/scope-manager': 8.50.0 - '@typescript-eslint/types': 8.50.0 - '@typescript-eslint/typescript-estree': 8.50.0(typescript@5.9.3) - '@typescript-eslint/visitor-keys': 8.50.0 - debug: 4.4.3 + '@typescript-eslint/scope-manager': 8.52.0 + '@typescript-eslint/types': 8.52.0 + '@typescript-eslint/typescript-estree': 8.52.0(typescript@5.9.3) + '@typescript-eslint/visitor-keys': 8.52.0 + debug: 4.4.3(supports-color@10.2.2) eslint: 8.57.1 typescript: 5.9.3 transitivePeerDependencies: - supports-color - '@typescript-eslint/project-service@8.50.0(typescript@5.9.3)': + '@typescript-eslint/project-service@8.52.0(typescript@5.9.3)': dependencies: - '@typescript-eslint/tsconfig-utils': 8.50.0(typescript@5.9.3) - '@typescript-eslint/types': 8.50.0 - debug: 4.4.3 + '@typescript-eslint/tsconfig-utils': 8.52.0(typescript@5.9.3) + '@typescript-eslint/types': 8.52.0 + debug: 4.4.3(supports-color@10.2.2) typescript: 5.9.3 transitivePeerDependencies: - supports-color - '@typescript-eslint/scope-manager@8.50.0': + '@typescript-eslint/scope-manager@8.52.0': dependencies: - '@typescript-eslint/types': 8.50.0 - '@typescript-eslint/visitor-keys': 8.50.0 + '@typescript-eslint/types': 8.52.0 + '@typescript-eslint/visitor-keys': 8.52.0 - '@typescript-eslint/tsconfig-utils@8.50.0(typescript@5.9.3)': + '@typescript-eslint/tsconfig-utils@8.52.0(typescript@5.9.3)': dependencies: typescript: 5.9.3 - '@typescript-eslint/type-utils@8.50.0(eslint@8.57.1)(typescript@5.9.3)': + '@typescript-eslint/type-utils@8.52.0(eslint@8.57.1)(typescript@5.9.3)': dependencies: - '@typescript-eslint/types': 8.50.0 - '@typescript-eslint/typescript-estree': 8.50.0(typescript@5.9.3) - '@typescript-eslint/utils': 8.50.0(eslint@8.57.1)(typescript@5.9.3) - debug: 4.4.3 + '@typescript-eslint/types': 8.52.0 + '@typescript-eslint/typescript-estree': 8.52.0(typescript@5.9.3) + '@typescript-eslint/utils': 8.52.0(eslint@8.57.1)(typescript@5.9.3) + debug: 4.4.3(supports-color@10.2.2) eslint: 8.57.1 - ts-api-utils: 2.1.0(typescript@5.9.3) + ts-api-utils: 2.4.0(typescript@5.9.3) typescript: 5.9.3 transitivePeerDependencies: - supports-color - '@typescript-eslint/types@8.50.0': {} + '@typescript-eslint/types@8.52.0': {} - '@typescript-eslint/typescript-estree@8.50.0(typescript@5.9.3)': + '@typescript-eslint/typescript-estree@8.52.0(typescript@5.9.3)': dependencies: - '@typescript-eslint/project-service': 8.50.0(typescript@5.9.3) - '@typescript-eslint/tsconfig-utils': 8.50.0(typescript@5.9.3) - '@typescript-eslint/types': 8.50.0 - '@typescript-eslint/visitor-keys': 8.50.0 - debug: 4.4.3 + '@typescript-eslint/project-service': 8.52.0(typescript@5.9.3) + '@typescript-eslint/tsconfig-utils': 8.52.0(typescript@5.9.3) + '@typescript-eslint/types': 8.52.0 + '@typescript-eslint/visitor-keys': 8.52.0 + debug: 4.4.3(supports-color@10.2.2) minimatch: 9.0.5 semver: 7.7.3 tinyglobby: 0.2.15 - ts-api-utils: 2.1.0(typescript@5.9.3) + ts-api-utils: 2.4.0(typescript@5.9.3) typescript: 5.9.3 transitivePeerDependencies: - supports-color - '@typescript-eslint/utils@8.50.0(eslint@8.57.1)(typescript@5.9.3)': + '@typescript-eslint/utils@8.52.0(eslint@8.57.1)(typescript@5.9.3)': dependencies: - '@eslint-community/eslint-utils': 4.9.0(eslint@8.57.1) - '@typescript-eslint/scope-manager': 8.50.0 - '@typescript-eslint/types': 8.50.0 - '@typescript-eslint/typescript-estree': 8.50.0(typescript@5.9.3) + '@eslint-community/eslint-utils': 4.9.1(eslint@8.57.1) + '@typescript-eslint/scope-manager': 8.52.0 + '@typescript-eslint/types': 8.52.0 + '@typescript-eslint/typescript-estree': 8.52.0(typescript@5.9.3) eslint: 8.57.1 typescript: 5.9.3 transitivePeerDependencies: - supports-color - '@typescript-eslint/visitor-keys@8.50.0': + '@typescript-eslint/visitor-keys@8.52.0': dependencies: - '@typescript-eslint/types': 8.50.0 + '@typescript-eslint/types': 8.52.0 eslint-visitor-keys: 4.2.1 '@ungap/structured-clone@1.3.0': {} @@ -6868,7 +6982,7 @@ snapshots: base64id@2.0.0: {} - baseline-browser-mapping@2.9.8: {} + baseline-browser-mapping@2.9.13: {} binary-extensions@2.3.0: {} @@ -6895,15 +7009,15 @@ snapshots: transitivePeerDependencies: - supports-color - body-parser@2.2.1: + body-parser@2.2.2: dependencies: bytes: 3.1.2 content-type: 1.0.5 - debug: 4.4.3 - http-errors: 2.0.0 - iconv-lite: 0.7.1 + debug: 4.4.3(supports-color@10.2.2) + http-errors: 2.0.1 + iconv-lite: 0.7.2 on-finished: 2.4.1 - qs: 6.14.0 + qs: 6.14.1 raw-body: 3.0.2 type-is: 2.0.1 transitivePeerDependencies: @@ -6926,8 +7040,8 @@ snapshots: browserslist@4.28.1: dependencies: - baseline-browser-mapping: 2.9.8 - caniuse-lite: 1.0.30001760 + baseline-browser-mapping: 2.9.13 + caniuse-lite: 1.0.30001763 electron-to-chromium: 1.5.267 node-releases: 2.0.27 update-browserslist-db: 1.2.3(browserslist@4.28.1) @@ -6976,7 +7090,7 @@ snapshots: camelcase@6.3.0: {} - caniuse-lite@1.0.30001760: {} + caniuse-lite@1.0.30001763: {} chalk@4.1.2: dependencies: @@ -7005,6 +7119,8 @@ snapshots: dependencies: readdirp: 4.1.2 + chownr@3.0.0: {} + chrome-trace-event@1.0.4: {} ci-info@3.9.0: {} @@ -7016,8 +7132,8 @@ snapshots: class-validator@0.14.3: dependencies: '@types/validator': 13.15.10 - libphonenumber-js: 1.12.31 - validator: 13.15.23 + libphonenumber-js: 1.12.33 + validator: 13.15.26 cli-cursor@3.1.0: dependencies: @@ -7099,7 +7215,7 @@ snapshots: cookie-signature@1.0.6: {} - cookie@0.7.1: {} + cookie-signature@1.0.7: {} cookie@0.7.2: {} @@ -7157,6 +7273,8 @@ snapshots: cssfilter@0.0.10: {} + data-uri-to-buffer@4.0.1: {} + data-view-buffer@1.0.2: dependencies: call-bound: 1.0.4 @@ -7187,11 +7305,13 @@ snapshots: dependencies: ms: 2.1.3 - debug@4.4.3: + debug@4.4.3(supports-color@10.2.2): dependencies: ms: 2.1.3 + optionalDependencies: + supports-color: 10.2.2 - dedent@1.7.0: {} + dedent@1.7.1: {} deep-is@0.1.4: {} @@ -7266,8 +7386,6 @@ snapshots: emoji-regex@9.2.2: {} - encodeurl@1.0.2: {} - encodeurl@2.0.0: {} engine.io-parser@5.2.3: {} @@ -7280,7 +7398,7 @@ snapshots: base64id: 2.0.0 cookie: 0.7.2 cors: 2.8.5 - debug: 4.4.3 + debug: 4.4.3(supports-color@10.2.2) engine.io-parser: 5.2.3 ws: 8.18.3 transitivePeerDependencies: @@ -7412,7 +7530,7 @@ snapshots: eslint-import-resolver-typescript@4.4.4(eslint-plugin-import@2.32.0)(eslint@8.57.1): dependencies: - debug: 4.4.3 + debug: 4.4.3(supports-color@10.2.2) eslint: 8.57.1 eslint-import-context: 0.1.9(unrs-resolver@1.11.1) get-tsconfig: 4.13.0 @@ -7421,22 +7539,22 @@ snapshots: tinyglobby: 0.2.15 unrs-resolver: 1.11.1 optionalDependencies: - eslint-plugin-import: 2.32.0(@typescript-eslint/parser@8.50.0(eslint@8.57.1)(typescript@5.9.3))(eslint-import-resolver-typescript@4.4.4)(eslint@8.57.1) + eslint-plugin-import: 2.32.0(@typescript-eslint/parser@8.52.0(eslint@8.57.1)(typescript@5.9.3))(eslint-import-resolver-typescript@4.4.4)(eslint@8.57.1) transitivePeerDependencies: - supports-color - eslint-module-utils@2.12.1(@typescript-eslint/parser@8.50.0(eslint@8.57.1)(typescript@5.9.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@4.4.4)(eslint@8.57.1): + eslint-module-utils@2.12.1(@typescript-eslint/parser@8.52.0(eslint@8.57.1)(typescript@5.9.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@4.4.4)(eslint@8.57.1): dependencies: debug: 3.2.7 optionalDependencies: - '@typescript-eslint/parser': 8.50.0(eslint@8.57.1)(typescript@5.9.3) + '@typescript-eslint/parser': 8.52.0(eslint@8.57.1)(typescript@5.9.3) eslint: 8.57.1 eslint-import-resolver-node: 0.3.9 eslint-import-resolver-typescript: 4.4.4(eslint-plugin-import@2.32.0)(eslint@8.57.1) transitivePeerDependencies: - supports-color - eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.50.0(eslint@8.57.1)(typescript@5.9.3))(eslint-import-resolver-typescript@4.4.4)(eslint@8.57.1): + eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.52.0(eslint@8.57.1)(typescript@5.9.3))(eslint-import-resolver-typescript@4.4.4)(eslint@8.57.1): dependencies: '@rtsao/scc': 1.1.0 array-includes: 3.1.9 @@ -7447,7 +7565,7 @@ snapshots: doctrine: 2.1.0 eslint: 8.57.1 eslint-import-resolver-node: 0.3.9 - eslint-module-utils: 2.12.1(@typescript-eslint/parser@8.50.0(eslint@8.57.1)(typescript@5.9.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@4.4.4)(eslint@8.57.1) + eslint-module-utils: 2.12.1(@typescript-eslint/parser@8.52.0(eslint@8.57.1)(typescript@5.9.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@4.4.4)(eslint@8.57.1) hasown: 2.0.2 is-core-module: 2.16.1 is-glob: 4.0.3 @@ -7459,7 +7577,7 @@ snapshots: string.prototype.trimend: 1.0.9 tsconfig-paths: 3.15.0 optionalDependencies: - '@typescript-eslint/parser': 8.50.0(eslint@8.57.1)(typescript@5.9.3) + '@typescript-eslint/parser': 8.52.0(eslint@8.57.1)(typescript@5.9.3) transitivePeerDependencies: - eslint-import-resolver-typescript - eslint-import-resolver-webpack @@ -7469,7 +7587,7 @@ snapshots: dependencies: eslint: 8.57.1 prettier: 3.7.4 - prettier-linter-helpers: 1.0.0 + prettier-linter-helpers: 1.0.1 synckit: 0.11.11 optionalDependencies: '@types/eslint': 9.6.1 @@ -7491,7 +7609,7 @@ snapshots: eslint@8.57.1: dependencies: - '@eslint-community/eslint-utils': 4.9.0(eslint@8.57.1) + '@eslint-community/eslint-utils': 4.9.1(eslint@8.57.1) '@eslint-community/regexpp': 4.12.2 '@eslint/eslintrc': 2.1.4 '@eslint/js': 8.57.1 @@ -7502,13 +7620,13 @@ snapshots: ajv: 6.12.6 chalk: 4.1.2 cross-spawn: 7.0.6 - debug: 4.4.3 + debug: 4.4.3(supports-color@10.2.2) doctrine: 3.0.0 escape-string-regexp: 4.0.0 eslint-scope: 7.2.2 eslint-visitor-keys: 3.4.3 espree: 9.6.1 - esquery: 1.6.0 + esquery: 1.7.0 esutils: 2.0.3 fast-deep-equal: 3.1.3 file-entry-cache: 6.0.1 @@ -7540,7 +7658,7 @@ snapshots: esprima@4.0.1: {} - esquery@1.6.0: + esquery@1.7.0: dependencies: estraverse: 5.3.0 @@ -7582,36 +7700,36 @@ snapshots: jest-message-util: 29.7.0 jest-util: 29.7.0 - express@4.21.2: + express@4.22.1: dependencies: accepts: 1.3.8 array-flatten: 1.1.1 body-parser: 1.20.3 content-disposition: 0.5.4 content-type: 1.0.5 - cookie: 0.7.1 - cookie-signature: 1.0.6 + cookie: 0.7.2 + cookie-signature: 1.0.7 debug: 2.6.9 depd: 2.0.0 encodeurl: 2.0.0 escape-html: 1.0.3 etag: 1.8.1 - finalhandler: 1.3.1 + finalhandler: 1.3.2 fresh: 0.5.2 - http-errors: 2.0.0 + http-errors: 2.0.1 merge-descriptors: 1.0.3 methods: 1.1.2 on-finished: 2.4.1 parseurl: 1.3.3 path-to-regexp: 0.1.12 proxy-addr: 2.0.7 - qs: 6.13.0 + qs: 6.14.1 range-parser: 1.2.1 safe-buffer: 5.2.1 - send: 0.19.0 - serve-static: 1.16.2 + send: 0.19.2 + serve-static: 1.16.3 setprototypeof: 1.2.0 - statuses: 2.0.1 + statuses: 2.0.2 type-is: 1.6.18 utils-merge: 1.0.1 vary: 1.1.2 @@ -7648,7 +7766,7 @@ snapshots: dependencies: strnum: 2.1.2 - fastq@1.19.1: + fastq@1.20.1: dependencies: reusify: 1.1.0 @@ -7660,6 +7778,11 @@ snapshots: optionalDependencies: picomatch: 4.0.3 + fetch-blob@3.2.0: + dependencies: + node-domexception: 1.0.0 + web-streams-polyfill: 3.3.3 + fflate@0.8.2: {} figures@3.2.0: @@ -7674,7 +7797,7 @@ snapshots: dependencies: '@tokenizer/inflate': 0.2.7 strtok3: 10.3.4 - token-types: 6.1.1 + token-types: 6.1.2 uint8array-extras: 1.5.0 transitivePeerDependencies: - supports-color @@ -7683,26 +7806,26 @@ snapshots: dependencies: to-regex-range: 5.0.1 - finalhandler@1.3.1: + finalhandler@1.3.2: dependencies: debug: 2.6.9 encodeurl: 2.0.0 escape-html: 1.0.3 on-finished: 2.4.1 parseurl: 1.3.3 - statuses: 2.0.1 + statuses: 2.0.2 unpipe: 1.0.0 transitivePeerDependencies: - supports-color finalhandler@2.1.1: dependencies: - debug: 4.4.3 + debug: 4.4.3(supports-color@10.2.2) encodeurl: 2.0.0 escape-html: 1.0.3 on-finished: 2.4.1 parseurl: 1.3.3 - statuses: 2.0.1 + statuses: 2.0.2 transitivePeerDependencies: - supports-color @@ -7722,6 +7845,8 @@ snapshots: keyv: 4.5.4 rimraf: 3.0.2 + flatbuffers@25.9.23: {} + flatted@3.3.3: {} follow-redirects@1.15.11: {} @@ -7760,12 +7885,16 @@ snapshots: hasown: 2.0.2 mime-types: 2.1.35 + formdata-polyfill@4.0.10: + dependencies: + fetch-blob: 3.2.0 + formidable@2.1.5: dependencies: '@paralleldrive/cuid2': 2.3.1 dezalgo: 1.0.4 once: 1.4.0 - qs: 6.14.0 + qs: 6.14.1 forwarded@0.2.0: {} @@ -7899,6 +8028,12 @@ snapshots: graphql@16.12.0: {} + h264-profile-level-id@2.3.2(supports-color@10.2.2): + dependencies: + debug: 4.4.3(supports-color@10.2.2) + transitivePeerDependencies: + - supports-color + handlebars@4.7.8: dependencies: minimist: 1.2.8 @@ -7956,7 +8091,7 @@ snapshots: dependencies: safer-buffer: 2.1.2 - iconv-lite@0.7.1: + iconv-lite@0.7.2: dependencies: safer-buffer: 2.1.2 @@ -7985,6 +8120,8 @@ snapshots: inherits@2.0.4: {} + ini@6.0.0: {} + inquirer@8.2.6: dependencies: ansi-escapes: 4.3.2 @@ -8195,7 +8332,7 @@ snapshots: istanbul-lib-source-maps@4.0.1: dependencies: - debug: 4.4.3 + debug: 4.4.3(supports-color@10.2.2) istanbul-lib-coverage: 3.2.2 source-map: 0.6.1 transitivePeerDependencies: @@ -8231,7 +8368,7 @@ snapshots: '@types/node': 20.19.27 chalk: 4.1.2 co: 4.6.0 - dedent: 1.7.0 + dedent: 1.7.1 is-generator-fn: 2.1.0 jest-each: 29.7.0 jest-matcher-utils: 29.7.0 @@ -8585,7 +8722,7 @@ snapshots: prelude-ls: 1.2.1 type-check: 0.4.0 - libphonenumber-js@1.12.31: {} + libphonenumber-js@1.12.33: {} lines-and-columns@1.2.4: {} @@ -8650,6 +8787,17 @@ snapshots: media-typer@1.1.0: {} + mediasoup@3.19.14: + dependencies: + '@types/ini': 4.1.1 + debug: 4.4.3(supports-color@10.2.2) + flatbuffers: 25.9.23 + h264-profile-level-id: 2.3.2(supports-color@10.2.2) + ini: 6.0.0 + node-fetch: 3.3.2 + supports-color: 10.2.2 + tar: 7.5.2 + memfs@3.5.3: dependencies: fs-monkey: 1.1.0 @@ -8697,6 +8845,10 @@ snapshots: minipass@7.1.2: {} + minizlib@3.1.0: + dependencies: + minipass: 7.1.2 + mkdirp@0.5.6: dependencies: minimist: 1.2.8 @@ -8724,7 +8876,7 @@ snapshots: aws-ssl-profiles: 1.1.2 denque: 2.1.0 generate-function: 2.3.1 - iconv-lite: 0.7.1 + iconv-lite: 0.7.2 long: 5.3.2 lru.min: 1.1.3 named-placeholders: 1.1.6 @@ -8749,6 +8901,8 @@ snapshots: node-addon-api@8.5.0: {} + node-domexception@1.0.0: {} + node-emoji@1.11.0: dependencies: lodash: 4.17.21 @@ -8757,6 +8911,12 @@ snapshots: dependencies: whatwg-url: 5.0.0 + node-fetch@3.3.2: + dependencies: + data-uri-to-buffer: 4.0.1 + fetch-blob: 3.2.0 + formdata-polyfill: 4.0.10 + node-gyp-build@4.8.4: {} node-int64@0.4.0: {} @@ -8921,7 +9081,7 @@ snapshots: prelude-ls@1.2.1: {} - prettier-linter-helpers@1.0.0: + prettier-linter-helpers@1.0.1: dependencies: fast-diff: 1.3.0 @@ -8953,7 +9113,7 @@ snapshots: dependencies: side-channel: 1.1.0 - qs@6.14.0: + qs@6.14.1: dependencies: side-channel: 1.1.0 @@ -8976,7 +9136,7 @@ snapshots: dependencies: bytes: 3.1.2 http-errors: 2.0.1 - iconv-lite: 0.7.1 + iconv-lite: 0.7.2 unpipe: 1.0.0 react-is@18.3.1: {} @@ -9116,21 +9276,21 @@ snapshots: semver@7.7.3: {} - send@0.19.0: + send@0.19.2: dependencies: debug: 2.6.9 depd: 2.0.0 destroy: 1.2.0 - encodeurl: 1.0.2 + encodeurl: 2.0.0 escape-html: 1.0.3 etag: 1.8.1 fresh: 0.5.2 - http-errors: 2.0.0 + http-errors: 2.0.1 mime: 1.6.0 ms: 2.1.3 on-finished: 2.4.1 range-parser: 1.2.1 - statuses: 2.0.1 + statuses: 2.0.2 transitivePeerDependencies: - supports-color @@ -9140,12 +9300,12 @@ snapshots: dependencies: randombytes: 2.1.0 - serve-static@1.16.2: + serve-static@1.16.3: dependencies: encodeurl: 2.0.0 escape-html: 1.0.3 parseurl: 1.3.3 - send: 0.19.0 + send: 0.19.2 transitivePeerDependencies: - supports-color @@ -9223,7 +9383,7 @@ snapshots: socket.io-adapter@2.5.6: dependencies: - debug: 4.4.3 + debug: 4.4.3(supports-color@10.2.2) ws: 8.18.3 transitivePeerDependencies: - bufferutil @@ -9233,16 +9393,30 @@ snapshots: socket.io-parser@4.2.5: dependencies: '@socket.io/component-emitter': 3.1.2 - debug: 4.4.3 + debug: 4.4.3(supports-color@10.2.2) + transitivePeerDependencies: + - supports-color + + socket.io@4.8.1: + dependencies: + accepts: 1.3.8 + base64id: 2.0.0 + cors: 2.8.5 + debug: 4.3.7 + engine.io: 6.6.5 + socket.io-adapter: 2.5.6 + socket.io-parser: 4.2.5 transitivePeerDependencies: + - bufferutil - supports-color + - utf-8-validate socket.io@4.8.3: dependencies: accepts: 1.3.8 base64id: 2.0.0 cors: 2.8.5 - debug: 4.4.3 + debug: 4.4.3(supports-color@10.2.2) engine.io: 6.6.5 socket.io-adapter: 2.5.6 socket.io-parser: 4.2.5 @@ -9370,13 +9544,13 @@ snapshots: dependencies: component-emitter: 1.3.1 cookiejar: 2.1.4 - debug: 4.4.3 + debug: 4.4.3(supports-color@10.2.2) fast-safe-stringify: 2.1.1 form-data: 4.0.5 formidable: 2.1.5 methods: 1.1.2 mime: 2.6.0 - qs: 6.14.0 + qs: 6.14.1 semver: 7.7.3 transitivePeerDependencies: - supports-color @@ -9388,6 +9562,8 @@ snapshots: transitivePeerDependencies: - supports-color + supports-color@10.2.2: {} + supports-color@7.2.0: dependencies: has-flag: 4.0.0 @@ -9408,6 +9584,14 @@ snapshots: tapable@2.3.0: {} + tar@7.5.2: + dependencies: + '@isaacs/fs-minipass': 4.0.1 + chownr: 3.0.0 + minipass: 7.1.2 + minizlib: 3.1.0 + yallist: 5.0.0 + terser-webpack-plugin@5.3.16(webpack@5.97.1): dependencies: '@jridgewell/trace-mapping': 0.3.31 @@ -9457,9 +9641,9 @@ snapshots: toidentifier@1.0.1: {} - token-types@6.1.1: + token-types@6.1.2: dependencies: - '@borewit/text-codec': 0.1.1 + '@borewit/text-codec': 0.2.1 '@tokenizer/token': 0.3.0 ieee754: 1.2.1 @@ -9467,7 +9651,7 @@ snapshots: tree-kill@1.2.2: {} - ts-api-utils@2.1.0(typescript@5.9.3): + ts-api-utils@2.4.0(typescript@5.9.3): dependencies: typescript: 5.9.3 @@ -9677,7 +9861,7 @@ snapshots: '@types/istanbul-lib-coverage': 2.0.6 convert-source-map: 2.0.0 - validator@13.15.23: {} + validator@13.15.26: {} vary@1.1.2: {} @@ -9685,7 +9869,7 @@ snapshots: dependencies: makeerror: 1.0.12 - watchpack@2.4.4: + watchpack@2.5.0: dependencies: glob-to-regexp: 0.4.1 graceful-fs: 4.2.11 @@ -9694,6 +9878,8 @@ snapshots: dependencies: defaults: 1.0.4 + web-streams-polyfill@3.3.3: {} + webidl-conversions@3.0.1: {} webpack-node-externals@3.0.0: {} @@ -9723,7 +9909,7 @@ snapshots: schema-utils: 3.3.0 tapable: 2.3.0 terser-webpack-plugin: 5.3.16(webpack@5.97.1) - watchpack: 2.4.4 + watchpack: 2.5.0 webpack-sources: 3.3.3 transitivePeerDependencies: - '@swc/core' @@ -9826,6 +10012,8 @@ snapshots: yallist@3.1.1: {} + yallist@5.0.0: {} + yargs-parser@21.1.1: {} yargs@17.7.2: diff --git a/rep/main_backend/src/0.common/error/application/sfu/sfu.error.ts b/rep/main_backend/src/0.common/error/application/sfu/sfu.error.ts new file mode 100644 index 000000000..bfe316932 --- /dev/null +++ b/rep/main_backend/src/0.common/error/application/sfu/sfu.error.ts @@ -0,0 +1,19 @@ +import { BaseError } from '../../error'; + +export class SfuError extends BaseError { + constructor(err: Error) { + super({ + message: `${err}`, + status: 500, + }); + } +} + +export class SfuErrorMessage extends BaseError { + constructor(message : string) { + super({ + message, + status: 500, + }); + } +} \ No newline at end of file diff --git a/rep/main_backend/src/0.common/error/presentation/sfu/sfu.error.ts b/rep/main_backend/src/0.common/error/presentation/sfu/sfu.error.ts new file mode 100644 index 000000000..bfe316932 --- /dev/null +++ b/rep/main_backend/src/0.common/error/presentation/sfu/sfu.error.ts @@ -0,0 +1,19 @@ +import { BaseError } from '../../error'; + +export class SfuError extends BaseError { + constructor(err: Error) { + super({ + message: `${err}`, + status: 500, + }); + } +} + +export class SfuErrorMessage extends BaseError { + constructor(message : string) { + super({ + message, + status: 500, + }); + } +} \ No newline at end of file diff --git a/rep/main_backend/src/0.common/error/presentation/signalling/signalling.error.ts b/rep/main_backend/src/0.common/error/presentation/signalling/signalling.error.ts new file mode 100644 index 000000000..c3636e9c0 --- /dev/null +++ b/rep/main_backend/src/0.common/error/presentation/signalling/signalling.error.ts @@ -0,0 +1,10 @@ +import { BaseError } from '../../error'; + +export class NotConnectSignalling extends BaseError { + constructor() { + super({ + message: '화상 회의방에 연결하지 못했습니다.', + status: 500, + }); + } +} \ No newline at end of file diff --git a/rep/main_backend/src/2.application/room/commands/dto/index.ts b/rep/main_backend/src/2.application/room/commands/dto/index.ts index bce9a7089..2dad4fe15 100644 --- a/rep/main_backend/src/2.application/room/commands/dto/index.ts +++ b/rep/main_backend/src/2.application/room/commands/dto/index.ts @@ -1,3 +1,3 @@ export * from "./create-room.dto"; export * from "./connect-room.dto"; -export * from "./disconnect-room.dto" \ No newline at end of file +export * from "./disconnect-room.dto" diff --git a/rep/main_backend/src/2.application/sfu/commands/dto/create-router.dto.ts b/rep/main_backend/src/2.application/sfu/commands/dto/create-router.dto.ts new file mode 100644 index 000000000..31810b1f3 --- /dev/null +++ b/rep/main_backend/src/2.application/sfu/commands/dto/create-router.dto.ts @@ -0,0 +1,12 @@ +import { Router } from "mediasoup/types"; + + +// room_id에 대해서 worker, router, room_id, created_at으로 메모리에서 worker에서 router를 어떻게 관리하는지 작성한다. +export type RoomEntry = { + room_id : string; + worker_idx : number; // 이 라우터를 운영하기 위해서 필요한 정보 + worker_pid : number; // worker를 디버깅 하기 위해서 필요한 정보 + router : Router; // 실질적으로 기능구현에 사용되는 라우터 -> 여기에 있으면 안되고 domain에서 가져오도록 해야한다. ( 나중에 수정 모먼트 ) + transport_ids : Set; // transport들이 저장된 라우터 + created_at : Date; +}; \ No newline at end of file diff --git a/rep/main_backend/src/2.application/sfu/commands/dto/create-transport.dto.ts b/rep/main_backend/src/2.application/sfu/commands/dto/create-transport.dto.ts new file mode 100644 index 000000000..b3c715fb1 --- /dev/null +++ b/rep/main_backend/src/2.application/sfu/commands/dto/create-transport.dto.ts @@ -0,0 +1,24 @@ +import { DtlsParameters, IceCandidate, IceParameters } from "mediasoup/types"; // 나중에 domain으로 빼야 함 + + +export type TransportEntry = { + transportId: string; + iceParameters: IceParameters; + iceCandidates: Array; + dtlsParameters: DtlsParameters; +}; + +export type CreateTransportDto = { + room_id : string; + socket_id : string; + user_id : string; + type : "send" | "recv" +}; + +export type CreateRoomTransportDto = { + room_id : string; + socket_id : string; + transport_id : string; + user_id : string; + type : "send" | "recv" +}; \ No newline at end of file diff --git a/rep/main_backend/src/2.application/sfu/commands/dto/index.ts b/rep/main_backend/src/2.application/sfu/commands/dto/index.ts new file mode 100644 index 000000000..db4feeb52 --- /dev/null +++ b/rep/main_backend/src/2.application/sfu/commands/dto/index.ts @@ -0,0 +1,2 @@ +export * from "./create-transport.dto"; +export * from "./create-router.dto"; \ No newline at end of file diff --git a/rep/main_backend/src/2.application/sfu/commands/usecase/create-router.usecase.ts b/rep/main_backend/src/2.application/sfu/commands/usecase/create-router.usecase.ts new file mode 100644 index 000000000..12f16a247 --- /dev/null +++ b/rep/main_backend/src/2.application/sfu/commands/usecase/create-router.usecase.ts @@ -0,0 +1,78 @@ +import { Injectable, Logger } from "@nestjs/common"; +import type { RoomCreateLockPort, RoomRouterRepositoryPort, RouterFactoryPort } from "../../ports"; +import { RoomEntry } from "../dto"; +import { SfuError } from "@error/application/sfu/sfu.error"; + + +@Injectable() +export class CreateRouterUsecase { + private readonly logger = new Logger(CreateRouterUsecase.name); + + // 기존의 in-memory를 그대로 사용해야 하기때문에 의존성을 그대로 부여 + constructor( + private readonly roomRepo : RoomRouterRepositoryPort, + private readonly roomCreateLockRepo : RoomCreateLockPort, + private readonly routerFactory : RouterFactoryPort + ) {} + + async execute(room_id : string) : Promise { + + // 이미 생성된 router라면 + const roomRouterExist = this.roomRepo.get(room_id); + if ( roomRouterExist && !roomRouterExist.router.closed ) { + return roomRouterExist; + }; + + // 생성 중이라면 + const createRoomRoutingExist = this.roomCreateLockRepo.get(room_id); + if ( createRoomRoutingExist ) { + return createRoomRoutingExist; + }; + + // 아무것도 없다면 새롭게 생성할 수 있다. + const roomEntry = this.createRoomRouting(room_id); + this.roomCreateLockRepo.set(room_id, roomEntry); + + return roomEntry; + + } + + private async createRoomRouting(room_id : string) : Promise { + try { + // 여기서 router까지 생성 + const { router, workerIdx, workerPid } = await this.routerFactory.createRouter(); + + // 기본 설정 + const roomEntry : RoomEntry = { + worker_idx : workerIdx, + room_id, + router, + worker_pid : workerPid, + transport_ids : new Set(), + created_at : new Date() + }; + + // 메모리에 저장 + this.roomRepo.set(room_id, roomEntry); + + // router가 만약 내려간다면? ( 근데 worker가 내려가면 애초에 그 방은 내려가니까 worker에 죽음은 그 프로세스 전체에 삭제 ) + router.observer.on("close", () => { + const closedRouterEntry = this.roomRepo.get(room_id); + if ( closedRouterEntry?.router === router ) { + // 방이랑 worker_id 기록한거 삭제 + this.roomRepo.delete(room_id); + }; + }); + + // 여기에서 대표 producer transport도 나중에 등록 예정 + + return roomEntry; + } catch (err) { + this.logger.error(err); // error가 발생한다면 + throw new SfuError(err); + } finally { + this.roomCreateLockRepo.delete(room_id); + }; + }; + +}; \ No newline at end of file diff --git a/rep/main_backend/src/2.application/sfu/commands/usecase/create-transport.usecase.ts b/rep/main_backend/src/2.application/sfu/commands/usecase/create-transport.usecase.ts new file mode 100644 index 000000000..d624e5b89 --- /dev/null +++ b/rep/main_backend/src/2.application/sfu/commands/usecase/create-transport.usecase.ts @@ -0,0 +1,78 @@ +import { Injectable, Logger } from "@nestjs/common"; +import type { Router } from "mediasoup/types"; +import { SfuErrorMessage } from "@error/application/sfu/sfu.error"; +import type { + RoomRouterRepositoryPort, + TransportRepositoryPort, + TransportFactoryPort, +} from "../../ports"; +import { CreateTransportDto, CreateRoomTransportDto, RoomEntry, TransportEntry } from "../dto"; +import { DeleteDataToCache, InsertDataToCache } from "@app/ports/cache/cache.outbound"; + + +@Injectable() +export class CreateTransportUsecase { + private readonly logger = new Logger(CreateTransportUsecase.name); + + constructor( + private readonly roomRepo: RoomRouterRepositoryPort, + private readonly transportRepo: TransportRepositoryPort, + private readonly transportFactory: TransportFactoryPort, + private readonly insertTranportInfoToRedis : InsertDataToCache, + private readonly deleteTransportInfoToRedis : DeleteDataToCache, + ) {} + + async execute(dto: CreateTransportDto): Promise { + + // 1. roomEntry 찾기 + const roomEntry: RoomEntry | undefined = this.roomRepo.get(dto.room_id); + if (!roomEntry || roomEntry.router.closed) { + throw new SfuErrorMessage("room_id를 다시 확인해주세요"); + } + const router: Router = roomEntry.router; + + // transport 설정 + let transport: any; // WebRtcTransport + try { + + // 2. transport 생성 + transport = await this.transportFactory.createWebRtcTransport(router); + + // transport 로깅 및 transport_id 설정 + this.transportFactory.attachDebugHooks(dto.room_id, transport); + const transportId = transport.id; + + // 3. 메모리 저장 + this.transportRepo.set(transportId, transport); + + // 4. router 갱신 + this.roomRepo.patch(dto.room_id, (e) => e.transport_ids.add(transportId)); + + // 5. cache에 transport info 정보 저장 + const validate: CreateRoomTransportDto = { ...dto, transport_id: transportId }; + await this.insertTranportInfoToRedis.insert(validate); + + // 6. transport 없어졌을때 이벤트 생성 + transport.observer.on("close", () => { + this.transportRepo.delete(transportId); + + // router 갱신 + this.roomRepo.patch(dto.room_id, (e) => e.transport_ids.delete(transportId)); + + // cache에 삭제 - transport_id를 전달하고 namespace를 잘 정리해야 한다. + this.deleteTransportInfoToRedis.deleteNamespace(transportId); + }); + + return { + transportId, + iceParameters: transport.iceParameters, + iceCandidates: transport.iceCandidates, + dtlsParameters: transport.dtlsParameters, + }; + } catch (err) { + this.logger.error(err); + if (transport && !transport.closed) transport.close(); + throw err; + } + } +} diff --git a/rep/main_backend/src/2.application/sfu/commands/usecase/index.ts b/rep/main_backend/src/2.application/sfu/commands/usecase/index.ts new file mode 100644 index 000000000..26c631926 --- /dev/null +++ b/rep/main_backend/src/2.application/sfu/commands/usecase/index.ts @@ -0,0 +1,2 @@ +export * from "./create-router.usecase"; +export * from "./create-transport.usecase"; \ No newline at end of file diff --git a/rep/main_backend/src/2.application/sfu/ports/index.ts b/rep/main_backend/src/2.application/sfu/ports/index.ts new file mode 100644 index 000000000..64eea5c73 --- /dev/null +++ b/rep/main_backend/src/2.application/sfu/ports/index.ts @@ -0,0 +1,5 @@ +export * from "./room-router-repo.port"; +export * from "./room-create-lock-repo.port"; +export * from "./router-factory.port"; +export * from "./transport-repo.port"; +export * from "./transport-factory.port"; \ No newline at end of file diff --git a/rep/main_backend/src/2.application/sfu/ports/room-create-lock-repo.port.ts b/rep/main_backend/src/2.application/sfu/ports/room-create-lock-repo.port.ts new file mode 100644 index 000000000..92a97e8bc --- /dev/null +++ b/rep/main_backend/src/2.application/sfu/ports/room-create-lock-repo.port.ts @@ -0,0 +1,8 @@ +import { RoomEntry } from "../commands/dto"; + + +export interface RoomCreateLockPort { + get(roomId: string): Promise | undefined; + set(roomId: string, inflight: Promise): void; + delete(roomId: string): void; +} diff --git a/rep/main_backend/src/2.application/sfu/ports/room-router-repo.port.ts b/rep/main_backend/src/2.application/sfu/ports/room-router-repo.port.ts new file mode 100644 index 000000000..34df819d6 --- /dev/null +++ b/rep/main_backend/src/2.application/sfu/ports/room-router-repo.port.ts @@ -0,0 +1,9 @@ +import { RoomEntry } from "../commands/dto"; + + +export interface RoomRouterRepositoryPort { + get(roomId: string): RoomEntry | undefined; + set(roomId: string, entry: RoomEntry): void; + delete(roomId: string): void; + patch(roomId: string, patch: (entry: RoomEntry) => void): void; +} diff --git a/rep/main_backend/src/2.application/sfu/ports/router-factory.port.ts b/rep/main_backend/src/2.application/sfu/ports/router-factory.port.ts new file mode 100644 index 000000000..c023ae790 --- /dev/null +++ b/rep/main_backend/src/2.application/sfu/ports/router-factory.port.ts @@ -0,0 +1,11 @@ +import type { Router } from "mediasoup/types"; + +export type CreateRouterResult = { + router: Router; + workerIdx: number; + workerPid: number; +}; + +export interface RouterFactoryPort { + createRouter(): Promise; +} \ No newline at end of file diff --git a/rep/main_backend/src/2.application/sfu/ports/transport-factory.port.ts b/rep/main_backend/src/2.application/sfu/ports/transport-factory.port.ts new file mode 100644 index 000000000..baab9c8ea --- /dev/null +++ b/rep/main_backend/src/2.application/sfu/ports/transport-factory.port.ts @@ -0,0 +1,7 @@ +import type { Router, WebRtcTransport } from "mediasoup/types"; + + +export interface TransportFactoryPort { + createWebRtcTransport(router: Router): Promise; + attachDebugHooks(roomId: string, transport: WebRtcTransport): void; +} \ No newline at end of file diff --git a/rep/main_backend/src/2.application/sfu/ports/transport-repo.port.ts b/rep/main_backend/src/2.application/sfu/ports/transport-repo.port.ts new file mode 100644 index 000000000..42398e562 --- /dev/null +++ b/rep/main_backend/src/2.application/sfu/ports/transport-repo.port.ts @@ -0,0 +1,8 @@ +import type { WebRtcTransport } from "mediasoup/types"; // 나중에 domain으로 빼야하 + + +export interface TransportRepositoryPort { + get(id: string): WebRtcTransport | undefined; + set(id: string, transport: WebRtcTransport): void; + delete(id: string): void; +} diff --git a/rep/main_backend/src/2.application/sfu/queries/dto/connect-transport.dto.ts b/rep/main_backend/src/2.application/sfu/queries/dto/connect-transport.dto.ts new file mode 100644 index 000000000..b0204a4f0 --- /dev/null +++ b/rep/main_backend/src/2.application/sfu/queries/dto/connect-transport.dto.ts @@ -0,0 +1,18 @@ +import { DtlsParameters } from "mediasoup/types"; // 마찬가지 domain으로 빼야함 + + +export type RoomTransportInfo = { + room_id : string; + socket_id : string; + user_id : string; + type : string; +}; + +export type ConnectTransportType = { + room_id : string; + socket_id : string; + user_id : string; + type : "send" | "recv"; + transport_id : string; + dtlsParameters: DtlsParameters +}; \ No newline at end of file diff --git a/rep/main_backend/src/2.application/sfu/queries/dto/index.ts b/rep/main_backend/src/2.application/sfu/queries/dto/index.ts new file mode 100644 index 000000000..d9913bce0 --- /dev/null +++ b/rep/main_backend/src/2.application/sfu/queries/dto/index.ts @@ -0,0 +1 @@ +export * from "./connect-transport.dto" \ No newline at end of file diff --git a/rep/main_backend/src/2.application/sfu/queries/usecase/connect-transport.usecase.ts b/rep/main_backend/src/2.application/sfu/queries/usecase/connect-transport.usecase.ts new file mode 100644 index 000000000..062b48452 --- /dev/null +++ b/rep/main_backend/src/2.application/sfu/queries/usecase/connect-transport.usecase.ts @@ -0,0 +1,30 @@ +import { Injectable } from "@nestjs/common"; +import type { TransportRepositoryPort } from "../../ports"; +import { SelectDataFromCache } from "@app/ports/cache/cache.inbound"; +import { ConnectTransportType, RoomTransportInfo } from "../dto"; +import { SfuErrorMessage } from "@error/application/sfu/sfu.error"; + + +@Injectable() +export class ConnectTransportUsecase { + + constructor( + private readonly transportRepo: TransportRepositoryPort, + private readonly selectSfuTransportInfoFromRedis : SelectDataFromCache + ) {} + + async execute( dto : ConnectTransportType ) { + + // 1. transport에 정합성에 대해서 검증 + const transportInfo : RoomTransportInfo | undefined = await this.selectSfuTransportInfoFromRedis.select({ namespace : dto.transport_id, keyName : "" }); + if ( !transportInfo ) throw new SfuErrorMessage("transport_id를 다시 확인해주세요. - 데이터 찾는데 문제 발생"); + + if ( dto.room_id !== transportInfo.room_id || dto.socket_id !== transportInfo.socket_id || dto.type !== transportInfo.type || dto.user_id !== transportInfo.user_id ) throw new SfuErrorMessage("잘못된 transport_id에 연결하고자 합니다 다시 확인해주시길 발반디ㅏ."); + + // 2. transport 가져온 후 연결 + const transport = this.transportRepo.get(dto.transport_id); + if ( !transport ) throw new SfuErrorMessage("transport_id를 다시 확인해주세요."); + + await transport.connect({ dtlsParameters : dto.dtlsParameters }); + }; +}; \ No newline at end of file diff --git a/rep/main_backend/src/2.application/sfu/queries/usecase/index.ts b/rep/main_backend/src/2.application/sfu/queries/usecase/index.ts new file mode 100644 index 000000000..484ceebc5 --- /dev/null +++ b/rep/main_backend/src/2.application/sfu/queries/usecase/index.ts @@ -0,0 +1 @@ +export * from "./connect-transport.usecase"; \ No newline at end of file diff --git a/rep/main_backend/src/3-1.infra/cache/cache.constants.ts b/rep/main_backend/src/3-1.infra/cache/cache.constants.ts index 042530770..2f2122f45 100644 --- a/rep/main_backend/src/3-1.infra/cache/cache.constants.ts +++ b/rep/main_backend/src/3-1.infra/cache/cache.constants.ts @@ -46,4 +46,16 @@ export const CACHE_ROOM_SOCKETS_KEY_PROPS_NAME = Object.freeze({ USER_ID : "user_id", ROOM_ID : "room_id", IP : "ip" +} as const); + +// sfu와 관련된 cache 정보 +export const CACHE_SFU_NAMESPACE_NAME = Object.freeze({ + TRANSPORT_INFO : "cache:sfu:transports" +} as const); + +export const CACHE_SFU_TRANSPORTS_KEY_NAME = Object.freeze({ + SOCKET_ID : "socket_id", + USER_ID : "user_id", + TYPE : "type", + ROOM_ID : "room_id" } as const); \ No newline at end of file diff --git a/rep/main_backend/src/3-1.infra/cache/redis/cache.ts b/rep/main_backend/src/3-1.infra/cache/redis/cache.ts index eb9350b1a..8471866a5 100644 --- a/rep/main_backend/src/3-1.infra/cache/redis/cache.ts +++ b/rep/main_backend/src/3-1.infra/cache/redis/cache.ts @@ -9,6 +9,8 @@ import { REDIS_SERVER } from '../cache.constants'; import { SelectHsetDataFromRedis } from './user/user.inbound'; import { DeleteRoomDatasToRedis, InsertRoomDatasToRedis, InsertRoomDataToRedis } from './room/room.outbound'; import { SelectRoomInfoFromRedis } from './room/room.inbound'; +import { CreateSfuTransportInfoToRedis, DeleteSfuTransportInfoToRedis, } from "./sfu/sfu.outbound" +import { SelectSfuTransportDataFromRedis } from './sfu/sfu.inbound'; @Global() @@ -52,6 +54,9 @@ import { SelectRoomInfoFromRedis } from './room/room.inbound'; SelectRoomInfoFromRedis, // roominfo 정보를 찾을때 사용 InsertRoomDatasToRedis, // room data들을 저장할때 사용 DeleteRoomDatasToRedis, // room정보들을 삭제하기 위해 사용 + CreateSfuTransportInfoToRedis, // transport들의 정보를 저장하기 위한 로직 + DeleteSfuTransportInfoToRedis, // transport가 만약 에러가 발생하거나 삭제될때 발동하는 로직 + SelectSfuTransportDataFromRedis // transport의 정보를 체크하기 위해서 필요한 로직 ], exports: [ REDIS_SERVER, @@ -61,7 +66,10 @@ import { SelectRoomInfoFromRedis } from './room/room.inbound'; InsertRoomDataToRedis, SelectRoomInfoFromRedis, InsertRoomDatasToRedis, - DeleteRoomDatasToRedis + DeleteRoomDatasToRedis, + CreateSfuTransportInfoToRedis, + DeleteSfuTransportInfoToRedis, + SelectSfuTransportDataFromRedis ], }) export class RedisModule {} diff --git a/rep/main_backend/src/3-1.infra/cache/redis/room/room.outbound.ts b/rep/main_backend/src/3-1.infra/cache/redis/room/room.outbound.ts index 6958df6a9..c24b9437c 100644 --- a/rep/main_backend/src/3-1.infra/cache/redis/room/room.outbound.ts +++ b/rep/main_backend/src/3-1.infra/cache/redis/room/room.outbound.ts @@ -1,4 +1,4 @@ -import { DeleteDatasToCache, InsertDataToCache } from "@app/ports/cache/cache.outbound"; +import { DeleteDatasToCache, DeleteDataToCache, InsertDataToCache } from "@app/ports/cache/cache.outbound"; import { Inject, Injectable } from "@nestjs/common"; import { type RedisClientType } from "redis"; import { CACHE_ROOM_INFO_KEY_NAME, CACHE_ROOM_MEMBERS_KEY_PROPS_NAME, CACHE_ROOM_NAMESPACE_NAME, CACHE_ROOM_SOCKETS_KEY_PROPS_NAME, CACHE_ROOM_SUB_NAMESPACE_NAME, REDIS_SERVER } from "../../cache.constants"; diff --git a/rep/main_backend/src/3-1.infra/cache/redis/sfu/sfu.inbound.ts b/rep/main_backend/src/3-1.infra/cache/redis/sfu/sfu.inbound.ts new file mode 100644 index 000000000..8d84204a6 --- /dev/null +++ b/rep/main_backend/src/3-1.infra/cache/redis/sfu/sfu.inbound.ts @@ -0,0 +1,36 @@ +import { SelectDataFromCache } from "@app/ports/cache/cache.inbound"; +import { Inject, Injectable } from "@nestjs/common"; +import { type RedisClientType } from "redis"; +import { CACHE_SFU_NAMESPACE_NAME, CACHE_SFU_TRANSPORTS_KEY_NAME, REDIS_SERVER } from "../../cache.constants"; +import { RoomTransportInfo } from "@app/sfu/queries/dto"; + + +@Injectable() +export class SelectSfuTransportDataFromRedis extends SelectDataFromCache> { + + constructor( + @Inject(REDIS_SERVER) cache : RedisClientType, + ) { super(cache); }; + + // namespace 부분만 있고 keyname은 사용 안될 예정이다. + async select({ namespace, keyName, }: { namespace: string; keyName: string; }): Promise { + + const transportNamespace : string = `${CACHE_SFU_NAMESPACE_NAME.TRANSPORT_INFO}:${namespace}`; + + const data = await this.cache.hGetAll(transportNamespace); + + // 값이 하나라도 없다면 문제가 있는 것이다. + if ( !data || !data[CACHE_SFU_TRANSPORTS_KEY_NAME.SOCKET_ID] || !data[CACHE_SFU_TRANSPORTS_KEY_NAME.USER_ID] || !data[CACHE_SFU_TRANSPORTS_KEY_NAME.ROOM_ID] || !data[CACHE_SFU_TRANSPORTS_KEY_NAME.TYPE] ) return undefined; + + + // 이러한 형태가 아니라면 에러가 발생할것이니 여기서 잡는다. + if (data[CACHE_SFU_TRANSPORTS_KEY_NAME.TYPE] !== "send" && data[CACHE_SFU_TRANSPORTS_KEY_NAME.TYPE] !== "recv") return undefined; + + return { + socket_id : data[CACHE_SFU_TRANSPORTS_KEY_NAME.SOCKET_ID], + user_id : data[CACHE_SFU_TRANSPORTS_KEY_NAME.USER_ID], + room_id : data[CACHE_SFU_TRANSPORTS_KEY_NAME.ROOM_ID], + type : data[CACHE_SFU_TRANSPORTS_KEY_NAME.TYPE] + } + } +}; \ No newline at end of file diff --git a/rep/main_backend/src/3-1.infra/cache/redis/sfu/sfu.outbound.ts b/rep/main_backend/src/3-1.infra/cache/redis/sfu/sfu.outbound.ts new file mode 100644 index 000000000..ebb6a79fd --- /dev/null +++ b/rep/main_backend/src/3-1.infra/cache/redis/sfu/sfu.outbound.ts @@ -0,0 +1,48 @@ +import { DeleteDataToCache, InsertDataToCache } from "@app/ports/cache/cache.outbound"; +import { Inject, Injectable } from "@nestjs/common"; +import { type RedisClientType } from "redis"; +import { CACHE_SFU_NAMESPACE_NAME, CACHE_SFU_TRANSPORTS_KEY_NAME, REDIS_SERVER } from "../../cache.constants"; +import { CreateRoomTransportDto } from "@app/sfu/commands/dto"; + + + +@Injectable() +export class CreateSfuTransportInfoToRedis extends InsertDataToCache> { + + constructor( + @Inject(REDIS_SERVER) cache : RedisClientType, + ) { super(cache); }; + + async insert(entity: CreateRoomTransportDto): Promise { + + const namespace : string = `${CACHE_SFU_NAMESPACE_NAME.TRANSPORT_INFO}:${entity.transport_id}`; + + await this.cache.hSet(namespace, { + [CACHE_SFU_TRANSPORTS_KEY_NAME.ROOM_ID] : entity.room_id, + [CACHE_SFU_TRANSPORTS_KEY_NAME.SOCKET_ID] : entity.socket_id, + [CACHE_SFU_TRANSPORTS_KEY_NAME.TYPE] : entity.type, + [CACHE_SFU_TRANSPORTS_KEY_NAME.USER_ID] : entity.user_id + }); + + return true; + }; +}; + +@Injectable() +export class DeleteSfuTransportInfoToRedis extends DeleteDataToCache> { + + constructor( + @Inject(REDIS_SERVER) cache : RedisClientType, + ) { super(cache); }; + + // namespace는 해당 transport_id에 대해서 삭제이다. -> 잘 봐주어야 함 + async deleteNamespace(namespace: string): Promise { + // 문자 검증? 이것도 필요해 보인다. + const transportNamespace : string = `${CACHE_SFU_NAMESPACE_NAME.TRANSPORT_INFO}:${namespace}`; + + await this.cache.del(transportNamespace); + + return true; + } + +}; \ No newline at end of file diff --git a/rep/main_backend/src/3-1.infra/media/media.module.ts b/rep/main_backend/src/3-1.infra/media/media.module.ts new file mode 100644 index 000000000..a7496999c --- /dev/null +++ b/rep/main_backend/src/3-1.infra/media/media.module.ts @@ -0,0 +1,17 @@ +import { Global, Module } from "@nestjs/common"; +import { MediasoupService } from "./mediasoup/media"; +import { MediasoupTransportFactory } from "./mediasoup/sfu/sfu.outbound"; + + +@Global() +@Module({ + providers : [ + MediasoupService, + MediasoupTransportFactory + ], + exports : [ + MediasoupService, + MediasoupTransportFactory + ] +}) +export class MediaModule {}; \ No newline at end of file diff --git a/rep/main_backend/src/3-1.infra/media/mediasoup/config.ts b/rep/main_backend/src/3-1.infra/media/mediasoup/config.ts new file mode 100644 index 000000000..7c79a945a --- /dev/null +++ b/rep/main_backend/src/3-1.infra/media/mediasoup/config.ts @@ -0,0 +1,79 @@ +import { RouterOptions, RtpCodecCapability, type TransportListenIp, type WorkerSettings } from "mediasoup/types"; + + +// worker에 쓰이는 config +export const mediaSoupWorkerConfig : WorkerSettings = { + // log에 남길 레벨을 정한다. ( warn ) + logLevel : process.env.NODE_ENV === "production" ? "warn" : "debug", + + logTags : process.env.NODE_ENV === "production" ? + [ + "info", "ice", "dtls", "rtp" + ] + : + [ + "info", + "ice", // ice 후보 찾기 등 할때 로깅을 남기려고 한다. + "dtls", // dtls 핸드세이킹 할때 로깅 남기기 + "srtp", // rtp 패킷을 암호화해서 보내는 부분 로깅 + "rtp", // rtp 전송할때 로깅을 남김 + "rtcp" // rtp를 제어하는 프로토콜인데 이 부분에서 로깅을 남긴다. + ], + + // udp 사용 포트 + rtcMinPort: 40000, + rtcMaxPort: process.env.NODE_ENV === "production" ? 41999 : 40199, + + // 현재나의 로그 메타데이터를 추가하려면 + appData : { + env : process.env.NODE_ENV + // 나중에 ip를 추가해서 어느 ip에 worker인지도 확인이 가능하고 탐색할때 유용하다. + } + +}; + +// router에 쓰이는 config ( router가 허용하는 config ) +export const mediaSoupRouterConfig : RouterOptions = { + + mediaCodecs: > [ + // audio 설정 + { + kind : "audio", + mimeType: "audio/opus", // 음성에서 기본적으로 사용하는 코덱이다. + clockRate: 48000, + // stereo 타입으로 화면공유에서 사용되는 사운드, 음악도 허용 한다는 의미 + channels : 2 + }, + + // video + 화면공유 설정 + { + kind: "video", + mimeType: "video/VP8", + clockRate: 90000, + // 비트레이트를 안정적으로 관리하기 위해서 사용하는요소 - simulcast에서 제어할때 사용할 것이다. + rtcpFeedback : [ + // 패킷 재전송 요청에 사용됨 + { type : "nack" }, + // 특정 키프레임을 요청하며 화질 복구에 사용됨 + { type : "nack", parameter : "pli" }, + // 전체 키프레임 요청 + { type : "ccm", parameter : "fir" }, + // 수신자가 감당 가능한 최대 비트레이트 알려줄때 사용 ( chrome 계열에서 사용 ) + { type : "goog-remb" }, + // 현재 패킷 전송에서 네트워크 상태를 계산할때 사용 + { type : "transport-cc" } + ], + parameters: { + "x-google-start-bitrate": 1000 // 첫 시작에서 쓰일 bite_rate + } + } + ] +}; + +// listenIps에 대해서 +export const listenIps : Array = [ + { + ip : "0.0.0.0", // 허용하는 프론트엔드 ip, + announcedIp: process.env.NODE_APP_SFU_PUBLIC_IP ?? "127.0.0.1" // ice 검증에서 나의 ip를 소개 클라이언트는 이 ip로 접근한다. 즉 udp로 접근이 가능한 ip를 알려주어야 한다. + } +]; \ No newline at end of file diff --git a/rep/main_backend/src/3-1.infra/media/mediasoup/media.ts b/rep/main_backend/src/3-1.infra/media/mediasoup/media.ts new file mode 100644 index 000000000..7a19aff4e --- /dev/null +++ b/rep/main_backend/src/3-1.infra/media/mediasoup/media.ts @@ -0,0 +1,67 @@ +import { Injectable, Logger, OnModuleDestroy, OnModuleInit } from "@nestjs/common"; +import { Router, type Worker } from "mediasoup/types"; +import * as mediasoup from "mediasoup"; +import * as os from "os"; +import { mediaSoupRouterConfig, mediaSoupWorkerConfig } from "./config"; + + +@Injectable() +export class MediasoupService implements OnModuleInit, OnModuleDestroy { + + private readonly logger = new Logger(MediasoupService.name); + + // 가용 가능한 worker ( cpu 개수당 보통 한개의 worker를 부여한다. ) + private readonly workers : Array = []; // 이 infra에 worker들이 관리한다. ( 나중에 분리 가능 ) + private workerIdx : number = 0; // 현재 worker 위치 + + // 처음에 이 모듈의 의존성을 부여할때 실행 + async onModuleInit() : Promise { + const workerCount : number = Number(Math.min(4, os.cpus().length)) // 일단은 worker = cpu 갯수로 가고 나중에 조정할 수도? ( 최대 4 ) + + this.logger.log(`mediasoup에서 가용할 worker의 갯수: ${workerCount}`); + + // worker 생성 + for ( let i = 0; i < workerCount; i++ ) { + const worker = await mediasoup.createWorker(mediaSoupWorkerConfig); + + // worker가 죽었을때 속성 부여 + worker.on("died", () => { + this.logger.warn(`mediasoup에 ${worker.pid} 사망`); + if (process.env.NODE_ENV === "production") process.exit(1); // 배포환경에서 해당 worker 프로세스를 종료 ( 재시작 루트를 다시 만들어 줘야 한다. -> 배포 환경이라면 ) -> 극단적으로 하는 이유는 이 기능이 거의 가장 핵심이 되기 때문이다. + }); + + this.workers.push(worker); + }; + }; + + // module이 내려가면 worker를 정리해주어야 한다. ( 안정성 높이기 위해 ) + async onModuleDestroy() : Promise { + for (const worker of this.workers) { + try { worker.close(); } catch (e) { this.logger.error(e); } + } + this.workers.length = 0; // 메모리에도 삭제 + }; + + // worker 가져오기 ( round-robin 알고리즘 사용 ) + picWorker() : { worker : Worker, workerIdx : number} { + if ( this.workers.length === 0 ) throw new Error("worker가 존재하지 않습니다."); + + const workerIdx : number = this.workerIdx; + const worker = this.workers[workerIdx]; + this.workerIdx = ( workerIdx + 1 ) % this.workers.length; + return { worker, workerIdx }; + }; + + // 특정 worker 가져오기 + getWorker(worker_idx : number) : Worker | undefined { + return this.workers[worker_idx]; + }; + + // router 생성해주는 메서드 + async createRouterOnPickedWorker(): Promise<{ router: Router; workerIdx: number; workerPid: number }> { + const { worker, workerIdx } = this.picWorker(); + const router = await worker.createRouter(mediaSoupRouterConfig); + return { router, workerIdx, workerPid: worker.pid }; + } + +}; \ No newline at end of file diff --git a/rep/main_backend/src/3-1.infra/media/mediasoup/sfu/sfu.outbound.ts b/rep/main_backend/src/3-1.infra/media/mediasoup/sfu/sfu.outbound.ts new file mode 100644 index 000000000..4b3ea28d9 --- /dev/null +++ b/rep/main_backend/src/3-1.infra/media/mediasoup/sfu/sfu.outbound.ts @@ -0,0 +1,40 @@ +import { Injectable, Logger } from "@nestjs/common"; +import type { Router, WebRtcTransport } from "mediasoup/types"; +import { listenIps } from "@infra/media/mediasoup/config"; +import type { TransportFactoryPort } from "@app/sfu/ports"; + + +@Injectable() +export class MediasoupTransportFactory implements TransportFactoryPort { + private readonly logger = new Logger(MediasoupTransportFactory.name); + + async createWebRtcTransport(router: Router): Promise { + + // ip 설정을 해두고 + const transport = await router.createWebRtcTransport({ + listenIps, + enableUdp: true, // udp 허용 + enableTcp: true, // tcp 허용 + preferUdp: true, // udp가 최우선 + initialAvailableOutgoingBitrate: 1_000_000, // 첫 바이트는 이정도 + }); + + // 최대 전송율 정하기 + try { + await transport.setMaxIncomingBitrate(1_500_000); + } catch (e) { + this.logger.warn(e); // 에러 메시지만 이유는 화면공유, 음성, 비디오등 다 다른데 하나로 고정할수는 없음 ( 상황에 따라서 나중에 개선해야 함 ) + } + + return transport; + } + + attachDebugHooks(roomId: string, transport: WebRtcTransport) { + transport.on("icestatechange", (state) => { + this.logger.debug({ roomId, transportId: transport.id, state }, "ICE state changed"); + }); + transport.on("dtlsstatechange", (state) => { + this.logger.debug({ roomId, transportId: transport.id, state }, "DTLS state changed"); + }); + } +} diff --git a/rep/main_backend/src/3-1.infra/memory/memory.module.ts b/rep/main_backend/src/3-1.infra/memory/memory.module.ts new file mode 100644 index 000000000..5f2174498 --- /dev/null +++ b/rep/main_backend/src/3-1.infra/memory/memory.module.ts @@ -0,0 +1,19 @@ +import { Global, Module } from "@nestjs/common"; +import { RoomCreateLockRepo, RoomRouterRepository } from "./sfu"; +import { TransportRepository } from "./sfu/transport-repo"; + + +@Global() +@Module({ + providers : [ + RoomRouterRepository, + RoomCreateLockRepo, + TransportRepository + ], + exports : [ + RoomRouterRepository, + RoomCreateLockRepo, + TransportRepository + ] +}) +export class MemoryModule {}; \ No newline at end of file diff --git a/rep/main_backend/src/3-1.infra/memory/sfu/index.ts b/rep/main_backend/src/3-1.infra/memory/sfu/index.ts new file mode 100644 index 000000000..af4a714d1 --- /dev/null +++ b/rep/main_backend/src/3-1.infra/memory/sfu/index.ts @@ -0,0 +1,3 @@ +export * from "./room-create-lock.repo"; +export * from "./room-router.repo" +export * from "./transport-repo"; \ No newline at end of file diff --git a/rep/main_backend/src/3-1.infra/memory/sfu/room-create-lock.repo.ts b/rep/main_backend/src/3-1.infra/memory/sfu/room-create-lock.repo.ts new file mode 100644 index 000000000..1310f0a81 --- /dev/null +++ b/rep/main_backend/src/3-1.infra/memory/sfu/room-create-lock.repo.ts @@ -0,0 +1,13 @@ +import { RoomEntry } from '@app/sfu/commands/dto'; +import { RoomCreateLockPort } from '@app/sfu/ports'; +import { Injectable } from '@nestjs/common'; + + +@Injectable() +export class RoomCreateLockRepo implements RoomCreateLockPort { + private readonly creating = new Map>(); + + get(roomId: string) { return this.creating.get(roomId); } + set(roomId: string, inflight: Promise) { this.creating.set(roomId, inflight); } + delete(roomId: string) { this.creating.delete(roomId); } +} diff --git a/rep/main_backend/src/3-1.infra/memory/sfu/room-router.repo.ts b/rep/main_backend/src/3-1.infra/memory/sfu/room-router.repo.ts new file mode 100644 index 000000000..b78638436 --- /dev/null +++ b/rep/main_backend/src/3-1.infra/memory/sfu/room-router.repo.ts @@ -0,0 +1,21 @@ +import { Injectable } from '@nestjs/common'; +import { RoomRouterRepositoryPort } from '@app/sfu/ports'; +import { RoomEntry } from '@app/sfu/commands/dto'; + + +@Injectable() +export class RoomRouterRepository implements RoomRouterRepositoryPort { + private readonly roomRouters = new Map(); + + get(roomId: string) { return this.roomRouters.get(roomId); } + set(roomId: string, entry: RoomEntry) { this.roomRouters.set(roomId, entry); } + delete(roomId: string) { this.roomRouters.delete(roomId); } + + patch(room_id : string, patchEntry : (entry : RoomEntry) => void) : void { + const entry = this.roomRouters.get(room_id); + if ( !entry ) return; + patchEntry(entry); + + this.roomRouters.set(room_id, entry); +}; +} diff --git a/rep/main_backend/src/3-1.infra/memory/sfu/transport-repo.ts b/rep/main_backend/src/3-1.infra/memory/sfu/transport-repo.ts new file mode 100644 index 000000000..3762591d9 --- /dev/null +++ b/rep/main_backend/src/3-1.infra/memory/sfu/transport-repo.ts @@ -0,0 +1,12 @@ +import { Injectable } from "@nestjs/common"; +import type { WebRtcTransport } from "mediasoup/types"; +import type { TransportRepositoryPort } from "@app/sfu/ports"; + + +@Injectable() +export class TransportRepository implements TransportRepositoryPort { + private readonly transports = new Map(); + get(id: string) { return this.transports.get(id); } + set(id: string, t: WebRtcTransport) { this.transports.set(id, t); } + delete(id: string) { this.transports.delete(id); } +}; \ No newline at end of file diff --git a/rep/main_backend/src/3-2.presentation/webrtc/sfu/sfu.interface.ts b/rep/main_backend/src/3-2.presentation/webrtc/sfu/sfu.interface.ts new file mode 100644 index 000000000..43c02df34 --- /dev/null +++ b/rep/main_backend/src/3-2.presentation/webrtc/sfu/sfu.interface.ts @@ -0,0 +1,13 @@ +import { RouterFactoryPort } from "@app/sfu/ports"; +import { MediasoupService } from "@infra/media/mediasoup/media"; +import { Injectable } from "@nestjs/common"; + + +@Injectable() +export class MediasoupRouterFactory implements RouterFactoryPort { + constructor(private readonly mediasoupService: MediasoupService) {} + + async createRouter() { + return this.mediasoupService.createRouterOnPickedWorker(); + }; +}; diff --git a/rep/main_backend/src/3-2.presentation/webrtc/sfu/sfu.module.ts b/rep/main_backend/src/3-2.presentation/webrtc/sfu/sfu.module.ts new file mode 100644 index 000000000..18b11b48c --- /dev/null +++ b/rep/main_backend/src/3-2.presentation/webrtc/sfu/sfu.module.ts @@ -0,0 +1,89 @@ +import { Module } from "@nestjs/common"; +import { SfuService } from "./sfu.service"; +import { MediasoupRouterFactory } from "./sfu.interface"; +import { CreateRouterUsecase, CreateTransportUsecase } from "@app/sfu/commands/usecase"; +import { RoomCreateLockRepo, RoomRouterRepository, TransportRepository } from "@infra/memory/sfu"; +import { RoomCreateLockPort, RoomRouterRepositoryPort, RouterFactoryPort, TransportFactoryPort, TransportRepositoryPort } from "@app/sfu/ports"; +import { CreateSfuTransportInfoToRedis, DeleteSfuTransportInfoToRedis } from "@infra/cache/redis/sfu/sfu.outbound"; +import { MediasoupTransportFactory } from "@infra/media/mediasoup/sfu/sfu.outbound"; +import { ConnectTransportUsecase } from "@app/sfu/queries/usecase"; +import { SelectSfuTransportDataFromRedis } from "@infra/cache/redis/sfu/sfu.inbound"; + + +@Module({ + providers : [ + SfuService, + + // 메모리 사용을 위한 의존성 주입 + MediasoupRouterFactory, + + // usecase들 + // router를 생성하기 위한 usecase + { + provide : CreateRouterUsecase, + useFactory : ( + roomRepo : RoomRouterRepositoryPort, + roomCreateLockRepo : RoomCreateLockPort, + routerFactory : RouterFactoryPort + ) => { + return new CreateRouterUsecase( + roomRepo, roomCreateLockRepo, routerFactory + ) + }, + inject : [ + RoomRouterRepository, + RoomCreateLockRepo, + MediasoupRouterFactory + ] + }, + + // transport 생성 usecase + { + provide : CreateTransportUsecase, + useFactory : ( + roomRepo : RoomRouterRepositoryPort, + transportRepo: TransportRepositoryPort, + transportFactory: TransportFactoryPort, + insertTranportInfoToRedis : CreateSfuTransportInfoToRedis, + deleteTransportInfoToRedis : DeleteSfuTransportInfoToRedis, + ) => { + return new CreateTransportUsecase( + roomRepo, + transportRepo, + transportFactory, + insertTranportInfoToRedis, + deleteTransportInfoToRedis + ) + }, + inject : [ + RoomRouterRepository, + TransportRepository, + MediasoupTransportFactory, + CreateSfuTransportInfoToRedis, + DeleteSfuTransportInfoToRedis, + ] + }, + + // transport 연결 usecase + { + provide : ConnectTransportUsecase, + useFactory : ( + transportRepo: TransportRepositoryPort, + selectSfuTransportInfoFromRedis : SelectSfuTransportDataFromRedis, + ) => { + return new ConnectTransportUsecase( + transportRepo, selectSfuTransportInfoFromRedis + ) + }, + inject : [ + TransportRepository, + SelectSfuTransportDataFromRedis + ] + } + + ], + exports : [ + SfuService, + ] +}) +export class SfuModule {}; \ No newline at end of file diff --git a/rep/main_backend/src/3-2.presentation/webrtc/sfu/sfu.service.ts b/rep/main_backend/src/3-2.presentation/webrtc/sfu/sfu.service.ts new file mode 100644 index 000000000..618f98602 --- /dev/null +++ b/rep/main_backend/src/3-2.presentation/webrtc/sfu/sfu.service.ts @@ -0,0 +1,58 @@ +import { Injectable } from "@nestjs/common"; +import { CreateTransportDto, RoomEntry, TransportEntry } from "@app/sfu/commands/dto"; +import { CreateRouterUsecase, CreateTransportUsecase } from "@app/sfu/commands/usecase"; +import { RoomRouterRepository, TransportRepository } from "@infra/memory/sfu"; +import { ConnectTransportUsecase } from "@app/sfu/queries/usecase"; +import { ConnectTransportType } from "@app/sfu/queries/dto"; + + +@Injectable() +export class SfuService { + + constructor( + // usecase + private readonly createRouterUsecase : CreateRouterUsecase, + private readonly createTransportUsecase : CreateTransportUsecase, + private readonly connectTransportUsecase : ConnectTransportUsecase, + // infra + private readonly roomRouters : RoomRouterRepository, + private readonly transports : TransportRepository, + ) {} + + // 1. router 생성 관련 함수 -> 생성 혹은 얻는 이유는 방을 만들었다고 무조건 router를 부여하면 비어있는 방에 낭비가 심할 수 있기에 들어와야 활성화가 된다. + async getOrCreateRoomRouter(room_id : string) : Promise { + return this.createRouterUsecase.execute(room_id); + }; + + // 방이 닫혔을때 로직도 구현해두자 + closeRoomRouter(room_id : string) : void { + const entry = this.roomRouters.get(room_id); + if ( !entry ) return; + + // transport를 관리 + for (const transport_id of entry.transport_ids) { + const t = this.transports.get(transport_id); + if (t && !t.closed) { + try { t.close(); } catch {} + } + this.transports.delete(transport_id); + }; + + try { + if (!entry.router.closed) entry.router.close(); + } finally { + this.roomRouters.delete(room_id); // 근데 생각해보면 close를 해두어서 삭제되기는 할거다. + }; + }; + + // 2. transport 부분 생성 ( 나중에 전체적인 부분 usecase로 빼자고 ) + async createTransPort(dto : CreateTransportDto) : Promise { + return this.createTransportUsecase.execute(dto); + }; + + // 3. transport connect 연결 ( 이때 부터는 이제 sfu와 webrtc 통신이 가능해졌다고 생각하면 된다. ) + async connectTransport(dto : ConnectTransportType) : Promise { + await this.connectTransportUsecase.execute(dto); + }; + +}; \ No newline at end of file diff --git a/rep/main_backend/src/3-2.presentation/websocket/signaling/signaling.gateway.ts b/rep/main_backend/src/3-2.presentation/websocket/signaling/signaling.gateway.ts index 27231cfdb..2c2adca23 100644 --- a/rep/main_backend/src/3-2.presentation/websocket/signaling/signaling.gateway.ts +++ b/rep/main_backend/src/3-2.presentation/websocket/signaling/signaling.gateway.ts @@ -1,25 +1,26 @@ // 시그널링 서버의 역할이라고 할 수 있을 것 같다. import { Logger, UsePipes, ValidationPipe } from "@nestjs/common"; -import { ConnectedSocket, MessageBody, OnGatewayConnection, OnGatewayDisconnect, OnGatewayInit, SubscribeMessage, WebSocketGateway, WebSocketServer } from "@nestjs/websockets"; +import { ConnectedSocket, MessageBody, OnGatewayConnection, OnGatewayDisconnect, OnGatewayInit, SubscribeMessage, WebSocketGateway, WebSocketServer, WsException } from "@nestjs/websockets"; import { Server, Socket } from "socket.io" import { SignalingWebsocketService } from "./signaling.service"; import { TokenDto } from "@app/auth/commands/dto"; import { PayloadRes } from "@app/auth/queries/dto"; import { JwtWsGuard } from "../auth/guards/jwt.guard"; -import { WEBSOCKET_AUTH_CLIENT_EVENT_NAME, WEBSOCKET_NAMESPACE, WEBSOCKET_SIGNALING_CLIENT_EVENT_NAME, WEBSOCKET_SIGNALING_EVENT_NAME } from "../websocket.constants"; -import { JoinRoomValidate, SocketPayload } from "./signaling.validate"; +import { WEBSOCKET_AUTH_CLIENT_EVENT_NAME, WEBSOCKET_NAMESPACE, WEBSOCKET_PATH, WEBSOCKET_SIGNALING_CLIENT_EVENT_NAME, WEBSOCKET_SIGNALING_EVENT_NAME } from "../websocket.constants"; +import { DtlsHandshakeValidate, JoinRoomValidate, NegotiateIceValidate, SocketPayload } from "./signaling.validate"; import { ConnectResult, ConnectRoomDto } from "@app/room/commands/dto"; import { CHANNEL_NAMESPACE } from "@infra/channel/channel.constants"; @WebSocketGateway({ namespace : WEBSOCKET_NAMESPACE.SIGNALING, + path : WEBSOCKET_PATH, // http 핸드세이킹이 있을때 붙게 되는 cors : { origin : process.env.NODE_ALLOWED_ORIGIN?.split(",").map((origin) => origin.trim()), credentials : process.env.NODE_ALLOWED_CREDENTIALS === "true" }, transports : ["websocket"], - pingTimeout: 60 * 60 * 1000 // ping pong 허용 시간 + pingTimeout: 20 * 1000 // ping pong 허용 시간 ( 20초 ) }) export class SignalingWebsocketGateway implements OnGatewayInit, OnGatewayConnection, OnGatewayDisconnect { @@ -115,4 +116,68 @@ export class SignalingWebsocketGateway implements OnGatewayInit, OnGatewayConnec }; }; + // 방에 가입 한 후 라우터 생성 -> 방에 가입 시킨 다음에 본격적으로 라우터를 생성하는 이벤트를 주어야 한다. + // 따로 분리해놓은 이유는 -> 정확히 방에 가입한 사람들만 이것을 이용하게 하고 싶기 때문이다. + // reture을 안준 이유도 sdp를 하는 방식이 다 다르기 때문이다. + @SubscribeMessage(WEBSOCKET_SIGNALING_EVENT_NAME.NEGOTIATE_SDP) + async sdpNegotiateGateway( + @ConnectedSocket() client : Socket + ) { + const room_id : string = client.data.room_id; + + try { + // 1. sfu 서버에 room_id에 router에 설정을 요구한다 이를 바탕으로 SDP를 할 예정 + const rtpCapabilities = await this.signalingService.sdpNegotiate(room_id); + + // 2. sdp 정보 반환 + return { ok : true, rtpCapabilities }; + } catch (err) { + this.logger.error(err); + throw new WsException({ message : err.message ?? "에러 발생", status : err.status ?? 500 }); + }; + }; + + // 여기서 추가할 점은 ( send, recv에 역할을 알 수 있는 매개변수가 있다면 좋을것이다 왜냐하면 이걸 이용해서 두개다 협상을 할거니까 ) + @SubscribeMessage(WEBSOCKET_SIGNALING_EVENT_NAME.NEGOTIATE_ICE) + @UsePipes(new ValidationPipe({ + whitelist : true, + transform : true + })) + async iceNegotiateGateway( + @ConnectedSocket() client : Socket, + @MessageBody() validate : NegotiateIceValidate + ) { + try { + // 1. sfu 서버에서 ice 협상에 필요한 정보 요구하고 dtls에 필요한 정보도 요구 + const transportOptions = await this.signalingService.iceNegotiate(client, validate.type); + + return { ok : true, transportOptions }; + } catch (err) { + this.logger.error(err); + throw new WsException({ message : err.message ?? "에러 발생", status : err.status ?? 500 }); + }; + }; + + // dtls 핸드세이크 과정 -> 사실상 여기에서 webrtc가 준비되었다고 보면 된다. - 보안 검증도 여기서 거쳐야 한다. + @SubscribeMessage(WEBSOCKET_SIGNALING_EVENT_NAME.DTLS_HANDSHAKE) + @UsePipes(new ValidationPipe({ + whitelist : true, + transform : true + })) + async dtlsHandshakeGateway( + @ConnectedSocket() client : Socket, + @MessageBody() validate : DtlsHandshakeValidate + ) { + try { + // 1. dtls 핸드세이크를 거칠것이다. + await this.signalingService.dtlsHandshake(client, validate); + + return { ok : true }; + } catch (err){ + this.logger.error(err); + throw new WsException({ message : err.message ?? "에러 발생", status : err.status ?? 500 }); + } + }; + + }; \ No newline at end of file diff --git a/rep/main_backend/src/3-2.presentation/websocket/signaling/signaling.module.ts b/rep/main_backend/src/3-2.presentation/websocket/signaling/signaling.module.ts index 9b8dac75f..8bc77c921 100644 --- a/rep/main_backend/src/3-2.presentation/websocket/signaling/signaling.module.ts +++ b/rep/main_backend/src/3-2.presentation/websocket/signaling/signaling.module.ts @@ -8,11 +8,13 @@ import { DeleteRoomDatasToRedis, InsertRoomDatasToRedis } from "@infra/cache/red import { SignalingWebsocketService } from "./signaling.service"; import { AuthWebsocketModule } from "../auth/auth.module"; import { SignalingWebsocketGateway } from "./signaling.gateway"; +import { SfuModule } from "@present/webrtc/sfu/sfu.module"; @Module({ imports : [ - AuthWebsocketModule + AuthWebsocketModule, + SfuModule ], providers : [ // sfu 자체적인 모듈 @@ -21,6 +23,8 @@ import { SignalingWebsocketGateway } from "./signaling.gateway"; CompareRoomArgonHash, // usecase 모아두기 + + // 방에 연결하기 위한 usecase { provide : ConnectRoomUsecase, useFactory : ( @@ -44,6 +48,8 @@ import { SignalingWebsocketGateway } from "./signaling.gateway"; DeleteHardRoomParticipantInfoDataToMysql ] }, + + // 방의 연결을 끊기 위한 usecase { provide : DisconnectRoomUsecase, useFactory : ( diff --git a/rep/main_backend/src/3-2.presentation/websocket/signaling/signaling.service.ts b/rep/main_backend/src/3-2.presentation/websocket/signaling/signaling.service.ts index 90cdec92d..dd13d2a80 100644 --- a/rep/main_backend/src/3-2.presentation/websocket/signaling/signaling.service.ts +++ b/rep/main_backend/src/3-2.presentation/websocket/signaling/signaling.service.ts @@ -5,9 +5,13 @@ import * as cookie from "cookie"; import { ConnectResult, ConnectRoomDto, DisconnectRoomDto } from "@app/room/commands/dto"; import { ConnectRoomUsecase, DisconnectRoomUsecase } from "@app/room/commands/usecase"; import { v7 as uuidV7 } from "uuid"; -import { SocketPayload } from "./signaling.validate"; +import { DtlsHandshakeValidate, SocketPayload } from "./signaling.validate"; import { PayloadRes } from "@app/auth/queries/dto"; -import { UnthorizedError } from "@error/application/user/user.error"; +import { SfuService } from "@present/webrtc/sfu/sfu.service"; +import { NotConnectSignalling } from "@error/presentation/signalling/signalling.error"; +import { CHANNEL_NAMESPACE } from "@infra/channel/channel.constants"; +import { CreateTransportDto } from "@app/sfu/commands/dto"; +import { ConnectTransportType } from "@app/sfu/queries/dto"; @Injectable() @@ -15,7 +19,8 @@ export class SignalingWebsocketService { constructor( private readonly disconnectRoomUsecase : DisconnectRoomUsecase, - private readonly connectRoomUsecase : ConnectRoomUsecase + private readonly connectRoomUsecase : ConnectRoomUsecase, + private readonly sfuServer : SfuService, ) {} parseJwtToken( client : Socket ) : TokenDto | undefined { @@ -42,8 +47,7 @@ export class SignalingWebsocketService { refresh_token = cookies["refresh_token"]; }; - if ( !access_token && !refresh_token ) return undefined; - if ( !access_token || !refresh_token ) throw new UnthorizedError("토큰이 존재하지 않습니다."); + if ( !access_token || !refresh_token ) return undefined; return { access_token, refresh_token @@ -56,6 +60,13 @@ export class SignalingWebsocketService { // ip를 파싱할때 사용하는 함수 private extractClientIp(client : Socket) : string { + + // 아래에서 위조 될수 있으니 최우선은 이 ip를 기준으로 한다. + const realIp = client.handshake.headers['x-real-ip']; + if (typeof realIp === 'string') { + return realIp; + } + // nginx가 클라이언트의 원 IP를 전달하기 위해서 만든 헤더이다. ( 즉 Nginx가 집적 걸어줌 ) const forwarded = client.handshake.headers['x-forwarded-for']; @@ -68,6 +79,10 @@ export class SignalingWebsocketService { return client.handshake.address; }; + makeRoomNamespace(room_id : string) : string { + return `${CHANNEL_NAMESPACE.SIGNALING}:${room_id}` + } + makeSocketData({ payload, socket } : { payload : PayloadRes | undefined, socket : Socket }) : SocketPayload { if ( payload ) return { ...payload, ip : this.extractClientIp(socket), socket_id : socket.id, is_guest : false @@ -80,6 +95,7 @@ export class SignalingWebsocketService { // 방에 나갈때 사용하는 함수 async disconnectRoomService(dto : DisconnectRoomDto) : Promise { await this.disconnectRoomUsecase.execute(dto); + this.sfuServer.closeRoomRouter(dto.room_id); // sfu 서버에 내용도 정리 }; // 방에 가입할때 사용하는 함수 @@ -92,4 +108,38 @@ export class SignalingWebsocketService { }; }; + // sdp 협상에 필요한 함수 + async sdpNegotiate( room_id : string ) { + if ( !room_id || room_id === "" ) throw new NotConnectSignalling(); + const entry = await this.sfuServer.getOrCreateRoomRouter(room_id); + return entry.router.rtpCapabilities; // sfu 서버에 codex 정보들 ( 나중에 변경 가능성 농후하다. ) + }; + + // ice 협상을 위해 sfu서버에 ice parameter와 후보들을 알려준다 그리고 dtls 핸드세이킹을 위한 파라미터도 전달 + async iceNegotiate( client : Socket, type : "send" | "recv" ) { + const room_id : string = client.data.room_id; + const payload : SocketPayload = client.data.user; + const dto : CreateTransportDto = { + room_id, + socket_id : payload.socket_id, + user_id : payload.user_id, + type,}; + if ( !room_id || room_id === "" ) throw new NotConnectSignalling(); + const transportOptions = await this.sfuServer.createTransPort(dto); + return transportOptions; + }; + + // dtls 핸드 세이크를 위한 + async dtlsHandshake( client: Socket, validate : DtlsHandshakeValidate) : Promise { + const room_id : string = client.data.room_id; + const payload : SocketPayload = client.data.user; + const dto : ConnectTransportType = { + ...validate, + room_id, + socket_id : payload.socket_id, + user_id : payload.user_id, + } + await this.sfuServer.connectTransport(dto); + } + }; \ No newline at end of file diff --git a/rep/main_backend/src/3-2.presentation/websocket/signaling/signaling.validate.ts b/rep/main_backend/src/3-2.presentation/websocket/signaling/signaling.validate.ts index 72e618ac7..b0f9bf34f 100644 --- a/rep/main_backend/src/3-2.presentation/websocket/signaling/signaling.validate.ts +++ b/rep/main_backend/src/3-2.presentation/websocket/signaling/signaling.validate.ts @@ -1,4 +1,5 @@ -import { IsNotEmpty, IsOptional, IsString, MaxLength, MinLength } from "class-validator"; +import { IsIn, IsNotEmpty, IsObject, IsOptional, IsString, MaxLength, MinLength } from "class-validator"; +import { type DtlsParameters } from "mediasoup/types"; // socket에서 사용할 @@ -29,4 +30,25 @@ export class JoinRoomValidate { @IsString() @MaxLength(16) // 16글자? nickname? : string; -} \ No newline at end of file +} + +export class NegotiateIceValidate { + @IsNotEmpty() + @IsIn([ "send", "recv" ]) + type : "send" | "recv" +}; + +export class DtlsHandshakeValidate { + + @IsNotEmpty() + @IsString() + transport_id : string; + + @IsNotEmpty() + @IsObject() + dtlsParameters: DtlsParameters; + + @IsNotEmpty() + @IsIn([ "send", "recv" ]) + type : "send" | "recv"; +}; \ No newline at end of file diff --git a/rep/main_backend/src/3-2.presentation/websocket/websocket.constants.ts b/rep/main_backend/src/3-2.presentation/websocket/websocket.constants.ts index 737f9f2d9..70b042600 100644 --- a/rep/main_backend/src/3-2.presentation/websocket/websocket.constants.ts +++ b/rep/main_backend/src/3-2.presentation/websocket/websocket.constants.ts @@ -1,8 +1,10 @@ +// websocket연결 path +export const WEBSOCKET_PATH = "/api/ws/" // websocket 연결할때 사용하는 namespace export const WEBSOCKET_NAMESPACE = Object.freeze({ - SIGNALING : "/api/ws/signal" + SIGNALING : "/signal" } as const); export const WEBSOCKET_AUTH_CLIENT_EVENT_NAME = Object.freeze({ @@ -12,9 +14,13 @@ export const WEBSOCKET_AUTH_CLIENT_EVENT_NAME = Object.freeze({ // export const WEBSOCKET_SIGNALING_EVENT_NAME = Object.freeze({ - JOIN_ROOM : "signaling:ws:join_room" + JOIN_ROOM : "signaling:ws:join_room", + NEGOTIATE_SDP : "signaling:ws:negotiate_sdp", + NEGOTIATE_ICE : "signaling:ws:negotiate_ice", + DTLS_HANDSHAKE : "signaling:ws:dtls_handshake" } as const); export const WEBSOCKET_SIGNALING_CLIENT_EVENT_NAME = Object.freeze({ - JOINED : "room:joined" + JOINED : "room:joined", + ADMISSION : "room:admission" } as const); \ No newline at end of file diff --git a/rep/main_backend/src/app.module.ts b/rep/main_backend/src/app.module.ts index ca73e5bec..f9fbd3deb 100644 --- a/rep/main_backend/src/app.module.ts +++ b/rep/main_backend/src/app.module.ts @@ -16,6 +16,9 @@ import { S3DiskModule } from '@infra/disk/s3/disk'; import { RedisChannelModule } from '@infra/channel/redis/channel'; import { RoomModule } from "@present/http/room/room.module"; import { SignalingWebsocketModule } from "@present/websocket/signaling/signaling.module"; +import { MediaModule } from "@infra/media/media.module"; +import { SfuModule } from "@present/webrtc/sfu/sfu.module"; +import { MemoryModule } from "@infra/memory/memory.module"; @Module({ @@ -29,12 +32,15 @@ import { SignalingWebsocketModule } from "@present/websocket/signaling/signalin JwtModule, // jwt를 사용하기 위한 모듈 S3DiskModule, // s3를 사용하기 위한 모듈 RedisChannelModule, // redis를 활용한 pub sub을 이용하기 위한 모듈 + MediaModule, // media를 다루기 위한 모듈 + MemoryModule, // in-memory를 사용하기 위해 필요한 모듈 // 우리가 집적 만든 모듈 SettingModule, // 헬스 체크를 위한 모듈 AuthModule, // 인증과 관련된 모듈 RoomModule, // 회의방 생성과 관련된 모듈 SignalingWebsocketModule , // 사실상 시그널링 서버의 역할을 하는 모듈 + SfuModule, // sfu와 관련되 모듈 ( 나중에 sfu 서버를 따로 분리할것을 생각하고 만든 모듈 그러니 sfu server라고 생각하면 될것 같다. ) ], controllers: [],