From 38d7ac5642067a1e10e092229477433cd169021d Mon Sep 17 00:00:00 2001 From: HYOJIN Date: Tue, 7 Apr 2026 10:56:10 +0900 Subject: [PATCH 1/2] =?UTF-8?q?feat:=20=ED=94=84=EB=A1=A0=ED=8A=B8?= =?UTF-8?q?=EC=97=94=EB=93=9C=20=ED=86=B5=ED=95=A9=20=ED=94=84=EB=A1=9C?= =?UTF-8?q?=EC=A0=9D=ED=8A=B8=20=EC=B4=88=EA=B8=B0=20=EA=B5=AC=EC=A1=B0=20?= =?UTF-8?q?=EA=B5=AC=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Spring Boot 3.2.1 백엔드 (com.gcsc.connection, 포트 8042, context /snp-connection) - React 19 + TypeScript + Vite 7 + Tailwind CSS 4 프론트엔드 - frontend-maven-plugin 통합 빌드 설정 - 팀 워크플로우 v1.6.1 동기화 Co-Authored-By: Claude Opus 4.6 (1M context) --- .claude/agents/explorer.md | 47 + .claude/agents/implementer.md | 55 + .claude/agents/reviewer.md | 53 + .claude/rules/frontend-code-style.md | 68 + .claude/rules/release-notes-guide.md | 38 + .claude/rules/subagent-policy.md | 61 + .claude/scripts/on-commit.sh | 14 + .claude/scripts/on-post-compact.sh | 23 + .claude/scripts/on-pre-compact.sh | 8 + .claude/skills/create-mr/SKILL.md | 1 - .claude/skills/fix-issue/SKILL.md | 1 - .claude/skills/init-project/SKILL.md | 237 +- .claude/skills/mr/SKILL.md | 279 ++ .claude/skills/push/SKILL.md | 148 + .claude/skills/release/SKILL.md | 233 + .claude/skills/sync-team-workflow/SKILL.md | 172 +- .claude/skills/version/SKILL.md | 124 + .gitignore | 8 + CLAUDE.md | 94 +- frontend/.editorconfig | 33 + frontend/.node-version | 1 + frontend/.npmrc | 5 + frontend/.prettierrc | 11 + frontend/eslint.config.js | 25 + frontend/index.html | 17 + frontend/package-lock.json | 3968 +++++++++++++++++ frontend/package.json | 35 + frontend/public/android-chrome-192x192.png | Bin 0 -> 12145 bytes frontend/public/android-chrome-512x512.png | Bin 0 -> 26809 bytes frontend/public/apple-touch-icon.png | Bin 0 -> 10764 bytes frontend/public/favicon-16x16.png | Bin 0 -> 680 bytes frontend/public/favicon-32x32.png | Bin 0 -> 1575 bytes frontend/public/favicon.ico | Bin 0 -> 15406 bytes frontend/public/site.webmanifest | 19 + frontend/src/App.tsx | 26 + frontend/src/index.css | 1 + frontend/src/main.tsx | 10 + frontend/src/vite-env.d.ts | 1 + frontend/tsconfig.app.json | 26 + frontend/tsconfig.json | 7 + frontend/tsconfig.node.json | 24 + frontend/vite.config.ts | 21 + pom.xml | 183 + .../connection/SnpConnectionApplication.java | 14 + .../connection/common/dto/ApiResponse.java | 37 + .../exception/GlobalExceptionHandler.java | 21 + .../connection/config/SecurityConfig.java | 26 + .../gcsc/connection/config/SwaggerConfig.java | 19 + .../global/controller/WebViewController.java | 21 + src/main/resources/application.yml | 60 + .../SnpConnectionApplicationTest.java | 14 + src/test/resources/application-test.yml | 13 + workflow-version.json | 29 +- 53 files changed, 6211 insertions(+), 120 deletions(-) create mode 100644 .claude/agents/explorer.md create mode 100644 .claude/agents/implementer.md create mode 100644 .claude/agents/reviewer.md create mode 100644 .claude/rules/frontend-code-style.md create mode 100644 .claude/rules/release-notes-guide.md create mode 100644 .claude/rules/subagent-policy.md create mode 100755 .claude/scripts/on-commit.sh create mode 100755 .claude/scripts/on-post-compact.sh create mode 100755 .claude/scripts/on-pre-compact.sh create mode 100644 .claude/skills/mr/SKILL.md create mode 100644 .claude/skills/push/SKILL.md create mode 100644 .claude/skills/release/SKILL.md create mode 100644 .claude/skills/version/SKILL.md create mode 100644 frontend/.editorconfig create mode 100644 frontend/.node-version create mode 100644 frontend/.npmrc create mode 100644 frontend/.prettierrc create mode 100644 frontend/eslint.config.js create mode 100644 frontend/index.html create mode 100644 frontend/package-lock.json create mode 100644 frontend/package.json create mode 100644 frontend/public/android-chrome-192x192.png create mode 100644 frontend/public/android-chrome-512x512.png create mode 100644 frontend/public/apple-touch-icon.png create mode 100644 frontend/public/favicon-16x16.png create mode 100644 frontend/public/favicon-32x32.png create mode 100644 frontend/public/favicon.ico create mode 100644 frontend/public/site.webmanifest create mode 100644 frontend/src/App.tsx create mode 100644 frontend/src/index.css create mode 100644 frontend/src/main.tsx create mode 100644 frontend/src/vite-env.d.ts create mode 100644 frontend/tsconfig.app.json create mode 100644 frontend/tsconfig.json create mode 100644 frontend/tsconfig.node.json create mode 100644 frontend/vite.config.ts create mode 100644 pom.xml create mode 100644 src/main/java/com/gcsc/connection/SnpConnectionApplication.java create mode 100644 src/main/java/com/gcsc/connection/common/dto/ApiResponse.java create mode 100644 src/main/java/com/gcsc/connection/common/exception/GlobalExceptionHandler.java create mode 100644 src/main/java/com/gcsc/connection/config/SecurityConfig.java create mode 100644 src/main/java/com/gcsc/connection/config/SwaggerConfig.java create mode 100644 src/main/java/com/gcsc/connection/global/controller/WebViewController.java create mode 100644 src/main/resources/application.yml create mode 100644 src/test/java/com/gcsc/connection/SnpConnectionApplicationTest.java create mode 100644 src/test/resources/application-test.yml diff --git a/.claude/agents/explorer.md b/.claude/agents/explorer.md new file mode 100644 index 0000000..be33b64 --- /dev/null +++ b/.claude/agents/explorer.md @@ -0,0 +1,47 @@ +--- +name: explorer +description: 코드베이스 탐색 및 분석 에이전트. 3개 이상의 파일을 탐색하거나 프로젝트 구조를 파악할 때 사용한다. +model: sonnet +tools: Read, Glob, Grep +maxTurns: 12 +--- + +지정된 영역 내에서 코드베이스를 분석하고 구조화된 결과를 반환한다. +읽기 전용 — 파일을 수정하지 않는다. + +## 자율 범위 + +- 메인 세션이 지정한 **탐색 영역**(디렉토리 또는 파일 목록) 내에서 자유롭게 탐색 +- 영역 내 파일 간 의존성 추적, 임포트 체인 분석은 자율 수행 +- 탐색 영역 밖 파일은 임포트/참조 관계 확인 목적으로만 열람 가능 + +## 입력 (메인 세션이 제공) + +- **탐색 영역**: 디렉토리 경로 또는 파일 목록 +- **목적**: 분석 목적이나 답변할 질문 (구체적일수록 좋음) + +## 출력 형식 + +``` +## 분석 결과 + +### 구조 +- 핵심 파일/디렉토리 구성 (파일:라인 근거) + +### 발견사항 +- 목적에 대한 답변 (파일:라인 근거 포함) + +### 패턴 +- 코드 컨벤션, 반복 패턴, 아키텍처 특성 + +### 확신도 +- 각 발견사항별: 확정 / 추정(근거) / 판단불가(필요 정보) + +### 범위 외 참고 +- 탐색 영역 밖에서 발견된 관련 사항 (해당 시) +``` + +## 제약 + +- 파일 수정/생성 금지 +- 정보 부족 시 추측하지 않고 "판단불가 — [필요한 정보]"로 표시 diff --git a/.claude/agents/implementer.md b/.claude/agents/implementer.md new file mode 100644 index 0000000..6ac24b6 --- /dev/null +++ b/.claude/agents/implementer.md @@ -0,0 +1,55 @@ +--- +name: implementer +description: 모듈 단위 코드 구현 에이전트. 독립 모듈 구현이나 병렬 작업이 필요할 때 사용한다. +model: sonnet +tools: Read, Write, Edit, Glob, Grep, Bash +maxTurns: 20 +--- + +메인 세션이 정의한 계약(인터페이스, 타입, 제약)에 따라 코드를 구현한다. +내부 구현 방식은 자율 판단하되, 계약과 제약을 벗어나지 않는다. + +## 자율 범위 + +- 계약(함수 시그니처, API 스펙, 타입)은 메인 세션이 확정 — 변경 불가 +- 내부 구현 로직, 헬퍼 함수, 에러 처리 방식은 자율 판단 +- **[참조]** 파일이 제공되면 해당 파일의 코드 패턴(네이밍, 구조, 에러 처리)을 따름 + +## 입력 (메인 세션이 제공) + +- **[파일]**: 수정/생성할 파일 경로 +- **[계약]**: 인터페이스, 타입, 함수 시그니처, API 스펙 등 외부 계약 +- **[참조]**: 패턴을 따를 기존 파일 (선택, 제공 시 해당 패턴 준수) +- **[제약]**: 특별한 요구사항 (선택) + +## 출력 형식 + +``` +## 구현 결과 + +### 수정 파일 +- 파일 경로 목록 + +### 파일별 변경 +- 각 파일에서 추가/수정한 내용 요약 + +### 자체 검증 +- tsc --noEmit: 통과 / 실패(에러 내용) +- [추가 검증 항목]: 결과 + +### 계약 외 판단 +- 자율 판단한 구현 결정 사항 (메인 세션 참고용) + +### 보고 사항 (해당 시) +- 계약 불충분: 추가 정보가 필요한 항목 +- 아키텍처 영향: 범위 밖 변경이 필요한 사항 +``` + +## 제약 + +- [파일]에 명시되지 않은 파일 수정 금지 +- [계약]의 시그니처/타입 임의 변경 금지 +- 아키텍처 변경이 필요하면 구현하지 않고 "보고 사항"에 기록 +- 커밋/푸시 금지 +- any 타입 금지, strict 모드 준수 +- 구현 완료 후 tsc --noEmit 자체 검증 수행 diff --git a/.claude/agents/reviewer.md b/.claude/agents/reviewer.md new file mode 100644 index 0000000..e4763ac --- /dev/null +++ b/.claude/agents/reviewer.md @@ -0,0 +1,53 @@ +--- +name: reviewer +description: 코드 리뷰 및 품질 검증 에이전트. 커밋 전 검증이나 MR 리뷰 시 사용한다. +model: sonnet +tools: Read, Glob, Grep, Bash +maxTurns: 12 +--- + +변경된 코드를 체크리스트 기반으로 검증한다. +읽기 전용 — 파일을 수정하지 않는다. + +## 자율 범위 + +- 지정된 변경 파일을 자유롭게 분석 +- 관련 파일(임포트 대상, 호출자)도 열람하여 영향 범위 확인 가능 +- 기본 체크리스트 + 자체 판단으로 추가 이슈 탐지 + +## 입력 (메인 세션이 제공) + +- **[대상]**: 리뷰할 파일 경로 목록 또는 git diff 범위 +- **[체크리스트]**: 검증 항목 (선택, 미제공 시 기본 체크리스트 사용) + +## 기본 체크리스트 + +1. 타입 안전성 — any, 타입 단언(as), non-null 단언(!) 사용 +2. 에러 처리 — try-catch 누락, empty catch, 에러 무시 +3. 보안 — 하드코딩 인증정보, injection 가능성, XSS +4. 미사용 코드 — 미사용 import, 변수, 함수 +5. 팀 정책 — team-policy.md 위반 사항 +6. 일관성 — 기존 코드 패턴과의 불일치 + +## 출력 형식 + +``` +## 리뷰 결과 + +| # | 항목 | 판정 | 근거 | +|---|------|------|------| +| 1 | [항목명] | PASS / FAIL | [파일:라인] 설명 | + +### 추가 발견 (자체 판단) +- [파일:라인] 설명 (심각도: Critical / Warning / Info) + +### 요약 +- 전체: N개 PASS / M개 FAIL +- 커밋 가능 여부: 가능 / 차단 권고(사유) +``` + +## 제약 + +- 파일 수정/생성 금지 +- 각 항목에 반드시 PASS 또는 FAIL 판정 (애매하면 FAIL + 사유) +- 스타일 개선 제안은 "추가 발견"에 Info로만 기록 diff --git a/.claude/rules/frontend-code-style.md b/.claude/rules/frontend-code-style.md new file mode 100644 index 0000000..75020ef --- /dev/null +++ b/.claude/rules/frontend-code-style.md @@ -0,0 +1,68 @@ +# TypeScript/React 코드 스타일 규칙 + +## TypeScript 일반 +- strict 모드 필수 (`tsconfig.json`) +- `any` 사용 금지 (불가피한 경우 주석으로 사유 명시) +- 타입 정의: `interface` 우선 (type은 유니온/인터섹션에만) +- 들여쓰기: 2 spaces +- 세미콜론: 사용 +- 따옴표: single quote +- trailing comma: 사용 + +## React 규칙 + +### 컴포넌트 +- 함수형 컴포넌트 + hooks 패턴만 사용 +- 클래스 컴포넌트 사용 금지 +- 컴포넌트 파일 당 하나의 export default 컴포넌트 +- Props 타입은 interface로 정의 (ComponentNameProps) + +```tsx +interface UserCardProps { + name: string; + email: string; + onEdit?: () => void; +} + +const UserCard = ({ name, email, onEdit }: UserCardProps) => { + return ( +
+

{name}

+

{email}

