bluewhale2025 commited on
Commit
148e8e7
·
1 Parent(s): c5d0166

Add AI Tree Portal

Browse files
.dockerignore ADDED
@@ -0,0 +1,11 @@
 
 
 
 
 
 
 
 
 
 
 
 
1
+ node_modules
2
+ .next
3
+ .git
4
+ .env*
5
+ *.md
6
+ .gitignore
7
+ .dockerignore
8
+ Dockerfile
9
+ *.log
10
+ dev.db
11
+ dev.db-journal
.gitignore CHANGED
@@ -3,12 +3,7 @@
3
  # dependencies
4
  /node_modules
5
  /.pnp
6
- .pnp.*
7
- .yarn/*
8
- !.yarn/patches
9
- !.yarn/plugins
10
- !.yarn/releases
11
- !.yarn/versions
12
 
13
  # testing
14
  /coverage
@@ -28,10 +23,9 @@
28
  npm-debug.log*
29
  yarn-debug.log*
30
  yarn-error.log*
31
- .pnpm-debug.log*
32
 
33
- # env files (can opt-in for committing if needed)
34
- .env*
35
 
36
  # vercel
37
  .vercel
 
3
  # dependencies
4
  /node_modules
5
  /.pnp
6
+ .pnp.js
 
 
 
 
 
7
 
8
  # testing
9
  /coverage
 
23
  npm-debug.log*
24
  yarn-debug.log*
25
  yarn-error.log*
 
26
 
27
+ # local env files
28
+ .env*.local
29
 
30
  # vercel
31
  .vercel
Dockerfile ADDED
@@ -0,0 +1,28 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ FROM node:18-slim
2
+
3
+ WORKDIR /app
4
+
5
+ # Install system dependencies
6
+ RUN apt-get update && apt-get install -y \
7
+ git \
8
+ sqlite3 \
9
+ && rm -rf /var/lib/apt/lists/*
10
+
11
+ # Install dependencies
12
+ COPY package*.json ./
13
+ RUN npm install
14
+
15
+ # Copy source code
16
+ COPY . .
17
+
18
+ # Generate Prisma client
19
+ RUN npx prisma generate
20
+
21
+ # Build the application
22
+ RUN npm run build
23
+
24
+ # Expose port
25
+ EXPOSE 3000
26
+
27
+ # Start the application
28
+ CMD ["npm", "start"]
README.md CHANGED
@@ -1,27 +1,50 @@
 
 
 
 
 
 
 
 
 
 
 
1
  # AI Tree Portal
2
 
3
- Next.js 기반의 마크다운 문서 관리 포털입니다.
4
 
5
  ## 주요 기능
6
 
7
- - ✍️ 마크다운 기반 문서 작성
8
- - 🏷️ 태그 및 카테고리 관리
9
- - 🔍 전체 텍스트 검색
10
- - 📊 JSON-LD 구조화 데이터 지원
11
- - 📝 Git 기반 버전 관리
12
- - 변경 이력 추적
13
- - 문서 버전 비교
14
- - 롤백 기능
 
 
 
 
 
 
 
 
 
 
 
15
 
16
  ## 기술 스택
17
 
18
- - **Frontend**: Next.js 15, React, TypeScript, Tailwind CSS
19
- - **Backend**: Next.js API Routes
20
- - **데이터 저장**: 마크다운 파일, Git
21
- - **검색**: 전체 텍스트 검색, 태그/카테고리 필터링
22
- - **SEO**: JSON-LD, 동적 메타데이터
 
23
 
24
- ## 설치 실행
25
 
26
  ```bash
27
  # 의존성 설치
@@ -29,29 +52,21 @@ npm install
29
 
30
  # 개발 서버 실행
31
  npm run dev
32
-
33
- # 프로덕션 빌드
34
- npm run build
35
- npm start
36
  ```
37
 
38
- ## 프로젝트 구조
39
 
 
 
 
40
  ```
41
- src/
42
- ├── app/ # Next.js 페이지 및 레이아웃
43
- ├── components/ # React 컴포넌트
44
- ├── lib/ # 유틸리티 함수
45
- └── types/ # TypeScript 타입 정의
46
 
47
- content/ # 마크다운 문서 저장소
48
- ```
49
-
50
- ## 라이선스
51
 
52
- MIT License
 
 
53
 
54
- ## 작성자
55
 
56
- - richard-kim-79
57
- - Email: [email protected]
 
1
+ ---
2
+ title: AI Tree Portal
3
+ emoji: 🌳
4
+ colorFrom: green
5
+ colorTo: blue
6
+ sdk: docker
7
+ app_port: 3000
8
+ pinned: false
9
+ license: mit
10
+ ---
11
+
12
  # AI Tree Portal
13
 
14
+ AI 관련 문서와 리소스를 관리하는 지식 포털입니다.
15
 
16
  ## 주요 기능
17
 
18
+ - 📝 마크다운 기반 문서 관리
19
+ - 실시간 미리보기
20
+ - 태그 카테고리 지원
21
+ - 버전 관리
22
+
23
+ - 🔍 검색 기능
24
+ - 전체 텍스트 검색
25
+ - 태그 기반 필터링
26
+ - 카테고리 탐색
27
+
28
+ - 🎯 인센티브 시스템
29
+ - 포인트 시스템
30
+ - 기여도 순위
31
+ - NFT 보상
32
+
33
+ - 🔄 실시간 업데이트
34
+ - WebSocket 기반 알림
35
+ - 실시간 협업
36
+ - 변경사항 추적
37
 
38
  ## 기술 스택
39
 
40
+ - Frontend: Next.js, React, TypeScript
41
+ - Backend: Next.js API Routes
42
+ - Database: SQLite, Prisma
43
+ - 검색: Meilisearch
44
+ - 실시간: WebSocket
45
+ - 스타일: Tailwind CSS
46
 
47
+ ## 로컬 실행 방법
48
 
49
  ```bash
50
  # 의존성 설치
 
52
 
53
  # 개발 서버 실행
54
  npm run dev
 
 
 
 
55
  ```
56
 
57
+ ## 환경 변수
58
 
59
+ ```env
60
+ DATABASE_URL="file:./dev.db"
61
+ NEXT_PUBLIC_API_URL="http://localhost:3000"
62
  ```
 
 
 
 
 
63
 
64
+ ## API 엔드포인트
 
 
 
65
 
66
+ - GraphQL: `/api/graphql`
67
+ - WebSocket: `ws://localhost:3000/api/graphql`
68
+ - REST API: `/api/v1/*`
69
 
70
+ ## 라이선스
71
 
72
+ MIT License
 
app.py ADDED
@@ -0,0 +1,21 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from gradio import Interface
2
+ import subprocess
3
+ import os
4
+
5
+ def start_nextjs():
6
+ # Next.js 서버 시작
7
+ subprocess.Popen(["npm", "start"])
8
+ return "Next.js 서버가 시작되었습니다. http://localhost:3000 에서 접속할 수 있습니다."
9
+
10
+ # Gradio 인터페이스 생성
11
+ iface = Interface(
12
+ fn=start_nextjs,
13
+ inputs=None,
14
+ outputs="text",
15
+ title="인센티브 시스템 데모",
16
+ description="Next.js 기반의 인센티브 시스템 데모입니다. GraphQL API와 WebSocket을 통해 실시간 업데이트를 제공합니다."
17
+ )
18
+
19
+ # 서버 시작
20
+ if __name__ == "__main__":
21
+ iface.launch()
incentive-system ADDED
@@ -0,0 +1 @@
 
 
1
+ Subproject commit 6c3d4edafda04e1e1d0cf633c8fa3f78db6e4a9e
package-lock.json CHANGED
@@ -8,16 +8,28 @@
8
  "name": "aitree_1",
9
  "version": "0.1.0",
10
  "dependencies": {
 
 
11
  "@tailwindcss/typography": "^0.5.16",
12
  "@types/lodash": "^4.17.16",
 
13
  "date-fns": "^4.1.0",
 
 
 
 
 
14
  "gray-matter": "^4.0.3",
15
  "lodash": "^4.17.21",
16
  "next": "15.2.4",
17
  "react": "^19.0.0",
18
  "react-dom": "^19.0.0",
19
  "remark": "^15.0.1",
20
- "remark-html": "^16.0.1"
 
 
 
 
21
  },
22
  "devDependencies": {
23
  "@eslint/eslintrc": "^3",
@@ -44,6 +56,49 @@
44
  "url": "https://github.com/sponsors/sindresorhus"
45
  }
46
  },
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
47
  "node_modules/@emnapi/core": {
48
  "version": "1.4.0",
49
  "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.4.0.tgz",
@@ -77,6 +132,47 @@
77
  "tslib": "^2.4.0"
78
  }
79
  },
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
80
  "node_modules/@eslint-community/eslint-utils": {
81
  "version": "4.5.1",
82
  "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.5.1.tgz",
@@ -228,6 +324,144 @@
228
  "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
229
  }
230
  },
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
231
  "node_modules/@humanfs/core": {
232
  "version": "0.19.1",
233
  "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.1.tgz",
@@ -816,7 +1050,6 @@
816
  "version": "2.1.5",
817
  "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz",
818
  "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==",
819
- "dev": true,
820
  "license": "MIT",
821
  "dependencies": {
822
  "@nodelib/fs.stat": "2.0.5",
@@ -830,7 +1063,6 @@
830
  "version": "2.0.5",
831
  "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz",
832
  "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==",
833
- "dev": true,
834
  "license": "MIT",
835
  "engines": {
836
  "node": ">= 8"
@@ -840,7 +1072,6 @@
840
  "version": "1.2.8",
841
  "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz",
842
  "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==",
843
- "dev": true,
844
  "license": "MIT",
845
  "dependencies": {
846
  "@nodelib/fs.scandir": "2.1.5",
@@ -860,6 +1091,12 @@
860
  "node": ">=12.4.0"
861
  }
862
  },
 
 
 
 
 
 
863
  "node_modules/@rtsao/scc": {
864
  "version": "1.1.0",
865
  "resolved": "https://registry.npmjs.org/@rtsao/scc/-/scc-1.1.0.tgz",
@@ -874,6 +1111,12 @@
874
  "dev": true,
875
  "license": "MIT"
876
  },
 
 
 
 
 
 
877
  "node_modules/@swc/counter": {
878
  "version": "0.1.3",
879
  "resolved": "https://registry.npmjs.org/@swc/counter/-/counter-0.1.3.tgz",
@@ -1152,6 +1395,15 @@
1152
  "tslib": "^2.4.0"
1153
  }
1154
  },
 
 
 
 
 
 
 
 
 
1155
  "node_modules/@types/debug": {
1156
  "version": "4.1.12",
1157
  "resolved": "https://registry.npmjs.org/@types/debug/-/debug-4.1.12.tgz",
@@ -1216,7 +1468,6 @@
1216
  "version": "20.17.30",
1217
  "resolved": "https://registry.npmjs.org/@types/node/-/node-20.17.30.tgz",
1218
  "integrity": "sha512-7zf4YyHA+jvBNfVrk2Gtvs6x7E8V+YDW05bNfG2XkWDJfYRXrTiP/DsB2zSYTaHX0bGIujTBQdMVAhb+j7mwpg==",
1219
- "dev": true,
1220
  "license": "MIT",
1221
  "dependencies": {
1222
  "undici-types": "~6.19.2"
@@ -1226,7 +1477,7 @@
1226
  "version": "19.1.0",
1227
  "resolved": "https://registry.npmjs.org/@types/react/-/react-19.1.0.tgz",
1228
  "integrity": "sha512-UaicktuQI+9UKyA4njtDOGBD/67t8YEBt2xdfqu8+gP9hqPUPsiXlNPcpS2gVdjmis5GKPG3fCxbQLVgxsQZ8w==",
1229
- "dev": true,
1230
  "license": "MIT",
1231
  "dependencies": {
1232
  "csstype": "^3.0.2"
@@ -1248,6 +1499,15 @@
1248
  "integrity": "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==",
1249
  "license": "MIT"
1250
  },
 
 
 
 
 
 
 
 
 
1251
  "node_modules/@typescript-eslint/eslint-plugin": {
1252
  "version": "8.29.0",
1253
  "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.29.0.tgz",
@@ -1703,6 +1963,152 @@
1703
  "win32"
1704
  ]
1705
  },
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1706
  "node_modules/acorn": {
1707
  "version": "8.14.1",
1708
  "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.14.1.tgz",
@@ -1814,6 +2220,15 @@
1814
  "url": "https://github.com/sponsors/ljharb"
1815
  }
1816
  },
 
 
 
 
 
 
 
 
 
1817
  "node_modules/array.prototype.findlast": {
1818
  "version": "1.2.5",
1819
  "resolved": "https://registry.npmjs.org/array.prototype.findlast/-/array.prototype.findlast-1.2.5.tgz",
@@ -2004,6 +2419,15 @@
2004
  "dev": true,
2005
  "license": "MIT"
2006
  },
 
 
 
 
 
 
 
 
 
2007
  "node_modules/brace-expansion": {
2008
  "version": "1.1.11",
2009
  "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
@@ -2019,7 +2443,6 @@
2019
  "version": "3.0.3",
2020
  "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz",
2021
  "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==",
2022
- "dev": true,
2023
  "license": "MIT",
2024
  "dependencies": {
2025
  "fill-range": "^7.1.1"
@@ -2244,6 +2667,40 @@
2244
  "dev": true,
2245
  "license": "MIT"
2246
  },
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2247
  "node_modules/cross-spawn": {
2248
  "version": "7.0.6",
2249
  "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz",
@@ -2275,7 +2732,7 @@
2275
  "version": "3.1.3",
2276
  "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz",
2277
  "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==",
2278
- "dev": true,
2279
  "license": "MIT"
2280
  },
2281
  "node_modules/damerau-levenshtein": {
@@ -2454,6 +2911,18 @@
2454
  "url": "https://github.com/sponsors/wooorm"
2455
  }
2456
  },
 
 
 
 
 
 
 
 
 
 
 
 
2457
  "node_modules/doctrine": {
2458
  "version": "2.1.0",
2459
  "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz",
@@ -2467,6 +2936,15 @@
2467
  "node": ">=0.10.0"
2468
  }
2469
  },
 
 
 
 
 
 
 
 
 
2470
  "node_modules/dunder-proto": {
2471
  "version": "1.0.1",
2472
  "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz",
@@ -2489,6 +2967,124 @@
2489
  "dev": true,
2490
  "license": "MIT"
2491
  },
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2492
  "node_modules/enhanced-resolve": {
2493
  "version": "5.18.1",
2494
  "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.18.1.tgz",
@@ -3158,7 +3754,6 @@
3158
  "version": "3.3.1",
3159
  "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.1.tgz",
3160
  "integrity": "sha512-kNFPyjhh5cKjrUltxs+wFx+ZkbRaxxmZ+X0ZU31SOsxCEtP9VPgtq2teZw1DebupL5GmDaNQ6yKMMVcM41iqDg==",
3161
- "dev": true,
3162
  "license": "MIT",
3163
  "dependencies": {
3164
  "@nodelib/fs.stat": "^2.0.2",
@@ -3175,7 +3770,6 @@
3175
  "version": "5.1.2",
3176
  "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz",
3177
  "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==",
3178
- "dev": true,
3179
  "license": "ISC",
3180
  "dependencies": {
3181
  "is-glob": "^4.0.1"
@@ -3202,7 +3796,6 @@
3202
  "version": "1.19.1",
3203
  "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.19.1.tgz",
3204
  "integrity": "sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ==",
3205
- "dev": true,
3206
  "license": "ISC",
3207
  "dependencies": {
3208
  "reusify": "^1.0.4"
@@ -3225,7 +3818,6 @@
3225
  "version": "7.1.1",
3226
  "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz",
3227
  "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==",
3228
- "dev": true,
3229
  "license": "MIT",
3230
  "dependencies": {
3231
  "to-regex-range": "^5.0.1"
@@ -3442,6 +4034,26 @@
3442
  "url": "https://github.com/sponsors/ljharb"
3443
  }
3444
  },
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
3445
  "node_modules/gopd": {
3446
  "version": "1.2.0",
3447
  "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz",
@@ -3449,26 +4061,132 @@
3449
  "dev": true,
3450
  "license": "MIT",
3451
  "engines": {
3452
- "node": ">= 0.4"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
3453
  },
3454
- "funding": {
3455
- "url": "https://github.com/sponsors/ljharb"
3456
  }
3457
  },
3458
- "node_modules/graceful-fs": {
3459
- "version": "4.2.11",
3460
- "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz",
3461
- "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==",
3462
- "dev": true,
3463
- "license": "ISC"
3464
- },
3465
- "node_modules/graphemer": {
3466
- "version": "1.4.0",
3467
- "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz",
3468
- "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==",
3469
- "dev": true,
3470
- "license": "MIT"
3471
- },
3472
  "node_modules/gray-matter": {
3473
  "version": "4.0.3",
3474
  "resolved": "https://registry.npmjs.org/gray-matter/-/gray-matter-4.0.3.tgz",
@@ -3651,6 +4369,16 @@
3651
  "url": "https://opencollective.com/unified"
3652
  }
3653
  },
 
 
 
 
 
 
 
 
 
 
3654
  "node_modules/html-void-elements": {
3655
  "version": "3.0.0",
3656
  "resolved": "https://registry.npmjs.org/html-void-elements/-/html-void-elements-3.0.0.tgz",
@@ -3665,7 +4393,6 @@
3665
  "version": "5.3.2",
3666
  "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz",
3667
  "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==",
3668
- "dev": true,
3669
  "license": "MIT",
3670
  "engines": {
3671
  "node": ">= 4"
@@ -3878,7 +4605,6 @@
3878
  "version": "2.1.1",
3879
  "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz",
3880
  "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==",
3881
- "dev": true,
3882
  "license": "MIT",
3883
  "engines": {
3884
  "node": ">=0.10.0"
@@ -3923,7 +4649,6 @@
3923
  "version": "4.0.3",
3924
  "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz",
3925
  "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==",
3926
- "dev": true,
3927
  "license": "MIT",
3928
  "dependencies": {
3929
  "is-extglob": "^2.1.1"
@@ -3949,7 +4674,6 @@
3949
  "version": "7.0.0",
3950
  "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz",
3951
  "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==",
3952
- "dev": true,
3953
  "license": "MIT",
3954
  "engines": {
3955
  "node": ">=0.12.0"
@@ -4175,7 +4899,7 @@
4175
  "version": "4.0.0",
4176
  "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
4177
  "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==",
4178
- "dev": true,
4179
  "license": "MIT"
4180
  },
4181
  "node_modules/js-yaml": {
@@ -4587,7 +5311,7 @@
4587
  "version": "1.4.0",
4588
  "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz",
4589
  "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==",
4590
- "dev": true,
4591
  "license": "MIT",
4592
  "dependencies": {
4593
  "js-tokens": "^3.0.0 || ^4.0.0"
@@ -4596,6 +5320,12 @@
4596
  "loose-envify": "cli.js"
4597
  }
4598
  },
 
 
 
 
 
 
4599
  "node_modules/math-intrinsics": {
4600
  "version": "1.1.0",
4601
  "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz",
@@ -4703,7 +5433,6 @@
4703
  "version": "1.4.1",
4704
  "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz",
4705
  "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==",
4706
- "dev": true,
4707
  "license": "MIT",
4708
  "engines": {
4709
  "node": ">= 8"
@@ -5155,7 +5884,6 @@
5155
  "version": "4.0.8",
5156
  "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz",
5157
  "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==",
5158
- "dev": true,
5159
  "license": "MIT",
5160
  "dependencies": {
5161
  "braces": "^3.0.3",
@@ -5165,6 +5893,27 @@
5165
  "node": ">=8.6"
5166
  }
5167
  },
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
5168
  "node_modules/minimatch": {
5169
  "version": "3.1.2",
5170
  "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
@@ -5219,6 +5968,15 @@
5219
  "dev": true,
5220
  "license": "MIT"
5221
  },
 
 
 
 
 
 
 
 
 
5222
  "node_modules/next": {
5223
  "version": "15.2.4",
5224
  "resolved": "https://registry.npmjs.org/next/-/next-15.2.4.tgz",
@@ -5301,11 +6059,22 @@
5301
  "node": "^10 || ^12 || >=14"
5302
  }
5303
  },
 
 
 
 
 
 
 
 
 
 
 
 
5304
  "node_modules/object-assign": {
5305
  "version": "4.1.1",
5306
  "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
5307
  "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==",
5308
- "dev": true,
5309
  "license": "MIT",
5310
  "engines": {
5311
  "node": ">=0.10.0"
@@ -5424,6 +6193,19 @@
5424
  "url": "https://github.com/sponsors/ljharb"
5425
  }
5426
  },
 
 
 
 
 
 
 
 
 
 
 
 
 
5427
  "node_modules/optionator": {
5428
  "version": "0.9.4",
5429
  "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz",
@@ -5532,6 +6314,15 @@
5532
  "dev": true,
5533
  "license": "MIT"
5534
  },
 
 
 
 
 
 
 
 
 
5535
  "node_modules/picocolors": {
5536
  "version": "1.1.1",
5537
  "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz",
@@ -5542,7 +6333,6 @@
5542
  "version": "2.3.1",
5543
  "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz",
5544
  "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==",
5545
- "dev": true,
5546
  "license": "MIT",
5547
  "engines": {
5548
  "node": ">=8.6"
@@ -5617,7 +6407,7 @@
5617
  "version": "15.8.1",
5618
  "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz",
5619
  "integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==",
5620
- "dev": true,
5621
  "license": "MIT",
5622
  "dependencies": {
5623
  "loose-envify": "^1.4.0",
@@ -5649,7 +6439,6 @@
5649
  "version": "1.2.3",
5650
  "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz",
5651
  "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==",
5652
- "dev": true,
5653
  "funding": [
5654
  {
5655
  "type": "github",
@@ -5691,7 +6480,7 @@
5691
  "version": "16.13.1",
5692
  "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz",
5693
  "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==",
5694
- "dev": true,
5695
  "license": "MIT"
5696
  },
5697
  "node_modules/reflect.getprototypeof": {
@@ -5738,6 +6527,25 @@
5738
  "url": "https://github.com/sponsors/ljharb"
5739
  }
5740
  },
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
5741
  "node_modules/remark": {
5742
  "version": "15.0.1",
5743
  "resolved": "https://registry.npmjs.org/remark/-/remark-15.0.1.tgz",
@@ -5802,6 +6610,12 @@
5802
  "url": "https://opencollective.com/unified"
5803
  }
5804
  },
 
 
 
 
 
 
5805
  "node_modules/resolve": {
5806
  "version": "1.22.10",
5807
  "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.10.tgz",
@@ -5847,7 +6661,6 @@
5847
  "version": "1.1.0",
5848
  "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.1.0.tgz",
5849
  "integrity": "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==",
5850
- "dev": true,
5851
  "license": "MIT",
5852
  "engines": {
5853
  "iojs": ">=1.0.0",
@@ -5858,7 +6671,6 @@
5858
  "version": "1.2.0",
5859
  "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz",
5860
  "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==",
5861
- "dev": true,
5862
  "funding": [
5863
  {
5864
  "type": "github",
@@ -6163,6 +6975,160 @@
6163
  "is-arrayish": "^0.3.1"
6164
  }
6165
  },
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
6166
  "node_modules/source-map-js": {
6167
  "version": "1.2.1",
6168
  "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz",
@@ -6411,6 +7377,16 @@
6411
  "url": "https://github.com/sponsors/ljharb"
6412
  }
6413
  },
 
 
 
 
 
 
 
 
 
 
6414
  "node_modules/tailwindcss": {
6415
  "version": "4.1.3",
6416
  "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.1.3.tgz",
@@ -6476,7 +7452,6 @@
6476
  "version": "5.0.1",
6477
  "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz",
6478
  "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==",
6479
- "dev": true,
6480
  "license": "MIT",
6481
  "dependencies": {
6482
  "is-number": "^7.0.0"
@@ -6518,6 +7493,19 @@
6518
  "typescript": ">=4.8.4"
6519
  }
6520
  },
 
 
 
 
 
 
 
 
 
 
 
 
 
6521
  "node_modules/tsconfig-paths": {
6522
  "version": "3.15.0",
6523
  "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.15.0.tgz",
@@ -6665,7 +7653,6 @@
6665
  "version": "6.19.8",
6666
  "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.19.8.tgz",
6667
  "integrity": "sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw==",
6668
- "dev": true,
6669
  "license": "MIT"
6670
  },
6671
  "node_modules/unified": {
@@ -6755,6 +7742,18 @@
6755
  "url": "https://opencollective.com/unified"
6756
  }
6757
  },
 
 
 
 
 
 
 
 
 
 
 
 
6758
  "node_modules/unrs-resolver": {
6759
  "version": "1.3.3",
6760
  "resolved": "https://registry.npmjs.org/unrs-resolver/-/unrs-resolver-1.3.3.tgz",
@@ -6792,12 +7791,27 @@
6792
  "punycode": "^2.1.0"
6793
  }
6794
  },
 
 
 
 
 
 
6795
  "node_modules/util-deprecate": {
6796
  "version": "1.0.2",
6797
  "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
6798
  "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==",
6799
  "license": "MIT"
6800
  },
 
 
 
 
 
 
 
 
 
6801
  "node_modules/vfile": {
6802
  "version": "6.0.3",
6803
  "resolved": "https://registry.npmjs.org/vfile/-/vfile-6.0.3.tgz",
@@ -6941,6 +7955,35 @@
6941
  "node": ">=0.10.0"
6942
  }
6943
  },
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
6944
  "node_modules/yocto-queue": {
6945
  "version": "0.1.0",
6946
  "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz",
@@ -6954,6 +7997,32 @@
6954
  "url": "https://github.com/sponsors/sindresorhus"
6955
  }
6956
  },
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
6957
  "node_modules/zwitch": {
6958
  "version": "2.0.4",
6959
  "resolved": "https://registry.npmjs.org/zwitch/-/zwitch-2.0.4.tgz",
 
8
  "name": "aitree_1",
9
  "version": "0.1.0",
10
  "dependencies": {
11
+ "@graphql-tools/load-files": "^7.0.1",
12
+ "@graphql-tools/schema": "^10.0.23",
13
  "@tailwindcss/typography": "^0.5.16",
14
  "@types/lodash": "^4.17.16",
15
+ "@types/ws": "^8.18.1",
16
  "date-fns": "^4.1.0",
17
+ "graphql": "^16.10.0",
18
+ "graphql-subscriptions": "^3.0.0",
19
+ "graphql-tools": "^9.0.18",
20
+ "graphql-ws": "^6.0.4",
21
+ "graphql-yoga": "^5.13.2",
22
  "gray-matter": "^4.0.3",
23
  "lodash": "^4.17.21",
24
  "next": "15.2.4",
25
  "react": "^19.0.0",
26
  "react-dom": "^19.0.0",
27
  "remark": "^15.0.1",
28
+ "remark-html": "^16.0.1",
29
+ "socket.io": "^4.8.1",
30
+ "socket.io-client": "^4.8.1",
31
+ "ws": "^8.18.1",
32
+ "zod": "^3.24.2"
33
  },
34
  "devDependencies": {
35
  "@eslint/eslintrc": "^3",
 
56
  "url": "https://github.com/sponsors/sindresorhus"
57
  }
58
  },
59
+ "node_modules/@apollo/client": {
60
+ "version": "3.13.6",
61
+ "resolved": "https://registry.npmjs.org/@apollo/client/-/client-3.13.6.tgz",
62
+ "integrity": "sha512-G6A8uNb13V/Tv4TJQOs5PnxuE5Rf5D2dMnBQcg9mng1Eo4YBecwFEJ0L022mraq/dLB0jD5tiAESOD2bTyJ6gg==",
63
+ "license": "MIT",
64
+ "optional": true,
65
+ "dependencies": {
66
+ "@graphql-typed-document-node/core": "^3.1.1",
67
+ "@wry/caches": "^1.0.0",
68
+ "@wry/equality": "^0.5.6",
69
+ "@wry/trie": "^0.5.0",
70
+ "graphql-tag": "^2.12.6",
71
+ "hoist-non-react-statics": "^3.3.2",
72
+ "optimism": "^0.18.0",
73
+ "prop-types": "^15.7.2",
74
+ "rehackt": "^0.1.0",
75
+ "symbol-observable": "^4.0.0",
76
+ "ts-invariant": "^0.10.3",
77
+ "tslib": "^2.3.0",
78
+ "zen-observable-ts": "^1.2.5"
79
+ },
80
+ "peerDependencies": {
81
+ "graphql": "^15.0.0 || ^16.0.0",
82
+ "graphql-ws": "^5.5.5 || ^6.0.3",
83
+ "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || >=19.0.0-rc",
84
+ "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0 || >=19.0.0-rc",
85
+ "subscriptions-transport-ws": "^0.9.0 || ^0.11.0"
86
+ },
87
+ "peerDependenciesMeta": {
88
+ "graphql-ws": {
89
+ "optional": true
90
+ },
91
+ "react": {
92
+ "optional": true
93
+ },
94
+ "react-dom": {
95
+ "optional": true
96
+ },
97
+ "subscriptions-transport-ws": {
98
+ "optional": true
99
+ }
100
+ }
101
+ },
102
  "node_modules/@emnapi/core": {
103
  "version": "1.4.0",
104
  "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.4.0.tgz",
 
132
  "tslib": "^2.4.0"
133
  }
134
  },
135
+ "node_modules/@envelop/core": {
136
+ "version": "5.2.3",
137
+ "resolved": "https://registry.npmjs.org/@envelop/core/-/core-5.2.3.tgz",
138
+ "integrity": "sha512-KfoGlYD/XXQSc3BkM1/k15+JQbkQ4ateHazeZoWl9P71FsLTDXSjGy6j7QqfhpIDSbxNISqhPMfZHYSbDFOofQ==",
139
+ "license": "MIT",
140
+ "dependencies": {
141
+ "@envelop/instrumentation": "^1.0.0",
142
+ "@envelop/types": "^5.2.1",
143
+ "@whatwg-node/promise-helpers": "^1.2.4",
144
+ "tslib": "^2.5.0"
145
+ },
146
+ "engines": {
147
+ "node": ">=18.0.0"
148
+ }
149
+ },
150
+ "node_modules/@envelop/instrumentation": {
151
+ "version": "1.0.0",
152
+ "resolved": "https://registry.npmjs.org/@envelop/instrumentation/-/instrumentation-1.0.0.tgz",
153
+ "integrity": "sha512-cxgkB66RQB95H3X27jlnxCRNTmPuSTgmBAq6/4n2Dtv4hsk4yz8FadA1ggmd0uZzvKqWD6CR+WFgTjhDqg7eyw==",
154
+ "license": "MIT",
155
+ "dependencies": {
156
+ "@whatwg-node/promise-helpers": "^1.2.1",
157
+ "tslib": "^2.5.0"
158
+ },
159
+ "engines": {
160
+ "node": ">=18.0.0"
161
+ }
162
+ },
163
+ "node_modules/@envelop/types": {
164
+ "version": "5.2.1",
165
+ "resolved": "https://registry.npmjs.org/@envelop/types/-/types-5.2.1.tgz",
166
+ "integrity": "sha512-CsFmA3u3c2QoLDTfEpGr4t25fjMU31nyvse7IzWTvb0ZycuPjMjb0fjlheh+PbhBYb9YLugnT2uY6Mwcg1o+Zg==",
167
+ "license": "MIT",
168
+ "dependencies": {
169
+ "@whatwg-node/promise-helpers": "^1.0.0",
170
+ "tslib": "^2.5.0"
171
+ },
172
+ "engines": {
173
+ "node": ">=18.0.0"
174
+ }
175
+ },
176
  "node_modules/@eslint-community/eslint-utils": {
177
  "version": "4.5.1",
178
  "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.5.1.tgz",
 
324
  "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
325
  }
326
  },
327
+ "node_modules/@graphql-tools/executor": {
328
+ "version": "1.4.7",
329
+ "resolved": "https://registry.npmjs.org/@graphql-tools/executor/-/executor-1.4.7.tgz",
330
+ "integrity": "sha512-U0nK9jzJRP9/9Izf1+0Gggd6K6RNRsheFo1gC/VWzfnsr0qjcOSS9qTjY0OTC5iTPt4tQ+W5Zpw/uc7mebI6aA==",
331
+ "license": "MIT",
332
+ "dependencies": {
333
+ "@graphql-tools/utils": "^10.8.6",
334
+ "@graphql-typed-document-node/core": "^3.2.0",
335
+ "@repeaterjs/repeater": "^3.0.4",
336
+ "@whatwg-node/disposablestack": "^0.0.6",
337
+ "@whatwg-node/promise-helpers": "^1.0.0",
338
+ "tslib": "^2.4.0"
339
+ },
340
+ "engines": {
341
+ "node": ">=16.0.0"
342
+ },
343
+ "peerDependencies": {
344
+ "graphql": "^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0"
345
+ }
346
+ },
347
+ "node_modules/@graphql-tools/load-files": {
348
+ "version": "7.0.1",
349
+ "resolved": "https://registry.npmjs.org/@graphql-tools/load-files/-/load-files-7.0.1.tgz",
350
+ "integrity": "sha512-oTNIENc9To9u8Gc3kY82C74caW6kXa8ya2GyxWRXp8gP4zK/7PmvlWJK0/GFCUH0cU3t9jM7k59zXz1+ZfP3Mw==",
351
+ "license": "MIT",
352
+ "dependencies": {
353
+ "globby": "11.1.0",
354
+ "tslib": "^2.4.0",
355
+ "unixify": "^1.0.0"
356
+ },
357
+ "engines": {
358
+ "node": ">=16.0.0"
359
+ },
360
+ "peerDependencies": {
361
+ "graphql": "^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0"
362
+ }
363
+ },
364
+ "node_modules/@graphql-tools/merge": {
365
+ "version": "9.0.24",
366
+ "resolved": "https://registry.npmjs.org/@graphql-tools/merge/-/merge-9.0.24.tgz",
367
+ "integrity": "sha512-NzWx/Afl/1qHT3Nm1bghGG2l4jub28AdvtG11PoUlmjcIjnFBJMv4vqL0qnxWe8A82peWo4/TkVdjJRLXwgGEw==",
368
+ "license": "MIT",
369
+ "dependencies": {
370
+ "@graphql-tools/utils": "^10.8.6",
371
+ "tslib": "^2.4.0"
372
+ },
373
+ "engines": {
374
+ "node": ">=16.0.0"
375
+ },
376
+ "peerDependencies": {
377
+ "graphql": "^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0"
378
+ }
379
+ },
380
+ "node_modules/@graphql-tools/schema": {
381
+ "version": "10.0.23",
382
+ "resolved": "https://registry.npmjs.org/@graphql-tools/schema/-/schema-10.0.23.tgz",
383
+ "integrity": "sha512-aEGVpd1PCuGEwqTXCStpEkmheTHNdMayiIKH1xDWqYp9i8yKv9FRDgkGrY4RD8TNxnf7iII+6KOBGaJ3ygH95A==",
384
+ "license": "MIT",
385
+ "dependencies": {
386
+ "@graphql-tools/merge": "^9.0.24",
387
+ "@graphql-tools/utils": "^10.8.6",
388
+ "tslib": "^2.4.0"
389
+ },
390
+ "engines": {
391
+ "node": ">=16.0.0"
392
+ },
393
+ "peerDependencies": {
394
+ "graphql": "^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0"
395
+ }
396
+ },
397
+ "node_modules/@graphql-tools/utils": {
398
+ "version": "10.8.6",
399
+ "resolved": "https://registry.npmjs.org/@graphql-tools/utils/-/utils-10.8.6.tgz",
400
+ "integrity": "sha512-Alc9Vyg0oOsGhRapfL3xvqh1zV8nKoFUdtLhXX7Ki4nClaIJXckrA86j+uxEuG3ic6j4jlM1nvcWXRn/71AVLQ==",
401
+ "license": "MIT",
402
+ "dependencies": {
403
+ "@graphql-typed-document-node/core": "^3.1.1",
404
+ "@whatwg-node/promise-helpers": "^1.0.0",
405
+ "cross-inspect": "1.0.1",
406
+ "dset": "^3.1.4",
407
+ "tslib": "^2.4.0"
408
+ },
409
+ "engines": {
410
+ "node": ">=16.0.0"
411
+ },
412
+ "peerDependencies": {
413
+ "graphql": "^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0"
414
+ }
415
+ },
416
+ "node_modules/@graphql-typed-document-node/core": {
417
+ "version": "3.2.0",
418
+ "resolved": "https://registry.npmjs.org/@graphql-typed-document-node/core/-/core-3.2.0.tgz",
419
+ "integrity": "sha512-mB9oAsNCm9aM3/SOv4YtBMqZbYj10R7dkq8byBqxGY/ncFwhf2oQzMV+LCRlWoDSEBJ3COiR1yeDvMtsoOsuFQ==",
420
+ "license": "MIT",
421
+ "peerDependencies": {
422
+ "graphql": "^0.8.0 || ^0.9.0 || ^0.10.0 || ^0.11.0 || ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0"
423
+ }
424
+ },
425
+ "node_modules/@graphql-yoga/logger": {
426
+ "version": "2.0.1",
427
+ "resolved": "https://registry.npmjs.org/@graphql-yoga/logger/-/logger-2.0.1.tgz",
428
+ "integrity": "sha512-Nv0BoDGLMg9QBKy9cIswQ3/6aKaKjlTh87x3GiBg2Z4RrjyrM48DvOOK0pJh1C1At+b0mUIM67cwZcFTDLN4sA==",
429
+ "license": "MIT",
430
+ "dependencies": {
431
+ "tslib": "^2.8.1"
432
+ },
433
+ "engines": {
434
+ "node": ">=18.0.0"
435
+ }
436
+ },
437
+ "node_modules/@graphql-yoga/subscription": {
438
+ "version": "5.0.3",
439
+ "resolved": "https://registry.npmjs.org/@graphql-yoga/subscription/-/subscription-5.0.3.tgz",
440
+ "integrity": "sha512-xLGEataxCULjL9rlTCVFL1IW2E90TnDIL+mE4lZBi9X92jc6s+Q+jk6Ax3I98kryCJh0UMiwjit7CaIIczTiJg==",
441
+ "license": "MIT",
442
+ "dependencies": {
443
+ "@graphql-yoga/typed-event-target": "^3.0.2",
444
+ "@repeaterjs/repeater": "^3.0.4",
445
+ "@whatwg-node/events": "^0.1.0",
446
+ "tslib": "^2.8.1"
447
+ },
448
+ "engines": {
449
+ "node": ">=18.0.0"
450
+ }
451
+ },
452
+ "node_modules/@graphql-yoga/typed-event-target": {
453
+ "version": "3.0.2",
454
+ "resolved": "https://registry.npmjs.org/@graphql-yoga/typed-event-target/-/typed-event-target-3.0.2.tgz",
455
+ "integrity": "sha512-ZpJxMqB+Qfe3rp6uszCQoag4nSw42icURnBRfFYSOmTgEeOe4rD0vYlbA8spvCu2TlCesNTlEN9BLWtQqLxabA==",
456
+ "license": "MIT",
457
+ "dependencies": {
458
+ "@repeaterjs/repeater": "^3.0.4",
459
+ "tslib": "^2.8.1"
460
+ },
461
+ "engines": {
462
+ "node": ">=18.0.0"
463
+ }
464
+ },
465
  "node_modules/@humanfs/core": {
466
  "version": "0.19.1",
467
  "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.1.tgz",
 
1050
  "version": "2.1.5",
1051
  "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz",
1052
  "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==",
 
1053
  "license": "MIT",
1054
  "dependencies": {
1055
  "@nodelib/fs.stat": "2.0.5",
 
1063
  "version": "2.0.5",
1064
  "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz",
1065
  "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==",
 
1066
  "license": "MIT",
1067
  "engines": {
1068
  "node": ">= 8"
 
1072
  "version": "1.2.8",
1073
  "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz",
1074
  "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==",
 
1075
  "license": "MIT",
1076
  "dependencies": {
1077
  "@nodelib/fs.scandir": "2.1.5",
 
1091
  "node": ">=12.4.0"
1092
  }
1093
  },
1094
+ "node_modules/@repeaterjs/repeater": {
1095
+ "version": "3.0.6",
1096
+ "resolved": "https://registry.npmjs.org/@repeaterjs/repeater/-/repeater-3.0.6.tgz",
1097
+ "integrity": "sha512-Javneu5lsuhwNCryN+pXH93VPQ8g0dBX7wItHFgYiwQmzE1sVdg5tWHiOgHywzL2W21XQopa7IwIEnNbmeUJYA==",
1098
+ "license": "MIT"
1099
+ },
1100
  "node_modules/@rtsao/scc": {
1101
  "version": "1.1.0",
1102
  "resolved": "https://registry.npmjs.org/@rtsao/scc/-/scc-1.1.0.tgz",
 
1111
  "dev": true,
1112
  "license": "MIT"
1113
  },
1114
+ "node_modules/@socket.io/component-emitter": {
1115
+ "version": "3.1.2",
1116
+ "resolved": "https://registry.npmjs.org/@socket.io/component-emitter/-/component-emitter-3.1.2.tgz",
1117
+ "integrity": "sha512-9BCxFwvbGg/RsZK9tjXd8s4UcwR0MWeFQ1XEKIQVVvAGJyINdrqKMcTRyLoK8Rse1GjzLV9cwjWV1olXRWEXVA==",
1118
+ "license": "MIT"
1119
+ },
1120
  "node_modules/@swc/counter": {
1121
  "version": "0.1.3",
1122
  "resolved": "https://registry.npmjs.org/@swc/counter/-/counter-0.1.3.tgz",
 
1395
  "tslib": "^2.4.0"
1396
  }
1397
  },
1398
+ "node_modules/@types/cors": {
1399
+ "version": "2.8.17",
1400
+ "resolved": "https://registry.npmjs.org/@types/cors/-/cors-2.8.17.tgz",
1401
+ "integrity": "sha512-8CGDvrBj1zgo2qE+oS3pOCyYNqCPryMWY2bGfwA0dcfopWGgxs+78df0Rs3rc9THP4JkOhLsAa+15VdpAqkcUA==",
1402
+ "license": "MIT",
1403
+ "dependencies": {
1404
+ "@types/node": "*"
1405
+ }
1406
+ },
1407
  "node_modules/@types/debug": {
1408
  "version": "4.1.12",
1409
  "resolved": "https://registry.npmjs.org/@types/debug/-/debug-4.1.12.tgz",
 
1468
  "version": "20.17.30",
1469
  "resolved": "https://registry.npmjs.org/@types/node/-/node-20.17.30.tgz",
1470
  "integrity": "sha512-7zf4YyHA+jvBNfVrk2Gtvs6x7E8V+YDW05bNfG2XkWDJfYRXrTiP/DsB2zSYTaHX0bGIujTBQdMVAhb+j7mwpg==",
 
1471
  "license": "MIT",
1472
  "dependencies": {
1473
  "undici-types": "~6.19.2"
 
1477
  "version": "19.1.0",
1478
  "resolved": "https://registry.npmjs.org/@types/react/-/react-19.1.0.tgz",
1479
  "integrity": "sha512-UaicktuQI+9UKyA4njtDOGBD/67t8YEBt2xdfqu8+gP9hqPUPsiXlNPcpS2gVdjmis5GKPG3fCxbQLVgxsQZ8w==",
1480
+ "devOptional": true,
1481
  "license": "MIT",
1482
  "dependencies": {
1483
  "csstype": "^3.0.2"
 
1499
  "integrity": "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==",
1500
  "license": "MIT"
1501
  },
1502
+ "node_modules/@types/ws": {
1503
+ "version": "8.18.1",
1504
+ "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.18.1.tgz",
1505
+ "integrity": "sha512-ThVF6DCVhA8kUGy+aazFQ4kXQ7E1Ty7A3ypFOe0IcJV8O/M511G99AW24irKrW56Wt44yG9+ij8FaqoBGkuBXg==",
1506
+ "license": "MIT",
1507
+ "dependencies": {
1508
+ "@types/node": "*"
1509
+ }
1510
+ },
1511
  "node_modules/@typescript-eslint/eslint-plugin": {
1512
  "version": "8.29.0",
1513
  "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.29.0.tgz",
 
1963
  "win32"
1964
  ]
1965
  },
1966
+ "node_modules/@whatwg-node/disposablestack": {
1967
+ "version": "0.0.6",
1968
+ "resolved": "https://registry.npmjs.org/@whatwg-node/disposablestack/-/disposablestack-0.0.6.tgz",
1969
+ "integrity": "sha512-LOtTn+JgJvX8WfBVJtF08TGrdjuFzGJc4mkP8EdDI8ADbvO7kiexYep1o8dwnt0okb0jYclCDXF13xU7Ge4zSw==",
1970
+ "license": "MIT",
1971
+ "dependencies": {
1972
+ "@whatwg-node/promise-helpers": "^1.0.0",
1973
+ "tslib": "^2.6.3"
1974
+ },
1975
+ "engines": {
1976
+ "node": ">=18.0.0"
1977
+ }
1978
+ },
1979
+ "node_modules/@whatwg-node/events": {
1980
+ "version": "0.1.2",
1981
+ "resolved": "https://registry.npmjs.org/@whatwg-node/events/-/events-0.1.2.tgz",
1982
+ "integrity": "sha512-ApcWxkrs1WmEMS2CaLLFUEem/49erT3sxIVjpzU5f6zmVcnijtDSrhoK2zVobOIikZJdH63jdAXOrvjf6eOUNQ==",
1983
+ "license": "MIT",
1984
+ "dependencies": {
1985
+ "tslib": "^2.6.3"
1986
+ },
1987
+ "engines": {
1988
+ "node": ">=18.0.0"
1989
+ }
1990
+ },
1991
+ "node_modules/@whatwg-node/fetch": {
1992
+ "version": "0.10.5",
1993
+ "resolved": "https://registry.npmjs.org/@whatwg-node/fetch/-/fetch-0.10.5.tgz",
1994
+ "integrity": "sha512-+yFJU3hmXPAHJULwx0VzCIsvr/H0lvbPvbOH3areOH3NAuCxCwaJsQ8w6/MwwMcvEWIynSsmAxoyaH04KeosPg==",
1995
+ "license": "MIT",
1996
+ "dependencies": {
1997
+ "@whatwg-node/node-fetch": "^0.7.11",
1998
+ "urlpattern-polyfill": "^10.0.0"
1999
+ },
2000
+ "engines": {
2001
+ "node": ">=18.0.0"
2002
+ }
2003
+ },
2004
+ "node_modules/@whatwg-node/node-fetch": {
2005
+ "version": "0.7.17",
2006
+ "resolved": "https://registry.npmjs.org/@whatwg-node/node-fetch/-/node-fetch-0.7.17.tgz",
2007
+ "integrity": "sha512-Ni8A2H/r6brNf4u8Y7ATxmWUD0xltsQ6a4NnjWSbw4PgaT34CbY+u4QtVsFj9pTC3dBKJADKjac3AieAig+PZA==",
2008
+ "license": "MIT",
2009
+ "dependencies": {
2010
+ "@whatwg-node/disposablestack": "^0.0.6",
2011
+ "@whatwg-node/promise-helpers": "^1.2.5",
2012
+ "busboy": "^1.6.0",
2013
+ "tslib": "^2.6.3"
2014
+ },
2015
+ "engines": {
2016
+ "node": ">=18.0.0"
2017
+ }
2018
+ },
2019
+ "node_modules/@whatwg-node/promise-helpers": {
2020
+ "version": "1.3.0",
2021
+ "resolved": "https://registry.npmjs.org/@whatwg-node/promise-helpers/-/promise-helpers-1.3.0.tgz",
2022
+ "integrity": "sha512-486CouizxHXucj8Ky153DDragfkMcHtVEToF5Pn/fInhUUSiCmt9Q4JVBa6UK5q4RammFBtGQ4C9qhGlXU9YbA==",
2023
+ "license": "MIT",
2024
+ "dependencies": {
2025
+ "tslib": "^2.6.3"
2026
+ },
2027
+ "engines": {
2028
+ "node": ">=16.0.0"
2029
+ }
2030
+ },
2031
+ "node_modules/@whatwg-node/server": {
2032
+ "version": "0.10.3",
2033
+ "resolved": "https://registry.npmjs.org/@whatwg-node/server/-/server-0.10.3.tgz",
2034
+ "integrity": "sha512-2Dnfey57vWR+hDUMjPhNzJQc9z116BBzSQwR9eD0vhnzYmN2rJXuY0QuMaHDCCqEZRmFHg2bo8iJ+/1uHOlxpg==",
2035
+ "license": "MIT",
2036
+ "dependencies": {
2037
+ "@envelop/instrumentation": "^1.0.0",
2038
+ "@whatwg-node/disposablestack": "^0.0.6",
2039
+ "@whatwg-node/fetch": "^0.10.5",
2040
+ "@whatwg-node/promise-helpers": "^1.2.3",
2041
+ "tslib": "^2.6.3"
2042
+ },
2043
+ "engines": {
2044
+ "node": ">=18.0.0"
2045
+ }
2046
+ },
2047
+ "node_modules/@wry/caches": {
2048
+ "version": "1.0.1",
2049
+ "resolved": "https://registry.npmjs.org/@wry/caches/-/caches-1.0.1.tgz",
2050
+ "integrity": "sha512-bXuaUNLVVkD20wcGBWRyo7j9N3TxePEWFZj2Y+r9OoUzfqmavM84+mFykRicNsBqatba5JLay1t48wxaXaWnlA==",
2051
+ "license": "MIT",
2052
+ "optional": true,
2053
+ "dependencies": {
2054
+ "tslib": "^2.3.0"
2055
+ },
2056
+ "engines": {
2057
+ "node": ">=8"
2058
+ }
2059
+ },
2060
+ "node_modules/@wry/context": {
2061
+ "version": "0.7.4",
2062
+ "resolved": "https://registry.npmjs.org/@wry/context/-/context-0.7.4.tgz",
2063
+ "integrity": "sha512-jmT7Sb4ZQWI5iyu3lobQxICu2nC/vbUhP0vIdd6tHC9PTfenmRmuIFqktc6GH9cgi+ZHnsLWPvfSvc4DrYmKiQ==",
2064
+ "license": "MIT",
2065
+ "optional": true,
2066
+ "dependencies": {
2067
+ "tslib": "^2.3.0"
2068
+ },
2069
+ "engines": {
2070
+ "node": ">=8"
2071
+ }
2072
+ },
2073
+ "node_modules/@wry/equality": {
2074
+ "version": "0.5.7",
2075
+ "resolved": "https://registry.npmjs.org/@wry/equality/-/equality-0.5.7.tgz",
2076
+ "integrity": "sha512-BRFORjsTuQv5gxcXsuDXx6oGRhuVsEGwZy6LOzRRfgu+eSfxbhUQ9L9YtSEIuIjY/o7g3iWFjrc5eSY1GXP2Dw==",
2077
+ "license": "MIT",
2078
+ "optional": true,
2079
+ "dependencies": {
2080
+ "tslib": "^2.3.0"
2081
+ },
2082
+ "engines": {
2083
+ "node": ">=8"
2084
+ }
2085
+ },
2086
+ "node_modules/@wry/trie": {
2087
+ "version": "0.5.0",
2088
+ "resolved": "https://registry.npmjs.org/@wry/trie/-/trie-0.5.0.tgz",
2089
+ "integrity": "sha512-FNoYzHawTMk/6KMQoEG5O4PuioX19UbwdQKF44yw0nLfOypfQdjtfZzo/UIJWAJ23sNIFbD1Ug9lbaDGMwbqQA==",
2090
+ "license": "MIT",
2091
+ "optional": true,
2092
+ "dependencies": {
2093
+ "tslib": "^2.3.0"
2094
+ },
2095
+ "engines": {
2096
+ "node": ">=8"
2097
+ }
2098
+ },
2099
+ "node_modules/accepts": {
2100
+ "version": "1.3.8",
2101
+ "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz",
2102
+ "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==",
2103
+ "license": "MIT",
2104
+ "dependencies": {
2105
+ "mime-types": "~2.1.34",
2106
+ "negotiator": "0.6.3"
2107
+ },
2108
+ "engines": {
2109
+ "node": ">= 0.6"
2110
+ }
2111
+ },
2112
  "node_modules/acorn": {
2113
  "version": "8.14.1",
2114
  "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.14.1.tgz",
 
2220
  "url": "https://github.com/sponsors/ljharb"
2221
  }
2222
  },
2223
+ "node_modules/array-union": {
2224
+ "version": "2.1.0",
2225
+ "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz",
2226
+ "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==",
2227
+ "license": "MIT",
2228
+ "engines": {
2229
+ "node": ">=8"
2230
+ }
2231
+ },
2232
  "node_modules/array.prototype.findlast": {
2233
  "version": "1.2.5",
2234
  "resolved": "https://registry.npmjs.org/array.prototype.findlast/-/array.prototype.findlast-1.2.5.tgz",
 
2419
  "dev": true,
2420
  "license": "MIT"
2421
  },
2422
+ "node_modules/base64id": {
2423
+ "version": "2.0.0",
2424
+ "resolved": "https://registry.npmjs.org/base64id/-/base64id-2.0.0.tgz",
2425
+ "integrity": "sha512-lGe34o6EHj9y3Kts9R4ZYs/Gr+6N7MCaMlIFA3F1R2O5/m7K06AxfSeO5530PEERE6/WyEg3lsuyw4GHlPZHog==",
2426
+ "license": "MIT",
2427
+ "engines": {
2428
+ "node": "^4.5.0 || >= 5.9"
2429
+ }
2430
+ },
2431
  "node_modules/brace-expansion": {
2432
  "version": "1.1.11",
2433
  "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
 
2443
  "version": "3.0.3",
2444
  "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz",
2445
  "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==",
 
2446
  "license": "MIT",
2447
  "dependencies": {
2448
  "fill-range": "^7.1.1"
 
2667
  "dev": true,
2668
  "license": "MIT"
2669
  },
2670
+ "node_modules/cookie": {
2671
+ "version": "0.7.2",
2672
+ "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.2.tgz",
2673
+ "integrity": "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==",
2674
+ "license": "MIT",
2675
+ "engines": {
2676
+ "node": ">= 0.6"
2677
+ }
2678
+ },
2679
+ "node_modules/cors": {
2680
+ "version": "2.8.5",
2681
+ "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz",
2682
+ "integrity": "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==",
2683
+ "license": "MIT",
2684
+ "dependencies": {
2685
+ "object-assign": "^4",
2686
+ "vary": "^1"
2687
+ },
2688
+ "engines": {
2689
+ "node": ">= 0.10"
2690
+ }
2691
+ },
2692
+ "node_modules/cross-inspect": {
2693
+ "version": "1.0.1",
2694
+ "resolved": "https://registry.npmjs.org/cross-inspect/-/cross-inspect-1.0.1.tgz",
2695
+ "integrity": "sha512-Pcw1JTvZLSJH83iiGWt6fRcT+BjZlCDRVwYLbUcHzv/CRpB7r0MlSrGbIyQvVSNyGnbt7G4AXuyCiDR3POvZ1A==",
2696
+ "license": "MIT",
2697
+ "dependencies": {
2698
+ "tslib": "^2.4.0"
2699
+ },
2700
+ "engines": {
2701
+ "node": ">=16.0.0"
2702
+ }
2703
+ },
2704
  "node_modules/cross-spawn": {
2705
  "version": "7.0.6",
2706
  "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz",
 
2732
  "version": "3.1.3",
2733
  "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz",
2734
  "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==",
2735
+ "devOptional": true,
2736
  "license": "MIT"
2737
  },
2738
  "node_modules/damerau-levenshtein": {
 
2911
  "url": "https://github.com/sponsors/wooorm"
2912
  }
2913
  },
2914
+ "node_modules/dir-glob": {
2915
+ "version": "3.0.1",
2916
+ "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz",
2917
+ "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==",
2918
+ "license": "MIT",
2919
+ "dependencies": {
2920
+ "path-type": "^4.0.0"
2921
+ },
2922
+ "engines": {
2923
+ "node": ">=8"
2924
+ }
2925
+ },
2926
  "node_modules/doctrine": {
2927
  "version": "2.1.0",
2928
  "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz",
 
2936
  "node": ">=0.10.0"
2937
  }
2938
  },
2939
+ "node_modules/dset": {
2940
+ "version": "3.1.4",
2941
+ "resolved": "https://registry.npmjs.org/dset/-/dset-3.1.4.tgz",
2942
+ "integrity": "sha512-2QF/g9/zTaPDc3BjNcVTGoBbXBgYfMTTceLaYcFJ/W9kggFUkhxD/hMEeuLKbugyef9SqAx8cpgwlIP/jinUTA==",
2943
+ "license": "MIT",
2944
+ "engines": {
2945
+ "node": ">=4"
2946
+ }
2947
+ },
2948
  "node_modules/dunder-proto": {
2949
  "version": "1.0.1",
2950
  "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz",
 
2967
  "dev": true,
2968
  "license": "MIT"
2969
  },
2970
+ "node_modules/engine.io": {
2971
+ "version": "6.6.4",
2972
+ "resolved": "https://registry.npmjs.org/engine.io/-/engine.io-6.6.4.tgz",
2973
+ "integrity": "sha512-ZCkIjSYNDyGn0R6ewHDtXgns/Zre/NT6Agvq1/WobF7JXgFff4SeDroKiCO3fNJreU9YG429Sc81o4w5ok/W5g==",
2974
+ "license": "MIT",
2975
+ "dependencies": {
2976
+ "@types/cors": "^2.8.12",
2977
+ "@types/node": ">=10.0.0",
2978
+ "accepts": "~1.3.4",
2979
+ "base64id": "2.0.0",
2980
+ "cookie": "~0.7.2",
2981
+ "cors": "~2.8.5",
2982
+ "debug": "~4.3.1",
2983
+ "engine.io-parser": "~5.2.1",
2984
+ "ws": "~8.17.1"
2985
+ },
2986
+ "engines": {
2987
+ "node": ">=10.2.0"
2988
+ }
2989
+ },
2990
+ "node_modules/engine.io-client": {
2991
+ "version": "6.6.3",
2992
+ "resolved": "https://registry.npmjs.org/engine.io-client/-/engine.io-client-6.6.3.tgz",
2993
+ "integrity": "sha512-T0iLjnyNWahNyv/lcjS2y4oE358tVS/SYQNxYXGAJ9/GLgH4VCvOQ/mhTjqU88mLZCQgiG8RIegFHYCdVC+j5w==",
2994
+ "license": "MIT",
2995
+ "dependencies": {
2996
+ "@socket.io/component-emitter": "~3.1.0",
2997
+ "debug": "~4.3.1",
2998
+ "engine.io-parser": "~5.2.1",
2999
+ "ws": "~8.17.1",
3000
+ "xmlhttprequest-ssl": "~2.1.1"
3001
+ }
3002
+ },
3003
+ "node_modules/engine.io-client/node_modules/debug": {
3004
+ "version": "4.3.7",
3005
+ "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz",
3006
+ "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==",
3007
+ "license": "MIT",
3008
+ "dependencies": {
3009
+ "ms": "^2.1.3"
3010
+ },
3011
+ "engines": {
3012
+ "node": ">=6.0"
3013
+ },
3014
+ "peerDependenciesMeta": {
3015
+ "supports-color": {
3016
+ "optional": true
3017
+ }
3018
+ }
3019
+ },
3020
+ "node_modules/engine.io-client/node_modules/ws": {
3021
+ "version": "8.17.1",
3022
+ "resolved": "https://registry.npmjs.org/ws/-/ws-8.17.1.tgz",
3023
+ "integrity": "sha512-6XQFvXTkbfUOZOKKILFG1PDK2NDQs4azKQl26T0YS5CxqWLgXajbPZ+h4gZekJyRqFU8pvnbAbbs/3TgRPy+GQ==",
3024
+ "license": "MIT",
3025
+ "engines": {
3026
+ "node": ">=10.0.0"
3027
+ },
3028
+ "peerDependencies": {
3029
+ "bufferutil": "^4.0.1",
3030
+ "utf-8-validate": ">=5.0.2"
3031
+ },
3032
+ "peerDependenciesMeta": {
3033
+ "bufferutil": {
3034
+ "optional": true
3035
+ },
3036
+ "utf-8-validate": {
3037
+ "optional": true
3038
+ }
3039
+ }
3040
+ },
3041
+ "node_modules/engine.io-parser": {
3042
+ "version": "5.2.3",
3043
+ "resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-5.2.3.tgz",
3044
+ "integrity": "sha512-HqD3yTBfnBxIrbnM1DoD6Pcq8NECnh8d4As1Qgh0z5Gg3jRRIqijury0CL3ghu/edArpUYiYqQiDUQBIs4np3Q==",
3045
+ "license": "MIT",
3046
+ "engines": {
3047
+ "node": ">=10.0.0"
3048
+ }
3049
+ },
3050
+ "node_modules/engine.io/node_modules/debug": {
3051
+ "version": "4.3.7",
3052
+ "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz",
3053
+ "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==",
3054
+ "license": "MIT",
3055
+ "dependencies": {
3056
+ "ms": "^2.1.3"
3057
+ },
3058
+ "engines": {
3059
+ "node": ">=6.0"
3060
+ },
3061
+ "peerDependenciesMeta": {
3062
+ "supports-color": {
3063
+ "optional": true
3064
+ }
3065
+ }
3066
+ },
3067
+ "node_modules/engine.io/node_modules/ws": {
3068
+ "version": "8.17.1",
3069
+ "resolved": "https://registry.npmjs.org/ws/-/ws-8.17.1.tgz",
3070
+ "integrity": "sha512-6XQFvXTkbfUOZOKKILFG1PDK2NDQs4azKQl26T0YS5CxqWLgXajbPZ+h4gZekJyRqFU8pvnbAbbs/3TgRPy+GQ==",
3071
+ "license": "MIT",
3072
+ "engines": {
3073
+ "node": ">=10.0.0"
3074
+ },
3075
+ "peerDependencies": {
3076
+ "bufferutil": "^4.0.1",
3077
+ "utf-8-validate": ">=5.0.2"
3078
+ },
3079
+ "peerDependenciesMeta": {
3080
+ "bufferutil": {
3081
+ "optional": true
3082
+ },
3083
+ "utf-8-validate": {
3084
+ "optional": true
3085
+ }
3086
+ }
3087
+ },
3088
  "node_modules/enhanced-resolve": {
3089
  "version": "5.18.1",
3090
  "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.18.1.tgz",
 
3754
  "version": "3.3.1",
3755
  "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.1.tgz",
3756
  "integrity": "sha512-kNFPyjhh5cKjrUltxs+wFx+ZkbRaxxmZ+X0ZU31SOsxCEtP9VPgtq2teZw1DebupL5GmDaNQ6yKMMVcM41iqDg==",
 
3757
  "license": "MIT",
3758
  "dependencies": {
3759
  "@nodelib/fs.stat": "^2.0.2",
 
3770
  "version": "5.1.2",
3771
  "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz",
3772
  "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==",
 
3773
  "license": "ISC",
3774
  "dependencies": {
3775
  "is-glob": "^4.0.1"
 
3796
  "version": "1.19.1",
3797
  "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.19.1.tgz",
3798
  "integrity": "sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ==",
 
3799
  "license": "ISC",
3800
  "dependencies": {
3801
  "reusify": "^1.0.4"
 
3818
  "version": "7.1.1",
3819
  "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz",
3820
  "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==",
 
3821
  "license": "MIT",
3822
  "dependencies": {
3823
  "to-regex-range": "^5.0.1"
 
4034
  "url": "https://github.com/sponsors/ljharb"
4035
  }
4036
  },
4037
+ "node_modules/globby": {
4038
+ "version": "11.1.0",
4039
+ "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz",
4040
+ "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==",
4041
+ "license": "MIT",
4042
+ "dependencies": {
4043
+ "array-union": "^2.1.0",
4044
+ "dir-glob": "^3.0.1",
4045
+ "fast-glob": "^3.2.9",
4046
+ "ignore": "^5.2.0",
4047
+ "merge2": "^1.4.1",
4048
+ "slash": "^3.0.0"
4049
+ },
4050
+ "engines": {
4051
+ "node": ">=10"
4052
+ },
4053
+ "funding": {
4054
+ "url": "https://github.com/sponsors/sindresorhus"
4055
+ }
4056
+ },
4057
  "node_modules/gopd": {
4058
  "version": "1.2.0",
4059
  "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz",
 
4061
  "dev": true,
4062
  "license": "MIT",
4063
  "engines": {
4064
+ "node": ">= 0.4"
4065
+ },
4066
+ "funding": {
4067
+ "url": "https://github.com/sponsors/ljharb"
4068
+ }
4069
+ },
4070
+ "node_modules/graceful-fs": {
4071
+ "version": "4.2.11",
4072
+ "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz",
4073
+ "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==",
4074
+ "dev": true,
4075
+ "license": "ISC"
4076
+ },
4077
+ "node_modules/graphemer": {
4078
+ "version": "1.4.0",
4079
+ "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz",
4080
+ "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==",
4081
+ "dev": true,
4082
+ "license": "MIT"
4083
+ },
4084
+ "node_modules/graphql": {
4085
+ "version": "16.10.0",
4086
+ "resolved": "https://registry.npmjs.org/graphql/-/graphql-16.10.0.tgz",
4087
+ "integrity": "sha512-AjqGKbDGUFRKIRCP9tCKiIGHyriz2oHEbPIbEtcSLSs4YjReZOIPQQWek4+6hjw62H9QShXHyaGivGiYVLeYFQ==",
4088
+ "license": "MIT",
4089
+ "engines": {
4090
+ "node": "^12.22.0 || ^14.16.0 || ^16.0.0 || >=17.0.0"
4091
+ }
4092
+ },
4093
+ "node_modules/graphql-subscriptions": {
4094
+ "version": "3.0.0",
4095
+ "resolved": "https://registry.npmjs.org/graphql-subscriptions/-/graphql-subscriptions-3.0.0.tgz",
4096
+ "integrity": "sha512-kZCdevgmzDjGAOqH7GlDmQXYAkuHoKpMlJrqF40HMPhUhM5ZWSFSxCwD/nSi6AkaijmMfsFhoJRGJ27UseCvRA==",
4097
+ "license": "MIT",
4098
+ "peerDependencies": {
4099
+ "graphql": "^15.7.2 || ^16.0.0"
4100
+ }
4101
+ },
4102
+ "node_modules/graphql-tag": {
4103
+ "version": "2.12.6",
4104
+ "resolved": "https://registry.npmjs.org/graphql-tag/-/graphql-tag-2.12.6.tgz",
4105
+ "integrity": "sha512-FdSNcu2QQcWnM2VNvSCCDCVS5PpPqpzgFT8+GXzqJuoDd0CBncxCY278u4mhRO7tMgo2JjgJA5aZ+nWSQ/Z+xg==",
4106
+ "license": "MIT",
4107
+ "optional": true,
4108
+ "dependencies": {
4109
+ "tslib": "^2.1.0"
4110
+ },
4111
+ "engines": {
4112
+ "node": ">=10"
4113
+ },
4114
+ "peerDependencies": {
4115
+ "graphql": "^0.9.0 || ^0.10.0 || ^0.11.0 || ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0 || ^16.0.0"
4116
+ }
4117
+ },
4118
+ "node_modules/graphql-tools": {
4119
+ "version": "9.0.18",
4120
+ "resolved": "https://registry.npmjs.org/graphql-tools/-/graphql-tools-9.0.18.tgz",
4121
+ "integrity": "sha512-gKRPo+kO/gcm+gTQioCQaFasSihrxQOTzdpAWz1dgeIGSzCOqHFkeWlRqyaJeEKin9HQ4rvAsA+BmHLBFVUPjA==",
4122
+ "license": "MIT",
4123
+ "dependencies": {
4124
+ "@graphql-tools/schema": "^10.0.23",
4125
+ "tslib": "^2.4.0"
4126
+ },
4127
+ "engines": {
4128
+ "node": ">=16.0.0"
4129
+ },
4130
+ "optionalDependencies": {
4131
+ "@apollo/client": "~3.2.5 || ~3.3.0 || ~3.4.0 || ~3.5.0 || ~3.6.0 || ~3.7.0 || ~3.8.0 || ~3.9.0 || ~3.10.0 || ~3.11.0 || ~3.12.0 || ~3.13.0"
4132
+ },
4133
+ "peerDependencies": {
4134
+ "graphql": "^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0"
4135
+ }
4136
+ },
4137
+ "node_modules/graphql-ws": {
4138
+ "version": "6.0.4",
4139
+ "resolved": "https://registry.npmjs.org/graphql-ws/-/graphql-ws-6.0.4.tgz",
4140
+ "integrity": "sha512-8b4OZtNOvv8+NZva8HXamrc0y1jluYC0+13gdh7198FKjVzXyTvVc95DCwGzaKEfn3YuWZxUqjJlHe3qKM/F2g==",
4141
+ "license": "MIT",
4142
+ "engines": {
4143
+ "node": ">=20"
4144
+ },
4145
+ "peerDependencies": {
4146
+ "@fastify/websocket": "^10 || ^11",
4147
+ "graphql": "^15.10.1 || ^16",
4148
+ "uWebSockets.js": "^20",
4149
+ "ws": "^8"
4150
+ },
4151
+ "peerDependenciesMeta": {
4152
+ "@fastify/websocket": {
4153
+ "optional": true
4154
+ },
4155
+ "uWebSockets.js": {
4156
+ "optional": true
4157
+ },
4158
+ "ws": {
4159
+ "optional": true
4160
+ }
4161
+ }
4162
+ },
4163
+ "node_modules/graphql-yoga": {
4164
+ "version": "5.13.2",
4165
+ "resolved": "https://registry.npmjs.org/graphql-yoga/-/graphql-yoga-5.13.2.tgz",
4166
+ "integrity": "sha512-ZXhIoAPCV2K2ozwpxDL1ZXhhI2SvIp3hJMaSRaHcojLGE9w9iV8oYGPnZKcV5eisF3VE13RcIF4Ys6TTkU338Q==",
4167
+ "license": "MIT",
4168
+ "dependencies": {
4169
+ "@envelop/core": "^5.2.3",
4170
+ "@envelop/instrumentation": "^1.0.0",
4171
+ "@graphql-tools/executor": "^1.4.0",
4172
+ "@graphql-tools/schema": "^10.0.11",
4173
+ "@graphql-tools/utils": "^10.6.2",
4174
+ "@graphql-yoga/logger": "^2.0.1",
4175
+ "@graphql-yoga/subscription": "^5.0.3",
4176
+ "@whatwg-node/fetch": "^0.10.5",
4177
+ "@whatwg-node/promise-helpers": "^1.2.4",
4178
+ "@whatwg-node/server": "^0.10.1",
4179
+ "dset": "^3.1.4",
4180
+ "lru-cache": "^10.0.0",
4181
+ "tslib": "^2.8.1"
4182
+ },
4183
+ "engines": {
4184
+ "node": ">=18.0.0"
4185
  },
4186
+ "peerDependencies": {
4187
+ "graphql": "^15.2.0 || ^16.0.0"
4188
  }
4189
  },
 
 
 
 
 
 
 
 
 
 
 
 
 
 
4190
  "node_modules/gray-matter": {
4191
  "version": "4.0.3",
4192
  "resolved": "https://registry.npmjs.org/gray-matter/-/gray-matter-4.0.3.tgz",
 
4369
  "url": "https://opencollective.com/unified"
4370
  }
4371
  },
4372
+ "node_modules/hoist-non-react-statics": {
4373
+ "version": "3.3.2",
4374
+ "resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz",
4375
+ "integrity": "sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==",
4376
+ "license": "BSD-3-Clause",
4377
+ "optional": true,
4378
+ "dependencies": {
4379
+ "react-is": "^16.7.0"
4380
+ }
4381
+ },
4382
  "node_modules/html-void-elements": {
4383
  "version": "3.0.0",
4384
  "resolved": "https://registry.npmjs.org/html-void-elements/-/html-void-elements-3.0.0.tgz",
 
4393
  "version": "5.3.2",
4394
  "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz",
4395
  "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==",
 
4396
  "license": "MIT",
4397
  "engines": {
4398
  "node": ">= 4"
 
4605
  "version": "2.1.1",
4606
  "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz",
4607
  "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==",
 
4608
  "license": "MIT",
4609
  "engines": {
4610
  "node": ">=0.10.0"
 
4649
  "version": "4.0.3",
4650
  "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz",
4651
  "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==",
 
4652
  "license": "MIT",
4653
  "dependencies": {
4654
  "is-extglob": "^2.1.1"
 
4674
  "version": "7.0.0",
4675
  "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz",
4676
  "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==",
 
4677
  "license": "MIT",
4678
  "engines": {
4679
  "node": ">=0.12.0"
 
4899
  "version": "4.0.0",
4900
  "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
4901
  "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==",
4902
+ "devOptional": true,
4903
  "license": "MIT"
4904
  },
4905
  "node_modules/js-yaml": {
 
5311
  "version": "1.4.0",
5312
  "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz",
5313
  "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==",
5314
+ "devOptional": true,
5315
  "license": "MIT",
5316
  "dependencies": {
5317
  "js-tokens": "^3.0.0 || ^4.0.0"
 
5320
  "loose-envify": "cli.js"
5321
  }
5322
  },
5323
+ "node_modules/lru-cache": {
5324
+ "version": "10.4.3",
5325
+ "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz",
5326
+ "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==",
5327
+ "license": "ISC"
5328
+ },
5329
  "node_modules/math-intrinsics": {
5330
  "version": "1.1.0",
5331
  "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz",
 
5433
  "version": "1.4.1",
5434
  "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz",
5435
  "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==",
 
5436
  "license": "MIT",
5437
  "engines": {
5438
  "node": ">= 8"
 
5884
  "version": "4.0.8",
5885
  "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz",
5886
  "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==",
 
5887
  "license": "MIT",
5888
  "dependencies": {
5889
  "braces": "^3.0.3",
 
5893
  "node": ">=8.6"
5894
  }
5895
  },
5896
+ "node_modules/mime-db": {
5897
+ "version": "1.52.0",
5898
+ "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz",
5899
+ "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==",
5900
+ "license": "MIT",
5901
+ "engines": {
5902
+ "node": ">= 0.6"
5903
+ }
5904
+ },
5905
+ "node_modules/mime-types": {
5906
+ "version": "2.1.35",
5907
+ "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz",
5908
+ "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==",
5909
+ "license": "MIT",
5910
+ "dependencies": {
5911
+ "mime-db": "1.52.0"
5912
+ },
5913
+ "engines": {
5914
+ "node": ">= 0.6"
5915
+ }
5916
+ },
5917
  "node_modules/minimatch": {
5918
  "version": "3.1.2",
5919
  "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
 
5968
  "dev": true,
5969
  "license": "MIT"
5970
  },
5971
+ "node_modules/negotiator": {
5972
+ "version": "0.6.3",
5973
+ "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz",
5974
+ "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==",
5975
+ "license": "MIT",
5976
+ "engines": {
5977
+ "node": ">= 0.6"
5978
+ }
5979
+ },
5980
  "node_modules/next": {
5981
  "version": "15.2.4",
5982
  "resolved": "https://registry.npmjs.org/next/-/next-15.2.4.tgz",
 
6059
  "node": "^10 || ^12 || >=14"
6060
  }
6061
  },
6062
+ "node_modules/normalize-path": {
6063
+ "version": "2.1.1",
6064
+ "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-2.1.1.tgz",
6065
+ "integrity": "sha512-3pKJwH184Xo/lnH6oyP1q2pMd7HcypqqmRs91/6/i2CGtWwIKGCkOOMTm/zXbgTEWHw1uNpNi/igc3ePOYHb6w==",
6066
+ "license": "MIT",
6067
+ "dependencies": {
6068
+ "remove-trailing-separator": "^1.0.1"
6069
+ },
6070
+ "engines": {
6071
+ "node": ">=0.10.0"
6072
+ }
6073
+ },
6074
  "node_modules/object-assign": {
6075
  "version": "4.1.1",
6076
  "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
6077
  "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==",
 
6078
  "license": "MIT",
6079
  "engines": {
6080
  "node": ">=0.10.0"
 
6193
  "url": "https://github.com/sponsors/ljharb"
6194
  }
6195
  },
6196
+ "node_modules/optimism": {
6197
+ "version": "0.18.1",
6198
+ "resolved": "https://registry.npmjs.org/optimism/-/optimism-0.18.1.tgz",
6199
+ "integrity": "sha512-mLXNwWPa9dgFyDqkNi54sjDyNJ9/fTI6WGBLgnXku1vdKY/jovHfZT5r+aiVeFFLOz+foPNOm5YJ4mqgld2GBQ==",
6200
+ "license": "MIT",
6201
+ "optional": true,
6202
+ "dependencies": {
6203
+ "@wry/caches": "^1.0.0",
6204
+ "@wry/context": "^0.7.0",
6205
+ "@wry/trie": "^0.5.0",
6206
+ "tslib": "^2.3.0"
6207
+ }
6208
+ },
6209
  "node_modules/optionator": {
6210
  "version": "0.9.4",
6211
  "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz",
 
6314
  "dev": true,
6315
  "license": "MIT"
6316
  },
6317
+ "node_modules/path-type": {
6318
+ "version": "4.0.0",
6319
+ "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz",
6320
+ "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==",
6321
+ "license": "MIT",
6322
+ "engines": {
6323
+ "node": ">=8"
6324
+ }
6325
+ },
6326
  "node_modules/picocolors": {
6327
  "version": "1.1.1",
6328
  "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz",
 
6333
  "version": "2.3.1",
6334
  "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz",
6335
  "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==",
 
6336
  "license": "MIT",
6337
  "engines": {
6338
  "node": ">=8.6"
 
6407
  "version": "15.8.1",
6408
  "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz",
6409
  "integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==",
6410
+ "devOptional": true,
6411
  "license": "MIT",
6412
  "dependencies": {
6413
  "loose-envify": "^1.4.0",
 
6439
  "version": "1.2.3",
6440
  "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz",
6441
  "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==",
 
6442
  "funding": [
6443
  {
6444
  "type": "github",
 
6480
  "version": "16.13.1",
6481
  "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz",
6482
  "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==",
6483
+ "devOptional": true,
6484
  "license": "MIT"
6485
  },
6486
  "node_modules/reflect.getprototypeof": {
 
6527
  "url": "https://github.com/sponsors/ljharb"
6528
  }
6529
  },
6530
+ "node_modules/rehackt": {
6531
+ "version": "0.1.0",
6532
+ "resolved": "https://registry.npmjs.org/rehackt/-/rehackt-0.1.0.tgz",
6533
+ "integrity": "sha512-7kRDOuLHB87D/JESKxQoRwv4DzbIdwkAGQ7p6QKGdVlY1IZheUnVhlk/4UZlNUVxdAXpyxikE3URsG067ybVzw==",
6534
+ "license": "MIT",
6535
+ "optional": true,
6536
+ "peerDependencies": {
6537
+ "@types/react": "*",
6538
+ "react": "*"
6539
+ },
6540
+ "peerDependenciesMeta": {
6541
+ "@types/react": {
6542
+ "optional": true
6543
+ },
6544
+ "react": {
6545
+ "optional": true
6546
+ }
6547
+ }
6548
+ },
6549
  "node_modules/remark": {
6550
  "version": "15.0.1",
6551
  "resolved": "https://registry.npmjs.org/remark/-/remark-15.0.1.tgz",
 
6610
  "url": "https://opencollective.com/unified"
6611
  }
6612
  },
6613
+ "node_modules/remove-trailing-separator": {
6614
+ "version": "1.1.0",
6615
+ "resolved": "https://registry.npmjs.org/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz",
6616
+ "integrity": "sha512-/hS+Y0u3aOfIETiaiirUFwDBDzmXPvO+jAfKTitUngIPzdKc6Z0LoFjM/CK5PL4C+eKwHohlHAb6H0VFfmmUsw==",
6617
+ "license": "ISC"
6618
+ },
6619
  "node_modules/resolve": {
6620
  "version": "1.22.10",
6621
  "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.10.tgz",
 
6661
  "version": "1.1.0",
6662
  "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.1.0.tgz",
6663
  "integrity": "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==",
 
6664
  "license": "MIT",
6665
  "engines": {
6666
  "iojs": ">=1.0.0",
 
6671
  "version": "1.2.0",
6672
  "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz",
6673
  "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==",
 
6674
  "funding": [
6675
  {
6676
  "type": "github",
 
6975
  "is-arrayish": "^0.3.1"
6976
  }
6977
  },
6978
+ "node_modules/slash": {
6979
+ "version": "3.0.0",
6980
+ "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz",
6981
+ "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==",
6982
+ "license": "MIT",
6983
+ "engines": {
6984
+ "node": ">=8"
6985
+ }
6986
+ },
6987
+ "node_modules/socket.io": {
6988
+ "version": "4.8.1",
6989
+ "resolved": "https://registry.npmjs.org/socket.io/-/socket.io-4.8.1.tgz",
6990
+ "integrity": "sha512-oZ7iUCxph8WYRHHcjBEc9unw3adt5CmSNlppj/5Q4k2RIrhl8Z5yY2Xr4j9zj0+wzVZ0bxmYoGSzKJnRl6A4yg==",
6991
+ "license": "MIT",
6992
+ "dependencies": {
6993
+ "accepts": "~1.3.4",
6994
+ "base64id": "~2.0.0",
6995
+ "cors": "~2.8.5",
6996
+ "debug": "~4.3.2",
6997
+ "engine.io": "~6.6.0",
6998
+ "socket.io-adapter": "~2.5.2",
6999
+ "socket.io-parser": "~4.2.4"
7000
+ },
7001
+ "engines": {
7002
+ "node": ">=10.2.0"
7003
+ }
7004
+ },
7005
+ "node_modules/socket.io-adapter": {
7006
+ "version": "2.5.5",
7007
+ "resolved": "https://registry.npmjs.org/socket.io-adapter/-/socket.io-adapter-2.5.5.tgz",
7008
+ "integrity": "sha512-eLDQas5dzPgOWCk9GuuJC2lBqItuhKI4uxGgo9aIV7MYbk2h9Q6uULEh8WBzThoI7l+qU9Ast9fVUmkqPP9wYg==",
7009
+ "license": "MIT",
7010
+ "dependencies": {
7011
+ "debug": "~4.3.4",
7012
+ "ws": "~8.17.1"
7013
+ }
7014
+ },
7015
+ "node_modules/socket.io-adapter/node_modules/debug": {
7016
+ "version": "4.3.7",
7017
+ "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz",
7018
+ "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==",
7019
+ "license": "MIT",
7020
+ "dependencies": {
7021
+ "ms": "^2.1.3"
7022
+ },
7023
+ "engines": {
7024
+ "node": ">=6.0"
7025
+ },
7026
+ "peerDependenciesMeta": {
7027
+ "supports-color": {
7028
+ "optional": true
7029
+ }
7030
+ }
7031
+ },
7032
+ "node_modules/socket.io-adapter/node_modules/ws": {
7033
+ "version": "8.17.1",
7034
+ "resolved": "https://registry.npmjs.org/ws/-/ws-8.17.1.tgz",
7035
+ "integrity": "sha512-6XQFvXTkbfUOZOKKILFG1PDK2NDQs4azKQl26T0YS5CxqWLgXajbPZ+h4gZekJyRqFU8pvnbAbbs/3TgRPy+GQ==",
7036
+ "license": "MIT",
7037
+ "engines": {
7038
+ "node": ">=10.0.0"
7039
+ },
7040
+ "peerDependencies": {
7041
+ "bufferutil": "^4.0.1",
7042
+ "utf-8-validate": ">=5.0.2"
7043
+ },
7044
+ "peerDependenciesMeta": {
7045
+ "bufferutil": {
7046
+ "optional": true
7047
+ },
7048
+ "utf-8-validate": {
7049
+ "optional": true
7050
+ }
7051
+ }
7052
+ },
7053
+ "node_modules/socket.io-client": {
7054
+ "version": "4.8.1",
7055
+ "resolved": "https://registry.npmjs.org/socket.io-client/-/socket.io-client-4.8.1.tgz",
7056
+ "integrity": "sha512-hJVXfu3E28NmzGk8o1sHhN3om52tRvwYeidbj7xKy2eIIse5IoKX3USlS6Tqt3BHAtflLIkCQBkzVrEEfWUyYQ==",
7057
+ "license": "MIT",
7058
+ "dependencies": {
7059
+ "@socket.io/component-emitter": "~3.1.0",
7060
+ "debug": "~4.3.2",
7061
+ "engine.io-client": "~6.6.1",
7062
+ "socket.io-parser": "~4.2.4"
7063
+ },
7064
+ "engines": {
7065
+ "node": ">=10.0.0"
7066
+ }
7067
+ },
7068
+ "node_modules/socket.io-client/node_modules/debug": {
7069
+ "version": "4.3.7",
7070
+ "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz",
7071
+ "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==",
7072
+ "license": "MIT",
7073
+ "dependencies": {
7074
+ "ms": "^2.1.3"
7075
+ },
7076
+ "engines": {
7077
+ "node": ">=6.0"
7078
+ },
7079
+ "peerDependenciesMeta": {
7080
+ "supports-color": {
7081
+ "optional": true
7082
+ }
7083
+ }
7084
+ },
7085
+ "node_modules/socket.io-parser": {
7086
+ "version": "4.2.4",
7087
+ "resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-4.2.4.tgz",
7088
+ "integrity": "sha512-/GbIKmo8ioc+NIWIhwdecY0ge+qVBSMdgxGygevmdHj24bsfgtCmcUUcQ5ZzcylGFHsN3k4HB4Cgkl96KVnuew==",
7089
+ "license": "MIT",
7090
+ "dependencies": {
7091
+ "@socket.io/component-emitter": "~3.1.0",
7092
+ "debug": "~4.3.1"
7093
+ },
7094
+ "engines": {
7095
+ "node": ">=10.0.0"
7096
+ }
7097
+ },
7098
+ "node_modules/socket.io-parser/node_modules/debug": {
7099
+ "version": "4.3.7",
7100
+ "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz",
7101
+ "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==",
7102
+ "license": "MIT",
7103
+ "dependencies": {
7104
+ "ms": "^2.1.3"
7105
+ },
7106
+ "engines": {
7107
+ "node": ">=6.0"
7108
+ },
7109
+ "peerDependenciesMeta": {
7110
+ "supports-color": {
7111
+ "optional": true
7112
+ }
7113
+ }
7114
+ },
7115
+ "node_modules/socket.io/node_modules/debug": {
7116
+ "version": "4.3.7",
7117
+ "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz",
7118
+ "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==",
7119
+ "license": "MIT",
7120
+ "dependencies": {
7121
+ "ms": "^2.1.3"
7122
+ },
7123
+ "engines": {
7124
+ "node": ">=6.0"
7125
+ },
7126
+ "peerDependenciesMeta": {
7127
+ "supports-color": {
7128
+ "optional": true
7129
+ }
7130
+ }
7131
+ },
7132
  "node_modules/source-map-js": {
7133
  "version": "1.2.1",
7134
  "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz",
 
7377
  "url": "https://github.com/sponsors/ljharb"
7378
  }
7379
  },
7380
+ "node_modules/symbol-observable": {
7381
+ "version": "4.0.0",
7382
+ "resolved": "https://registry.npmjs.org/symbol-observable/-/symbol-observable-4.0.0.tgz",
7383
+ "integrity": "sha512-b19dMThMV4HVFynSAM1++gBHAbk2Tc/osgLIBZMKsyqh34jb2e8Os7T6ZW/Bt3pJFdBTd2JwAnAAEQV7rSNvcQ==",
7384
+ "license": "MIT",
7385
+ "optional": true,
7386
+ "engines": {
7387
+ "node": ">=0.10"
7388
+ }
7389
+ },
7390
  "node_modules/tailwindcss": {
7391
  "version": "4.1.3",
7392
  "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.1.3.tgz",
 
7452
  "version": "5.0.1",
7453
  "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz",
7454
  "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==",
 
7455
  "license": "MIT",
7456
  "dependencies": {
7457
  "is-number": "^7.0.0"
 
7493
  "typescript": ">=4.8.4"
7494
  }
7495
  },
7496
+ "node_modules/ts-invariant": {
7497
+ "version": "0.10.3",
7498
+ "resolved": "https://registry.npmjs.org/ts-invariant/-/ts-invariant-0.10.3.tgz",
7499
+ "integrity": "sha512-uivwYcQaxAucv1CzRp2n/QdYPo4ILf9VXgH19zEIjFx2EJufV16P0JtJVpYHy89DItG6Kwj2oIUjrcK5au+4tQ==",
7500
+ "license": "MIT",
7501
+ "optional": true,
7502
+ "dependencies": {
7503
+ "tslib": "^2.1.0"
7504
+ },
7505
+ "engines": {
7506
+ "node": ">=8"
7507
+ }
7508
+ },
7509
  "node_modules/tsconfig-paths": {
7510
  "version": "3.15.0",
7511
  "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.15.0.tgz",
 
7653
  "version": "6.19.8",
7654
  "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.19.8.tgz",
7655
  "integrity": "sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw==",
 
7656
  "license": "MIT"
7657
  },
7658
  "node_modules/unified": {
 
7742
  "url": "https://opencollective.com/unified"
7743
  }
7744
  },
7745
+ "node_modules/unixify": {
7746
+ "version": "1.0.0",
7747
+ "resolved": "https://registry.npmjs.org/unixify/-/unixify-1.0.0.tgz",
7748
+ "integrity": "sha512-6bc58dPYhCMHHuwxldQxO3RRNZ4eCogZ/st++0+fcC1nr0jiGUtAdBJ2qzmLQWSxbtz42pWt4QQMiZ9HvZf5cg==",
7749
+ "license": "MIT",
7750
+ "dependencies": {
7751
+ "normalize-path": "^2.1.1"
7752
+ },
7753
+ "engines": {
7754
+ "node": ">=0.10.0"
7755
+ }
7756
+ },
7757
  "node_modules/unrs-resolver": {
7758
  "version": "1.3.3",
7759
  "resolved": "https://registry.npmjs.org/unrs-resolver/-/unrs-resolver-1.3.3.tgz",
 
7791
  "punycode": "^2.1.0"
7792
  }
7793
  },
7794
+ "node_modules/urlpattern-polyfill": {
7795
+ "version": "10.0.0",
7796
+ "resolved": "https://registry.npmjs.org/urlpattern-polyfill/-/urlpattern-polyfill-10.0.0.tgz",
7797
+ "integrity": "sha512-H/A06tKD7sS1O1X2SshBVeA5FLycRpjqiBeqGKmBwBDBy28EnRjORxTNe269KSSr5un5qyWi1iL61wLxpd+ZOg==",
7798
+ "license": "MIT"
7799
+ },
7800
  "node_modules/util-deprecate": {
7801
  "version": "1.0.2",
7802
  "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
7803
  "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==",
7804
  "license": "MIT"
7805
  },
7806
+ "node_modules/vary": {
7807
+ "version": "1.1.2",
7808
+ "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz",
7809
+ "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==",
7810
+ "license": "MIT",
7811
+ "engines": {
7812
+ "node": ">= 0.8"
7813
+ }
7814
+ },
7815
  "node_modules/vfile": {
7816
  "version": "6.0.3",
7817
  "resolved": "https://registry.npmjs.org/vfile/-/vfile-6.0.3.tgz",
 
7955
  "node": ">=0.10.0"
7956
  }
7957
  },
7958
+ "node_modules/ws": {
7959
+ "version": "8.18.1",
7960
+ "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.1.tgz",
7961
+ "integrity": "sha512-RKW2aJZMXeMxVpnZ6bck+RswznaxmzdULiBr6KY7XkTnW8uvt0iT9H5DkHUChXrc+uurzwa0rVI16n/Xzjdz1w==",
7962
+ "license": "MIT",
7963
+ "engines": {
7964
+ "node": ">=10.0.0"
7965
+ },
7966
+ "peerDependencies": {
7967
+ "bufferutil": "^4.0.1",
7968
+ "utf-8-validate": ">=5.0.2"
7969
+ },
7970
+ "peerDependenciesMeta": {
7971
+ "bufferutil": {
7972
+ "optional": true
7973
+ },
7974
+ "utf-8-validate": {
7975
+ "optional": true
7976
+ }
7977
+ }
7978
+ },
7979
+ "node_modules/xmlhttprequest-ssl": {
7980
+ "version": "2.1.2",
7981
+ "resolved": "https://registry.npmjs.org/xmlhttprequest-ssl/-/xmlhttprequest-ssl-2.1.2.tgz",
7982
+ "integrity": "sha512-TEU+nJVUUnA4CYJFLvK5X9AOeH4KvDvhIfm0vV1GaQRtchnG0hgK5p8hw/xjv8cunWYCsiPCSDzObPyhEwq3KQ==",
7983
+ "engines": {
7984
+ "node": ">=0.4.0"
7985
+ }
7986
+ },
7987
  "node_modules/yocto-queue": {
7988
  "version": "0.1.0",
7989
  "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz",
 
7997
  "url": "https://github.com/sponsors/sindresorhus"
7998
  }
7999
  },
8000
+ "node_modules/zen-observable": {
8001
+ "version": "0.8.15",
8002
+ "resolved": "https://registry.npmjs.org/zen-observable/-/zen-observable-0.8.15.tgz",
8003
+ "integrity": "sha512-PQ2PC7R9rslx84ndNBZB/Dkv8V8fZEpk83RLgXtYd0fwUgEjseMn1Dgajh2x6S8QbZAFa9p2qVCEuYZNgve0dQ==",
8004
+ "license": "MIT",
8005
+ "optional": true
8006
+ },
8007
+ "node_modules/zen-observable-ts": {
8008
+ "version": "1.2.5",
8009
+ "resolved": "https://registry.npmjs.org/zen-observable-ts/-/zen-observable-ts-1.2.5.tgz",
8010
+ "integrity": "sha512-QZWQekv6iB72Naeake9hS1KxHlotfRpe+WGNbNx5/ta+R3DNjVO2bswf63gXlWDcs+EMd7XY8HfVQyP1X6T4Zg==",
8011
+ "license": "MIT",
8012
+ "optional": true,
8013
+ "dependencies": {
8014
+ "zen-observable": "0.8.15"
8015
+ }
8016
+ },
8017
+ "node_modules/zod": {
8018
+ "version": "3.24.2",
8019
+ "resolved": "https://registry.npmjs.org/zod/-/zod-3.24.2.tgz",
8020
+ "integrity": "sha512-lY7CDW43ECgW9u1TcT3IoXHflywfVqDYze4waEz812jR/bZ8FHDsl7pFQoSZTz5N+2NqRXs8GBwnAwo3ZNxqhQ==",
8021
+ "license": "MIT",
8022
+ "funding": {
8023
+ "url": "https://github.com/sponsors/colinhacks"
8024
+ }
8025
+ },
8026
  "node_modules/zwitch": {
8027
  "version": "2.0.4",
8028
  "resolved": "https://registry.npmjs.org/zwitch/-/zwitch-2.0.4.tgz",
package.json CHANGED
@@ -9,16 +9,28 @@
9
  "lint": "next lint"
10
  },
11
  "dependencies": {
 
 
12
  "@tailwindcss/typography": "^0.5.16",
13
  "@types/lodash": "^4.17.16",
 
14
  "date-fns": "^4.1.0",
 
 
 
 
 
15
  "gray-matter": "^4.0.3",
16
  "lodash": "^4.17.21",
17
  "next": "15.2.4",
18
  "react": "^19.0.0",
19
  "react-dom": "^19.0.0",
20
  "remark": "^15.0.1",
21
- "remark-html": "^16.0.1"
 
 
 
 
22
  },
23
  "devDependencies": {
24
  "@eslint/eslintrc": "^3",
 
9
  "lint": "next lint"
10
  },
11
  "dependencies": {
12
+ "@graphql-tools/load-files": "^7.0.1",
13
+ "@graphql-tools/schema": "^10.0.23",
14
  "@tailwindcss/typography": "^0.5.16",
15
  "@types/lodash": "^4.17.16",
16
+ "@types/ws": "^8.18.1",
17
  "date-fns": "^4.1.0",
18
+ "graphql": "^16.10.0",
19
+ "graphql-subscriptions": "^3.0.0",
20
+ "graphql-tools": "^9.0.18",
21
+ "graphql-ws": "^6.0.4",
22
+ "graphql-yoga": "^5.13.2",
23
  "gray-matter": "^4.0.3",
24
  "lodash": "^4.17.21",
25
  "next": "15.2.4",
26
  "react": "^19.0.0",
27
  "react-dom": "^19.0.0",
28
  "remark": "^15.0.1",
29
+ "remark-html": "^16.0.1",
30
+ "socket.io": "^4.8.1",
31
+ "socket.io-client": "^4.8.1",
32
+ "ws": "^8.18.1",
33
+ "zod": "^3.24.2"
34
  },
35
  "devDependencies": {
36
  "@eslint/eslintrc": "^3",
posts/1743931717194.md ADDED
@@ -0,0 +1,9 @@
 
 
 
 
 
 
 
 
 
 
1
+ ---
2
+ title: 테스트 포스트
3
+ author: 테스터
4
+ date: 2024-03-21
5
+ tags: ["테스트"]
6
+ category: 일반
7
+ ---
8
+
9
+ 테스트 내용입니다
posts/1743932040407.md ADDED
@@ -0,0 +1,9 @@
 
 
 
 
 
 
 
 
 
 
1
+ ---
2
+ title: 테스트 포스트
3
+ author: 테스터
4
+ date: 2024-03-21
5
+ tags: ["테스트"]
6
+ category: 일반
7
+ ---
8
+
9
+ 테스트 내용입니다
posts/1743933868080.md ADDED
@@ -0,0 +1,9 @@
 
 
 
 
 
 
 
 
 
 
1
+ ---
2
+ title: WebSocket 테스트
3
+ author: 테스터
4
+ date: 2024-03-21
5
+ tags: ["테스트","웹소켓"]
6
+ category: 테스트
7
+ ---
8
+
9
+ 실시간 업데이트 테스트 중입니다.
requirements.txt ADDED
@@ -0,0 +1,2 @@
 
 
 
1
+ gradio==4.19.2
2
+ requests==2.31.0
scripts/start-ws.ts ADDED
@@ -0,0 +1,4 @@
 
 
 
 
 
1
+ import { startWebSocketServer } from '../src/lib/websocket';
2
+
3
+ console.log('Starting WebSocket server...');
4
+ startWebSocketServer();
src/app/api/git/route.ts CHANGED
@@ -1,144 +1,83 @@
1
- import { NextResponse } from 'next/server';
2
- import { exec } from 'node:child_process';
3
- import { promisify } from 'node:util';
4
- import path from 'node:path';
5
- import fs from 'node:fs/promises';
6
 
7
- const execAsync = promisify(exec);
8
- const contentDir = path.join(process.cwd(), 'content');
9
-
10
- export async function GET(request: Request) {
11
- const { searchParams } = new URL(request.url);
12
  const action = searchParams.get('action');
13
 
14
  try {
15
  switch (action) {
16
  case 'history':
17
- const { stdout: historyOutput } = await execAsync(
18
- 'git log --pretty=format:"%H|%an|%ad|%s" --name-status',
19
- { cwd: contentDir }
20
- );
21
-
22
- const commits = parseGitHistory(historyOutput);
23
  return NextResponse.json({ commits });
24
 
25
- case 'diff': {
26
  const hash1 = searchParams.get('hash1');
27
  const hash2 = searchParams.get('hash2');
28
- const filePath = searchParams.get('file') || '.';
29
 
30
- if (!hash1 || !hash2) {
31
  return NextResponse.json(
32
- { error: '커밋 해시가 필요합니다.' },
33
  { status: 400 }
34
  );
35
  }
36
 
37
- const { stdout: diffOutput } = await execAsync(
38
- `git diff ${hash1} ${hash2} -- ${filePath}`,
39
- { cwd: contentDir }
40
- );
41
-
42
- return NextResponse.json({ diff: diffOutput });
43
- }
44
 
45
  default:
46
  return NextResponse.json(
47
- { error: '잘못된 작업입니다.' },
48
  { status: 400 }
49
  );
50
  }
51
  } catch (error) {
52
- console.error('Git API 에러:', error);
53
  return NextResponse.json(
54
- { error: 'Git 작업 중 오류가 발생했습니다.' },
55
  { status: 500 }
56
  );
57
  }
58
  }
59
 
60
- export async function POST(request: Request) {
61
  try {
62
- const { action, commitHash, message } = await request.json();
 
63
 
64
  switch (action) {
65
  case 'rollback':
66
  if (!commitHash) {
67
  return NextResponse.json(
68
- { error: '커밋 해시가 필요합니다.' },
69
  { status: 400 }
70
  );
71
  }
72
-
73
- // 백업 생성
74
- const timestamp = new Date().toISOString().replace(/[:.]/g, '-');
75
- const backupDir = path.join(process.cwd(), 'backups', `backup-${timestamp}`);
76
- await fs.mkdir(backupDir, { recursive: true });
77
- await execAsync(`git archive HEAD -o "${backupDir}/content.zip"`, { cwd: contentDir });
78
-
79
- // 롤백 실행
80
- await execAsync(`git checkout ${commitHash}`, { cwd: contentDir });
81
- return NextResponse.json({ success: true, backupDir });
82
 
83
  case 'commit':
84
  if (!message) {
85
  return NextResponse.json(
86
- { error: '커밋 메시지가 필요합니다.' },
87
  { status: 400 }
88
  );
89
  }
90
-
91
- await execAsync('git add .', { cwd: contentDir });
92
- const { stdout } = await execAsync(`git commit -m "${message}"`, { cwd: contentDir });
93
- return NextResponse.json({ success: true, message: stdout });
94
 
95
  default:
96
  return NextResponse.json(
97
- { error: '잘못된 작업입니다.' },
98
  { status: 400 }
99
  );
100
  }
101
  } catch (error) {
102
- console.error('Git API 에러:', error);
103
  return NextResponse.json(
104
- { error: 'Git 작업 중 오류가 발생했습니다.' },
105
  { status: 500 }
106
  );
107
  }
108
- }
109
-
110
- function parseGitHistory(output: string) {
111
- const commits = [];
112
- const commitChunks = output.split('\n\n');
113
-
114
- for (const chunk of commitChunks) {
115
- if (!chunk.trim()) continue;
116
-
117
- const [headerLine, ...changes] = chunk.split('\n');
118
- const [hash, author, date, message] = headerLine.split('|');
119
-
120
- const changesList = {
121
- added: [] as string[],
122
- modified: [] as string[],
123
- deleted: [] as string[],
124
- };
125
-
126
- changes.forEach(change => {
127
- if (!change.trim()) return;
128
- const [status, file] = change.split('\t');
129
- if (status === 'A') changesList.added.push(file);
130
- else if (status === 'M') changesList.modified.push(file);
131
- else if (status === 'D') changesList.deleted.push(file);
132
- });
133
-
134
- commits.push({
135
- hash,
136
- author,
137
- date,
138
- message,
139
- changes: changesList,
140
- });
141
- }
142
-
143
- return commits;
144
  }
 
1
+ import { NextRequest, NextResponse } from 'next/server';
2
+ import { getCommitHistory, rollbackToCommit, getFileDiff, commitChanges } from '@/lib/git-server';
 
 
 
3
 
4
+ export async function GET(request: NextRequest) {
5
+ const searchParams = request.nextUrl.searchParams;
 
 
 
6
  const action = searchParams.get('action');
7
 
8
  try {
9
  switch (action) {
10
  case 'history':
11
+ const commits = await getCommitHistory();
 
 
 
 
 
12
  return NextResponse.json({ commits });
13
 
14
+ case 'diff':
15
  const hash1 = searchParams.get('hash1');
16
  const hash2 = searchParams.get('hash2');
17
+ const file = searchParams.get('file');
18
 
19
+ if (!hash1 || !hash2 || !file) {
20
  return NextResponse.json(
21
+ { error: 'Missing required parameters' },
22
  { status: 400 }
23
  );
24
  }
25
 
26
+ const diff = await getFileDiff(hash1, hash2, file);
27
+ return NextResponse.json(diff);
 
 
 
 
 
28
 
29
  default:
30
  return NextResponse.json(
31
+ { error: 'Invalid action' },
32
  { status: 400 }
33
  );
34
  }
35
  } catch (error) {
36
+ console.error('Git API error:', error);
37
  return NextResponse.json(
38
+ { error: 'Internal server error' },
39
  { status: 500 }
40
  );
41
  }
42
  }
43
 
44
+ export async function POST(request: NextRequest) {
45
  try {
46
+ const body = await request.json();
47
+ const { action, commitHash, message } = body;
48
 
49
  switch (action) {
50
  case 'rollback':
51
  if (!commitHash) {
52
  return NextResponse.json(
53
+ { error: 'Missing commit hash' },
54
  { status: 400 }
55
  );
56
  }
57
+ const success = await rollbackToCommit(commitHash);
58
+ return NextResponse.json({ success });
 
 
 
 
 
 
 
 
59
 
60
  case 'commit':
61
  if (!message) {
62
  return NextResponse.json(
63
+ { error: 'Missing commit message' },
64
  { status: 400 }
65
  );
66
  }
67
+ const output = await commitChanges(message);
68
+ return NextResponse.json({ message: output });
 
 
69
 
70
  default:
71
  return NextResponse.json(
72
+ { error: 'Invalid action' },
73
  { status: 400 }
74
  );
75
  }
76
  } catch (error) {
77
+ console.error('Git API error:', error);
78
  return NextResponse.json(
79
+ { error: 'Internal server error' },
80
  { status: 500 }
81
  );
82
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
83
  }
src/app/api/graphql/route.ts ADDED
@@ -0,0 +1,16 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import { createYoga } from 'graphql-yoga';
2
+ import { schema } from '../../../graphql/schema';
3
+ import { resolvers } from '../../../graphql/resolvers';
4
+
5
+ const yoga = createYoga({
6
+ schema,
7
+ graphqlEndpoint: '/api/graphql',
8
+ fetchAPI: { Response },
9
+ cors: {
10
+ origin: '*',
11
+ credentials: true,
12
+ methods: ['POST', 'GET', 'OPTIONS']
13
+ }
14
+ });
15
+
16
+ export { yoga as GET, yoga as POST };
src/app/page.tsx CHANGED
@@ -1,6 +1,7 @@
1
  import { getAllPosts, getAllTags, getAllCategories } from '@/lib/markdown';
2
  import Search from '@/components/Search';
3
  import Link from 'next/link';
 
4
 
5
  export default async function HomePage() {
6
  const [posts, tags, categories] = await Promise.all([
@@ -74,6 +75,7 @@ export default async function HomePage() {
74
  </div>
75
  </section>
76
  </div>
 
77
  </main>
78
  );
79
  }
 
1
  import { getAllPosts, getAllTags, getAllCategories } from '@/lib/markdown';
2
  import Search from '@/components/Search';
3
  import Link from 'next/link';
4
+ import WebSocketTest from '@/components/WebSocketTest';
5
 
6
  export default async function HomePage() {
7
  const [posts, tags, categories] = await Promise.all([
 
75
  </div>
76
  </section>
77
  </div>
78
+ <WebSocketTest />
79
  </main>
80
  );
81
  }
src/components/WebSocketTest.tsx ADDED
@@ -0,0 +1,283 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ 'use client';
2
+
3
+ import { useEffect, useState } from 'react';
4
+ import { createClient } from 'graphql-ws';
5
+
6
+ interface Message {
7
+ type: 'created' | 'updated' | 'deleted' | 'error' | 'info';
8
+ content: string;
9
+ timestamp: Date;
10
+ }
11
+
12
+ export default function WebSocketTest() {
13
+ const [messages, setMessages] = useState<Message[]>([]);
14
+ const [connected, setConnected] = useState(false);
15
+ const [lastPing, setLastPing] = useState<Date | null>(null);
16
+ const [retryCount, setRetryCount] = useState(0);
17
+
18
+ useEffect(() => {
19
+ let client: ReturnType<typeof createClient>;
20
+ let unsubscribers: (() => void)[] = [];
21
+ let pingInterval: NodeJS.Timeout;
22
+
23
+ const connect = () => {
24
+ try {
25
+ // 이전 구독 정리
26
+ unsubscribers.forEach(unsub => unsub());
27
+ unsubscribers = [];
28
+
29
+ // 새 클라이언트 생성
30
+ client = createClient({
31
+ url: 'ws://localhost:3001/api/graphql',
32
+ shouldRetry: (errOrCloseEvent: unknown) => {
33
+ console.log('Connection error:', errOrCloseEvent);
34
+ const errorMessage = errOrCloseEvent instanceof Error
35
+ ? errOrCloseEvent.message
36
+ : '알 수 없는 오류';
37
+ setMessages(prev => [...prev, {
38
+ type: 'error',
39
+ content: `연결 오류: ${errorMessage}`,
40
+ timestamp: new Date()
41
+ }]);
42
+ return retryCount < 5;
43
+ },
44
+ retryAttempts: 5,
45
+ onNonLazyError: (error) => {
46
+ console.error('Non-lazy error:', error);
47
+ setConnected(false);
48
+ },
49
+ });
50
+
51
+ // 포스트 생성 구독
52
+ const unsubPostCreated = client.subscribe(
53
+ {
54
+ query: `
55
+ subscription {
56
+ postCreated {
57
+ id
58
+ title
59
+ author
60
+ }
61
+ }
62
+ `,
63
+ },
64
+ {
65
+ next: (data: any) => {
66
+ const post = data.data.postCreated;
67
+ setMessages(prev => [...prev, {
68
+ type: 'created',
69
+ content: `새 포스트: ${post.title} (작성자: ${post.author})`,
70
+ timestamp: new Date()
71
+ }]);
72
+ },
73
+ error: (error: Error) => {
74
+ console.error('Subscription error:', error);
75
+ setConnected(false);
76
+ setMessages(prev => [...prev, {
77
+ type: 'error',
78
+ content: `구독 오류: ${error.message}`,
79
+ timestamp: new Date()
80
+ }]);
81
+ },
82
+ complete: () => {
83
+ console.log('Subscription completed');
84
+ setConnected(false);
85
+ },
86
+ },
87
+ );
88
+ unsubscribers.push(unsubPostCreated);
89
+
90
+ // 포스트 업데이트 구독
91
+ const unsubPostUpdated = client.subscribe(
92
+ {
93
+ query: `
94
+ subscription {
95
+ postUpdated {
96
+ id
97
+ title
98
+ }
99
+ }
100
+ `,
101
+ },
102
+ {
103
+ next: (data: any) => {
104
+ const post = data.data.postUpdated;
105
+ setMessages(prev => [...prev, {
106
+ type: 'updated',
107
+ content: `포스트 업데이트: ${post.title}`,
108
+ timestamp: new Date()
109
+ }]);
110
+ },
111
+ error: (error: Error) => {
112
+ console.error('Subscription error:', error);
113
+ setConnected(false);
114
+ setMessages(prev => [...prev, {
115
+ type: 'error',
116
+ content: `구독 오류: ${error.message}`,
117
+ timestamp: new Date()
118
+ }]);
119
+ },
120
+ complete: () => {
121
+ console.log('Subscription completed');
122
+ setConnected(false);
123
+ },
124
+ },
125
+ );
126
+ unsubscribers.push(unsubPostUpdated);
127
+
128
+ // 포스트 삭제 구독
129
+ const unsubPostDeleted = client.subscribe(
130
+ {
131
+ query: `
132
+ subscription {
133
+ postDeleted
134
+ }
135
+ `,
136
+ },
137
+ {
138
+ next: (data: any) => {
139
+ const postId = data.data.postDeleted;
140
+ setMessages(prev => [...prev, {
141
+ type: 'deleted',
142
+ content: `포스트 삭제: ${postId}`,
143
+ timestamp: new Date()
144
+ }]);
145
+ },
146
+ error: (error: Error) => {
147
+ console.error('Subscription error:', error);
148
+ setConnected(false);
149
+ setMessages(prev => [...prev, {
150
+ type: 'error',
151
+ content: `구독 오류: ${error.message}`,
152
+ timestamp: new Date()
153
+ }]);
154
+ },
155
+ complete: () => {
156
+ console.log('Subscription completed');
157
+ setConnected(false);
158
+ },
159
+ },
160
+ );
161
+ unsubscribers.push(unsubPostDeleted);
162
+
163
+ // 연결 상태 확인
164
+ const unsubPing = client.subscribe(
165
+ {
166
+ query: `
167
+ subscription {
168
+ _ping
169
+ }
170
+ `,
171
+ },
172
+ {
173
+ next: () => {
174
+ setConnected(true);
175
+ setLastPing(new Date());
176
+ setRetryCount(0);
177
+ setMessages(prev => [...prev, {
178
+ type: 'info',
179
+ content: '연결됨',
180
+ timestamp: new Date()
181
+ }]);
182
+ },
183
+ error: (error: Error) => {
184
+ setConnected(false);
185
+ setLastPing(null);
186
+ setRetryCount(prev => prev + 1);
187
+ setMessages(prev => [...prev, {
188
+ type: 'error',
189
+ content: '연결 끊김',
190
+ timestamp: new Date()
191
+ }]);
192
+ },
193
+ complete: () => {
194
+ console.log('Ping subscription completed');
195
+ setConnected(false);
196
+ setLastPing(null);
197
+ },
198
+ },
199
+ );
200
+ unsubscribers.push(unsubPing);
201
+
202
+ // 연결 상태 모니터링
203
+ pingInterval = setInterval(() => {
204
+ if (lastPing) {
205
+ const now = new Date();
206
+ const diff = now.getTime() - lastPing.getTime();
207
+ if (diff > 10000) {
208
+ setConnected(false);
209
+ setMessages(prev => [...prev, {
210
+ type: 'error',
211
+ content: 'ping 시간 초과',
212
+ timestamp: new Date()
213
+ }]);
214
+ // 재연결 시도
215
+ connect();
216
+ }
217
+ }
218
+ }, 5000);
219
+ } catch (error) {
220
+ console.error('Connection setup error:', error);
221
+ setMessages(prev => [...prev, {
222
+ type: 'error',
223
+ content: `연결 설정 오류: ${error instanceof Error ? error.message : String(error)}`,
224
+ timestamp: new Date()
225
+ }]);
226
+ }
227
+ };
228
+
229
+ // 초기 연결 시도
230
+ connect();
231
+
232
+ return () => {
233
+ unsubscribers.forEach(unsub => unsub());
234
+ if (pingInterval) {
235
+ clearInterval(pingInterval);
236
+ }
237
+ };
238
+ }, [lastPing, retryCount]);
239
+
240
+ return (
241
+ <div className="p-4">
242
+ <h2 className="text-xl font-bold mb-4">WebSocket 테스트</h2>
243
+ <div className="mb-4">
244
+ <span className={`inline-block px-3 py-1 rounded-full text-sm ${
245
+ connected ? 'bg-green-100 text-green-800' : 'bg-red-100 text-red-800'
246
+ }`}>
247
+ {connected ? '연결됨' : '연결 중...'}
248
+ {retryCount > 0 && ` (재시도: ${retryCount})`}
249
+ </span>
250
+ {lastPing && (
251
+ <span className="ml-2 text-sm text-gray-500">
252
+ 마지막 ping: {lastPing.toLocaleTimeString()}
253
+ </span>
254
+ )}
255
+ </div>
256
+ <div className="space-y-2">
257
+ {messages.map((msg, idx) => (
258
+ <div
259
+ key={idx}
260
+ className={`p-3 rounded ${
261
+ msg.type === 'created'
262
+ ? 'bg-green-50 text-green-700'
263
+ : msg.type === 'updated'
264
+ ? 'bg-blue-50 text-blue-700'
265
+ : msg.type === 'deleted'
266
+ ? 'bg-red-50 text-red-700'
267
+ : msg.type === 'error'
268
+ ? 'bg-red-100 text-red-800'
269
+ : 'bg-gray-50 text-gray-700'
270
+ }`}
271
+ >
272
+ <div className="flex justify-between items-center">
273
+ <span>{msg.content}</span>
274
+ <span className="text-xs text-gray-500">
275
+ {msg.timestamp.toLocaleTimeString()}
276
+ </span>
277
+ </div>
278
+ </div>
279
+ ))}
280
+ </div>
281
+ </div>
282
+ );
283
+ }
src/graphql/resolvers.ts ADDED
@@ -0,0 +1,317 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import { createPubSub } from 'graphql-yoga';
2
+ import { getAllPosts, getPostById, createPost, updatePost, deletePost, Post } from '../lib/markdown';
3
+ import { getCommitHistory, rollbackToCommit, getFileDiff } from '../lib/git';
4
+ import { User, NFT, Contribution, ContributionType, ContributionStatus, LeaderboardEntry } from '../types/incentive';
5
+
6
+ interface PostInput {
7
+ title: string;
8
+ content: string;
9
+ author: string;
10
+ date: string;
11
+ tags: string[];
12
+ category: string;
13
+ }
14
+
15
+ type PubSubEvents = {
16
+ [K in 'POST_CREATED' | 'POST_UPDATED' | 'POST_DELETED' | 'COMMIT_CREATED' | '_PING']: K extends '_PING'
17
+ ? boolean
18
+ : K extends 'POST_DELETED'
19
+ ? string
20
+ : K extends 'COMMIT_CREATED'
21
+ ? any
22
+ : Post;
23
+ };
24
+
25
+ interface PubSubEvents {
26
+ USER_POINTS_UPDATED: { userPointsUpdated: User };
27
+ NEW_NFT_MINTED: { newNFTMinted: NFT };
28
+ CONTRIBUTION_STATUS_CHANGED: { contributionStatusChanged: Contribution };
29
+ LEADERBOARD_UPDATED: { leaderboardUpdated: LeaderboardEntry[] };
30
+ }
31
+
32
+ const pubsub = createPubSub<PubSubEvents>();
33
+ let posts: Post[] = [];
34
+ let users: User[] = [];
35
+ let nfts: NFT[] = [];
36
+ let contributions: Contribution[] = [];
37
+ let tokenIdCounter = 1;
38
+
39
+ // 포인트 계산 규칙
40
+ const CONTRIBUTION_POINTS = {
41
+ [ContributionType.POST_CREATION]: 10,
42
+ [ContributionType.COMMENT]: 5,
43
+ [ContributionType.REVIEW]: 15,
44
+ [ContributionType.BUG_REPORT]: 20,
45
+ [ContributionType.FEATURE_SUGGESTION]: 15,
46
+ [ContributionType.CODE_CONTRIBUTION]: 30,
47
+ };
48
+
49
+ // 레벨 계산 함수
50
+ const calculateLevel = (points: number): number => {
51
+ return Math.floor(points / 100) + 1;
52
+ };
53
+
54
+ // 리더보드 업데이트 함수
55
+ const updateLeaderboard = (): LeaderboardEntry[] => {
56
+ const leaderboard = users.map(user => {
57
+ const userContributions = contributions.filter(
58
+ c => c.user.id === user.id && c.status === ContributionStatus.APPROVED
59
+ );
60
+ return {
61
+ user,
62
+ totalPoints: user.points,
63
+ contributionCount: userContributions.length,
64
+ rank: 0,
65
+ };
66
+ });
67
+
68
+ // 포인트로 정렬하고 순위 할당
69
+ return leaderboard
70
+ .sort((a, b) => b.totalPoints - a.totalPoints)
71
+ .map((entry, index) => ({
72
+ ...entry,
73
+ rank: index + 1,
74
+ }));
75
+ };
76
+
77
+ export const resolvers = {
78
+ Query: {
79
+ posts: () => posts,
80
+ post: (_: any, { id }: { id: string }) => {
81
+ return posts.find(post => post.id === id);
82
+ },
83
+ postsByTag: async (_: unknown, { tag }: { tag: string }) => {
84
+ const posts = await getAllPosts();
85
+ return posts.filter(post => post.tags.includes(tag));
86
+ },
87
+ postsByCategory: async (_: unknown, { category }: { category: string }) => {
88
+ const posts = await getAllPosts();
89
+ return posts.filter(post => post.category === category);
90
+ },
91
+ searchPosts: async (_: unknown, { query }: { query: string }) => {
92
+ const posts = await getAllPosts();
93
+ const searchQuery = query.toLowerCase();
94
+ return posts.filter(post =>
95
+ post.title.toLowerCase().includes(searchQuery) ||
96
+ post.content.toLowerCase().includes(searchQuery)
97
+ );
98
+ },
99
+ commits: async () => {
100
+ return await getCommitHistory();
101
+ },
102
+ fileDiff: async (_: unknown, { commitHash1, commitHash2, filePath }: { commitHash1: string, commitHash2: string, filePath: string }) => {
103
+ return await getFileDiff(commitHash1, commitHash2, filePath);
104
+ },
105
+ users: () => users,
106
+ user: (_: any, { id }: { id: string }) => users.find(user => user.id === id),
107
+ nfts: () => nfts,
108
+ nft: (_: any, { id }: { id: string }) => nfts.find(nft => nft.id === id),
109
+ contributions: () => contributions,
110
+ contribution: (_: any, { id }: { id: string }) =>
111
+ contributions.find(contribution => contribution.id === id),
112
+ leaderboard: (_: any, { limit }: { limit?: number }) => {
113
+ const board = updateLeaderboard();
114
+ return limit ? board.slice(0, limit) : board;
115
+ },
116
+ userRank: (_: any, { userId }: { userId: string }) => {
117
+ const board = updateLeaderboard();
118
+ return board.find(entry => entry.user.id === userId);
119
+ },
120
+ },
121
+
122
+ Mutation: {
123
+ createPost: async (_: any, { input }: { input: Omit<Post, 'id'> }) => {
124
+ const post: Post = {
125
+ id: Math.random().toString().slice(2),
126
+ ...input
127
+ };
128
+
129
+ posts.push(post);
130
+ console.log('Published POST_CREATED event:', post);
131
+ await pubsub.publish('POST_CREATED', { postCreated: post });
132
+ return post;
133
+ },
134
+
135
+ updatePost: async (_: any, { id, input }: { id: string; input: Partial<Post> }) => {
136
+ const index = posts.findIndex(post => post.id === id);
137
+ if (index !== -1) {
138
+ const post = { ...posts[index], ...input };
139
+ posts[index] = post;
140
+ await pubsub.publish('POST_UPDATED', { postUpdated: post });
141
+ return post;
142
+ }
143
+ return null;
144
+ },
145
+
146
+ deletePost: async (_: any, { id }: { id: string }) => {
147
+ const index = posts.findIndex(post => post.id === id);
148
+ if (index !== -1) {
149
+ posts.splice(index, 1);
150
+ await pubsub.publish('POST_DELETED', { postDeleted: id });
151
+ return id;
152
+ }
153
+ return null;
154
+ },
155
+ rollbackCommit: async (_: unknown, { commitHash }: { commitHash: string }) => {
156
+ return await rollbackToCommit(commitHash);
157
+ },
158
+ createUser: async (_: any, { input }: { input: { name: string; email: string } }) => {
159
+ const user: User = {
160
+ id: Math.random().toString(36).substr(2, 9),
161
+ ...input,
162
+ points: 0,
163
+ level: 1,
164
+ nfts: [],
165
+ contributions: [],
166
+ rank: null,
167
+ };
168
+ users.push(user);
169
+ return user;
170
+ },
171
+ mintNFT: async (_: any, { input }: { input: { title: string; description?: string; imageUrl: string; category: string; rarity: string } }) => {
172
+ const nft: NFT = {
173
+ id: Math.random().toString(36).substr(2, 9),
174
+ tokenId: `TOKEN_${tokenIdCounter++}`,
175
+ ...input,
176
+ owner: users[0], // 임시로 첫 번째 사용자에게 할당
177
+ createdAt: new Date().toISOString(),
178
+ };
179
+ nfts.push(nft);
180
+ await pubsub.publish('NEW_NFT_MINTED', { newNFTMinted: nft });
181
+ return nft;
182
+ },
183
+ createContribution: async (_: any, { input }: { input: { userId: string; type: ContributionType; description: string } }) => {
184
+ const user = users.find(u => u.id === input.userId);
185
+ if (!user) throw new Error('User not found');
186
+
187
+ const contribution: Contribution = {
188
+ id: Math.random().toString(36).substr(2, 9),
189
+ user,
190
+ type: input.type,
191
+ points: CONTRIBUTION_POINTS[input.type],
192
+ description: input.description,
193
+ createdAt: new Date().toISOString(),
194
+ status: ContributionStatus.PENDING,
195
+ };
196
+ contributions.push(contribution);
197
+ return contribution;
198
+ },
199
+ approveContribution: async (_: any, { id }: { id: string }) => {
200
+ const contribution = contributions.find(c => c.id === id);
201
+ if (!contribution) throw new Error('Contribution not found');
202
+ if (contribution.status !== ContributionStatus.PENDING)
203
+ throw new Error('Contribution is not pending');
204
+
205
+ contribution.status = ContributionStatus.APPROVED;
206
+
207
+ // 사용자 포인트 업데이트
208
+ const user = users.find(u => u.id === contribution.user.id);
209
+ if (user) {
210
+ user.points += contribution.points;
211
+ user.level = calculateLevel(user.points);
212
+ await pubsub.publish('USER_POINTS_UPDATED', { userPointsUpdated: user });
213
+ }
214
+
215
+ // 리더보드 업데이트
216
+ const leaderboard = updateLeaderboard();
217
+ await pubsub.publish('LEADERBOARD_UPDATED', { leaderboardUpdated: leaderboard });
218
+
219
+ await pubsub.publish('CONTRIBUTION_STATUS_CHANGED', { contributionStatusChanged: contribution });
220
+ return contribution;
221
+ },
222
+ rejectContribution: async (_: any, { id }: { id: string }) => {
223
+ const contribution = contributions.find(c => c.id === id);
224
+ if (!contribution) throw new Error('Contribution not found');
225
+ if (contribution.status !== ContributionStatus.PENDING)
226
+ throw new Error('Contribution is not pending');
227
+
228
+ contribution.status = ContributionStatus.REJECTED;
229
+ await pubsub.publish('CONTRIBUTION_STATUS_CHANGED', { contributionStatusChanged: contribution });
230
+ return contribution;
231
+ },
232
+ },
233
+
234
+ Subscription: {
235
+ postCreated: {
236
+ subscribe: () => pubsub.subscribe('POST_CREATED'),
237
+ resolve: (payload: { postCreated: Post }) => payload.postCreated
238
+ },
239
+
240
+ postUpdated: {
241
+ subscribe: () => pubsub.subscribe('POST_UPDATED'),
242
+ resolve: (payload: { postUpdated: Post }) => payload.postUpdated
243
+ },
244
+
245
+ postDeleted: {
246
+ subscribe: () => pubsub.subscribe('POST_DELETED'),
247
+ resolve: (payload: { postDeleted: string }) => payload.postDeleted
248
+ },
249
+
250
+ commitCreated: {
251
+ subscribe: () => pubsub.subscribe('COMMIT_CREATED'),
252
+ resolve: (payload: any) => payload
253
+ },
254
+
255
+ _ping: {
256
+ subscribe: () => {
257
+ const interval = setInterval(() => {
258
+ pubsub.publish('_PING', { _ping: true });
259
+ }, 5000);
260
+
261
+ return pubsub.subscribe('_PING');
262
+ },
263
+ resolve: (payload: { _ping: boolean }) => payload._ping
264
+ },
265
+ userPointsUpdated: {
266
+ subscribe: (_: any, { userId }: { userId: string }) => ({
267
+ [Symbol.asyncIterator]: () => ({
268
+ next: async () => {
269
+ const user = await new Promise<User>(resolve => {
270
+ pubsub.subscribe('USER_POINTS_UPDATED', ({ userPointsUpdated }) => {
271
+ if (userPointsUpdated.id === userId) {
272
+ resolve(userPointsUpdated);
273
+ }
274
+ });
275
+ });
276
+ return { value: { userPointsUpdated: user }, done: false };
277
+ },
278
+ }),
279
+ }),
280
+ },
281
+ newNFTMinted: {
282
+ subscribe: (_: any, { userId }: { userId: string }) => ({
283
+ [Symbol.asyncIterator]: () => ({
284
+ next: async () => {
285
+ const nft = await new Promise<NFT>(resolve => {
286
+ pubsub.subscribe('NEW_NFT_MINTED', ({ newNFTMinted }) => {
287
+ if (newNFTMinted.owner.id === userId) {
288
+ resolve(newNFTMinted);
289
+ }
290
+ });
291
+ });
292
+ return { value: { newNFTMinted: nft }, done: false };
293
+ },
294
+ }),
295
+ }),
296
+ },
297
+ contributionStatusChanged: {
298
+ subscribe: (_: any, { userId }: { userId: string }) => ({
299
+ [Symbol.asyncIterator]: () => ({
300
+ next: async () => {
301
+ const contribution = await new Promise<Contribution>(resolve => {
302
+ pubsub.subscribe('CONTRIBUTION_STATUS_CHANGED', ({ contributionStatusChanged }) => {
303
+ if (contributionStatusChanged.user.id === userId) {
304
+ resolve(contributionStatusChanged);
305
+ }
306
+ });
307
+ });
308
+ return { value: { contributionStatusChanged: contribution }, done: false };
309
+ },
310
+ }),
311
+ }),
312
+ },
313
+ leaderboardUpdated: {
314
+ subscribe: () => pubsub.subscribe('LEADERBOARD_UPDATED'),
315
+ },
316
+ },
317
+ };
src/graphql/schema.ts ADDED
@@ -0,0 +1,166 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import { createSchema } from 'graphql-yoga';
2
+ import { resolvers } from './resolvers';
3
+
4
+ const typeDefs = `#graphql
5
+ type Post {
6
+ id: ID!
7
+ title: String!
8
+ content: String!
9
+ author: String!
10
+ date: String!
11
+ tags: [String!]!
12
+ category: String
13
+ }
14
+
15
+ input PostInput {
16
+ title: String!
17
+ content: String!
18
+ author: String!
19
+ date: String!
20
+ tags: [String!]!
21
+ category: String
22
+ }
23
+
24
+ input PostUpdateInput {
25
+ title: String
26
+ content: String
27
+ author: String
28
+ date: String
29
+ tags: [String!]
30
+ category: String
31
+ }
32
+
33
+ type Commit {
34
+ hash: String!
35
+ message: String!
36
+ date: String!
37
+ author: String!
38
+ }
39
+
40
+ type FileDiff {
41
+ oldContent: String!
42
+ newContent: String!
43
+ changes: [String!]!
44
+ }
45
+
46
+ type User {
47
+ id: ID!
48
+ name: String!
49
+ email: String!
50
+ points: Int!
51
+ level: Int!
52
+ nfts: [NFT!]!
53
+ contributions: [Contribution!]!
54
+ rank: Int
55
+ }
56
+
57
+ type NFT {
58
+ id: ID!
59
+ tokenId: String!
60
+ title: String!
61
+ description: String
62
+ imageUrl: String!
63
+ owner: User!
64
+ createdAt: String!
65
+ category: String!
66
+ rarity: String!
67
+ }
68
+
69
+ type Contribution {
70
+ id: ID!
71
+ user: User!
72
+ type: ContributionType!
73
+ points: Int!
74
+ description: String!
75
+ createdAt: String!
76
+ status: ContributionStatus!
77
+ }
78
+
79
+ enum ContributionType {
80
+ POST_CREATION
81
+ COMMENT
82
+ REVIEW
83
+ BUG_REPORT
84
+ FEATURE_SUGGESTION
85
+ CODE_CONTRIBUTION
86
+ }
87
+
88
+ enum ContributionStatus {
89
+ PENDING
90
+ APPROVED
91
+ REJECTED
92
+ }
93
+
94
+ type LeaderboardEntry {
95
+ user: User!
96
+ totalPoints: Int!
97
+ rank: Int!
98
+ contributionCount: Int!
99
+ }
100
+
101
+ type Query {
102
+ posts: [Post!]!
103
+ post(id: ID!): Post
104
+ postsByTag(tag: String!): [Post!]!
105
+ postsByCategory(category: String!): [Post!]!
106
+ searchPosts(query: String!): [Post!]!
107
+ commits: [Commit!]!
108
+ fileDiff(commitHash1: String!, commitHash2: String!, filePath: String!): FileDiff!
109
+ users: [User!]!
110
+ user(id: ID!): User
111
+ nfts: [NFT!]!
112
+ nft(id: ID!): NFT
113
+ contributions: [Contribution!]!
114
+ contribution(id: ID!): Contribution
115
+ leaderboard(limit: Int): [LeaderboardEntry!]!
116
+ userRank(userId: ID!): LeaderboardEntry
117
+ }
118
+
119
+ input CreateUserInput {
120
+ name: String!
121
+ email: String!
122
+ }
123
+
124
+ input CreateNFTInput {
125
+ title: String!
126
+ description: String
127
+ imageUrl: String!
128
+ category: String!
129
+ rarity: String!
130
+ }
131
+
132
+ input CreateContributionInput {
133
+ userId: ID!
134
+ type: ContributionType!
135
+ description: String!
136
+ }
137
+
138
+ type Mutation {
139
+ createPost(input: PostInput!): Post!
140
+ updatePost(id: ID!, input: PostInput!): Post
141
+ deletePost(id: ID!): ID
142
+ rollbackCommit(commitHash: String!): Boolean!
143
+ createUser(input: CreateUserInput!): User!
144
+ mintNFT(input: CreateNFTInput!): NFT!
145
+ createContribution(input: CreateContributionInput!): Contribution!
146
+ approveContribution(id: ID!): Contribution!
147
+ rejectContribution(id: ID!): Contribution!
148
+ }
149
+
150
+ type Subscription {
151
+ postCreated: Post!
152
+ postUpdated: Post!
153
+ postDeleted: ID!
154
+ commitCreated: Commit!
155
+ _ping: Boolean!
156
+ userPointsUpdated(userId: ID!): User!
157
+ newNFTMinted(userId: ID!): NFT!
158
+ contributionStatusChanged(userId: ID!): Contribution!
159
+ leaderboardUpdated: [LeaderboardEntry!]!
160
+ }
161
+ `;
162
+
163
+ export const schema = createSchema({
164
+ typeDefs,
165
+ resolvers,
166
+ });
src/lib/git-server.ts ADDED
@@ -0,0 +1,88 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import { exec as nodeExec } from 'node:child_process';
2
+ import { promisify } from 'node:util';
3
+
4
+ const execAsync = promisify(nodeExec);
5
+
6
+ export interface Commit {
7
+ hash: string;
8
+ message: string;
9
+ date: string;
10
+ author: string;
11
+ }
12
+
13
+ export interface FileDiff {
14
+ oldContent: string;
15
+ newContent: string;
16
+ changes: string[];
17
+ }
18
+
19
+ export async function getCommitHistory(): Promise<Commit[]> {
20
+ try {
21
+ const { stdout } = await execAsync(
22
+ 'git log --pretty=format:"%H|%s|%ai|%an"'
23
+ );
24
+
25
+ return stdout.split('\n').map(line => {
26
+ const [hash, message, date, author] = line.split('|');
27
+ return { hash, message, date, author };
28
+ });
29
+ } catch (error) {
30
+ console.error('Error getting commit history:', error);
31
+ return [];
32
+ }
33
+ }
34
+
35
+ export async function rollbackToCommit(commitHash: string): Promise<boolean> {
36
+ try {
37
+ await execAsync(`git reset --hard ${commitHash}`);
38
+ return true;
39
+ } catch (error) {
40
+ console.error('Error rolling back to commit:', error);
41
+ return false;
42
+ }
43
+ }
44
+
45
+ export async function getFileDiff(
46
+ commitHash1: string,
47
+ commitHash2: string,
48
+ filePath: string
49
+ ): Promise<FileDiff> {
50
+ try {
51
+ const { stdout: oldContent } = await execAsync(
52
+ `git show ${commitHash1}:${filePath}`
53
+ );
54
+
55
+ const { stdout: newContent } = await execAsync(
56
+ `git show ${commitHash2}:${filePath}`
57
+ );
58
+
59
+ const { stdout: diffOutput } = await execAsync(
60
+ `git diff ${commitHash1} ${commitHash2} -- ${filePath}`
61
+ );
62
+
63
+ const changes = diffOutput
64
+ .split('\n')
65
+ .filter(line => line.startsWith('+') || line.startsWith('-'))
66
+ .map(line => line.substring(1).trim());
67
+
68
+ return {
69
+ oldContent,
70
+ newContent,
71
+ changes
72
+ };
73
+ } catch (error) {
74
+ console.error('Error getting file diff:', error);
75
+ throw new Error('Failed to get file diff');
76
+ }
77
+ }
78
+
79
+ export async function commitChanges(message: string): Promise<string> {
80
+ try {
81
+ await execAsync('git add .');
82
+ const { stdout } = await execAsync(`git commit -m "${message}"`);
83
+ return stdout;
84
+ } catch (error) {
85
+ console.error('Error committing changes:', error);
86
+ throw new Error('Failed to commit changes');
87
+ }
88
+ }
src/lib/git.ts CHANGED
@@ -1,18 +1,19 @@
1
  'use client';
2
 
3
- export interface CommitInfo {
4
  hash: string;
5
- author: string;
6
- date: string;
7
  message: string;
8
- changes: {
9
- added: string[];
10
- modified: string[];
11
- deleted: string[];
12
- };
13
  }
14
 
15
- export async function getCommitHistory(): Promise<CommitInfo[]> {
 
 
 
 
 
 
16
  try {
17
  const response = await fetch('/api/git?action=history');
18
  if (!response.ok) {
@@ -22,11 +23,11 @@ export async function getCommitHistory(): Promise<CommitInfo[]> {
22
  return data.commits;
23
  } catch (error) {
24
  console.error('커밋 이력 조회 실패:', error);
25
- throw error;
26
  }
27
  }
28
 
29
- export async function rollbackToCommit(commitHash: string): Promise<void> {
30
  try {
31
  const response = await fetch('/api/git', {
32
  method: 'POST',
@@ -40,32 +41,32 @@ export async function rollbackToCommit(commitHash: string): Promise<void> {
40
  });
41
 
42
  if (!response.ok) {
43
- const data = await response.json();
44
- throw new Error(data.error || '롤백 실패');
45
  }
 
 
 
46
  } catch (error) {
47
  console.error('롤백 실패:', error);
48
- throw error;
49
  }
50
  }
51
 
52
  export async function getFileDiff(
53
- filePath: string,
54
  commitHash1: string,
55
- commitHash2: string
56
- ): Promise<string> {
 
57
  try {
58
  const response = await fetch(
59
  `/api/git?action=diff&hash1=${commitHash1}&hash2=${commitHash2}&file=${encodeURIComponent(filePath)}`
60
  );
61
 
62
  if (!response.ok) {
63
- const data = await response.json();
64
- throw new Error(data.error || '파일 변경사항 비교 실패');
65
  }
66
 
67
- const data = await response.json();
68
- return data.diff;
69
  } catch (error) {
70
  console.error('파일 변경사항 비교 실패:', error);
71
  throw error;
 
1
  'use client';
2
 
3
+ export interface Commit {
4
  hash: string;
 
 
5
  message: string;
6
+ date: string;
7
+ author: string;
 
 
 
8
  }
9
 
10
+ export interface FileDiff {
11
+ oldContent: string;
12
+ newContent: string;
13
+ changes: string[];
14
+ }
15
+
16
+ export async function getCommitHistory(): Promise<Commit[]> {
17
  try {
18
  const response = await fetch('/api/git?action=history');
19
  if (!response.ok) {
 
23
  return data.commits;
24
  } catch (error) {
25
  console.error('커밋 이력 조회 실패:', error);
26
+ return [];
27
  }
28
  }
29
 
30
+ export async function rollbackToCommit(commitHash: string): Promise<boolean> {
31
  try {
32
  const response = await fetch('/api/git', {
33
  method: 'POST',
 
41
  });
42
 
43
  if (!response.ok) {
44
+ throw new Error('롤백 실패');
 
45
  }
46
+
47
+ const data = await response.json();
48
+ return data.success;
49
  } catch (error) {
50
  console.error('롤백 실패:', error);
51
+ return false;
52
  }
53
  }
54
 
55
  export async function getFileDiff(
 
56
  commitHash1: string,
57
+ commitHash2: string,
58
+ filePath: string
59
+ ): Promise<FileDiff> {
60
  try {
61
  const response = await fetch(
62
  `/api/git?action=diff&hash1=${commitHash1}&hash2=${commitHash2}&file=${encodeURIComponent(filePath)}`
63
  );
64
 
65
  if (!response.ok) {
66
+ throw new Error('파일 변경사항 비교 실패');
 
67
  }
68
 
69
+ return await response.json();
 
70
  } catch (error) {
71
  console.error('파일 변경사항 비교 실패:', error);
72
  throw error;
src/lib/markdown.ts CHANGED
@@ -1,10 +1,10 @@
1
- import fs from 'fs';
2
  import path from 'path';
3
  import matter from 'gray-matter';
4
  import { remark } from 'remark';
5
  import html from 'remark-html';
6
 
7
- const contentDirectory = path.join(process.cwd(), 'src/content');
8
 
9
  export interface PostMetadata {
10
  title: string;
@@ -21,8 +21,8 @@ export interface Post extends PostMetadata {
21
 
22
  export async function getPostById(id: string): Promise<Post | null> {
23
  try {
24
- const fullPath = path.join(contentDirectory, `${id}.md`);
25
- const fileContents = fs.readFileSync(fullPath, 'utf8');
26
 
27
  const { data, content } = matter(fileContents);
28
  const processedContent = await remark()
@@ -36,7 +36,7 @@ export async function getPostById(id: string): Promise<Post | null> {
36
  date: data.date,
37
  author: data.author,
38
  tags: data.tags || [],
39
- category: data.category
40
  };
41
  } catch (error) {
42
  console.error(`Error getting post ${id}:`, error);
@@ -45,31 +45,13 @@ export async function getPostById(id: string): Promise<Post | null> {
45
  }
46
 
47
  export async function getAllPosts(): Promise<Post[]> {
48
- const posts: Post[] = [];
49
-
50
- try {
51
- const directories = ['articles', 'docs'];
52
-
53
- for (const dir of directories) {
54
- const dirPath = path.join(contentDirectory, dir);
55
- if (!fs.existsSync(dirPath)) continue;
56
-
57
- const files = fs.readdirSync(dirPath);
58
-
59
- for (const file of files) {
60
- if (!file.endsWith('.md')) continue;
61
-
62
- const id = `${dir}/${file.replace(/\.md$/, '')}`;
63
- const post = await getPostById(id);
64
- if (post) posts.push(post);
65
- }
66
- }
67
-
68
- return posts.sort((a, b) => (new Date(b.date)).getTime() - (new Date(a.date)).getTime());
69
- } catch (error) {
70
- console.error('Error getting all posts:', error);
71
- return [];
72
- }
73
  }
74
 
75
  export async function getPostsByTag(tag: string): Promise<Post[]> {
@@ -102,4 +84,59 @@ export async function getAllCategories(): Promise<string[]> {
102
  });
103
 
104
  return Array.from(categorySet).sort();
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
105
  }
 
1
+ import fs from 'fs/promises';
2
  import path from 'path';
3
  import matter from 'gray-matter';
4
  import { remark } from 'remark';
5
  import html from 'remark-html';
6
 
7
+ const POSTS_DIRECTORY = path.join(process.cwd(), 'posts');
8
 
9
  export interface PostMetadata {
10
  title: string;
 
21
 
22
  export async function getPostById(id: string): Promise<Post | null> {
23
  try {
24
+ const fullPath = path.join(POSTS_DIRECTORY, `${id}.md`);
25
+ const fileContents = await fs.readFile(fullPath, 'utf8');
26
 
27
  const { data, content } = matter(fileContents);
28
  const processedContent = await remark()
 
36
  date: data.date,
37
  author: data.author,
38
  tags: data.tags || [],
39
+ category: data.category || 'uncategorized'
40
  };
41
  } catch (error) {
42
  console.error(`Error getting post ${id}:`, error);
 
45
  }
46
 
47
  export async function getAllPosts(): Promise<Post[]> {
48
+ const files = await fs.readdir(POSTS_DIRECTORY);
49
+ const posts = await Promise.all(
50
+ files.map(async (filename) => {
51
+ return await getPostById(filename.replace('.md', ''));
52
+ })
53
+ );
54
+ return posts.filter((post): post is Post => post !== null);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
55
  }
56
 
57
  export async function getPostsByTag(tag: string): Promise<Post[]> {
 
84
  });
85
 
86
  return Array.from(categorySet).sort();
87
+ }
88
+
89
+ export async function createPost(input: Omit<Post, 'id'>): Promise<Post> {
90
+ const id = Date.now().toString();
91
+ const post = { id, ...input };
92
+
93
+ const fileContent = `---
94
+ title: ${post.title}
95
+ author: ${post.author}
96
+ date: ${post.date}
97
+ tags: ${JSON.stringify(post.tags)}
98
+ category: ${post.category}
99
+ ---
100
+
101
+ ${post.content}`;
102
+
103
+ await fs.writeFile(
104
+ path.join(POSTS_DIRECTORY, `${id}.md`),
105
+ fileContent
106
+ );
107
+
108
+ return post;
109
+ }
110
+
111
+ export async function updatePost(id: string, input: Partial<Omit<Post, 'id'>>): Promise<Post | null> {
112
+ const existingPost = await getPostById(id);
113
+ if (!existingPost) return null;
114
+
115
+ const updatedPost = { ...existingPost, ...input };
116
+
117
+ const fileContent = `---
118
+ title: ${updatedPost.title}
119
+ author: ${updatedPost.author}
120
+ date: ${updatedPost.date}
121
+ tags: ${JSON.stringify(updatedPost.tags)}
122
+ category: ${updatedPost.category}
123
+ ---
124
+
125
+ ${updatedPost.content}`;
126
+
127
+ await fs.writeFile(
128
+ path.join(POSTS_DIRECTORY, `${id}.md`),
129
+ fileContent
130
+ );
131
+
132
+ return updatedPost;
133
+ }
134
+
135
+ export async function deletePost(id: string): Promise<boolean> {
136
+ try {
137
+ await fs.unlink(path.join(POSTS_DIRECTORY, `${id}.md`));
138
+ return true;
139
+ } catch (error) {
140
+ return false;
141
+ }
142
  }
src/lib/socket.ts ADDED
@@ -0,0 +1,58 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import { Server as SocketServer } from 'socket.io';
2
+ import { Server as HttpServer } from 'http';
3
+
4
+ let io: SocketServer | null = null;
5
+
6
+ export function initSocketServer(server: HttpServer) {
7
+ if (!io) {
8
+ io = new SocketServer(server, {
9
+ path: '/api/socket',
10
+ addTrailingSlash: false,
11
+ });
12
+
13
+ io.on('connection', (socket) => {
14
+ console.log('클라이언트 연결됨:', socket.id);
15
+
16
+ socket.on('subscribe_post', (postId) => {
17
+ socket.join(`post:${postId}`);
18
+ });
19
+
20
+ socket.on('unsubscribe_post', (postId) => {
21
+ socket.leave(`post:${postId}`);
22
+ });
23
+
24
+ socket.on('disconnect', () => {
25
+ console.log('클라이언트 연결 해제됨:', socket.id);
26
+ });
27
+ });
28
+ }
29
+ return io;
30
+ }
31
+
32
+ export function getSocketServer() {
33
+ return io;
34
+ }
35
+
36
+ export function emitPostUpdate(postId: string, data: any) {
37
+ if (io) {
38
+ io.to(`post:${postId}`).emit('post_updated', data);
39
+ }
40
+ }
41
+
42
+ export function emitPostCreated(data: any) {
43
+ if (io) {
44
+ io.emit('post_created', data);
45
+ }
46
+ }
47
+
48
+ export function emitPostDeleted(postId: string) {
49
+ if (io) {
50
+ io.emit('post_deleted', postId);
51
+ }
52
+ }
53
+
54
+ export function emitCommitCreated(data: any) {
55
+ if (io) {
56
+ io.emit('commit_created', data);
57
+ }
58
+ }
src/lib/validation.ts ADDED
@@ -0,0 +1,32 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import { z } from 'zod';
2
+
3
+ export const PostSchema = z.object({
4
+ title: z.string().min(1, '제목은 필수입니다.'),
5
+ content: z.string().min(1, '내용은 필수입니다.'),
6
+ author: z.string().min(1, '작성자는 필수입니다.'),
7
+ tags: z.array(z.string()).min(1, '최소 1개의 태그가 필요합니다.'),
8
+ category: z.string().optional(),
9
+ date: z.string().optional(),
10
+ });
11
+
12
+ export const CommitSchema = z.object({
13
+ hash: z.string().min(1, '커밋 해시는 필수입니다.'),
14
+ message: z.string().min(1, '커밋 메시지는 필수입니다.'),
15
+ });
16
+
17
+ export const SearchQuerySchema = z.object({
18
+ query: z.string().min(1, '검색어는 필수입니다.'),
19
+ tags: z.array(z.string()).optional(),
20
+ category: z.string().optional(),
21
+ });
22
+
23
+ export const FileDiffSchema = z.object({
24
+ commitHash1: z.string().min(1, '첫 번째 커밋 해시는 필수입니다.'),
25
+ commitHash2: z.string().min(1, '두 번째 커밋 해시는 필수입니다.'),
26
+ filePath: z.string().min(1, '파일 경로는 필수입니다.'),
27
+ });
28
+
29
+ export type Post = z.infer<typeof PostSchema>;
30
+ export type Commit = z.infer<typeof CommitSchema>;
31
+ export type SearchQuery = z.infer<typeof SearchQuerySchema>;
32
+ export type FileDiff = z.infer<typeof FileDiffSchema>;
src/lib/websocket.ts ADDED
@@ -0,0 +1,41 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import { WebSocketServer } from 'ws';
2
+ import { typeDefs } from '../graphql/schema';
3
+ import { resolvers } from '../graphql/resolvers';
4
+ import { createSchema } from 'graphql-yoga';
5
+ import type { Context } from 'graphql-ws';
6
+
7
+ const wsSchema = createSchema({
8
+ typeDefs,
9
+ resolvers,
10
+ });
11
+
12
+ export function startWebSocketServer() {
13
+ const wsServer = new WebSocketServer({
14
+ port: 4000,
15
+ path: '/graphql'
16
+ });
17
+
18
+ const server = {
19
+ schema: wsSchema,
20
+ onConnect: (ctx: Context) => {
21
+ console.log('Client connected');
22
+ },
23
+ onDisconnect: (ctx: Context) => {
24
+ console.log('Client disconnected');
25
+ },
26
+ };
27
+
28
+ wsServer.on('connection', (socket) => {
29
+ console.log('Client connected to WebSocket server');
30
+
31
+ socket.on('message', (message) => {
32
+ console.log('Received message:', message.toString());
33
+ });
34
+
35
+ socket.on('close', () => {
36
+ console.log('Client disconnected from WebSocket server');
37
+ });
38
+ });
39
+
40
+ console.log('WebSocket Server is running on ws://localhost:4000/graphql');
41
+ }
src/types/incentive.ts ADDED
@@ -0,0 +1,54 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ export enum ContributionType {
2
+ POST_CREATION = 'POST_CREATION',
3
+ COMMENT = 'COMMENT',
4
+ REVIEW = 'REVIEW',
5
+ BUG_REPORT = 'BUG_REPORT',
6
+ FEATURE_SUGGESTION = 'FEATURE_SUGGESTION',
7
+ CODE_CONTRIBUTION = 'CODE_CONTRIBUTION',
8
+ }
9
+
10
+ export enum ContributionStatus {
11
+ PENDING = 'PENDING',
12
+ APPROVED = 'APPROVED',
13
+ REJECTED = 'REJECTED',
14
+ }
15
+
16
+ export interface User {
17
+ id: string;
18
+ name: string;
19
+ email: string;
20
+ points: number;
21
+ level: number;
22
+ nfts: NFT[];
23
+ contributions: Contribution[];
24
+ rank: number | null;
25
+ }
26
+
27
+ export interface NFT {
28
+ id: string;
29
+ tokenId: string;
30
+ title: string;
31
+ description?: string;
32
+ imageUrl: string;
33
+ owner: User;
34
+ createdAt: string;
35
+ category: string;
36
+ rarity: string;
37
+ }
38
+
39
+ export interface Contribution {
40
+ id: string;
41
+ user: User;
42
+ type: ContributionType;
43
+ points: number;
44
+ description: string;
45
+ createdAt: string;
46
+ status: ContributionStatus;
47
+ }
48
+
49
+ export interface LeaderboardEntry {
50
+ user: User;
51
+ totalPoints: number;
52
+ rank: number;
53
+ contributionCount: number;
54
+ }
tsconfig.json CHANGED
@@ -1,6 +1,6 @@
1
  {
2
  "compilerOptions": {
3
- "target": "ES2017",
4
  "lib": ["dom", "dom.iterable", "esnext"],
5
  "allowJs": true,
6
  "skipLibCheck": true,
@@ -22,6 +22,12 @@
22
  "@/*": ["./src/*"]
23
  }
24
  },
 
 
 
 
 
 
25
  "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
26
  "exclude": ["node_modules"]
27
  }
 
1
  {
2
  "compilerOptions": {
3
+ "target": "es5",
4
  "lib": ["dom", "dom.iterable", "esnext"],
5
  "allowJs": true,
6
  "skipLibCheck": true,
 
22
  "@/*": ["./src/*"]
23
  }
24
  },
25
+ "ts-node": {
26
+ "compilerOptions": {
27
+ "module": "CommonJS",
28
+ "moduleResolution": "node"
29
+ }
30
+ },
31
  "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
32
  "exclude": ["node_modules"]
33
  }