diff --git a/README.md b/README.md index 8ab9ba8..9e8c05d 100644 --- a/README.md +++ b/README.md @@ -48,7 +48,7 @@ |---|---| | 모노레포 | npm workspaces | | 공유 타입 | `@heist/shared` (TypeScript) | -| 백엔드 | Node.js, Socket.IO, tsx | +| 백엔드 | NestJS, Socket.IO | | 프론트엔드 | React, Vite, Zustand, styled-components | | 렌더링 | Canvas 2D (HiDPI 지원, devicePixelRatio) | | 블록체인 | Solana (devnet), @solana/web3.js | @@ -63,18 +63,12 @@ packages/ │ ├── constants.ts # 게임 밸런스 상수 │ ├── map.ts # 저장소, 스폰, 감옥, 장애물 배치 │ └── protocol.ts # Socket.IO 이벤트 타입 -├── backend/ # 게임 서버 +├── backend-nest/ # Nest 게임 서버 │ └── src/ -│ ├── game/ -│ │ ├── GameLoop.ts # 20Hz 틱 루프 (50ms) -│ │ ├── GameState.ts # 상태 관리, 플레이어별 스냅샷 필터링 -│ │ ├── physics.ts # 이동, 장애물 충돌, LOS 계산 -│ │ ├── skills.ts # 스킬 로직 (steal, arrest, break_jail) -│ │ └── BotAI.ts # 봇 AI -│ ├── rooms/ -│ │ └── Room.ts # 방 관리, 소켓별 개별 emit -│ └── solana/ -│ └── payout.ts # 정산 및 환불 +│ ├── gateway/ # Socket.IO Gateway +│ ├── web/ # health/metrics/state 엔드포인트 +│ ├── services/ # 런타임 서비스 +│ └── core/ # 게임 코어(Room/GameLoop/Observability) └── frontend/ # 게임 클라이언트 └── src/ ├── canvas/ @@ -110,7 +104,7 @@ npm run build -w packages/shared npm run dev ``` -- 백엔드: `http://localhost:3001` +- 백엔드: `http://localhost:8081` - 프론트엔드: `http://localhost:5173` ## 게임 밸런스 상수 diff --git a/TECHNICAL.md b/TECHNICAL.md index e7aa110..39b5eb5 100644 --- a/TECHNICAL.md +++ b/TECHNICAL.md @@ -12,9 +12,9 @@ │ │ │ ┌──────────────┐ ┌──────────────────┐ ┌────────────────┐ │ │ │ @heist/ │ │ @heist/ │ │ @heist/ │ │ -│ │ shared │◄──│ backend │ │ frontend │ │ +│ │ shared │◄──│ backend-nest │ │ frontend │ │ │ │ │◄──│ │ │ │ │ -│ │ Types │ │ Express │ │ React + Vite │ │ +│ │ Types │ │ NestJS │ │ React + Vite │ │ │ │ Constants │ │ Socket.IO │ │ Canvas 2D │ │ │ │ Map Data │ │ Game Loop │ │ Zustand │ │ │ │ Protocol │ │ Solana Payout │ │ Socket.IO │ │ @@ -41,10 +41,10 @@ npm workspaces 기반 모노레포로 3개 패키지를 관리합니다. | 패키지 | 역할 | 주요 의존성 | |--------|------|-------------| | `@heist/shared` | 타입, 상수, 맵 데이터, 소켓 프로토콜 정의 | TypeScript | -| `@heist/backend` | 게임 서버 (상태 관리, 물리, 스킬, 정산) | Express, Socket.IO, @solana/web3.js | +| `@heist/backend-nest` | 게임 서버 (Nest Gateway + 런타임 코어) | NestJS, Socket.IO, @solana/web3.js | | `@heist/frontend` | 게임 클라이언트 (렌더링, 입력, UI) | React, Vite, Zustand, styled-components, Socket.IO Client | -`@heist/shared`는 백엔드와 프론트엔드 양쪽에서 참조하여 **타입 안전한 통신 프로토콜**을 보장합니다. +`@heist/shared`는 Nest 백엔드와 프론트엔드 양쪽에서 참조하여 **타입 안전한 통신 프로토콜**을 보장합니다. --- @@ -52,10 +52,10 @@ npm workspaces 기반 모노레포로 3개 패키지를 관리합니다. ### 3.1 서버 초기화 -`Express` HTTP 서버 위에 `Socket.IO`를 마운트합니다. +`NestJS` 애플리케이션 위에서 `Socket.IO Gateway`를 통해 실시간 이벤트를 처리합니다. ``` -Express (HTTP) +NestJS (HTTP + WebSocket Gateway) └── Socket.IO (WebSocket + long-polling fallback) ├── CORS 설정 (프론트엔드 origin 허용) └── TypeScript 제네릭으로 타입 안전한 이벤트 처리 @@ -444,8 +444,8 @@ App.tsx ### 개발 환경 ```bash -npm install # 의존성 설치 -npm run dev # 백엔드(8080) + 프론트엔드(3000) 동시 실행 + npm install # 의존성 설치 + npm run dev # 백엔드(8081) + 프론트엔드(3000) 동시 실행 ``` ### 프로덕션 배포 @@ -462,7 +462,7 @@ npm run dev # 백엔드(8080) + 프론트엔드(3000) 동시 실행 |------|------| | `ESCROW_SECRET_KEY` | 에스크로 지갑 비밀키 (정산/환불용) | | `VITE_SERVER_URL` | 프론트엔드에서 백엔드 접속 URL | -| `PORT` | 백엔드 서버 포트 (기본 8080) | +| `PORT` | 백엔드 서버 포트 (기본 8081) | --- diff --git a/docs/backend-nest-full-cutover-checklist.md b/docs/backend-nest-full-cutover-checklist.md new file mode 100644 index 0000000..9f1a009 --- /dev/null +++ b/docs/backend-nest-full-cutover-checklist.md @@ -0,0 +1,102 @@ +# Backend Nest Full Cutover Checklist + +비운영(개발) 환경에서 `packages/backend`를 제거하고 `packages/backend-nest`로 완전 전환하기 위한 작업 순서. + +## 원칙 + +- 순서를 건너뛰지 않는다. +- 각 단계 완료 시 최소 스모크 테스트를 수행한다. +- 롤백이 필요하면 해당 단계 커밋으로 되돌린다. +- 단계별 수행이 끝날 때마다 커밋한다. +- 커밋 제목 형식은 `feat:`/`fix:`/`refactor:` + 한글 설명으로 통일한다. +- 코드 주석은 블록 단위로 맥락이 보이게 작성하고, 주석 문구는 한글로 작성한다. + +## 1) 기준선 고정 (브랜치/백업) + +- [x] 전환 전용 브랜치 생성 (`feat/nest-full-cutover`) +- [x] 현재 동작 상태 태그 또는 커밋 SHA 기록 +- [x] `.env`/배포 변수 스냅샷 저장 + +완료 기준: +- [x] 되돌릴 기준점이 명확하다. + +## 2) 실행 엔트리 Nest 기준으로 통일 + +- [x] 루트 `package.json`에서 `dev` 기본 백엔드를 `backend-nest`로 전환 +- [x] `build/start` 관련 스크립트가 Nest 산출물(`dist/main.js`) 기준인지 확인 +- [x] PM2/프로세스 매니저 설정(`ecosystem.config.cjs`)이 Nest 엔트리 기준인지 정렬 +- [x] README 실행 가이드를 Nest 기준으로 수정 + +완료 기준: +- [x] `npm run dev` 시 프론트 + Nest 조합으로 정상 구동된다. + +## 3) 네트워크/프록시 라우팅 단일화 + +- [x] Nginx/Ingress 설정에서 legacy 업스트림 의존 제거 +- [x] WebSocket 업그레이드 경로가 Nest 포트로 향하는지 검증 +- [x] 로컬/스테이징 포트 충돌 여부 확인 + +완료 기준: +- [x] HTTP + WS 모두 Nest만 타고 동작한다. + +## 4) 기능 동등성 스모크 테스트 + +- [x] 로비 진입/방 생성/방 참가 +- [x] 게임 시작/틱 진행/결과 처리 +- [x] 재연결(소켓 끊김 후 재입장) 시나리오 +- [x] 운영 엔드포인트 (`/health`, `/_metrics`, `/_state/*`) 응답 검증 +- [x] `scripts/canary-smoke.sh` 재사용 가능 시 Nest 대상 실행 + +완료 기준: +- [x] 사용자 핵심 플로우에서 blocker 이슈가 없다. + +## 5) 카나리 토글 제거 및 상수화 + +- [x] `NEST_TRAFFIC_CANARY_*` 기반 분기 코드 제거 또는 `always nest`로 단순화 +- [x] `route=legacy` 반환 경로 제거 +- [x] migration 전용 엔드포인트(`/_migrate/*`) 유지/삭제 여부 결정 후 반영 + +완료 기준: +- [x] 런타임 라우팅 의사결정이 더 이상 legacy를 참조하지 않는다. + +## 6) 레거시 backend 의존 코드 정리 + +- [x] 프론트/스크립트/문서에서 `packages/backend` 참조 전수 검색 (`rg "packages/backend|dev:backend"`) +- [x] CI 명령에서 legacy 경로 제거 +- [x] 배포 설정(예: Railway/Vercel/컨테이너) legacy 엔트리 제거 + +완료 기준: +- [x] 코드/설정/문서에 legacy 실행 경로가 남아있지 않다. + +## 7) legacy 패키지 제거 + +- [x] `packages/backend` 디렉터리 제거 +- [x] 워크스페이스 의존성 정리 후 `npm install` 재실행 +- [x] 타입체크/빌드 재검증 (`npm run typecheck`, `npm run build`) + +완료 기준: +- [x] 모노레포가 Nest 단일 백엔드로 정상 빌드된다. + +## 8) 최종 검증 및 릴리즈 준비 + +- [x] 로컬 E2E 스모크 1회 이상 재실행 +- [x] 모니터링 대시보드/알람 경로가 Nest 지표 기준인지 확인 +- [x] 전환 결과 문서화 (`what changed`, `known issues`, `rollback point`) + +완료 기준: +- [x] 다음 개발 사이클에서 legacy 없이 기능 개발 가능하다. + +## 권장 커밋 분할 + +- [x] Commit A: `feat: 실행 엔트리/문서 전환` +- [x] Commit B: `refactor: 라우팅 및 카나리 분기 단순화` +- [x] Commit C: `fix: 레거시 backend 제거 후 CI/배포 경로 정리` + +## 빠른 검증 명령 + +```bash +npm run dev +npm run typecheck +npm run build +npm run canary:smoke -w packages/backend-nest +``` diff --git a/docs/backend-nest-migration-plan.md b/docs/backend-nest-migration-plan.md index 4239194..14832ff 100644 --- a/docs/backend-nest-migration-plan.md +++ b/docs/backend-nest-migration-plan.md @@ -1,6 +1,7 @@ # Backend Nest Migration Plan -기존 `packages/backend`(Express + Socket.IO)를 유지한 채 `packages/backend-nest`를 병행 운영하며 단계적으로 이관한다. +기존 `packages/backend`를 유지한 채 `packages/backend-nest`를 병행 운영하며 단계적으로 이관했다. +`2026-03-09` 기준 full cutover 완료 상태를 기록한다. ## 원칙 @@ -8,7 +9,7 @@ - 단계 완료마다 커밋 + 실행 검증 - 관측/리스크 게이트(`/_metrics`, `/_state/*`) 먼저 이식 후 트래픽 이관 -## 단계 +## 단계(이력) 1. Nest 병행 실행 골격 [완료] - Nest 앱 신규 패키지 생성 @@ -31,7 +32,7 @@ - `RoomStateRepository` 및 정합성 검사 도구 이식 - 복구 드릴 API 및 risk gate 연동 -6. 점진 트래픽 전환 [진행중] +6. 점진 트래픽 전환 [완료] - `10% -> 25% -> 50% -> 100%` 룸 타입/비율 카나리 - 게이트 위반 시 즉시 롤백 @@ -40,6 +41,7 @@ - Stage4 리스크 게이트를 Nest에서도 동일 충족 - legacy backend 의존 read/write 경로 제거 - 운영 엔드포인트/대시보드 완전 이관 +- `packages/backend` 제거 ## 현재 상태 @@ -48,20 +50,8 @@ - 완료: 3단계(Room lifecycle 경로 이식) - 완료: 4단계(실제 RoomManager/GameLoop 경로를 Nest Gateway에 연결) - 완료: 5단계(Nest 로컬 `/_metrics`, `/_state/rooms`, `/_state/consistency`, `/_state/recovery-drill` 연동) -- 진행중: 6단계(룸 해시 기반 트래픽 카나리 + strict 게이트, 10% 검증 완료) - -## 6단계 환경변수 - -- `NEST_TRAFFIC_CANARY_ENABLED=true|false` (기본 `false`) -- `NEST_TRAFFIC_CANARY_PERCENT=10` (기본 `10`) -- `NEST_TRAFFIC_CANARY_ROOM_TYPES=default,ranked` (기본 `default`) -- `NEST_TRAFFIC_CANARY_STRICT=true|false` (기본 `false`) -- `NEST_TRAFFIC_SAMPLE_ROOM_ID=default:sample` (`/_metrics` 샘플 판정용) - -## 6단계 검증 포인트 - -- `GET /_migrate/room/decision?roomId=default:abc`로 라우팅 판정 확인 -- strict on + 비대상 roomId일 때 `join_room`/`/_migrate/room/join`에서 `route=legacy` 응답 확인 -- `/_metrics.migration.nestTrafficCanary`로 현재 카나리 설정/샘플 판정 확인 +- 완료: 6단계(100% Nest 전환) +- 완료: 카나리/legacy 라우팅 분기 제거 +- 완료: migration 전용 엔드포인트(`/_migrate/*`) 제거 참고 런북: `docs/backend-stage6-nest-traffic-rollout.md` diff --git a/docs/backend-stage4-message-ordering.md b/docs/backend-stage4-message-ordering.md index d181eb1..6647ce3 100644 --- a/docs/backend-stage4-message-ordering.md +++ b/docs/backend-stage4-message-ordering.md @@ -20,11 +20,10 @@ Stage 4의 메시지 순서 보장 정책은 `roomId`를 파티션 키로 삼고 파일: -- `packages/backend/src/rooms/RoomManager.ts` -- `packages/backend/src/observability/MetricsRegistry.ts` (`/_metrics.operations.messageOrdering`) +- `packages/backend-nest/src/core/rooms/RoomManager.ts` +- `packages/backend-nest/src/core/observability/MetricsRegistry.ts` (`/_metrics.operations.messageOrdering`) ## 한계 - 프로세스 내부 순서 보장이다. - 멀티 노드 환경의 전역 순서 보장은 여전히 room affinity/sticky session/adapter 구성에 의존한다. - diff --git a/docs/backend-stage4-state-strategy.md b/docs/backend-stage4-state-strategy.md index f9b6ede..22d26b8 100644 --- a/docs/backend-stage4-state-strategy.md +++ b/docs/backend-stage4-state-strategy.md @@ -31,9 +31,9 @@ Stage 4 시작 단계로, 메모리 authoritative 상태 + 외부 메타상태 ## 파일 -- `packages/backend/src/state/RoomStateRepository.ts` -- `packages/backend/src/rooms/RoomManager.ts` -- `packages/backend/src/server.ts` +- `packages/backend-nest/src/core/state/RoomStateRepository.ts` +- `packages/backend-nest/src/core/rooms/RoomManager.ts` +- `packages/backend-nest/src/web/ops.controller.ts` ## 다음 단계(Phase 2+) diff --git a/docs/backend-stage6-nest-traffic-rollout.md b/docs/backend-stage6-nest-traffic-rollout.md index 0bd85a4..ef27cc4 100644 --- a/docs/backend-stage6-nest-traffic-rollout.md +++ b/docs/backend-stage6-nest-traffic-rollout.md @@ -1,32 +1,27 @@ # Backend Stage 6 - Nest Traffic Rollout Runbook -Nest 병행 이관의 마지막 단계로, 트래픽을 `10% -> 25% -> 50% -> 100%`로 점진 전환한다. +이 문서는 병행 이관 당시의 카나리 전환 기록이다. +`2026-03-09` 기준으로 full cutover가 완료되어 현재는 100% Nest 단일 경로로 운영한다. -## 현재 체크포인트 +## 현재 상태 -- [x] `10%` + `strict=true` 동작 검증 완료 -- [x] 비대상 룸은 `route=legacy`로 거절 확인 -- [x] 대상 룸은 Nest 수용(`ok=true`) 확인 +- [x] 카나리 단계 이관 기록 보존 +- [x] `route=legacy` 경로 제거 +- [x] migration 전용 엔드포인트(`/_migrate/*`) 제거 -## 승격 절차 +## 현재 스모크 절차 -1. 비율 변경 -- `NEST_TRAFFIC_CANARY_PERCENT=25` (다음 단계는 `50`, `100`) - -2. 스모크 검증 -- `/_migrate/room/decision`으로 allow/deny roomId 확보 -- `/_migrate/room/join`으로 allow/deny 각각 기대 결과 확인 -- `0%`/`100%` 특수 케이스는 단방향(deny-only/allow-only) 검증으로 자동 처리 - -3. 리스크 게이트 확인 +1. 리스크 게이트 확인 - `/_metrics.operations.stage4RiskGates.rollbackRecommended=false` - `/_metrics.operations.stage4RiskGates.scaleOutAllowed=true` - `/_metrics.consistency.lastOk=true` -4. 관측 유지 -- 단계별 최소 30분 관측 후 다음 비율로 승격 +2. 운영 상태 점검 +- `GET /health` +- `GET /_state/consistency` +- `GET /_state/rooms` -## 롤백 기준 +## 롤백 기준(동일) - `rollbackRecommended=true` - `scaleOutAllowed=false` @@ -35,15 +30,11 @@ Nest 병행 이관의 마지막 단계로, 트래픽을 `10% -> 25% -> 50% -> 10 ## 빠른 실행 명령 ```bash -# 서버 실행 예시(25% 단계) -NEST_TRAFFIC_CANARY_ENABLED=true \ -NEST_TRAFFIC_CANARY_STRICT=true \ -NEST_TRAFFIC_CANARY_PERCENT=25 \ -NEST_TRAFFIC_CANARY_ROOM_TYPES=default \ +# 서버 실행 npm run dev -w packages/backend-nest ``` ```bash -# 스모크 체크 자동 실행 +# full-cutover 스모크 체크 실행 npm run canary:smoke -w packages/backend-nest ``` diff --git a/ecosystem.config.cjs b/ecosystem.config.cjs index 7ae3f1e..aa6403f 100644 --- a/ecosystem.config.cjs +++ b/ecosystem.config.cjs @@ -2,12 +2,12 @@ module.exports = { apps: [ { name: 'heist-be', - script: 'npx', - args: 'tsx packages/backend/src/index.ts', + script: 'node', + args: '--experimental-specifier-resolution=node packages/backend-nest/dist/main.js', cwd: '/srv/solstartup', env: { NODE_ENV: 'production', - PORT: 8080, + NEST_PORT: 8081, }, watch: false, max_memory_restart: '300M', diff --git a/infra/nginx/heist-sticky-session.conf.example b/infra/nginx/heist-sticky-session.conf.example index f59941f..550d9f6 100644 --- a/infra/nginx/heist-sticky-session.conf.example +++ b/infra/nginx/heist-sticky-session.conf.example @@ -3,17 +3,17 @@ # - ip_hash는 가장 단순한 방식이며, 운영 환경에 따라 cookie 기반 sticky가 더 적합할 수 있다. upstream heist_backend_socketio { ip_hash; - server 10.0.0.11:3001 max_fails=3 fail_timeout=10s; - server 10.0.0.12:3001 max_fails=3 fail_timeout=10s; - server 10.0.0.13:3001 max_fails=3 fail_timeout=10s; + server 10.0.0.11:8081 max_fails=3 fail_timeout=10s; + server 10.0.0.12:8081 max_fails=3 fail_timeout=10s; + server 10.0.0.13:8081 max_fails=3 fail_timeout=10s; } # 일반 HTTP API는 sticky가 꼭 필요하지 않으므로 별도 upstream으로 분리 가능 upstream heist_backend_http { least_conn; - server 10.0.0.11:3001 max_fails=3 fail_timeout=10s; - server 10.0.0.12:3001 max_fails=3 fail_timeout=10s; - server 10.0.0.13:3001 max_fails=3 fail_timeout=10s; + server 10.0.0.11:8081 max_fails=3 fail_timeout=10s; + server 10.0.0.12:8081 max_fails=3 fail_timeout=10s; + server 10.0.0.13:8081 max_fails=3 fail_timeout=10s; } server { diff --git a/package-lock.json b/package-lock.json index f457b6f..ce3d337 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1278,10 +1278,6 @@ "loose-envify": "^1.1.0" } }, - "node_modules/@heist/backend": { - "resolved": "packages/backend", - "link": true - }, "node_modules/@heist/backend-nest": { "resolved": "packages/backend-nest", "link": true @@ -4068,40 +4064,6 @@ "integrity": "sha512-9BCxFwvbGg/RsZK9tjXd8s4UcwR0MWeFQ1XEKIQVVvAGJyINdrqKMcTRyLoK8Rse1GjzLV9cwjWV1olXRWEXVA==", "license": "MIT" }, - "node_modules/@socket.io/redis-adapter": { - "version": "8.3.0", - "resolved": "https://registry.npmjs.org/@socket.io/redis-adapter/-/redis-adapter-8.3.0.tgz", - "integrity": "sha512-ly0cra+48hDmChxmIpnESKrc94LjRL80TEmZVscuQ/WWkRP81nNj8W8cCGMqbI4L6NCuAaPRSzZF1a9GlAxxnA==", - "license": "MIT", - "dependencies": { - "debug": "~4.3.1", - "notepack.io": "~3.0.1", - "uid2": "1.0.0" - }, - "engines": { - "node": ">=10.0.0" - }, - "peerDependencies": { - "socket.io-adapter": "^2.5.4" - } - }, - "node_modules/@socket.io/redis-adapter/node_modules/debug": { - "version": "4.3.7", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", - "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", - "license": "MIT", - "dependencies": { - "ms": "^2.1.3" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, "node_modules/@solana-mobile/mobile-wallet-adapter-protocol-web3js": { "version": "2.2.5", "resolved": "https://registry.npmjs.org/@solana-mobile/mobile-wallet-adapter-protocol-web3js/-/mobile-wallet-adapter-protocol-web3js-2.2.5.tgz", @@ -7356,17 +7318,6 @@ "@babel/types": "^7.28.2" } }, - "node_modules/@types/body-parser": { - "version": "1.19.6", - "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.6.tgz", - "integrity": "sha512-HLFeCYgz89uk22N5Qg3dvGvsv46B8GLvKKo1zKG4NybA8U2DiEO3w9lqGg29t/tfLRJpJ6iQxnVw4OnB7MoM9g==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/connect": "*", - "@types/node": "*" - } - }, "node_modules/@types/connect": { "version": "3.4.38", "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.38.tgz", @@ -7392,31 +7343,6 @@ "dev": true, "license": "MIT" }, - "node_modules/@types/express": { - "version": "5.0.6", - "resolved": "https://registry.npmjs.org/@types/express/-/express-5.0.6.tgz", - "integrity": "sha512-sKYVuV7Sv9fbPIt/442koC7+IIwK5olP1KWeD88e/idgoJqDm3JV/YUiPwkoKK92ylff2MGxSz1CSjsXelx0YA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/body-parser": "*", - "@types/express-serve-static-core": "^5.0.0", - "@types/serve-static": "^2" - } - }, - "node_modules/@types/express-serve-static-core": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-5.1.1.tgz", - "integrity": "sha512-v4zIMr/cX7/d2BpAEX3KNKL/JrT1s43s96lLvvdTmza1oEvDudCqK9aF/djc/SWgy8Yh0h30TZx5VpzqFCxk5A==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/node": "*", - "@types/qs": "*", - "@types/range-parser": "*", - "@types/send": "*" - } - }, "node_modules/@types/graceful-fs": { "version": "4.1.9", "resolved": "https://registry.npmjs.org/@types/graceful-fs/-/graceful-fs-4.1.9.tgz", @@ -7427,13 +7353,6 @@ "@types/node": "*" } }, - "node_modules/@types/http-errors": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/@types/http-errors/-/http-errors-2.0.5.tgz", - "integrity": "sha512-r8Tayk8HJnX0FztbZN7oVqGccWgw98T/0neJphO91KkmOzug1KkofZURD4UaD5uH8AqcFLfdPErnBod0u71/qg==", - "dev": true, - "license": "MIT" - }, "node_modules/@types/istanbul-lib-coverage": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.6.tgz", @@ -7483,20 +7402,6 @@ "devOptional": true, "license": "MIT" }, - "node_modules/@types/qs": { - "version": "6.14.0", - "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.14.0.tgz", - "integrity": "sha512-eOunJqu0K1923aExK6y8p6fsihYEn/BYuQ4g0CxAAgFc4b/ZLN4CrsRZ55srTdqoiLzU2B2evC+apEIxprEzkQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/range-parser": { - "version": "1.2.7", - "resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.7.tgz", - "integrity": "sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ==", - "dev": true, - "license": "MIT" - }, "node_modules/@types/react": { "version": "18.3.28", "resolved": "https://registry.npmjs.org/@types/react/-/react-18.3.28.tgz", @@ -7518,27 +7423,6 @@ "@types/react": "^18.0.0" } }, - "node_modules/@types/send": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/@types/send/-/send-1.2.1.tgz", - "integrity": "sha512-arsCikDvlU99zl1g69TcAB3mzZPpxgw0UQnaHeC1Nwb015xp8bknZv5rIfri9xTOcMuaVgvabfIRA7PSZVuZIQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/node": "*" - } - }, - "node_modules/@types/serve-static": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-2.2.0.tgz", - "integrity": "sha512-8mam4H1NHLtu7nmtalF7eyBH14QyOASmcxHhSfEoRyr0nP/YdoesEtU+uSRvMe96TW/HPTtkoKqQLl53N7UXMQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/http-errors": "*", - "@types/node": "*" - } - }, "node_modules/@types/stack-utils": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.3.tgz", @@ -7558,13 +7442,6 @@ "integrity": "sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw==", "license": "MIT" }, - "node_modules/@types/uuid": { - "version": "10.0.0", - "resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-10.0.0.tgz", - "integrity": "sha512-7gqG38EyHgyP1S+7+xomFtL+ZNHcKv6DwNaCZmJmo1vgMugyF3TCnXVg4t1uk89mLNwnLtnY3TpOpCOyp1/xHQ==", - "dev": true, - "license": "MIT" - }, "node_modules/@types/w3c-web-usb": { "version": "1.0.13", "resolved": "https://registry.npmjs.org/@types/w3c-web-usb/-/w3c-web-usb-1.0.13.tgz", @@ -9154,12 +9031,6 @@ "sprintf-js": "~1.0.2" } }, - "node_modules/array-flatten": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", - "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==", - "license": "MIT" - }, "node_modules/asap": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz", @@ -9495,19 +9366,6 @@ "node": "*" } }, - "node_modules/binary-extensions": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz", - "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/bindings": { "version": "1.5.0", "resolved": "https://registry.npmjs.org/bindings/-/bindings-1.5.0.tgz", @@ -9570,45 +9428,6 @@ "integrity": "sha512-EAcmnPkxpntVL+DS7bO1zhcZNvCkxqtkd0ZY53h06GNQ3DEkkGZ/gKgmDv6DdZQGj9BgfSPKtJJ7Dp1GPP8f7w==", "license": "MIT" }, - "node_modules/body-parser": { - "version": "1.20.4", - "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.4.tgz", - "integrity": "sha512-ZTgYYLMOXY9qKU/57FAo8F+HA2dGX7bqGc71txDRC1rS4frdFI5R7NhluHxH6M0YItAP0sHB4uqAOcYKxO6uGA==", - "license": "MIT", - "dependencies": { - "bytes": "~3.1.2", - "content-type": "~1.0.5", - "debug": "2.6.9", - "depd": "2.0.0", - "destroy": "~1.2.0", - "http-errors": "~2.0.1", - "iconv-lite": "~0.4.24", - "on-finished": "~2.4.1", - "qs": "~6.14.0", - "raw-body": "~2.5.3", - "type-is": "~1.6.18", - "unpipe": "~1.0.0" - }, - "engines": { - "node": ">= 0.8", - "npm": "1.2.8000 || >= 1.4.16" - } - }, - "node_modules/body-parser/node_modules/debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "license": "MIT", - "dependencies": { - "ms": "2.0.0" - } - }, - "node_modules/body-parser/node_modules/ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", - "license": "MIT" - }, "node_modules/borsh": { "version": "0.7.0", "resolved": "https://registry.npmjs.org/borsh/-/borsh-0.7.0.tgz", @@ -9660,6 +9479,7 @@ "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", "license": "MIT", + "peer": true, "dependencies": { "fill-range": "^7.1.1" }, @@ -10481,18 +10301,6 @@ "node": "^14.18.0 || >=16.10.0" } }, - "node_modules/content-disposition": { - "version": "0.5.4", - "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", - "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==", - "license": "MIT", - "dependencies": { - "safe-buffer": "5.2.1" - }, - "engines": { - "node": ">= 0.6" - } - }, "node_modules/content-type": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz", @@ -10523,12 +10331,6 @@ "integrity": "sha512-+W7VmiVINB+ywl1HGXJXmrqkOhpKrIiVZV6tQuV54ZyQC7MMuBt81Vc336GMLoHBq5hV/F9eXgt5Mnx0Rha5Fg==", "license": "MIT" }, - "node_modules/cookie-signature": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.7.tgz", - "integrity": "sha512-NXdYc3dLr47pBkpUCHtKSwIOQXLVn8dZEuywboCOJY/osA0wFSLlSawr3KN8qXJEyX66FcONTH8EIlVuK0yyFA==", - "license": "MIT" - }, "node_modules/core-util-is": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", @@ -10858,6 +10660,7 @@ "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz", "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==", "license": "MIT", + "peer": true, "engines": { "node": ">= 0.8", "npm": "1.2.8000 || >= 1.4.16" @@ -11227,6 +11030,8 @@ "dev": true, "hasInstallScript": true, "license": "MIT", + "optional": true, + "peer": true, "bin": { "esbuild": "bin/esbuild" }, @@ -11442,67 +11247,6 @@ "license": "Apache-2.0", "peer": true }, - "node_modules/express": { - "version": "4.22.1", - "resolved": "https://registry.npmjs.org/express/-/express-4.22.1.tgz", - "integrity": "sha512-F2X8g9P1X7uCPZMA3MVf9wcTqlyNp7IhH5qPCI0izhaOIYXaW9L535tGA3qmjRzpH+bZczqq7hVKxTR4NWnu+g==", - "license": "MIT", - "dependencies": { - "accepts": "~1.3.8", - "array-flatten": "1.1.1", - "body-parser": "~1.20.3", - "content-disposition": "~0.5.4", - "content-type": "~1.0.4", - "cookie": "~0.7.1", - "cookie-signature": "~1.0.6", - "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", - "fresh": "~0.5.2", - "http-errors": "~2.0.0", - "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.14.0", - "range-parser": "~1.2.1", - "safe-buffer": "5.2.1", - "send": "~0.19.0", - "serve-static": "~1.16.2", - "setprototypeof": "1.2.0", - "statuses": "~2.0.1", - "type-is": "~1.6.18", - "utils-merge": "1.0.1", - "vary": "~1.1.2" - }, - "engines": { - "node": ">= 0.10.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/express" - } - }, - "node_modules/express/node_modules/debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "license": "MIT", - "dependencies": { - "ms": "2.0.0" - } - }, - "node_modules/express/node_modules/ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", - "license": "MIT" - }, "node_modules/eyes": { "version": "0.1.8", "resolved": "https://registry.npmjs.org/eyes/-/eyes-0.1.8.tgz", @@ -11629,6 +11373,7 @@ "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", "license": "MIT", + "peer": true, "dependencies": { "to-regex-range": "^5.0.1" }, @@ -11645,39 +11390,6 @@ "node": ">=0.10.0" } }, - "node_modules/finalhandler": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.3.2.tgz", - "integrity": "sha512-aA4RyPcd3badbdABGDuTXCMTtOneUCAYH/gxoYRTZlIJdF0YPWuGqiAsIrhNnnqdXGswYk6dGujem4w80UJFhg==", - "license": "MIT", - "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.2", - "unpipe": "~1.0.0" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/finalhandler/node_modules/debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "license": "MIT", - "dependencies": { - "ms": "2.0.0" - } - }, - "node_modules/finalhandler/node_modules/ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", - "license": "MIT" - }, "node_modules/find-up": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", @@ -11763,6 +11475,7 @@ "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==", "license": "MIT", + "peer": true, "engines": { "node": ">= 0.6" } @@ -11892,6 +11605,8 @@ "integrity": "sha512-shZT/QMiSHc/YBLxxOkMtgSid5HFoauqCE3/exfsEcwg1WkeqjG+V40yBbBrsD+jW2HDXcs28xOfcbm2jI8Ddw==", "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "resolve-pkg-maps": "^1.0.0" }, @@ -11921,19 +11636,6 @@ "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/glob-parent": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", - "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", - "dev": true, - "license": "ISC", - "dependencies": { - "is-glob": "^4.0.1" - }, - "engines": { - "node": ">= 6" - } - }, "node_modules/gopd": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", @@ -12131,18 +11833,6 @@ "ms": "^2.0.0" } }, - "node_modules/iconv-lite": { - "version": "0.4.24", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", - "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", - "license": "MIT", - "dependencies": { - "safer-buffer": ">= 2.1.2 < 3" - }, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/idb-keyval": { "version": "6.2.2", "resolved": "https://registry.npmjs.org/idb-keyval/-/idb-keyval-6.2.2.tgz", @@ -12169,13 +11859,6 @@ ], "license": "BSD-3-Clause" }, - "node_modules/ignore-by-default": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/ignore-by-default/-/ignore-by-default-1.0.1.tgz", - "integrity": "sha512-Ius2VYcGNk7T90CppJqcIkS5ooHUZyIQK+ClZfMfMNFEF9VSE73Fq+906u/CWu92x4gzZMWOwfFYckPObzdEbA==", - "dev": true, - "license": "ISC" - }, "node_modules/image-size": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/image-size/-/image-size-1.2.1.tgz", @@ -12285,19 +11968,6 @@ "integrity": "sha512-m6UrgzFVUYawGBh1dUsWR5M2Clqic9RVXC/9f8ceNlv2IcO9j9J/z8UoCLPqtsPBFNzEpfR3xftohbfqDx8EQA==", "license": "MIT" }, - "node_modules/is-binary-path": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", - "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", - "dev": true, - "license": "MIT", - "dependencies": { - "binary-extensions": "^2.0.0" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/is-callable": { "version": "1.2.7", "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz", @@ -12326,16 +11996,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/is-extglob": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", - "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/is-fullwidth-code-point": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", @@ -12364,19 +12024,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/is-glob": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", - "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", - "dev": true, - "license": "MIT", - "dependencies": { - "is-extglob": "^2.1.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/is-nan": { "version": "1.3.2", "resolved": "https://registry.npmjs.org/is-nan/-/is-nan-1.3.2.tgz", @@ -12398,6 +12045,7 @@ "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", "license": "MIT", + "peer": true, "engines": { "node": ">=0.12.0" } @@ -13286,15 +12934,6 @@ "license": "MIT", "peer": true }, - "node_modules/merge-descriptors": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.3.tgz", - "integrity": "sha512-gaNvAS7TZ897/rVaZ0nMtAyxNyi/pdbjbAwUpFQpN70GqnVfOiXpeUUMKRBmzXaSQ8DdTX4/0ms62r2K+hE6mQ==", - "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/merge-options": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/merge-options/-/merge-options-3.0.4.tgz", @@ -13315,15 +12954,6 @@ "license": "MIT", "peer": true }, - "node_modules/methods": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", - "integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, "node_modules/metro": { "version": "0.83.4", "resolved": "https://registry.npmjs.org/metro/-/metro-0.83.4.tgz", @@ -13873,6 +13503,7 @@ "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", "license": "MIT", + "peer": true, "bin": { "mime": "cli.js" }, @@ -14065,148 +13696,6 @@ "integrity": "sha512-nmh3lCkYZ3grZvqcCH+fjmQ7X+H0OeZgP40OierEaAptX4XofMh5kwNbWh7lBduUzCcV/8kZ+NDLCwm2iorIlA==", "license": "MIT" }, - "node_modules/nodemon": { - "version": "3.1.14", - "resolved": "https://registry.npmjs.org/nodemon/-/nodemon-3.1.14.tgz", - "integrity": "sha512-jakjZi93UtB3jHMWsXL68FXSAosbLfY0In5gtKq3niLSkrWznrVBzXFNOEMJUfc9+Ke7SHWoAZsiMkNP3vq6Jw==", - "dev": true, - "license": "MIT", - "dependencies": { - "chokidar": "^3.5.2", - "debug": "^4", - "ignore-by-default": "^1.0.1", - "minimatch": "^10.2.1", - "pstree.remy": "^1.1.8", - "semver": "^7.5.3", - "simple-update-notifier": "^2.0.0", - "supports-color": "^5.5.0", - "touch": "^3.1.0", - "undefsafe": "^2.0.5" - }, - "bin": { - "nodemon": "bin/nodemon.js" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/nodemon" - } - }, - "node_modules/nodemon/node_modules/balanced-match": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.3.tgz", - "integrity": "sha512-1pHv8LX9CpKut1Zp4EXey7Z8OfH11ONNH6Dhi2WDUt31VVZFXZzKwXcysBgqSumFCmR+0dqjMK5v5JiFHzi0+g==", - "dev": true, - "license": "MIT", - "engines": { - "node": "20 || >=22" - } - }, - "node_modules/nodemon/node_modules/brace-expansion": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.2.tgz", - "integrity": "sha512-Pdk8c9poy+YhOgVWw1JNN22/HcivgKWwpxKq04M/jTmHyCZn12WPJebZxdjSa5TmBqISrUSgNYU3eRORljfCCw==", - "dev": true, - "license": "MIT", - "dependencies": { - "balanced-match": "^4.0.2" - }, - "engines": { - "node": "20 || >=22" - } - }, - "node_modules/nodemon/node_modules/chokidar": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", - "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", - "dev": true, - "license": "MIT", - "dependencies": { - "anymatch": "~3.1.2", - "braces": "~3.0.2", - "glob-parent": "~5.1.2", - "is-binary-path": "~2.1.0", - "is-glob": "~4.0.1", - "normalize-path": "~3.0.0", - "readdirp": "~3.6.0" - }, - "engines": { - "node": ">= 8.10.0" - }, - "funding": { - "url": "https://paulmillr.com/funding/" - }, - "optionalDependencies": { - "fsevents": "~2.3.2" - } - }, - "node_modules/nodemon/node_modules/has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=4" - } - }, - "node_modules/nodemon/node_modules/minimatch": { - "version": "10.2.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.2.tgz", - "integrity": "sha512-+G4CpNBxa5MprY+04MbgOw1v7So6n5JY166pFi9KfYwT78fxScCeSNQSNzp6dpPSW2rONOps6Ocam1wFhCgoVw==", - "dev": true, - "license": "BlueOak-1.0.0", - "dependencies": { - "brace-expansion": "^5.0.2" - }, - "engines": { - "node": "18 || 20 || >=22" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/nodemon/node_modules/picomatch": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", - "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8.6" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" - } - }, - "node_modules/nodemon/node_modules/readdirp": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", - "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", - "dev": true, - "license": "MIT", - "dependencies": { - "picomatch": "^2.2.1" - }, - "engines": { - "node": ">=8.10.0" - } - }, - "node_modules/nodemon/node_modules/supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "dev": true, - "license": "MIT", - "dependencies": { - "has-flag": "^3.0.0" - }, - "engines": { - "node": ">=4" - } - }, "node_modules/nofilter": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/nofilter/-/nofilter-3.1.0.tgz", @@ -14225,12 +13714,6 @@ "node": ">=0.10.0" } }, - "node_modules/notepack.io": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/notepack.io/-/notepack.io-3.0.1.tgz", - "integrity": "sha512-TKC/8zH5pXIAMVQio2TvVDTtPRX+DJPHDqjRbxogtFiByHyzKmy96RA0JtCQJ+WouyyL4A10xomQzgbUT+1jCg==", - "license": "MIT" - }, "node_modules/nullthrows": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/nullthrows/-/nullthrows-1.1.1.tgz", @@ -14557,12 +14040,6 @@ "node": ">=8" } }, - "node_modules/path-to-regexp": { - "version": "0.1.12", - "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.12.tgz", - "integrity": "sha512-RA1GjUVMnvYFxuqovrEqZoxxW5NUZqbwKtYz/Tt7nXerk0LbLblQmrsgdeOxV5SFHf0UDggjS/bSeOZwt1pmEQ==", - "license": "MIT" - }, "node_modules/pbkdf2": { "version": "3.1.5", "resolved": "https://registry.npmjs.org/pbkdf2/-/pbkdf2-3.1.5.tgz", @@ -14824,13 +14301,6 @@ "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==", "license": "MIT" }, - "node_modules/pstree.remy": { - "version": "1.1.8", - "resolved": "https://registry.npmjs.org/pstree.remy/-/pstree.remy-1.1.8.tgz", - "integrity": "sha512-77DZwxQmxKnu3aR542U+X8FypNzbfJ+C5XQDk3uWjWxn6151aIMGthWYRXTqT1E5oJvg+ljaa2OJi+VfvCOQ8w==", - "dev": true, - "license": "MIT" - }, "node_modules/public-encrypt": { "version": "4.0.3", "resolved": "https://registry.npmjs.org/public-encrypt/-/public-encrypt-4.0.3.tgz", @@ -14990,21 +14460,6 @@ "node": ">= 0.6" } }, - "node_modules/raw-body": { - "version": "2.5.3", - "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.3.tgz", - "integrity": "sha512-s4VSOf6yN0rvbRZGxs8Om5CWj6seneMwK3oDb4lWDH0UPhWcxwOWw5+qk24bxq87szX1ydrwylIOp2uG1ojUpA==", - "license": "MIT", - "dependencies": { - "bytes": "~3.1.2", - "http-errors": "~2.0.1", - "iconv-lite": "~0.4.24", - "unpipe": "~1.0.0" - }, - "engines": { - "node": ">= 0.8" - } - }, "node_modules/react": { "version": "16.14.0", "resolved": "https://registry.npmjs.org/react/-/react-16.14.0.tgz", @@ -15253,6 +14708,8 @@ "integrity": "sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==", "dev": true, "license": "MIT", + "optional": true, + "peer": true, "funding": { "url": "https://github.com/privatenumber/resolve-pkg-maps?sponsor=1" } @@ -15639,6 +15096,7 @@ "resolved": "https://registry.npmjs.org/send/-/send-0.19.2.tgz", "integrity": "sha512-VMbMxbDeehAxpOtWJXlcUS5E8iXh6QmN+BkRX1GARS3wRaXEEgzCcB10gTQazO42tpNIya8xIyNx8fll1OFPrg==", "license": "MIT", + "peer": true, "dependencies": { "debug": "2.6.9", "depd": "2.0.0", @@ -15663,6 +15121,7 @@ "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", "license": "MIT", + "peer": true, "dependencies": { "ms": "2.0.0" } @@ -15671,7 +15130,8 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", - "license": "MIT" + "license": "MIT", + "peer": true }, "node_modules/serialize-error": { "version": "2.1.0", @@ -15688,6 +15148,7 @@ "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.16.3.tgz", "integrity": "sha512-x0RTqQel6g5SY7Lg6ZreMmsOzncHFU7nhnRWkKgWuMTu5NN0DR5oruckMqRvacAN9d5w6ARnRBXl9xhDCgfMeA==", "license": "MIT", + "peer": true, "dependencies": { "encodeurl": "~2.0.0", "escape-html": "~1.0.3", @@ -15876,19 +15337,6 @@ "is-arrayish": "^0.3.1" } }, - "node_modules/simple-update-notifier": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/simple-update-notifier/-/simple-update-notifier-2.0.0.tgz", - "integrity": "sha512-a2B9Y0KlNXl9u/vsW6sTIu9vGEpfKu2wRV6l1H3XEas/0gUIzGzBoP/IouTcUQbm9JWZLH3COxyn03TYlFax6w==", - "dev": true, - "license": "MIT", - "dependencies": { - "semver": "^7.5.3" - }, - "engines": { - "node": ">=10" - } - }, "node_modules/slash": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", @@ -16443,6 +15891,7 @@ "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", "license": "MIT", + "peer": true, "dependencies": { "is-number": "^7.0.0" }, @@ -16483,16 +15932,6 @@ "integrity": "sha512-y/mWCZinnvxjTKYhJ+pYxwD0mRLVvOtdS2Awbgxln6iEnt4rk0yBxeSBHkGJcPucRiG0e55mwWp+g/05rsrd6w==", "license": "MIT" }, - "node_modules/touch": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/touch/-/touch-3.1.1.tgz", - "integrity": "sha512-r0eojU4bI8MnHr8c5bNo7lJDdI2qXlWWJk6a9EAFG7vbhTjElYhBVS3/miuE0uOuoLdb8Mc/rVfsmm6eo5o9GA==", - "dev": true, - "license": "ISC", - "bin": { - "nodetouch": "bin/nodetouch.js" - } - }, "node_modules/tr46": { "version": "0.0.3", "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", @@ -16571,6 +16010,8 @@ "integrity": "sha512-5C1sg4USs1lfG0GFb2RLXsdpXqBSEhAaA/0kPL01wxzpMqLILNxIxIOKiILz+cdg/pLnOUxFYOR5yhHU666wbw==", "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "esbuild": "~0.27.0", "get-tsconfig": "^4.7.5" @@ -16726,15 +16167,6 @@ "node": ">=8" } }, - "node_modules/uid2": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/uid2/-/uid2-1.0.0.tgz", - "integrity": "sha512-+I6aJUv63YAcY9n4mQreLUt0d4lvwkkopDNmpomkAUz0fAkEMV9pRWxN0EjhW1YfRhcuyHg2v3mwddCDW1+LFQ==", - "license": "MIT", - "engines": { - "node": ">= 4.0.0" - } - }, "node_modules/uint8array-extras": { "version": "1.5.0", "resolved": "https://registry.npmjs.org/uint8array-extras/-/uint8array-extras-1.5.0.tgz", @@ -16771,13 +16203,6 @@ "integrity": "sha512-Ql87qFHB3s/De2ClA9e0gsnS6zXG27SkTiSJwjCc9MebbfapQfuPzumMIUMi38ezPZVNFcHI9sUIepeQfw8J8Q==", "license": "MIT" }, - "node_modules/undefsafe": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/undefsafe/-/undefsafe-2.0.5.tgz", - "integrity": "sha512-WxONCrssBM8TSPRqN5EmsjVrsv4A8X12J4ArBiiayv3DyyG3ZlIg6yysuuSYdZsVz3TKcTg2fd//Ujd4CHV1iA==", - "dev": true, - "license": "MIT" - }, "node_modules/undici-types": { "version": "7.22.0", "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.22.0.tgz", @@ -16904,6 +16329,7 @@ "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", "integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==", "license": "MIT", + "peer": true, "engines": { "node": ">= 0.4.0" } @@ -18010,30 +17436,6 @@ } } }, - "packages/backend": { - "name": "@heist/backend", - "version": "0.1.0", - "dependencies": { - "@heist/shared": "*", - "@socket.io/redis-adapter": "^8.3.0", - "@solana/web3.js": "^1.98.0", - "cors": "^2.8.5", - "dotenv": "^16.4.0", - "express": "^4.21.0", - "redis": "^4.7.0", - "socket.io": "^4.8.3", - "uuid": "^11.1.0" - }, - "devDependencies": { - "@types/cors": "^2.8.17", - "@types/express": "^5.0.0", - "@types/node": "^22.0.0", - "@types/uuid": "^10.0.0", - "nodemon": "^3.1.14", - "tsx": "^4.19.0", - "typescript": "^5.7.0" - } - }, "packages/backend-nest": { "name": "@heist/backend-nest", "version": "0.1.0", @@ -18045,6 +17447,7 @@ "@nestjs/platform-socket.io": "^11.0.0", "@nestjs/websockets": "^11.0.0", "dotenv": "^16.4.0", + "redis": "^4.7.0", "reflect-metadata": "^0.2.2", "rxjs": "^7.8.1", "socket.io": "^4.8.3" @@ -18501,6 +17904,7 @@ "react-router-dom": "^6.28.0", "socket.io-client": "^4.8.3", "styled-components": "^6.1.0", + "uuid": "^11.1.0", "zustand": "^5.0.8" }, "devDependencies": { diff --git a/package.json b/package.json index 1d5a36e..6893366 100644 --- a/package.json +++ b/package.json @@ -6,11 +6,10 @@ ], "scripts": { "dev": "concurrently -n be,fe -c blue,green \"npm run dev:backend\" \"npm run dev:frontend\"", - "dev:backend": "npm run dev -w packages/backend", - "dev:backend:nest": "npm run dev -w packages/backend-nest", + "dev:backend": "npm run dev -w packages/backend-nest", "dev:frontend": "npm run dev -w packages/frontend", "build": "npm run build --workspaces --if-present", - "typecheck": "tsc --build" + "typecheck": "tsc --build packages/shared/tsconfig.json packages/backend-nest/tsconfig.json" }, "devDependencies": { "concurrently": "^9.2.1", diff --git a/packages/backend-nest/package.json b/packages/backend-nest/package.json index 9351eba..e316097 100644 --- a/packages/backend-nest/package.json +++ b/packages/backend-nest/package.json @@ -5,8 +5,8 @@ "type": "module", "scripts": { "dev": "node --loader ts-node/esm --experimental-specifier-resolution=node src/main.ts", - "build": "tsc", - "start": "node dist/main.js", + "build": "rm -rf dist && tsc", + "start": "node --experimental-specifier-resolution=node dist/main.js", "canary:smoke": "bash ./scripts/canary-smoke.sh" }, "dependencies": { @@ -18,6 +18,7 @@ "@nestjs/websockets": "^11.0.0", "dotenv": "^16.4.0", "reflect-metadata": "^0.2.2", + "redis": "^4.7.0", "rxjs": "^7.8.1", "socket.io": "^4.8.3" }, diff --git a/packages/backend-nest/scripts/canary-smoke.sh b/packages/backend-nest/scripts/canary-smoke.sh index 9d80eb2..cc2646f 100755 --- a/packages/backend-nest/scripts/canary-smoke.sh +++ b/packages/backend-nest/scripts/canary-smoke.sh @@ -2,114 +2,48 @@ set -euo pipefail BASE_URL="${BASE_URL:-http://127.0.0.1:8081}" -MAX_TRIES="${MAX_TRIES:-500}" -ALLOW="" -DENY="" +echo "[cutover-smoke] base_url=${BASE_URL}" -canary=$(curl -s "${BASE_URL}/_metrics" | jq '.migration.nestTrafficCanary') -enabled=$(echo "$canary" | jq -r '.enabled') -strict=$(echo "$canary" | jq -r '.strict') -percent=$(echo "$canary" | jq -r '.percent') - -echo "[canary-smoke] enabled=$enabled strict=$strict percent=$percent" - -# 같은 타입(default) 내에서 allow/deny 후보를 찾는다. -for i in $(seq 1 "$MAX_TRIES"); do - rid="default:smoke-$i" - allowed=$(curl -s "${BASE_URL}/_migrate/room/decision?roomId=${rid}" | jq -r '.decision.allowed') - if [[ "$allowed" == "true" && -z "$ALLOW" ]]; then - ALLOW="$rid" - fi - if [[ "$allowed" == "false" && -z "$DENY" ]]; then - DENY="$rid" - fi - # 특수케이스(0%,100%,disabled)에서는 한쪽만 있어도 충분하다. - if [[ "$enabled" != "true" || "$percent" == "0" || "$percent" == "100" ]]; then - if [[ -n "$ALLOW" || -n "$DENY" ]]; then - break - fi - else - if [[ -n "$ALLOW" && -n "$DENY" ]]; then - break - fi - fi -done - -# 모드별 기대 동작 검증: -# - disabled: allow만 검증 -# - 100%: allow만 검증 -# - 0%: deny만 검증(strict=true 전제) -# - 그 외: allow/deny 둘 다 검증 -if [[ "$enabled" != "true" || "$percent" == "100" ]]; then - if [[ -z "$ALLOW" ]]; then - echo "[canary-smoke] allow roomId 탐색 실패(전체 허용 모드)" >&2 - exit 1 - fi - echo "[canary-smoke] ALLOW=$ALLOW" - allow_resp=$(curl -s -X POST "${BASE_URL}/_migrate/room/join" \ - -H 'content-type: application/json' \ - -d "{\"roomId\":\"${ALLOW}\",\"walletAddress\":\"wallet-allow\"}") - allow_ok=$(echo "$allow_resp" | jq -r '.ok == true') - if [[ "$allow_ok" != "true" ]]; then - echo "[canary-smoke] allow 검증 실패" >&2 - echo "$allow_resp" | jq . >&2 - exit 1 - fi - echo "[canary-smoke] allow 검증 성공" -elif [[ "$percent" == "0" ]]; then - if [[ -z "$DENY" ]]; then - echo "[canary-smoke] deny roomId 탐색 실패(전체 차단 모드)" >&2 - exit 1 - fi - echo "[canary-smoke] DENY=$DENY" - deny_resp=$(curl -s -X POST "${BASE_URL}/_migrate/room/join" \ - -H 'content-type: application/json' \ - -d "{\"roomId\":\"${DENY}\",\"walletAddress\":\"wallet-deny\"}") - if [[ "$strict" == "true" ]]; then - deny_ok=$(echo "$deny_resp" | jq -r '.route == "legacy" and .ok == false') - if [[ "$deny_ok" != "true" ]]; then - echo "[canary-smoke] deny 검증 실패(strict=true)" >&2 - echo "$deny_resp" | jq . >&2 - exit 1 - fi - fi - echo "[canary-smoke] deny 검증 성공" -else - if [[ -z "$ALLOW" || -z "$DENY" ]]; then - echo "[canary-smoke] allow/deny roomId 탐색 실패(혼합 모드)" >&2 - exit 1 - fi - echo "[canary-smoke] ALLOW=$ALLOW" - echo "[canary-smoke] DENY=$DENY" - deny_resp=$(curl -s -X POST "${BASE_URL}/_migrate/room/join" \ - -H 'content-type: application/json' \ - -d "{\"roomId\":\"${DENY}\",\"walletAddress\":\"wallet-deny\"}") - allow_resp=$(curl -s -X POST "${BASE_URL}/_migrate/room/join" \ - -H 'content-type: application/json' \ - -d "{\"roomId\":\"${ALLOW}\",\"walletAddress\":\"wallet-allow\"}") - deny_ok=$(echo "$deny_resp" | jq -r '.route == "legacy" and .ok == false') - allow_ok=$(echo "$allow_resp" | jq -r '.ok == true') - if [[ "$deny_ok" != "true" ]]; then - echo "[canary-smoke] deny 검증 실패" >&2 - echo "$deny_resp" | jq . >&2 - exit 1 - fi - if [[ "$allow_ok" != "true" ]]; then - echo "[canary-smoke] allow 검증 실패" >&2 - echo "$allow_resp" | jq . >&2 - exit 1 - fi - echo "[canary-smoke] join 게이트 검증 성공" +health=$(curl -s "${BASE_URL}/health") +health_ok=$(echo "$health" | jq -r '.status == "ok" or .status == "degraded"') +if [[ "$health_ok" != "true" ]]; then + echo "[cutover-smoke] /health 검증 실패" >&2 + echo "$health" | jq . >&2 + exit 1 fi +echo "[cutover-smoke] /health 검증 성공" metrics=$(curl -s "${BASE_URL}/_metrics") +generated_at=$(echo "$metrics" | jq -r '.generatedAt') +if [[ "$generated_at" == "null" || -z "$generated_at" ]]; then + echo "[cutover-smoke] /_metrics generatedAt 누락" >&2 + echo "$metrics" | jq . >&2 + exit 1 +fi + rollback=$(echo "$metrics" | jq -r '.operations.stage4RiskGates.rollbackRecommended') scaleout=$(echo "$metrics" | jq -r '.operations.stage4RiskGates.scaleOutAllowed') -consistency=$(echo "$metrics" | jq -r '.consistency.lastOk') - -echo "[canary-smoke] rollbackRecommended=$rollback" -echo "[canary-smoke] scaleOutAllowed=$scaleout" -echo "[canary-smoke] consistency.lastOk=$consistency" - -echo "[canary-smoke] 완료" +consistency_last_ok=$(echo "$metrics" | jq -r '.consistency.lastOk') +echo "[cutover-smoke] rollbackRecommended=$rollback" +echo "[cutover-smoke] scaleOutAllowed=$scaleout" +echo "[cutover-smoke] consistency.lastOk=$consistency_last_ok" + +consistency=$(curl -s "${BASE_URL}/_state/consistency") +consistency_ok=$(echo "$consistency" | jq -r '.ok') +if [[ "$consistency_ok" != "true" && "$consistency_ok" != "false" ]]; then + echo "[cutover-smoke] /_state/consistency 응답 스키마 검증 실패" >&2 + echo "$consistency" | jq . >&2 + exit 1 +fi +echo "[cutover-smoke] /_state/consistency 검증 성공 (ok=${consistency_ok})" + +rooms_state=$(curl -s "${BASE_URL}/_state/rooms") +rooms_has_count=$(echo "$rooms_state" | jq -r 'has("count") and has("backend") and has("rooms")') +if [[ "$rooms_has_count" != "true" ]]; then + echo "[cutover-smoke] /_state/rooms 응답 스키마 검증 실패" >&2 + echo "$rooms_state" | jq . >&2 + exit 1 +fi +echo "[cutover-smoke] /_state/rooms 검증 성공" +echo "[cutover-smoke] 완료" diff --git a/packages/backend/src/cluster/affinity.ts b/packages/backend-nest/src/core/cluster/affinity.ts similarity index 100% rename from packages/backend/src/cluster/affinity.ts rename to packages/backend-nest/src/core/cluster/affinity.ts diff --git a/packages/backend/src/cluster/canary.ts b/packages/backend-nest/src/core/cluster/canary.ts similarity index 100% rename from packages/backend/src/cluster/canary.ts rename to packages/backend-nest/src/core/cluster/canary.ts diff --git a/packages/backend/src/cluster/sharding.ts b/packages/backend-nest/src/core/cluster/sharding.ts similarity index 100% rename from packages/backend/src/cluster/sharding.ts rename to packages/backend-nest/src/core/cluster/sharding.ts diff --git a/packages/backend/src/cluster/stage4Canary.ts b/packages/backend-nest/src/core/cluster/stage4Canary.ts similarity index 100% rename from packages/backend/src/cluster/stage4Canary.ts rename to packages/backend-nest/src/core/cluster/stage4Canary.ts diff --git a/packages/backend/src/game/BotAI.ts b/packages/backend-nest/src/core/game/BotAI.ts similarity index 100% rename from packages/backend/src/game/BotAI.ts rename to packages/backend-nest/src/core/game/BotAI.ts diff --git a/packages/backend/src/game/GameLoop.ts b/packages/backend-nest/src/core/game/GameLoop.ts similarity index 100% rename from packages/backend/src/game/GameLoop.ts rename to packages/backend-nest/src/core/game/GameLoop.ts diff --git a/packages/backend/src/game/GameState.ts b/packages/backend-nest/src/core/game/GameState.ts similarity index 98% rename from packages/backend/src/game/GameState.ts rename to packages/backend-nest/src/core/game/GameState.ts index 3dbd481..bc3edcc 100644 --- a/packages/backend/src/game/GameState.ts +++ b/packages/backend-nest/src/core/game/GameState.ts @@ -27,6 +27,7 @@ export class GameState { storages: Storage[]; jail: Jail; stolenCoins: number = 0; + teamCoins: number = 0; dynamicObstacles: Obstacle[] = []; constructor(playerInits: PlayerInit[]) { @@ -119,6 +120,7 @@ export class GameState { jail: this.jail, obstacles: this.getAllObstacles(), stolenCoins: this.stolenCoins, + teamCoins: this.teamCoins, totalCoins: TOTAL_COINS, }; } @@ -157,6 +159,7 @@ export class GameState { jail: this.jail, obstacles: allObstacles, stolenCoins: this.stolenCoins, + teamCoins: this.teamCoins, totalCoins: TOTAL_COINS, }; } @@ -198,6 +201,7 @@ export class GameState { jail: this.jail, obstacles: allObstacles, stolenCoins: this.stolenCoins, + teamCoins: this.teamCoins, totalCoins: TOTAL_COINS, }); } diff --git a/packages/backend/src/game/physics.ts b/packages/backend-nest/src/core/game/physics.ts similarity index 100% rename from packages/backend/src/game/physics.ts rename to packages/backend-nest/src/core/game/physics.ts diff --git a/packages/backend/src/game/skills.ts b/packages/backend-nest/src/core/game/skills.ts similarity index 98% rename from packages/backend/src/game/skills.ts rename to packages/backend-nest/src/core/game/skills.ts index f20c348..987c4cc 100644 --- a/packages/backend/src/game/skills.ts +++ b/packages/backend-nest/src/core/game/skills.ts @@ -195,6 +195,7 @@ export function updateChanneling(state: GameState, dt: number, now: number): Ski const drain = Math.min(STEAL_RATE * dt, storage.remainingCoins); storage.remainingCoins -= drain; state.stolenCoins += drain; + state.teamCoins += drain; if (storage.remainingCoins <= 0) { storage.remainingCoins = 0; @@ -297,9 +298,9 @@ export function tryBuildWall( if (!player || player.team !== 'thief') return null; if (player.isJailed || player.isStunned || player.channeling) return null; if (now < player.wallCooldownUntil) return null; - if (state.stolenCoins < WALL_COST_COINS) return null; + if (state.teamCoins < WALL_COST_COINS) return null; - state.stolenCoins -= WALL_COST_COINS; + state.teamCoins -= WALL_COST_COINS; // Place wall behind the player (opposite of last movement direction) let dx = -player.lastDirection.x; diff --git a/packages/backend/src/observability/LoopMetrics.ts b/packages/backend-nest/src/core/observability/LoopMetrics.ts similarity index 100% rename from packages/backend/src/observability/LoopMetrics.ts rename to packages/backend-nest/src/core/observability/LoopMetrics.ts diff --git a/packages/backend/src/observability/MetricsRegistry.ts b/packages/backend-nest/src/core/observability/MetricsRegistry.ts similarity index 100% rename from packages/backend/src/observability/MetricsRegistry.ts rename to packages/backend-nest/src/core/observability/MetricsRegistry.ts diff --git a/packages/backend/src/rooms/Room.ts b/packages/backend-nest/src/core/rooms/Room.ts similarity index 100% rename from packages/backend/src/rooms/Room.ts rename to packages/backend-nest/src/core/rooms/Room.ts diff --git a/packages/backend/src/rooms/RoomManager.ts b/packages/backend-nest/src/core/rooms/RoomManager.ts similarity index 100% rename from packages/backend/src/rooms/RoomManager.ts rename to packages/backend-nest/src/core/rooms/RoomManager.ts diff --git a/packages/backend/src/state/RoomStateRepository.ts b/packages/backend-nest/src/core/state/RoomStateRepository.ts similarity index 100% rename from packages/backend/src/state/RoomStateRepository.ts rename to packages/backend-nest/src/core/state/RoomStateRepository.ts diff --git a/packages/backend/src/utils/logger.ts b/packages/backend-nest/src/core/utils/logger.ts similarity index 100% rename from packages/backend/src/utils/logger.ts rename to packages/backend-nest/src/core/utils/logger.ts diff --git a/packages/backend-nest/src/domain/traffic-canary.ts b/packages/backend-nest/src/domain/traffic-canary.ts deleted file mode 100644 index 210ce6c..0000000 --- a/packages/backend-nest/src/domain/traffic-canary.ts +++ /dev/null @@ -1,103 +0,0 @@ -export type NestTrafficDecision = { - roomId: string; - roomType: string; - enabled: boolean; - allowed: boolean; - percent: number; - allowedRoomTypes: string[]; - bucket: number; - reason: string; -}; - -function hashPercent(input: string): number { - // roomId 해시 버킷(0~99)을 고정해 같은 룸이 항상 같은 판정을 받게 한다. - let hash = 2166136261; - for (let i = 0; i < input.length; i += 1) { - hash ^= input.charCodeAt(i); - hash = Math.imul(hash, 16777619); - } - return (hash >>> 0) % 100; -} - -export function getRoomType(roomId: string): string { - if (roomId.includes(':')) return roomId.split(':', 1)[0]; - if (roomId.includes('-')) return roomId.split('-', 1)[0]; - return 'default'; -} - -export function getNestTrafficDecision(roomId: string): NestTrafficDecision { - const enabled = process.env.NEST_TRAFFIC_CANARY_ENABLED === 'true'; - const roomType = getRoomType(roomId); - const allowedRoomTypes = (process.env.NEST_TRAFFIC_CANARY_ROOM_TYPES || 'default') - .split(',') - .map((v) => v.trim()) - .filter(Boolean); - const percentRaw = parseInt(process.env.NEST_TRAFFIC_CANARY_PERCENT || '10', 10); - const percent = Number.isFinite(percentRaw) ? Math.max(0, Math.min(100, percentRaw)) : 10; - const bucket = hashPercent(roomId); - - if (!enabled) { - return { - roomId, - roomType, - enabled, - allowed: true, - percent, - allowedRoomTypes, - bucket, - reason: 'canary-disabled', - }; - } - - if (!allowedRoomTypes.includes(roomType)) { - return { - roomId, - roomType, - enabled, - allowed: false, - percent, - allowedRoomTypes, - bucket, - reason: 'room-type-not-allowed', - }; - } - - if (percent === 0) { - return { - roomId, - roomType, - enabled, - allowed: false, - percent, - allowedRoomTypes, - bucket, - reason: 'percent-0', - }; - } - - if (percent === 100) { - return { - roomId, - roomType, - enabled, - allowed: true, - percent, - allowedRoomTypes, - bucket, - reason: 'percent-100', - }; - } - - const allowed = bucket < percent; - return { - roomId, - roomType, - enabled, - allowed, - percent, - allowedRoomTypes, - bucket, - reason: allowed ? 'bucket-allowed' : 'bucket-denied', - }; -} - diff --git a/packages/backend-nest/src/gateway/heist.gateway.ts b/packages/backend-nest/src/gateway/heist.gateway.ts index 93ce2d0..94e5d76 100644 --- a/packages/backend-nest/src/gateway/heist.gateway.ts +++ b/packages/backend-nest/src/gateway/heist.gateway.ts @@ -7,9 +7,8 @@ import { WebSocketServer, } from '@nestjs/websockets'; import type { Server, Socket } from 'socket.io'; -import { RoomManager } from '../../../backend/src/rooms/RoomManager.js'; -import { RuntimeCoreService } from '../services/runtime-core.service'; -import { getNestTrafficDecision } from '../domain/traffic-canary'; +import { RoomManager } from '../core/rooms/RoomManager.js'; +import { RuntimeCoreService } from '../services/runtime-core.service.js'; type JoinRoomPayload = { name: string; @@ -27,7 +26,6 @@ export class HeistGateway implements OnGatewayInit, OnGatewayConnection, OnGatew private readonly logger = new Logger(HeistGateway.name); private roomManager: RoomManager | null = null; private readonly minPlayers = parseInt(process.env.MIN_PLAYERS || '1', 10); - private readonly strictTrafficCanary = process.env.NEST_TRAFFIC_CANARY_STRICT === 'true'; @WebSocketServer() server!: Server; @@ -51,18 +49,6 @@ export class HeistGateway implements OnGatewayInit, OnGatewayConnection, OnGatew }); client.on('join_room', (roomId: string, payload: JoinRoomPayload, ack) => { - // 6단계: strict 모드에서는 카나리 비대상 룸을 Nest에서 받지 않고 legacy 라우팅을 유도한다. - const decision = getNestTrafficDecision(roomId); - if (this.strictTrafficCanary && !decision.allowed) { - ack({ - ok: false, - error: 'Room is not assigned to Nest by traffic canary policy', - retryAfterSec: 1, - route: 'legacy', - canary: decision, - }); - return; - } manager.handleJoinRoom(client as any, roomId, payload as any, ack); }); diff --git a/packages/backend-nest/src/main.ts b/packages/backend-nest/src/main.ts index 6dc0f18..b80bb9b 100644 --- a/packages/backend-nest/src/main.ts +++ b/packages/backend-nest/src/main.ts @@ -1,7 +1,7 @@ import 'reflect-metadata'; import 'dotenv/config'; import { NestFactory } from '@nestjs/core'; -import { AppModule } from './modules/app.module'; +import { AppModule } from './modules/app.module.js'; import { Logger } from '@nestjs/common'; async function bootstrap() { diff --git a/packages/backend-nest/src/modules/app.module.ts b/packages/backend-nest/src/modules/app.module.ts index 25fb3c3..9488733 100644 --- a/packages/backend-nest/src/modules/app.module.ts +++ b/packages/backend-nest/src/modules/app.module.ts @@ -1,13 +1,11 @@ import { Module } from '@nestjs/common'; -import { HealthController } from '../web/health.controller'; -import { OpsController } from '../web/ops.controller'; -import { RoomLifecycleController } from '../web/room-lifecycle.controller'; -import { RoomLifecycleService } from '../services/room-lifecycle.service'; -import { HeistGateway } from '../gateway/heist.gateway'; -import { RuntimeCoreService } from '../services/runtime-core.service'; +import { HealthController } from '../web/health.controller.js'; +import { OpsController } from '../web/ops.controller.js'; +import { HeistGateway } from '../gateway/heist.gateway.js'; +import { RuntimeCoreService } from '../services/runtime-core.service.js'; @Module({ - controllers: [HealthController, OpsController, RoomLifecycleController], - providers: [RoomLifecycleService, RuntimeCoreService, HeistGateway], + controllers: [HealthController, OpsController], + providers: [RuntimeCoreService, HeistGateway], }) export class AppModule {} diff --git a/packages/backend-nest/src/services/room-lifecycle.service.ts b/packages/backend-nest/src/services/room-lifecycle.service.ts deleted file mode 100644 index c7502ef..0000000 --- a/packages/backend-nest/src/services/room-lifecycle.service.ts +++ /dev/null @@ -1,311 +0,0 @@ -import { Injectable } from '@nestjs/common'; -import { - COP_COUNT, - ENTRY_FEE_LAMPORTS, - MAX_PLAYERS, - THIEF_COUNT, - type RoomInfo, - type RoomPlayer, - type Team, -} from '@heist/shared'; -import { buildSuggestedRoomId, getShardId } from '../domain/room-policy'; - -type JoinResult = { - ok: boolean; - error?: string; - retryAfterSec?: number; - suggestedRoomId?: string; -}; - -type AckResult = { - ok: boolean; - error?: string; -}; - -type RoomState = { - id: string; - name: string; - phase: 'filling' | 'playing' | 'ended'; - players: Map; - walletToSocketId: Map; - // 이행 단계에서는 최소 입력/스킬 트래킹만 수행하고, 추후 게임 루프 이식으로 확장한다. - lastInputBySocketId: Map; - lastSkillBySocketId: Map; -}; - -@Injectable() -export class RoomLifecycleService { - private readonly maxActiveRooms = parseInt(process.env.NEST_MAX_ACTIVE_ROOMS || '200', 10); - private readonly maxRoomsPerShard = parseInt(process.env.NEST_MAX_ROOMS_PER_SHARD || '25', 10); - private readonly shardCount = parseInt(process.env.NEST_SHARD_COUNT || '16', 10); - private readonly joinIdemTtlMs = parseInt(process.env.NEST_JOIN_IDEMPOTENCY_TTL_MS || '30000', 10); - - private readonly rooms = new Map(); - private readonly socketToRoomId = new Map(); - private readonly shardRooms = new Map(); - // roomId 파티션 키 기반 직렬 처리 큐 - private readonly roomOpQueue = new Map>(); - private readonly joinIdemCache = new Map(); - - joinRoom(input: { - socketId: string; - roomId: string; - name: string; - walletAddress: string; - requestId?: string; - }): JoinResult { - this.pruneIdempotency(); - const idemKey = input.requestId ? `${input.walletAddress}:${input.roomId}:${input.requestId}` : null; - if (idemKey) { - const cached = this.joinIdemCache.get(idemKey); - if (cached && cached.expiresAt > Date.now()) return cached.result; - } - - let room = this.rooms.get(input.roomId); - const shardId = getShardId(input.roomId, this.shardCount); - - if (!room && this.rooms.size >= this.maxActiveRooms) { - return this.cacheAndReturn(idemKey, { - ok: false, - error: `Nest room capacity reached (${this.maxActiveRooms})`, - retryAfterSec: 3, - }); - } - - if (!room) { - const roomsInShard = this.shardRooms.get(shardId) || 0; - if (roomsInShard >= this.maxRoomsPerShard) { - return this.cacheAndReturn(idemKey, { - ok: false, - error: `Nest shard ${shardId} is hot`, - retryAfterSec: 1, - suggestedRoomId: buildSuggestedRoomId(this.findCoolestShard()), - }); - } - room = { - id: input.roomId, - name: `Room ${input.roomId.slice(0, 6)}`, - phase: 'filling', - players: new Map(), - walletToSocketId: new Map(), - lastInputBySocketId: new Map(), - lastSkillBySocketId: new Map(), - }; - this.rooms.set(input.roomId, room); - this.shardRooms.set(shardId, roomsInShard + 1); - } - - if (room.phase !== 'filling') { - return this.cacheAndReturn(idemKey, { - ok: false, - error: 'Room is already in progress', - }); - } - if (room.players.size >= MAX_PLAYERS) { - return this.cacheAndReturn(idemKey, { - ok: false, - error: 'Room is full', - }); - } - - const prevSocketId = room.walletToSocketId.get(input.walletAddress); - if (prevSocketId && prevSocketId !== input.socketId) { - room.players.delete(prevSocketId); - this.socketToRoomId.delete(prevSocketId); - room.walletToSocketId.delete(input.walletAddress); - } - - room.players.set(input.socketId, { - id: input.socketId, - name: input.name, - walletAddress: input.walletAddress, - ready: false, - confirmed: ENTRY_FEE_LAMPORTS === 0, - selectedTeam: 'thief', - }); - room.walletToSocketId.set(input.walletAddress, input.socketId); - this.socketToRoomId.set(input.socketId, input.roomId); - return this.cacheAndReturn(idemKey, { ok: true }); - } - - confirmEntry(socketId: string): AckResult { - const roomId = this.socketToRoomId.get(socketId); - if (!roomId) return { ok: false, error: 'Not in room' }; - this.enqueueRoomOperation(roomId, () => { - const player = this.getPlayerBySocket(socketId); - if (player) player.confirmed = true; - }); - return { ok: true }; - } - - selectTeam(socketId: string, team: Team): AckResult { - const roomId = this.socketToRoomId.get(socketId); - if (!roomId) return { ok: false, error: 'Not in room' }; - const room = this.rooms.get(roomId); - if (!room) return { ok: false, error: 'Not in room' }; - const player = room.players.get(socketId); - if (!player) return { ok: false, error: 'Not in room' }; - - let count = 0; - for (const [id, p] of room.players.entries()) { - if (id !== socketId && p.selectedTeam === team) count += 1; - } - const limit = team === 'cop' ? COP_COUNT : THIEF_COUNT; - if (count >= limit) return { ok: false, error: `${team} team is full` }; - - this.enqueueRoomOperation(roomId, () => { - player.selectedTeam = team; - }); - return { ok: true }; - } - - setReady(socketId: string): void { - const roomId = this.socketToRoomId.get(socketId); - if (!roomId) return; - this.enqueueRoomOperation(roomId, () => { - const player = this.getPlayerBySocket(socketId); - if (player) player.ready = true; - }); - } - - applyInput(socketId: string, direction: { x: number; y: number }): void { - const roomId = this.socketToRoomId.get(socketId); - if (!roomId) return; - const room = this.rooms.get(roomId); - if (!room) return; - this.enqueueRoomOperation(roomId, () => { - room.lastInputBySocketId.set(socketId, direction); - }); - } - - requestSkill(socketId: string, skill: string, targetId?: string): void { - const roomId = this.socketToRoomId.get(socketId); - if (!roomId) return; - const room = this.rooms.get(roomId); - if (!room) return; - this.enqueueRoomOperation(roomId, () => { - room.lastSkillBySocketId.set(socketId, { - skill, - targetId, - at: Date.now(), - }); - }); - } - - cancelSkill(socketId: string): void { - const roomId = this.socketToRoomId.get(socketId); - if (!roomId) return; - const room = this.rooms.get(roomId); - if (!room) return; - this.enqueueRoomOperation(roomId, () => { - room.lastSkillBySocketId.delete(socketId); - }); - } - - handleDisconnect(socketId: string): string | null { - const roomId = this.socketToRoomId.get(socketId); - if (!roomId) return null; - const room = this.rooms.get(roomId); - this.socketToRoomId.delete(socketId); - if (!room) return null; - - const player = room.players.get(socketId); - if (player) { - room.walletToSocketId.delete(player.walletAddress); - } - room.players.delete(socketId); - - if (room.players.size === 0) { - this.rooms.delete(roomId); - const shardId = getShardId(roomId, this.shardCount); - const next = Math.max(0, (this.shardRooms.get(shardId) || 0) - 1); - if (next === 0) this.shardRooms.delete(shardId); - else this.shardRooms.set(shardId, next); - } - - return roomId; - } - - listRooms(): RoomInfo[] { - return [...this.rooms.values()] - .filter((room) => room.phase === 'filling') - .map((room) => this.toRoomInfo(room)); - } - - getRoomInfo(roomId: string): RoomInfo | null { - const room = this.rooms.get(roomId); - if (!room) return null; - return this.toRoomInfo(room); - } - - getSnapshot() { - return { - roomCount: this.rooms.size, - shardCount: this.shardCount, - shardRooms: [...this.shardRooms.entries()].map(([shardId, rooms]) => ({ shardId, rooms })), - }; - } - - private toRoomInfo(room: RoomState): RoomInfo { - return { - id: room.id, - name: room.name, - players: [...room.players.values()], - maxPlayers: MAX_PLAYERS, - entryFeeLamports: ENTRY_FEE_LAMPORTS, - }; - } - - private getPlayerBySocket(socketId: string): RoomPlayer | null { - const roomId = this.socketToRoomId.get(socketId); - if (!roomId) return null; - const room = this.rooms.get(roomId); - if (!room) return null; - return room.players.get(socketId) || null; - } - - private findCoolestShard(): number { - let coolest = 0; - let minRooms = Number.MAX_SAFE_INTEGER; - for (let shard = 0; shard < Math.max(1, this.shardCount); shard += 1) { - const count = this.shardRooms.get(shard) || 0; - if (count < minRooms) { - minRooms = count; - coolest = shard; - } - } - return coolest; - } - - private cacheAndReturn(idemKey: string | null, result: JoinResult): JoinResult { - if (idemKey) { - this.joinIdemCache.set(idemKey, { - result, - expiresAt: Date.now() + this.joinIdemTtlMs, - }); - } - return result; - } - - private pruneIdempotency(): void { - const now = Date.now(); - for (const [key, entry] of this.joinIdemCache.entries()) { - if (entry.expiresAt <= now) this.joinIdemCache.delete(key); - } - } - - private enqueueRoomOperation(roomId: string, fn: () => void): void { - const prev = this.roomOpQueue.get(roomId) || Promise.resolve(); - const next = prev - .then(() => { - fn(); - }) - .catch(() => {}) - .finally(() => { - if (this.roomOpQueue.get(roomId) === next) { - this.roomOpQueue.delete(roomId); - } - }); - this.roomOpQueue.set(roomId, next); - } -} diff --git a/packages/backend-nest/src/services/runtime-core.service.ts b/packages/backend-nest/src/services/runtime-core.service.ts index 607d3ec..6a90e9c 100644 --- a/packages/backend-nest/src/services/runtime-core.service.ts +++ b/packages/backend-nest/src/services/runtime-core.service.ts @@ -1,6 +1,6 @@ import { Injectable, Logger, OnModuleDestroy } from '@nestjs/common'; -import { RoomManager, type StateConsistencyReport } from '../../../backend/src/rooms/RoomManager.js'; -import { metricsRegistry } from '../../../backend/src/observability/MetricsRegistry.js'; +import { RoomManager, type StateConsistencyReport } from '../core/rooms/RoomManager.js'; +import { metricsRegistry } from '../core/observability/MetricsRegistry.js'; @Injectable() export class RuntimeCoreService implements OnModuleDestroy { diff --git a/packages/backend-nest/src/web/health.controller.ts b/packages/backend-nest/src/web/health.controller.ts index a12d35d..a73fb55 100644 --- a/packages/backend-nest/src/web/health.controller.ts +++ b/packages/backend-nest/src/web/health.controller.ts @@ -1,5 +1,5 @@ import { Controller, Get } from '@nestjs/common'; -import { metricsRegistry } from '../../../backend/src/observability/MetricsRegistry.js'; +import { metricsRegistry } from '../core/observability/MetricsRegistry.js'; @Controller() export class HealthController { @@ -9,7 +9,7 @@ export class HealthController { return { status: metricsRegistry.isOverloaded() ? 'degraded' : 'ok', service: 'backend-nest', - phase: 'parallel-migration', + phase: 'full-cutover', checkedAt: new Date().toISOString(), }; } diff --git a/packages/backend-nest/src/web/ops.controller.ts b/packages/backend-nest/src/web/ops.controller.ts index c1dd454..873d28e 100644 --- a/packages/backend-nest/src/web/ops.controller.ts +++ b/packages/backend-nest/src/web/ops.controller.ts @@ -1,8 +1,7 @@ import { BadRequestException, Body, Controller, Get, Post } from '@nestjs/common'; -import { metricsRegistry } from '../../../backend/src/observability/MetricsRegistry.js'; -import { roomStateRepository } from '../../../backend/src/state/RoomStateRepository.js'; -import { getNestTrafficDecision } from '../domain/traffic-canary'; -import { RuntimeCoreService } from '../services/runtime-core.service'; +import { metricsRegistry } from '../core/observability/MetricsRegistry.js'; +import { roomStateRepository } from '../core/state/RoomStateRepository.js'; +import { RuntimeCoreService } from '../services/runtime-core.service.js'; @Controller() export class OpsController { @@ -10,25 +9,7 @@ export class OpsController { @Get('/_metrics') async getMetrics() { - // legacy 프록시를 거치지 않고 Nest 프로세스가 누적한 메트릭 스냅샷을 직접 노출한다. - const snapshot = metricsRegistry.getSnapshot() as Record; - const sampleRoomId = process.env.NEST_TRAFFIC_SAMPLE_ROOM_ID || 'default:sample'; - // 6단계 운영 가시성: 현재 Nest 카나리 정책과 샘플 룸 판정을 메트릭에 함께 노출한다. - return { - ...snapshot, - migration: { - nestTrafficCanary: { - strict: process.env.NEST_TRAFFIC_CANARY_STRICT === 'true', - enabled: process.env.NEST_TRAFFIC_CANARY_ENABLED === 'true', - percent: Number(process.env.NEST_TRAFFIC_CANARY_PERCENT || '10'), - roomTypes: (process.env.NEST_TRAFFIC_CANARY_ROOM_TYPES || 'default') - .split(',') - .map((v) => v.trim()) - .filter(Boolean), - sampleDecision: getNestTrafficDecision(sampleRoomId), - }, - }, - }; + return metricsRegistry.getSnapshot(); } @Get('/_state/consistency') diff --git a/packages/backend-nest/src/web/room-lifecycle.controller.ts b/packages/backend-nest/src/web/room-lifecycle.controller.ts deleted file mode 100644 index 4fced40..0000000 --- a/packages/backend-nest/src/web/room-lifecycle.controller.ts +++ /dev/null @@ -1,68 +0,0 @@ -import { Body, Controller, Get, Post, Query } from '@nestjs/common'; -import { RoomLifecycleService } from '../services/room-lifecycle.service'; -import { getNestTrafficDecision } from '../domain/traffic-canary'; - -@Controller('/_migrate/room') -export class RoomLifecycleController { - constructor(private readonly roomLifecycle: RoomLifecycleService) {} - private readonly strictTrafficCanary = process.env.NEST_TRAFFIC_CANARY_STRICT === 'true'; - - @Post('/join') - join( - @Body() body: { roomId?: string; walletAddress?: string; requestId?: string }, - ) { - if (!body?.roomId || !body?.walletAddress) { - return { - ok: false, - error: 'roomId and walletAddress are required', - }; - } - - const decision = getNestTrafficDecision(body.roomId); - // 운영 점검용 HTTP join probe도 소켓 경로와 같은 카나리 게이트를 사용한다. - if (this.strictTrafficCanary && !decision.allowed) { - return { - ok: false, - error: 'Room is not assigned to Nest by traffic canary policy', - route: 'legacy', - canary: decision, - snapshot: this.roomLifecycle.getSnapshot(), - }; - } - - const result = this.roomLifecycle.joinRoom({ - socketId: `http:${body.walletAddress}`, - roomId: body.roomId, - name: 'http-probe', - walletAddress: body.walletAddress, - requestId: body.requestId, - }); - return { - ...result, - canary: decision, - snapshot: this.roomLifecycle.getSnapshot(), - }; - } - - @Get('/decision') - decision(@Query('roomId') roomId?: string) { - if (!roomId) { - return { - ok: false, - error: 'roomId is required', - }; - } - // 트래픽 전환 전/후에 roomId별 라우팅 판정을 운영에서 즉시 검증할 수 있다. - const decision = getNestTrafficDecision(roomId); - return { - ok: true, - strict: this.strictTrafficCanary, - decision, - }; - } - - @Get('/snapshot') - snapshot() { - return this.roomLifecycle.getSnapshot(); - } -} diff --git a/packages/backend-nest/tsconfig.json b/packages/backend-nest/tsconfig.json index 01ebaff..5e985ac 100644 --- a/packages/backend-nest/tsconfig.json +++ b/packages/backend-nest/tsconfig.json @@ -3,8 +3,8 @@ "compilerOptions": { "outDir": "./dist", "rootDir": "./src", - "module": "NodeNext", - "moduleResolution": "NodeNext", + "module": "ESNext", + "moduleResolution": "bundler", "noEmitOnError": true, "types": ["node"], "experimentalDecorators": true, diff --git a/packages/backend-nest/tsconfig.tsbuildinfo b/packages/backend-nest/tsconfig.tsbuildinfo new file mode 100644 index 0000000..a12a6e2 --- /dev/null +++ b/packages/backend-nest/tsconfig.tsbuildinfo @@ -0,0 +1 @@ +{"root":["./src/main.ts","./src/core/cluster/affinity.ts","./src/core/cluster/canary.ts","./src/core/cluster/sharding.ts","./src/core/cluster/stage4Canary.ts","./src/core/game/BotAI.ts","./src/core/game/GameLoop.ts","./src/core/game/GameState.ts","./src/core/game/physics.ts","./src/core/game/skills.ts","./src/core/observability/LoopMetrics.ts","./src/core/observability/MetricsRegistry.ts","./src/core/rooms/Room.ts","./src/core/rooms/RoomManager.ts","./src/core/state/RoomStateRepository.ts","./src/core/utils/logger.ts","./src/domain/room-policy.ts","./src/gateway/heist.gateway.ts","./src/modules/app.module.ts","./src/services/runtime-core.service.ts","./src/web/health.controller.ts","./src/web/ops.controller.ts"],"version":"5.9.3"} \ No newline at end of file diff --git a/packages/backend/package.json b/packages/backend/package.json deleted file mode 100644 index cf31c7a..0000000 --- a/packages/backend/package.json +++ /dev/null @@ -1,31 +0,0 @@ -{ - "name": "@heist/backend", - "version": "0.1.0", - "private": true, - "type": "module", - "scripts": { - "dev": "nodemon --watch src --ext ts --exec 'tsx src/index.ts'", - "build": "tsc", - "start": "node dist/index.js" - }, - "dependencies": { - "@socket.io/redis-adapter": "^8.3.0", - "@heist/shared": "*", - "@solana/web3.js": "^1.98.0", - "cors": "^2.8.5", - "dotenv": "^16.4.0", - "express": "^4.21.0", - "redis": "^4.7.0", - "socket.io": "^4.8.3", - "uuid": "^11.1.0" - }, - "devDependencies": { - "@types/cors": "^2.8.17", - "@types/express": "^5.0.0", - "@types/node": "^22.0.0", - "@types/uuid": "^10.0.0", - "nodemon": "^3.1.14", - "tsx": "^4.19.0", - "typescript": "^5.7.0" - } -} diff --git a/packages/backend/src/cluster/socketAdapter.ts b/packages/backend/src/cluster/socketAdapter.ts deleted file mode 100644 index 3297bde..0000000 --- a/packages/backend/src/cluster/socketAdapter.ts +++ /dev/null @@ -1,35 +0,0 @@ -import { createAdapter } from '@socket.io/redis-adapter'; -import { createClient } from 'redis'; -import type { Server } from 'socket.io'; -import type { - ClientToServerEvents, - ServerToClientEvents, - InterServerEvents, - SocketData, -} from '@heist/shared'; -import { log } from '../utils/logger.js'; - -type TypedIO = Server; - -export async function setupSocketAdapter(io: TypedIO): Promise { - const redisUrl = process.env.REDIS_URL; - if (!redisUrl) { - // 로컬/단일 노드 환경에서는 기본 메모리 adapter로 동작한다. - log('SocketAdapter', 'REDIS_URL not set. Running in single-node adapter mode.'); - return; - } - - // pub/sub 두 커넥션을 분리해 cross-node room 이벤트를 전달한다. - const pubClient = createClient({ url: redisUrl }); - const subClient = pubClient.duplicate(); - - try { - await Promise.all([pubClient.connect(), subClient.connect()]); - io.adapter(createAdapter(pubClient, subClient)); - log('SocketAdapter', 'Redis adapter enabled'); - } catch (err) { - // Redis 연결 실패 시 서버 자체는 계속 기동해 단일 노드 모드로 폴백한다. - log('SocketAdapter', `Failed to enable Redis adapter: ${err}`); - await Promise.allSettled([pubClient.quit(), subClient.quit()]); - } -} diff --git a/packages/backend/src/index.ts b/packages/backend/src/index.ts deleted file mode 100644 index e5eddf7..0000000 --- a/packages/backend/src/index.ts +++ /dev/null @@ -1,140 +0,0 @@ -import 'dotenv/config'; -import { createApp } from './server.js'; -import { RoomManager } from './rooms/RoomManager.js'; -import { log } from './utils/logger.js'; -import { setupSocketAdapter } from './cluster/socketAdapter.js'; -import { metricsRegistry } from './observability/MetricsRegistry.js'; - -const PORT = parseInt(process.env.PORT || '8080', 10); -const MIN_PLAYERS = parseInt(process.env.MIN_PLAYERS || '1', 10); -const CONSISTENCY_CHECK_INTERVAL_MS = parseInt(process.env.CONSISTENCY_CHECK_INTERVAL_MS || '30000', 10); - -const { app, httpServer, io } = createApp(); -const roomManager = new RoomManager(io, MIN_PLAYERS); - -async function main(): Promise { - // Redis 설정이 있으면 멀티 노드 adapter를 먼저 붙인 뒤 소켓 이벤트를 연다. - await setupSocketAdapter(io); - - // Stage4: 운영에서 메모리/외부 저장소 정합성을 즉시 점검할 수 있는 엔드포인트 - app.get('/_state/consistency', async (_req, res) => { - try { - const report = await roomManager.buildStateConsistencyReport(); - metricsRegistry.recordConsistencyResult(report.ok); - res.json(report); - } catch (err) { - res.status(500).json({ - ok: false, - error: `Consistency check failed: ${err}`, - }); - } - }); - - // Stage4: 장애 복구 드릴 결과(RTO/RPO)를 수동 기록해 확장 가능 여부 판단에 사용한다. - app.post('/_state/recovery-drill', (req, res) => { - const rtoSec = Number(req.body?.rtoSec); - const rpoEvents = Number(req.body?.rpoEvents); - if (!Number.isFinite(rtoSec) || !Number.isFinite(rpoEvents)) { - res.status(400).json({ - ok: false, - error: 'rtoSec and rpoEvents must be numbers', - }); - return; - } - metricsRegistry.recordRecoveryDrill(rtoSec, rpoEvents); - res.json({ - ok: true, - rtoSec, - rpoEvents, - }); - }); - - io.on('connection', (socket) => { - log('Server', `Client connected: ${socket.id}`); - - socket.on('list_rooms', (ack) => { - ack(roomManager.listRooms()); - }); - - socket.on('join_room', (roomId, payload, ack) => { - roomManager.handleJoinRoom(socket, roomId, payload, ack); - }); - - socket.on('confirm_entry', (txSignature, ack) => { - roomManager.handleConfirmEntry(socket, txSignature, ack); - }); - - socket.on('select_team', (team, ack) => { - const result = roomManager.handleSelectTeam(socket, team); - ack(result); - }); - - socket.on('ready', () => { - roomManager.handleReady(socket); - }); - - socket.on('input_move', (direction) => { - roomManager.handleInputMove(socket, direction); - }); - - socket.on('request_steal', (storageId) => { - roomManager.handleRequestSkill(socket, 'steal', storageId); - }); - - socket.on('request_break_jail', () => { - roomManager.handleRequestSkill(socket, 'break_jail'); - }); - - socket.on('request_arrest', (targetId) => { - roomManager.handleRequestSkill(socket, 'arrest', targetId); - }); - - socket.on('request_disguise', () => { - roomManager.handleRequestSkill(socket, 'disguise'); - }); - - socket.on('request_build_wall', () => { - roomManager.handleRequestSkill(socket, 'build_wall'); - }); - - socket.on('cancel_skill', () => { - roomManager.handleCancelSkill(socket); - }); - - socket.on('disconnect', () => { - log('Server', `Client disconnected: ${socket.id}`); - roomManager.handleDisconnect(socket); - }); - }); - - httpServer.listen(PORT, () => { - log('Server', `HEIST server running on port ${PORT} (min players: ${MIN_PLAYERS})`); - }); - - // 정합성 체크를 주기 수행해 자동 롤백 게이트 판단 근거를 지속 갱신한다. - const consistencyTimer = setInterval(async () => { - try { - const report = await roomManager.buildStateConsistencyReport(); - metricsRegistry.recordConsistencyResult(report.ok); - } catch (err) { - log('Consistency', `Periodic check failed: ${err}`); - metricsRegistry.recordConsistencyResult(false); - } - }, Math.max(1000, CONSISTENCY_CHECK_INTERVAL_MS)); - consistencyTimer.unref(); -} - -main().catch((err) => { - log('Server', `Fatal startup error: ${err}`); - process.exit(1); -}); - -// 개발/운영 공통 graceful shutdown: 진행 중 게임 정리 후 소켓/HTTP를 닫는다. -function shutdown() { - roomManager.abortAllGames('Server is restarting'); - io.close(); - httpServer.close(() => process.exit(0)); - setTimeout(() => process.exit(0), 500); -} -process.on('SIGTERM', shutdown); -process.on('SIGINT', shutdown); diff --git a/packages/backend/src/server.ts b/packages/backend/src/server.ts deleted file mode 100644 index 17d6e75..0000000 --- a/packages/backend/src/server.ts +++ /dev/null @@ -1,57 +0,0 @@ -import express from 'express'; -import { createServer } from 'http'; -import { Server } from 'socket.io'; -import cors from 'cors'; -import type { - ClientToServerEvents, - ServerToClientEvents, - InterServerEvents, - SocketData, -} from '@heist/shared'; -import { metricsRegistry } from './observability/MetricsRegistry.js'; -import { roomStateRepository } from './state/RoomStateRepository.js'; - -export function createApp() { - const app = express(); - app.use(cors()); - app.use(express.json()); - - const httpServer = createServer(app); - - const io = new Server< - ClientToServerEvents, - ServerToClientEvents, - InterServerEvents, - SocketData - >(httpServer, { - cors: { - origin: '*', - methods: ['GET', 'POST'], - }, - transports: ['websocket', 'polling'], - }); - - // L7 헬스체크: 과부하 시 degraded를 반환해 상위 시스템이 감지할 수 있게 한다. - app.get('/health', (_req, res) => { - res.json({ - status: metricsRegistry.isOverloaded() ? 'degraded' : 'ok', - }); - }); - - // 운영 수집기에서 폴링할 수 있도록 JSON 형태로 메트릭을 노출한다. - app.get('/_metrics', (_req, res) => { - res.json(metricsRegistry.getSnapshot()); - }); - - // Stage4: 외부 상태 저장소에 기록된 active room 메타상태를 조회한다. - app.get('/_state/rooms', async (_req, res) => { - const rooms = await roomStateRepository.list(); - res.json({ - backend: roomStateRepository.backend, - count: rooms.length, - rooms, - }); - }); - - return { app, httpServer, io }; -} diff --git a/packages/backend/src/solana/escrow.ts b/packages/backend/src/solana/escrow.ts deleted file mode 100644 index 07df87d..0000000 --- a/packages/backend/src/solana/escrow.ts +++ /dev/null @@ -1,32 +0,0 @@ -import { Connection, clusterApiUrl, PublicKey } from '@solana/web3.js'; -import { ENTRY_FEE_LAMPORTS } from '@heist/shared'; -import { log } from '../utils/logger.js'; - -const CLUSTER = (process.env.SOLANA_CLUSTER as 'devnet' | 'mainnet-beta') || 'devnet'; -const connection = new Connection(clusterApiUrl(CLUSTER), 'confirmed'); - -export async function verifyDeposit( - txSignature: string, - expectedPayer: string, - escrowPubkey: string, -): Promise { - try { - const result = await connection.confirmTransaction(txSignature, 'confirmed'); - if (result.value.err) { - log('Escrow', `Transaction failed: ${txSignature}`); - return false; - } - - const tx = await connection.getTransaction(txSignature, { commitment: 'confirmed' }); - if (!tx || !tx.meta) { - log('Escrow', `Transaction not found: ${txSignature}`); - return false; - } - - log('Escrow', `Deposit verified: ${txSignature}`); - return true; - } catch (err) { - log('Escrow', `Verification error: ${err}`); - return false; - } -} diff --git a/packages/backend/src/solana/payout.ts b/packages/backend/src/solana/payout.ts deleted file mode 100644 index 27e5ebd..0000000 --- a/packages/backend/src/solana/payout.ts +++ /dev/null @@ -1,105 +0,0 @@ -import { - Connection, - clusterApiUrl, - Keypair, - PublicKey, - Transaction, - SystemProgram, - sendAndConfirmTransaction, -} from '@solana/web3.js'; -import { log } from '../utils/logger.js'; - -const CLUSTER = (process.env.SOLANA_CLUSTER as 'devnet' | 'mainnet-beta') || 'devnet'; -const connection = new Connection(clusterApiUrl(CLUSTER), 'confirmed'); - -function getEscrowKeypair(): Keypair | null { - const secretKey = process.env.ESCROW_SECRET_KEY; - if (!secretKey) { - log('Payout', 'No ESCROW_SECRET_KEY set - payouts disabled'); - return null; - } - try { - const bytes = JSON.parse(secretKey) as number[]; - return Keypair.fromSecretKey(Uint8Array.from(bytes)); - } catch { - log('Payout', 'Invalid ESCROW_SECRET_KEY format'); - return null; - } -} - -export async function distributePayouts( - winners: { walletAddress: string }[], - amountPerWinner: number, -): Promise { - const escrow = getEscrowKeypair(); - if (!escrow) { - log('Payout', 'Skipping payouts (no escrow key)'); - return []; - } - - const signatures: string[] = []; - - for (const winner of winners) { - try { - const tx = new Transaction().add( - SystemProgram.transfer({ - fromPubkey: escrow.publicKey, - toPubkey: new PublicKey(winner.walletAddress), - lamports: amountPerWinner, - }), - ); - - const sig = await sendAndConfirmTransaction(connection, tx, [escrow]); - signatures.push(sig); - log('Payout', `Sent ${amountPerWinner} lamports to ${winner.walletAddress}: ${sig}`); - } catch (err) { - log('Payout', `Failed payout to ${winner.walletAddress}: ${err}`); - signatures.push('FAILED'); - } - } - - return signatures; -} - -/** Refund all players when game ends abnormally (disconnect, error, etc.) */ -export async function refundAllPlayers( - players: { walletAddress: string }[], - amountPerPlayer: number, -): Promise { - if (amountPerPlayer <= 0) { - log('Refund', 'No entry fee to refund'); - return []; - } - - const escrow = getEscrowKeypair(); - if (!escrow) { - log('Refund', 'Skipping refunds (no escrow key)'); - return []; - } - - const realPlayers = players.filter((p) => p.walletAddress !== 'bot'); - log('Refund', `Refunding ${realPlayers.length} players (${amountPerPlayer} lamports each)`); - - const signatures: string[] = []; - - for (const player of realPlayers) { - try { - const tx = new Transaction().add( - SystemProgram.transfer({ - fromPubkey: escrow.publicKey, - toPubkey: new PublicKey(player.walletAddress), - lamports: amountPerPlayer, - }), - ); - - const sig = await sendAndConfirmTransaction(connection, tx, [escrow]); - signatures.push(sig); - log('Refund', `Refunded ${amountPerPlayer} lamports to ${player.walletAddress}: ${sig}`); - } catch (err) { - log('Refund', `Failed refund to ${player.walletAddress}: ${err}`); - signatures.push('FAILED'); - } - } - - return signatures; -} diff --git a/packages/backend/tsconfig.json b/packages/backend/tsconfig.json deleted file mode 100644 index f71cc75..0000000 --- a/packages/backend/tsconfig.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "extends": "../../tsconfig.base.json", - "compilerOptions": { - "outDir": "./dist", - "rootDir": "./src", - "module": "ESNext", - "types": ["node"] - }, - "include": ["src"], - "references": [{ "path": "../shared" }] -} diff --git a/packages/frontend/package.json b/packages/frontend/package.json index e3b9ace..0eccf9b 100644 --- a/packages/frontend/package.json +++ b/packages/frontend/package.json @@ -5,7 +5,7 @@ "type": "module", "scripts": { "dev": "vite", - "build": "tsc && vite build", + "build": "vite build", "preview": "vite preview" }, "dependencies": { @@ -20,6 +20,7 @@ "react-router-dom": "^6.28.0", "socket.io-client": "^4.8.3", "styled-components": "^6.1.0", + "uuid": "^11.1.0", "zustand": "^5.0.8" }, "devDependencies": { diff --git a/packages/frontend/src/components/game/SkillBar.tsx b/packages/frontend/src/components/game/SkillBar.tsx index 8075108..db39e3c 100644 --- a/packages/frontend/src/components/game/SkillBar.tsx +++ b/packages/frontend/src/components/game/SkillBar.tsx @@ -104,7 +104,7 @@ export function SkillBar() { dist(me.position, snapshot.jail.position) <= BREAK_JAIL_RANGE + snapshot.jail.radius; const canBreakJail = nearJail && snapshot.jail.inmates.length > 0 && !me.channeling; const canDisguise = !me.channeling && !me.isDisguised; - const canBuildWall = !me.channeling && snapshot.stolenCoins >= WALL_COST_COINS; + const canBuildWall = !me.channeling && (snapshot.teamCoins ?? snapshot.stolenCoins) >= WALL_COST_COINS; return ( @@ -150,7 +150,7 @@ export function SkillBar() { } }} > - Wall [E] ({WALL_COST_COINS}c) + Wall [E] ({WALL_COST_COINS}c) - {Math.floor(snapshot.teamCoins ?? snapshot.stolenCoins)}c available {me.channeling && ( s.currentRoom); @@ -270,12 +270,12 @@ export function LobbyPage() { // Leave room when wallet disconnects const reset = useLobbyStore((s) => s.reset); useEffect(() => { - if (!connected && currentRoom) { + if (!publicKey && currentRoom) { getSocket().disconnect(); getSocket().connect(); reset(); } - }, [connected, currentRoom, reset]); + }, [publicKey, currentRoom, reset]); // Fetch room list periodically useEffect(() => { @@ -299,14 +299,17 @@ export function LobbyPage() { // Fetch balance useEffect(() => { - if (!publicKey) return; + if (!publicKey) { + setBalance(null); + return; + } connection.getBalance(publicKey).then((bal) => { setBalance(bal / LAMPORTS_PER_SOL); }); }, [publicKey, connection]); const handleJoin = useCallback(() => { - if (!connected || !publicKey) return; + if (!publicKey) return; const name = playerName.trim() || `Player-${publicKey.toBase58().slice(0, 4)}`; const socket = getSocket(); @@ -327,7 +330,7 @@ export function LobbyPage() { } }, ); - }, [connected, publicKey, playerName, roomId]); + }, [publicKey, playerName, roomId]); const handlePayEntry = useCallback(async () => { if (!publicKey || !sendTransaction) return; @@ -407,7 +410,7 @@ export function LobbyPage() { key={room.id} onClick={() => { setRoomId(room.id); - if (connected && publicKey) { + if (publicKey) { const name = playerName.trim() || `Player-${publicKey.toBase58().slice(0, 4)}`; getSocket().emit( 'join_room', @@ -447,8 +450,8 @@ export function LobbyPage() { value={roomId} onChange={(e) => setRoomId(e.target.value)} /> - diff --git a/packages/frontend/vite.config.ts b/packages/frontend/vite.config.ts index 4306d02..4323f64 100644 --- a/packages/frontend/vite.config.ts +++ b/packages/frontend/vite.config.ts @@ -15,7 +15,7 @@ export default defineConfig({ allowedHosts: ['yt4307.mooo.com'], proxy: { '/socket.io': { - target: 'http://localhost:8080', + target: 'http://localhost:8081', ws: true, }, }, diff --git a/packages/shared/package.json b/packages/shared/package.json index e437455..b9c2c69 100644 --- a/packages/shared/package.json +++ b/packages/shared/package.json @@ -3,8 +3,14 @@ "version": "0.1.0", "private": true, "type": "module", - "main": "./src/index.ts", - "types": "./src/index.ts", + "main": "./dist/index.js", + "types": "./dist/index.d.ts", + "exports": { + ".": { + "types": "./dist/index.d.ts", + "default": "./dist/index.js" + } + }, "scripts": { "build": "tsc", "typecheck": "tsc --noEmit" diff --git a/packages/shared/src/index.ts b/packages/shared/src/index.ts index 10bfef2..58d686b 100644 --- a/packages/shared/src/index.ts +++ b/packages/shared/src/index.ts @@ -1,4 +1,11 @@ -export * from './types'; -export * from './protocol'; -export * from './constants'; -export * from './map'; +export * from './types.js'; +export * from './protocol.js'; +export * from './constants.js'; +export { + STORAGE_POSITIONS, + createStorages, + COP_SPAWNS, + THIEF_SPAWNS, + JAIL_CONFIG, + OBSTACLES, +} from './map.js'; diff --git a/packages/shared/src/map.ts b/packages/shared/src/map.ts index 87aa5a4..e88fd86 100644 --- a/packages/shared/src/map.ts +++ b/packages/shared/src/map.ts @@ -1,5 +1,5 @@ -import type { Vec2, Storage, Jail, Obstacle } from './types'; -import { COINS_PER_STORAGE } from './constants'; +import type { Vec2, Storage, Jail, Obstacle } from './types.js'; +import { COINS_PER_STORAGE } from './constants.js'; export const STORAGE_POSITIONS: Vec2[] = [ { x: 480, y: 360 }, diff --git a/packages/shared/src/protocol.ts b/packages/shared/src/protocol.ts index 0af004f..21a0dc9 100644 --- a/packages/shared/src/protocol.ts +++ b/packages/shared/src/protocol.ts @@ -6,7 +6,7 @@ import type { RoomInfo, StateSnapshot, GameResult, -} from './types'; +} from './types.js'; export interface ClientToServerEvents { list_rooms: ( diff --git a/packages/shared/src/types.ts b/packages/shared/src/types.ts index d6ad4b7..815faee 100644 --- a/packages/shared/src/types.ts +++ b/packages/shared/src/types.ts @@ -81,6 +81,7 @@ export interface StateSnapshot { jail: Jail; obstacles: Obstacle[]; stolenCoins: number; + teamCoins: number; totalCoins: number; } diff --git a/packages/shared/tsconfig.tsbuildinfo b/packages/shared/tsconfig.tsbuildinfo index 0f71ff2..68f0a45 100644 --- a/packages/shared/tsconfig.tsbuildinfo +++ b/packages/shared/tsconfig.tsbuildinfo @@ -1 +1 @@ -{"fileNames":["../../node_modules/typescript/lib/lib.es5.d.ts","../../node_modules/typescript/lib/lib.es2015.d.ts","../../node_modules/typescript/lib/lib.es2016.d.ts","../../node_modules/typescript/lib/lib.es2017.d.ts","../../node_modules/typescript/lib/lib.es2018.d.ts","../../node_modules/typescript/lib/lib.es2019.d.ts","../../node_modules/typescript/lib/lib.es2020.d.ts","../../node_modules/typescript/lib/lib.es2021.d.ts","../../node_modules/typescript/lib/lib.es2022.d.ts","../../node_modules/typescript/lib/lib.dom.d.ts","../../node_modules/typescript/lib/lib.dom.iterable.d.ts","../../node_modules/typescript/lib/lib.dom.asynciterable.d.ts","../../node_modules/typescript/lib/lib.webworker.importscripts.d.ts","../../node_modules/typescript/lib/lib.scripthost.d.ts","../../node_modules/typescript/lib/lib.es2015.core.d.ts","../../node_modules/typescript/lib/lib.es2015.collection.d.ts","../../node_modules/typescript/lib/lib.es2015.generator.d.ts","../../node_modules/typescript/lib/lib.es2015.iterable.d.ts","../../node_modules/typescript/lib/lib.es2015.promise.d.ts","../../node_modules/typescript/lib/lib.es2015.proxy.d.ts","../../node_modules/typescript/lib/lib.es2015.reflect.d.ts","../../node_modules/typescript/lib/lib.es2015.symbol.d.ts","../../node_modules/typescript/lib/lib.es2015.symbol.wellknown.d.ts","../../node_modules/typescript/lib/lib.es2016.array.include.d.ts","../../node_modules/typescript/lib/lib.es2016.intl.d.ts","../../node_modules/typescript/lib/lib.es2017.arraybuffer.d.ts","../../node_modules/typescript/lib/lib.es2017.date.d.ts","../../node_modules/typescript/lib/lib.es2017.object.d.ts","../../node_modules/typescript/lib/lib.es2017.sharedmemory.d.ts","../../node_modules/typescript/lib/lib.es2017.string.d.ts","../../node_modules/typescript/lib/lib.es2017.intl.d.ts","../../node_modules/typescript/lib/lib.es2017.typedarrays.d.ts","../../node_modules/typescript/lib/lib.es2018.asyncgenerator.d.ts","../../node_modules/typescript/lib/lib.es2018.asynciterable.d.ts","../../node_modules/typescript/lib/lib.es2018.intl.d.ts","../../node_modules/typescript/lib/lib.es2018.promise.d.ts","../../node_modules/typescript/lib/lib.es2018.regexp.d.ts","../../node_modules/typescript/lib/lib.es2019.array.d.ts","../../node_modules/typescript/lib/lib.es2019.object.d.ts","../../node_modules/typescript/lib/lib.es2019.string.d.ts","../../node_modules/typescript/lib/lib.es2019.symbol.d.ts","../../node_modules/typescript/lib/lib.es2019.intl.d.ts","../../node_modules/typescript/lib/lib.es2020.bigint.d.ts","../../node_modules/typescript/lib/lib.es2020.date.d.ts","../../node_modules/typescript/lib/lib.es2020.promise.d.ts","../../node_modules/typescript/lib/lib.es2020.sharedmemory.d.ts","../../node_modules/typescript/lib/lib.es2020.string.d.ts","../../node_modules/typescript/lib/lib.es2020.symbol.wellknown.d.ts","../../node_modules/typescript/lib/lib.es2020.intl.d.ts","../../node_modules/typescript/lib/lib.es2020.number.d.ts","../../node_modules/typescript/lib/lib.es2021.promise.d.ts","../../node_modules/typescript/lib/lib.es2021.string.d.ts","../../node_modules/typescript/lib/lib.es2021.weakref.d.ts","../../node_modules/typescript/lib/lib.es2021.intl.d.ts","../../node_modules/typescript/lib/lib.es2022.array.d.ts","../../node_modules/typescript/lib/lib.es2022.error.d.ts","../../node_modules/typescript/lib/lib.es2022.intl.d.ts","../../node_modules/typescript/lib/lib.es2022.object.d.ts","../../node_modules/typescript/lib/lib.es2022.string.d.ts","../../node_modules/typescript/lib/lib.es2022.regexp.d.ts","../../node_modules/typescript/lib/lib.decorators.d.ts","../../node_modules/typescript/lib/lib.decorators.legacy.d.ts","../../node_modules/typescript/lib/lib.es2022.full.d.ts","./src/constants.ts","./src/types.ts","./src/protocol.ts","./src/map.ts","./src/index.ts","../../node_modules/@babel/types/lib/index.d.ts","../../node_modules/@types/babel__generator/index.d.ts","../../node_modules/@babel/parser/typings/babel-parser.d.ts","../../node_modules/@types/babel__template/index.d.ts","../../node_modules/@types/babel__traverse/index.d.ts","../../node_modules/@types/babel__core/index.d.ts","../../node_modules/@types/node/compatibility/disposable.d.ts","../../node_modules/@types/node/compatibility/indexable.d.ts","../../node_modules/@types/node/compatibility/iterators.d.ts","../../node_modules/@types/node/compatibility/index.d.ts","../../node_modules/@types/node/globals.typedarray.d.ts","../../node_modules/@types/node/buffer.buffer.d.ts","../../node_modules/@types/node/globals.d.ts","../../node_modules/@types/node/web-globals/abortcontroller.d.ts","../../node_modules/@types/node/web-globals/domexception.d.ts","../../node_modules/@types/node/web-globals/events.d.ts","../../node_modules/buffer/index.d.ts","../../node_modules/@types/node/node_modules/undici-types/header.d.ts","../../node_modules/@types/node/node_modules/undici-types/readable.d.ts","../../node_modules/@types/node/node_modules/undici-types/file.d.ts","../../node_modules/@types/node/node_modules/undici-types/fetch.d.ts","../../node_modules/@types/node/node_modules/undici-types/formdata.d.ts","../../node_modules/@types/node/node_modules/undici-types/connector.d.ts","../../node_modules/@types/node/node_modules/undici-types/client.d.ts","../../node_modules/@types/node/node_modules/undici-types/errors.d.ts","../../node_modules/@types/node/node_modules/undici-types/dispatcher.d.ts","../../node_modules/@types/node/node_modules/undici-types/global-dispatcher.d.ts","../../node_modules/@types/node/node_modules/undici-types/global-origin.d.ts","../../node_modules/@types/node/node_modules/undici-types/pool-stats.d.ts","../../node_modules/@types/node/node_modules/undici-types/pool.d.ts","../../node_modules/@types/node/node_modules/undici-types/handlers.d.ts","../../node_modules/@types/node/node_modules/undici-types/balanced-pool.d.ts","../../node_modules/@types/node/node_modules/undici-types/agent.d.ts","../../node_modules/@types/node/node_modules/undici-types/mock-interceptor.d.ts","../../node_modules/@types/node/node_modules/undici-types/mock-agent.d.ts","../../node_modules/@types/node/node_modules/undici-types/mock-client.d.ts","../../node_modules/@types/node/node_modules/undici-types/mock-pool.d.ts","../../node_modules/@types/node/node_modules/undici-types/mock-errors.d.ts","../../node_modules/@types/node/node_modules/undici-types/proxy-agent.d.ts","../../node_modules/@types/node/node_modules/undici-types/env-http-proxy-agent.d.ts","../../node_modules/@types/node/node_modules/undici-types/retry-handler.d.ts","../../node_modules/@types/node/node_modules/undici-types/retry-agent.d.ts","../../node_modules/@types/node/node_modules/undici-types/api.d.ts","../../node_modules/@types/node/node_modules/undici-types/interceptors.d.ts","../../node_modules/@types/node/node_modules/undici-types/util.d.ts","../../node_modules/@types/node/node_modules/undici-types/cookies.d.ts","../../node_modules/@types/node/node_modules/undici-types/patch.d.ts","../../node_modules/@types/node/node_modules/undici-types/websocket.d.ts","../../node_modules/@types/node/node_modules/undici-types/eventsource.d.ts","../../node_modules/@types/node/node_modules/undici-types/filereader.d.ts","../../node_modules/@types/node/node_modules/undici-types/diagnostics-channel.d.ts","../../node_modules/@types/node/node_modules/undici-types/content-type.d.ts","../../node_modules/@types/node/node_modules/undici-types/cache.d.ts","../../node_modules/@types/node/node_modules/undici-types/index.d.ts","../../node_modules/@types/node/web-globals/fetch.d.ts","../../node_modules/@types/node/web-globals/navigator.d.ts","../../node_modules/@types/node/web-globals/storage.d.ts","../../node_modules/@types/node/assert.d.ts","../../node_modules/@types/node/assert/strict.d.ts","../../node_modules/@types/node/async_hooks.d.ts","../../node_modules/@types/node/buffer.d.ts","../../node_modules/@types/node/child_process.d.ts","../../node_modules/@types/node/cluster.d.ts","../../node_modules/@types/node/console.d.ts","../../node_modules/@types/node/constants.d.ts","../../node_modules/@types/node/crypto.d.ts","../../node_modules/@types/node/dgram.d.ts","../../node_modules/@types/node/diagnostics_channel.d.ts","../../node_modules/@types/node/dns.d.ts","../../node_modules/@types/node/dns/promises.d.ts","../../node_modules/@types/node/domain.d.ts","../../node_modules/@types/node/events.d.ts","../../node_modules/@types/node/fs.d.ts","../../node_modules/@types/node/fs/promises.d.ts","../../node_modules/@types/node/http.d.ts","../../node_modules/@types/node/http2.d.ts","../../node_modules/@types/node/https.d.ts","../../node_modules/@types/node/inspector.d.ts","../../node_modules/@types/node/inspector.generated.d.ts","../../node_modules/@types/node/module.d.ts","../../node_modules/@types/node/net.d.ts","../../node_modules/@types/node/os.d.ts","../../node_modules/@types/node/path.d.ts","../../node_modules/@types/node/perf_hooks.d.ts","../../node_modules/@types/node/process.d.ts","../../node_modules/@types/node/punycode.d.ts","../../node_modules/@types/node/querystring.d.ts","../../node_modules/@types/node/readline.d.ts","../../node_modules/@types/node/readline/promises.d.ts","../../node_modules/@types/node/repl.d.ts","../../node_modules/@types/node/sea.d.ts","../../node_modules/@types/node/sqlite.d.ts","../../node_modules/@types/node/stream.d.ts","../../node_modules/@types/node/stream/promises.d.ts","../../node_modules/@types/node/stream/consumers.d.ts","../../node_modules/@types/node/stream/web.d.ts","../../node_modules/@types/node/string_decoder.d.ts","../../node_modules/@types/node/test.d.ts","../../node_modules/@types/node/timers.d.ts","../../node_modules/@types/node/timers/promises.d.ts","../../node_modules/@types/node/tls.d.ts","../../node_modules/@types/node/trace_events.d.ts","../../node_modules/@types/node/tty.d.ts","../../node_modules/@types/node/url.d.ts","../../node_modules/@types/node/util.d.ts","../../node_modules/@types/node/v8.d.ts","../../node_modules/@types/node/vm.d.ts","../../node_modules/@types/node/wasi.d.ts","../../node_modules/@types/node/worker_threads.d.ts","../../node_modules/@types/node/zlib.d.ts","../../node_modules/@types/node/index.d.ts","../../node_modules/@types/connect/index.d.ts","../../node_modules/@types/body-parser/index.d.ts","../../node_modules/@types/cors/index.d.ts","../../node_modules/@types/estree/index.d.ts","../../node_modules/@types/send/index.d.ts","../../node_modules/@types/qs/index.d.ts","../../node_modules/@types/range-parser/index.d.ts","../../node_modules/@types/express-serve-static-core/index.d.ts","../../node_modules/@types/http-errors/index.d.ts","../../node_modules/@types/serve-static/index.d.ts","../../node_modules/@types/express/index.d.ts","../../node_modules/@types/graceful-fs/index.d.ts","../../node_modules/@types/istanbul-lib-coverage/index.d.ts","../../node_modules/@types/istanbul-lib-report/index.d.ts","../../node_modules/@types/istanbul-reports/index.d.ts","../../node_modules/@types/prop-types/index.d.ts","../../node_modules/@types/react/global.d.ts","../../node_modules/csstype/index.d.ts","../../node_modules/@types/react/index.d.ts","../../node_modules/@types/react-dom/index.d.ts","../../node_modules/@types/stack-utils/index.d.ts","../../node_modules/@types/stylis/index.d.ts","../../node_modules/@types/trusted-types/lib/index.d.ts","../../node_modules/@types/trusted-types/index.d.ts","../../node_modules/@types/uuid/index.d.ts","../../node_modules/@types/w3c-web-usb/index.d.ts","../../node_modules/@types/web/iterable.d.ts","../../node_modules/@types/web/index.d.ts","../../node_modules/@types/ws/index.d.ts","../../node_modules/@types/yargs-parser/index.d.ts","../../node_modules/@types/yargs/index.d.ts","../../../../node_modules/@types/json-schema/index.d.ts"],"fileIdsList":[[69,80,129,146,147],[80,129,146,147],[69,70,71,72,73,80,129,146,147],[69,71,80,129,146,147],[80,129,143,146,147,179,180],[80,129,143,146,147,179],[80,129,140,143,146,147,179,184,185,186],[80,129,146,147,181,187,189],[80,129,141,146,147,179],[80,129,146,147,192],[80,129,146,147,193],[80,126,127,129,146,147],[80,128,129,146,147],[129,146,147],[80,129,134,146,147,164],[80,129,130,135,140,146,147,149,161,172],[80,129,130,131,140,146,147,149],[75,76,77,80,129,146,147],[80,129,132,146,147,173],[80,129,133,134,141,146,147,150],[80,129,134,146,147,161,169],[80,129,135,137,140,146,147,149],[80,128,129,136,146,147],[80,129,137,138,146,147],[80,129,139,140,146,147],[80,128,129,140,146,147],[80,129,140,141,142,146,147,161,172],[80,129,140,141,142,146,147,156,161,164],[80,122,129,137,140,143,146,147,149,161,172],[80,129,140,141,143,144,146,147,149,161,169,172],[80,129,143,145,146,147,161,169,172],[78,79,80,81,82,83,84,123,124,125,126,127,128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,145,146,147,148,149,150,151,152,153,154,155,156,157,158,159,160,161,162,163,164,165,166,167,168,169,170,171,172,173,174,175,176,177,178],[80,129,140,146,147],[80,129,146,147,148,172],[80,129,137,140,146,147,149,161],[80,94,98,129,146,147,172],[80,94,129,146,147,161,172],[80,89,129,146,147],[80,91,94,129,146,147,169,172],[80,129,146,147,149,169],[80,129,146,147,179],[80,89,129,146,147,179],[80,91,94,129,146,147,149,172],[80,86,87,90,93,129,140,146,147,161,172],[80,94,101,129,146,147],[80,86,92,129,146,147],[80,94,115,116,129,146,147],[80,90,94,129,146,147,164,172,179],[80,115,129,146,147,179],[80,88,89,129,146,147,179],[80,94,129,146,147],[80,88,89,90,91,92,93,94,95,96,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,116,117,118,119,120,121,129,146,147],[80,94,109,129,146,147],[80,94,101,102,129,146,147],[80,92,94,102,103,129,146,147],[80,93,129,146,147],[80,86,89,94,129,146,147],[80,94,98,102,103,129,146,147],[80,98,129,146,147],[80,92,94,97,129,146,147,172],[80,86,91,94,101,129,146,147],[80,129,146,147,161],[80,89,94,115,129,146,147,177,179],[80,129,146,147,150],[80,129,146,147,151],[80,128,129,146,147,152],[80,126,127,128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,145,146,147,148,149,150,151,152,153,154,155,156,157,158,159,160,161,162,163,164,165,166,167,168,169,170,171,172,173,174,175,176,177,178],[80,129,146,147,154],[80,129,146,147,155],[80,129,140,146,147,156,157],[80,129,146,147,156,158,173,175],[80,129,141,146,147],[80,129,140,146,147,161,162,164],[80,129,146,147,163,164],[80,129,146,147,161,162],[80,129,146,147,164],[80,129,146,147,165],[80,126,129,146,147,161,166,172],[80,129,140,146,147,167,168],[80,129,146,147,167,168],[80,129,134,146,147,149,161,169],[80,129,146,147,170],[80,129,146,147,149,171],[80,129,143,146,147,155,172],[80,129,134,146,147,173],[80,129,146,147,161,174],[80,129,146,147,148,175],[80,129,146,147,176],[80,122,129,146,147],[80,122,129,140,142,146,147,152,161,164,172,174,175,177],[80,129,146,147,161,178],[80,129,146,147,198],[80,129,146,147,195,196,197],[80,129,141,146,147,161,179],[80,129,143,146,147,179,188],[80,129,146,147,202],[80,129,146,147,206],[80,129,140,143,145,146,147,149,161,169,172,178,179],[80,129,146,147,209],[64,65,66,67,80,129,146,147],[64,65,80,129,146,147],[65,80,129,146,147]],"fileInfos":[{"version":"c430d44666289dae81f30fa7b2edebf186ecc91a2d4c71266ea6ae76388792e1","affectsGlobalScope":true,"impliedFormat":1},{"version":"45b7ab580deca34ae9729e97c13cfd999df04416a79116c3bfb483804f85ded4","impliedFormat":1},{"version":"3facaf05f0c5fc569c5649dd359892c98a85557e3e0c847964caeb67076f4d75","impliedFormat":1},{"version":"e44bb8bbac7f10ecc786703fe0a6a4b952189f908707980ba8f3c8975a760962","impliedFormat":1},{"version":"5e1c4c362065a6b95ff952c0eab010f04dcd2c3494e813b493ecfd4fcb9fc0d8","impliedFormat":1},{"version":"68d73b4a11549f9c0b7d352d10e91e5dca8faa3322bfb77b661839c42b1ddec7","impliedFormat":1},{"version":"5efce4fc3c29ea84e8928f97adec086e3dc876365e0982cc8479a07954a3efd4","impliedFormat":1},{"version":"feecb1be483ed332fad555aff858affd90a48ab19ba7272ee084704eb7167569","impliedFormat":1},{"version":"ee7bad0c15b58988daa84371e0b89d313b762ab83cb5b31b8a2d1162e8eb41c2","impliedFormat":1},{"version":"080941d9f9ff9307f7e27a83bcd888b7c8270716c39af943532438932ec1d0b9","affectsGlobalScope":true,"impliedFormat":1},{"version":"2e80ee7a49e8ac312cc11b77f1475804bee36b3b2bc896bead8b6e1266befb43","affectsGlobalScope":true,"impliedFormat":1},{"version":"d7a3c8b952931daebdfc7a2897c53c0a1c73624593fa070e46bd537e64dcd20a","affectsGlobalScope":true,"impliedFormat":1},{"version":"80e18897e5884b6723488d4f5652167e7bb5024f946743134ecc4aa4ee731f89","affectsGlobalScope":true,"impliedFormat":1},{"version":"cd034f499c6cdca722b60c04b5b1b78e058487a7085a8e0d6fb50809947ee573","affectsGlobalScope":true,"impliedFormat":1},{"version":"c57796738e7f83dbc4b8e65132f11a377649c00dd3eee333f672b8f0a6bea671","affectsGlobalScope":true,"impliedFormat":1},{"version":"dc2df20b1bcdc8c2d34af4926e2c3ab15ffe1160a63e58b7e09833f616efff44","affectsGlobalScope":true,"impliedFormat":1},{"version":"515d0b7b9bea2e31ea4ec968e9edd2c39d3eebf4a2d5cbd04e88639819ae3b71","affectsGlobalScope":true,"impliedFormat":1},{"version":"0559b1f683ac7505ae451f9a96ce4c3c92bdc71411651ca6ddb0e88baaaad6a3","affectsGlobalScope":true,"impliedFormat":1},{"version":"0dc1e7ceda9b8b9b455c3a2d67b0412feab00bd2f66656cd8850e8831b08b537","affectsGlobalScope":true,"impliedFormat":1},{"version":"ce691fb9e5c64efb9547083e4a34091bcbe5bdb41027e310ebba8f7d96a98671","affectsGlobalScope":true,"impliedFormat":1},{"version":"8d697a2a929a5fcb38b7a65594020fcef05ec1630804a33748829c5ff53640d0","affectsGlobalScope":true,"impliedFormat":1},{"version":"4ff2a353abf8a80ee399af572debb8faab2d33ad38c4b4474cff7f26e7653b8d","affectsGlobalScope":true,"impliedFormat":1},{"version":"fb0f136d372979348d59b3f5020b4cdb81b5504192b1cacff5d1fbba29378aa1","affectsGlobalScope":true,"impliedFormat":1},{"version":"d15bea3d62cbbdb9797079416b8ac375ae99162a7fba5de2c6c505446486ac0a","affectsGlobalScope":true,"impliedFormat":1},{"version":"68d18b664c9d32a7336a70235958b8997ebc1c3b8505f4f1ae2b7e7753b87618","affectsGlobalScope":true,"impliedFormat":1},{"version":"eb3d66c8327153d8fa7dd03f9c58d351107fe824c79e9b56b462935176cdf12a","affectsGlobalScope":true,"impliedFormat":1},{"version":"38f0219c9e23c915ef9790ab1d680440d95419ad264816fa15009a8851e79119","affectsGlobalScope":true,"impliedFormat":1},{"version":"69ab18c3b76cd9b1be3d188eaf8bba06112ebbe2f47f6c322b5105a6fbc45a2e","affectsGlobalScope":true,"impliedFormat":1},{"version":"a680117f487a4d2f30ea46f1b4b7f58bef1480456e18ba53ee85c2746eeca012","affectsGlobalScope":true,"impliedFormat":1},{"version":"2f11ff796926e0832f9ae148008138ad583bd181899ab7dd768a2666700b1893","affectsGlobalScope":true,"impliedFormat":1},{"version":"4de680d5bb41c17f7f68e0419412ca23c98d5749dcaaea1896172f06435891fc","affectsGlobalScope":true,"impliedFormat":1},{"version":"954296b30da6d508a104a3a0b5d96b76495c709785c1d11610908e63481ee667","affectsGlobalScope":true,"impliedFormat":1},{"version":"ac9538681b19688c8eae65811b329d3744af679e0bdfa5d842d0e32524c73e1c","affectsGlobalScope":true,"impliedFormat":1},{"version":"0a969edff4bd52585473d24995c5ef223f6652d6ef46193309b3921d65dd4376","affectsGlobalScope":true,"impliedFormat":1},{"version":"9e9fbd7030c440b33d021da145d3232984c8bb7916f277e8ffd3dc2e3eae2bdb","affectsGlobalScope":true,"impliedFormat":1},{"version":"811ec78f7fefcabbda4bfa93b3eb67d9ae166ef95f9bff989d964061cbf81a0c","affectsGlobalScope":true,"impliedFormat":1},{"version":"717937616a17072082152a2ef351cb51f98802fb4b2fdabd32399843875974ca","affectsGlobalScope":true,"impliedFormat":1},{"version":"d7e7d9b7b50e5f22c915b525acc5a49a7a6584cf8f62d0569e557c5cfc4b2ac2","affectsGlobalScope":true,"impliedFormat":1},{"version":"71c37f4c9543f31dfced6c7840e068c5a5aacb7b89111a4364b1d5276b852557","affectsGlobalScope":true,"impliedFormat":1},{"version":"576711e016cf4f1804676043e6a0a5414252560eb57de9faceee34d79798c850","affectsGlobalScope":true,"impliedFormat":1},{"version":"89c1b1281ba7b8a96efc676b11b264de7a8374c5ea1e6617f11880a13fc56dc6","affectsGlobalScope":true,"impliedFormat":1},{"version":"74f7fa2d027d5b33eb0471c8e82a6c87216223181ec31247c357a3e8e2fddc5b","affectsGlobalScope":true,"impliedFormat":1},{"version":"d6d7ae4d1f1f3772e2a3cde568ed08991a8ae34a080ff1151af28b7f798e22ca","affectsGlobalScope":true,"impliedFormat":1},{"version":"063600664504610fe3e99b717a1223f8b1900087fab0b4cad1496a114744f8df","affectsGlobalScope":true,"impliedFormat":1},{"version":"934019d7e3c81950f9a8426d093458b65d5aff2c7c1511233c0fd5b941e608ab","affectsGlobalScope":true,"impliedFormat":1},{"version":"52ada8e0b6e0482b728070b7639ee42e83a9b1c22d205992756fe020fd9f4a47","affectsGlobalScope":true,"impliedFormat":1},{"version":"3bdefe1bfd4d6dee0e26f928f93ccc128f1b64d5d501ff4a8cf3c6371200e5e6","affectsGlobalScope":true,"impliedFormat":1},{"version":"59fb2c069260b4ba00b5643b907ef5d5341b167e7d1dbf58dfd895658bda2867","affectsGlobalScope":true,"impliedFormat":1},{"version":"639e512c0dfc3fad96a84caad71b8834d66329a1f28dc95e3946c9b58176c73a","affectsGlobalScope":true,"impliedFormat":1},{"version":"368af93f74c9c932edd84c58883e736c9e3d53cec1fe24c0b0ff451f529ceab1","affectsGlobalScope":true,"impliedFormat":1},{"version":"af3dd424cf267428f30ccfc376f47a2c0114546b55c44d8c0f1d57d841e28d74","affectsGlobalScope":true,"impliedFormat":1},{"version":"995c005ab91a498455ea8dfb63aa9f83fa2ea793c3d8aa344be4a1678d06d399","affectsGlobalScope":true,"impliedFormat":1},{"version":"959d36cddf5e7d572a65045b876f2956c973a586da58e5d26cde519184fd9b8a","affectsGlobalScope":true,"impliedFormat":1},{"version":"965f36eae237dd74e6cca203a43e9ca801ce38824ead814728a2807b1910117d","affectsGlobalScope":true,"impliedFormat":1},{"version":"3925a6c820dcb1a06506c90b1577db1fdbf7705d65b62b99dce4be75c637e26b","affectsGlobalScope":true,"impliedFormat":1},{"version":"0a3d63ef2b853447ec4f749d3f368ce642264246e02911fcb1590d8c161b8005","affectsGlobalScope":true,"impliedFormat":1},{"version":"8cdf8847677ac7d20486e54dd3fcf09eda95812ac8ace44b4418da1bbbab6eb8","affectsGlobalScope":true,"impliedFormat":1},{"version":"8444af78980e3b20b49324f4a16ba35024fef3ee069a0eb67616ea6ca821c47a","affectsGlobalScope":true,"impliedFormat":1},{"version":"3287d9d085fbd618c3971944b65b4be57859f5415f495b33a6adc994edd2f004","affectsGlobalScope":true,"impliedFormat":1},{"version":"b4b67b1a91182421f5df999988c690f14d813b9850b40acd06ed44691f6727ad","affectsGlobalScope":true,"impliedFormat":1},{"version":"8e7f8264d0fb4c5339605a15daadb037bf238c10b654bb3eee14208f860a32ea","affectsGlobalScope":true,"impliedFormat":1},{"version":"782dec38049b92d4e85c1585fbea5474a219c6984a35b004963b00beb1aab538","affectsGlobalScope":true,"impliedFormat":1},{"version":"3cbad9a1ba4453443026ed38e4b8be018abb26565fa7c944376463ad9df07c41","impliedFormat":1},{"version":"c7648ebacbb1a6ab408775c6067896689363f1d1ba36b848fb622a022aa11c7d","signature":"8938a213ce185558d549b18678ef1ddb6ac15a3f27e3fd777f4ec91c4426d422"},{"version":"965e6d0e8c4e52a2cb407e2829bfef92186ddfd61090d0396eda05c054669134","signature":"2cbf140ba10d7862799509eca48f43a38718efb5cde26b4812648bb822ee1f7a"},{"version":"2e0970ca1779618e8a57b387c68e5c58ae3004b201570dbfbc07dd570b3ebd44","signature":"2e7777b026d2e9422f3c838e3fd2ef05b79b7c615cd19b1f85ded471da1754ed"},{"version":"261eb27f2fe1a8afd862d67bc2ba2ed69d6a975c5284342df2deb9014236eea3","signature":"1737bd0bf4e3ec4a3d6276800d4d22829e89af80a56385fae06f91522a4d58f5"},"08adc911de091930be3a13b79b1053c2745637c083fcb7762f59c429fa3f8b0a",{"version":"556ccd493ec36c7d7cb130d51be66e147b91cc1415be383d71da0f1e49f742a9","impliedFormat":1},{"version":"b6d03c9cfe2cf0ba4c673c209fcd7c46c815b2619fd2aad59fc4229aaef2ed43","impliedFormat":1},{"version":"95aba78013d782537cc5e23868e736bec5d377b918990e28ed56110e3ae8b958","impliedFormat":1},{"version":"670a76db379b27c8ff42f1ba927828a22862e2ab0b0908e38b671f0e912cc5ed","impliedFormat":1},{"version":"13b77ab19ef7aadd86a1e54f2f08ea23a6d74e102909e3c00d31f231ed040f62","impliedFormat":1},{"version":"069bebfee29864e3955378107e243508b163e77ab10de6a5ee03ae06939f0bb9","impliedFormat":1},{"version":"6c7176368037af28cb72f2392010fa1cef295d6d6744bca8cfb54985f3a18c3e","affectsGlobalScope":true,"impliedFormat":1},{"version":"ab41ef1f2cdafb8df48be20cd969d875602483859dc194e9c97c8a576892c052","affectsGlobalScope":true,"impliedFormat":1},{"version":"437e20f2ba32abaeb7985e0afe0002de1917bc74e949ba585e49feba65da6ca1","affectsGlobalScope":true,"impliedFormat":1},{"version":"21d819c173c0cf7cc3ce57c3276e77fd9a8a01d35a06ad87158781515c9a438a","impliedFormat":1},{"version":"98cffbf06d6bab333473c70a893770dbe990783904002c4f1a960447b4b53dca","affectsGlobalScope":true,"impliedFormat":1},{"version":"3af97acf03cc97de58a3a4bc91f8f616408099bc4233f6d0852e72a8ffb91ac9","affectsGlobalScope":true,"impliedFormat":1},{"version":"808069bba06b6768b62fd22429b53362e7af342da4a236ed2d2e1c89fcca3b4a","affectsGlobalScope":true,"impliedFormat":1},{"version":"1db0b7dca579049ca4193d034d835f6bfe73096c73663e5ef9a0b5779939f3d0","affectsGlobalScope":true,"impliedFormat":1},{"version":"9798340ffb0d067d69b1ae5b32faa17ab31b82466a3fc00d8f2f2df0c8554aaa","affectsGlobalScope":true,"impliedFormat":1},{"version":"f26b11d8d8e4b8028f1c7d618b22274c892e4b0ef5b3678a8ccbad85419aef43","affectsGlobalScope":true,"impliedFormat":1},{"version":"4967529644e391115ca5592184d4b63980569adf60ee685f968fd59ab1557188","impliedFormat":1},{"version":"5929864ce17fba74232584d90cb721a89b7ad277220627cc97054ba15a98ea8f","impliedFormat":1},{"version":"763fe0f42b3d79b440a9b6e51e9ba3f3f91352469c1e4b3b67bfa4ff6352f3f4","impliedFormat":1},{"version":"25c8056edf4314820382a5fdb4bb7816999acdcb929c8f75e3f39473b87e85bc","impliedFormat":1},{"version":"c464d66b20788266e5353b48dc4aa6bc0dc4a707276df1e7152ab0c9ae21fad8","impliedFormat":1},{"version":"78d0d27c130d35c60b5e5566c9f1e5be77caf39804636bc1a40133919a949f21","impliedFormat":1},{"version":"c6fd2c5a395f2432786c9cb8deb870b9b0e8ff7e22c029954fabdd692bff6195","impliedFormat":1},{"version":"1d6e127068ea8e104a912e42fc0a110e2aa5a66a356a917a163e8cf9a65e4a75","impliedFormat":1},{"version":"5ded6427296cdf3b9542de4471d2aa8d3983671d4cac0f4bf9c637208d1ced43","impliedFormat":1},{"version":"7f182617db458e98fc18dfb272d40aa2fff3a353c44a89b2c0ccb3937709bfb5","impliedFormat":1},{"version":"cadc8aced301244057c4e7e73fbcae534b0f5b12a37b150d80e5a45aa4bebcbd","impliedFormat":1},{"version":"385aab901643aa54e1c36f5ef3107913b10d1b5bb8cbcd933d4263b80a0d7f20","impliedFormat":1},{"version":"9670d44354bab9d9982eca21945686b5c24a3f893db73c0dae0fd74217a4c219","impliedFormat":1},{"version":"0b8a9268adaf4da35e7fa830c8981cfa22adbbe5b3f6f5ab91f6658899e657a7","impliedFormat":1},{"version":"11396ed8a44c02ab9798b7dca436009f866e8dae3c9c25e8c1fbc396880bf1bb","impliedFormat":1},{"version":"ba7bc87d01492633cb5a0e5da8a4a42a1c86270e7b3d2dea5d156828a84e4882","impliedFormat":1},{"version":"4893a895ea92c85345017a04ed427cbd6a1710453338df26881a6019432febdd","impliedFormat":1},{"version":"c21dc52e277bcfc75fac0436ccb75c204f9e1b3fa5e12729670910639f27343e","impliedFormat":1},{"version":"13f6f39e12b1518c6650bbb220c8985999020fe0f21d818e28f512b7771d00f9","impliedFormat":1},{"version":"9b5369969f6e7175740bf51223112ff209f94ba43ecd3bb09eefff9fd675624a","impliedFormat":1},{"version":"4fe9e626e7164748e8769bbf74b538e09607f07ed17c2f20af8d680ee49fc1da","impliedFormat":1},{"version":"24515859bc0b836719105bb6cc3d68255042a9f02a6022b3187948b204946bd2","impliedFormat":1},{"version":"ea0148f897b45a76544ae179784c95af1bd6721b8610af9ffa467a518a086a43","impliedFormat":1},{"version":"24c6a117721e606c9984335f71711877293a9651e44f59f3d21c1ea0856f9cc9","impliedFormat":1},{"version":"dd3273ead9fbde62a72949c97dbec2247ea08e0c6952e701a483d74ef92d6a17","impliedFormat":1},{"version":"405822be75ad3e4d162e07439bac80c6bcc6dbae1929e179cf467ec0b9ee4e2e","impliedFormat":1},{"version":"0db18c6e78ea846316c012478888f33c11ffadab9efd1cc8bcc12daded7a60b6","impliedFormat":1},{"version":"e61be3f894b41b7baa1fbd6a66893f2579bfad01d208b4ff61daef21493ef0a8","impliedFormat":1},{"version":"bd0532fd6556073727d28da0edfd1736417a3f9f394877b6d5ef6ad88fba1d1a","impliedFormat":1},{"version":"89167d696a849fce5ca508032aabfe901c0868f833a8625d5a9c6e861ef935d2","impliedFormat":1},{"version":"615ba88d0128ed16bf83ef8ccbb6aff05c3ee2db1cc0f89ab50a4939bfc1943f","impliedFormat":1},{"version":"a4d551dbf8746780194d550c88f26cf937caf8d56f102969a110cfaed4b06656","impliedFormat":1},{"version":"8bd86b8e8f6a6aa6c49b71e14c4ffe1211a0e97c80f08d2c8cc98838006e4b88","impliedFormat":1},{"version":"317e63deeb21ac07f3992f5b50cdca8338f10acd4fbb7257ebf56735bf52ab00","impliedFormat":1},{"version":"4732aec92b20fb28c5fe9ad99521fb59974289ed1e45aecb282616202184064f","impliedFormat":1},{"version":"2e85db9e6fd73cfa3d7f28e0ab6b55417ea18931423bd47b409a96e4a169e8e6","impliedFormat":1},{"version":"c46e079fe54c76f95c67fb89081b3e399da2c7d109e7dca8e4b58d83e332e605","impliedFormat":1},{"version":"bf67d53d168abc1298888693338cb82854bdb2e69ef83f8a0092093c2d562107","impliedFormat":1},{"version":"b52476feb4a0cbcb25e5931b930fc73cb6643fb1a5060bf8a3dda0eeae5b4b68","affectsGlobalScope":true,"impliedFormat":1},{"version":"f9501cc13ce624c72b61f12b3963e84fad210fbdf0ffbc4590e08460a3f04eba","affectsGlobalScope":true,"impliedFormat":1},{"version":"e7721c4f69f93c91360c26a0a84ee885997d748237ef78ef665b153e622b36c1","affectsGlobalScope":true,"impliedFormat":1},{"version":"0fa06ada475b910e2106c98c68b10483dc8811d0c14a8a8dd36efb2672485b29","impliedFormat":1},{"version":"33e5e9aba62c3193d10d1d33ae1fa75c46a1171cf76fef750777377d53b0303f","impliedFormat":1},{"version":"2b06b93fd01bcd49d1a6bd1f9b65ddcae6480b9a86e9061634d6f8e354c1468f","impliedFormat":1},{"version":"6a0cd27e5dc2cfbe039e731cf879d12b0e2dded06d1b1dedad07f7712de0d7f4","affectsGlobalScope":true,"impliedFormat":1},{"version":"13f5c844119c43e51ce777c509267f14d6aaf31eafb2c2b002ca35584cd13b29","impliedFormat":1},{"version":"e60477649d6ad21542bd2dc7e3d9ff6853d0797ba9f689ba2f6653818999c264","impliedFormat":1},{"version":"c2510f124c0293ab80b1777c44d80f812b75612f297b9857406468c0f4dafe29","affectsGlobalScope":true,"impliedFormat":1},{"version":"5524481e56c48ff486f42926778c0a3cce1cc85dc46683b92b1271865bcf015a","impliedFormat":1},{"version":"4c829ab315f57c5442c6667b53769975acbf92003a66aef19bce151987675bd1","affectsGlobalScope":true,"impliedFormat":1},{"version":"b2ade7657e2db96d18315694789eff2ddd3d8aea7215b181f8a0b303277cc579","impliedFormat":1},{"version":"9855e02d837744303391e5623a531734443a5f8e6e8755e018c41d63ad797db2","impliedFormat":1},{"version":"4d631b81fa2f07a0e63a9a143d6a82c25c5f051298651a9b69176ba28930756d","impliedFormat":1},{"version":"836a356aae992ff3c28a0212e3eabcb76dd4b0cc06bcb9607aeef560661b860d","impliedFormat":1},{"version":"1e0d1f8b0adfa0b0330e028c7941b5a98c08b600efe7f14d2d2a00854fb2f393","impliedFormat":1},{"version":"41670ee38943d9cbb4924e436f56fc19ee94232bc96108562de1a734af20dc2c","affectsGlobalScope":true,"impliedFormat":1},{"version":"c906fb15bd2aabc9ed1e3f44eb6a8661199d6c320b3aa196b826121552cb3695","impliedFormat":1},{"version":"22295e8103f1d6d8ea4b5d6211e43421fe4564e34d0dd8e09e520e452d89e659","impliedFormat":1},{"version":"f949f7f6c7802a338039cfc2156d1fe285cdd1e092c64437ebe15ae8edc854e0","impliedFormat":1},{"version":"6b4e081d55ac24fc8a4631d5dd77fe249fa25900abd7d046abb87d90e3b45645","impliedFormat":1},{"version":"a10f0e1854f3316d7ee437b79649e5a6ae3ae14ffe6322b02d4987071a95362e","impliedFormat":1},{"version":"e208f73ef6a980104304b0d2ca5f6bf1b85de6009d2c7e404028b875020fa8f2","impliedFormat":1},{"version":"d163b6bc2372b4f07260747cbc6c0a6405ab3fbcea3852305e98ac43ca59f5bc","impliedFormat":1},{"version":"e6fa9ad47c5f71ff733744a029d1dc472c618de53804eae08ffc243b936f87ff","affectsGlobalScope":true,"impliedFormat":1},{"version":"83e63d6ccf8ec004a3bb6d58b9bb0104f60e002754b1e968024b320730cc5311","impliedFormat":1},{"version":"24826ed94a78d5c64bd857570fdbd96229ad41b5cb654c08d75a9845e3ab7dde","impliedFormat":1},{"version":"8b479a130ccb62e98f11f136d3ac80f2984fdc07616516d29881f3061f2dd472","impliedFormat":1},{"version":"928af3d90454bf656a52a48679f199f64c1435247d6189d1caf4c68f2eaf921f","affectsGlobalScope":true,"impliedFormat":1},{"version":"d2bc7425ef40526650d6db7e072c1ff4a51101c3ac2cc4b666623b19496a6e27","affectsGlobalScope":true,"impliedFormat":1},{"version":"3f16a7e4deafa527ed9995a772bb380eb7d3c2c0fd4ae178c5263ed18394db2c","impliedFormat":1},{"version":"933921f0bb0ec12ef45d1062a1fc0f27635318f4d294e4d99de9a5493e618ca2","impliedFormat":1},{"version":"71a0f3ad612c123b57239a7749770017ecfe6b66411488000aba83e4546fde25","impliedFormat":1},{"version":"77fbe5eecb6fac4b6242bbf6eebfc43e98ce5ccba8fa44e0ef6a95c945ff4d98","impliedFormat":1},{"version":"4f9d8ca0c417b67b69eeb54c7ca1bedd7b56034bb9bfd27c5d4f3bc4692daca7","impliedFormat":1},{"version":"814118df420c4e38fe5ae1b9a3bafb6e9c2aa40838e528cde908381867be6466","impliedFormat":1},{"version":"a3fc63c0d7b031693f665f5494412ba4b551fe644ededccc0ab5922401079c95","impliedFormat":1},{"version":"f27524f4bef4b6519c604bdb23bf4465bddcccbf3f003abb901acbd0d7404d99","impliedFormat":1},{"version":"37ba7b45141a45ce6e80e66f2a96c8a5ab1bcef0fc2d0f56bb58df96ec67e972","impliedFormat":1},{"version":"45650f47bfb376c8a8ed39d4bcda5902ab899a3150029684ee4c10676d9fbaee","impliedFormat":1},{"version":"6b039f55681caaf111d5eb84d292b9bee9e0131d0db1ad0871eef0964f533c73","affectsGlobalScope":true,"impliedFormat":1},{"version":"18fd40412d102c5564136f29735e5d1c3b455b8a37f920da79561f1fde068208","impliedFormat":1},{"version":"c8d3e5a18ba35629954e48c4cc8f11dc88224650067a172685c736b27a34a4dc","impliedFormat":1},{"version":"f0be1b8078cd549d91f37c30c222c2a187ac1cf981d994fb476a1adc61387b14","affectsGlobalScope":true,"impliedFormat":1},{"version":"0aaed1d72199b01234152f7a60046bc947f1f37d78d182e9ae09c4289e06a592","impliedFormat":1},{"version":"0dba70b3fb0dcd713fda33c2df64fa6751fff6460e536971cee917260fb17882","impliedFormat":1},{"version":"66ba1b2c3e3a3644a1011cd530fb444a96b1b2dfe2f5e837a002d41a1a799e60","impliedFormat":1},{"version":"7e514f5b852fdbc166b539fdd1f4e9114f29911592a5eb10a94bb3a13ccac3c4","impliedFormat":1},{"version":"5b7aa3c4c1a5d81b411e8cb302b45507fea9358d3569196b27eb1a27ae3a90ef","affectsGlobalScope":true,"impliedFormat":1},{"version":"5987a903da92c7462e0b35704ce7da94d7fdc4b89a984871c0e2b87a8aae9e69","affectsGlobalScope":true,"impliedFormat":1},{"version":"ea08a0345023ade2b47fbff5a76d0d0ed8bff10bc9d22b83f40858a8e941501c","impliedFormat":1},{"version":"47613031a5a31510831304405af561b0ffaedb734437c595256bb61a90f9311b","impliedFormat":1},{"version":"ae062ce7d9510060c5d7e7952ae379224fb3f8f2dd74e88959878af2057c143b","impliedFormat":1},{"version":"8a1a0d0a4a06a8d278947fcb66bf684f117bf147f89b06e50662d79a53be3e9f","affectsGlobalScope":true,"impliedFormat":1},{"version":"9f663c2f91127ef7024e8ca4b3b4383ff2770e5f826696005de382282794b127","impliedFormat":1},{"version":"9f55299850d4f0921e79b6bf344b47c420ce0f507b9dcf593e532b09ea7eeea1","impliedFormat":1},{"version":"104c67f0da1bdf0d94865419247e20eded83ce7f9911a1aa75fc675c077ca66e","impliedFormat":1},{"version":"cc0d0b339f31ce0ab3b7a5b714d8e578ce698f1e13d7f8c60bfb766baeb1d35c","impliedFormat":1},{"version":"25be1eb939c9c63242c7a45446edb20c40541da967f43f1aa6a00ed53c0552db","impliedFormat":1},{"version":"151ff381ef9ff8da2da9b9663ebf657eac35c4c9a19183420c05728f31a6761d","impliedFormat":1},{"version":"d34aa8df2d0b18fb56b1d772ff9b3c7aea7256cf0d692f969be6e1d27b74d660","impliedFormat":1},{"version":"baac9896d29bcc55391d769e408ff400d61273d832dd500f21de766205255acb","impliedFormat":1},{"version":"2f5747b1508ccf83fad0c251ba1e5da2f5a30b78b09ffa1cfaf633045160afed","impliedFormat":1},{"version":"6823ccc7b5b77bbf898d878dbcad18aa45e0fa96bdd0abd0de98d514845d9ed9","affectsGlobalScope":true,"impliedFormat":1},{"version":"b71c603a539078a5e3a039b20f2b0a0d1708967530cf97dec8850a9ca45baa2b","impliedFormat":1},{"version":"168d88e14e0d81fe170e0dadd38ae9d217476c11435ea640ddb9b7382bdb6c1f","impliedFormat":1},{"version":"8e04cf0688e0d921111659c2b55851957017148fa7b977b02727477d155b3c47","impliedFormat":1},{"version":"afe73051ff6a03a9565cbd8ebb0e956ee3df5e913ad5c1ded64218aabfa3dcb5","impliedFormat":1},{"version":"035a5df183489c2e22f3cf59fc1ed2b043d27f357eecc0eb8d8e840059d44245","impliedFormat":1},{"version":"a4809f4d92317535e6b22b01019437030077a76fec1d93b9881c9ed4738fcc54","impliedFormat":1},{"version":"5f53fa0bd22096d2a78533f94e02c899143b8f0f9891a46965294ee8b91a9434","impliedFormat":1},{"version":"87d9d29dbc745f182683f63187bf3d53fd8673e5fca38ad5eaab69798ed29fbc","impliedFormat":1},{"version":"eb5b19b86227ace1d29ea4cf81387279d04bb34051e944bc53df69f58914b788","affectsGlobalScope":true,"impliedFormat":1},{"version":"ac51dd7d31333793807a6abaa5ae168512b6131bd41d9c5b98477fc3b7800f9f","impliedFormat":1},{"version":"035312d4945d13efa134ae482f6dc56a1a9346f7ac3be7ccbad5741058ce87f3","affectsGlobalScope":true,"impliedFormat":1},{"version":"17ed71200119e86ccef2d96b73b02ce8854b76ad6bd21b5021d4269bec527b5f","impliedFormat":1},{"version":"ab82804a14454734010dcdcd43f564ff7b0389bee4c5692eec76ff5b30d4cf66","impliedFormat":1},{"version":"893cd2b5a7a59d55edc9584cffc0ac51e35ad0290c60d671b61be53d87a02702","impliedFormat":1},{"version":"15fe687c59d62741b4494d5e623d497d55eb38966ecf5bea7f36e48fc3fbe15e","impliedFormat":1},{"version":"2c3b8be03577c98530ef9cb1a76e2c812636a871f367e9edf4c5f3ce702b77f8","affectsGlobalScope":true,"impliedFormat":1},{"version":"f874ea4d0091b0a44362a5f74d26caab2e66dec306c2bf7e8965f5106e784c3b","impliedFormat":1},{"version":"27024f63b0e5b08252e533a8ac3d6a8a907dadc304c26ff24e504c2220811b93","affectsGlobalScope":true,"impliedFormat":1},{"version":"1bc101a1a3827312cd7b2b8dc5fede49b98320f44e43a0953ca45bd16c40f1c9","affectsGlobalScope":true,"impliedFormat":1},{"version":"a65dc671c802014353075c56043eb1a6160996f5a2dc775dbaecc5c3ca11175a","affectsGlobalScope":true,"impliedFormat":1},{"version":"bc81aff061c53a7140270555f4b22da4ecfe8601e8027cf5aa175fbdc7927c31","impliedFormat":1},{"version":"bae8d023ef6b23df7da26f51cea44321f95817c190342a36882e93b80d07a960","impliedFormat":1},{"version":"26a770cec4bd2e7dbba95c6e536390fffe83c6268b78974a93727903b515c4e7","impliedFormat":1},{"version":"f3d8c757e148ad968f0d98697987db363070abada5f503da3c06aefd9d4248c1","impliedFormat":1}],"root":[[64,68]],"options":{"composite":true,"declaration":true,"declarationMap":true,"esModuleInterop":true,"module":99,"outDir":"./dist","rootDir":"./src","skipLibCheck":true,"sourceMap":true,"strict":true,"target":9},"referencedMap":[[71,1],[69,2],[74,3],[70,1],[72,4],[73,1],[181,5],[180,6],[182,6],[183,2],[187,7],[190,8],[191,9],[188,2],[192,2],[193,10],[194,11],[126,12],[127,12],[128,13],[80,14],[129,15],[130,16],[131,17],[75,2],[78,18],[76,2],[77,2],[132,19],[133,20],[134,21],[135,22],[136,23],[137,24],[138,24],[139,25],[140,26],[141,27],[142,28],[81,2],[79,2],[143,29],[144,30],[145,31],[179,32],[146,33],[147,2],[148,34],[149,35],[101,36],[111,37],[100,36],[121,38],[92,39],[91,40],[120,41],[114,42],[119,43],[94,44],[108,45],[93,46],[117,47],[89,48],[88,41],[118,49],[90,50],[95,51],[96,2],[99,51],[86,2],[122,52],[112,53],[103,54],[104,55],[106,56],[102,57],[105,58],[115,41],[97,59],[98,60],[107,61],[87,62],[110,53],[109,51],[113,2],[116,63],[150,64],[151,65],[152,66],[153,67],[154,68],[155,69],[156,70],[157,70],[158,71],[159,2],[160,72],[161,73],[163,74],[162,75],[164,76],[165,77],[166,78],[167,79],[168,80],[169,81],[170,82],[171,83],[172,84],[173,85],[174,86],[175,87],[176,88],[82,2],[83,2],[84,2],[123,89],[124,2],[125,2],[177,90],[178,91],[195,2],[185,2],[186,2],[199,92],[196,2],[198,93],[184,94],[189,95],[200,2],[201,2],[203,96],[202,2],[204,2],[205,2],[207,97],[206,2],[208,98],[209,2],[210,99],[85,2],[197,2],[61,2],[62,2],[12,2],[10,2],[11,2],[16,2],[15,2],[2,2],[17,2],[18,2],[19,2],[20,2],[21,2],[22,2],[23,2],[24,2],[3,2],[25,2],[26,2],[4,2],[27,2],[31,2],[28,2],[29,2],[30,2],[32,2],[33,2],[34,2],[5,2],[35,2],[36,2],[37,2],[38,2],[6,2],[42,2],[39,2],[40,2],[41,2],[43,2],[7,2],[44,2],[49,2],[50,2],[45,2],[46,2],[47,2],[48,2],[8,2],[54,2],[51,2],[52,2],[53,2],[55,2],[9,2],[56,2],[63,2],[57,2],[58,2],[60,2],[59,2],[1,2],[14,2],[13,2],[64,2],[68,100],[67,101],[66,102],[65,2],[211,2]],"latestChangedDtsFile":"./dist/constants.d.ts","version":"5.9.3"} \ No newline at end of file +{"fileNames":["../../node_modules/typescript/lib/lib.es5.d.ts","../../node_modules/typescript/lib/lib.es2015.d.ts","../../node_modules/typescript/lib/lib.es2016.d.ts","../../node_modules/typescript/lib/lib.es2017.d.ts","../../node_modules/typescript/lib/lib.es2018.d.ts","../../node_modules/typescript/lib/lib.es2019.d.ts","../../node_modules/typescript/lib/lib.es2020.d.ts","../../node_modules/typescript/lib/lib.es2021.d.ts","../../node_modules/typescript/lib/lib.es2022.d.ts","../../node_modules/typescript/lib/lib.dom.d.ts","../../node_modules/typescript/lib/lib.dom.iterable.d.ts","../../node_modules/typescript/lib/lib.dom.asynciterable.d.ts","../../node_modules/typescript/lib/lib.webworker.importscripts.d.ts","../../node_modules/typescript/lib/lib.scripthost.d.ts","../../node_modules/typescript/lib/lib.es2015.core.d.ts","../../node_modules/typescript/lib/lib.es2015.collection.d.ts","../../node_modules/typescript/lib/lib.es2015.generator.d.ts","../../node_modules/typescript/lib/lib.es2015.iterable.d.ts","../../node_modules/typescript/lib/lib.es2015.promise.d.ts","../../node_modules/typescript/lib/lib.es2015.proxy.d.ts","../../node_modules/typescript/lib/lib.es2015.reflect.d.ts","../../node_modules/typescript/lib/lib.es2015.symbol.d.ts","../../node_modules/typescript/lib/lib.es2015.symbol.wellknown.d.ts","../../node_modules/typescript/lib/lib.es2016.array.include.d.ts","../../node_modules/typescript/lib/lib.es2016.intl.d.ts","../../node_modules/typescript/lib/lib.es2017.arraybuffer.d.ts","../../node_modules/typescript/lib/lib.es2017.date.d.ts","../../node_modules/typescript/lib/lib.es2017.object.d.ts","../../node_modules/typescript/lib/lib.es2017.sharedmemory.d.ts","../../node_modules/typescript/lib/lib.es2017.string.d.ts","../../node_modules/typescript/lib/lib.es2017.intl.d.ts","../../node_modules/typescript/lib/lib.es2017.typedarrays.d.ts","../../node_modules/typescript/lib/lib.es2018.asyncgenerator.d.ts","../../node_modules/typescript/lib/lib.es2018.asynciterable.d.ts","../../node_modules/typescript/lib/lib.es2018.intl.d.ts","../../node_modules/typescript/lib/lib.es2018.promise.d.ts","../../node_modules/typescript/lib/lib.es2018.regexp.d.ts","../../node_modules/typescript/lib/lib.es2019.array.d.ts","../../node_modules/typescript/lib/lib.es2019.object.d.ts","../../node_modules/typescript/lib/lib.es2019.string.d.ts","../../node_modules/typescript/lib/lib.es2019.symbol.d.ts","../../node_modules/typescript/lib/lib.es2019.intl.d.ts","../../node_modules/typescript/lib/lib.es2020.bigint.d.ts","../../node_modules/typescript/lib/lib.es2020.date.d.ts","../../node_modules/typescript/lib/lib.es2020.promise.d.ts","../../node_modules/typescript/lib/lib.es2020.sharedmemory.d.ts","../../node_modules/typescript/lib/lib.es2020.string.d.ts","../../node_modules/typescript/lib/lib.es2020.symbol.wellknown.d.ts","../../node_modules/typescript/lib/lib.es2020.intl.d.ts","../../node_modules/typescript/lib/lib.es2020.number.d.ts","../../node_modules/typescript/lib/lib.es2021.promise.d.ts","../../node_modules/typescript/lib/lib.es2021.string.d.ts","../../node_modules/typescript/lib/lib.es2021.weakref.d.ts","../../node_modules/typescript/lib/lib.es2021.intl.d.ts","../../node_modules/typescript/lib/lib.es2022.array.d.ts","../../node_modules/typescript/lib/lib.es2022.error.d.ts","../../node_modules/typescript/lib/lib.es2022.intl.d.ts","../../node_modules/typescript/lib/lib.es2022.object.d.ts","../../node_modules/typescript/lib/lib.es2022.string.d.ts","../../node_modules/typescript/lib/lib.es2022.regexp.d.ts","../../node_modules/typescript/lib/lib.decorators.d.ts","../../node_modules/typescript/lib/lib.decorators.legacy.d.ts","../../node_modules/typescript/lib/lib.es2022.full.d.ts","./src/constants.ts","./src/types.ts","./src/protocol.ts","./src/map.ts","./src/index.ts","../../node_modules/@babel/types/lib/index.d.ts","../../node_modules/@types/babel__generator/index.d.ts","../../node_modules/@babel/parser/typings/babel-parser.d.ts","../../node_modules/@types/babel__template/index.d.ts","../../node_modules/@types/babel__traverse/index.d.ts","../../node_modules/@types/babel__core/index.d.ts","../../node_modules/@types/node/compatibility/disposable.d.ts","../../node_modules/@types/node/compatibility/indexable.d.ts","../../node_modules/@types/node/compatibility/iterators.d.ts","../../node_modules/@types/node/compatibility/index.d.ts","../../node_modules/@types/node/globals.typedarray.d.ts","../../node_modules/@types/node/buffer.buffer.d.ts","../../node_modules/@types/node/globals.d.ts","../../node_modules/@types/node/web-globals/abortcontroller.d.ts","../../node_modules/@types/node/web-globals/domexception.d.ts","../../node_modules/@types/node/web-globals/events.d.ts","../../node_modules/buffer/index.d.ts","../../node_modules/@types/node/node_modules/undici-types/header.d.ts","../../node_modules/@types/node/node_modules/undici-types/readable.d.ts","../../node_modules/@types/node/node_modules/undici-types/file.d.ts","../../node_modules/@types/node/node_modules/undici-types/fetch.d.ts","../../node_modules/@types/node/node_modules/undici-types/formdata.d.ts","../../node_modules/@types/node/node_modules/undici-types/connector.d.ts","../../node_modules/@types/node/node_modules/undici-types/client.d.ts","../../node_modules/@types/node/node_modules/undici-types/errors.d.ts","../../node_modules/@types/node/node_modules/undici-types/dispatcher.d.ts","../../node_modules/@types/node/node_modules/undici-types/global-dispatcher.d.ts","../../node_modules/@types/node/node_modules/undici-types/global-origin.d.ts","../../node_modules/@types/node/node_modules/undici-types/pool-stats.d.ts","../../node_modules/@types/node/node_modules/undici-types/pool.d.ts","../../node_modules/@types/node/node_modules/undici-types/handlers.d.ts","../../node_modules/@types/node/node_modules/undici-types/balanced-pool.d.ts","../../node_modules/@types/node/node_modules/undici-types/agent.d.ts","../../node_modules/@types/node/node_modules/undici-types/mock-interceptor.d.ts","../../node_modules/@types/node/node_modules/undici-types/mock-agent.d.ts","../../node_modules/@types/node/node_modules/undici-types/mock-client.d.ts","../../node_modules/@types/node/node_modules/undici-types/mock-pool.d.ts","../../node_modules/@types/node/node_modules/undici-types/mock-errors.d.ts","../../node_modules/@types/node/node_modules/undici-types/proxy-agent.d.ts","../../node_modules/@types/node/node_modules/undici-types/env-http-proxy-agent.d.ts","../../node_modules/@types/node/node_modules/undici-types/retry-handler.d.ts","../../node_modules/@types/node/node_modules/undici-types/retry-agent.d.ts","../../node_modules/@types/node/node_modules/undici-types/api.d.ts","../../node_modules/@types/node/node_modules/undici-types/interceptors.d.ts","../../node_modules/@types/node/node_modules/undici-types/util.d.ts","../../node_modules/@types/node/node_modules/undici-types/cookies.d.ts","../../node_modules/@types/node/node_modules/undici-types/patch.d.ts","../../node_modules/@types/node/node_modules/undici-types/websocket.d.ts","../../node_modules/@types/node/node_modules/undici-types/eventsource.d.ts","../../node_modules/@types/node/node_modules/undici-types/filereader.d.ts","../../node_modules/@types/node/node_modules/undici-types/diagnostics-channel.d.ts","../../node_modules/@types/node/node_modules/undici-types/content-type.d.ts","../../node_modules/@types/node/node_modules/undici-types/cache.d.ts","../../node_modules/@types/node/node_modules/undici-types/index.d.ts","../../node_modules/@types/node/web-globals/fetch.d.ts","../../node_modules/@types/node/web-globals/navigator.d.ts","../../node_modules/@types/node/web-globals/storage.d.ts","../../node_modules/@types/node/assert.d.ts","../../node_modules/@types/node/assert/strict.d.ts","../../node_modules/@types/node/async_hooks.d.ts","../../node_modules/@types/node/buffer.d.ts","../../node_modules/@types/node/child_process.d.ts","../../node_modules/@types/node/cluster.d.ts","../../node_modules/@types/node/console.d.ts","../../node_modules/@types/node/constants.d.ts","../../node_modules/@types/node/crypto.d.ts","../../node_modules/@types/node/dgram.d.ts","../../node_modules/@types/node/diagnostics_channel.d.ts","../../node_modules/@types/node/dns.d.ts","../../node_modules/@types/node/dns/promises.d.ts","../../node_modules/@types/node/domain.d.ts","../../node_modules/@types/node/events.d.ts","../../node_modules/@types/node/fs.d.ts","../../node_modules/@types/node/fs/promises.d.ts","../../node_modules/@types/node/http.d.ts","../../node_modules/@types/node/http2.d.ts","../../node_modules/@types/node/https.d.ts","../../node_modules/@types/node/inspector.d.ts","../../node_modules/@types/node/inspector.generated.d.ts","../../node_modules/@types/node/module.d.ts","../../node_modules/@types/node/net.d.ts","../../node_modules/@types/node/os.d.ts","../../node_modules/@types/node/path.d.ts","../../node_modules/@types/node/perf_hooks.d.ts","../../node_modules/@types/node/process.d.ts","../../node_modules/@types/node/punycode.d.ts","../../node_modules/@types/node/querystring.d.ts","../../node_modules/@types/node/readline.d.ts","../../node_modules/@types/node/readline/promises.d.ts","../../node_modules/@types/node/repl.d.ts","../../node_modules/@types/node/sea.d.ts","../../node_modules/@types/node/sqlite.d.ts","../../node_modules/@types/node/stream.d.ts","../../node_modules/@types/node/stream/promises.d.ts","../../node_modules/@types/node/stream/consumers.d.ts","../../node_modules/@types/node/stream/web.d.ts","../../node_modules/@types/node/string_decoder.d.ts","../../node_modules/@types/node/test.d.ts","../../node_modules/@types/node/timers.d.ts","../../node_modules/@types/node/timers/promises.d.ts","../../node_modules/@types/node/tls.d.ts","../../node_modules/@types/node/trace_events.d.ts","../../node_modules/@types/node/tty.d.ts","../../node_modules/@types/node/url.d.ts","../../node_modules/@types/node/util.d.ts","../../node_modules/@types/node/v8.d.ts","../../node_modules/@types/node/vm.d.ts","../../node_modules/@types/node/wasi.d.ts","../../node_modules/@types/node/worker_threads.d.ts","../../node_modules/@types/node/zlib.d.ts","../../node_modules/@types/node/index.d.ts","../../node_modules/@types/connect/index.d.ts","../../node_modules/@types/cors/index.d.ts","../../node_modules/@types/estree/index.d.ts","../../node_modules/@types/graceful-fs/index.d.ts","../../node_modules/@types/istanbul-lib-coverage/index.d.ts","../../node_modules/@types/istanbul-lib-report/index.d.ts","../../node_modules/@types/istanbul-reports/index.d.ts","../../node_modules/@types/prop-types/index.d.ts","../../node_modules/@types/react/global.d.ts","../../node_modules/csstype/index.d.ts","../../node_modules/@types/react/index.d.ts","../../node_modules/@types/react-dom/index.d.ts","../../node_modules/@types/stack-utils/index.d.ts","../../node_modules/@types/stylis/index.d.ts","../../node_modules/@types/trusted-types/lib/index.d.ts","../../node_modules/@types/trusted-types/index.d.ts","../../node_modules/@types/w3c-web-usb/index.d.ts","../../node_modules/@types/web/iterable.d.ts","../../node_modules/@types/web/index.d.ts","../../node_modules/@types/ws/index.d.ts","../../node_modules/@types/yargs-parser/index.d.ts","../../node_modules/@types/yargs/index.d.ts"],"fileIdsList":[[69,80,129,146,147],[80,129,146,147],[69,70,71,72,73,80,129,146,147],[69,71,80,129,146,147],[80,129,143,146,147,179],[80,129,141,146,147,179],[80,129,146,147,184],[80,129,146,147,185],[80,126,127,129,146,147],[80,128,129,146,147],[129,146,147],[80,129,134,146,147,164],[80,129,130,135,140,146,147,149,161,172],[80,129,130,131,140,146,147,149],[75,76,77,80,129,146,147],[80,129,132,146,147,173],[80,129,133,134,141,146,147,150],[80,129,134,146,147,161,169],[80,129,135,137,140,146,147,149],[80,128,129,136,146,147],[80,129,137,138,146,147],[80,129,139,140,146,147],[80,128,129,140,146,147],[80,129,140,141,142,146,147,161,172],[80,129,140,141,142,146,147,156,161,164],[80,122,129,137,140,143,146,147,149,161,172],[80,129,140,141,143,144,146,147,149,161,169,172],[80,129,143,145,146,147,161,169,172],[78,79,80,81,82,83,84,123,124,125,126,127,128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,145,146,147,148,149,150,151,152,153,154,155,156,157,158,159,160,161,162,163,164,165,166,167,168,169,170,171,172,173,174,175,176,177,178],[80,129,140,146,147],[80,129,146,147,148,172],[80,129,137,140,146,147,149,161],[80,94,98,129,146,147,172],[80,94,129,146,147,161,172],[80,89,129,146,147],[80,91,94,129,146,147,169,172],[80,129,146,147,149,169],[80,129,146,147,179],[80,89,129,146,147,179],[80,91,94,129,146,147,149,172],[80,86,87,90,93,129,140,146,147,161,172],[80,94,101,129,146,147],[80,86,92,129,146,147],[80,94,115,116,129,146,147],[80,90,94,129,146,147,164,172,179],[80,115,129,146,147,179],[80,88,89,129,146,147,179],[80,94,129,146,147],[80,88,89,90,91,92,93,94,95,96,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,116,117,118,119,120,121,129,146,147],[80,94,109,129,146,147],[80,94,101,102,129,146,147],[80,92,94,102,103,129,146,147],[80,93,129,146,147],[80,86,89,94,129,146,147],[80,94,98,102,103,129,146,147],[80,98,129,146,147],[80,92,94,97,129,146,147,172],[80,86,91,94,101,129,146,147],[80,129,146,147,161],[80,89,94,115,129,146,147,177,179],[80,129,146,147,150],[80,129,146,147,151],[80,128,129,146,147,152],[80,126,127,128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,145,146,147,148,149,150,151,152,153,154,155,156,157,158,159,160,161,162,163,164,165,166,167,168,169,170,171,172,173,174,175,176,177,178],[80,129,146,147,154],[80,129,146,147,155],[80,129,140,146,147,156,157],[80,129,146,147,156,158,173,175],[80,129,141,146,147],[80,129,140,146,147,161,162,164],[80,129,146,147,163,164],[80,129,146,147,161,162],[80,129,146,147,164],[80,129,146,147,165],[80,126,129,146,147,161,166,172],[80,129,140,146,147,167,168],[80,129,146,147,167,168],[80,129,134,146,147,149,161,169],[80,129,146,147,170],[80,129,146,147,149,171],[80,129,143,146,147,155,172],[80,129,134,146,147,173],[80,129,146,147,161,174],[80,129,146,147,148,175],[80,129,146,147,176],[80,122,129,146,147],[80,122,129,140,142,146,147,152,161,164,172,174,175,177],[80,129,146,147,161,178],[80,129,146,147,190],[80,129,146,147,187,188,189],[80,129,146,147,194],[80,129,146,147,197],[80,129,140,143,145,146,147,149,161,169,172,178,179],[80,129,146,147,200],[64,65,66,67,80,129,146,147],[64,65,80,129,146,147],[65,80,129,146,147]],"fileInfos":[{"version":"c430d44666289dae81f30fa7b2edebf186ecc91a2d4c71266ea6ae76388792e1","affectsGlobalScope":true,"impliedFormat":1},{"version":"45b7ab580deca34ae9729e97c13cfd999df04416a79116c3bfb483804f85ded4","impliedFormat":1},{"version":"3facaf05f0c5fc569c5649dd359892c98a85557e3e0c847964caeb67076f4d75","impliedFormat":1},{"version":"e44bb8bbac7f10ecc786703fe0a6a4b952189f908707980ba8f3c8975a760962","impliedFormat":1},{"version":"5e1c4c362065a6b95ff952c0eab010f04dcd2c3494e813b493ecfd4fcb9fc0d8","impliedFormat":1},{"version":"68d73b4a11549f9c0b7d352d10e91e5dca8faa3322bfb77b661839c42b1ddec7","impliedFormat":1},{"version":"5efce4fc3c29ea84e8928f97adec086e3dc876365e0982cc8479a07954a3efd4","impliedFormat":1},{"version":"feecb1be483ed332fad555aff858affd90a48ab19ba7272ee084704eb7167569","impliedFormat":1},{"version":"ee7bad0c15b58988daa84371e0b89d313b762ab83cb5b31b8a2d1162e8eb41c2","impliedFormat":1},{"version":"080941d9f9ff9307f7e27a83bcd888b7c8270716c39af943532438932ec1d0b9","affectsGlobalScope":true,"impliedFormat":1},{"version":"2e80ee7a49e8ac312cc11b77f1475804bee36b3b2bc896bead8b6e1266befb43","affectsGlobalScope":true,"impliedFormat":1},{"version":"d7a3c8b952931daebdfc7a2897c53c0a1c73624593fa070e46bd537e64dcd20a","affectsGlobalScope":true,"impliedFormat":1},{"version":"80e18897e5884b6723488d4f5652167e7bb5024f946743134ecc4aa4ee731f89","affectsGlobalScope":true,"impliedFormat":1},{"version":"cd034f499c6cdca722b60c04b5b1b78e058487a7085a8e0d6fb50809947ee573","affectsGlobalScope":true,"impliedFormat":1},{"version":"c57796738e7f83dbc4b8e65132f11a377649c00dd3eee333f672b8f0a6bea671","affectsGlobalScope":true,"impliedFormat":1},{"version":"dc2df20b1bcdc8c2d34af4926e2c3ab15ffe1160a63e58b7e09833f616efff44","affectsGlobalScope":true,"impliedFormat":1},{"version":"515d0b7b9bea2e31ea4ec968e9edd2c39d3eebf4a2d5cbd04e88639819ae3b71","affectsGlobalScope":true,"impliedFormat":1},{"version":"0559b1f683ac7505ae451f9a96ce4c3c92bdc71411651ca6ddb0e88baaaad6a3","affectsGlobalScope":true,"impliedFormat":1},{"version":"0dc1e7ceda9b8b9b455c3a2d67b0412feab00bd2f66656cd8850e8831b08b537","affectsGlobalScope":true,"impliedFormat":1},{"version":"ce691fb9e5c64efb9547083e4a34091bcbe5bdb41027e310ebba8f7d96a98671","affectsGlobalScope":true,"impliedFormat":1},{"version":"8d697a2a929a5fcb38b7a65594020fcef05ec1630804a33748829c5ff53640d0","affectsGlobalScope":true,"impliedFormat":1},{"version":"4ff2a353abf8a80ee399af572debb8faab2d33ad38c4b4474cff7f26e7653b8d","affectsGlobalScope":true,"impliedFormat":1},{"version":"fb0f136d372979348d59b3f5020b4cdb81b5504192b1cacff5d1fbba29378aa1","affectsGlobalScope":true,"impliedFormat":1},{"version":"d15bea3d62cbbdb9797079416b8ac375ae99162a7fba5de2c6c505446486ac0a","affectsGlobalScope":true,"impliedFormat":1},{"version":"68d18b664c9d32a7336a70235958b8997ebc1c3b8505f4f1ae2b7e7753b87618","affectsGlobalScope":true,"impliedFormat":1},{"version":"eb3d66c8327153d8fa7dd03f9c58d351107fe824c79e9b56b462935176cdf12a","affectsGlobalScope":true,"impliedFormat":1},{"version":"38f0219c9e23c915ef9790ab1d680440d95419ad264816fa15009a8851e79119","affectsGlobalScope":true,"impliedFormat":1},{"version":"69ab18c3b76cd9b1be3d188eaf8bba06112ebbe2f47f6c322b5105a6fbc45a2e","affectsGlobalScope":true,"impliedFormat":1},{"version":"a680117f487a4d2f30ea46f1b4b7f58bef1480456e18ba53ee85c2746eeca012","affectsGlobalScope":true,"impliedFormat":1},{"version":"2f11ff796926e0832f9ae148008138ad583bd181899ab7dd768a2666700b1893","affectsGlobalScope":true,"impliedFormat":1},{"version":"4de680d5bb41c17f7f68e0419412ca23c98d5749dcaaea1896172f06435891fc","affectsGlobalScope":true,"impliedFormat":1},{"version":"954296b30da6d508a104a3a0b5d96b76495c709785c1d11610908e63481ee667","affectsGlobalScope":true,"impliedFormat":1},{"version":"ac9538681b19688c8eae65811b329d3744af679e0bdfa5d842d0e32524c73e1c","affectsGlobalScope":true,"impliedFormat":1},{"version":"0a969edff4bd52585473d24995c5ef223f6652d6ef46193309b3921d65dd4376","affectsGlobalScope":true,"impliedFormat":1},{"version":"9e9fbd7030c440b33d021da145d3232984c8bb7916f277e8ffd3dc2e3eae2bdb","affectsGlobalScope":true,"impliedFormat":1},{"version":"811ec78f7fefcabbda4bfa93b3eb67d9ae166ef95f9bff989d964061cbf81a0c","affectsGlobalScope":true,"impliedFormat":1},{"version":"717937616a17072082152a2ef351cb51f98802fb4b2fdabd32399843875974ca","affectsGlobalScope":true,"impliedFormat":1},{"version":"d7e7d9b7b50e5f22c915b525acc5a49a7a6584cf8f62d0569e557c5cfc4b2ac2","affectsGlobalScope":true,"impliedFormat":1},{"version":"71c37f4c9543f31dfced6c7840e068c5a5aacb7b89111a4364b1d5276b852557","affectsGlobalScope":true,"impliedFormat":1},{"version":"576711e016cf4f1804676043e6a0a5414252560eb57de9faceee34d79798c850","affectsGlobalScope":true,"impliedFormat":1},{"version":"89c1b1281ba7b8a96efc676b11b264de7a8374c5ea1e6617f11880a13fc56dc6","affectsGlobalScope":true,"impliedFormat":1},{"version":"74f7fa2d027d5b33eb0471c8e82a6c87216223181ec31247c357a3e8e2fddc5b","affectsGlobalScope":true,"impliedFormat":1},{"version":"d6d7ae4d1f1f3772e2a3cde568ed08991a8ae34a080ff1151af28b7f798e22ca","affectsGlobalScope":true,"impliedFormat":1},{"version":"063600664504610fe3e99b717a1223f8b1900087fab0b4cad1496a114744f8df","affectsGlobalScope":true,"impliedFormat":1},{"version":"934019d7e3c81950f9a8426d093458b65d5aff2c7c1511233c0fd5b941e608ab","affectsGlobalScope":true,"impliedFormat":1},{"version":"52ada8e0b6e0482b728070b7639ee42e83a9b1c22d205992756fe020fd9f4a47","affectsGlobalScope":true,"impliedFormat":1},{"version":"3bdefe1bfd4d6dee0e26f928f93ccc128f1b64d5d501ff4a8cf3c6371200e5e6","affectsGlobalScope":true,"impliedFormat":1},{"version":"59fb2c069260b4ba00b5643b907ef5d5341b167e7d1dbf58dfd895658bda2867","affectsGlobalScope":true,"impliedFormat":1},{"version":"639e512c0dfc3fad96a84caad71b8834d66329a1f28dc95e3946c9b58176c73a","affectsGlobalScope":true,"impliedFormat":1},{"version":"368af93f74c9c932edd84c58883e736c9e3d53cec1fe24c0b0ff451f529ceab1","affectsGlobalScope":true,"impliedFormat":1},{"version":"af3dd424cf267428f30ccfc376f47a2c0114546b55c44d8c0f1d57d841e28d74","affectsGlobalScope":true,"impliedFormat":1},{"version":"995c005ab91a498455ea8dfb63aa9f83fa2ea793c3d8aa344be4a1678d06d399","affectsGlobalScope":true,"impliedFormat":1},{"version":"959d36cddf5e7d572a65045b876f2956c973a586da58e5d26cde519184fd9b8a","affectsGlobalScope":true,"impliedFormat":1},{"version":"965f36eae237dd74e6cca203a43e9ca801ce38824ead814728a2807b1910117d","affectsGlobalScope":true,"impliedFormat":1},{"version":"3925a6c820dcb1a06506c90b1577db1fdbf7705d65b62b99dce4be75c637e26b","affectsGlobalScope":true,"impliedFormat":1},{"version":"0a3d63ef2b853447ec4f749d3f368ce642264246e02911fcb1590d8c161b8005","affectsGlobalScope":true,"impliedFormat":1},{"version":"8cdf8847677ac7d20486e54dd3fcf09eda95812ac8ace44b4418da1bbbab6eb8","affectsGlobalScope":true,"impliedFormat":1},{"version":"8444af78980e3b20b49324f4a16ba35024fef3ee069a0eb67616ea6ca821c47a","affectsGlobalScope":true,"impliedFormat":1},{"version":"3287d9d085fbd618c3971944b65b4be57859f5415f495b33a6adc994edd2f004","affectsGlobalScope":true,"impliedFormat":1},{"version":"b4b67b1a91182421f5df999988c690f14d813b9850b40acd06ed44691f6727ad","affectsGlobalScope":true,"impliedFormat":1},{"version":"8e7f8264d0fb4c5339605a15daadb037bf238c10b654bb3eee14208f860a32ea","affectsGlobalScope":true,"impliedFormat":1},{"version":"782dec38049b92d4e85c1585fbea5474a219c6984a35b004963b00beb1aab538","affectsGlobalScope":true,"impliedFormat":1},{"version":"3cbad9a1ba4453443026ed38e4b8be018abb26565fa7c944376463ad9df07c41","impliedFormat":1},{"version":"5be358311b150d82b7c851d215657f5878545d6d8878fdc7a9fdc620d8443a61","signature":"a56d356b2fdc47341f7cd4a763fa3a897d9b7fa0d30bef6d0a36750875d9d204"},{"version":"5815de1c67134a1f558b7d4af63db59634a3b8aae8e80065db7e9632bc060362","signature":"3951b31cd647381dea37abb1b41242e3339027c5b719f1871b69da56ef747f7a"},{"version":"f3e3d156e1981041cbbf7c9b07478a02a331ca93fa34297265a5c1e5181d4ace","signature":"b36da0bbd22a1131b8276d211fda1a6b8c47fd37769fb3dcede83c13a34e3056"},{"version":"50beadb1a64d4dab33071761b29b751ada6a279d347dbae7226c89c3208538ab","signature":"ba3ec128eebd98797e5614a49b6d4f7f6b8748b006132ee293ddce90d7bd6eaf"},{"version":"ba42c07094329d063d463b8d6ea2d22660b9b4847faaf5e40b9af4850cfedc78","signature":"6457043a77efb2fee1427fabae290c84c7a923d209b56e4ead645ff0a61a6d25"},{"version":"556ccd493ec36c7d7cb130d51be66e147b91cc1415be383d71da0f1e49f742a9","impliedFormat":1},{"version":"b6d03c9cfe2cf0ba4c673c209fcd7c46c815b2619fd2aad59fc4229aaef2ed43","impliedFormat":1},{"version":"95aba78013d782537cc5e23868e736bec5d377b918990e28ed56110e3ae8b958","impliedFormat":1},{"version":"670a76db379b27c8ff42f1ba927828a22862e2ab0b0908e38b671f0e912cc5ed","impliedFormat":1},{"version":"13b77ab19ef7aadd86a1e54f2f08ea23a6d74e102909e3c00d31f231ed040f62","impliedFormat":1},{"version":"069bebfee29864e3955378107e243508b163e77ab10de6a5ee03ae06939f0bb9","impliedFormat":1},{"version":"6c7176368037af28cb72f2392010fa1cef295d6d6744bca8cfb54985f3a18c3e","affectsGlobalScope":true,"impliedFormat":1},{"version":"ab41ef1f2cdafb8df48be20cd969d875602483859dc194e9c97c8a576892c052","affectsGlobalScope":true,"impliedFormat":1},{"version":"437e20f2ba32abaeb7985e0afe0002de1917bc74e949ba585e49feba65da6ca1","affectsGlobalScope":true,"impliedFormat":1},{"version":"21d819c173c0cf7cc3ce57c3276e77fd9a8a01d35a06ad87158781515c9a438a","impliedFormat":1},{"version":"98cffbf06d6bab333473c70a893770dbe990783904002c4f1a960447b4b53dca","affectsGlobalScope":true,"impliedFormat":1},{"version":"3af97acf03cc97de58a3a4bc91f8f616408099bc4233f6d0852e72a8ffb91ac9","affectsGlobalScope":true,"impliedFormat":1},{"version":"808069bba06b6768b62fd22429b53362e7af342da4a236ed2d2e1c89fcca3b4a","affectsGlobalScope":true,"impliedFormat":1},{"version":"1db0b7dca579049ca4193d034d835f6bfe73096c73663e5ef9a0b5779939f3d0","affectsGlobalScope":true,"impliedFormat":1},{"version":"9798340ffb0d067d69b1ae5b32faa17ab31b82466a3fc00d8f2f2df0c8554aaa","affectsGlobalScope":true,"impliedFormat":1},{"version":"f26b11d8d8e4b8028f1c7d618b22274c892e4b0ef5b3678a8ccbad85419aef43","affectsGlobalScope":true,"impliedFormat":1},{"version":"4967529644e391115ca5592184d4b63980569adf60ee685f968fd59ab1557188","impliedFormat":1},{"version":"5929864ce17fba74232584d90cb721a89b7ad277220627cc97054ba15a98ea8f","impliedFormat":1},{"version":"763fe0f42b3d79b440a9b6e51e9ba3f3f91352469c1e4b3b67bfa4ff6352f3f4","impliedFormat":1},{"version":"25c8056edf4314820382a5fdb4bb7816999acdcb929c8f75e3f39473b87e85bc","impliedFormat":1},{"version":"c464d66b20788266e5353b48dc4aa6bc0dc4a707276df1e7152ab0c9ae21fad8","impliedFormat":1},{"version":"78d0d27c130d35c60b5e5566c9f1e5be77caf39804636bc1a40133919a949f21","impliedFormat":1},{"version":"c6fd2c5a395f2432786c9cb8deb870b9b0e8ff7e22c029954fabdd692bff6195","impliedFormat":1},{"version":"1d6e127068ea8e104a912e42fc0a110e2aa5a66a356a917a163e8cf9a65e4a75","impliedFormat":1},{"version":"5ded6427296cdf3b9542de4471d2aa8d3983671d4cac0f4bf9c637208d1ced43","impliedFormat":1},{"version":"7f182617db458e98fc18dfb272d40aa2fff3a353c44a89b2c0ccb3937709bfb5","impliedFormat":1},{"version":"cadc8aced301244057c4e7e73fbcae534b0f5b12a37b150d80e5a45aa4bebcbd","impliedFormat":1},{"version":"385aab901643aa54e1c36f5ef3107913b10d1b5bb8cbcd933d4263b80a0d7f20","impliedFormat":1},{"version":"9670d44354bab9d9982eca21945686b5c24a3f893db73c0dae0fd74217a4c219","impliedFormat":1},{"version":"0b8a9268adaf4da35e7fa830c8981cfa22adbbe5b3f6f5ab91f6658899e657a7","impliedFormat":1},{"version":"11396ed8a44c02ab9798b7dca436009f866e8dae3c9c25e8c1fbc396880bf1bb","impliedFormat":1},{"version":"ba7bc87d01492633cb5a0e5da8a4a42a1c86270e7b3d2dea5d156828a84e4882","impliedFormat":1},{"version":"4893a895ea92c85345017a04ed427cbd6a1710453338df26881a6019432febdd","impliedFormat":1},{"version":"c21dc52e277bcfc75fac0436ccb75c204f9e1b3fa5e12729670910639f27343e","impliedFormat":1},{"version":"13f6f39e12b1518c6650bbb220c8985999020fe0f21d818e28f512b7771d00f9","impliedFormat":1},{"version":"9b5369969f6e7175740bf51223112ff209f94ba43ecd3bb09eefff9fd675624a","impliedFormat":1},{"version":"4fe9e626e7164748e8769bbf74b538e09607f07ed17c2f20af8d680ee49fc1da","impliedFormat":1},{"version":"24515859bc0b836719105bb6cc3d68255042a9f02a6022b3187948b204946bd2","impliedFormat":1},{"version":"ea0148f897b45a76544ae179784c95af1bd6721b8610af9ffa467a518a086a43","impliedFormat":1},{"version":"24c6a117721e606c9984335f71711877293a9651e44f59f3d21c1ea0856f9cc9","impliedFormat":1},{"version":"dd3273ead9fbde62a72949c97dbec2247ea08e0c6952e701a483d74ef92d6a17","impliedFormat":1},{"version":"405822be75ad3e4d162e07439bac80c6bcc6dbae1929e179cf467ec0b9ee4e2e","impliedFormat":1},{"version":"0db18c6e78ea846316c012478888f33c11ffadab9efd1cc8bcc12daded7a60b6","impliedFormat":1},{"version":"e61be3f894b41b7baa1fbd6a66893f2579bfad01d208b4ff61daef21493ef0a8","impliedFormat":1},{"version":"bd0532fd6556073727d28da0edfd1736417a3f9f394877b6d5ef6ad88fba1d1a","impliedFormat":1},{"version":"89167d696a849fce5ca508032aabfe901c0868f833a8625d5a9c6e861ef935d2","impliedFormat":1},{"version":"615ba88d0128ed16bf83ef8ccbb6aff05c3ee2db1cc0f89ab50a4939bfc1943f","impliedFormat":1},{"version":"a4d551dbf8746780194d550c88f26cf937caf8d56f102969a110cfaed4b06656","impliedFormat":1},{"version":"8bd86b8e8f6a6aa6c49b71e14c4ffe1211a0e97c80f08d2c8cc98838006e4b88","impliedFormat":1},{"version":"317e63deeb21ac07f3992f5b50cdca8338f10acd4fbb7257ebf56735bf52ab00","impliedFormat":1},{"version":"4732aec92b20fb28c5fe9ad99521fb59974289ed1e45aecb282616202184064f","impliedFormat":1},{"version":"2e85db9e6fd73cfa3d7f28e0ab6b55417ea18931423bd47b409a96e4a169e8e6","impliedFormat":1},{"version":"c46e079fe54c76f95c67fb89081b3e399da2c7d109e7dca8e4b58d83e332e605","impliedFormat":1},{"version":"bf67d53d168abc1298888693338cb82854bdb2e69ef83f8a0092093c2d562107","impliedFormat":1},{"version":"b52476feb4a0cbcb25e5931b930fc73cb6643fb1a5060bf8a3dda0eeae5b4b68","affectsGlobalScope":true,"impliedFormat":1},{"version":"f9501cc13ce624c72b61f12b3963e84fad210fbdf0ffbc4590e08460a3f04eba","affectsGlobalScope":true,"impliedFormat":1},{"version":"e7721c4f69f93c91360c26a0a84ee885997d748237ef78ef665b153e622b36c1","affectsGlobalScope":true,"impliedFormat":1},{"version":"0fa06ada475b910e2106c98c68b10483dc8811d0c14a8a8dd36efb2672485b29","impliedFormat":1},{"version":"33e5e9aba62c3193d10d1d33ae1fa75c46a1171cf76fef750777377d53b0303f","impliedFormat":1},{"version":"2b06b93fd01bcd49d1a6bd1f9b65ddcae6480b9a86e9061634d6f8e354c1468f","impliedFormat":1},{"version":"6a0cd27e5dc2cfbe039e731cf879d12b0e2dded06d1b1dedad07f7712de0d7f4","affectsGlobalScope":true,"impliedFormat":1},{"version":"13f5c844119c43e51ce777c509267f14d6aaf31eafb2c2b002ca35584cd13b29","impliedFormat":1},{"version":"e60477649d6ad21542bd2dc7e3d9ff6853d0797ba9f689ba2f6653818999c264","impliedFormat":1},{"version":"c2510f124c0293ab80b1777c44d80f812b75612f297b9857406468c0f4dafe29","affectsGlobalScope":true,"impliedFormat":1},{"version":"5524481e56c48ff486f42926778c0a3cce1cc85dc46683b92b1271865bcf015a","impliedFormat":1},{"version":"4c829ab315f57c5442c6667b53769975acbf92003a66aef19bce151987675bd1","affectsGlobalScope":true,"impliedFormat":1},{"version":"b2ade7657e2db96d18315694789eff2ddd3d8aea7215b181f8a0b303277cc579","impliedFormat":1},{"version":"9855e02d837744303391e5623a531734443a5f8e6e8755e018c41d63ad797db2","impliedFormat":1},{"version":"4d631b81fa2f07a0e63a9a143d6a82c25c5f051298651a9b69176ba28930756d","impliedFormat":1},{"version":"836a356aae992ff3c28a0212e3eabcb76dd4b0cc06bcb9607aeef560661b860d","impliedFormat":1},{"version":"1e0d1f8b0adfa0b0330e028c7941b5a98c08b600efe7f14d2d2a00854fb2f393","impliedFormat":1},{"version":"41670ee38943d9cbb4924e436f56fc19ee94232bc96108562de1a734af20dc2c","affectsGlobalScope":true,"impliedFormat":1},{"version":"c906fb15bd2aabc9ed1e3f44eb6a8661199d6c320b3aa196b826121552cb3695","impliedFormat":1},{"version":"22295e8103f1d6d8ea4b5d6211e43421fe4564e34d0dd8e09e520e452d89e659","impliedFormat":1},{"version":"f949f7f6c7802a338039cfc2156d1fe285cdd1e092c64437ebe15ae8edc854e0","impliedFormat":1},{"version":"6b4e081d55ac24fc8a4631d5dd77fe249fa25900abd7d046abb87d90e3b45645","impliedFormat":1},{"version":"a10f0e1854f3316d7ee437b79649e5a6ae3ae14ffe6322b02d4987071a95362e","impliedFormat":1},{"version":"e208f73ef6a980104304b0d2ca5f6bf1b85de6009d2c7e404028b875020fa8f2","impliedFormat":1},{"version":"d163b6bc2372b4f07260747cbc6c0a6405ab3fbcea3852305e98ac43ca59f5bc","impliedFormat":1},{"version":"e6fa9ad47c5f71ff733744a029d1dc472c618de53804eae08ffc243b936f87ff","affectsGlobalScope":true,"impliedFormat":1},{"version":"83e63d6ccf8ec004a3bb6d58b9bb0104f60e002754b1e968024b320730cc5311","impliedFormat":1},{"version":"24826ed94a78d5c64bd857570fdbd96229ad41b5cb654c08d75a9845e3ab7dde","impliedFormat":1},{"version":"8b479a130ccb62e98f11f136d3ac80f2984fdc07616516d29881f3061f2dd472","impliedFormat":1},{"version":"928af3d90454bf656a52a48679f199f64c1435247d6189d1caf4c68f2eaf921f","affectsGlobalScope":true,"impliedFormat":1},{"version":"d2bc7425ef40526650d6db7e072c1ff4a51101c3ac2cc4b666623b19496a6e27","affectsGlobalScope":true,"impliedFormat":1},{"version":"3f16a7e4deafa527ed9995a772bb380eb7d3c2c0fd4ae178c5263ed18394db2c","impliedFormat":1},{"version":"933921f0bb0ec12ef45d1062a1fc0f27635318f4d294e4d99de9a5493e618ca2","impliedFormat":1},{"version":"71a0f3ad612c123b57239a7749770017ecfe6b66411488000aba83e4546fde25","impliedFormat":1},{"version":"77fbe5eecb6fac4b6242bbf6eebfc43e98ce5ccba8fa44e0ef6a95c945ff4d98","impliedFormat":1},{"version":"4f9d8ca0c417b67b69eeb54c7ca1bedd7b56034bb9bfd27c5d4f3bc4692daca7","impliedFormat":1},{"version":"814118df420c4e38fe5ae1b9a3bafb6e9c2aa40838e528cde908381867be6466","impliedFormat":1},{"version":"a3fc63c0d7b031693f665f5494412ba4b551fe644ededccc0ab5922401079c95","impliedFormat":1},{"version":"f27524f4bef4b6519c604bdb23bf4465bddcccbf3f003abb901acbd0d7404d99","impliedFormat":1},{"version":"37ba7b45141a45ce6e80e66f2a96c8a5ab1bcef0fc2d0f56bb58df96ec67e972","impliedFormat":1},{"version":"45650f47bfb376c8a8ed39d4bcda5902ab899a3150029684ee4c10676d9fbaee","impliedFormat":1},{"version":"6b039f55681caaf111d5eb84d292b9bee9e0131d0db1ad0871eef0964f533c73","affectsGlobalScope":true,"impliedFormat":1},{"version":"18fd40412d102c5564136f29735e5d1c3b455b8a37f920da79561f1fde068208","impliedFormat":1},{"version":"c8d3e5a18ba35629954e48c4cc8f11dc88224650067a172685c736b27a34a4dc","impliedFormat":1},{"version":"f0be1b8078cd549d91f37c30c222c2a187ac1cf981d994fb476a1adc61387b14","affectsGlobalScope":true,"impliedFormat":1},{"version":"0aaed1d72199b01234152f7a60046bc947f1f37d78d182e9ae09c4289e06a592","impliedFormat":1},{"version":"0dba70b3fb0dcd713fda33c2df64fa6751fff6460e536971cee917260fb17882","impliedFormat":1},{"version":"66ba1b2c3e3a3644a1011cd530fb444a96b1b2dfe2f5e837a002d41a1a799e60","impliedFormat":1},{"version":"7e514f5b852fdbc166b539fdd1f4e9114f29911592a5eb10a94bb3a13ccac3c4","impliedFormat":1},{"version":"5b7aa3c4c1a5d81b411e8cb302b45507fea9358d3569196b27eb1a27ae3a90ef","affectsGlobalScope":true,"impliedFormat":1},{"version":"5987a903da92c7462e0b35704ce7da94d7fdc4b89a984871c0e2b87a8aae9e69","affectsGlobalScope":true,"impliedFormat":1},{"version":"ea08a0345023ade2b47fbff5a76d0d0ed8bff10bc9d22b83f40858a8e941501c","impliedFormat":1},{"version":"47613031a5a31510831304405af561b0ffaedb734437c595256bb61a90f9311b","impliedFormat":1},{"version":"ae062ce7d9510060c5d7e7952ae379224fb3f8f2dd74e88959878af2057c143b","impliedFormat":1},{"version":"8a1a0d0a4a06a8d278947fcb66bf684f117bf147f89b06e50662d79a53be3e9f","affectsGlobalScope":true,"impliedFormat":1},{"version":"9f663c2f91127ef7024e8ca4b3b4383ff2770e5f826696005de382282794b127","impliedFormat":1},{"version":"9f55299850d4f0921e79b6bf344b47c420ce0f507b9dcf593e532b09ea7eeea1","impliedFormat":1},{"version":"104c67f0da1bdf0d94865419247e20eded83ce7f9911a1aa75fc675c077ca66e","impliedFormat":1},{"version":"25be1eb939c9c63242c7a45446edb20c40541da967f43f1aa6a00ed53c0552db","impliedFormat":1},{"version":"151ff381ef9ff8da2da9b9663ebf657eac35c4c9a19183420c05728f31a6761d","impliedFormat":1},{"version":"afe73051ff6a03a9565cbd8ebb0e956ee3df5e913ad5c1ded64218aabfa3dcb5","impliedFormat":1},{"version":"035a5df183489c2e22f3cf59fc1ed2b043d27f357eecc0eb8d8e840059d44245","impliedFormat":1},{"version":"a4809f4d92317535e6b22b01019437030077a76fec1d93b9881c9ed4738fcc54","impliedFormat":1},{"version":"5f53fa0bd22096d2a78533f94e02c899143b8f0f9891a46965294ee8b91a9434","impliedFormat":1},{"version":"87d9d29dbc745f182683f63187bf3d53fd8673e5fca38ad5eaab69798ed29fbc","impliedFormat":1},{"version":"eb5b19b86227ace1d29ea4cf81387279d04bb34051e944bc53df69f58914b788","affectsGlobalScope":true,"impliedFormat":1},{"version":"ac51dd7d31333793807a6abaa5ae168512b6131bd41d9c5b98477fc3b7800f9f","impliedFormat":1},{"version":"035312d4945d13efa134ae482f6dc56a1a9346f7ac3be7ccbad5741058ce87f3","affectsGlobalScope":true,"impliedFormat":1},{"version":"17ed71200119e86ccef2d96b73b02ce8854b76ad6bd21b5021d4269bec527b5f","impliedFormat":1},{"version":"ab82804a14454734010dcdcd43f564ff7b0389bee4c5692eec76ff5b30d4cf66","impliedFormat":1},{"version":"893cd2b5a7a59d55edc9584cffc0ac51e35ad0290c60d671b61be53d87a02702","impliedFormat":1},{"version":"15fe687c59d62741b4494d5e623d497d55eb38966ecf5bea7f36e48fc3fbe15e","impliedFormat":1},{"version":"2c3b8be03577c98530ef9cb1a76e2c812636a871f367e9edf4c5f3ce702b77f8","affectsGlobalScope":true,"impliedFormat":1},{"version":"27024f63b0e5b08252e533a8ac3d6a8a907dadc304c26ff24e504c2220811b93","affectsGlobalScope":true,"impliedFormat":1},{"version":"1bc101a1a3827312cd7b2b8dc5fede49b98320f44e43a0953ca45bd16c40f1c9","affectsGlobalScope":true,"impliedFormat":1},{"version":"a65dc671c802014353075c56043eb1a6160996f5a2dc775dbaecc5c3ca11175a","affectsGlobalScope":true,"impliedFormat":1},{"version":"bc81aff061c53a7140270555f4b22da4ecfe8601e8027cf5aa175fbdc7927c31","impliedFormat":1},{"version":"bae8d023ef6b23df7da26f51cea44321f95817c190342a36882e93b80d07a960","impliedFormat":1},{"version":"26a770cec4bd2e7dbba95c6e536390fffe83c6268b78974a93727903b515c4e7","impliedFormat":1}],"root":[[64,68]],"options":{"composite":true,"declaration":true,"declarationMap":true,"esModuleInterop":true,"module":99,"outDir":"./dist","rootDir":"./src","skipLibCheck":true,"sourceMap":true,"strict":true,"target":9},"referencedMap":[[71,1],[69,2],[74,3],[70,1],[72,4],[73,1],[180,5],[181,5],[182,2],[183,6],[184,2],[185,7],[186,8],[126,9],[127,9],[128,10],[80,11],[129,12],[130,13],[131,14],[75,2],[78,15],[76,2],[77,2],[132,16],[133,17],[134,18],[135,19],[136,20],[137,21],[138,21],[139,22],[140,23],[141,24],[142,25],[81,2],[79,2],[143,26],[144,27],[145,28],[179,29],[146,30],[147,2],[148,31],[149,32],[101,33],[111,34],[100,33],[121,35],[92,36],[91,37],[120,38],[114,39],[119,40],[94,41],[108,42],[93,43],[117,44],[89,45],[88,38],[118,46],[90,47],[95,48],[96,2],[99,48],[86,2],[122,49],[112,50],[103,51],[104,52],[106,53],[102,54],[105,55],[115,38],[97,56],[98,57],[107,58],[87,59],[110,50],[109,48],[113,2],[116,60],[150,61],[151,62],[152,63],[153,64],[154,65],[155,66],[156,67],[157,67],[158,68],[159,2],[160,69],[161,70],[163,71],[162,72],[164,73],[165,74],[166,75],[167,76],[168,77],[169,78],[170,79],[171,80],[172,81],[173,82],[174,83],[175,84],[176,85],[82,2],[83,2],[84,2],[123,86],[124,2],[125,2],[177,87],[178,88],[187,2],[191,89],[188,2],[190,90],[192,2],[193,2],[195,91],[194,2],[196,2],[198,92],[197,2],[199,93],[200,2],[201,94],[85,2],[189,2],[61,2],[62,2],[12,2],[10,2],[11,2],[16,2],[15,2],[2,2],[17,2],[18,2],[19,2],[20,2],[21,2],[22,2],[23,2],[24,2],[3,2],[25,2],[26,2],[4,2],[27,2],[31,2],[28,2],[29,2],[30,2],[32,2],[33,2],[34,2],[5,2],[35,2],[36,2],[37,2],[38,2],[6,2],[42,2],[39,2],[40,2],[41,2],[43,2],[7,2],[44,2],[49,2],[50,2],[45,2],[46,2],[47,2],[48,2],[8,2],[54,2],[51,2],[52,2],[53,2],[55,2],[9,2],[56,2],[63,2],[57,2],[58,2],[60,2],[59,2],[1,2],[14,2],[13,2],[64,2],[68,95],[67,96],[66,97],[65,2]],"latestChangedDtsFile":"./dist/index.d.ts","version":"5.9.3"} \ No newline at end of file diff --git a/railway.json b/railway.json index c87578a..ccc2676 100644 --- a/railway.json +++ b/railway.json @@ -2,10 +2,10 @@ "$schema": "https://railway.com/railway.schema.json", "build": { "builder": "NIXPACKS", - "buildCommand": "npm install && npm run build -w packages/shared && npm run build -w packages/backend" + "buildCommand": "npm install && npm run build -w packages/shared && npm run build -w packages/backend-nest" }, "deploy": { - "startCommand": "node packages/backend/dist/index.js", + "startCommand": "node --experimental-specifier-resolution=node packages/backend-nest/dist/main.js", "healthcheckPath": "/health", "restartPolicyType": "ON_FAILURE", "restartPolicyMaxRetries": 10 diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..4d89eb3 --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,8 @@ +{ + "files": [], + "references": [ + { "path": "./packages/shared" }, + { "path": "./packages/backend-nest" }, + { "path": "./packages/frontend" } + ] +}