+ {onEdit && } +
+ ); +}; + +export default UserCard; +``` + +### Hooks +- 커스텀 훅은 `use` 접두사 (예: `useAuth`, `useFetch`) +- 훅은 `src/hooks/` 디렉토리에 분리 +- 복잡한 상태 로직은 커스텀 훅으로 추출 + +### 상태 관리 +- 컴포넌트 로컬 상태: `useState` +- 공유 상태: Context API 또는 Zustand +- 서버 상태: React Query (TanStack Query) 권장 + +### 이벤트 핸들러 +- `handle` 접두사: `handleClick`, `handleSubmit` +- Props로 전달 시 `on` 접두사: `onClick`, `onSubmit` + +## 스타일링 +- Tailwind CSS 사용 +- 인라인 스타일 지양 +- !important 사용 금지 + +## API 호출 +- API 호출 로직은 `src/services/`에 분리 +- Axios 또는 fetch wrapper 사용 +- 에러 처리: try-catch + 사용자 친화적 에러 메시지 + +## 기타 +- console.log 커밋 금지 (디버깅 후 제거) +- 매직 넘버/문자열 → 상수 파일로 추출 +- 사용하지 않는 import, 변수 제거 (ESLint로 검증) +- 이미지/아이콘은 `src/assets/`에 관리 diff --git a/.claude/rules/release-notes-guide.md b/.claude/rules/release-notes-guide.md new file mode 100644 index 0000000..48bf706 --- /dev/null +++ b/.claude/rules/release-notes-guide.md @@ -0,0 +1,38 @@ +# 릴리즈 노트 관리 정책 + +## 원칙 + +- MR은 반드시 `/mr` 또는 `/release` 스킬을 통해 생성한다 +- 웹에서 직접 생성한 MR은 릴리즈 노트 누락으로 리뷰에서 반려한다 +- 코드 리뷰 시 `docs/RELEASE-NOTES.md` 변경 여부를 확인한다 + +## 2계층 구조 + +### Tier 1: 내부 릴리즈 노트 — `docs/RELEASE-NOTES.md` +- 대상: 내부 개발자 +- 형식: [Keep a Changelog](https://keepachangelog.com/ko/1.0.0/) 기반 +- 관리: `/mr` → [Unreleased] 항목 추가, `/release` → 날짜 버전 전환 + 압축 +- 모든 커밋 타입 기록 + +### Tier 2: 버저닝 릴리즈 노트 — `docs/VERSION-HISTORY.md` +- 대상: 개발자 + 비개발자 + 외부 공유 +- 형식: Semantic Versioning, 사용자 관점의 변화 중심 +- 관리: `/version` 스킬로 RELEASE-NOTES.md 기반 생성 + +## 변경 타입 매핑 (RELEASE-NOTES.md) + +| Conventional Commits | 릴리즈 노트 섹션 | +|---------------------|-----------------| +| feat | **추가** | +| fix | **수정** | +| refactor, perf | **변경** | +| docs | **문서** | +| test | **테스트** | +| style, chore, ci | **기타** | + +## 권한 기반 스킬 접근 + +- `/push`, `/mr`: `permissions.push` (write 이상) +- `/release`, `/version`: `permissions.admin` (프로젝트 관리자) +- 권한은 Gitea API `repos/{owner}/{repo}` → `permissions` 필드로 확인 +- Gitea site admin이 아닌 **리포 단위 팀 권한** 기준 diff --git a/.claude/rules/subagent-policy.md b/.claude/rules/subagent-policy.md new file mode 100644 index 0000000..05164c5 --- /dev/null +++ b/.claude/rules/subagent-policy.md @@ -0,0 +1,61 @@ +# 서브에이전트 활용 정책 + +커스텀 에이전트(`.claude/agents/`)를 활용하여 컨텍스트를 보호하고 병렬 작업을 수행한다. +메인 세션은 리더 역할(설계, 조율, 최종 판단)에 집중하고, 실제 작업은 서브에이전트에 위임한다. + +## 에이전트 구성 + +| 에이전트 | 역할 | 자율성 | 모델 | +|----------|------|--------|------| +| explorer | 코드베이스 탐색/분석 (읽기 전용) | 높음 | sonnet | +| implementer | 모듈 단위 코드 구현 | 중간 | sonnet | +| reviewer | 코드 리뷰/품질 검증 (읽기 전용) | 높음 | sonnet | + +## 사용 시점 + +### explorer +- 3개 이상의 파일/디렉토리를 탐색해야 할 때 +- 프로젝트 구조나 패턴을 파악할 때 +- 의존성 체인, 임포트 관계를 추적할 때 + +### implementer +- 독립 모듈/컴포넌트를 구현할 때 +- 여러 모듈을 병렬로 구현할 때 (각각 별도 implementer) +- 반복 패턴을 여러 파일에 적용할 때 + +### reviewer +- 구현 완료 후 커밋 전 검증 +- MR 생성 전 자체 리뷰 +- 변경 범위가 클 때 (5개 이상 파일) + +## 사용하지 않는 경우 + +- 단일 파일의 간단한 수정 +- 위치를 이미 아는 코드 수정 +- 설정 파일 변경 + +## 메인 세션 작업 흐름 + +### 단일 모듈 +1. 메인: 계약(인터페이스, 타입) 설계 +2. implementer: 계약 기반 구현 + 자체 검증 +3. reviewer: 변경 파일 리뷰 +4. 메인: 결과 확인 → 커밋 + +### 다중 모듈 (병렬) +1. 메인: 모듈 간 공유 인터페이스 확정 +2. implementer A + B: 각 모듈 동시 구현 +3. 메인: 통합 확인 (인터페이스 일치) +4. reviewer: 전체 변경 리뷰 +5. 메인: 최종 확인 → 커밋 + +### 분석 +1. explorer: 탐색 영역 + 목적 전달 → 분석 결과 반환 +2. 메인: "추정" 항목만 직접 확인 → 판단 + +## 핵심 원칙 + +- **읽기 전용 에이전트(explorer/reviewer)**: 결과가 부정확해도 손해 없음 → 높은 자율성 부여 +- **쓰기 에이전트(implementer)**: 계약은 고정, 내부 구현은 자율 → 중간 자율성 +- **같은 파일을 두 에이전트가 동시에 수정하지 않는다** +- **커밋/푸시는 반드시 메인 세션에서 수행** diff --git a/.claude/scripts/on-commit.sh b/.claude/scripts/on-commit.sh new file mode 100755 index 0000000..f473403 --- /dev/null +++ b/.claude/scripts/on-commit.sh @@ -0,0 +1,14 @@ +#!/bin/bash +INPUT=$(cat) +COMMAND=$(echo "$INPUT" | python3 -c "import sys,json;print(json.load(sys.stdin).get('tool_input',{}).get('command',''))" 2>/dev/null || echo "") +if echo "$COMMAND" | grep -qE 'git commit'; then + cat </dev/null || echo "") +if [ -z "$CWD" ]; then + CWD=$(pwd) +fi +PROJECT_HASH=$(echo "$CWD" | sed 's|/|-|g') +MEMORY_DIR="$HOME/.claude/projects/$PROJECT_HASH/memory" +CONTEXT="" +if [ -f "$MEMORY_DIR/MEMORY.md" ]; then + SUMMARY=$(head -100 "$MEMORY_DIR/MEMORY.md" | python3 -c "import sys;print(sys.stdin.read().replace('\\\\','\\\\\\\\').replace('\"','\\\\\"').replace('\n','\\\\n'))" 2>/dev/null) + CONTEXT="컨텍스트가 압축되었습니다.\\n\\n[세션 요약]\\n${SUMMARY}" +fi +if [ -f "$MEMORY_DIR/project-snapshot.md" ]; then + SNAP=$(head -50 "$MEMORY_DIR/project-snapshot.md" | python3 -c "import sys;print(sys.stdin.read().replace('\\\\','\\\\\\\\').replace('\"','\\\\\"').replace('\n','\\\\n'))" 2>/dev/null) + CONTEXT="${CONTEXT}\\n\\n[프로젝트 최신 상태]\\n${SNAP}" +fi +if [ -n "$CONTEXT" ]; then + CONTEXT="${CONTEXT}\\n\\n위 내용을 참고하여 작업을 이어가세요. 상세 내용은 memory/ 디렉토리의 각 파일을 참조하세요." + echo "{\"hookSpecificOutput\":{\"additionalContext\":\"${CONTEXT}\"}}" +else + echo "{\"hookSpecificOutput\":{\"additionalContext\":\"컨텍스트가 압축되었습니다. memory 파일이 없으므로 사용자에게 이전 작업 내용을 확인하세요.\"}}" +fi diff --git a/.claude/scripts/on-pre-compact.sh b/.claude/scripts/on-pre-compact.sh new file mode 100755 index 0000000..3f52f09 --- /dev/null +++ b/.claude/scripts/on-pre-compact.sh @@ -0,0 +1,8 @@ +#!/bin/bash +# PreCompact hook: systemMessage만 지원 (hookSpecificOutput 사용 불가) +INPUT=$(cat) +cat <" --- diff --git a/.claude/skills/init-project/SKILL.md b/.claude/skills/init-project/SKILL.md index f0d4c30..d0df8e5 100644 --- a/.claude/skills/init-project/SKILL.md +++ b/.claude/skills/init-project/SKILL.md @@ -1,7 +1,6 @@ --- name: init-project description: 팀 표준 워크플로우로 프로젝트를 초기화합니다 -allowed-tools: "Bash, Read, Write, Edit, Glob, Grep" argument-hint: "[project-type: java-maven|java-gradle|react-ts|auto]" --- @@ -22,22 +21,164 @@ $ARGUMENTS가 "auto"이거나 비어있으면 다음 순서로 감지: - 빌드 파일, 설정 파일, 디렉토리 구조 파악 - 사용 중인 프레임워크, 라이브러리 감지 - 기존 `.claude/` 디렉토리 존재 여부 확인 +- eslint, prettier, checkstyle, spotless 등 lint 도구 설치 여부 확인 ### 2. CLAUDE.md 생성 프로젝트 루트에 CLAUDE.md를 생성하고 다음 내용 포함: - 프로젝트 개요 (이름, 타입, 주요 기술 스택) - 빌드/실행 명령어 (감지된 빌드 도구 기반) - 테스트 실행 명령어 +- lint 실행 명령어 (감지된 도구 기반) - 프로젝트 디렉토리 구조 요약 - 팀 컨벤션 참조 (`.claude/rules/` 안내) -### 3. .claude/ 디렉토리 구성 -이미 팀 표준 파일이 존재하면 건너뜀. 없는 경우: -- `.claude/settings.json` — 프로젝트 타입별 표준 권한 설정 -- `.claude/rules/` — 팀 규칙 파일 (team-policy, git-workflow, code-style, naming, testing) -- `.claude/skills/` — 팀 스킬 (create-mr, fix-issue, sync-team-workflow) +### Gitea 파일 다운로드 URL 패턴 +⚠️ Gitea raw 파일은 반드시 **web raw URL**을 사용해야 합니다 (`/api/v1/` 경로 사용 불가): +```bash +GITEA_URL="${GITEA_URL:-https://gitea.gc-si.dev}" +# common 파일: ${GITEA_URL}/gc/template-common/raw/branch/develop/<파일경로> +# 타입별 파일: ${GITEA_URL}/gc/template-<타입>/raw/branch/develop/<파일경로> +# 예시: +curl -sf "${GITEA_URL}/gc/template-common/raw/branch/develop/.claude/rules/team-policy.md" +curl -sf "${GITEA_URL}/gc/template-react-ts/raw/branch/develop/.editorconfig" +``` -### 4. Git Hooks 설정 +### 3. .claude/ 디렉토리 구성 +이미 팀 표준 파일이 존재하면 건너뜀. 없는 경우 위의 URL 패턴으로 Gitea에서 다운로드: +- `.claude/settings.json` — 프로젝트 타입별 표준 권한 설정 + env(CLAUDE_BOT_TOKEN 등) + hooks 섹션 (4단계 참조) + +⚠️ 팀 규칙(.claude/rules/), 에이전트(.claude/agents/), 스킬 6종, 스크립트는 12단계(sync-team-workflow)에서 자동 다운로드된다. 여기서는 settings.json만 설정한다. + +### 3.5. Gitea 토큰 설정 + +**CLAUDE_BOT_TOKEN** (팀 공용): `settings.json`의 `env` 필드에 이미 포함되어 있음 (3단계에서 설정됨). 별도 조치 불필요. + +**GITEA_TOKEN** (개인): `/push`, `/mr`, `/release` 등 Git 스킬에 필요한 개인 토큰. + +```bash +# 현재 GITEA_TOKEN 설정 여부 확인 +if [ -z "$GITEA_TOKEN" ]; then + echo "GITEA_TOKEN 미설정" +fi +``` + +**GITEA_TOKEN이 없는 경우**, 다음 안내를 **AskUserQuestion**으로 표시: + +**질문**: "GITEA_TOKEN이 설정되지 않았습니다. Gitea 개인 토큰을 생성하시겠습니까?" +- 옵션 1: 토큰 생성 안내 보기 (추천) +- 옵션 2: 이미 있음 (토큰 입력) +- 옵션 3: 나중에 하기 + +**토큰 생성 안내 선택 시**, 다음 내용을 표시: + +``` +📋 Gitea 토큰 생성 방법: + +1. 브라우저에서 접속: + https://gitea.gc-si.dev/user/settings/applications + +2. "Manage Access Tokens" 섹션에서 "Generate New Token" 클릭 + +3. 입력: + - Token Name: "claude-code" (자유롭게 지정) + - Repository and Organization Access: ✅ All (public, private, and limited) + +4. Select permissions (아래 4개만 설정, 나머지는 No Access 유지): + + ┌─────────────────┬──────────────────┬──────────────────────────────┐ + │ 항목 │ 권한 │ 용도 │ + ├─────────────────┼──────────────────┼──────────────────────────────┤ + │ issue │ Read and Write │ /fix-issue 이슈 조회/코멘트 │ + │ organization │ Read │ gc 조직 리포 접근 │ + │ repository │ Read and Write │ /push, /mr, /release API 호출 │ + │ user │ Read │ API 사용자 인증 확인 │ + └─────────────────┴──────────────────┴──────────────────────────────┘ + +5. "Generate Token" 클릭 → ⚠️ 토큰이 한 번만 표시됩니다! 반드시 복사하세요. +``` + +표시 후 **AskUserQuestion**: "생성한 토큰을 입력하세요" +- 옵션 1: 토큰 입력 (Other로 입력) +- 옵션 2: 나중에 하기 + +**토큰 입력 시**: + +1. Gitea API로 유효성 검증: +```bash +curl -sf "https://gitea.gc-si.dev/api/v1/user" \ + -H "Authorization: token <입력된 토큰>" +``` +- 성공: `✅ () 인증 확인` 출력 +- 실패: `❌ 토큰이 유효하지 않습니다. 다시 확인해주세요.` 출력 → 재입력 요청 + +2. `.claude/settings.local.json`에 저장 (이 파일은 .gitignore에 포함, 리포 커밋 안됨): +```json +{ + "env": { + "GITEA_TOKEN": "<입력된 토큰>" + } +} +``` + +기존 `settings.local.json`이 있으면 `env.GITEA_TOKEN`만 추가/갱신. + +**나중에 하기 선택 시**: 경고 표시 후 다음 단계로 진행: +``` +⚠️ GITEA_TOKEN 없이는 /push, /mr, /release 스킬을 사용할 수 없습니다. + 나중에 토큰을 생성하면 .claude/settings.local.json에 다음을 추가하세요: + { "env": { "GITEA_TOKEN": "your-token-here" } } +``` + +### 4. Hook 스크립트 설정 + +⚠️ `.claude/scripts/` 스크립트 파일은 12단계(sync-team-workflow)에서 서버로부터 자동 다운로드된다. +여기서는 `settings.json`에 hooks 섹션만 설정한다. + +`.claude/settings.json`에 hooks 섹션이 없으면 추가 (기존 settings.json의 내용에 병합): + +```json +{ + "hooks": { + "SessionStart": [ + { + "matcher": "compact", + "hooks": [ + { + "type": "command", + "command": "bash .claude/scripts/on-post-compact.sh", + "timeout": 10 + } + ] + } + ], + "PreCompact": [ + { + "hooks": [ + { + "type": "command", + "command": "bash .claude/scripts/on-pre-compact.sh", + "timeout": 30 + } + ] + } + ], + "PostToolUse": [ + { + "matcher": "Bash", + "hooks": [ + { + "type": "command", + "command": "bash .claude/scripts/on-commit.sh", + "timeout": 15 + } + ] + } + ] + } +} +``` + +### 5. Git Hooks 설정 ```bash git config core.hooksPath .githooks ``` @@ -46,7 +187,14 @@ git config core.hooksPath .githooks chmod +x .githooks/* ``` -### 5. 프로젝트 타입별 추가 설정 +**pre-commit 훅 검증**: `.githooks/pre-commit`을 실행하여 빌드 검증이 정상 동작하는지 확인. +에러 발생 시 (예: 모노레포가 아닌 특수 구조, 빌드 명령 불일치 등): +1. 프로젝트에 맞게 `.githooks/pre-commit`을 커스텀 수정 +2. `.claude/workflow-version.json`에 `"custom_pre_commit": true` 추가 +3. 이후 `/sync-team-workflow` 실행 시 pre-commit은 덮어쓰지 않고 보존됨 + (`commit-msg`, `post-checkout`은 항상 팀 표준으로 동기화) + +### 6. 프로젝트 타입별 추가 설정 #### java-maven - `.sdkmanrc` 생성 (java=17.0.18-amzn 또는 프로젝트에 맞는 버전) @@ -63,7 +211,7 @@ chmod +x .githooks/* - `.npmrc` Nexus 레지스트리 설정 확인 - `npm install && npm run build` 성공 확인 -### 6. .gitignore 확인 +### 7. .gitignore 확인 다음 항목이 .gitignore에 포함되어 있는지 확인하고, 없으면 추가: ``` .claude/settings.local.json @@ -73,18 +221,75 @@ chmod +x .githooks/* *.local ``` -### 7. workflow-version.json 생성 -`.claude/workflow-version.json` 파일을 생성하여 현재 글로벌 워크플로우 버전 기록: +**팀 워크플로우 관리 경로** (sync로 생성/관리되는 파일, 리포에 커밋하지 않음): +``` +# Team workflow (managed by /sync-team-workflow) +.claude/rules/ +.claude/agents/ +.claude/skills/push/ +.claude/skills/mr/ +.claude/skills/create-mr/ +.claude/skills/release/ +.claude/skills/version/ +.claude/skills/fix-issue/ +.claude/scripts/ +``` + +### 8. Git exclude 설정 +`.git/info/exclude` 파일을 읽고, 기존 내용을 보존하면서 하단에 추가: + +```gitignore + +# Claude Code 워크플로우 (로컬 전용) +docs/CHANGELOG.md +*.tmp +``` + +### 9. Memory 초기화 +프로젝트 memory 디렉토리의 위치를 확인하고 (보통 `~/.claude/projects//memory/`) 다음 파일들을 생성: + +- `memory/MEMORY.md` — 프로젝트 분석 결과 기반 핵심 요약 (200줄 이내) + - 현재 상태, 프로젝트 개요, 기술 스택, 주요 패키지 구조, 상세 참조 링크 +- `memory/project-snapshot.md` — 디렉토리 구조, 패키지 구성, 주요 의존성, API 엔드포인트 +- `memory/project-history.md` — "초기 팀 워크플로우 구성" 항목으로 시작 +- `memory/api-types.md` — 주요 인터페이스/DTO/Entity 타입 요약 +- `memory/decisions.md` — 빈 템플릿 (# 의사결정 기록) +- `memory/debugging.md` — 빈 템플릿 (# 디버깅 경험 & 패턴) + +### 10. Lint 도구 확인 +- TypeScript: eslint, prettier 설치 여부 확인. 미설치 시 사용자에게 설치 제안 +- Java: checkstyle, spotless 등 설정 확인 +- CLAUDE.md에 lint 실행 명령어가 이미 기록되었는지 확인 + +### 11. workflow-version.json 생성 +Gitea API로 최신 팀 워크플로우 버전을 조회: +```bash +curl -sf --max-time 5 "https://gitea.gc-si.dev/gc/template-common/raw/branch/develop/workflow-version.json" +``` +조회 성공 시 해당 `version` 값 사용, 실패 시 "1.0.0" 기본값 사용. + +`.claude/workflow-version.json` 파일 생성: ```json { - "applied_global_version": "1.0.0", - "applied_date": "현재날짜", - "project_type": "감지된타입" + "applied_global_version": "<조회된 버전>", + "applied_date": "<현재날짜>", + "project_type": "<감지된타입>", + "gitea_url": "https://gitea.gc-si.dev" } ``` -### 8. 검증 및 요약 +### 12. 팀 워크플로우 최신화 + +`/sync-team-workflow`를 자동으로 1회 실행하여 최신 팀 파일(rules, agents, skills 6종, scripts, hooks)을 서버에서 다운로드하고 로컬에 적용한다. + +이 단계에서 `.claude/rules/`, `.claude/agents/`, `.claude/skills/push/` 등 팀 관리 파일이 생성된다. +(이 파일들은 7단계에서 .gitignore에 추가되었으므로 리포에 커밋되지 않음) + +### 13. 검증 및 요약 - 생성/수정된 파일 목록 출력 - `git config core.hooksPath` 확인 - 빌드 명령 실행 가능 확인 -- 다음 단계 안내 (개발 시작, 첫 커밋 방법 등) +- Hook 스크립트 실행 권한 확인 +- 다음 단계 안내: + - 개발 시작, 첫 커밋 방법 + - 범용 스킬: `/api-registry`, `/changelog`, `/swagger-spec` diff --git a/.claude/skills/mr/SKILL.md b/.claude/skills/mr/SKILL.md new file mode 100644 index 0000000..cb218b1 --- /dev/null +++ b/.claude/skills/mr/SKILL.md @@ -0,0 +1,279 @@ +--- +name: mr +description: 커밋 + 푸시 + Gitea MR을 한 번에 생성합니다 +user-invokable: true +argument-hint: "[target-branch: develop|main] (기본: develop)" +--- + +현재 브랜치의 변경 사항을 커밋+푸시하고, Gitea에 MR을 생성합니다. +타겟 브랜치: $ARGUMENTS (기본: develop) + +## 수행 단계 + +### 0. 권한 확인 + +```bash +# GITEA_TOKEN 확인 +if [ -z "$GITEA_TOKEN" ]; then + echo "GITEA_TOKEN이 필요합니다. Gitea → Settings → Applications에서 생성하세요" + exit 1 +fi + +# Gitea API로 리포 권한 조회 +REMOTE_URL=$(git remote get-url origin) +GITEA_HOST=$(echo "$REMOTE_URL" | sed -E 's|^https?://([^/]+)/.*|\1|') +REPO_PATH=$(echo "$REMOTE_URL" | grep -oE '[^/]+/[^/]+\.git$' | sed 's/.git$//') +PERMISSIONS=$(curl -sf "https://${GITEA_HOST}/api/v1/repos/${REPO_PATH}" \ + -H "Authorization: token ${GITEA_TOKEN}") +CAN_PUSH=$(echo "$PERMISSIONS" | python3 -c "import sys,json; print(json.load(sys.stdin)['permissions']['push'])") +``` + +- `CAN_PUSH`가 `False`이면: "MR 생성 권한이 필요합니다. 프로젝트 관리자에게 요청하세요." 안내 후 종료 + +### 0.5. 팀 워크플로우 최신화 확인 + +`.claude/workflow-version.json`이 존재하지 않으면 이 단계를 건너뛴다 (팀 프로젝트가 아닌 경우). + +```bash +# 로컬 설정 읽기 +GITEA_URL=$(python3 -c "import json; print(json.load(open('.claude/workflow-version.json')).get('gitea_url', 'https://gitea.gc-si.dev'))" 2>/dev/null) +PROJECT_TYPE=$(python3 -c "import json; print(json.load(open('.claude/workflow-version.json')).get('project_type', ''))" 2>/dev/null) +CUSTOM_PRECOMMIT=$(python3 -c "import json; print(json.load(open('.claude/workflow-version.json')).get('custom_pre_commit', False))" 2>/dev/null) + +# 서버 해시 조회 (custom_pre_commit이면 pre-commit 제외 해시 사용) +SERVER_VER=$(curl -sf --max-time 5 "${GITEA_URL}/gc/template-common/raw/branch/develop/workflow-version.json") +if [ "$CUSTOM_PRECOMMIT" = "True" ]; then + SERVER_HASH=$(echo "$SERVER_VER" | python3 -c "import sys,json; print(json.load(sys.stdin).get('content_hashes_custom_precommit',{}).get('${PROJECT_TYPE}',''))" 2>/dev/null) +else + SERVER_HASH=$(echo "$SERVER_VER" | python3 -c "import sys,json; print(json.load(sys.stdin).get('content_hashes',{}).get('${PROJECT_TYPE}',''))" 2>/dev/null) +fi + +# 로컬 해시 계산 (custom_pre_commit이면 .githooks/pre-commit 제외) +if [ "$CUSTOM_PRECOMMIT" = "True" ]; then + LOCAL_HASH=$(find .claude/rules .claude/agents .claude/scripts .githooks \ + .claude/skills/push .claude/skills/mr .claude/skills/create-mr \ + .claude/skills/release .claude/skills/version .claude/skills/fix-issue \ + -type f ! -path '.githooks/pre-commit' 2>/dev/null | sort | xargs cat 2>/dev/null | shasum -a 256 | cut -d' ' -f1) +else + LOCAL_HASH=$(find .claude/rules .claude/agents .claude/scripts .githooks \ + .claude/skills/push .claude/skills/mr .claude/skills/create-mr \ + .claude/skills/release .claude/skills/version .claude/skills/fix-issue \ + -type f 2>/dev/null | sort | xargs cat 2>/dev/null | shasum -a 256 | cut -d' ' -f1) +fi +``` + +**비교 결과 처리**: +- **서버 조회 실패** (`SERVER_HASH` 비어있음): "⚠️ 서버 연결 불가, 워크플로우 체크를 건너뜁니다" 경고 후 다음 단계 진행 +- **일치** (`LOCAL_HASH == SERVER_HASH`): 다음 단계 진행 +- **불일치**: "⚠️ 팀 워크플로우가 최신이 아닙니다. 동기화를 실행합니다..." 출력 → **sync-team-workflow 절차를 자동 실행** → 완료 후 원래 작업 계속 + +### 1. 사전 검증 + +```bash +# 현재 브랜치 확인 (main/develop이면 중단) +BRANCH=$(git branch --show-current) +``` + +- 현재 브랜치가 `main` 또는 `develop`이면: "feature 브랜치에서 실행해주세요" 안내 후 종료 + +### 2. 커밋 + 푸시 (변경 사항이 있을 때만) + +```bash +git status --short +``` + +**커밋되지 않은 변경이 있으면**: +- 변경 범위(파일 목록, 추가/수정/삭제) 요약 표시 +- Conventional Commits 형식 커밋 메시지 자동 생성 +- **사용자 확인** (AskUserQuestion): 커밋 메시지 수락/수정/취소 +- 수락 시: `git add -A` → `git commit` → `git push` + +**변경이 없으면**: +- 이미 커밋된 내용으로 MR 생성 진행 +- 리모트에 push되지 않은 커밋이 있으면 `git push` + +### 3. MR 대상 브랜치 결정 + +타겟 브랜치 후보를 분석하여 표시: + +```bash +# develop과의 차이 +git log develop..HEAD --oneline 2>/dev/null +# main과의 차이 +git log main..HEAD --oneline 2>/dev/null +``` + +**사용자 확인** (AskUserQuestion): +- **질문**: "MR 타겟 브랜치를 선택하세요" +- 옵션 1: develop (추천, N건 커밋 차이) +- 옵션 2: main (N건 커밋 차이) +- 옵션 3: 취소 + +인자($ARGUMENTS)로 브랜치가 지정되었으면 확인 없이 바로 진행. + +### 4. RELEASE-NOTES.md 갱신 (develop 대상 MR일 때만) + +타겟이 `develop`일 때 릴리즈 노트를 자동 갱신한다. + +**4-1. 커밋 분석** + +```bash +# develop 대비 현재 브랜치의 커밋 목록 (Conventional Commits 파싱) +git log develop..HEAD --format="%s" +``` + +모든 커밋 타입을 타입별 섹션으로 분류: + +| 타입 | 섹션 | +|------|------| +| feat | 추가 | +| fix | 수정 | +| refactor, perf | 변경 | +| docs | 문서 | +| test | 테스트 | +| style, chore, ci | 기타 | + +- 커밋 메시지에서 이슈 번호를 자동 추출하여 `(#번호)` 형태로 연결 +- 커밋 메시지의 scope는 제거하고, 설명부만 사람이 읽기 좋은 형태로 변환 + - 예: `feat(auth): 로그인 검증 로직 추가` → `- 로그인 검증 로직 추가` + +**4-2. RELEASE-NOTES.md 갱신** + +`docs/RELEASE-NOTES.md` 파일이 없으면 새로 생성: + +```markdown +# Release Notes + +이 문서는 [Keep a Changelog](https://keepachangelog.com/ko/1.0.0/) 형식을 따릅니다. + +## [Unreleased] +``` + +기존 파일이 있으면 `[Unreleased]` 섹션에 항목을 추가한다. +이미 같은 내용이 있으면 중복 추가하지 않는다. + +**4-3. 사용자 첨삭 확인** + +사용자에게 릴리즈 노트 초안을 표시하고 **AskUserQuestion**으로 확인: +- **질문**: "릴리즈 노트 초안입니다. 수정할 내용이 있으면 알려주세요." +- 옵션 1: 이대로 진행 (추천) +- 옵션 2: 수정 (Other 입력) +- 옵션 3: 릴리즈 노트 생략 + +사용자가 수정 사항을 입력하면 반영 후 커밋. +생략을 선택하면 릴리즈 노트 변경 없이 MR 생성 진행. + +**4-4. 추가 커밋 + 푸시** + +릴리즈 노트 변경이 있으면: +```bash +git add docs/RELEASE-NOTES.md +git commit -m "docs: 릴리즈 노트 업데이트" +git push +``` + +### 5. MR 정보 구성 + +```bash +# 커밋 목록 +git log {target}..HEAD --oneline +# 변경 파일 통계 +git diff {target}..HEAD --stat +``` + +- **제목**: 커밋이 1개면 커밋 메시지 사용, 여러 개면 브랜치명에서 추출 + - `feature/ISSUE-42-user-login` → `feat: ISSUE-42 user-login` + - `bugfix/fix-timeout` → `fix: fix-timeout` +- **본문**: + ```markdown + ## 변경 사항 + - (커밋 목록 기반 자동 생성) + + ## 관련 이슈 + - closes #이슈번호 (브랜치명에서 추출, 없으면 생략) + + ## 테스트 + - [ ] 빌드 성공 확인 + - [ ] 기존 테스트 통과 + ``` + +### 6. Gitea API로 MR 생성 + +```bash +curl -X POST "https://{host}/api/v1/repos/{owner}/{repo}/pulls" \ + -H "Authorization: token ${GITEA_TOKEN}" \ + -H "Content-Type: application/json" \ + -d '{ + "title": "MR 제목", + "body": "MR 본문", + "head": "현재브랜치", + "base": "타겟브랜치" + }' +``` + +### 7. PR 승인 + 머지 (선택) + +**사용자 확인** (AskUserQuestion): +- **질문**: "MR을 어떻게 처리하시겠습니까?" +- 옵션 1: 리뷰 대기 (추천) — MR만 생성, 리뷰어 지정 후 수동 머지 +- 옵션 2: 승인 + 머지 — claude-bot으로 즉시 승인하고 머지 + +**승인 + 머지 선택 시**: + +```bash +# CLAUDE_BOT_TOKEN 확인 +if [ -z "$CLAUDE_BOT_TOKEN" ]; then + echo "CLAUDE_BOT_TOKEN이 설정되지 않아 자동 승인이 불가합니다. MR만 생성합니다." + # MR URL 출력 후 종료 +fi + +# 1. claude-bot이 PR 리뷰 승인 +curl -X POST "https://{host}/api/v1/repos/{owner}/{repo}/pulls/{pr_number}/reviews" \ + -H "Authorization: token ${CLAUDE_BOT_TOKEN}" \ + -H "Content-Type: application/json" \ + -d '{"event": "APPROVED", "body": "MR 승인 (via /mr skill)"}' + +# 2. 머지 실행 +curl -X POST "https://{host}/api/v1/repos/{owner}/{repo}/pulls/{pr_number}/merge" \ + -H "Authorization: token ${GITEA_TOKEN}" \ + -H "Content-Type: application/json" \ + -d '{"Do": "merge", "merge_message_field": "MR 제목", "delete_branch_after_merge": true}' +``` + +⚠️ `delete_branch_after_merge: true` — feature 브랜치는 머지 후 삭제한다 (develop/main과 달리). + +### 8. 결과 출력 + +**승인 + 머지한 경우**: +``` +✅ MR 머지 완료 + 브랜치: feature/my-branch → develop + MR: https://gitea.gc-si.dev/gc/my-project/pulls/42 + 커밋: 3건, 파일: 5개 변경 + 릴리즈 노트: docs/RELEASE-NOTES.md [Unreleased] 갱신됨 + PR 승인: claude-bot ✅ + 머지: 완료 ✅ + 브랜치 삭제: feature/my-branch ✅ +``` + +**리뷰 대기 선택 시**: +``` +✅ MR 생성 완료 + 브랜치: feature/my-branch → develop + MR: https://gitea.gc-si.dev/gc/my-project/pulls/42 + 커밋: 3건, 파일: 5개 변경 + 릴리즈 노트: docs/RELEASE-NOTES.md [Unreleased] 갱신됨 + + 다음 단계: 리뷰어 지정 → 승인 대기 → 머지 +``` + +## 필요 환경변수 + +- `GITEA_TOKEN`: Gitea API 접근 토큰 + - 없으면: "Gitea 토큰이 필요합니다. Settings → Applications에서 생성하세요" 안내 +- `CLAUDE_BOT_TOKEN`: claude-bot PR 승인용 토큰 (선택, 없으면 자동 승인 건너뜀) + +## 기존 /create-mr과의 차이 + +- `/mr`: 커밋+푸시 + 릴리즈 노트 포함, 빠른 실행 (일상적 사용) +- `/create-mr`: MR 생성만, 세부 옵션 지원 (상세 제어) diff --git a/.claude/skills/push/SKILL.md b/.claude/skills/push/SKILL.md new file mode 100644 index 0000000..b9b7203 --- /dev/null +++ b/.claude/skills/push/SKILL.md @@ -0,0 +1,148 @@ +--- +name: push +description: 변경 사항을 확인하고 커밋 + 푸시합니다 +user-invokable: true +argument-hint: "[commit-message] (생략 시 자동 생성)" +--- + +현재 브랜치의 변경 사항을 확인하고, 사용자 승인 후 커밋 + 푸시합니다. +커밋 메시지 인자: $ARGUMENTS (생략 시 변경 내용 기반 자동 생성) + +## 수행 단계 + +### 0. 권한 확인 + +```bash +# GITEA_TOKEN 확인 +if [ -z "$GITEA_TOKEN" ]; then + echo "GITEA_TOKEN이 필요합니다. Gitea → Settings → Applications에서 생성하세요" + exit 1 +fi + +# Gitea API로 리포 권한 조회 +REMOTE_URL=$(git remote get-url origin) +GITEA_HOST=$(echo "$REMOTE_URL" | sed -E 's|^https?://([^/]+)/.*|\1|') +REPO_PATH=$(echo "$REMOTE_URL" | grep -oE '[^/]+/[^/]+\.git$' | sed 's/.git$//') +PERMISSIONS=$(curl -sf "https://${GITEA_HOST}/api/v1/repos/${REPO_PATH}" \ + -H "Authorization: token ${GITEA_TOKEN}") +CAN_PUSH=$(echo "$PERMISSIONS" | python3 -c "import sys,json; print(json.load(sys.stdin)['permissions']['push'])") +``` + +- `CAN_PUSH`가 `False`이면: "push 권한이 필요합니다. 프로젝트 관리자에게 요청하세요." 안내 후 종료 + +### 0.5. 팀 워크플로우 최신화 확인 + +`.claude/workflow-version.json`이 존재하지 않으면 이 단계를 건너뛴다 (팀 프로젝트가 아닌 경우). + +```bash +# 로컬 설정 읽기 +GITEA_URL=$(python3 -c "import json; print(json.load(open('.claude/workflow-version.json')).get('gitea_url', 'https://gitea.gc-si.dev'))" 2>/dev/null) +PROJECT_TYPE=$(python3 -c "import json; print(json.load(open('.claude/workflow-version.json')).get('project_type', ''))" 2>/dev/null) +CUSTOM_PRECOMMIT=$(python3 -c "import json; print(json.load(open('.claude/workflow-version.json')).get('custom_pre_commit', False))" 2>/dev/null) + +# 서버 해시 조회 (custom_pre_commit이면 pre-commit 제외 해시 사용) +SERVER_VER=$(curl -sf --max-time 5 "${GITEA_URL}/gc/template-common/raw/branch/develop/workflow-version.json") +if [ "$CUSTOM_PRECOMMIT" = "True" ]; then + SERVER_HASH=$(echo "$SERVER_VER" | python3 -c "import sys,json; print(json.load(sys.stdin).get('content_hashes_custom_precommit',{}).get('${PROJECT_TYPE}',''))" 2>/dev/null) +else + SERVER_HASH=$(echo "$SERVER_VER" | python3 -c "import sys,json; print(json.load(sys.stdin).get('content_hashes',{}).get('${PROJECT_TYPE}',''))" 2>/dev/null) +fi + +# 로컬 해시 계산 (custom_pre_commit이면 .githooks/pre-commit 제외) +if [ "$CUSTOM_PRECOMMIT" = "True" ]; then + LOCAL_HASH=$(find .claude/rules .claude/agents .claude/scripts .githooks \ + .claude/skills/push .claude/skills/mr .claude/skills/create-mr \ + .claude/skills/release .claude/skills/version .claude/skills/fix-issue \ + -type f ! -path '.githooks/pre-commit' 2>/dev/null | sort | xargs cat 2>/dev/null | shasum -a 256 | cut -d' ' -f1) +else + LOCAL_HASH=$(find .claude/rules .claude/agents .claude/scripts .githooks \ + .claude/skills/push .claude/skills/mr .claude/skills/create-mr \ + .claude/skills/release .claude/skills/version .claude/skills/fix-issue \ + -type f 2>/dev/null | sort | xargs cat 2>/dev/null | shasum -a 256 | cut -d' ' -f1) +fi +``` + +**비교 결과 처리**: +- **서버 조회 실패** (`SERVER_HASH` 비어있음): "⚠️ 서버 연결 불가, 워크플로우 체크를 건너뜁니다" 경고 후 다음 단계 진행 +- **일치** (`LOCAL_HASH == SERVER_HASH`): 다음 단계 진행 +- **불일치**: "⚠️ 팀 워크플로우가 최신이 아닙니다. 동기화를 실행합니다..." 출력 → **sync-team-workflow 절차를 자동 실행** → 완료 후 원래 작업 계속 + +### 1. 현재 상태 수집 + +```bash +# 현재 브랜치 +git branch --show-current + +# 커밋되지 않은 변경 사항 +git status --short + +# 변경 통계 +git diff --stat +git diff --cached --stat +``` + +### 2. 변경 범위 표시 + +사용자에게 다음 정보를 **표 형태**로 요약하여 보여준다: + +- 현재 브랜치명 +- 변경된 파일 목록 (추가/수정/삭제 구분) +- staged vs unstaged 구분 +- 변경 라인 수 요약 + +변경 사항이 없으면 "커밋할 변경 사항이 없습니다" 출력 후 종료. + +### 3. 커밋 메시지 결정 + +**인자가 있는 경우** ($ARGUMENTS가 비어있지 않으면): +- 전달받은 메시지를 커밋 메시지로 사용 +- Conventional Commits 형식인지 검증 (아니면 자동 보정 제안) + +**인자가 없는 경우**: +- 변경 내용을 분석하여 Conventional Commits 형식 메시지 자동 생성 +- 형식: `type(scope): 한국어 설명` +- type 판단 기준: + - 새 파일 추가 → `feat` + - 기존 파일 수정 → `fix` 또는 `refactor` + - 테스트 파일 → `test` + - 설정/빌드 파일 → `chore` + - 문서 파일 → `docs` + +### 4. 사용자 확인 + +AskUserQuestion으로 다음을 확인: + +**질문**: "다음 내용으로 커밋하시겠습니까?" +- 옵션 1: 제안된 메시지로 커밋 (추천) +- 옵션 2: 메시지 수정 (Other 입력) +- 옵션 3: 취소 + +### 5. 커밋 + 푸시 실행 + +사용자가 수락하면: + +```bash +# 모든 변경 사항 스테이징 (untracked 포함) +# 단, .env, secrets/ 등 민감 파일은 제외 +git add -A + +# 커밋 (.githooks/commit-msg가 형식 검증) +git commit -m "커밋메시지" + +# 푸시 (리모트 트래킹 없으면 -u 추가) +git push origin $(git branch --show-current) +``` + +**주의사항**: +- `git add` 전에 `.env`, `*.key`, `secrets/` 등 민감 파일이 포함되어 있으면 경고 +- pre-commit hook 실패 시 에러 메시지 표시 후 수동 해결 안내 +- 리모트에 브랜치가 없으면 `git push -u origin {branch}` 사용 + +### 6. 결과 출력 + +``` +✅ 푸시 완료 + 브랜치: feature/my-branch + 커밋: abc1234 feat(auth): 로그인 검증 로직 추가 + 변경: 3 files changed, 45 insertions(+), 12 deletions(-) +``` diff --git a/.claude/skills/release/SKILL.md b/.claude/skills/release/SKILL.md new file mode 100644 index 0000000..4d99a21 --- /dev/null +++ b/.claude/skills/release/SKILL.md @@ -0,0 +1,233 @@ +--- +name: release +description: develop에서 main으로 릴리즈 MR을 생성합니다 +user-invokable: true +--- + +develop 브랜치와 원격 동기화를 확인하고, 릴리즈 노트를 정리하고, develop → main 릴리즈 MR을 생성합니다. +사용자 승인 시 claude-bot으로 PR 승인 + 머지까지 자동 처리합니다. + +## 수행 단계 + +### 0. 권한 확인 + +```bash +# GITEA_TOKEN 확인 +if [ -z "$GITEA_TOKEN" ]; then + echo "GITEA_TOKEN이 필요합니다. Gitea → Settings → Applications에서 생성하세요" + exit 1 +fi + +# Gitea API로 리포 권한 조회 +REMOTE_URL=$(git remote get-url origin) +GITEA_HOST=$(echo "$REMOTE_URL" | sed -E 's|^https?://([^/]+)/.*|\1|') +REPO_PATH=$(echo "$REMOTE_URL" | grep -oE '[^/]+/[^/]+\.git$' | sed 's/.git$//') +PERMISSIONS=$(curl -sf "https://${GITEA_HOST}/api/v1/repos/${REPO_PATH}" \ + -H "Authorization: token ${GITEA_TOKEN}") +IS_ADMIN=$(echo "$PERMISSIONS" | python3 -c "import sys,json; print(json.load(sys.stdin)['permissions']['admin'])") +``` + +- `IS_ADMIN`이 `False`이면: "릴리즈는 프로젝트 관리자만 실행할 수 있습니다." 안내 후 종료 + +### 0.5. 팀 워크플로우 최신화 확인 + +`.claude/workflow-version.json`이 존재하지 않으면 이 단계를 건너뛴다 (팀 프로젝트가 아닌 경우). + +```bash +# 로컬 설정 읽기 +GITEA_URL=$(python3 -c "import json; print(json.load(open('.claude/workflow-version.json')).get('gitea_url', 'https://gitea.gc-si.dev'))" 2>/dev/null) +PROJECT_TYPE=$(python3 -c "import json; print(json.load(open('.claude/workflow-version.json')).get('project_type', ''))" 2>/dev/null) +CUSTOM_PRECOMMIT=$(python3 -c "import json; print(json.load(open('.claude/workflow-version.json')).get('custom_pre_commit', False))" 2>/dev/null) + +# 서버 해시 조회 (custom_pre_commit이면 pre-commit 제외 해시 사용) +SERVER_VER=$(curl -sf --max-time 5 "${GITEA_URL}/gc/template-common/raw/branch/develop/workflow-version.json") +if [ "$CUSTOM_PRECOMMIT" = "True" ]; then + SERVER_HASH=$(echo "$SERVER_VER" | python3 -c "import sys,json; print(json.load(sys.stdin).get('content_hashes_custom_precommit',{}).get('${PROJECT_TYPE}',''))" 2>/dev/null) +else + SERVER_HASH=$(echo "$SERVER_VER" | python3 -c "import sys,json; print(json.load(sys.stdin).get('content_hashes',{}).get('${PROJECT_TYPE}',''))" 2>/dev/null) +fi + +# 로컬 해시 계산 (custom_pre_commit이면 .githooks/pre-commit 제외) +if [ "$CUSTOM_PRECOMMIT" = "True" ]; then + LOCAL_HASH=$(find .claude/rules .claude/agents .claude/scripts .githooks \ + .claude/skills/push .claude/skills/mr .claude/skills/create-mr \ + .claude/skills/release .claude/skills/version .claude/skills/fix-issue \ + -type f ! -path '.githooks/pre-commit' 2>/dev/null | sort | xargs cat 2>/dev/null | shasum -a 256 | cut -d' ' -f1) +else + LOCAL_HASH=$(find .claude/rules .claude/agents .claude/scripts .githooks \ + .claude/skills/push .claude/skills/mr .claude/skills/create-mr \ + .claude/skills/release .claude/skills/version .claude/skills/fix-issue \ + -type f 2>/dev/null | sort | xargs cat 2>/dev/null | shasum -a 256 | cut -d' ' -f1) +fi +``` + +**비교 결과 처리**: +- **서버 조회 실패** (`SERVER_HASH` 비어있음): "⚠️ 서버 연결 불가, 워크플로우 체크를 건너뜁니다" 경고 후 다음 단계 진행 +- **일치** (`LOCAL_HASH == SERVER_HASH`): 다음 단계 진행 +- **불일치**: "⚠️ 팀 워크플로우가 최신이 아닙니다. 동기화를 실행합니다..." 출력 → **sync-team-workflow 절차를 자동 실행** → 완료 후 원래 작업 계속 + +### 1. 사전 검증 + +- 커밋되지 않은 변경 사항이 있으면 경고 ("먼저 /push로 커밋하세요") + +### 2. develop 브랜치 동기화 확인 + +```bash +git fetch origin +LOCAL=$(git rev-parse develop 2>/dev/null) +REMOTE=$(git rev-parse origin/develop 2>/dev/null) +BASE=$(git merge-base develop origin/develop 2>/dev/null) +``` + +| 상태 | 조건 | 행동 | +|------|------|------| +| 동일 | LOCAL == REMOTE | 바로 진행 | +| 로컬 뒤처짐 | LOCAL == BASE, LOCAL != REMOTE | "origin/develop에 새 커밋이 있습니다. `git pull origin develop` 후 다시 시도하세요" | +| 로컬 앞섬 | REMOTE == BASE, LOCAL != REMOTE | "push되지 않은 커밋이 있습니다" → 사용자 확인 후 push | +| 분기됨 | 그 외 | "분기되었습니다. 수동으로 해결해주세요" | + +### 3. develop → main 차이 분석 + +```bash +git log main..origin/develop --oneline +git diff main..origin/develop --stat +git rev-list --count main..origin/develop +``` + +차이가 없으면 "릴리즈할 변경이 없습니다" 출력 후 종료. + +### 4. 미기록 커밋 감지 + +`docs/RELEASE-NOTES.md`에 기록되지 않은 커밋이 있는지 확인: + +```bash +# develop의 커밋 중 RELEASE-NOTES.md에 언급되지 않은 feat/fix 커밋 추출 +git log main..origin/develop --format="%s" | grep -E "^(feat|fix|refactor|perf|docs|test|style|chore|ci)" +``` + +- 릴리즈 노트의 [Unreleased] + 날짜 릴리즈 항목과 비교 +- 미기록 커밋이 있으면 경고 표시 + 보충 기회 제공 + - 사용자에게 미기록 커밋 목록 표시 + - [Unreleased]에 추가할지 확인 + +### 5. 이전 날짜 넘버링 릴리즈 압축 + +`docs/RELEASE-NOTES.md`에서 **오늘 이전 날짜에 넘버링된 릴리즈**가 있으면 압축: + +``` +[2026-02-28.1] + [2026-02-28.2] → [2026-02-28] +``` + +압축 규칙: +- 같은 기능의 반복 수정 → 최종 상태만 유지 +- 추가 후 수정된 항목 → "추가" 섹션에 최종 상태로 합침 +- 추가 후 제거된 항목 → 양쪽 모두에서 삭제 +- 오늘 날짜의 넘버링은 유지 (압축 대상 아님) + +### 6. [Unreleased] → 날짜 버전 전환 + +``` +## [Unreleased] → ## [2026-03-01] 또는 ## [2026-03-01.2] +``` + +- 같은 날 이전 릴리즈가 있으면 순번 부여: `.1`, `.2`, `.3` +- [Unreleased]는 빈 섹션으로 다시 생성 + +### 7. 사용자 첨삭 확인 + +최종 릴리즈 노트를 표시하고 **AskUserQuestion**으로 확인: +- **질문**: "릴리즈 노트 최종 초안입니다. 수정할 내용이 있으면 알려주세요." +- 옵션 1: 이대로 진행 (추천) +- 옵션 2: 수정 (Other 입력) + +### 8. develop 커밋 + 푸시 + +```bash +git checkout develop +git add docs/RELEASE-NOTES.md +git commit -m "docs: 릴리즈 노트 정리 ($(date +%Y-%m-%d))" +git push origin develop +``` + +### 9. MR 정보 구성 + 생성 + +**제목**: `release: YYYY-MM-DD (N건 커밋)` + +**본문**: 릴리즈 노트 내용 포함 (커밋 type별 그룹핑) + +```bash +curl -X POST "https://{host}/api/v1/repos/{owner}/{repo}/pulls" \ + -H "Authorization: token ${GITEA_TOKEN}" \ + -H "Content-Type: application/json" \ + -d '{ + "title": "release: 2026-03-01 (12건 커밋)", + "body": "릴리즈 본문 (RELEASE-NOTES.md 내용 포함)", + "head": "develop", + "base": "main" + }' +``` + +### 10. PR 승인 + 머지 (선택) + +**사용자 확인** (AskUserQuestion): +- **질문**: "MR을 승인하고 머지하시겠습니까?" +- 옵션 1: 승인 + 머지 (추천) +- 옵션 2: MR만 생성 (수동 리뷰/머지) + +**승인 + 머지 선택 시**: + +```bash +# CLAUDE_BOT_TOKEN 확인 +if [ -z "$CLAUDE_BOT_TOKEN" ]; then + echo "CLAUDE_BOT_TOKEN이 설정되지 않아 자동 승인이 불가합니다. MR만 생성합니다." + # MR URL 출력 후 종료 +fi + +# 1. claude-bot이 PR 리뷰 승인 +curl -X POST "https://{host}/api/v1/repos/{owner}/{repo}/pulls/{pr_number}/reviews" \ + -H "Authorization: token ${CLAUDE_BOT_TOKEN}" \ + -H "Content-Type: application/json" \ + -d '{"event": "APPROVED", "body": "릴리즈 승인 (via /release skill)"}' + +# 2. 머지 실행 +curl -X POST "https://{host}/api/v1/repos/{owner}/{repo}/pulls/{pr_number}/merge" \ + -H "Authorization: token ${GITEA_TOKEN}" \ + -H "Content-Type: application/json" \ + -d '{"Do": "merge", "merge_message_field": "release: YYYY-MM-DD", "delete_branch_after_merge": false}' +``` + +⚠️ `delete_branch_after_merge: false` — develop 브랜치는 삭제하지 않는다. + +### 11. 결과 출력 + +``` +✅ 릴리즈 완료 + 브랜치: develop → main + MR: https://gitea.gc-si.dev/gc/my-project/pulls/50 + 커밋: 12건, 파일: 28개 변경 + 릴리즈 노트: [2026-03-01] 생성됨 + PR 승인: claude-bot ✅ + 머지: 완료 ✅ + CI/CD: 자동 배포 시작됨 + + 다음 단계: CI/CD 배포 결과 확인 +``` + +또는 MR만 생성한 경우: + +``` +✅ 릴리즈 MR 생성 완료 + 브랜치: develop → main + MR: https://gitea.gc-si.dev/gc/my-project/pulls/50 + 릴리즈 노트: [2026-03-01] 생성됨 + + 다음 단계: + 1. 리뷰어 지정 (main 브랜치는 1명 이상 리뷰 필수) + 2. 승인 후 머지 + 3. CI/CD 자동 배포 확인 +``` + +## 필요 환경변수 + +- `GITEA_TOKEN`: Gitea API 접근 토큰 (필수) +- `CLAUDE_BOT_TOKEN`: claude-bot PR 승인용 토큰 (선택, 없으면 자동 승인 건너뜀) diff --git a/.claude/skills/sync-team-workflow/SKILL.md b/.claude/skills/sync-team-workflow/SKILL.md index ad0e5ee..5aa6441 100644 --- a/.claude/skills/sync-team-workflow/SKILL.md +++ b/.claude/skills/sync-team-workflow/SKILL.md @@ -1,73 +1,165 @@ --- name: sync-team-workflow description: 팀 글로벌 워크플로우를 현재 프로젝트에 동기화합니다 -allowed-tools: "Bash, Read, Write, Edit, Glob, Grep" --- -팀 글로벌 워크플로우의 최신 버전을 현재 프로젝트에 적용합니다. +팀 글로벌 워크플로우의 최신 파일을 서버에서 다운로드하여 로컬에 적용합니다. +호출 시 항상 서버 기준으로 전체 동기화합니다 (버전 비교 없음). ## 수행 절차 -### 1. 글로벌 버전 조회 -Gitea API로 template-common 리포의 workflow-version.json 조회: -```bash -GITEA_URL=$(python3 -c "import json; print(json.load(open('.claude/workflow-version.json')).get('gitea_url', 'http://211.208.115.83:3000'))" 2>/dev/null || echo "http://211.208.115.83:3000") +### 1. 사전 조건 확인 -curl -sf "${GITEA_URL}/api/v1/repos/gcsc/template-common/raw/workflow-version.json" +`.claude/workflow-version.json` 존재 확인: +- 없으면 → "/init-project를 먼저 실행해주세요" 안내 후 종료 + +설정 읽기: +```bash +GITEA_URL=$(python3 -c "import json; print(json.load(open('.claude/workflow-version.json')).get('gitea_url', 'https://gitea.gc-si.dev'))" 2>/dev/null || echo "https://gitea.gc-si.dev") +PROJECT_TYPE=$(python3 -c "import json; print(json.load(open('.claude/workflow-version.json')).get('project_type', ''))" 2>/dev/null || echo "") ``` -### 2. 버전 비교 -로컬 `.claude/workflow-version.json`과 비교: -- 버전 일치 → "최신 버전입니다" 안내 후 종료 -- 버전 불일치 → 미적용 변경 항목 추출하여 표시 +프로젝트 타입이 비어있으면 자동 감지: +1. `pom.xml` → java-maven +2. `build.gradle` / `build.gradle.kts` → java-gradle +3. `package.json` + `tsconfig.json` → react-ts +4. 감지 실패 → 사용자에게 선택 요청 -### 3. 프로젝트 타입 감지 -자동 감지 순서: -1. `.claude/workflow-version.json`의 `project_type` 필드 확인 -2. 없으면: `pom.xml` → java-maven, `build.gradle` → java-gradle, `package.json` → react-ts +### Gitea 파일 다운로드 URL 패턴 +⚠️ Gitea raw 파일은 반드시 **web raw URL** 사용: +```bash +# common 파일: ${GITEA_URL}/gc/template-common/raw/branch/develop/<파일경로> +# 타입별 파일: ${GITEA_URL}/gc/template-${PROJECT_TYPE}/raw/branch/develop/<파일경로> +``` -### 4. 파일 다운로드 및 적용 -Gitea API로 해당 타입 + common 템플릿 파일 다운로드: +### 2. 디렉토리 준비 -#### 4-1. 규칙 파일 (덮어쓰기) -팀 규칙은 로컬 수정 불가 — 항상 글로벌 최신으로 교체: +필요한 디렉토리가 없으면 생성: +```bash +mkdir -p .claude/rules .claude/agents .claude/scripts +mkdir -p .claude/skills/push .claude/skills/mr .claude/skills/create-mr +mkdir -p .claude/skills/release .claude/skills/version .claude/skills/fix-issue +mkdir -p .githooks +``` + +### 3. 서버 파일 다운로드 + 적용 + +각 파일을 `curl -sf` 로 다운로드하여 프로젝트 루트의 동일 경로에 저장. +다운로드 실패한 파일은 경고 출력 후 건너뜀. + +#### 3-1. template-common 파일 (덮어쓰기) + +**규칙 파일**: ``` .claude/rules/team-policy.md .claude/rules/git-workflow.md -.claude/rules/code-style.md (타입별) -.claude/rules/naming.md (타입별) -.claude/rules/testing.md (타입별) +.claude/rules/release-notes-guide.md +.claude/rules/subagent-policy.md ``` -#### 4-2. settings.json (부분 갱신) -- `deny` 목록: 글로벌 최신으로 교체 -- `allow` 목록: 기존 사용자 커스텀 유지 + 글로벌 기본값 병합 -- `hooks`: 글로벌 최신으로 교체 - -#### 4-3. 스킬 파일 (덮어쓰기) +**에이전트 파일**: ``` +.claude/agents/explorer.md +.claude/agents/implementer.md +.claude/agents/reviewer.md +``` + +**스킬 파일 (6종)**: +``` +.claude/skills/push/SKILL.md +.claude/skills/mr/SKILL.md .claude/skills/create-mr/SKILL.md +.claude/skills/release/SKILL.md +.claude/skills/version/SKILL.md .claude/skills/fix-issue/SKILL.md -.claude/skills/sync-team-workflow/SKILL.md ``` -#### 4-4. Git Hooks (덮어쓰기 + 실행 권한) +**Hook 스크립트**: +``` +.claude/scripts/on-pre-compact.sh +.claude/scripts/on-post-compact.sh +.claude/scripts/on-commit.sh +``` + +**Git Hooks** (commit-msg, post-checkout은 항상 교체): +``` +.githooks/commit-msg +.githooks/post-checkout +``` + +다운로드 예시: ```bash -chmod +x .githooks/* +curl -sf "${GITEA_URL}/gc/template-common/raw/branch/develop/.claude/rules/team-policy.md" -o ".claude/rules/team-policy.md" ``` -### 5. 로컬 버전 업데이트 -`.claude/workflow-version.json` 갱신: +#### 3-2. template-{type} 파일 (타입별 덮어쓰기) + +``` +.claude/rules/code-style.md +.claude/rules/naming.md +.claude/rules/testing.md +``` + +**pre-commit hook**: +`.claude/workflow-version.json`의 `custom_pre_commit` 플래그 확인: +- `"custom_pre_commit": true` → pre-commit 건너뜀, "⚠️ pre-commit은 프로젝트 커스텀 유지" 로그 +- 플래그 없거나 false → `.githooks/pre-commit` 교체 + +다운로드 예시: +```bash +curl -sf "${GITEA_URL}/gc/template-${PROJECT_TYPE}/raw/branch/develop/.claude/rules/code-style.md" -o ".claude/rules/code-style.md" +``` + +#### 3-3. 실행 권한 부여 +```bash +chmod +x .githooks/* 2>/dev/null +chmod +x .claude/scripts/*.sh 2>/dev/null +``` + +### 4. settings.json 부분 머지 + +⚠️ settings.json은 **타입별 템플릿**에서 다운로드 (template-common에는 없음): +```bash +SERVER_SETTINGS=$(curl -sf "${GITEA_URL}/gc/template-${PROJECT_TYPE}/raw/branch/develop/.claude/settings.json") +``` + +다운로드한 최신 settings.json과 로컬 `.claude/settings.json`을 비교하여 부분 갱신: +- `env`: 서버 최신으로 교체 +- `deny` 목록: 서버 최신으로 교체 +- `allow` 목록: 기존 사용자 커스텀 유지 + 서버 기본값 병합 +- `hooks`: 서버 최신으로 교체 + +### 5. workflow-version.json 갱신 + +서버의 최신 `workflow-version.json` 조회: +```bash +SERVER_VER=$(curl -sf "${GITEA_URL}/gc/template-common/raw/branch/develop/workflow-version.json") +SERVER_VERSION=$(echo "$SERVER_VER" | python3 -c "import sys,json; print(json.load(sys.stdin).get('version',''))") +``` + +`.claude/workflow-version.json` 업데이트: ```json { - "applied_global_version": "새버전", - "applied_date": "오늘날짜", - "project_type": "감지된타입" + "applied_global_version": "<서버 version>", + "applied_date": "<현재날짜>", + "project_type": "<프로젝트타입>", + "gitea_url": "" } ``` +기존 필드(`custom_pre_commit` 등)는 보존. ### 6. 변경 보고 -- `git diff`로 변경 내역 확인 -- 업데이트된 파일 목록 출력 -- 변경 로그(글로벌 workflow-version.json의 changes) 표시 -- 필요한 추가 조치 안내 (빌드 확인, 의존성 업데이트 등) + +- 다운로드/갱신된 파일 목록 출력 +- 서버 `workflow-version.json`의 `changes` 중 최신 항목 표시 +- 결과 형태: +``` +✅ 팀 워크플로우 동기화 완료 + 버전: v1.6.0 + 갱신 파일: 22개 (rules 7, agents 3, skills 6, scripts 3, hooks 3) + settings.json: 부분 갱신 (env, deny, hooks) +``` + +## 필요 환경변수 + +없음 (Gitea raw URL은 인증 불필요) diff --git a/.claude/skills/version/SKILL.md b/.claude/skills/version/SKILL.md new file mode 100644 index 0000000..1358d53 --- /dev/null +++ b/.claude/skills/version/SKILL.md @@ -0,0 +1,124 @@ +--- +name: version +description: RELEASE-NOTES.md 기반으로 버저닝 릴리즈 노트(VERSION-HISTORY.md)를 생성합니다 +user-invokable: true +argument-hint: "[version: 1.0.0] (생략 시 자동 결정)" +--- + +`docs/RELEASE-NOTES.md`의 내부 릴리즈 노트를 기반으로 +사용자 관점의 `docs/VERSION-HISTORY.md`를 생성/갱신합니다. +버전: $ARGUMENTS (생략 시 변경 타입에 따라 자동 결정) + +## 수행 단계 + +### 0. 권한 확인 + +```bash +REMOTE_URL=$(git remote get-url origin) +GITEA_HOST=$(echo "$REMOTE_URL" | sed -E 's|^https?://([^/]+)/.*|\1|') +REPO_PATH=$(echo "$REMOTE_URL" | grep -oE '[^/]+/[^/]+\.git$' | sed 's/.git$//') +PERMISSIONS=$(curl -sf "https://${GITEA_HOST}/api/v1/repos/${REPO_PATH}" \ + -H "Authorization: token ${GITEA_TOKEN}") +IS_ADMIN=$(echo "$PERMISSIONS" | python3 -c "import sys,json; print(json.load(sys.stdin)['permissions']['admin'])") +``` + +- `IS_ADMIN`이 `False`이면: "버전 릴리즈는 프로젝트 관리자만 실행할 수 있습니다." 안내 후 종료 +- `GITEA_TOKEN` 미설정: 설정 안내 후 종료 + +### 1. RELEASE-NOTES.md 분석 + +`docs/RELEASE-NOTES.md`가 없으면 종료: "RELEASE-NOTES.md가 없습니다. 먼저 /mr로 릴리즈 노트를 생성하세요." + +```bash +cat docs/RELEASE-NOTES.md +``` + +### 2. 수집 범위 결정 + +- `docs/VERSION-HISTORY.md`가 있으면: 마지막 버전의 날짜를 확인 +- 해당 날짜 이후의 RELEASE-NOTES.md 항목을 수집 +- VERSION-HISTORY.md가 없으면: 전체 RELEASE-NOTES.md 항목 수집 + +### 3. 버전 결정 + +**인자가 있으면** ($ARGUMENTS): 해당 버전 사용 + +**인자가 없으면** 자동 결정: +- `feat` 포함 → minor 버전 올림 (x.Y.0) +- `fix`만 → patch 버전 올림 (x.y.Z) +- breaking change 포함 → major 버전 올림 (X.0.0) +- 이전 버전이 없으면 `1.0.0`부터 시작 + +### 4. 사용자 관점으로 재작성 + +수집된 항목을 다음 섹션으로 재구성: + +| 섹션 | 포함 기준 | +|------|----------| +| **주요 변경** | 사용자가 눈에 띄게 느낄 수 있는 기능 (feat 중 UI/UX 영향 큰 것) | +| **개선** | 성능/편의성 향상 (refactor, perf, 사소한 feat) | +| **버그 수정** | 사용자 영향 있는 문제 해결 | +| **보안** | 보안 관련 수정 (해당 시) | + +변환 규칙: +- 기술 용어 → 사용자 언어로 재작성 +- 관련 항목 그룹핑 (여러 커밋이 하나의 기능이면 합침) +- docs, test, chore, ci 등 사용자에게 보이지 않는 변경은 제외 +- 각 항목은 사용자가 "이게 뭐가 바뀐 거지?" 이해할 수 있는 수준으로 + +### 5. 초안 표시 + 첨삭 확인 + +초안을 표시하고 **AskUserQuestion**으로 확인: +- **질문**: "VERSION-HISTORY.md 초안입니다. 수정할 내용이 있으면 알려주세요." +- 옵션 1: 이대로 진행 (추천) +- 옵션 2: 수정 (Other 입력) + +### 6. VERSION-HISTORY.md 갱신 + +파일이 없으면 새로 생성: + +```markdown +# Version History + +이 프로젝트는 [Semantic Versioning](https://semver.org/lang/ko/)을 따릅니다. +``` + +새 버전을 기존 내용 위에 추가: + +```markdown +## [1.2.0] - 2026-03-01 + +### 주요 변경 +- **해역별 조업 현황 대시보드 추가**: 지도에서 해역을 선택하면 실시간 조업 통계를 확인할 수 있습니다 + +### 개선 +- 지도 레이어 전환 속도 향상 + +### 버그 수정 +- 야간 배치 실행 시 타임아웃 발생하던 문제 수정 + +> 내부 릴리즈: 2026-02-28 ~ 2026-03-01 (3건) +``` + +마지막 줄에 참조한 내부 릴리즈 날짜 범위와 건수를 표시. + +### 7. 커밋 + +```bash +git add docs/VERSION-HISTORY.md +git commit -m "docs: v$VERSION 릴리즈 노트 작성" +git push +``` + +### 8. 결과 출력 + +``` +✅ 버전 릴리즈 노트 작성 완료 + 버전: 1.2.0 + 파일: docs/VERSION-HISTORY.md + 기반: RELEASE-NOTES.md (2026-02-28 ~ 2026-03-01, 3건) +``` + +## 필요 환경변수 + +- `GITEA_TOKEN`: Gitea API 접근 토큰 (필수) diff --git a/.gitignore b/.gitignore index 5cba520..3c8b18e 100644 --- a/.gitignore +++ b/.gitignore @@ -35,6 +35,14 @@ application-local.properties .env.* secrets/ +# === Frontend === +frontend/node_modules/ +frontend/node/ +frontend/dist/ + +# === Generated static assets (built by frontend) === +src/main/resources/static/ + # === Claude Code (개인 설정) === .claude/settings.local.json .claude/CLAUDE.local.md diff --git a/CLAUDE.md b/CLAUDE.md index 3bcad04..4c0fedf 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -1,53 +1,72 @@ -# 프로젝트 개요 +# SNP Connection Monitoring -- **타입**: Java + Spring Boot + Maven -- **JDK**: 17 (`.sdkmanrc` 참조) -- **프레임워크**: Spring Boot -- **빌드 도구**: Maven (Maven Wrapper 사용) +API Gateway + 모니터링 통합 플랫폼. 모든 서비스 사용자가 모니터링 애플리케이션에 데이터를 요청할 때 API 인증키를 통해 인증하고, 모든 요청/응답 데이터를 추적/관리하는 서비스. + +## 기술 스택 +- Java 17, Spring Boot 3.2.1, Spring Data JPA +- PostgreSQL (스키마: std_snp_connection) +- Spring Security (JWT 기반 인증 예정) +- WebFlux WebClient (Heartbeat, Gateway Proxy) +- Springdoc OpenAPI 2.3.0 (Swagger) +- Lombok +- Frontend: React 19 + TypeScript + Vite + Tailwind CSS 4 ## 빌드 및 실행 ```bash -# 빌드 -./mvnw clean compile +# 프론트엔드 빌드 (별도) +cd frontend && npm install && npm run build && cd .. -# 패키징 +# 백엔드 빌드 (프론트엔드 자동 빌드 포함) ./mvnw clean package -DskipTests -# 테스트 -./mvnw test - -# 특정 테스트 클래스 실행 -./mvnw test -Dtest=클래스명 - # 로컬 실행 ./mvnw spring-boot:run -# 린트 (Checkstyle 설정된 경우) -./mvnw checkstyle:check +# 테스트 +./mvnw test + +# 프론트엔드 개발 서버 +cd frontend && npm run dev ``` -## 프로젝트 구조 +**주의**: frontend-maven-plugin의 Node 호환성 문제로, 프론트엔드와 백엔드를 분리하여 빌드합니다. + +## 서버 설정 +- 포트: 8042 +- Context Path: /snp-connection +- Swagger UI: http://localhost:8042/snp-connection/swagger-ui/index.html +- 프론트엔드 개발 서버: http://localhost:5173/snp-connection/ + +## 디렉토리 구조 ``` -src/ -├── main/ -│ ├── java/ -│ │ └── com/gcsc/{프로젝트}/ -│ │ ├── config/ # 설정 클래스 -│ │ ├── controller/ # REST 컨트롤러 -│ │ ├── service/ # 비즈니스 로직 -│ │ ├── repository/ # 데이터 접근 -│ │ ├── domain/ # 엔티티 -│ │ ├── dto/ # 데이터 전송 객체 -│ │ ├── exception/ # 예외 처리 -│ │ └── util/ # 유틸리티 -│ └── resources/ -│ ├── application.yml # 공통 설정 -│ ├── application-local.yml # 로컬 설정 (.gitignore) -│ └── application-prod.yml # 운영 설정 -└── test/ - └── java/ # 테스트 코드 +snp-connection-monitoring/ +├── pom.xml # Maven 설정 (frontend-maven-plugin 포함) +├── frontend/ # React + TypeScript 프론트엔드 +│ ├── package.json +│ ├── vite.config.ts # Vite 빌드 설정 (output → ../src/main/resources/static) +│ ├── tsconfig.json +│ ├── index.html +│ └── src/ +│ ├── main.tsx # React 엔트리 포인트 +│ ├── App.tsx # 루트 컴포넌트 (라우팅) +│ ├── index.css # 글로벌 스타일 (Tailwind) +│ ├── pages/ # 페이지 컴포넌트 +│ ├── components/ # 재사용 가능 컴포넌트 +│ ├── hooks/ # 커스텀 훅 +│ ├── services/ # API 호출 모듈 +│ ├── store/ # 상태 관리 +│ ├── types/ # TypeScript 타입 정의 +│ └── utils/ # 유틸리티 함수 +├── src/main/java/com/gcsc/connection/ +│ ├── SnpConnectionApplication.java # 메인 애플리케이션 +│ ├── config/ # 설정 클래스 +│ ├── common/ # 공통 모듈 (ApiResponse, Exception) +│ └── global/ # 글로벌 컨트롤러 (WebViewController) +└── src/main/resources/ + ├── application.yml # 공통 설정 + └── static/ # 프론트엔드 빌드 결과물 (자동 생성) ``` ## 팀 규칙 @@ -60,6 +79,5 @@ src/ ## 의존성 관리 -- Nexus 프록시 레포지토리를 통해 의존성 관리 (`.mvn/settings.xml`) -- 새 의존성 추가 시 `pom.xml`에 버전 명시 -- Spring Boot BOM 범위 내 의존성은 버전 생략 가능 +- Maven: Nexus 프록시 레포지토리 (`.mvn/settings.xml`) +- npm: Nexus npm 프록시 레지스트리 (`frontend/.npmrc`) diff --git a/frontend/.editorconfig b/frontend/.editorconfig new file mode 100644 index 0000000..6f831b5 --- /dev/null +++ b/frontend/.editorconfig @@ -0,0 +1,33 @@ +root = true + +[*] +charset = utf-8 +end_of_line = lf +insert_final_newline = true +trim_trailing_whitespace = true + +[*.{java,kt}] +indent_style = space +indent_size = 4 + +[*.{js,jsx,ts,tsx,json,yml,yaml,css,scss,html}] +indent_style = space +indent_size = 2 + +[*.md] +trim_trailing_whitespace = false + +[*.{sh,bash}] +indent_style = space +indent_size = 4 + +[Makefile] +indent_style = tab + +[*.{gradle,groovy}] +indent_style = space +indent_size = 4 + +[*.xml] +indent_style = space +indent_size = 4 diff --git a/frontend/.node-version b/frontend/.node-version new file mode 100644 index 0000000..209e3ef --- /dev/null +++ b/frontend/.node-version @@ -0,0 +1 @@ +20 diff --git a/frontend/.npmrc b/frontend/.npmrc new file mode 100644 index 0000000..67e04d2 --- /dev/null +++ b/frontend/.npmrc @@ -0,0 +1,5 @@ +# Nexus npm 프록시 레지스트리 +registry=https://nexus.gc-si.dev/repository/npm-public/ + +# Nexus 인증 +//nexus.gc-si.dev/repository/npm-public/:_auth=YWRtaW46R2NzYyE4OTMy diff --git a/frontend/.prettierrc b/frontend/.prettierrc new file mode 100644 index 0000000..73576ff --- /dev/null +++ b/frontend/.prettierrc @@ -0,0 +1,11 @@ +{ + "semi": true, + "singleQuote": true, + "trailingComma": "all", + "printWidth": 100, + "tabWidth": 2, + "useTabs": false, + "bracketSpacing": true, + "arrowParens": "always", + "endOfLine": "lf" +} diff --git a/frontend/eslint.config.js b/frontend/eslint.config.js new file mode 100644 index 0000000..cfd81c9 --- /dev/null +++ b/frontend/eslint.config.js @@ -0,0 +1,25 @@ +import js from '@eslint/js'; +import globals from 'globals'; +import reactHooks from 'eslint-plugin-react-hooks'; +import reactRefresh from 'eslint-plugin-react-refresh'; +import tseslint from 'typescript-eslint'; + +export default tseslint.config( + { ignores: ['dist'] }, + { + extends: [js.configs.recommended, ...tseslint.configs.recommended], + files: ['**/*.{ts,tsx}'], + languageOptions: { + ecmaVersion: 2020, + globals: globals.browser, + }, + plugins: { + 'react-hooks': reactHooks, + 'react-refresh': reactRefresh, + }, + rules: { + ...reactHooks.configs.recommended.rules, + 'react-refresh/only-export-components': ['warn', { allowConstantExport: true }], + }, + }, +); diff --git a/frontend/index.html b/frontend/index.html new file mode 100644 index 0000000..0dd76ac --- /dev/null +++ b/frontend/index.html @@ -0,0 +1,17 @@ + + + + + + + + + + + SNP Connection Monitoring + + +
+ + + diff --git a/frontend/package-lock.json b/frontend/package-lock.json new file mode 100644 index 0000000..87c8372 --- /dev/null +++ b/frontend/package-lock.json @@ -0,0 +1,3968 @@ +{ + "name": "frontend", + "version": "0.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "frontend", + "version": "0.0.0", + "dependencies": { + "react": "^19.2.0", + "react-dom": "^19.2.0", + "react-router-dom": "^7.13.0" + }, + "devDependencies": { + "@eslint/js": "^9.39.1", + "@tailwindcss/vite": "^4.1.18", + "@types/node": "^24.10.1", + "@types/react": "^19.2.7", + "@types/react-dom": "^19.2.3", + "@vitejs/plugin-react": "^5.1.1", + "eslint": "^9.39.1", + "eslint-plugin-react-hooks": "^7.0.1", + "eslint-plugin-react-refresh": "^0.4.24", + "globals": "^16.5.0", + "prettier": "^3.5.3", + "tailwindcss": "^4.1.18", + "typescript": "~5.9.3", + "typescript-eslint": "^8.48.0", + "vite": "^7.3.1" + } + }, + "node_modules/@babel/code-frame": { + "version": "7.29.0", + "resolved": "https://nexus.gc-si.dev/repository/npm-public/@babel/code-frame/-/code-frame-7.29.0.tgz", + "integrity": "sha512-9NhCeYjq9+3uxgdtp20LSiJXJvN0FeCtNGpJxuMFZ1Kv3cWUNb6DOhJwUvcVCzKGR66cw4njwM6hrJLqgOwbcw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-validator-identifier": "^7.28.5", + "js-tokens": "^4.0.0", + "picocolors": "^1.1.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/compat-data": { + "version": "7.29.0", + "resolved": "https://nexus.gc-si.dev/repository/npm-public/@babel/compat-data/-/compat-data-7.29.0.tgz", + "integrity": "sha512-T1NCJqT/j9+cn8fvkt7jtwbLBfLC/1y1c7NtCeXFRgzGTsafi68MRv8yzkYSapBnFA6L3U2VSc02ciDzoAJhJg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/core": { + "version": "7.29.0", + "resolved": "https://nexus.gc-si.dev/repository/npm-public/@babel/core/-/core-7.29.0.tgz", + "integrity": "sha512-CGOfOJqWjg2qW/Mb6zNsDm+u5vFQ8DxXfbM09z69p5Z6+mE1ikP2jUXw+j42Pf1XTYED2Rni5f95npYeuwMDQA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.29.0", + "@babel/generator": "^7.29.0", + "@babel/helper-compilation-targets": "^7.28.6", + "@babel/helper-module-transforms": "^7.28.6", + "@babel/helpers": "^7.28.6", + "@babel/parser": "^7.29.0", + "@babel/template": "^7.28.6", + "@babel/traverse": "^7.29.0", + "@babel/types": "^7.29.0", + "@jridgewell/remapping": "^2.3.5", + "convert-source-map": "^2.0.0", + "debug": "^4.1.0", + "gensync": "^1.0.0-beta.2", + "json5": "^2.2.3", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/babel" + } + }, + "node_modules/@babel/generator": { + "version": "7.29.1", + "resolved": "https://nexus.gc-si.dev/repository/npm-public/@babel/generator/-/generator-7.29.1.tgz", + "integrity": "sha512-qsaF+9Qcm2Qv8SRIMMscAvG4O3lJ0F1GuMo5HR/Bp02LopNgnZBC/EkbevHFeGs4ls/oPz9v+Bsmzbkbe+0dUw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.29.0", + "@babel/types": "^7.29.0", + "@jridgewell/gen-mapping": "^0.3.12", + "@jridgewell/trace-mapping": "^0.3.28", + "jsesc": "^3.0.2" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-compilation-targets": { + "version": "7.28.6", + "resolved": "https://nexus.gc-si.dev/repository/npm-public/@babel/helper-compilation-targets/-/helper-compilation-targets-7.28.6.tgz", + "integrity": "sha512-JYtls3hqi15fcx5GaSNL7SCTJ2MNmjrkHXg4FSpOA/grxK8KwyZ5bubHsCq8FXCkua6xhuaaBit+3b7+VZRfcA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/compat-data": "^7.28.6", + "@babel/helper-validator-option": "^7.27.1", + "browserslist": "^4.24.0", + "lru-cache": "^5.1.1", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-globals": { + "version": "7.28.0", + "resolved": "https://nexus.gc-si.dev/repository/npm-public/@babel/helper-globals/-/helper-globals-7.28.0.tgz", + "integrity": "sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-imports": { + "version": "7.28.6", + "resolved": "https://nexus.gc-si.dev/repository/npm-public/@babel/helper-module-imports/-/helper-module-imports-7.28.6.tgz", + "integrity": "sha512-l5XkZK7r7wa9LucGw9LwZyyCUscb4x37JWTPz7swwFE/0FMQAGpiWUZn8u9DzkSBWEcK25jmvubfpw2dnAMdbw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/traverse": "^7.28.6", + "@babel/types": "^7.28.6" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-transforms": { + "version": "7.28.6", + "resolved": "https://nexus.gc-si.dev/repository/npm-public/@babel/helper-module-transforms/-/helper-module-transforms-7.28.6.tgz", + "integrity": "sha512-67oXFAYr2cDLDVGLXTEABjdBJZ6drElUSI7WKp70NrpyISso3plG9SAGEF6y7zbha/wOzUByWWTJvEDVNIUGcA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-module-imports": "^7.28.6", + "@babel/helper-validator-identifier": "^7.28.5", + "@babel/traverse": "^7.28.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-plugin-utils": { + "version": "7.28.6", + "resolved": "https://nexus.gc-si.dev/repository/npm-public/@babel/helper-plugin-utils/-/helper-plugin-utils-7.28.6.tgz", + "integrity": "sha512-S9gzZ/bz83GRysI7gAD4wPT/AI3uCnY+9xn+Mx/KPs2JwHJIz1W8PZkg2cqyt3RNOBM8ejcXhV6y8Og7ly/Dug==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-string-parser": { + "version": "7.27.1", + "resolved": "https://nexus.gc-si.dev/repository/npm-public/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz", + "integrity": "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.28.5", + "resolved": "https://nexus.gc-si.dev/repository/npm-public/@babel/helper-validator-identifier/-/helper-validator-identifier-7.28.5.tgz", + "integrity": "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-option": { + "version": "7.27.1", + "resolved": "https://nexus.gc-si.dev/repository/npm-public/@babel/helper-validator-option/-/helper-validator-option-7.27.1.tgz", + "integrity": "sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helpers": { + "version": "7.29.2", + "resolved": "https://nexus.gc-si.dev/repository/npm-public/@babel/helpers/-/helpers-7.29.2.tgz", + "integrity": "sha512-HoGuUs4sCZNezVEKdVcwqmZN8GoHirLUcLaYVNBK2J0DadGtdcqgr3BCbvH8+XUo4NGjNl3VOtSjEKNzqfFgKw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/template": "^7.28.6", + "@babel/types": "^7.29.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/parser": { + "version": "7.29.2", + "resolved": "https://nexus.gc-si.dev/repository/npm-public/@babel/parser/-/parser-7.29.2.tgz", + "integrity": "sha512-4GgRzy/+fsBa72/RZVJmGKPmZu9Byn8o4MoLpmNe1m8ZfYnz5emHLQz3U4gLud6Zwl0RZIcgiLD7Uq7ySFuDLA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.29.0" + }, + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/plugin-transform-react-jsx-self": { + "version": "7.27.1", + "resolved": "https://nexus.gc-si.dev/repository/npm-public/@babel/plugin-transform-react-jsx-self/-/plugin-transform-react-jsx-self-7.27.1.tgz", + "integrity": "sha512-6UzkCs+ejGdZ5mFFC/OCUrv028ab2fp1znZmCZjAOBKiBK2jXD1O+BPSfX8X2qjJ75fZBMSnQn3Rq2mrBJK2mw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-react-jsx-source": { + "version": "7.27.1", + "resolved": "https://nexus.gc-si.dev/repository/npm-public/@babel/plugin-transform-react-jsx-source/-/plugin-transform-react-jsx-source-7.27.1.tgz", + "integrity": "sha512-zbwoTsBruTeKB9hSq73ha66iFeJHuaFkUbwvqElnygoNbj/jHRsSeokowZFN3CZ64IvEqcmmkVe89OPXc7ldAw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/template": { + "version": "7.28.6", + "resolved": "https://nexus.gc-si.dev/repository/npm-public/@babel/template/-/template-7.28.6.tgz", + "integrity": "sha512-YA6Ma2KsCdGb+WC6UpBVFJGXL58MDA6oyONbjyF/+5sBgxY/dwkhLogbMT2GXXyU84/IhRw/2D1Os1B/giz+BQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.28.6", + "@babel/parser": "^7.28.6", + "@babel/types": "^7.28.6" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/traverse": { + "version": "7.29.0", + "resolved": "https://nexus.gc-si.dev/repository/npm-public/@babel/traverse/-/traverse-7.29.0.tgz", + "integrity": "sha512-4HPiQr0X7+waHfyXPZpWPfWL/J7dcN1mx9gL6WdQVMbPnF3+ZhSMs8tCxN7oHddJE9fhNE7+lxdnlyemKfJRuA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.29.0", + "@babel/generator": "^7.29.0", + "@babel/helper-globals": "^7.28.0", + "@babel/parser": "^7.29.0", + "@babel/template": "^7.28.6", + "@babel/types": "^7.29.0", + "debug": "^4.3.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/types": { + "version": "7.29.0", + "resolved": "https://nexus.gc-si.dev/repository/npm-public/@babel/types/-/types-7.29.0.tgz", + "integrity": "sha512-LwdZHpScM4Qz8Xw2iKSzS+cfglZzJGvofQICy7W7v4caru4EaAmyUuO6BGrbyQ2mYV11W0U8j5mBhd14dd3B0A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-string-parser": "^7.27.1", + "@babel/helper-validator-identifier": "^7.28.5" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@esbuild/aix-ppc64": { + "version": "0.27.7", + "resolved": "https://nexus.gc-si.dev/repository/npm-public/@esbuild/aix-ppc64/-/aix-ppc64-0.27.7.tgz", + "integrity": "sha512-EKX3Qwmhz1eMdEJokhALr0YiD0lhQNwDqkPYyPhiSwKrh7/4KRjQc04sZ8db+5DVVnZ1LmbNDI1uAMPEUBnQPg==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.27.7", + "resolved": "https://nexus.gc-si.dev/repository/npm-public/@esbuild/android-arm/-/android-arm-0.27.7.tgz", + "integrity": "sha512-jbPXvB4Yj2yBV7HUfE2KHe4GJX51QplCN1pGbYjvsyCZbQmies29EoJbkEc+vYuU5o45AfQn37vZlyXy4YJ8RQ==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.27.7", + "resolved": "https://nexus.gc-si.dev/repository/npm-public/@esbuild/android-arm64/-/android-arm64-0.27.7.tgz", + "integrity": "sha512-62dPZHpIXzvChfvfLJow3q5dDtiNMkwiRzPylSCfriLvZeq0a1bWChrGx/BbUbPwOrsWKMn8idSllklzBy+dgQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.27.7", + "resolved": "https://nexus.gc-si.dev/repository/npm-public/@esbuild/android-x64/-/android-x64-0.27.7.tgz", + "integrity": "sha512-x5VpMODneVDb70PYV2VQOmIUUiBtY3D3mPBG8NxVk5CogneYhkR7MmM3yR/uMdITLrC1ml/NV1rj4bMJuy9MCg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.27.7", + "resolved": "https://nexus.gc-si.dev/repository/npm-public/@esbuild/darwin-arm64/-/darwin-arm64-0.27.7.tgz", + "integrity": "sha512-5lckdqeuBPlKUwvoCXIgI2D9/ABmPq3Rdp7IfL70393YgaASt7tbju3Ac+ePVi3KDH6N2RqePfHnXkaDtY9fkw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.27.7", + "resolved": "https://nexus.gc-si.dev/repository/npm-public/@esbuild/darwin-x64/-/darwin-x64-0.27.7.tgz", + "integrity": "sha512-rYnXrKcXuT7Z+WL5K980jVFdvVKhCHhUwid+dDYQpH+qu+TefcomiMAJpIiC2EM3Rjtq0sO3StMV/+3w3MyyqQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.27.7", + "resolved": "https://nexus.gc-si.dev/repository/npm-public/@esbuild/freebsd-arm64/-/freebsd-arm64-0.27.7.tgz", + "integrity": "sha512-B48PqeCsEgOtzME2GbNM2roU29AMTuOIN91dsMO30t+Ydis3z/3Ngoj5hhnsOSSwNzS+6JppqWsuhTp6E82l2w==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.27.7", + "resolved": "https://nexus.gc-si.dev/repository/npm-public/@esbuild/freebsd-x64/-/freebsd-x64-0.27.7.tgz", + "integrity": "sha512-jOBDK5XEjA4m5IJK3bpAQF9/Lelu/Z9ZcdhTRLf4cajlB+8VEhFFRjWgfy3M1O4rO2GQ/b2dLwCUGpiF/eATNQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.27.7", + "resolved": "https://nexus.gc-si.dev/repository/npm-public/@esbuild/linux-arm/-/linux-arm-0.27.7.tgz", + "integrity": "sha512-RkT/YXYBTSULo3+af8Ib0ykH8u2MBh57o7q/DAs3lTJlyVQkgQvlrPTnjIzzRPQyavxtPtfg0EopvDyIt0j1rA==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.27.7", + "resolved": "https://nexus.gc-si.dev/repository/npm-public/@esbuild/linux-arm64/-/linux-arm64-0.27.7.tgz", + "integrity": "sha512-RZPHBoxXuNnPQO9rvjh5jdkRmVizktkT7TCDkDmQ0W2SwHInKCAV95GRuvdSvA7w4VMwfCjUiPwDi0ZO6Nfe9A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.27.7", + "resolved": "https://nexus.gc-si.dev/repository/npm-public/@esbuild/linux-ia32/-/linux-ia32-0.27.7.tgz", + "integrity": "sha512-GA48aKNkyQDbd3KtkplYWT102C5sn/EZTY4XROkxONgruHPU72l+gW+FfF8tf2cFjeHaRbWpOYa/uRBz/Xq1Pg==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.27.7", + "resolved": "https://nexus.gc-si.dev/repository/npm-public/@esbuild/linux-loong64/-/linux-loong64-0.27.7.tgz", + "integrity": "sha512-a4POruNM2oWsD4WKvBSEKGIiWQF8fZOAsycHOt6JBpZ+JN2n2JH9WAv56SOyu9X5IqAjqSIPTaJkqN8F7XOQ5Q==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.27.7", + "resolved": "https://nexus.gc-si.dev/repository/npm-public/@esbuild/linux-mips64el/-/linux-mips64el-0.27.7.tgz", + "integrity": "sha512-KabT5I6StirGfIz0FMgl1I+R1H73Gp0ofL9A3nG3i/cYFJzKHhouBV5VWK1CSgKvVaG4q1RNpCTR2LuTVB3fIw==", + "cpu": [ + "mips64el" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.27.7", + "resolved": "https://nexus.gc-si.dev/repository/npm-public/@esbuild/linux-ppc64/-/linux-ppc64-0.27.7.tgz", + "integrity": "sha512-gRsL4x6wsGHGRqhtI+ifpN/vpOFTQtnbsupUF5R5YTAg+y/lKelYR1hXbnBdzDjGbMYjVJLJTd2OFmMewAgwlQ==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.27.7", + "resolved": "https://nexus.gc-si.dev/repository/npm-public/@esbuild/linux-riscv64/-/linux-riscv64-0.27.7.tgz", + "integrity": "sha512-hL25LbxO1QOngGzu2U5xeXtxXcW+/GvMN3ejANqXkxZ/opySAZMrc+9LY/WyjAan41unrR3YrmtTsUpwT66InQ==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.27.7", + "resolved": "https://nexus.gc-si.dev/repository/npm-public/@esbuild/linux-s390x/-/linux-s390x-0.27.7.tgz", + "integrity": "sha512-2k8go8Ycu1Kb46vEelhu1vqEP+UeRVj2zY1pSuPdgvbd5ykAw82Lrro28vXUrRmzEsUV0NzCf54yARIK8r0fdw==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.27.7", + "resolved": "https://nexus.gc-si.dev/repository/npm-public/@esbuild/linux-x64/-/linux-x64-0.27.7.tgz", + "integrity": "sha512-hzznmADPt+OmsYzw1EE33ccA+HPdIqiCRq7cQeL1Jlq2gb1+OyWBkMCrYGBJ+sxVzve2ZJEVeePbLM2iEIZSxA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-arm64": { + "version": "0.27.7", + "resolved": "https://nexus.gc-si.dev/repository/npm-public/@esbuild/netbsd-arm64/-/netbsd-arm64-0.27.7.tgz", + "integrity": "sha512-b6pqtrQdigZBwZxAn1UpazEisvwaIDvdbMbmrly7cDTMFnw/+3lVxxCTGOrkPVnsYIosJJXAsILG9XcQS+Yu6w==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.27.7", + "resolved": "https://nexus.gc-si.dev/repository/npm-public/@esbuild/netbsd-x64/-/netbsd-x64-0.27.7.tgz", + "integrity": "sha512-OfatkLojr6U+WN5EDYuoQhtM+1xco+/6FSzJJnuWiUw5eVcicbyK3dq5EeV/QHT1uy6GoDhGbFpprUiHUYggrw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-arm64": { + "version": "0.27.7", + "resolved": "https://nexus.gc-si.dev/repository/npm-public/@esbuild/openbsd-arm64/-/openbsd-arm64-0.27.7.tgz", + "integrity": "sha512-AFuojMQTxAz75Fo8idVcqoQWEHIXFRbOc1TrVcFSgCZtQfSdc1RXgB3tjOn/krRHENUB4j00bfGjyl2mJrU37A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.27.7", + "resolved": "https://nexus.gc-si.dev/repository/npm-public/@esbuild/openbsd-x64/-/openbsd-x64-0.27.7.tgz", + "integrity": "sha512-+A1NJmfM8WNDv5CLVQYJ5PshuRm/4cI6WMZRg1by1GwPIQPCTs1GLEUHwiiQGT5zDdyLiRM/l1G0Pv54gvtKIg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openharmony-arm64": { + "version": "0.27.7", + "resolved": "https://nexus.gc-si.dev/repository/npm-public/@esbuild/openharmony-arm64/-/openharmony-arm64-0.27.7.tgz", + "integrity": "sha512-+KrvYb/C8zA9CU/g0sR6w2RBw7IGc5J2BPnc3dYc5VJxHCSF1yNMxTV5LQ7GuKteQXZtspjFbiuW5/dOj7H4Yw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.27.7", + "resolved": "https://nexus.gc-si.dev/repository/npm-public/@esbuild/sunos-x64/-/sunos-x64-0.27.7.tgz", + "integrity": "sha512-ikktIhFBzQNt/QDyOL580ti9+5mL/YZeUPKU2ivGtGjdTYoqz6jObj6nOMfhASpS4GU4Q/Clh1QtxWAvcYKamA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.27.7", + "resolved": "https://nexus.gc-si.dev/repository/npm-public/@esbuild/win32-arm64/-/win32-arm64-0.27.7.tgz", + "integrity": "sha512-7yRhbHvPqSpRUV7Q20VuDwbjW5kIMwTHpptuUzV+AA46kiPze5Z7qgt6CLCK3pWFrHeNfDd1VKgyP4O+ng17CA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.27.7", + "resolved": "https://nexus.gc-si.dev/repository/npm-public/@esbuild/win32-ia32/-/win32-ia32-0.27.7.tgz", + "integrity": "sha512-SmwKXe6VHIyZYbBLJrhOoCJRB/Z1tckzmgTLfFYOfpMAx63BJEaL9ExI8x7v0oAO3Zh6D/Oi1gVxEYr5oUCFhw==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.27.7", + "resolved": "https://nexus.gc-si.dev/repository/npm-public/@esbuild/win32-x64/-/win32-x64-0.27.7.tgz", + "integrity": "sha512-56hiAJPhwQ1R4i+21FVF7V8kSD5zZTdHcVuRFMW0hn753vVfQN8xlx4uOPT4xoGH0Z/oVATuR82AiqSTDIpaHg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@eslint-community/eslint-utils": { + "version": "4.9.1", + "resolved": "https://nexus.gc-si.dev/repository/npm-public/@eslint-community/eslint-utils/-/eslint-utils-4.9.1.tgz", + "integrity": "sha512-phrYmNiYppR7znFEdqgfWHXR6NCkZEK7hwWDHZUjit/2/U0r6XvkDl0SYnoM51Hq7FhCGdLDT6zxCCOY1hexsQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "eslint-visitor-keys": "^3.4.3" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" + } + }, + "node_modules/@eslint-community/eslint-utils/node_modules/eslint-visitor-keys": { + "version": "3.4.3", + "resolved": "https://nexus.gc-si.dev/repository/npm-public/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", + "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@eslint-community/regexpp": { + "version": "4.12.2", + "resolved": "https://nexus.gc-si.dev/repository/npm-public/@eslint-community/regexpp/-/regexpp-4.12.2.tgz", + "integrity": "sha512-EriSTlt5OC9/7SXkRSCAhfSxxoSUgBm33OH+IkwbdpgoqsSsUg7y3uh+IICI/Qg4BBWr3U2i39RpmycbxMq4ew==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.0.0 || ^14.0.0 || >=16.0.0" + } + }, + "node_modules/@eslint/config-array": { + "version": "0.21.2", + "resolved": "https://nexus.gc-si.dev/repository/npm-public/@eslint/config-array/-/config-array-0.21.2.tgz", + "integrity": "sha512-nJl2KGTlrf9GjLimgIru+V/mzgSK0ABCDQRvxw5BjURL7WfH5uoWmizbH7QB6MmnMBd8cIC9uceWnezL1VZWWw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@eslint/object-schema": "^2.1.7", + "debug": "^4.3.1", + "minimatch": "^3.1.5" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/config-helpers": { + "version": "0.4.2", + "resolved": "https://nexus.gc-si.dev/repository/npm-public/@eslint/config-helpers/-/config-helpers-0.4.2.tgz", + "integrity": "sha512-gBrxN88gOIf3R7ja5K9slwNayVcZgK6SOUORm2uBzTeIEfeVaIhOpCtTox3P6R7o2jLFwLFTLnC7kU/RGcYEgw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@eslint/core": "^0.17.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/core": { + "version": "0.17.0", + "resolved": "https://nexus.gc-si.dev/repository/npm-public/@eslint/core/-/core-0.17.0.tgz", + "integrity": "sha512-yL/sLrpmtDaFEiUj1osRP4TI2MDz1AddJL+jZ7KSqvBuliN4xqYY54IfdN8qD8Toa6g1iloph1fxQNkjOxrrpQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@types/json-schema": "^7.0.15" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/eslintrc": { + "version": "3.3.5", + "resolved": "https://nexus.gc-si.dev/repository/npm-public/@eslint/eslintrc/-/eslintrc-3.3.5.tgz", + "integrity": "sha512-4IlJx0X0qftVsN5E+/vGujTRIFtwuLbNsVUe7TO6zYPDR1O6nFwvwhIKEKSrl6dZchmYBITazxKoUYOjdtjlRg==", + "dev": true, + "license": "MIT", + "dependencies": { + "ajv": "^6.14.0", + "debug": "^4.3.2", + "espree": "^10.0.1", + "globals": "^14.0.0", + "ignore": "^5.2.0", + "import-fresh": "^3.2.1", + "js-yaml": "^4.1.1", + "minimatch": "^3.1.5", + "strip-json-comments": "^3.1.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@eslint/eslintrc/node_modules/globals": { + "version": "14.0.0", + "resolved": "https://nexus.gc-si.dev/repository/npm-public/globals/-/globals-14.0.0.tgz", + "integrity": "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@eslint/js": { + "version": "9.39.4", + "resolved": "https://nexus.gc-si.dev/repository/npm-public/@eslint/js/-/js-9.39.4.tgz", + "integrity": "sha512-nE7DEIchvtiFTwBw4Lfbu59PG+kCofhjsKaCWzxTpt4lfRjRMqG6uMBzKXuEcyXhOHoUp9riAm7/aWYGhXZ9cw==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://eslint.org/donate" + } + }, + "node_modules/@eslint/object-schema": { + "version": "2.1.7", + "resolved": "https://nexus.gc-si.dev/repository/npm-public/@eslint/object-schema/-/object-schema-2.1.7.tgz", + "integrity": "sha512-VtAOaymWVfZcmZbp6E2mympDIHvyjXs/12LqWYjVw6qjrfF+VK+fyG33kChz3nnK+SU5/NeHOqrTEHS8sXO3OA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/plugin-kit": { + "version": "0.4.1", + "resolved": "https://nexus.gc-si.dev/repository/npm-public/@eslint/plugin-kit/-/plugin-kit-0.4.1.tgz", + "integrity": "sha512-43/qtrDUokr7LJqoF2c3+RInu/t4zfrpYdoSDfYyhg52rwLV6TnOvdG4fXm7IkSB3wErkcmJS9iEhjVtOSEjjA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@eslint/core": "^0.17.0", + "levn": "^0.4.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@humanfs/core": { + "version": "0.19.1", + "resolved": "https://nexus.gc-si.dev/repository/npm-public/@humanfs/core/-/core-0.19.1.tgz", + "integrity": "sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=18.18.0" + } + }, + "node_modules/@humanfs/node": { + "version": "0.16.7", + "resolved": "https://nexus.gc-si.dev/repository/npm-public/@humanfs/node/-/node-0.16.7.tgz", + "integrity": "sha512-/zUx+yOsIrG4Y43Eh2peDeKCxlRt/gET6aHfaKpuq267qXdYDFViVHfMaLyygZOnl0kGWxFIgsBy8QFuTLUXEQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@humanfs/core": "^0.19.1", + "@humanwhocodes/retry": "^0.4.0" + }, + "engines": { + "node": ">=18.18.0" + } + }, + "node_modules/@humanwhocodes/module-importer": { + "version": "1.0.1", + "resolved": "https://nexus.gc-si.dev/repository/npm-public/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", + "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=12.22" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@humanwhocodes/retry": { + "version": "0.4.3", + "resolved": "https://nexus.gc-si.dev/repository/npm-public/@humanwhocodes/retry/-/retry-0.4.3.tgz", + "integrity": "sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=18.18" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@jridgewell/gen-mapping": { + "version": "0.3.13", + "resolved": "https://nexus.gc-si.dev/repository/npm-public/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz", + "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.0", + "@jridgewell/trace-mapping": "^0.3.24" + } + }, + "node_modules/@jridgewell/remapping": { + "version": "2.3.5", + "resolved": "https://nexus.gc-si.dev/repository/npm-public/@jridgewell/remapping/-/remapping-2.3.5.tgz", + "integrity": "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.24" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.2", + "resolved": "https://nexus.gc-si.dev/repository/npm-public/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.5.5", + "resolved": "https://nexus.gc-si.dev/repository/npm-public/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", + "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", + "dev": true, + "license": "MIT" + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.31", + "resolved": "https://nexus.gc-si.dev/repository/npm-public/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz", + "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, + "node_modules/@rolldown/pluginutils": { + "version": "1.0.0-rc.3", + "resolved": "https://nexus.gc-si.dev/repository/npm-public/@rolldown/pluginutils/-/pluginutils-1.0.0-rc.3.tgz", + "integrity": "sha512-eybk3TjzzzV97Dlj5c+XrBFW57eTNhzod66y9HrBlzJ6NsCrWCp/2kaPS3K9wJmurBC0Tdw4yPjXKZqlznim3Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/@rollup/rollup-android-arm-eabi": { + "version": "4.60.1", + "resolved": "https://nexus.gc-si.dev/repository/npm-public/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.60.1.tgz", + "integrity": "sha512-d6FinEBLdIiK+1uACUttJKfgZREXrF0Qc2SmLII7W2AD8FfiZ9Wjd+rD/iRuf5s5dWrr1GgwXCvPqOuDquOowA==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-android-arm64": { + "version": "4.60.1", + "resolved": "https://nexus.gc-si.dev/repository/npm-public/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.60.1.tgz", + "integrity": "sha512-YjG/EwIDvvYI1YvYbHvDz/BYHtkY4ygUIXHnTdLhG+hKIQFBiosfWiACWortsKPKU/+dUwQQCKQM3qrDe8c9BA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-darwin-arm64": { + "version": "4.60.1", + "resolved": "https://nexus.gc-si.dev/repository/npm-public/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.60.1.tgz", + "integrity": "sha512-mjCpF7GmkRtSJwon+Rq1N8+pI+8l7w5g9Z3vWj4T7abguC4Czwi3Yu/pFaLvA3TTeMVjnu3ctigusqWUfjZzvw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-darwin-x64": { + "version": "4.60.1", + "resolved": "https://nexus.gc-si.dev/repository/npm-public/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.60.1.tgz", + "integrity": "sha512-haZ7hJ1JT4e9hqkoT9R/19XW2QKqjfJVv+i5AGg57S+nLk9lQnJ1F/eZloRO3o9Scy9CM3wQ9l+dkXtcBgN5Ew==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-freebsd-arm64": { + "version": "4.60.1", + "resolved": "https://nexus.gc-si.dev/repository/npm-public/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.60.1.tgz", + "integrity": "sha512-czw90wpQq3ZsAVBlinZjAYTKduOjTywlG7fEeWKUA7oCmpA8xdTkxZZlwNJKWqILlq0wehoZcJYfBvOyhPTQ6w==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-freebsd-x64": { + "version": "4.60.1", + "resolved": "https://nexus.gc-si.dev/repository/npm-public/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.60.1.tgz", + "integrity": "sha512-KVB2rqsxTHuBtfOeySEyzEOB7ltlB/ux38iu2rBQzkjbwRVlkhAGIEDiiYnO2kFOkJp+Z7pUXKyrRRFuFUKt+g==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-linux-arm-gnueabihf": { + "version": "4.60.1", + "resolved": "https://nexus.gc-si.dev/repository/npm-public/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.60.1.tgz", + "integrity": "sha512-L+34Qqil+v5uC0zEubW7uByo78WOCIrBvci69E7sFASRl0X7b/MB6Cqd1lky/CtcSVTydWa2WZwFuWexjS5o6g==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm-musleabihf": { + "version": "4.60.1", + "resolved": "https://nexus.gc-si.dev/repository/npm-public/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.60.1.tgz", + "integrity": "sha512-n83O8rt4v34hgFzlkb1ycniJh7IR5RCIqt6mz1VRJD6pmhRi0CXdmfnLu9dIUS6buzh60IvACM842Ffb3xd6Gg==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-gnu": { + "version": "4.60.1", + "resolved": "https://nexus.gc-si.dev/repository/npm-public/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.60.1.tgz", + "integrity": "sha512-Nql7sTeAzhTAja3QXeAI48+/+GjBJ+QmAH13snn0AJSNL50JsDqotyudHyMbO2RbJkskbMbFJfIJKWA6R1LCJQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-musl": { + "version": "4.60.1", + "resolved": "https://nexus.gc-si.dev/repository/npm-public/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.60.1.tgz", + "integrity": "sha512-+pUymDhd0ys9GcKZPPWlFiZ67sTWV5UU6zOJat02M1+PiuSGDziyRuI/pPue3hoUwm2uGfxdL+trT6Z9rxnlMA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-loong64-gnu": { + "version": "4.60.1", + "resolved": "https://nexus.gc-si.dev/repository/npm-public/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.60.1.tgz", + "integrity": "sha512-VSvgvQeIcsEvY4bKDHEDWcpW4Yw7BtlKG1GUT4FzBUlEKQK0rWHYBqQt6Fm2taXS+1bXvJT6kICu5ZwqKCnvlQ==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-loong64-musl": { + "version": "4.60.1", + "resolved": "https://nexus.gc-si.dev/repository/npm-public/@rollup/rollup-linux-loong64-musl/-/rollup-linux-loong64-musl-4.60.1.tgz", + "integrity": "sha512-4LqhUomJqwe641gsPp6xLfhqWMbQV04KtPp7/dIp0nzPxAkNY1AbwL5W0MQpcalLYk07vaW9Kp1PBhdpZYYcEw==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-ppc64-gnu": { + "version": "4.60.1", + "resolved": "https://nexus.gc-si.dev/repository/npm-public/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.60.1.tgz", + "integrity": "sha512-tLQQ9aPvkBxOc/EUT6j3pyeMD6Hb8QF2BTBnCQWP/uu1lhc9AIrIjKnLYMEroIz/JvtGYgI9dF3AxHZNaEH0rw==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-ppc64-musl": { + "version": "4.60.1", + "resolved": "https://nexus.gc-si.dev/repository/npm-public/@rollup/rollup-linux-ppc64-musl/-/rollup-linux-ppc64-musl-4.60.1.tgz", + "integrity": "sha512-RMxFhJwc9fSXP6PqmAz4cbv3kAyvD1etJFjTx4ONqFP9DkTkXsAMU4v3Vyc5BgzC+anz7nS/9tp4obsKfqkDHg==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-gnu": { + "version": "4.60.1", + "resolved": "https://nexus.gc-si.dev/repository/npm-public/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.60.1.tgz", + "integrity": "sha512-QKgFl+Yc1eEk6MmOBfRHYF6lTxiiiV3/z/BRrbSiW2I7AFTXoBFvdMEyglohPj//2mZS4hDOqeB0H1ACh3sBbg==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-musl": { + "version": "4.60.1", + "resolved": "https://nexus.gc-si.dev/repository/npm-public/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.60.1.tgz", + "integrity": "sha512-RAjXjP/8c6ZtzatZcA1RaQr6O1TRhzC+adn8YZDnChliZHviqIjmvFwHcxi4JKPSDAt6Uhf/7vqcBzQJy0PDJg==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-s390x-gnu": { + "version": "4.60.1", + "resolved": "https://nexus.gc-si.dev/repository/npm-public/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.60.1.tgz", + "integrity": "sha512-wcuocpaOlaL1COBYiA89O6yfjlp3RwKDeTIA0hM7OpmhR1Bjo9j31G1uQVpDlTvwxGn2nQs65fBFL5UFd76FcQ==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-gnu": { + "version": "4.60.1", + "resolved": "https://nexus.gc-si.dev/repository/npm-public/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.60.1.tgz", + "integrity": "sha512-77PpsFQUCOiZR9+LQEFg9GClyfkNXj1MP6wRnzYs0EeWbPcHs02AXu4xuUbM1zhwn3wqaizle3AEYg5aeoohhg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-musl": { + "version": "4.60.1", + "resolved": "https://nexus.gc-si.dev/repository/npm-public/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.60.1.tgz", + "integrity": "sha512-5cIATbk5vynAjqqmyBjlciMJl1+R/CwX9oLk/EyiFXDWd95KpHdrOJT//rnUl4cUcskrd0jCCw3wpZnhIHdD9w==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-openbsd-x64": { + "version": "4.60.1", + "resolved": "https://nexus.gc-si.dev/repository/npm-public/@rollup/rollup-openbsd-x64/-/rollup-openbsd-x64-4.60.1.tgz", + "integrity": "sha512-cl0w09WsCi17mcmWqqglez9Gk8isgeWvoUZ3WiJFYSR3zjBQc2J5/ihSjpl+VLjPqjQ/1hJRcqBfLjssREQILw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ] + }, + "node_modules/@rollup/rollup-openharmony-arm64": { + "version": "4.60.1", + "resolved": "https://nexus.gc-si.dev/repository/npm-public/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.60.1.tgz", + "integrity": "sha512-4Cv23ZrONRbNtbZa37mLSueXUCtN7MXccChtKpUnQNgF010rjrjfHx3QxkS2PI7LqGT5xXyYs1a7LbzAwT0iCA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ] + }, + "node_modules/@rollup/rollup-win32-arm64-msvc": { + "version": "4.60.1", + "resolved": "https://nexus.gc-si.dev/repository/npm-public/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.60.1.tgz", + "integrity": "sha512-i1okWYkA4FJICtr7KpYzFpRTHgy5jdDbZiWfvny21iIKky5YExiDXP+zbXzm3dUcFpkEeYNHgQ5fuG236JPq0g==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-ia32-msvc": { + "version": "4.60.1", + "resolved": "https://nexus.gc-si.dev/repository/npm-public/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.60.1.tgz", + "integrity": "sha512-u09m3CuwLzShA0EYKMNiFgcjjzwqtUMLmuCJLeZWjjOYA3IT2Di09KaxGBTP9xVztWyIWjVdsB2E9goMjZvTQg==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-gnu": { + "version": "4.60.1", + "resolved": "https://nexus.gc-si.dev/repository/npm-public/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.60.1.tgz", + "integrity": "sha512-k+600V9Zl1CM7eZxJgMyTUzmrmhB/0XZnF4pRypKAlAgxmedUA+1v9R+XOFv56W4SlHEzfeMtzujLJD22Uz5zg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-msvc": { + "version": "4.60.1", + "resolved": "https://nexus.gc-si.dev/repository/npm-public/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.60.1.tgz", + "integrity": "sha512-lWMnixq/QzxyhTV6NjQJ4SFo1J6PvOX8vUx5Wb4bBPsEb+8xZ89Bz6kOXpfXj9ak9AHTQVQzlgzBEc1SyM27xQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@tailwindcss/node": { + "version": "4.2.2", + "resolved": "https://nexus.gc-si.dev/repository/npm-public/@tailwindcss/node/-/node-4.2.2.tgz", + "integrity": "sha512-pXS+wJ2gZpVXqFaUEjojq7jzMpTGf8rU6ipJz5ovJV6PUGmlJ+jvIwGrzdHdQ80Sg+wmQxUFuoW1UAAwHNEdFA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/remapping": "^2.3.5", + "enhanced-resolve": "^5.19.0", + "jiti": "^2.6.1", + "lightningcss": "1.32.0", + "magic-string": "^0.30.21", + "source-map-js": "^1.2.1", + "tailwindcss": "4.2.2" + } + }, + "node_modules/@tailwindcss/oxide": { + "version": "4.2.2", + "resolved": "https://nexus.gc-si.dev/repository/npm-public/@tailwindcss/oxide/-/oxide-4.2.2.tgz", + "integrity": "sha512-qEUA07+E5kehxYp9BVMpq9E8vnJuBHfJEC0vPC5e7iL/hw7HR61aDKoVoKzrG+QKp56vhNZe4qwkRmMC0zDLvg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 20" + }, + "optionalDependencies": { + "@tailwindcss/oxide-android-arm64": "4.2.2", + "@tailwindcss/oxide-darwin-arm64": "4.2.2", + "@tailwindcss/oxide-darwin-x64": "4.2.2", + "@tailwindcss/oxide-freebsd-x64": "4.2.2", + "@tailwindcss/oxide-linux-arm-gnueabihf": "4.2.2", + "@tailwindcss/oxide-linux-arm64-gnu": "4.2.2", + "@tailwindcss/oxide-linux-arm64-musl": "4.2.2", + "@tailwindcss/oxide-linux-x64-gnu": "4.2.2", + "@tailwindcss/oxide-linux-x64-musl": "4.2.2", + "@tailwindcss/oxide-wasm32-wasi": "4.2.2", + "@tailwindcss/oxide-win32-arm64-msvc": "4.2.2", + "@tailwindcss/oxide-win32-x64-msvc": "4.2.2" + } + }, + "node_modules/@tailwindcss/oxide-android-arm64": { + "version": "4.2.2", + "resolved": "https://nexus.gc-si.dev/repository/npm-public/@tailwindcss/oxide-android-arm64/-/oxide-android-arm64-4.2.2.tgz", + "integrity": "sha512-dXGR1n+P3B6748jZO/SvHZq7qBOqqzQ+yFrXpoOWWALWndF9MoSKAT3Q0fYgAzYzGhxNYOoysRvYlpixRBBoDg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-darwin-arm64": { + "version": "4.2.2", + "resolved": "https://nexus.gc-si.dev/repository/npm-public/@tailwindcss/oxide-darwin-arm64/-/oxide-darwin-arm64-4.2.2.tgz", + "integrity": "sha512-iq9Qjr6knfMpZHj55/37ouZeykwbDqF21gPFtfnhCCKGDcPI/21FKC9XdMO/XyBM7qKORx6UIhGgg6jLl7BZlg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-darwin-x64": { + "version": "4.2.2", + "resolved": "https://nexus.gc-si.dev/repository/npm-public/@tailwindcss/oxide-darwin-x64/-/oxide-darwin-x64-4.2.2.tgz", + "integrity": "sha512-BlR+2c3nzc8f2G639LpL89YY4bdcIdUmiOOkv2GQv4/4M0vJlpXEa0JXNHhCHU7VWOKWT/CjqHdTP8aUuDJkuw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-freebsd-x64": { + "version": "4.2.2", + "resolved": "https://nexus.gc-si.dev/repository/npm-public/@tailwindcss/oxide-freebsd-x64/-/oxide-freebsd-x64-4.2.2.tgz", + "integrity": "sha512-YUqUgrGMSu2CDO82hzlQ5qSb5xmx3RUrke/QgnoEx7KvmRJHQuZHZmZTLSuuHwFf0DJPybFMXMYf+WJdxHy/nQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-linux-arm-gnueabihf": { + "version": "4.2.2", + "resolved": "https://nexus.gc-si.dev/repository/npm-public/@tailwindcss/oxide-linux-arm-gnueabihf/-/oxide-linux-arm-gnueabihf-4.2.2.tgz", + "integrity": "sha512-FPdhvsW6g06T9BWT0qTwiVZYE2WIFo2dY5aCSpjG/S/u1tby+wXoslXS0kl3/KXnULlLr1E3NPRRw0g7t2kgaQ==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-linux-arm64-gnu": { + "version": "4.2.2", + "resolved": "https://nexus.gc-si.dev/repository/npm-public/@tailwindcss/oxide-linux-arm64-gnu/-/oxide-linux-arm64-gnu-4.2.2.tgz", + "integrity": "sha512-4og1V+ftEPXGttOO7eCmW7VICmzzJWgMx+QXAJRAhjrSjumCwWqMfkDrNu1LXEQzNAwz28NCUpucgQPrR4S2yw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-linux-arm64-musl": { + "version": "4.2.2", + "resolved": "https://nexus.gc-si.dev/repository/npm-public/@tailwindcss/oxide-linux-arm64-musl/-/oxide-linux-arm64-musl-4.2.2.tgz", + "integrity": "sha512-oCfG/mS+/+XRlwNjnsNLVwnMWYH7tn/kYPsNPh+JSOMlnt93mYNCKHYzylRhI51X+TbR+ufNhhKKzm6QkqX8ag==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-linux-x64-gnu": { + "version": "4.2.2", + "resolved": "https://nexus.gc-si.dev/repository/npm-public/@tailwindcss/oxide-linux-x64-gnu/-/oxide-linux-x64-gnu-4.2.2.tgz", + "integrity": "sha512-rTAGAkDgqbXHNp/xW0iugLVmX62wOp2PoE39BTCGKjv3Iocf6AFbRP/wZT/kuCxC9QBh9Pu8XPkv/zCZB2mcMg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-linux-x64-musl": { + "version": "4.2.2", + "resolved": "https://nexus.gc-si.dev/repository/npm-public/@tailwindcss/oxide-linux-x64-musl/-/oxide-linux-x64-musl-4.2.2.tgz", + "integrity": "sha512-XW3t3qwbIwiSyRCggeO2zxe3KWaEbM0/kW9e8+0XpBgyKU4ATYzcVSMKteZJ1iukJ3HgHBjbg9P5YPRCVUxlnQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-wasm32-wasi": { + "version": "4.2.2", + "resolved": "https://nexus.gc-si.dev/repository/npm-public/@tailwindcss/oxide-wasm32-wasi/-/oxide-wasm32-wasi-4.2.2.tgz", + "integrity": "sha512-eKSztKsmEsn1O5lJ4ZAfyn41NfG7vzCg496YiGtMDV86jz1q/irhms5O0VrY6ZwTUkFy/EKG3RfWgxSI3VbZ8Q==", + "bundleDependencies": [ + "@napi-rs/wasm-runtime", + "@emnapi/core", + "@emnapi/runtime", + "@tybys/wasm-util", + "@emnapi/wasi-threads", + "tslib" + ], + "cpu": [ + "wasm32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@emnapi/core": "^1.8.1", + "@emnapi/runtime": "^1.8.1", + "@emnapi/wasi-threads": "^1.1.0", + "@napi-rs/wasm-runtime": "^1.1.1", + "@tybys/wasm-util": "^0.10.1", + "tslib": "^2.8.1" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@tailwindcss/oxide-win32-arm64-msvc": { + "version": "4.2.2", + "resolved": "https://nexus.gc-si.dev/repository/npm-public/@tailwindcss/oxide-win32-arm64-msvc/-/oxide-win32-arm64-msvc-4.2.2.tgz", + "integrity": "sha512-qPmaQM4iKu5mxpsrWZMOZRgZv1tOZpUm+zdhhQP0VhJfyGGO3aUKdbh3gDZc/dPLQwW4eSqWGrrcWNBZWUWaXQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-win32-x64-msvc": { + "version": "4.2.2", + "resolved": "https://nexus.gc-si.dev/repository/npm-public/@tailwindcss/oxide-win32-x64-msvc/-/oxide-win32-x64-msvc-4.2.2.tgz", + "integrity": "sha512-1T/37VvI7WyH66b+vqHj/cLwnCxt7Qt3WFu5Q8hk65aOvlwAhs7rAp1VkulBJw/N4tMirXjVnylTR72uI0HGcA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/vite": { + "version": "4.2.2", + "resolved": "https://nexus.gc-si.dev/repository/npm-public/@tailwindcss/vite/-/vite-4.2.2.tgz", + "integrity": "sha512-mEiF5HO1QqCLXoNEfXVA1Tzo+cYsrqV7w9Juj2wdUFyW07JRenqMG225MvPwr3ZD9N1bFQj46X7r33iHxLUW0w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@tailwindcss/node": "4.2.2", + "@tailwindcss/oxide": "4.2.2", + "tailwindcss": "4.2.2" + }, + "peerDependencies": { + "vite": "^5.2.0 || ^6 || ^7 || ^8" + } + }, + "node_modules/@types/babel__core": { + "version": "7.20.5", + "resolved": "https://nexus.gc-si.dev/repository/npm-public/@types/babel__core/-/babel__core-7.20.5.tgz", + "integrity": "sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.20.7", + "@babel/types": "^7.20.7", + "@types/babel__generator": "*", + "@types/babel__template": "*", + "@types/babel__traverse": "*" + } + }, + "node_modules/@types/babel__generator": { + "version": "7.27.0", + "resolved": "https://nexus.gc-si.dev/repository/npm-public/@types/babel__generator/-/babel__generator-7.27.0.tgz", + "integrity": "sha512-ufFd2Xi92OAVPYsy+P4n7/U7e68fex0+Ee8gSG9KX7eo084CWiQ4sdxktvdl0bOPupXtVJPY19zk6EwWqUQ8lg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.0.0" + } + }, + "node_modules/@types/babel__template": { + "version": "7.4.4", + "resolved": "https://nexus.gc-si.dev/repository/npm-public/@types/babel__template/-/babel__template-7.4.4.tgz", + "integrity": "sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.1.0", + "@babel/types": "^7.0.0" + } + }, + "node_modules/@types/babel__traverse": { + "version": "7.28.0", + "resolved": "https://nexus.gc-si.dev/repository/npm-public/@types/babel__traverse/-/babel__traverse-7.28.0.tgz", + "integrity": "sha512-8PvcXf70gTDZBgt9ptxJ8elBeBjcLOAcOtoO/mPJjtji1+CdGbHgm77om1GrsPxsiE+uXIpNSK64UYaIwQXd4Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.28.2" + } + }, + "node_modules/@types/estree": { + "version": "1.0.8", + "resolved": "https://nexus.gc-si.dev/repository/npm-public/@types/estree/-/estree-1.0.8.tgz", + "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/json-schema": { + "version": "7.0.15", + "resolved": "https://nexus.gc-si.dev/repository/npm-public/@types/json-schema/-/json-schema-7.0.15.tgz", + "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/node": { + "version": "24.12.2", + "resolved": "https://nexus.gc-si.dev/repository/npm-public/@types/node/-/node-24.12.2.tgz", + "integrity": "sha512-A1sre26ke7HDIuY/M23nd9gfB+nrmhtYyMINbjI1zHJxYteKR6qSMX56FsmjMcDb3SMcjJg5BiRRgOCC/yBD0g==", + "dev": true, + "license": "MIT", + "dependencies": { + "undici-types": "~7.16.0" + } + }, + "node_modules/@types/react": { + "version": "19.2.14", + "resolved": "https://nexus.gc-si.dev/repository/npm-public/@types/react/-/react-19.2.14.tgz", + "integrity": "sha512-ilcTH/UniCkMdtexkoCN0bI7pMcJDvmQFPvuPvmEaYA/NSfFTAgdUSLAoVjaRJm7+6PvcM+q1zYOwS4wTYMF9w==", + "dev": true, + "license": "MIT", + "dependencies": { + "csstype": "^3.2.2" + } + }, + "node_modules/@types/react-dom": { + "version": "19.2.3", + "resolved": "https://nexus.gc-si.dev/repository/npm-public/@types/react-dom/-/react-dom-19.2.3.tgz", + "integrity": "sha512-jp2L/eY6fn+KgVVQAOqYItbF0VY/YApe5Mz2F0aykSO8gx31bYCZyvSeYxCHKvzHG5eZjc+zyaS5BrBWya2+kQ==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "@types/react": "^19.2.0" + } + }, + "node_modules/@typescript-eslint/eslint-plugin": { + "version": "8.58.0", + "resolved": "https://nexus.gc-si.dev/repository/npm-public/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.58.0.tgz", + "integrity": "sha512-RLkVSiNuUP1C2ROIWfqX+YcUfLaSnxGE/8M+Y57lopVwg9VTYYfhuz15Yf1IzCKgZj6/rIbYTmJCUSqr76r0Wg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/regexpp": "^4.12.2", + "@typescript-eslint/scope-manager": "8.58.0", + "@typescript-eslint/type-utils": "8.58.0", + "@typescript-eslint/utils": "8.58.0", + "@typescript-eslint/visitor-keys": "8.58.0", + "ignore": "^7.0.5", + "natural-compare": "^1.4.0", + "ts-api-utils": "^2.5.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "@typescript-eslint/parser": "^8.58.0", + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", + "typescript": ">=4.8.4 <6.1.0" + } + }, + "node_modules/@typescript-eslint/eslint-plugin/node_modules/ignore": { + "version": "7.0.5", + "resolved": "https://nexus.gc-si.dev/repository/npm-public/ignore/-/ignore-7.0.5.tgz", + "integrity": "sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/@typescript-eslint/parser": { + "version": "8.58.0", + "resolved": "https://nexus.gc-si.dev/repository/npm-public/@typescript-eslint/parser/-/parser-8.58.0.tgz", + "integrity": "sha512-rLoGZIf9afaRBYsPUMtvkDWykwXwUPL60HebR4JgTI8mxfFe2cQTu3AGitANp4b9B2QlVru6WzjgB2IzJKiCSA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/scope-manager": "8.58.0", + "@typescript-eslint/types": "8.58.0", + "@typescript-eslint/typescript-estree": "8.58.0", + "@typescript-eslint/visitor-keys": "8.58.0", + "debug": "^4.4.3" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", + "typescript": ">=4.8.4 <6.1.0" + } + }, + "node_modules/@typescript-eslint/project-service": { + "version": "8.58.0", + "resolved": "https://nexus.gc-si.dev/repository/npm-public/@typescript-eslint/project-service/-/project-service-8.58.0.tgz", + "integrity": "sha512-8Q/wBPWLQP1j16NxoPNIKpDZFMaxl7yWIoqXWYeWO+Bbd2mjgvoF0dxP2jKZg5+x49rgKdf7Ck473M8PC3V9lg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/tsconfig-utils": "^8.58.0", + "@typescript-eslint/types": "^8.58.0", + "debug": "^4.4.3" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <6.1.0" + } + }, + "node_modules/@typescript-eslint/scope-manager": { + "version": "8.58.0", + "resolved": "https://nexus.gc-si.dev/repository/npm-public/@typescript-eslint/scope-manager/-/scope-manager-8.58.0.tgz", + "integrity": "sha512-W1Lur1oF50FxSnNdGp3Vs6P+yBRSmZiw4IIjEeYxd8UQJwhUF0gDgDD/W/Tgmh73mxgEU3qX0Bzdl/NGuSPEpQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.58.0", + "@typescript-eslint/visitor-keys": "8.58.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/tsconfig-utils": { + "version": "8.58.0", + "resolved": "https://nexus.gc-si.dev/repository/npm-public/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.58.0.tgz", + "integrity": "sha512-doNSZEVJsWEu4htiVC+PR6NpM+pa+a4ClH9INRWOWCUzMst/VA9c4gXq92F8GUD1rwhNvRLkgjfYtFXegXQF7A==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <6.1.0" + } + }, + "node_modules/@typescript-eslint/type-utils": { + "version": "8.58.0", + "resolved": "https://nexus.gc-si.dev/repository/npm-public/@typescript-eslint/type-utils/-/type-utils-8.58.0.tgz", + "integrity": "sha512-aGsCQImkDIqMyx1u4PrVlbi/krmDsQUs4zAcCV6M7yPcPev+RqVlndsJy9kJ8TLihW9TZ0kbDAzctpLn5o+lOg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.58.0", + "@typescript-eslint/typescript-estree": "8.58.0", + "@typescript-eslint/utils": "8.58.0", + "debug": "^4.4.3", + "ts-api-utils": "^2.5.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", + "typescript": ">=4.8.4 <6.1.0" + } + }, + "node_modules/@typescript-eslint/types": { + "version": "8.58.0", + "resolved": "https://nexus.gc-si.dev/repository/npm-public/@typescript-eslint/types/-/types-8.58.0.tgz", + "integrity": "sha512-O9CjxypDT89fbHxRfETNoAnHj/i6IpRK0CvbVN3qibxlLdo5p5hcLmUuCCrHMpxiWSwKyI8mCP7qRNYuOJ0Uww==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/typescript-estree": { + "version": "8.58.0", + "resolved": "https://nexus.gc-si.dev/repository/npm-public/@typescript-eslint/typescript-estree/-/typescript-estree-8.58.0.tgz", + "integrity": "sha512-7vv5UWbHqew/dvs+D3e1RvLv1v2eeZ9txRHPnEEBUgSNLx5ghdzjHa0sgLWYVKssH+lYmV0JaWdoubo0ncGYLA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/project-service": "8.58.0", + "@typescript-eslint/tsconfig-utils": "8.58.0", + "@typescript-eslint/types": "8.58.0", + "@typescript-eslint/visitor-keys": "8.58.0", + "debug": "^4.4.3", + "minimatch": "^10.2.2", + "semver": "^7.7.3", + "tinyglobby": "^0.2.15", + "ts-api-utils": "^2.5.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <6.1.0" + } + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/balanced-match": { + "version": "4.0.4", + "resolved": "https://nexus.gc-si.dev/repository/npm-public/balanced-match/-/balanced-match-4.0.4.tgz", + "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "18 || 20 || >=22" + } + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/brace-expansion": { + "version": "5.0.5", + "resolved": "https://nexus.gc-si.dev/repository/npm-public/brace-expansion/-/brace-expansion-5.0.5.tgz", + "integrity": "sha512-VZznLgtwhn+Mact9tfiwx64fA9erHH/MCXEUfB/0bX/6Fz6ny5EGTXYltMocqg4xFAQZtnO3DHWWXi8RiuN7cQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^4.0.2" + }, + "engines": { + "node": "18 || 20 || >=22" + } + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/minimatch": { + "version": "10.2.5", + "resolved": "https://nexus.gc-si.dev/repository/npm-public/minimatch/-/minimatch-10.2.5.tgz", + "integrity": "sha512-MULkVLfKGYDFYejP07QOurDLLQpcjk7Fw+7jXS2R2czRQzR56yHRveU5NDJEOviH+hETZKSkIk5c+T23GjFUMg==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "brace-expansion": "^5.0.5" + }, + "engines": { + "node": "18 || 20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/semver": { + "version": "7.7.4", + "resolved": "https://nexus.gc-si.dev/repository/npm-public/semver/-/semver-7.7.4.tgz", + "integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@typescript-eslint/utils": { + "version": "8.58.0", + "resolved": "https://nexus.gc-si.dev/repository/npm-public/@typescript-eslint/utils/-/utils-8.58.0.tgz", + "integrity": "sha512-RfeSqcFeHMHlAWzt4TBjWOAtoW9lnsAGiP3GbaX9uVgTYYrMbVnGONEfUCiSss+xMHFl+eHZiipmA8WkQ7FuNA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/eslint-utils": "^4.9.1", + "@typescript-eslint/scope-manager": "8.58.0", + "@typescript-eslint/types": "8.58.0", + "@typescript-eslint/typescript-estree": "8.58.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", + "typescript": ">=4.8.4 <6.1.0" + } + }, + "node_modules/@typescript-eslint/visitor-keys": { + "version": "8.58.0", + "resolved": "https://nexus.gc-si.dev/repository/npm-public/@typescript-eslint/visitor-keys/-/visitor-keys-8.58.0.tgz", + "integrity": "sha512-XJ9UD9+bbDo4a4epraTwG3TsNPeiB9aShrUneAVXy8q4LuwowN+qu89/6ByLMINqvIMeI9H9hOHQtg/ijrYXzQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.58.0", + "eslint-visitor-keys": "^5.0.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/visitor-keys/node_modules/eslint-visitor-keys": { + "version": "5.0.1", + "resolved": "https://nexus.gc-si.dev/repository/npm-public/eslint-visitor-keys/-/eslint-visitor-keys-5.0.1.tgz", + "integrity": "sha512-tD40eHxA35h0PEIZNeIjkHoDR4YjjJp34biM0mDvplBe//mB+IHCqHDGV7pxF+7MklTvighcCPPZC7ynWyjdTA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@vitejs/plugin-react": { + "version": "5.2.0", + "resolved": "https://nexus.gc-si.dev/repository/npm-public/@vitejs/plugin-react/-/plugin-react-5.2.0.tgz", + "integrity": "sha512-YmKkfhOAi3wsB1PhJq5Scj3GXMn3WvtQ/JC0xoopuHoXSdmtdStOpFrYaT1kie2YgFBcIe64ROzMYRjCrYOdYw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/core": "^7.29.0", + "@babel/plugin-transform-react-jsx-self": "^7.27.1", + "@babel/plugin-transform-react-jsx-source": "^7.27.1", + "@rolldown/pluginutils": "1.0.0-rc.3", + "@types/babel__core": "^7.20.5", + "react-refresh": "^0.18.0" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + }, + "peerDependencies": { + "vite": "^4.2.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, + "node_modules/acorn": { + "version": "8.16.0", + "resolved": "https://nexus.gc-si.dev/repository/npm-public/acorn/-/acorn-8.16.0.tgz", + "integrity": "sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw==", + "dev": true, + "license": "MIT", + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-jsx": { + "version": "5.3.2", + "resolved": "https://nexus.gc-si.dev/repository/npm-public/acorn-jsx/-/acorn-jsx-5.3.2.tgz", + "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, + "node_modules/ajv": { + "version": "6.14.0", + "resolved": "https://nexus.gc-si.dev/repository/npm-public/ajv/-/ajv-6.14.0.tgz", + "integrity": "sha512-IWrosm/yrn43eiKqkfkHis7QioDleaXQHdDVPKg0FSwwd/DuvyX79TZnFOnYpB7dcsFAMmtFztZuXPDvSePkFw==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://nexus.gc-si.dev/repository/npm-public/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://nexus.gc-si.dev/repository/npm-public/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true, + "license": "Python-2.0" + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://nexus.gc-si.dev/repository/npm-public/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true, + "license": "MIT" + }, + "node_modules/baseline-browser-mapping": { + "version": "2.10.16", + "resolved": "https://nexus.gc-si.dev/repository/npm-public/baseline-browser-mapping/-/baseline-browser-mapping-2.10.16.tgz", + "integrity": "sha512-Lyf3aK28zpsD1yQMiiHD4RvVb6UdMoo8xzG2XzFIfR9luPzOpcBlAsT/qfB1XWS1bxWT+UtE4WmQgsp297FYOA==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "baseline-browser-mapping": "dist/cli.cjs" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/brace-expansion": { + "version": "1.1.13", + "resolved": "https://nexus.gc-si.dev/repository/npm-public/brace-expansion/-/brace-expansion-1.1.13.tgz", + "integrity": "sha512-9ZLprWS6EENmhEOpjCYW2c8VkmOvckIJZfkr7rBW6dObmfgJ/L1GpSYW5Hpo9lDz4D1+n0Ckz8rU7FwHDQiG/w==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/browserslist": { + "version": "4.28.2", + "resolved": "https://nexus.gc-si.dev/repository/npm-public/browserslist/-/browserslist-4.28.2.tgz", + "integrity": "sha512-48xSriZYYg+8qXna9kwqjIVzuQxi+KYWp2+5nCYnYKPTr0LvD89Jqk2Or5ogxz0NUMfIjhh2lIUX/LyX9B4oIg==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "baseline-browser-mapping": "^2.10.12", + "caniuse-lite": "^1.0.30001782", + "electron-to-chromium": "^1.5.328", + "node-releases": "^2.0.36", + "update-browserslist-db": "^1.2.3" + }, + "bin": { + "browserslist": "cli.js" + }, + "engines": { + "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" + } + }, + "node_modules/callsites": { + "version": "3.1.0", + "resolved": "https://nexus.gc-si.dev/repository/npm-public/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/caniuse-lite": { + "version": "1.0.30001786", + "resolved": "https://nexus.gc-si.dev/repository/npm-public/caniuse-lite/-/caniuse-lite-1.0.30001786.tgz", + "integrity": "sha512-4oxTZEvqmLLrERwxO76yfKM7acZo310U+v4kqexI2TL1DkkUEMT8UijrxxcnVdxR3qkVf5awGRX+4Z6aPHVKrA==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/caniuse-lite" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "CC-BY-4.0" + }, + "node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://nexus.gc-si.dev/repository/npm-public/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://nexus.gc-si.dev/repository/npm-public/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://nexus.gc-si.dev/repository/npm-public/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true, + "license": "MIT" + }, + "node_modules/concat-map": { + "version": "0.0.1", + "resolved": "https://nexus.gc-si.dev/repository/npm-public/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", + "dev": true, + "license": "MIT" + }, + "node_modules/convert-source-map": { + "version": "2.0.0", + "resolved": "https://nexus.gc-si.dev/repository/npm-public/convert-source-map/-/convert-source-map-2.0.0.tgz", + "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", + "dev": true, + "license": "MIT" + }, + "node_modules/cookie": { + "version": "1.1.1", + "resolved": "https://nexus.gc-si.dev/repository/npm-public/cookie/-/cookie-1.1.1.tgz", + "integrity": "sha512-ei8Aos7ja0weRpFzJnEA9UHJ/7XQmqglbRwnf2ATjcB9Wq874VKH9kfjjirM6UhU2/E5fFYadylyhFldcqSidQ==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/cross-spawn": { + "version": "7.0.6", + "resolved": "https://nexus.gc-si.dev/repository/npm-public/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", + "dev": true, + "license": "MIT", + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/csstype": { + "version": "3.2.3", + "resolved": "https://nexus.gc-si.dev/repository/npm-public/csstype/-/csstype-3.2.3.tgz", + "integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/debug": { + "version": "4.4.3", + "resolved": "https://nexus.gc-si.dev/repository/npm-public/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/deep-is": { + "version": "0.1.4", + "resolved": "https://nexus.gc-si.dev/repository/npm-public/deep-is/-/deep-is-0.1.4.tgz", + "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/detect-libc": { + "version": "2.1.2", + "resolved": "https://nexus.gc-si.dev/repository/npm-public/detect-libc/-/detect-libc-2.1.2.tgz", + "integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=8" + } + }, + "node_modules/electron-to-chromium": { + "version": "1.5.331", + "resolved": "https://nexus.gc-si.dev/repository/npm-public/electron-to-chromium/-/electron-to-chromium-1.5.331.tgz", + "integrity": "sha512-IbxXrsTlD3hRodkLnbxAPP4OuJYdWCeM3IOdT+CpcMoIwIoDfCmRpEtSPfwBXxVkg9xmBeY7Lz2Eo2TDn/HC3Q==", + "dev": true, + "license": "ISC" + }, + "node_modules/enhanced-resolve": { + "version": "5.20.1", + "resolved": "https://nexus.gc-si.dev/repository/npm-public/enhanced-resolve/-/enhanced-resolve-5.20.1.tgz", + "integrity": "sha512-Qohcme7V1inbAfvjItgw0EaxVX5q2rdVEZHRBrEQdRZTssLDGsL8Lwrznl8oQ/6kuTJONLaDcGjkNP247XEhcA==", + "dev": true, + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.2.4", + "tapable": "^2.3.0" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/esbuild": { + "version": "0.27.7", + "resolved": "https://nexus.gc-si.dev/repository/npm-public/esbuild/-/esbuild-0.27.7.tgz", + "integrity": "sha512-IxpibTjyVnmrIQo5aqNpCgoACA/dTKLTlhMHihVHhdkxKyPO1uBBthumT0rdHmcsk9uMonIWS0m4FljWzILh3w==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.27.7", + "@esbuild/android-arm": "0.27.7", + "@esbuild/android-arm64": "0.27.7", + "@esbuild/android-x64": "0.27.7", + "@esbuild/darwin-arm64": "0.27.7", + "@esbuild/darwin-x64": "0.27.7", + "@esbuild/freebsd-arm64": "0.27.7", + "@esbuild/freebsd-x64": "0.27.7", + "@esbuild/linux-arm": "0.27.7", + "@esbuild/linux-arm64": "0.27.7", + "@esbuild/linux-ia32": "0.27.7", + "@esbuild/linux-loong64": "0.27.7", + "@esbuild/linux-mips64el": "0.27.7", + "@esbuild/linux-ppc64": "0.27.7", + "@esbuild/linux-riscv64": "0.27.7", + "@esbuild/linux-s390x": "0.27.7", + "@esbuild/linux-x64": "0.27.7", + "@esbuild/netbsd-arm64": "0.27.7", + "@esbuild/netbsd-x64": "0.27.7", + "@esbuild/openbsd-arm64": "0.27.7", + "@esbuild/openbsd-x64": "0.27.7", + "@esbuild/openharmony-arm64": "0.27.7", + "@esbuild/sunos-x64": "0.27.7", + "@esbuild/win32-arm64": "0.27.7", + "@esbuild/win32-ia32": "0.27.7", + "@esbuild/win32-x64": "0.27.7" + } + }, + "node_modules/escalade": { + "version": "3.2.0", + "resolved": "https://nexus.gc-si.dev/repository/npm-public/escalade/-/escalade-3.2.0.tgz", + "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://nexus.gc-si.dev/repository/npm-public/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint": { + "version": "9.39.4", + "resolved": "https://nexus.gc-si.dev/repository/npm-public/eslint/-/eslint-9.39.4.tgz", + "integrity": "sha512-XoMjdBOwe/esVgEvLmNsD3IRHkm7fbKIUGvrleloJXUZgDHig2IPWNniv+GwjyJXzuNqVjlr5+4yVUZjycJwfQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/eslint-utils": "^4.8.0", + "@eslint-community/regexpp": "^4.12.1", + "@eslint/config-array": "^0.21.2", + "@eslint/config-helpers": "^0.4.2", + "@eslint/core": "^0.17.0", + "@eslint/eslintrc": "^3.3.5", + "@eslint/js": "9.39.4", + "@eslint/plugin-kit": "^0.4.1", + "@humanfs/node": "^0.16.6", + "@humanwhocodes/module-importer": "^1.0.1", + "@humanwhocodes/retry": "^0.4.2", + "@types/estree": "^1.0.6", + "ajv": "^6.14.0", + "chalk": "^4.0.0", + "cross-spawn": "^7.0.6", + "debug": "^4.3.2", + "escape-string-regexp": "^4.0.0", + "eslint-scope": "^8.4.0", + "eslint-visitor-keys": "^4.2.1", + "espree": "^10.4.0", + "esquery": "^1.5.0", + "esutils": "^2.0.2", + "fast-deep-equal": "^3.1.3", + "file-entry-cache": "^8.0.0", + "find-up": "^5.0.0", + "glob-parent": "^6.0.2", + "ignore": "^5.2.0", + "imurmurhash": "^0.1.4", + "is-glob": "^4.0.0", + "json-stable-stringify-without-jsonify": "^1.0.1", + "lodash.merge": "^4.6.2", + "minimatch": "^3.1.5", + "natural-compare": "^1.4.0", + "optionator": "^0.9.3" + }, + "bin": { + "eslint": "bin/eslint.js" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://eslint.org/donate" + }, + "peerDependencies": { + "jiti": "*" + }, + "peerDependenciesMeta": { + "jiti": { + "optional": true + } + } + }, + "node_modules/eslint-plugin-react-hooks": { + "version": "7.0.1", + "resolved": "https://nexus.gc-si.dev/repository/npm-public/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-7.0.1.tgz", + "integrity": "sha512-O0d0m04evaNzEPoSW+59Mezf8Qt0InfgGIBJnpC0h3NH/WjUAR7BIKUfysC6todmtiZ/A0oUVS8Gce0WhBrHsA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/core": "^7.24.4", + "@babel/parser": "^7.24.4", + "hermes-parser": "^0.25.1", + "zod": "^3.25.0 || ^4.0.0", + "zod-validation-error": "^3.5.0 || ^4.0.0" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "eslint": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0 || ^9.0.0" + } + }, + "node_modules/eslint-plugin-react-refresh": { + "version": "0.4.26", + "resolved": "https://nexus.gc-si.dev/repository/npm-public/eslint-plugin-react-refresh/-/eslint-plugin-react-refresh-0.4.26.tgz", + "integrity": "sha512-1RETEylht2O6FM/MvgnyvT+8K21wLqDNg4qD51Zj3guhjt433XbnnkVttHMyaVyAFD03QSV4LPS5iE3VQmO7XQ==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "eslint": ">=8.40" + } + }, + "node_modules/eslint-scope": { + "version": "8.4.0", + "resolved": "https://nexus.gc-si.dev/repository/npm-public/eslint-scope/-/eslint-scope-8.4.0.tgz", + "integrity": "sha512-sNXOfKCn74rt8RICKMvJS7XKV/Xk9kA7DyJr8mJik3S7Cwgy3qlkkmyS2uQB3jiJg6VNdZd/pDBJu0nvG2NlTg==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^5.2.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint-visitor-keys": { + "version": "4.2.1", + "resolved": "https://nexus.gc-si.dev/repository/npm-public/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz", + "integrity": "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/espree": { + "version": "10.4.0", + "resolved": "https://nexus.gc-si.dev/repository/npm-public/espree/-/espree-10.4.0.tgz", + "integrity": "sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "acorn": "^8.15.0", + "acorn-jsx": "^5.3.2", + "eslint-visitor-keys": "^4.2.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/esquery": { + "version": "1.7.0", + "resolved": "https://nexus.gc-si.dev/repository/npm-public/esquery/-/esquery-1.7.0.tgz", + "integrity": "sha512-Ap6G0WQwcU/LHsvLwON1fAQX9Zp0A2Y6Y/cJBl9r/JbW90Zyg4/zbG6zzKa2OTALELarYHmKu0GhpM5EO+7T0g==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "estraverse": "^5.1.0" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/esrecurse": { + "version": "4.3.0", + "resolved": "https://nexus.gc-si.dev/repository/npm-public/esrecurse/-/esrecurse-4.3.0.tgz", + "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "estraverse": "^5.2.0" + }, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://nexus.gc-si.dev/repository/npm-public/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=4.0" + } + }, + "node_modules/esutils": { + "version": "2.0.3", + "resolved": "https://nexus.gc-si.dev/repository/npm-public/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://nexus.gc-si.dev/repository/npm-public/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://nexus.gc-si.dev/repository/npm-public/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", + "dev": true, + "license": "MIT" + }, + "node_modules/fast-levenshtein": { + "version": "2.0.6", + "resolved": "https://nexus.gc-si.dev/repository/npm-public/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", + "dev": true, + "license": "MIT" + }, + "node_modules/fdir": { + "version": "6.5.0", + "resolved": "https://nexus.gc-si.dev/repository/npm-public/fdir/-/fdir-6.5.0.tgz", + "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12.0.0" + }, + "peerDependencies": { + "picomatch": "^3 || ^4" + }, + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } + } + }, + "node_modules/file-entry-cache": { + "version": "8.0.0", + "resolved": "https://nexus.gc-si.dev/repository/npm-public/file-entry-cache/-/file-entry-cache-8.0.0.tgz", + "integrity": "sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "flat-cache": "^4.0.0" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/find-up": { + "version": "5.0.0", + "resolved": "https://nexus.gc-si.dev/repository/npm-public/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "dev": true, + "license": "MIT", + "dependencies": { + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/flat-cache": { + "version": "4.0.1", + "resolved": "https://nexus.gc-si.dev/repository/npm-public/flat-cache/-/flat-cache-4.0.1.tgz", + "integrity": "sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==", + "dev": true, + "license": "MIT", + "dependencies": { + "flatted": "^3.2.9", + "keyv": "^4.5.4" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/flatted": { + "version": "3.4.2", + "resolved": "https://nexus.gc-si.dev/repository/npm-public/flatted/-/flatted-3.4.2.tgz", + "integrity": "sha512-PjDse7RzhcPkIJwy5t7KPWQSZ9cAbzQXcafsetQoD7sOJRQlGikNbx7yZp2OotDnJyrDcbyRq3Ttb18iYOqkxA==", + "dev": true, + "license": "ISC" + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://nexus.gc-si.dev/repository/npm-public/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/gensync": { + "version": "1.0.0-beta.2", + "resolved": "https://nexus.gc-si.dev/repository/npm-public/gensync/-/gensync-1.0.0-beta.2.tgz", + "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/glob-parent": { + "version": "6.0.2", + "resolved": "https://nexus.gc-si.dev/repository/npm-public/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", + "dev": true, + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.3" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/globals": { + "version": "16.5.0", + "resolved": "https://nexus.gc-si.dev/repository/npm-public/globals/-/globals-16.5.0.tgz", + "integrity": "sha512-c/c15i26VrJ4IRt5Z89DnIzCGDn9EcebibhAOjw5ibqEHsE1wLUgkPn9RDmNcUKyU87GeaL633nyJ+pplFR2ZQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/graceful-fs": { + "version": "4.2.11", + "resolved": "https://nexus.gc-si.dev/repository/npm-public/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://nexus.gc-si.dev/repository/npm-public/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/hermes-estree": { + "version": "0.25.1", + "resolved": "https://nexus.gc-si.dev/repository/npm-public/hermes-estree/-/hermes-estree-0.25.1.tgz", + "integrity": "sha512-0wUoCcLp+5Ev5pDW2OriHC2MJCbwLwuRx+gAqMTOkGKJJiBCLjtrvy4PWUGn6MIVefecRpzoOZ/UV6iGdOr+Cw==", + "dev": true, + "license": "MIT" + }, + "node_modules/hermes-parser": { + "version": "0.25.1", + "resolved": "https://nexus.gc-si.dev/repository/npm-public/hermes-parser/-/hermes-parser-0.25.1.tgz", + "integrity": "sha512-6pEjquH3rqaI6cYAXYPcz9MS4rY6R4ngRgrgfDshRptUZIc3lw0MCIJIGDj9++mfySOuPTHB4nrSW99BCvOPIA==", + "dev": true, + "license": "MIT", + "dependencies": { + "hermes-estree": "0.25.1" + } + }, + "node_modules/ignore": { + "version": "5.3.2", + "resolved": "https://nexus.gc-si.dev/repository/npm-public/ignore/-/ignore-5.3.2.tgz", + "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/import-fresh": { + "version": "3.3.1", + "resolved": "https://nexus.gc-si.dev/repository/npm-public/import-fresh/-/import-fresh-3.3.1.tgz", + "integrity": "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/imurmurhash": { + "version": "0.1.4", + "resolved": "https://nexus.gc-si.dev/repository/npm-public/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.8.19" + } + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://nexus.gc-si.dev/repository/npm-public/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://nexus.gc-si.dev/repository/npm-public/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://nexus.gc-si.dev/repository/npm-public/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "dev": true, + "license": "ISC" + }, + "node_modules/jiti": { + "version": "2.6.1", + "resolved": "https://nexus.gc-si.dev/repository/npm-public/jiti/-/jiti-2.6.1.tgz", + "integrity": "sha512-ekilCSN1jwRvIbgeg/57YFh8qQDNbwDb9xT/qu2DAHbFFZUicIl4ygVaAvzveMhMVr3LnpSKTNnwt8PoOfmKhQ==", + "dev": true, + "license": "MIT", + "bin": { + "jiti": "lib/jiti-cli.mjs" + } + }, + "node_modules/js-tokens": { + "version": "4.0.0", + "resolved": "https://nexus.gc-si.dev/repository/npm-public/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/js-yaml": { + "version": "4.1.1", + "resolved": "https://nexus.gc-si.dev/repository/npm-public/js-yaml/-/js-yaml-4.1.1.tgz", + "integrity": "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==", + "dev": true, + "license": "MIT", + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/jsesc": { + "version": "3.1.0", + "resolved": "https://nexus.gc-si.dev/repository/npm-public/jsesc/-/jsesc-3.1.0.tgz", + "integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==", + "dev": true, + "license": "MIT", + "bin": { + "jsesc": "bin/jsesc" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/json-buffer": { + "version": "3.0.1", + "resolved": "https://nexus.gc-si.dev/repository/npm-public/json-buffer/-/json-buffer-3.0.1.tgz", + "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://nexus.gc-si.dev/repository/npm-public/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true, + "license": "MIT" + }, + "node_modules/json-stable-stringify-without-jsonify": { + "version": "1.0.1", + "resolved": "https://nexus.gc-si.dev/repository/npm-public/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", + "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", + "dev": true, + "license": "MIT" + }, + "node_modules/json5": { + "version": "2.2.3", + "resolved": "https://nexus.gc-si.dev/repository/npm-public/json5/-/json5-2.2.3.tgz", + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", + "dev": true, + "license": "MIT", + "bin": { + "json5": "lib/cli.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/keyv": { + "version": "4.5.4", + "resolved": "https://nexus.gc-si.dev/repository/npm-public/keyv/-/keyv-4.5.4.tgz", + "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", + "dev": true, + "license": "MIT", + "dependencies": { + "json-buffer": "3.0.1" + } + }, + "node_modules/levn": { + "version": "0.4.1", + "resolved": "https://nexus.gc-si.dev/repository/npm-public/levn/-/levn-0.4.1.tgz", + "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "prelude-ls": "^1.2.1", + "type-check": "~0.4.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/lightningcss": { + "version": "1.32.0", + "resolved": "https://nexus.gc-si.dev/repository/npm-public/lightningcss/-/lightningcss-1.32.0.tgz", + "integrity": "sha512-NXYBzinNrblfraPGyrbPoD19C1h9lfI/1mzgWYvXUTe414Gz/X1FD2XBZSZM7rRTrMA8JL3OtAaGifrIKhQ5yQ==", + "dev": true, + "license": "MPL-2.0", + "dependencies": { + "detect-libc": "^2.0.3" + }, + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + }, + "optionalDependencies": { + "lightningcss-android-arm64": "1.32.0", + "lightningcss-darwin-arm64": "1.32.0", + "lightningcss-darwin-x64": "1.32.0", + "lightningcss-freebsd-x64": "1.32.0", + "lightningcss-linux-arm-gnueabihf": "1.32.0", + "lightningcss-linux-arm64-gnu": "1.32.0", + "lightningcss-linux-arm64-musl": "1.32.0", + "lightningcss-linux-x64-gnu": "1.32.0", + "lightningcss-linux-x64-musl": "1.32.0", + "lightningcss-win32-arm64-msvc": "1.32.0", + "lightningcss-win32-x64-msvc": "1.32.0" + } + }, + "node_modules/lightningcss-android-arm64": { + "version": "1.32.0", + "resolved": "https://nexus.gc-si.dev/repository/npm-public/lightningcss-android-arm64/-/lightningcss-android-arm64-1.32.0.tgz", + "integrity": "sha512-YK7/ClTt4kAK0vo6w3X+Pnm0D2cf2vPHbhOXdoNti1Ga0al1P4TBZhwjATvjNwLEBCnKvjJc2jQgHXH0NEwlAg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-darwin-arm64": { + "version": "1.32.0", + "resolved": "https://nexus.gc-si.dev/repository/npm-public/lightningcss-darwin-arm64/-/lightningcss-darwin-arm64-1.32.0.tgz", + "integrity": "sha512-RzeG9Ju5bag2Bv1/lwlVJvBE3q6TtXskdZLLCyfg5pt+HLz9BqlICO7LZM7VHNTTn/5PRhHFBSjk5lc4cmscPQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-darwin-x64": { + "version": "1.32.0", + "resolved": "https://nexus.gc-si.dev/repository/npm-public/lightningcss-darwin-x64/-/lightningcss-darwin-x64-1.32.0.tgz", + "integrity": "sha512-U+QsBp2m/s2wqpUYT/6wnlagdZbtZdndSmut/NJqlCcMLTWp5muCrID+K5UJ6jqD2BFshejCYXniPDbNh73V8w==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-freebsd-x64": { + "version": "1.32.0", + "resolved": "https://nexus.gc-si.dev/repository/npm-public/lightningcss-freebsd-x64/-/lightningcss-freebsd-x64-1.32.0.tgz", + "integrity": "sha512-JCTigedEksZk3tHTTthnMdVfGf61Fky8Ji2E4YjUTEQX14xiy/lTzXnu1vwiZe3bYe0q+SpsSH/CTeDXK6WHig==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-arm-gnueabihf": { + "version": "1.32.0", + "resolved": "https://nexus.gc-si.dev/repository/npm-public/lightningcss-linux-arm-gnueabihf/-/lightningcss-linux-arm-gnueabihf-1.32.0.tgz", + "integrity": "sha512-x6rnnpRa2GL0zQOkt6rts3YDPzduLpWvwAF6EMhXFVZXD4tPrBkEFqzGowzCsIWsPjqSK+tyNEODUBXeeVHSkw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-arm64-gnu": { + "version": "1.32.0", + "resolved": "https://nexus.gc-si.dev/repository/npm-public/lightningcss-linux-arm64-gnu/-/lightningcss-linux-arm64-gnu-1.32.0.tgz", + "integrity": "sha512-0nnMyoyOLRJXfbMOilaSRcLH3Jw5z9HDNGfT/gwCPgaDjnx0i8w7vBzFLFR1f6CMLKF8gVbebmkUN3fa/kQJpQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-arm64-musl": { + "version": "1.32.0", + "resolved": "https://nexus.gc-si.dev/repository/npm-public/lightningcss-linux-arm64-musl/-/lightningcss-linux-arm64-musl-1.32.0.tgz", + "integrity": "sha512-UpQkoenr4UJEzgVIYpI80lDFvRmPVg6oqboNHfoH4CQIfNA+HOrZ7Mo7KZP02dC6LjghPQJeBsvXhJod/wnIBg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-x64-gnu": { + "version": "1.32.0", + "resolved": "https://nexus.gc-si.dev/repository/npm-public/lightningcss-linux-x64-gnu/-/lightningcss-linux-x64-gnu-1.32.0.tgz", + "integrity": "sha512-V7Qr52IhZmdKPVr+Vtw8o+WLsQJYCTd8loIfpDaMRWGUZfBOYEJeyJIkqGIDMZPwPx24pUMfwSxxI8phr/MbOA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-x64-musl": { + "version": "1.32.0", + "resolved": "https://nexus.gc-si.dev/repository/npm-public/lightningcss-linux-x64-musl/-/lightningcss-linux-x64-musl-1.32.0.tgz", + "integrity": "sha512-bYcLp+Vb0awsiXg/80uCRezCYHNg1/l3mt0gzHnWV9XP1W5sKa5/TCdGWaR/zBM2PeF/HbsQv/j2URNOiVuxWg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-win32-arm64-msvc": { + "version": "1.32.0", + "resolved": "https://nexus.gc-si.dev/repository/npm-public/lightningcss-win32-arm64-msvc/-/lightningcss-win32-arm64-msvc-1.32.0.tgz", + "integrity": "sha512-8SbC8BR40pS6baCM8sbtYDSwEVQd4JlFTOlaD3gWGHfThTcABnNDBda6eTZeqbofalIJhFx0qKzgHJmcPTnGdw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-win32-x64-msvc": { + "version": "1.32.0", + "resolved": "https://nexus.gc-si.dev/repository/npm-public/lightningcss-win32-x64-msvc/-/lightningcss-win32-x64-msvc-1.32.0.tgz", + "integrity": "sha512-Amq9B/SoZYdDi1kFrojnoqPLxYhQ4Wo5XiL8EVJrVsB8ARoC1PWW6VGtT0WKCemjy8aC+louJnjS7U18x3b06Q==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/locate-path": { + "version": "6.0.0", + "resolved": "https://nexus.gc-si.dev/repository/npm-public/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-locate": "^5.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/lodash.merge": { + "version": "4.6.2", + "resolved": "https://nexus.gc-si.dev/repository/npm-public/lodash.merge/-/lodash.merge-4.6.2.tgz", + "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/lru-cache": { + "version": "5.1.1", + "resolved": "https://nexus.gc-si.dev/repository/npm-public/lru-cache/-/lru-cache-5.1.1.tgz", + "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", + "dev": true, + "license": "ISC", + "dependencies": { + "yallist": "^3.0.2" + } + }, + "node_modules/magic-string": { + "version": "0.30.21", + "resolved": "https://nexus.gc-si.dev/repository/npm-public/magic-string/-/magic-string-0.30.21.tgz", + "integrity": "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.5" + } + }, + "node_modules/minimatch": { + "version": "3.1.5", + "resolved": "https://nexus.gc-si.dev/repository/npm-public/minimatch/-/minimatch-3.1.5.tgz", + "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://nexus.gc-si.dev/repository/npm-public/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true, + "license": "MIT" + }, + "node_modules/nanoid": { + "version": "3.3.11", + "resolved": "https://nexus.gc-si.dev/repository/npm-public/nanoid/-/nanoid-3.3.11.tgz", + "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/natural-compare": { + "version": "1.4.0", + "resolved": "https://nexus.gc-si.dev/repository/npm-public/natural-compare/-/natural-compare-1.4.0.tgz", + "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", + "dev": true, + "license": "MIT" + }, + "node_modules/node-releases": { + "version": "2.0.37", + "resolved": "https://nexus.gc-si.dev/repository/npm-public/node-releases/-/node-releases-2.0.37.tgz", + "integrity": "sha512-1h5gKZCF+pO/o3Iqt5Jp7wc9rH3eJJ0+nh/CIoiRwjRxde/hAHyLPXYN4V3CqKAbiZPSeJFSWHmJsbkicta0Eg==", + "dev": true, + "license": "MIT" + }, + "node_modules/optionator": { + "version": "0.9.4", + "resolved": "https://nexus.gc-si.dev/repository/npm-public/optionator/-/optionator-0.9.4.tgz", + "integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==", + "dev": true, + "license": "MIT", + "dependencies": { + "deep-is": "^0.1.3", + "fast-levenshtein": "^2.0.6", + "levn": "^0.4.1", + "prelude-ls": "^1.2.1", + "type-check": "^0.4.0", + "word-wrap": "^1.2.5" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/p-limit": { + "version": "3.1.0", + "resolved": "https://nexus.gc-si.dev/repository/npm-public/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "yocto-queue": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-locate": { + "version": "5.0.0", + "resolved": "https://nexus.gc-si.dev/repository/npm-public/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-limit": "^3.0.2" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/parent-module": { + "version": "1.0.1", + "resolved": "https://nexus.gc-si.dev/repository/npm-public/parent-module/-/parent-module-1.0.1.tgz", + "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", + "dev": true, + "license": "MIT", + "dependencies": { + "callsites": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/path-exists": { + "version": "4.0.0", + "resolved": "https://nexus.gc-si.dev/repository/npm-public/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://nexus.gc-si.dev/repository/npm-public/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://nexus.gc-si.dev/repository/npm-public/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "dev": true, + "license": "ISC" + }, + "node_modules/picomatch": { + "version": "4.0.4", + "resolved": "https://nexus.gc-si.dev/repository/npm-public/picomatch/-/picomatch-4.0.4.tgz", + "integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/postcss": { + "version": "8.5.8", + "resolved": "https://nexus.gc-si.dev/repository/npm-public/postcss/-/postcss-8.5.8.tgz", + "integrity": "sha512-OW/rX8O/jXnm82Ey1k44pObPtdblfiuWnrd8X7GJ7emImCOstunGbXUpp7HdBrFQX6rJzn3sPT397Wp5aCwCHg==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "nanoid": "^3.3.11", + "picocolors": "^1.1.1", + "source-map-js": "^1.2.1" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/prelude-ls": { + "version": "1.2.1", + "resolved": "https://nexus.gc-si.dev/repository/npm-public/prelude-ls/-/prelude-ls-1.2.1.tgz", + "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/prettier": { + "version": "3.8.1", + "resolved": "https://nexus.gc-si.dev/repository/npm-public/prettier/-/prettier-3.8.1.tgz", + "integrity": "sha512-UOnG6LftzbdaHZcKoPFtOcCKztrQ57WkHDeRD9t/PTQtmT0NHSeWWepj6pS0z/N7+08BHFDQVUrfmfMRcZwbMg==", + "dev": true, + "license": "MIT", + "bin": { + "prettier": "bin/prettier.cjs" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/prettier/prettier?sponsor=1" + } + }, + "node_modules/punycode": { + "version": "2.3.1", + "resolved": "https://nexus.gc-si.dev/repository/npm-public/punycode/-/punycode-2.3.1.tgz", + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/react": { + "version": "19.2.4", + "resolved": "https://nexus.gc-si.dev/repository/npm-public/react/-/react-19.2.4.tgz", + "integrity": "sha512-9nfp2hYpCwOjAN+8TZFGhtWEwgvWHXqESH8qT89AT/lWklpLON22Lc8pEtnpsZz7VmawabSU0gCjnj8aC0euHQ==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/react-dom": { + "version": "19.2.4", + "resolved": "https://nexus.gc-si.dev/repository/npm-public/react-dom/-/react-dom-19.2.4.tgz", + "integrity": "sha512-AXJdLo8kgMbimY95O2aKQqsz2iWi9jMgKJhRBAxECE4IFxfcazB2LmzloIoibJI3C12IlY20+KFaLv+71bUJeQ==", + "license": "MIT", + "dependencies": { + "scheduler": "^0.27.0" + }, + "peerDependencies": { + "react": "^19.2.4" + } + }, + "node_modules/react-refresh": { + "version": "0.18.0", + "resolved": "https://nexus.gc-si.dev/repository/npm-public/react-refresh/-/react-refresh-0.18.0.tgz", + "integrity": "sha512-QgT5//D3jfjJb6Gsjxv0Slpj23ip+HtOpnNgnb2S5zU3CB26G/IDPGoy4RJB42wzFE46DRsstbW6tKHoKbhAxw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/react-router": { + "version": "7.14.0", + "resolved": "https://nexus.gc-si.dev/repository/npm-public/react-router/-/react-router-7.14.0.tgz", + "integrity": "sha512-m/xR9N4LQLmAS0ZhkY2nkPA1N7gQ5TUVa5n8TgANuDTARbn1gt+zLPXEm7W0XDTbrQ2AJSJKhoa6yx1D8BcpxQ==", + "license": "MIT", + "dependencies": { + "cookie": "^1.0.1", + "set-cookie-parser": "^2.6.0" + }, + "engines": { + "node": ">=20.0.0" + }, + "peerDependencies": { + "react": ">=18", + "react-dom": ">=18" + }, + "peerDependenciesMeta": { + "react-dom": { + "optional": true + } + } + }, + "node_modules/react-router-dom": { + "version": "7.14.0", + "resolved": "https://nexus.gc-si.dev/repository/npm-public/react-router-dom/-/react-router-dom-7.14.0.tgz", + "integrity": "sha512-2G3ajSVSZMEtmTjIklRWlNvo8wICEpLihfD/0YMDxbWK2UyP5EGfnoIn9AIQGnF3G/FX0MRbHXdFcD+rL1ZreQ==", + "license": "MIT", + "dependencies": { + "react-router": "7.14.0" + }, + "engines": { + "node": ">=20.0.0" + }, + "peerDependencies": { + "react": ">=18", + "react-dom": ">=18" + } + }, + "node_modules/resolve-from": { + "version": "4.0.0", + "resolved": "https://nexus.gc-si.dev/repository/npm-public/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/rollup": { + "version": "4.60.1", + "resolved": "https://nexus.gc-si.dev/repository/npm-public/rollup/-/rollup-4.60.1.tgz", + "integrity": "sha512-VmtB2rFU/GroZ4oL8+ZqXgSA38O6GR8KSIvWmEFv63pQ0G6KaBH9s07PO8XTXP4vI+3UJUEypOfjkGfmSBBR0w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "1.0.8" + }, + "bin": { + "rollup": "dist/bin/rollup" + }, + "engines": { + "node": ">=18.0.0", + "npm": ">=8.0.0" + }, + "optionalDependencies": { + "@rollup/rollup-android-arm-eabi": "4.60.1", + "@rollup/rollup-android-arm64": "4.60.1", + "@rollup/rollup-darwin-arm64": "4.60.1", + "@rollup/rollup-darwin-x64": "4.60.1", + "@rollup/rollup-freebsd-arm64": "4.60.1", + "@rollup/rollup-freebsd-x64": "4.60.1", + "@rollup/rollup-linux-arm-gnueabihf": "4.60.1", + "@rollup/rollup-linux-arm-musleabihf": "4.60.1", + "@rollup/rollup-linux-arm64-gnu": "4.60.1", + "@rollup/rollup-linux-arm64-musl": "4.60.1", + "@rollup/rollup-linux-loong64-gnu": "4.60.1", + "@rollup/rollup-linux-loong64-musl": "4.60.1", + "@rollup/rollup-linux-ppc64-gnu": "4.60.1", + "@rollup/rollup-linux-ppc64-musl": "4.60.1", + "@rollup/rollup-linux-riscv64-gnu": "4.60.1", + "@rollup/rollup-linux-riscv64-musl": "4.60.1", + "@rollup/rollup-linux-s390x-gnu": "4.60.1", + "@rollup/rollup-linux-x64-gnu": "4.60.1", + "@rollup/rollup-linux-x64-musl": "4.60.1", + "@rollup/rollup-openbsd-x64": "4.60.1", + "@rollup/rollup-openharmony-arm64": "4.60.1", + "@rollup/rollup-win32-arm64-msvc": "4.60.1", + "@rollup/rollup-win32-ia32-msvc": "4.60.1", + "@rollup/rollup-win32-x64-gnu": "4.60.1", + "@rollup/rollup-win32-x64-msvc": "4.60.1", + "fsevents": "~2.3.2" + } + }, + "node_modules/scheduler": { + "version": "0.27.0", + "resolved": "https://nexus.gc-si.dev/repository/npm-public/scheduler/-/scheduler-0.27.0.tgz", + "integrity": "sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q==", + "license": "MIT" + }, + "node_modules/semver": { + "version": "6.3.1", + "resolved": "https://nexus.gc-si.dev/repository/npm-public/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/set-cookie-parser": { + "version": "2.7.2", + "resolved": "https://nexus.gc-si.dev/repository/npm-public/set-cookie-parser/-/set-cookie-parser-2.7.2.tgz", + "integrity": "sha512-oeM1lpU/UvhTxw+g3cIfxXHyJRc/uidd3yK1P242gzHds0udQBYzs3y8j4gCCW+ZJ7ad0yctld8RYO+bdurlvw==", + "license": "MIT" + }, + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://nexus.gc-si.dev/repository/npm-public/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "license": "MIT", + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://nexus.gc-si.dev/repository/npm-public/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/source-map-js": { + "version": "1.2.1", + "resolved": "https://nexus.gc-si.dev/repository/npm-public/source-map-js/-/source-map-js-1.2.1.tgz", + "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/strip-json-comments": { + "version": "3.1.1", + "resolved": "https://nexus.gc-si.dev/repository/npm-public/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://nexus.gc-si.dev/repository/npm-public/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/tailwindcss": { + "version": "4.2.2", + "resolved": "https://nexus.gc-si.dev/repository/npm-public/tailwindcss/-/tailwindcss-4.2.2.tgz", + "integrity": "sha512-KWBIxs1Xb6NoLdMVqhbhgwZf2PGBpPEiwOqgI4pFIYbNTfBXiKYyWoTsXgBQ9WFg/OlhnvHaY+AEpW7wSmFo2Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/tapable": { + "version": "2.3.2", + "resolved": "https://nexus.gc-si.dev/repository/npm-public/tapable/-/tapable-2.3.2.tgz", + "integrity": "sha512-1MOpMXuhGzGL5TTCZFItxCc0AARf1EZFQkGqMm7ERKj8+Hgr5oLvJOVFcC+lRmR8hCe2S3jC4T5D7Vg/d7/fhA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, + "node_modules/tinyglobby": { + "version": "0.2.15", + "resolved": "https://nexus.gc-si.dev/repository/npm-public/tinyglobby/-/tinyglobby-0.2.15.tgz", + "integrity": "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "fdir": "^6.5.0", + "picomatch": "^4.0.3" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/SuperchupuDev" + } + }, + "node_modules/ts-api-utils": { + "version": "2.5.0", + "resolved": "https://nexus.gc-si.dev/repository/npm-public/ts-api-utils/-/ts-api-utils-2.5.0.tgz", + "integrity": "sha512-OJ/ibxhPlqrMM0UiNHJ/0CKQkoKF243/AEmplt3qpRgkW8VG7IfOS41h7V8TjITqdByHzrjcS/2si+y4lIh8NA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18.12" + }, + "peerDependencies": { + "typescript": ">=4.8.4" + } + }, + "node_modules/type-check": { + "version": "0.4.0", + "resolved": "https://nexus.gc-si.dev/repository/npm-public/type-check/-/type-check-0.4.0.tgz", + "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", + "dev": true, + "license": "MIT", + "dependencies": { + "prelude-ls": "^1.2.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/typescript": { + "version": "5.9.3", + "resolved": "https://nexus.gc-si.dev/repository/npm-public/typescript/-/typescript-5.9.3.tgz", + "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/typescript-eslint": { + "version": "8.58.0", + "resolved": "https://nexus.gc-si.dev/repository/npm-public/typescript-eslint/-/typescript-eslint-8.58.0.tgz", + "integrity": "sha512-e2TQzKfaI85fO+F3QywtX+tCTsu/D3WW5LVU6nz8hTFKFZ8yBJ6mSYRpXqdR3mFjPWmO0eWsTa5f+UpAOe/FMA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/eslint-plugin": "8.58.0", + "@typescript-eslint/parser": "8.58.0", + "@typescript-eslint/typescript-estree": "8.58.0", + "@typescript-eslint/utils": "8.58.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", + "typescript": ">=4.8.4 <6.1.0" + } + }, + "node_modules/undici-types": { + "version": "7.16.0", + "resolved": "https://nexus.gc-si.dev/repository/npm-public/undici-types/-/undici-types-7.16.0.tgz", + "integrity": "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw==", + "dev": true, + "license": "MIT" + }, + "node_modules/update-browserslist-db": { + "version": "1.2.3", + "resolved": "https://nexus.gc-si.dev/repository/npm-public/update-browserslist-db/-/update-browserslist-db-1.2.3.tgz", + "integrity": "sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "escalade": "^3.2.0", + "picocolors": "^1.1.1" + }, + "bin": { + "update-browserslist-db": "cli.js" + }, + "peerDependencies": { + "browserslist": ">= 4.21.0" + } + }, + "node_modules/uri-js": { + "version": "4.4.1", + "resolved": "https://nexus.gc-si.dev/repository/npm-public/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "punycode": "^2.1.0" + } + }, + "node_modules/vite": { + "version": "7.3.2", + "resolved": "https://nexus.gc-si.dev/repository/npm-public/vite/-/vite-7.3.2.tgz", + "integrity": "sha512-Bby3NOsna2jsjfLVOHKes8sGwgl4TT0E6vvpYgnAYDIF/tie7MRaFthmKuHx1NSXjiTueXH3do80FMQgvEktRg==", + "dev": true, + "license": "MIT", + "dependencies": { + "esbuild": "^0.27.0", + "fdir": "^6.5.0", + "picomatch": "^4.0.3", + "postcss": "^8.5.6", + "rollup": "^4.43.0", + "tinyglobby": "^0.2.15" + }, + "bin": { + "vite": "bin/vite.js" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + }, + "funding": { + "url": "https://github.com/vitejs/vite?sponsor=1" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + }, + "peerDependencies": { + "@types/node": "^20.19.0 || >=22.12.0", + "jiti": ">=1.21.0", + "less": "^4.0.0", + "lightningcss": "^1.21.0", + "sass": "^1.70.0", + "sass-embedded": "^1.70.0", + "stylus": ">=0.54.8", + "sugarss": "^5.0.0", + "terser": "^5.16.0", + "tsx": "^4.8.1", + "yaml": "^2.4.2" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "jiti": { + "optional": true + }, + "less": { + "optional": true + }, + "lightningcss": { + "optional": true + }, + "sass": { + "optional": true + }, + "sass-embedded": { + "optional": true + }, + "stylus": { + "optional": true + }, + "sugarss": { + "optional": true + }, + "terser": { + "optional": true + }, + "tsx": { + "optional": true + }, + "yaml": { + "optional": true + } + } + }, + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://nexus.gc-si.dev/repository/npm-public/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "license": "ISC", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/word-wrap": { + "version": "1.2.5", + "resolved": "https://nexus.gc-si.dev/repository/npm-public/word-wrap/-/word-wrap-1.2.5.tgz", + "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/yallist": { + "version": "3.1.1", + "resolved": "https://nexus.gc-si.dev/repository/npm-public/yallist/-/yallist-3.1.1.tgz", + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", + "dev": true, + "license": "ISC" + }, + "node_modules/yocto-queue": { + "version": "0.1.0", + "resolved": "https://nexus.gc-si.dev/repository/npm-public/yocto-queue/-/yocto-queue-0.1.0.tgz", + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/zod": { + "version": "4.3.6", + "resolved": "https://nexus.gc-si.dev/repository/npm-public/zod/-/zod-4.3.6.tgz", + "integrity": "sha512-rftlrkhHZOcjDwkGlnUtZZkvaPHCsDATp4pGpuOOMDaTdDDXF91wuVDJoWoPsKX/3YPQ5fHuF3STjcYyKr+Qhg==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/colinhacks" + } + }, + "node_modules/zod-validation-error": { + "version": "4.0.2", + "resolved": "https://nexus.gc-si.dev/repository/npm-public/zod-validation-error/-/zod-validation-error-4.0.2.tgz", + "integrity": "sha512-Q6/nZLe6jxuU80qb/4uJ4t5v2VEZ44lzQjPDhYJNztRQ4wyWc6VF3D3Kb/fAuPetZQnhS3hnajCf9CsWesghLQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18.0.0" + }, + "peerDependencies": { + "zod": "^3.25.0 || ^4.0.0" + } + } + } +} diff --git a/frontend/package.json b/frontend/package.json new file mode 100644 index 0000000..6b95e8f --- /dev/null +++ b/frontend/package.json @@ -0,0 +1,35 @@ +{ + "name": "frontend", + "private": true, + "version": "0.0.0", + "type": "module", + "scripts": { + "dev": "vite", + "build": "tsc -b && vite build", + "lint": "eslint .", + "preview": "vite preview", + "format": "prettier --write \"src/**/*.{ts,tsx,css}\"" + }, + "dependencies": { + "react": "^19.2.0", + "react-dom": "^19.2.0", + "react-router-dom": "^7.13.0" + }, + "devDependencies": { + "@eslint/js": "^9.39.1", + "@tailwindcss/vite": "^4.1.18", + "@types/node": "^24.10.1", + "@types/react": "^19.2.7", + "@types/react-dom": "^19.2.3", + "@vitejs/plugin-react": "^5.1.1", + "eslint": "^9.39.1", + "eslint-plugin-react-hooks": "^7.0.1", + "eslint-plugin-react-refresh": "^0.4.24", + "globals": "^16.5.0", + "prettier": "^3.5.3", + "tailwindcss": "^4.1.18", + "typescript": "~5.9.3", + "typescript-eslint": "^8.48.0", + "vite": "^7.3.1" + } +} diff --git a/frontend/public/android-chrome-192x192.png b/frontend/public/android-chrome-192x192.png new file mode 100644 index 0000000000000000000000000000000000000000..994bcbdeff2fe79203380d741ed27eff0e2afd0f GIT binary patch literal 12145 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%0D^eCG%m%CHe~9b*;oh& zH5?dP%ISGFDpHelA6|GX09+7byhErdsGs8WZ6TgbaxlBjwYZ;fL0IVk3M!<&0GIxF zdmc2{`rpot z@%B4xL@N6o&aqUP~ zJu{4dQejD>otAV}OVTs?}fxAN2345Sulrwr_$E9p` zogMq2)UJAFHn6TNo2{@xz)X2UdLISVIY-cGiwzx}*o84n=)}*tpWh+PM{bl$9@cOy zSu4V&@A(h#b)Ikp(C#afQ;S1}^It=93G<&JsSWQ~J1@cV(q5thH8*V|fqg&ix4|k+ zJ2rBK^jr8plQBM2bHsAQHktCfco~6cN-aP|r9KZ&~- z;q>**DP0dkRt`uiM31ERUK%`;q!f|tb5w>wL04PMmJNyWOACR3dUr)=+W$DHShd4~ zRX-*3IuX zwkE{N{F5Ztdjr*p4v(Cjm^B9Y^Of+o8SJoYGFQe$zBU{)#6Jn~6L(5H1d^`p(sW(8 zz$mKt!f1)>8mBKd=~d}Ee9CA1fWL;?==p%YCN=;k-$jS<7BvwI@WCR(%w>-0dZzV4 zvbV}-{ZsS>jrM{GdIIy~eANO|VUT6`9=}+h1^_CbZRx$nUPpDx*}!p{(nVX)-6e&> zXLnU;Vo+4?i@ZMN5PboVgZ|0VX-&eFB|;eFANLWZfMK3sqshz*=DEbol=x zCNL+xoyLnNoyLEQ>_)qlj8Q6qN`D7X2|0-i>fJA^U{Yb2G-un@NtfUgSM%*yqe*k(sE3yCp literal 0 HcmV?d00001 diff --git a/frontend/public/android-chrome-512x512.png b/frontend/public/android-chrome-512x512.png new file mode 100644 index 0000000000000000000000000000000000000000..d2350da3bb5b7e37fa8f8b75c8fe7e9f5652d6e3 GIT binary patch literal 26809 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 a0~s*5$E`9PMZ|%>0BTCw*9)&%KK?)AoY|BB literal 0 HcmV?d00001 diff --git a/frontend/public/apple-touch-icon.png b/frontend/public/apple-touch-icon.png new file mode 100644 index 0000000000000000000000000000000000000000..29219562e9df118314826c616d80dbbe6b24e6f7 GIT binary patch literal 10764 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 E1H|7zX#fBK literal 0 HcmV?d00001 diff --git a/frontend/public/site.webmanifest b/frontend/public/site.webmanifest new file mode 100644 index 0000000..3e8b25c --- /dev/null +++ b/frontend/public/site.webmanifest @@ -0,0 +1,19 @@ +{ + "name": "SNP Connection Monitoring", + "short_name": "SNP Connection", + "icons": [ + { + "src": "/snp-connection/android-chrome-192x192.png", + "sizes": "192x192", + "type": "image/png" + }, + { + "src": "/snp-connection/android-chrome-512x512.png", + "sizes": "512x512", + "type": "image/png" + } + ], + "theme_color": "#ffffff", + "background_color": "#ffffff", + "display": "standalone" +} diff --git a/frontend/src/App.tsx b/frontend/src/App.tsx new file mode 100644 index 0000000..ce1211a --- /dev/null +++ b/frontend/src/App.tsx @@ -0,0 +1,26 @@ +import { BrowserRouter, Routes, Route, Navigate } from 'react-router-dom'; + +const BASE_PATH = '/snp-connection'; + +function App() { + return ( + + + } /> + +
+

SNP Connection Monitoring

+

Dashboard - Coming Soon

+
+ + } + /> +
+
+ ); +} + +export default App; diff --git a/frontend/src/index.css b/frontend/src/index.css new file mode 100644 index 0000000..d4b5078 --- /dev/null +++ b/frontend/src/index.css @@ -0,0 +1 @@ +@import 'tailwindcss'; diff --git a/frontend/src/main.tsx b/frontend/src/main.tsx new file mode 100644 index 0000000..dfacde0 --- /dev/null +++ b/frontend/src/main.tsx @@ -0,0 +1,10 @@ +import { StrictMode } from 'react'; +import { createRoot } from 'react-dom/client'; +import App from './App'; +import './index.css'; + +createRoot(document.getElementById('root')!).render( + + + , +); diff --git a/frontend/src/vite-env.d.ts b/frontend/src/vite-env.d.ts new file mode 100644 index 0000000..11f02fe --- /dev/null +++ b/frontend/src/vite-env.d.ts @@ -0,0 +1 @@ +/// diff --git a/frontend/tsconfig.app.json b/frontend/tsconfig.app.json new file mode 100644 index 0000000..2cf6c6c --- /dev/null +++ b/frontend/tsconfig.app.json @@ -0,0 +1,26 @@ +{ + "compilerOptions": { + "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsBuildInfo", + "target": "ES2020", + "useDefineForClassFields": true, + "lib": ["ES2020", "DOM", "DOM.Iterable"], + "module": "ESNext", + "skipLibCheck": true, + + /* Bundler mode */ + "moduleResolution": "bundler", + "allowImportingTsExtensions": true, + "verbatimModuleSyntax": true, + "moduleDetection": "force", + "noEmit": true, + "jsx": "react-jsx", + + /* Linting */ + "strict": true, + "noUnusedLocals": true, + "noUnusedParameters": true, + "noFallthroughCasesInSwitch": true, + "noUncheckedSideEffectImports": true + }, + "include": ["src"] +} diff --git a/frontend/tsconfig.json b/frontend/tsconfig.json new file mode 100644 index 0000000..1ffef60 --- /dev/null +++ b/frontend/tsconfig.json @@ -0,0 +1,7 @@ +{ + "files": [], + "references": [ + { "path": "./tsconfig.app.json" }, + { "path": "./tsconfig.node.json" } + ] +} diff --git a/frontend/tsconfig.node.json b/frontend/tsconfig.node.json new file mode 100644 index 0000000..4c53087 --- /dev/null +++ b/frontend/tsconfig.node.json @@ -0,0 +1,24 @@ +{ + "compilerOptions": { + "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.node.tsBuildInfo", + "target": "ES2022", + "lib": ["ES2023"], + "module": "ESNext", + "skipLibCheck": true, + + /* Bundler mode */ + "moduleResolution": "bundler", + "allowImportingTsExtensions": true, + "verbatimModuleSyntax": true, + "moduleDetection": "force", + "noEmit": true, + + /* Linting */ + "strict": true, + "noUnusedLocals": true, + "noUnusedParameters": true, + "noFallthroughCasesInSwitch": true, + "noUncheckedSideEffectImports": true + }, + "include": ["vite.config.ts"] +} diff --git a/frontend/vite.config.ts b/frontend/vite.config.ts new file mode 100644 index 0000000..23e2a72 --- /dev/null +++ b/frontend/vite.config.ts @@ -0,0 +1,21 @@ +import { defineConfig } from 'vite'; +import react from '@vitejs/plugin-react'; +import tailwindcss from '@tailwindcss/vite'; + +export default defineConfig({ + plugins: [react(), tailwindcss()], + server: { + port: 5173, + proxy: { + '/snp-connection/api': { + target: 'http://localhost:8042', + changeOrigin: true, + }, + }, + }, + base: '/snp-connection/', + build: { + outDir: '../src/main/resources/static', + emptyOutDir: true, + }, +}); diff --git a/pom.xml b/pom.xml new file mode 100644 index 0000000..1408c9f --- /dev/null +++ b/pom.xml @@ -0,0 +1,183 @@ + + + 4.0.0 + + + org.springframework.boot + spring-boot-starter-parent + 3.2.1 + + + + com.gcsc + snp-connection-monitoring + 1.0.0 + SNP Connection Monitoring + API Gateway + Monitoring Platform for S&P Connection Services + + + 17 + UTF-8 + 17 + 17 + + + 3.2.1 + 42.7.6 + 1.18.30 + + + + + + org.springframework.boot + spring-boot-starter-web + + + + + org.springframework.boot + spring-boot-starter-data-jpa + + + + + org.springframework.boot + spring-boot-starter-security + + + + + org.springframework.boot + spring-boot-starter-validation + + + + + org.postgresql + postgresql + ${postgresql.version} + + + + + org.springframework.boot + spring-boot-starter-webflux + + + + + com.fasterxml.jackson.core + jackson-databind + + + + + org.projectlombok + lombok + ${lombok.version} + true + + + + + org.springframework.boot + spring-boot-devtools + runtime + true + + + + + org.springframework.boot + spring-boot-starter-actuator + + + + + org.springdoc + springdoc-openapi-starter-webmvc-ui + 2.3.0 + + + + + org.springframework.boot + spring-boot-starter-test + test + + + + org.springframework.security + spring-security-test + test + + + + + + + org.springframework.boot + spring-boot-maven-plugin + ${spring-boot.version} + + + + org.projectlombok + lombok + + + + + + com.github.eirslett + frontend-maven-plugin + 1.15.1 + + frontend + v20.19.0 + + + + install-node-and-npm + install-node-and-npm + + + npm-install + npm + + install + + + + npm-build + npm + + run build + + + + + + org.apache.maven.plugins + maven-compiler-plugin + 3.11.0 + + 17 + 17 + UTF-8 + + + org.projectlombok + lombok + ${lombok.version} + + + + + + + diff --git a/src/main/java/com/gcsc/connection/SnpConnectionApplication.java b/src/main/java/com/gcsc/connection/SnpConnectionApplication.java new file mode 100644 index 0000000..0367f71 --- /dev/null +++ b/src/main/java/com/gcsc/connection/SnpConnectionApplication.java @@ -0,0 +1,14 @@ +package com.gcsc.connection; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.scheduling.annotation.EnableScheduling; + +@SpringBootApplication +@EnableScheduling +public class SnpConnectionApplication { + + public static void main(String[] args) { + SpringApplication.run(SnpConnectionApplication.class, args); + } +} diff --git a/src/main/java/com/gcsc/connection/common/dto/ApiResponse.java b/src/main/java/com/gcsc/connection/common/dto/ApiResponse.java new file mode 100644 index 0000000..434b34f --- /dev/null +++ b/src/main/java/com/gcsc/connection/common/dto/ApiResponse.java @@ -0,0 +1,37 @@ +package com.gcsc.connection.common.dto; + +import com.fasterxml.jackson.annotation.JsonInclude; +import lombok.Builder; +import lombok.Getter; + +@Getter +@Builder +@JsonInclude(JsonInclude.Include.NON_NULL) +public class ApiResponse { + + private final boolean success; + private final String message; + private final T data; + + public static ApiResponse ok(T data) { + return ApiResponse.builder() + .success(true) + .data(data) + .build(); + } + + public static ApiResponse ok(T data, String message) { + return ApiResponse.builder() + .success(true) + .message(message) + .data(data) + .build(); + } + + public static ApiResponse error(String message) { + return ApiResponse.builder() + .success(false) + .message(message) + .build(); + } +} diff --git a/src/main/java/com/gcsc/connection/common/exception/GlobalExceptionHandler.java b/src/main/java/com/gcsc/connection/common/exception/GlobalExceptionHandler.java new file mode 100644 index 0000000..ab87f26 --- /dev/null +++ b/src/main/java/com/gcsc/connection/common/exception/GlobalExceptionHandler.java @@ -0,0 +1,21 @@ +package com.gcsc.connection.common.exception; + +import com.gcsc.connection.common.dto.ApiResponse; +import lombok.extern.slf4j.Slf4j; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.ExceptionHandler; +import org.springframework.web.bind.annotation.RestControllerAdvice; + +@Slf4j +@RestControllerAdvice +public class GlobalExceptionHandler { + + @ExceptionHandler(Exception.class) + public ResponseEntity> handleException(Exception e) { + log.error("Unhandled exception: {}", e.getMessage(), e); + return ResponseEntity + .status(HttpStatus.INTERNAL_SERVER_ERROR) + .body(ApiResponse.error(e.getMessage())); + } +} diff --git a/src/main/java/com/gcsc/connection/config/SecurityConfig.java b/src/main/java/com/gcsc/connection/config/SecurityConfig.java new file mode 100644 index 0000000..44f84ed --- /dev/null +++ b/src/main/java/com/gcsc/connection/config/SecurityConfig.java @@ -0,0 +1,26 @@ +package com.gcsc.connection.config; + +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.security.config.annotation.web.builders.HttpSecurity; +import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; +import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer; +import org.springframework.security.config.annotation.web.configurers.HeadersConfigurer; +import org.springframework.security.web.SecurityFilterChain; + +@Configuration +@EnableWebSecurity +public class SecurityConfig { + + @Bean + public SecurityFilterChain filterChain(HttpSecurity http) throws Exception { + http + .csrf(AbstractHttpConfigurer::disable) + .headers(headers -> headers + .frameOptions(HeadersConfigurer.FrameOptionsConfig::sameOrigin)) + .authorizeHttpRequests(auth -> auth + .anyRequest().permitAll()); + + return http.build(); + } +} diff --git a/src/main/java/com/gcsc/connection/config/SwaggerConfig.java b/src/main/java/com/gcsc/connection/config/SwaggerConfig.java new file mode 100644 index 0000000..da54310 --- /dev/null +++ b/src/main/java/com/gcsc/connection/config/SwaggerConfig.java @@ -0,0 +1,19 @@ +package com.gcsc.connection.config; + +import io.swagger.v3.oas.models.OpenAPI; +import io.swagger.v3.oas.models.info.Info; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +@Configuration +public class SwaggerConfig { + + @Bean + public OpenAPI openAPI() { + return new OpenAPI() + .info(new Info() + .title("SNP Connection Monitoring API") + .description("API Gateway + Monitoring Platform for S&P Connection Services") + .version("1.0.0")); + } +} diff --git a/src/main/java/com/gcsc/connection/global/controller/WebViewController.java b/src/main/java/com/gcsc/connection/global/controller/WebViewController.java new file mode 100644 index 0000000..eedf791 --- /dev/null +++ b/src/main/java/com/gcsc/connection/global/controller/WebViewController.java @@ -0,0 +1,21 @@ +package com.gcsc.connection.global.controller; + +import org.springframework.stereotype.Controller; +import org.springframework.web.bind.annotation.GetMapping; + +/** + * SPA(React) fallback router + * + * React Router handles client-side routing, + * so all frontend paths are forwarded to index.html. + */ +@Controller +public class WebViewController { + + @GetMapping({"/", "/login", "/dashboard", "/dashboard/**", + "/monitoring/**", "/apikeys", "/apikeys/**", + "/admin/**"}) + public String forward() { + return "forward:/index.html"; + } +} diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml new file mode 100644 index 0000000..dec39a7 --- /dev/null +++ b/src/main/resources/application.yml @@ -0,0 +1,60 @@ +spring: + application: + name: snp-connection-monitoring + + # PostgreSQL Database Configuration + datasource: + url: jdbc:postgresql://211.208.115.83:5432/snpdb + username: snp + password: snp#8932 + driver-class-name: org.postgresql.Driver + hikari: + maximum-pool-size: 10 + minimum-idle: 5 + connection-timeout: 30000 + + # JPA Configuration + jpa: + hibernate: + ddl-auto: validate + show-sql: false + properties: + hibernate: + dialect: org.hibernate.dialect.PostgreSQLDialect + format_sql: true + default_schema: std_snp_connection + +# Server Configuration +server: + port: 8042 + servlet: + context-path: /snp-connection + +# Actuator Configuration +management: + endpoints: + web: + exposure: + include: health,info,metrics + endpoint: + health: + show-details: always + +# Springdoc / Swagger UI +springdoc: + swagger-ui: + deep-linking: true + display-request-duration: true + +# Logging Configuration +logging: + level: + root: INFO + com.gcsc.connection: DEBUG + +# Custom Application Properties +app: + environment: dev + heartbeat: + default-interval-seconds: 30 + timeout-seconds: 5 diff --git a/src/test/java/com/gcsc/connection/SnpConnectionApplicationTest.java b/src/test/java/com/gcsc/connection/SnpConnectionApplicationTest.java new file mode 100644 index 0000000..f8ae55e --- /dev/null +++ b/src/test/java/com/gcsc/connection/SnpConnectionApplicationTest.java @@ -0,0 +1,14 @@ +package com.gcsc.connection; + +import org.junit.jupiter.api.Test; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.test.context.ActiveProfiles; + +@SpringBootTest +@ActiveProfiles("test") +class SnpConnectionApplicationTest { + + @Test + void contextLoads() { + } +} diff --git a/src/test/resources/application-test.yml b/src/test/resources/application-test.yml new file mode 100644 index 0000000..6ebd006 --- /dev/null +++ b/src/test/resources/application-test.yml @@ -0,0 +1,13 @@ +spring: + datasource: + url: jdbc:h2:mem:testdb + driver-class-name: org.h2.Driver + username: sa + password: + + jpa: + hibernate: + ddl-auto: create-drop + properties: + hibernate: + dialect: org.hibernate.dialect.H2Dialect diff --git a/workflow-version.json b/workflow-version.json index 9b7f00a..5c0b6b4 100644 --- a/workflow-version.json +++ b/workflow-version.json @@ -1,26 +1,7 @@ { - "version": "1.0.0", - "updated": "2026-02-14", - "gitea_url": "http://211.208.115.83:3000", - "nexus_url": "http://211.208.115.83:9081", - "changes": [ - { - "version": "1.0.0", - "date": "2026-02-14", - "description": "팀 워크플로우 초기 구성", - "items": [ - "3계층 정책 강제 (프로젝트 deny + 서버 hooks + Branch Protection)", - "Java Maven/Gradle + React TS 템플릿", - "Nexus 자체 레포지토리 연동", - "Conventional Commits (한/영 혼용)", - "Git hooks: pre-commit(빌드검증), commit-msg(형식검증), post-checkout(hooksPath 자동설정)" - ], - "affected_files": [ - ".claude/settings.json", - ".claude/rules/*", - ".claude/skills/*", - ".githooks/*" - ] - } - ] + "applied_global_version": "1.6.1", + "applied_date": "2026-04-07", + "project_type": "java-maven", + "gitea_url": "https://gitea.gc-si.dev", + "nexus_url": "https://nexus.gc-si.dev" } From f7d304dc18a2e98566d2447c5e10e7e27dc8f6a1 Mon Sep 17 00:00:00 2001 From: HYOJIN Date: Tue, 7 Apr 2026 10:59:19 +0900 Subject: [PATCH 2/2] =?UTF-8?q?docs:=20=EB=A6=B4=EB=A6=AC=EC=A6=88=20?= =?UTF-8?q?=EB=85=B8=ED=8A=B8=20=EC=A0=95=EB=A6=AC=20(2026-04-07)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Opus 4.6 (1M context) --- docs/RELEASE-NOTES.md | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) create mode 100644 docs/RELEASE-NOTES.md diff --git a/docs/RELEASE-NOTES.md b/docs/RELEASE-NOTES.md new file mode 100644 index 0000000..83bc788 --- /dev/null +++ b/docs/RELEASE-NOTES.md @@ -0,0 +1,19 @@ +# Changelog + +All notable changes to this project will be documented in this file. + +The format is based on [Keep a Changelog](https://keepachangelog.com/ko/1.0.0/). + +## [Unreleased] + +## [2026-04-07] + +### 추가 + +- Spring Boot 3.2.1 백엔드 초기 구조 (com.gcsc.connection, 포트 8042, context /snp-connection) +- React 19 + TypeScript + Vite 7 + Tailwind CSS 4 프론트엔드 통합 +- frontend-maven-plugin 기반 통합 빌드 설정 +- SPA fallback WebViewController, SecurityConfig, SwaggerConfig +- 공통 모듈 (ApiResponse, GlobalExceptionHandler) +- 파비콘 등록 (favicon_io_red) +- 팀 워크플로우 v1.6.1 동기화