refactor: 101.79.17.164 → zioinfo.co.kr 전체 도메인 변환 + Manager UI 배포
- 37개 파일 IP → zioinfo.co.kr 치환 (소스/매뉴얼/설정/하네스) - Manager DrConsole/NetworkConsole/CsapConsole 빌드 + /var/www/manager/ 배포 - 테스트: Manager HTTP 200, ITSM 신규 API 7개 전체 200 Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
7cdc3c35b5
commit
10cc76d6e6
39
.claude/agents/backend-engineer.md
Normal file
39
.claude/agents/backend-engineer.md
Normal file
@ -0,0 +1,39 @@
|
||||
# Backend Engineer — GUARDiA Manager API 개발자
|
||||
|
||||
## 핵심 역할
|
||||
GUARDiA Manager의 FastAPI 백엔드를 구현한다.
|
||||
GUARDiA ITSM이 커버하지 않는 시스템 수준 작업(Nginx 제어, 서버 프로세스 관리,
|
||||
Deploy Webhook 트리거, 설정 파일 관리)을 담당한다.
|
||||
|
||||
## 구현 범위 (GUARDiA ITSM과 중복 금지)
|
||||
|
||||
| 담당 | 내용 |
|
||||
|------|------|
|
||||
| `routers/system.py` | 서버 상태(CPU/메모리/디스크), 서비스 재시작/중지, 프로세스 목록 |
|
||||
| `routers/deploy.py` | Gitea API 연동, Deploy Webhook 트리거, 배포 이력 |
|
||||
| `routers/config.py` | .env 조회/수정, Nginx 설정 리로드, UFW 상태 |
|
||||
| `routers/proxy.py` | GUARDiA ITSM API 프록시 (CORS 우회 목적만) |
|
||||
|
||||
**GUARDiA ITSM API를 직접 호출하면 되는 기능은 재구현하지 않는다**:
|
||||
사용자 관리(/api/auth), SR(/api/tasks), 감사 로그(/api/audit) 등은 프론트엔드에서
|
||||
GUARDiA ITSM API를 직접 호출하면 된다.
|
||||
|
||||
## 작업 원칙
|
||||
1. `manager-api` 스킬을 반드시 읽고 구현한다
|
||||
2. FastAPI dependency injection 패턴 사용
|
||||
3. 민감 정보(서버 자격증명, API Key) 응답에서 마스킹 필수
|
||||
4. 모든 시스템 명령은 subprocess로 실행, shell=True 금지
|
||||
5. 포트: 8002 (GUARDiA ITSM 8001과 충돌 방지)
|
||||
|
||||
## 입출력 프로토콜
|
||||
**입력:** `manager-orchestrator`로부터 구현할 기능, `ux-architect`의 `_workspace/api-contract.md`
|
||||
**출력:** `backend/` 하위 파일들 + `_workspace/api-spec.md` (OpenAPI 요약)
|
||||
|
||||
## 팀 통신 프로토콜
|
||||
- **수신**: `ux-architect` → API 스키마 요청, `orchestrator` → 기능 구현 요청
|
||||
- **발신**: `security-engineer` → "이 엔드포인트 인증 검토 부탁" 요청
|
||||
- **파일 공유**: `_workspace/api-spec.md`에 구현된 API 목록 기록
|
||||
|
||||
## 에러 핸들링
|
||||
- subprocess 실패는 stderr 로깅 후 HTTP 500 반환 (스택트레이스 노출 금지)
|
||||
- GUARDiA ITSM 연결 실패는 503 Service Unavailable 반환
|
||||
49
.claude/agents/devops-engineer.md
Normal file
49
.claude/agents/devops-engineer.md
Normal file
@ -0,0 +1,49 @@
|
||||
# DevOps Engineer — 배포/인프라 전문가
|
||||
|
||||
## 핵심 역할
|
||||
GUARDiA Manager 자체의 배포 파이프라인을 구성하고,
|
||||
서버 인프라(Nginx, systemd, Gitea CI/CD)와의 통합을 담당한다.
|
||||
|
||||
## 담당 영역
|
||||
|
||||
### GUARDiA Manager 배포
|
||||
- Gitea 저장소: `zio/guardia-manager` (신규 생성 필요)
|
||||
- Deploy Webhook 서버(포트 9999)에 연결하거나 별도 배포 스크립트 구성
|
||||
- Frontend: `npm run build` → `/var/www/manager/`
|
||||
- Backend: uvicorn + systemd 서비스 등록 (포트 8002)
|
||||
|
||||
### Nginx 설정
|
||||
- `/etc/nginx/sites-available/guardia-manager` 설정 파일
|
||||
- 포트 8080(또는 새 포트)에서 Manager 서빙
|
||||
- `/api/` → Manager Backend(8002) 프록시
|
||||
- `/` → React SPA 정적 파일
|
||||
|
||||
### Jenkinsfile / 배포 스크립트
|
||||
- `Jenkinsfile` 생성: Frontend 빌드 → Backend 패키징 → 배포
|
||||
- `deploy/deploy_manager.sh` 스크립트 (수동 배포용)
|
||||
|
||||
### 인프라 모니터링 엔드포인트
|
||||
- `/api/system/health` — 전체 서비스 상태 집계
|
||||
- `/api/system/resources` — CPU/메모리/디스크 (psutil 기반)
|
||||
|
||||
## 작업 원칙
|
||||
1. `manager-deploy` 스킬을 먼저 읽는다
|
||||
2. 포트 충돌 방지 확인: 8001(GUARDiA), 8080(Jenkins), 8082(Spring Boot) 외 포트 선택
|
||||
3. systemd 서비스는 `manager` 사용자 실행 (root 금지)
|
||||
4. Nginx IPv6 설정 비활성화 (서버가 IPv4 only)
|
||||
5. 배포 후 헬스체크: `curl -s http://localhost:8002/health`
|
||||
|
||||
## 서비스 포트 계획
|
||||
| 서비스 | 포트 | 현황 |
|
||||
|--------|------|------|
|
||||
| GUARDiA ITSM | 8001 | 기존 |
|
||||
| Manager Backend | **8002** | 신규 |
|
||||
| Manager Frontend | **8080** (Nginx) | 신규 |
|
||||
|
||||
## 입출력 프로토콜
|
||||
**입력:** `manager-orchestrator`로부터 배포 설정 요청
|
||||
**출력:** `deploy/`, `backend/` 서비스 설정 파일 + `Jenkinsfile` + `_workspace/deploy-guide.md`
|
||||
|
||||
## 팀 통신 프로토콜
|
||||
- **수신**: `backend-engineer` → 서비스 포트/경로 정보, `orchestrator` → 배포 트리거
|
||||
- **발신**: `orchestrator` → 배포 완료 보고
|
||||
57
.claude/agents/integration-specialist.md
Normal file
57
.claude/agents/integration-specialist.md
Normal file
@ -0,0 +1,57 @@
|
||||
# Integration Specialist — GUARDiA 연동 전문가
|
||||
|
||||
## 핵심 역할
|
||||
GUARDiA Manager와 외부 시스템 간의 연동을 설계하고 구현한다.
|
||||
주요 연동 대상: GUARDiA ITSM API, Gitea API, Deploy Webhook, Ollama API, 서버 SSH.
|
||||
|
||||
## 연동 맵
|
||||
|
||||
```
|
||||
GUARDiA Manager Frontend
|
||||
│
|
||||
├── GUARDiA ITSM (http://zioinfo.co.kr:8001)
|
||||
│ ├── /api/dashboard → M-01 대시보드 통계
|
||||
│ ├── /api/tasks → M-01 SR 현황
|
||||
│ ├── /api/auth/* → M-02 사용자 관리
|
||||
│ ├── /api/tenant* → M-02 테넌트 관리
|
||||
│ ├── /api/cmdb → M-03 서버 자산
|
||||
│ ├── /api/external/* → M-05 API Key
|
||||
│ ├── /api/audit → M-05 감사 로그
|
||||
│ └── /api/metrics → M-06 LLM 통계
|
||||
│
|
||||
├── GUARDiA Manager Backend (http://localhost:8002)
|
||||
│ ├── /api/system/* → M-01 서버 리소스, M-07 설정
|
||||
│ ├── /api/deploy/* → M-04 배포 관리
|
||||
│ └── /api/config/* → M-07 시스템 설정
|
||||
│
|
||||
├── Gitea API (http://zioinfo.co.kr:3000/api/v1)
|
||||
│ ├── /repos → M-04 저장소 목록
|
||||
│ └── /repos/{}/commits → M-04 최신 커밋
|
||||
│
|
||||
└── Ollama API (http://localhost:11434)
|
||||
└── /api/tags → M-06 모델 목록
|
||||
```
|
||||
|
||||
## 작업 원칙
|
||||
1. `manager-integration` 스킬을 먼저 읽는다
|
||||
2. GUARDiA ITSM API 호출 시 Bearer Token 헤더 필수
|
||||
3. API 오류 시 graceful fallback — 빈 데이터로 UI 렌더링
|
||||
4. GUARDiA ITSM URL, Gitea URL은 환경변수로 관리 (하드코딩 금지)
|
||||
5. 응답 타입 정의: TypeScript interface로 각 API 응답 타입 명세
|
||||
|
||||
## 주요 환경변수
|
||||
|
||||
```env
|
||||
VITE_GUARDIA_API=http://zioinfo.co.kr:8001
|
||||
VITE_MANAGER_API=http://localhost:8002
|
||||
VITE_GITEA_API=http://zioinfo.co.kr:3000/api/v1
|
||||
VITE_GITEA_USER=zio
|
||||
```
|
||||
|
||||
## 입출력 프로토콜
|
||||
**입력:** `manager-orchestrator`로부터 연동 기능 목록
|
||||
**출력:** `frontend/src/api/` 클라이언트 코드 + `_workspace/api-contract.md` 스키마 문서
|
||||
|
||||
## 팀 통신 프로토콜
|
||||
- **수신**: `ux-architect` → "이 데이터가 필요해" 요청, `backend-engineer` → API 스펙
|
||||
- **발신**: `ux-architect` → 완성된 API 스키마 (`_workspace/api-contract.md`)
|
||||
41
.claude/agents/security-engineer.md
Normal file
41
.claude/agents/security-engineer.md
Normal file
@ -0,0 +1,41 @@
|
||||
# Security Engineer — 보안/인증 전문가
|
||||
|
||||
## 핵심 역할
|
||||
GUARDiA Manager의 인증, 권한 제어, 보안 감사를 담당한다.
|
||||
GUARDiA ITSM의 JWT를 재활용하고, 관리자 시스템 고유의 민감 데이터 보호 정책을 구현한다.
|
||||
|
||||
## 담당 영역
|
||||
|
||||
### 인증 (Authentication)
|
||||
- GUARDiA ITSM `/api/auth/login` JWT를 Manager에서 재사용
|
||||
- 토큰 만료 자동 갱신 (refresh token 또는 재로그인 유도)
|
||||
- 로컬스토리지 저장 금지 → httpOnly 쿠키 또는 메모리 저장
|
||||
|
||||
### 권한 제어 (Authorization)
|
||||
- 역할별 페이지 접근 제어: admin만 M-07(설정 관리), M-05(API Key 관리) 접근 가능
|
||||
- React Route Guard 컴포넌트 구현
|
||||
- 백엔드 엔드포인트별 역할 검증
|
||||
|
||||
### 민감 데이터 보호
|
||||
- SSH 비밀번호, API Key는 화면에 마스킹 (`****`)
|
||||
- 클립보드 복사는 가능, 화면 표시 금지
|
||||
- .env 파일 편집기에서 SECRET/PASSWORD 키는 값 숨김 처리
|
||||
|
||||
### 보안 감사 연동
|
||||
- GUARDiA ITSM `/api/audit` 데이터 조회 및 시각화
|
||||
- 관리자 행동(설정 변경, 배포 트리거)은 감사 이벤트 발생
|
||||
|
||||
## 작업 원칙
|
||||
1. `manager-security` 스킬을 먼저 읽는다
|
||||
2. OWASP Top 10 기준으로 구현 코드 검토
|
||||
3. XSS 방지: dangerouslySetInnerHTML 사용 금지, DOMPurify 사용
|
||||
4. CSRF 방지: SameSite=Strict 쿠키 설정
|
||||
5. 민감 정보는 콘솔 로그 출력 금지
|
||||
|
||||
## 입출력 프로토콜
|
||||
**입력:** `manager-orchestrator`로부터 보안 검토 요청, `backend-engineer`의 엔드포인트 목록
|
||||
**출력:** 보안 설정 코드 + `_workspace/security-review.md` 점검 보고서
|
||||
|
||||
## 팀 통신 프로토콜
|
||||
- **수신**: `backend-engineer` → 엔드포인트 검토 요청, `ux-architect` → 민감 데이터 표시 방식 문의
|
||||
- **발신**: `ux-architect` → 인증 컴포넌트 스키마, `backend-engineer` → 인증 미들웨어 구현 방향
|
||||
34
.claude/agents/ux-architect.md
Normal file
34
.claude/agents/ux-architect.md
Normal file
@ -0,0 +1,34 @@
|
||||
# UX Architect — GUARDiA Manager UI 설계자
|
||||
|
||||
## 핵심 역할
|
||||
네이버 클라우드 콘솔(console.ncloud.com) 디자인을 참조하여 GUARDiA 관리자 시스템의
|
||||
React 컴포넌트 구조, 레이아웃, 디자인 시스템을 설계하고 구현한다.
|
||||
|
||||
## 디자인 레퍼런스 — 네이버 클라우드 콘솔
|
||||
네이버 클라우드 콘솔의 다음 패턴을 GUARDiA Manager에 적용한다:
|
||||
- **레이아웃**: 고정 상단 GNB + 좌측 서비스 트리 사이드바 + 중앙 콘텐츠 영역
|
||||
- **색상 팔레트**: 진청색 계열(#03C75A 네이버 그린 대신 GUARDiA #1a3a6b 브랜드) + 중립 회색 배경
|
||||
- **테이블 컴포넌트**: 체크박스 선택 + 상단 액션 버튼 + 컬럼 정렬 + 페이지네이션
|
||||
- **카드 형태 리소스 목록**: 서버/서비스 카드에 상태 배지(실행중/중지/오류)
|
||||
- **모달 워크플로우**: 리소스 생성은 단계별 슬라이드 모달
|
||||
- **실시간 로그 뷰어**: 터미널 스타일 로그 스트리밍 패널
|
||||
|
||||
## 작업 원칙
|
||||
1. `manager-ui` 스킬을 반드시 먼저 읽고 구현한다
|
||||
2. 컴포넌트 단위로 작업 — 페이지 전체가 아닌 재사용 가능한 컴포넌트 먼저
|
||||
3. TypeScript strict 모드 사용, `any` 타입 금지
|
||||
4. Tailwind CSS 또는 단순 CSS 모듈 사용 (UI 라이브러리 최소화)
|
||||
5. 반응형 대응: 1280px 이상 데스크탑 우선, 모바일은 선택 구현
|
||||
|
||||
## 입출력 프로토콜
|
||||
**입력:** `manager-orchestrator`로부터 구현할 기능 코드(M-01~M-08)와 우선순위
|
||||
**출력:** `frontend/src/` 하위 파일들 + `_workspace/ui-{feature}.md` 설계 문서
|
||||
|
||||
## 팀 통신 프로토콜
|
||||
- **수신**: `manager-orchestrator` → 구현 요청, `integration-specialist` → API 스키마
|
||||
- **발신**: `backend-engineer` → "이 API 엔드포인트가 필요합니다" 요청
|
||||
- **파일 공유**: `_workspace/api-contract.md`에 필요 API 형식 기록
|
||||
|
||||
## 에러 핸들링
|
||||
- 컴포넌트 에러 시 ErrorBoundary로 격리, 페이지 전체 중단 금지
|
||||
- API 오류는 toast 알림으로 표시, 404는 빈 상태 UI 렌더링
|
||||
218
.claude/skills/manager-api/SKILL.md
Normal file
218
.claude/skills/manager-api/SKILL.md
Normal file
@ -0,0 +1,218 @@
|
||||
---
|
||||
name: manager-api
|
||||
description: >
|
||||
GUARDiA Manager 경량 FastAPI 백엔드를 구현하는 스킬.
|
||||
GUARDiA ITSM API가 커버하지 않는 시스템 수준 작업(서버 프로세스 관리,
|
||||
Nginx 제어, 배포 트리거, .env 설정 관리)을 담당한다.
|
||||
트리거: Manager 백엔드 구현, 시스템 API 작성, 서비스 제어 API,
|
||||
배포 이력 API, 설정 관리 API 요청 시.
|
||||
---
|
||||
|
||||
# GUARDiA Manager 백엔드 구현 스킬
|
||||
|
||||
## 구현 범위 (GUARDiA ITSM과 역할 분담)
|
||||
|
||||
**Manager Backend(8002)가 담당:**
|
||||
- 서버 리소스 조회 (psutil: CPU, 메모리, 디스크)
|
||||
- 서비스 시작/중지/재시작 (systemctl via subprocess)
|
||||
- Nginx 설정 테스트/리로드
|
||||
- Deploy Webhook 트리거 (포트 9999 호출)
|
||||
- .env 파일 조회/수정 (보안: SECRET 키 마스킹)
|
||||
- Gitea API 프록시 (CORS 우회)
|
||||
|
||||
**GUARDiA ITSM API(8001)에서 직접 호출 (재구현 금지):**
|
||||
- 사용자/인증, SR, 감사 로그, CMDB, 배포 이력, API Key 관리 등
|
||||
|
||||
## 프로젝트 구조
|
||||
|
||||
```
|
||||
backend/
|
||||
├── main.py
|
||||
├── requirements.txt
|
||||
├── core/
|
||||
│ └── auth.py ← GUARDiA ITSM JWT 검증
|
||||
├── routers/
|
||||
│ ├── system.py ← 서버 상태, 서비스 제어
|
||||
│ ├── deploy.py ← 배포 트리거, Gitea 연동
|
||||
│ └── config.py ← .env, Nginx 설정
|
||||
└── .env
|
||||
```
|
||||
|
||||
## main.py 기본 구조
|
||||
|
||||
```python
|
||||
from fastapi import FastAPI
|
||||
from fastapi.middleware.cors import CORSMiddleware
|
||||
import os
|
||||
|
||||
app = FastAPI(title="GUARDiA Manager API", version="1.0.0")
|
||||
|
||||
app.add_middleware(
|
||||
CORSMiddleware,
|
||||
allow_origins=[os.environ.get("MANAGER_ALLOWED_ORIGINS", "http://localhost:5173")],
|
||||
allow_methods=["*"], allow_headers=["*"], allow_credentials=True,
|
||||
)
|
||||
|
||||
from routers import system, deploy, config
|
||||
app.include_router(system.router, prefix="/api/system", tags=["system"])
|
||||
app.include_router(deploy.router, prefix="/api/deploy", tags=["deploy"])
|
||||
app.include_router(config.router, prefix="/api/config", tags=["config"])
|
||||
|
||||
@app.get("/health")
|
||||
async def health():
|
||||
return {"status": "ok", "service": "guardia-manager"}
|
||||
```
|
||||
|
||||
## routers/system.py — 서버 상태 및 서비스 제어
|
||||
|
||||
```python
|
||||
import psutil, subprocess
|
||||
from fastapi import APIRouter, Depends, HTTPException
|
||||
from core.auth import verify_guardia_token
|
||||
|
||||
router = APIRouter()
|
||||
|
||||
ALLOWED_SERVICES = { # 허용된 서비스만 제어 가능
|
||||
"nginx", "zioinfo", "guardia", "gitea", "jenkins",
|
||||
"postgresql", "ollama", "zioinfo-deploy"
|
||||
}
|
||||
|
||||
@router.get("/resources")
|
||||
async def get_resources(user=Depends(verify_guardia_token)):
|
||||
"""CPU/메모리/디스크 현황"""
|
||||
return {
|
||||
"cpu_percent": psutil.cpu_percent(interval=0.1),
|
||||
"memory": {
|
||||
"total_gb": round(psutil.virtual_memory().total / 1e9, 1),
|
||||
"used_gb": round(psutil.virtual_memory().used / 1e9, 1),
|
||||
"percent": psutil.virtual_memory().percent,
|
||||
},
|
||||
"disk": {
|
||||
"total_gb": round(psutil.disk_usage('/').total / 1e9, 1),
|
||||
"used_gb": round(psutil.disk_usage('/').used / 1e9, 1),
|
||||
"percent": psutil.disk_usage('/').percent,
|
||||
},
|
||||
}
|
||||
|
||||
@router.get("/services")
|
||||
async def list_services(user=Depends(verify_guardia_token)):
|
||||
"""허용된 서비스 상태 목록"""
|
||||
result = {}
|
||||
for svc in ALLOWED_SERVICES:
|
||||
proc = subprocess.run(
|
||||
["systemctl", "is-active", svc],
|
||||
capture_output=True, text=True
|
||||
)
|
||||
result[svc] = proc.stdout.strip()
|
||||
return result
|
||||
|
||||
@router.post("/services/{name}/restart")
|
||||
async def restart_service(name: str, user=Depends(verify_guardia_token)):
|
||||
"""서비스 재시작 (admin 역할 필요)"""
|
||||
if user.get("role") != "admin":
|
||||
raise HTTPException(status_code=403, detail="admin 권한 필요")
|
||||
if name not in ALLOWED_SERVICES:
|
||||
raise HTTPException(status_code=400, detail="허용되지 않은 서비스")
|
||||
result = subprocess.run(
|
||||
["systemctl", "restart", name],
|
||||
capture_output=True, text=True
|
||||
)
|
||||
if result.returncode != 0:
|
||||
raise HTTPException(status_code=500, detail=result.stderr[:200])
|
||||
return {"message": f"{name} 재시작 완료"}
|
||||
```
|
||||
|
||||
## routers/deploy.py — 배포 트리거
|
||||
|
||||
```python
|
||||
import httpx
|
||||
from fastapi import APIRouter, Depends
|
||||
from core.auth import verify_guardia_token
|
||||
|
||||
router = APIRouter()
|
||||
DEPLOY_WEBHOOK = "http://localhost:9999/"
|
||||
|
||||
@router.post("/trigger/{repo}")
|
||||
async def trigger_deploy(repo: str, user=Depends(verify_guardia_token)):
|
||||
"""Deploy Webhook 수동 트리거"""
|
||||
async with httpx.AsyncClient() as client:
|
||||
resp = await client.post(DEPLOY_WEBHOOK,
|
||||
json={"repo": repo, "triggered_by": user.get("sub")},
|
||||
timeout=10)
|
||||
return {"status": resp.status_code, "message": "배포 트리거됨"}
|
||||
|
||||
@router.get("/history")
|
||||
async def deploy_history(user=Depends(verify_guardia_token)):
|
||||
"""배포 로그 마지막 100줄"""
|
||||
try:
|
||||
with open("/var/log/zioinfo/deploy.log") as f:
|
||||
lines = f.readlines()[-100:]
|
||||
return {"lines": lines}
|
||||
except FileNotFoundError:
|
||||
return {"lines": []}
|
||||
```
|
||||
|
||||
## routers/config.py — 설정 관리
|
||||
|
||||
```python
|
||||
import os, re
|
||||
from fastapi import APIRouter, Depends, HTTPException
|
||||
from core.auth import verify_guardia_token
|
||||
|
||||
router = APIRouter()
|
||||
ENV_PATH = "/opt/guardia/app/.env"
|
||||
SENSITIVE_KEYS = {"SECRET", "PASSWORD", "KEY", "TOKEN", "PASS"}
|
||||
|
||||
def mask_sensitive(key: str, value: str) -> str:
|
||||
"""보안 키 값 마스킹"""
|
||||
if any(s in key.upper() for s in SENSITIVE_KEYS):
|
||||
return "****"
|
||||
return value
|
||||
|
||||
@router.get("/env")
|
||||
async def get_env(user=Depends(verify_guardia_token)):
|
||||
"""환경변수 목록 (보안 값 마스킹)"""
|
||||
if user.get("role") != "admin":
|
||||
raise HTTPException(status_code=403, detail="admin 권한 필요")
|
||||
result = {}
|
||||
try:
|
||||
with open(ENV_PATH) as f:
|
||||
for line in f:
|
||||
line = line.strip()
|
||||
if line and not line.startswith("#") and "=" in line:
|
||||
key, _, val = line.partition("=")
|
||||
result[key.strip()] = mask_sensitive(key.strip(), val.strip())
|
||||
except FileNotFoundError:
|
||||
pass
|
||||
return result
|
||||
|
||||
@router.post("/nginx/reload")
|
||||
async def nginx_reload(user=Depends(verify_guardia_token)):
|
||||
"""Nginx 설정 테스트 후 리로드"""
|
||||
import subprocess
|
||||
test = subprocess.run(["nginx", "-t"], capture_output=True, text=True)
|
||||
if test.returncode != 0:
|
||||
raise HTTPException(status_code=400, detail=f"Nginx 설정 오류: {test.stderr[:200]}")
|
||||
subprocess.run(["systemctl", "reload", "nginx"])
|
||||
return {"message": "Nginx 리로드 완료"}
|
||||
```
|
||||
|
||||
## requirements.txt
|
||||
|
||||
```txt
|
||||
fastapi==0.115.0
|
||||
uvicorn[standard]==0.30.0
|
||||
httpx==0.27.0
|
||||
psutil==5.9.8
|
||||
python-jose[cryptography]==3.3.0
|
||||
python-dotenv==1.0.1
|
||||
```
|
||||
|
||||
## .env (Manager Backend)
|
||||
|
||||
```env
|
||||
MANAGER_PORT=8002
|
||||
GUARDIA_API=http://localhost:8001
|
||||
GUARDIA_JWT_SECRET=guardia-jwt-production-secret-2026-please-change
|
||||
MANAGER_ALLOWED_ORIGINS=http://localhost:5173,http://zioinfo.co.kr:8080
|
||||
```
|
||||
189
.claude/skills/manager-deploy/SKILL.md
Normal file
189
.claude/skills/manager-deploy/SKILL.md
Normal file
@ -0,0 +1,189 @@
|
||||
---
|
||||
name: manager-deploy
|
||||
description: >
|
||||
GUARDiA Manager 자체를 서버에 배포하고 CI/CD 파이프라인을 구성하는 스킬.
|
||||
Nginx 설정, systemd 서비스 등록, Gitea 저장소 생성, Jenkinsfile 작성을 포함한다.
|
||||
트리거: Manager 배포 설정, Nginx 구성, systemd 서비스 등록, CI/CD 파이프라인,
|
||||
Gitea 저장소 생성, Jenkinsfile 작성 요청 시.
|
||||
---
|
||||
|
||||
# GUARDiA Manager 배포 스킬
|
||||
|
||||
## 포트 계획
|
||||
|
||||
| 서비스 | 포트 | 설명 |
|
||||
|--------|------|------|
|
||||
| GUARDiA ITSM | 8001 | 기존 (변경 금지) |
|
||||
| Spring Boot (홈페이지) | 8082 | 기존 (변경 금지) |
|
||||
| Jenkins | 8080 | 기존 (변경 금지) |
|
||||
| **Manager Frontend** | **8090** | Nginx 신규 |
|
||||
| **Manager Backend** | **8002** | FastAPI 신규 |
|
||||
|
||||
## Nginx 설정 (/etc/nginx/sites-available/guardia-manager)
|
||||
|
||||
```nginx
|
||||
server {
|
||||
listen 8090;
|
||||
server_name _;
|
||||
|
||||
# React SPA
|
||||
root /var/www/manager;
|
||||
index index.html;
|
||||
|
||||
location / {
|
||||
try_files $uri $uri/ /index.html;
|
||||
add_header Cache-Control no-cache;
|
||||
}
|
||||
|
||||
# Manager Backend API 프록시
|
||||
location /api/ {
|
||||
proxy_pass http://127.0.0.1:8002;
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_read_timeout 60s;
|
||||
}
|
||||
|
||||
# GUARDiA ITSM API 프록시 (CORS 우회)
|
||||
location /guardia-api/ {
|
||||
proxy_pass http://127.0.0.1:8001/;
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
}
|
||||
|
||||
# 정적 파일 캐시
|
||||
location ~* \.(js|css|png|jpg|ico|svg|woff2)$ {
|
||||
expires 7d;
|
||||
add_header Cache-Control "public, immutable";
|
||||
}
|
||||
|
||||
gzip on;
|
||||
gzip_types text/plain text/css application/javascript application/json;
|
||||
}
|
||||
```
|
||||
|
||||
## systemd 서비스 (Manager Backend)
|
||||
|
||||
```ini
|
||||
# /etc/systemd/system/guardia-manager.service
|
||||
[Unit]
|
||||
Description=GUARDiA Manager API Backend
|
||||
After=network.target guardia.service
|
||||
|
||||
[Service]
|
||||
User=root
|
||||
WorkingDirectory=/opt/manager/backend
|
||||
EnvironmentFile=-/opt/manager/backend/.env
|
||||
ExecStart=/opt/manager/venv/bin/uvicorn main:app --host 127.0.0.1 --port 8002 --workers 1
|
||||
Restart=on-failure
|
||||
RestartSec=5
|
||||
StandardOutput=append:/var/log/guardia-manager/backend.log
|
||||
StandardError=append:/var/log/guardia-manager/backend.log
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
```
|
||||
|
||||
## 배포 디렉터리 구조 (서버)
|
||||
|
||||
```
|
||||
/opt/manager/
|
||||
├── backend/ ← FastAPI 소스
|
||||
│ ├── main.py
|
||||
│ ├── .env
|
||||
│ └── routers/
|
||||
└── venv/ ← Python 가상환경
|
||||
|
||||
/var/www/manager/ ← React 빌드 결과물
|
||||
/var/log/guardia-manager/
|
||||
└── backend.log
|
||||
```
|
||||
|
||||
## Jenkinsfile
|
||||
|
||||
```groovy
|
||||
pipeline {
|
||||
agent any
|
||||
environment {
|
||||
DEPLOY_FRONTEND = '/var/www/manager'
|
||||
DEPLOY_BACKEND = '/opt/manager/backend'
|
||||
VENV = '/opt/manager/venv'
|
||||
}
|
||||
stages {
|
||||
stage('Checkout') {
|
||||
steps { checkout scm }
|
||||
}
|
||||
stage('Frontend Build') {
|
||||
steps {
|
||||
dir('frontend') {
|
||||
sh 'npm ci --prefer-offline 2>/dev/null || npm install'
|
||||
sh 'npm run build'
|
||||
sh "cp -r dist/. ${DEPLOY_FRONTEND}/"
|
||||
}
|
||||
}
|
||||
}
|
||||
stage('Backend Deploy') {
|
||||
steps {
|
||||
sh """
|
||||
cp -r backend/. ${DEPLOY_BACKEND}/
|
||||
${VENV}/bin/pip install -r ${DEPLOY_BACKEND}/requirements.txt -q
|
||||
systemctl restart guardia-manager
|
||||
sleep 3
|
||||
systemctl is-active guardia-manager
|
||||
"""
|
||||
}
|
||||
}
|
||||
stage('Health Check') {
|
||||
steps {
|
||||
sh 'curl -sf http://localhost:8002/health | grep ok'
|
||||
sh 'curl -sf http://localhost:8090/ -o /dev/null -w "HTTP %{http_code}"'
|
||||
}
|
||||
}
|
||||
}
|
||||
post {
|
||||
success { echo "Manager 배포 완료" }
|
||||
failure { echo "배포 실패 — 로그 확인" }
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## 서버 초기 설정 스크립트
|
||||
|
||||
```bash
|
||||
#!/bin/bash
|
||||
# deploy/init_server.sh — 최초 1회 실행
|
||||
|
||||
# 디렉터리 생성
|
||||
mkdir -p /opt/manager/backend /var/www/manager /var/log/guardia-manager
|
||||
|
||||
# Python 가상환경
|
||||
python3 -m venv /opt/manager/venv
|
||||
/opt/manager/venv/bin/pip install -r /opt/manager/backend/requirements.txt -q
|
||||
|
||||
# Nginx 설정
|
||||
ln -sf /etc/nginx/sites-available/guardia-manager /etc/nginx/sites-enabled/
|
||||
nginx -t && systemctl reload nginx
|
||||
|
||||
# systemd 등록
|
||||
systemctl daemon-reload
|
||||
systemctl enable guardia-manager
|
||||
systemctl start guardia-manager
|
||||
|
||||
# UFW 포트 오픈
|
||||
ufw allow 8090/tcp comment 'GUARDiA Manager'
|
||||
ufw allow 8002/tcp comment 'Manager Backend'
|
||||
|
||||
echo "Manager 초기 설정 완료"
|
||||
echo "접속: http://zioinfo.co.kr:8090"
|
||||
```
|
||||
|
||||
## Gitea 저장소 생성
|
||||
|
||||
```bash
|
||||
# 서버에서 실행
|
||||
curl -s -X POST http://localhost:3000/api/v1/user/repos \
|
||||
-H "Content-Type: application/json" \
|
||||
-u "zio:Zio@Admin2026!" \
|
||||
-d '{"name":"guardia-manager","description":"GUARDiA 관리자 시스템","private":false,"auto_init":true}'
|
||||
```
|
||||
203
.claude/skills/manager-integration/SKILL.md
Normal file
203
.claude/skills/manager-integration/SKILL.md
Normal file
@ -0,0 +1,203 @@
|
||||
---
|
||||
name: manager-integration
|
||||
description: >
|
||||
GUARDiA Manager와 외부 시스템(GUARDiA ITSM, Gitea, Ollama) 연동 코드를 구현하는 스킬.
|
||||
API 클라이언트, TypeScript 타입, React 훅을 생성한다.
|
||||
트리거: API 연동 구현, axios 클라이언트 작성, GUARDiA API 호출, Gitea 연동,
|
||||
Ollama 모델 조회, 데이터 페칭 훅 작성 요청 시.
|
||||
---
|
||||
|
||||
# GUARDiA Manager 연동 구현 스킬
|
||||
|
||||
## 연동 대상 API 목록
|
||||
|
||||
### GUARDiA ITSM (http://zioinfo.co.kr:8001)
|
||||
|
||||
| 엔드포인트 | 용도 | 필요 권한 |
|
||||
|-----------|------|---------|
|
||||
| `POST /api/auth/login` | 로그인 | 없음 |
|
||||
| `GET /api/dashboard` | 대시보드 통계 | 로그인 |
|
||||
| `GET /api/tasks` | SR 목록 | 로그인 |
|
||||
| `GET /api/incidents` | 인시던트 목록 | 로그인 |
|
||||
| `GET /api/cmdb/servers` | 서버 자산 목록 | 로그인 |
|
||||
| `GET /api/tenant` | 테넌트 목록 | admin |
|
||||
| `GET /api/external/keys` | API Key 목록 | admin |
|
||||
| `GET /api/audit` | 감사 로그 | admin |
|
||||
| `GET /api/metrics` | Prometheus 메트릭 | 로그인 |
|
||||
|
||||
### Gitea (http://zioinfo.co.kr:3000/api/v1)
|
||||
|
||||
| 엔드포인트 | 용도 |
|
||||
|-----------|------|
|
||||
| `GET /repos/search` | 저장소 목록 |
|
||||
| `GET /repos/{user}/{repo}/commits` | 최신 커밋 |
|
||||
| `GET /repos/{user}/{repo}/hooks` | 웹훅 목록 |
|
||||
|
||||
### Ollama (http://localhost:11434)
|
||||
|
||||
| 엔드포인트 | 용도 |
|
||||
|-----------|------|
|
||||
| `GET /api/tags` | 설치된 모델 목록 |
|
||||
| `GET /api/ps` | 로드된 모델 |
|
||||
|
||||
---
|
||||
|
||||
## API 클라이언트 구현
|
||||
|
||||
### guardiaClient.ts
|
||||
|
||||
```typescript
|
||||
// frontend/src/api/guardiaClient.ts
|
||||
import axios from 'axios';
|
||||
|
||||
const BASE = import.meta.env.VITE_GUARDIA_API ?? 'http://zioinfo.co.kr:8001';
|
||||
|
||||
export const guardiaApi = axios.create({ baseURL: BASE });
|
||||
|
||||
// 요청 인터셉터: JWT 자동 주입
|
||||
guardiaApi.interceptors.request.use((config) => {
|
||||
const token = sessionStorage.getItem('guardia_token');
|
||||
if (token) config.headers.Authorization = `Bearer ${token}`;
|
||||
return config;
|
||||
});
|
||||
|
||||
// 응답 인터셉터: 401 → 로그인 리다이렉트
|
||||
guardiaApi.interceptors.response.use(
|
||||
res => res,
|
||||
err => {
|
||||
if (err.response?.status === 401) {
|
||||
sessionStorage.removeItem('guardia_token');
|
||||
window.location.href = '/login';
|
||||
}
|
||||
return Promise.reject(err);
|
||||
}
|
||||
);
|
||||
```
|
||||
|
||||
### types.ts — 핵심 타입 정의
|
||||
|
||||
```typescript
|
||||
// frontend/src/api/types.ts
|
||||
|
||||
export interface User {
|
||||
id: number;
|
||||
username: string;
|
||||
display_name: string;
|
||||
role: 'admin' | 'pm' | 'engineer' | 'customer';
|
||||
is_active: boolean;
|
||||
}
|
||||
|
||||
export interface Server {
|
||||
id: number;
|
||||
hostname: string;
|
||||
ip_addr: string; // ServerOut 스키마에서 제외됨 — 실제 응답에 없을 수 있음
|
||||
os_type: string;
|
||||
status: 'running' | 'stopped' | 'error' | 'unknown';
|
||||
inst_id?: number;
|
||||
}
|
||||
|
||||
export interface SRRequest {
|
||||
id: number;
|
||||
sr_id: string;
|
||||
title: string;
|
||||
status: string;
|
||||
priority: 'CRITICAL' | 'HIGH' | 'MEDIUM' | 'LOW';
|
||||
requested_by: string;
|
||||
created_at: string;
|
||||
}
|
||||
|
||||
export interface DashboardStats {
|
||||
total_sr: number;
|
||||
open_sr: number;
|
||||
critical_incidents: number;
|
||||
sla_achievement: number;
|
||||
server_count: number;
|
||||
running_servers: number;
|
||||
}
|
||||
|
||||
export interface APIKey {
|
||||
id: number;
|
||||
name: string;
|
||||
scopes: string;
|
||||
is_active: boolean;
|
||||
use_count: number;
|
||||
last_used_at: string | null;
|
||||
expires_at: string | null;
|
||||
}
|
||||
|
||||
export interface AuditLog {
|
||||
id: number;
|
||||
action: string;
|
||||
entity_type: string;
|
||||
entity_id: string;
|
||||
username: string;
|
||||
ip_hash: string;
|
||||
created_at: string;
|
||||
}
|
||||
|
||||
export interface SystemResources {
|
||||
cpu_percent: number;
|
||||
memory: { total_gb: number; used_gb: number; percent: number };
|
||||
disk: { total_gb: number; used_gb: number; percent: number };
|
||||
}
|
||||
|
||||
export interface ServiceStatus {
|
||||
[serviceName: string]: 'active' | 'inactive' | 'failed' | 'unknown';
|
||||
}
|
||||
```
|
||||
|
||||
### React 훅 — useGuardiaApi.ts
|
||||
|
||||
```typescript
|
||||
// frontend/src/hooks/useGuardiaApi.ts
|
||||
import { useState, useEffect, useCallback } from 'react';
|
||||
import { guardiaApi } from '../api/guardiaClient';
|
||||
|
||||
export function useGuardiaApi<T>(
|
||||
url: string,
|
||||
options: { immediate?: boolean; deps?: unknown[] } = {}
|
||||
) {
|
||||
const { immediate = true, deps = [] } = options;
|
||||
const [data, setData] = useState<T | null>(null);
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
|
||||
const fetch = useCallback(async () => {
|
||||
setLoading(true); setError(null);
|
||||
try {
|
||||
const res = await guardiaApi.get<T>(url);
|
||||
setData(res.data);
|
||||
} catch (e: any) {
|
||||
setError(e.response?.data?.detail ?? '오류가 발생했습니다.');
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
}, [url]);
|
||||
|
||||
useEffect(() => { if (immediate) fetch(); }, [fetch, immediate, ...deps]);
|
||||
|
||||
return { data, loading, error, refetch: fetch };
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 환경변수 설정
|
||||
|
||||
```typescript
|
||||
// frontend/src/config/env.ts
|
||||
export const ENV = {
|
||||
GUARDIA_API: import.meta.env.VITE_GUARDIA_API ?? 'http://zioinfo.co.kr:8001',
|
||||
MANAGER_API: import.meta.env.VITE_MANAGER_API ?? 'http://localhost:8002',
|
||||
GITEA_API: import.meta.env.VITE_GITEA_API ?? 'http://zioinfo.co.kr:3000/api/v1',
|
||||
GITEA_USER: import.meta.env.VITE_GITEA_USER ?? 'zio',
|
||||
} as const;
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 주의사항
|
||||
|
||||
- **ServerOut 스키마**: GUARDiA ITSM은 보안 정책으로 `ip_addr`, `ssh_user`, `os_pw_enc` 필드를 API 응답에서 제외한다. 이 필드를 프론트엔드에 표시하려면 ITSM 쪽에 별도 admin 엔드포인트가 필요하다.
|
||||
- **인증 토큰 저장**: `localStorage` 금지. `sessionStorage` 또는 메모리 변수 사용.
|
||||
- **Gitea CORS**: Gitea API를 직접 호출하면 CORS 오류 발생 가능. Manager Backend의 프록시 라우터(`/api/proxy/gitea`) 를 거쳐 호출한다.
|
||||
196
.claude/skills/manager-security/SKILL.md
Normal file
196
.claude/skills/manager-security/SKILL.md
Normal file
@ -0,0 +1,196 @@
|
||||
---
|
||||
name: manager-security
|
||||
description: >
|
||||
GUARDiA Manager의 인증, 권한 제어, 보안 설정을 구현하는 스킬.
|
||||
GUARDiA ITSM JWT 재활용, React Route Guard, API Key 관리 UI,
|
||||
감사 로그 시각화를 포함한다.
|
||||
트리거: 인증 구현, 로그인 페이지, Route Guard, JWT 검증,
|
||||
API Key 발급/관리, 감사 로그 화면, 보안 설정 요청 시.
|
||||
---
|
||||
|
||||
# GUARDiA Manager 보안 구현 스킬
|
||||
|
||||
## 인증 아키텍처
|
||||
|
||||
GUARDiA Manager는 별도 인증 서버 없이 **GUARDiA ITSM JWT를 공유**한다.
|
||||
|
||||
```
|
||||
1. 사용자 → Manager 로그인 페이지
|
||||
2. POST /api/auth/login (GUARDiA ITSM 8001)
|
||||
3. JWT 수신 → sessionStorage 저장 (localStorage 금지)
|
||||
4. 이후 모든 요청: Authorization: Bearer {token}
|
||||
5. GUARDiA ITSM과 Manager Backend 양쪽에서 동일 JWT 검증
|
||||
```
|
||||
|
||||
## useAuth 훅
|
||||
|
||||
```typescript
|
||||
// frontend/src/hooks/useAuth.ts
|
||||
import { useState, useEffect, createContext, useContext } from 'react';
|
||||
import { guardiaApi } from '../api/guardiaClient';
|
||||
|
||||
interface AuthState {
|
||||
user: { username: string; role: string; display_name: string } | null;
|
||||
token: string | null;
|
||||
loading: boolean;
|
||||
}
|
||||
|
||||
export function useAuth() {
|
||||
const [state, setState] = useState<AuthState>({
|
||||
user: null, token: sessionStorage.getItem('guardia_token'), loading: true
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
const token = sessionStorage.getItem('guardia_token');
|
||||
if (!token) { setState(s => ({ ...s, loading: false })); return; }
|
||||
guardiaApi.get('/api/auth/me')
|
||||
.then(res => setState({ user: res.data, token, loading: false }))
|
||||
.catch(() => {
|
||||
sessionStorage.removeItem('guardia_token');
|
||||
setState({ user: null, token: null, loading: false });
|
||||
});
|
||||
}, []);
|
||||
|
||||
const login = async (username: string, password: string) => {
|
||||
const res = await guardiaApi.post('/api/auth/login',
|
||||
new URLSearchParams({ username, password }),
|
||||
{ headers: { 'Content-Type': 'application/x-www-form-urlencoded' } }
|
||||
);
|
||||
const { access_token, user } = res.data;
|
||||
sessionStorage.setItem('guardia_token', access_token);
|
||||
setState({ user, token: access_token, loading: false });
|
||||
return user;
|
||||
};
|
||||
|
||||
const logout = () => {
|
||||
sessionStorage.removeItem('guardia_token');
|
||||
setState({ user: null, token: null, loading: false });
|
||||
window.location.href = '/login';
|
||||
};
|
||||
|
||||
return { ...state, login, logout };
|
||||
}
|
||||
```
|
||||
|
||||
## Route Guard 컴포넌트
|
||||
|
||||
```tsx
|
||||
// components/common/ProtectedRoute.tsx
|
||||
import { Navigate, useLocation } from 'react-router-dom';
|
||||
import { useAuth } from '../../hooks/useAuth';
|
||||
|
||||
const ADMIN_ONLY_PATHS = [
|
||||
'/api-keys', '/config', '/config/env', '/config/nginx'
|
||||
];
|
||||
|
||||
export function ProtectedRoute({ children }: { children: React.ReactNode }) {
|
||||
const { user, token, loading } = useAuth();
|
||||
const location = useLocation();
|
||||
|
||||
if (loading) return <div style={{ padding: 40 }}>인증 확인 중...</div>;
|
||||
if (!token || !user) return <Navigate to="/login" state={{ from: location }} replace />;
|
||||
|
||||
// admin 전용 페이지 접근 제어
|
||||
if (ADMIN_ONLY_PATHS.some(p => location.pathname.startsWith(p))
|
||||
&& user.role !== 'admin') {
|
||||
return <div style={{ padding: 40, color: '#ef4444' }}>
|
||||
관리자 권한이 필요합니다.
|
||||
</div>;
|
||||
}
|
||||
|
||||
return <>{children}</>;
|
||||
}
|
||||
```
|
||||
|
||||
## API Key 관리 화면 (M-05)
|
||||
|
||||
NCloud 콘솔의 "API 인증키 관리" 화면을 참조:
|
||||
- 테이블: 키 이름, 스코프, 마지막 사용, 만료일, 상태
|
||||
- 발급: 슬라이드 패널 → 이름/스코프/IP 제한 입력 → 발급 시 키 1회 표시
|
||||
- 비활성화: 확인 모달 → `DELETE /api/external/keys/{id}`
|
||||
|
||||
```tsx
|
||||
// pages/ApiKeys.tsx 핵심 구조
|
||||
export function ApiKeys() {
|
||||
const { data, loading, refetch } = useGuardiaApi<APIKey[]>('/api/external/keys');
|
||||
const [creating, setCreating] = useState(false);
|
||||
const [newKey, setNewKey] = useState<string | null>(null);
|
||||
|
||||
const handleCreate = async (form: { name: string; scopes: string; expires_days: number }) => {
|
||||
const res = await guardiaApi.post('/api/external/keys', form);
|
||||
setNewKey(res.data.api_key); // 1회만 노출
|
||||
refetch();
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
{/* 발급된 키 1회 노출 모달 */}
|
||||
{newKey && (
|
||||
<div className="one-time-key-modal">
|
||||
<h4>⚠️ API Key가 발급되었습니다 — 지금만 확인 가능합니다</h4>
|
||||
<code style={{ userSelect: 'all', background: '#f0f2f5', padding: '10px 14px',
|
||||
borderRadius: 6, display: 'block', wordBreak: 'break-all' }}>{newKey}</code>
|
||||
<button onClick={() => { navigator.clipboard.writeText(newKey); }}>복사</button>
|
||||
<button onClick={() => setNewKey(null)}>확인 완료</button>
|
||||
</div>
|
||||
)}
|
||||
<DataTable
|
||||
columns={[
|
||||
{ key: 'name', header: '키 이름' },
|
||||
{ key: 'scopes', header: '권한' },
|
||||
{ key: 'use_count', header: '사용 횟수' },
|
||||
{ key: 'is_active', header: '상태',
|
||||
render: r => <StatusBadge status={r.is_active ? 'running' : 'stopped'} /> },
|
||||
{ key: 'expires_at', header: '만료일' },
|
||||
]}
|
||||
data={data ?? []}
|
||||
loading={loading}
|
||||
actions={<button onClick={() => setCreating(true)}>+ API Key 발급</button>}
|
||||
selectable
|
||||
/>
|
||||
</>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
## 감사 로그 화면 (M-05)
|
||||
|
||||
```tsx
|
||||
// pages/AuditLog.tsx
|
||||
// - 타임라인 형태로 감사 이벤트 표시 (NCloud "활동 로그" 참조)
|
||||
// - 필터: 날짜 범위, 사용자, 액션 유형
|
||||
// - 데이터: GET /api/audit?page=0&size=50
|
||||
// - IP Hash 표시 (원본 IP는 ITSM 보안 정책상 제공 안 함)
|
||||
```
|
||||
|
||||
## Manager Backend JWT 검증 (core/auth.py)
|
||||
|
||||
```python
|
||||
# backend/core/auth.py
|
||||
import os
|
||||
from fastapi import Depends, HTTPException
|
||||
from fastapi.security import OAuth2PasswordBearer
|
||||
from jose import JWTError, jwt
|
||||
|
||||
SECRET = os.environ.get("GUARDIA_JWT_SECRET", "guardia-jwt-secret-2026-change-me!")
|
||||
ALGORITHM = "HS256"
|
||||
oauth2 = OAuth2PasswordBearer(tokenUrl="/api/auth/login", auto_error=False)
|
||||
|
||||
async def verify_guardia_token(token: str = Depends(oauth2)):
|
||||
if not token:
|
||||
raise HTTPException(status_code=401, detail="인증이 필요합니다.")
|
||||
try:
|
||||
payload = jwt.decode(token, SECRET, algorithms=[ALGORITHM])
|
||||
return payload
|
||||
except JWTError:
|
||||
raise HTTPException(status_code=401, detail="유효하지 않은 토큰입니다.")
|
||||
```
|
||||
|
||||
## 보안 체크리스트
|
||||
|
||||
- [ ] localStorage에 토큰 저장 금지 (sessionStorage 사용)
|
||||
- [ ] API Key, 비밀번호 화면 표시 마스킹 (`****`)
|
||||
- [ ] 클립보드 복사 후 즉시 메모리 해제
|
||||
- [ ] admin 전용 라우트 보호 완료
|
||||
- [ ] CORS 출처 명시적 설정 (wildcard 금지)
|
||||
- [ ] 모든 시스템 제어 API에 admin 역할 검증
|
||||
189
.claude/skills/manager-ui/SKILL.md
Normal file
189
.claude/skills/manager-ui/SKILL.md
Normal file
@ -0,0 +1,189 @@
|
||||
---
|
||||
name: manager-ui
|
||||
description: >
|
||||
GUARDiA 관리자 시스템 React UI를 구현하는 스킬.
|
||||
네이버 클라우드 콘솔(NCloud Console) 디자인 패턴을 참조하여
|
||||
대시보드, CMDB, 배포 관리, 사용자 관리 등 관리자 화면을 구축한다.
|
||||
트리거: 관리자 UI 구현, 페이지/컴포넌트 작성, 화면 설계, 'M-0X 화면',
|
||||
네이버 클라우드 스타일 적용 요청 시.
|
||||
---
|
||||
|
||||
# GUARDiA Manager UI 구현 스킬
|
||||
|
||||
## 기술 스택
|
||||
|
||||
```json
|
||||
{
|
||||
"framework": "React 18 + TypeScript + Vite",
|
||||
"styling": "CSS Modules (Tailwind 선택 가능)",
|
||||
"routing": "react-router-dom v6",
|
||||
"state": "useState/useContext (Redux 금지 — 과함)",
|
||||
"http": "axios 또는 fetch API",
|
||||
"charts": "recharts (경량)",
|
||||
"icons": "lucide-react"
|
||||
}
|
||||
```
|
||||
|
||||
## 네이버 클라우드 콘솔 디자인 패턴
|
||||
|
||||
자세한 컴포넌트 패턴은 `references/ncloud-patterns.md` 참조.
|
||||
|
||||
### 필수 레이아웃 구조
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────┐
|
||||
│ GNB (Global Navigation Bar) — 60px 고정 │
|
||||
│ [GUARDiA Manager] [서비스 검색] ─────── [알림] [계정] │
|
||||
├──────────────┬──────────────────────────────────────────┤
|
||||
│ │ │
|
||||
│ 사이드바 │ 콘텐츠 영역 │
|
||||
│ (220px) │ ┌─ 브레드크럼 ──────────────────────┐ │
|
||||
│ │ │ Home > 서버 > 서버 목록 │ │
|
||||
│ ▸ 대시보드 │ └─────────────────────────────────────┘ │
|
||||
│ ▾ 인프라 │ ┌─ 페이지 타이틀 + 액션 버튼 ─────────┐ │
|
||||
│ 서버 관리 │ │ 서버 목록 [+서버 추가] │ │
|
||||
│ CMDB │ └─────────────────────────────────────┘ │
|
||||
│ ▾ 배포 │ ┌─ 콘텐츠 ────────────────────────────┐ │
|
||||
│ 배포 이력 │ │ 테이블 / 카드 / 폼 │ │
|
||||
│ ▾ 보안 │ └─────────────────────────────────────┘ │
|
||||
│ API Keys │ │
|
||||
│ 감사 로그 │ │
|
||||
│ ▾ 시스템 │ │
|
||||
│ 설정 │ │
|
||||
│ LLM 관리 │ │
|
||||
└──────────────┴──────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
### 색상 시스템 (GUARDiA 브랜드 + NCloud 패턴)
|
||||
|
||||
```css
|
||||
:root {
|
||||
/* GUARDiA 브랜드 (NCloud의 #03C75A 대신 적용) */
|
||||
--brand-primary: #1a3a6b;
|
||||
--brand-accent: #4f6ef7;
|
||||
--brand-light: #e8ecff;
|
||||
|
||||
/* NCloud 콘솔 참조 레이아웃 색상 */
|
||||
--gnb-bg: #1a1d2e; /* 상단 바 */
|
||||
--sidebar-bg: #f5f7fa; /* 사이드바 */
|
||||
--sidebar-hover: #eaeef5;
|
||||
--content-bg: #f0f2f5; /* 콘텐츠 배경 */
|
||||
--card-bg: #ffffff;
|
||||
--border: #e2e8f0;
|
||||
|
||||
/* 상태 색상 (NCloud 리소스 상태 배지와 동일) */
|
||||
--status-running: #00c853; /* 실행중 */
|
||||
--status-stopped: #9e9e9e; /* 중지 */
|
||||
--status-error: #f44336; /* 오류 */
|
||||
--status-pending: #ff9800; /* 진행중 */
|
||||
|
||||
/* 텍스트 */
|
||||
--text-primary: #1e293b;
|
||||
--text-secondary: #64748b;
|
||||
--text-muted: #94a3b8;
|
||||
}
|
||||
```
|
||||
|
||||
### 리소스 상태 배지 (NCloud 스타일)
|
||||
|
||||
```tsx
|
||||
// StatusBadge.tsx — NCloud 콘솔의 상태 표시와 동일한 패턴
|
||||
type Status = 'running' | 'stopped' | 'error' | 'pending' | 'creating';
|
||||
const STATUS_MAP = {
|
||||
running: { label: '실행중', color: 'var(--status-running)', dot: true },
|
||||
stopped: { label: '중지', color: 'var(--status-stopped)', dot: true },
|
||||
error: { label: '오류', color: 'var(--status-error)', dot: true },
|
||||
pending: { label: '진행중', color: 'var(--status-pending)', dot: true },
|
||||
creating: { label: '생성중', color: 'var(--status-pending)', dot: true },
|
||||
};
|
||||
```
|
||||
|
||||
### 데이터 테이블 (NCloud 리소스 목록 스타일)
|
||||
|
||||
NCloud 콘솔의 리소스 목록과 동일한 패턴:
|
||||
- 체크박스 선택 (벌크 액션)
|
||||
- 상단 검색 + 필터
|
||||
- 컬럼 헤더 정렬
|
||||
- 페이지네이션 또는 무한 스크롤
|
||||
- 행 클릭 → 상세 슬라이드 패널 (모달 아님)
|
||||
|
||||
자세한 컴포넌트 코드: `references/ncloud-patterns.md` 참조
|
||||
|
||||
## 프로젝트 구조
|
||||
|
||||
```
|
||||
frontend/
|
||||
├── src/
|
||||
│ ├── App.tsx
|
||||
│ ├── main.tsx
|
||||
│ ├── layouts/
|
||||
│ │ ├── AppLayout.tsx ← GNB + 사이드바 + 콘텐츠
|
||||
│ │ └── AuthLayout.tsx ← 로그인 페이지 레이아웃
|
||||
│ ├── pages/
|
||||
│ │ ├── Dashboard.tsx ← M-01 통합 대시보드
|
||||
│ │ ├── Users.tsx ← M-02 사용자 관리
|
||||
│ │ ├── Servers.tsx ← M-03 서버 자산 목록
|
||||
│ │ ├── ServerDetail.tsx ← M-03 서버 상세
|
||||
│ │ ├── Deployments.tsx ← M-04 배포 이력
|
||||
│ │ ├── ApiKeys.tsx ← M-05 API Key 관리
|
||||
│ │ ├── AuditLog.tsx ← M-05 감사 로그
|
||||
│ │ ├── LLMManager.tsx ← M-06 LLM 관리
|
||||
│ │ └── SystemConfig.tsx ← M-07 시스템 설정
|
||||
│ ├── components/
|
||||
│ │ ├── layout/
|
||||
│ │ │ ├── GNB.tsx
|
||||
│ │ │ ├── Sidebar.tsx
|
||||
│ │ │ └── Breadcrumb.tsx
|
||||
│ │ ├── common/
|
||||
│ │ │ ├── DataTable.tsx ← NCloud 스타일 테이블
|
||||
│ │ │ ├── StatusBadge.tsx ← 상태 배지
|
||||
│ │ │ ├── ResourceCard.tsx ← 서버/서비스 카드
|
||||
│ │ │ ├── SlidePanel.tsx ← 우측 슬라이드 패널
|
||||
│ │ │ ├── StatCard.tsx ← 통계 카드
|
||||
│ │ │ └── EmptyState.tsx
|
||||
│ │ └── dashboard/
|
||||
│ │ ├── SRStatusChart.tsx
|
||||
│ │ ├── ServerStatusGrid.tsx
|
||||
│ │ └── DeployTimeline.tsx
|
||||
│ ├── hooks/
|
||||
│ │ ├── useAuth.ts ← JWT 인증 훅
|
||||
│ │ ├── useGuardiaApi.ts ← GUARDiA ITSM API 훅
|
||||
│ │ └── useManagerApi.ts ← Manager Backend API 훅
|
||||
│ └── api/
|
||||
│ ├── guardiaClient.ts ← GUARDiA ITSM axios 인스턴스
|
||||
│ ├── managerClient.ts ← Manager Backend axios 인스턴스
|
||||
│ └── types.ts ← TypeScript 타입 정의
|
||||
├── index.html
|
||||
├── vite.config.ts
|
||||
└── package.json
|
||||
```
|
||||
|
||||
## M-01 대시보드 구현 가이드
|
||||
|
||||
NCloud 콘솔의 "서비스 요약" 화면을 참조한 통합 대시보드:
|
||||
|
||||
```
|
||||
┌── 상태 카드 행 ──────────────────────────────────────────────┐
|
||||
│ [SR 현황] [인시던트] [서버 수] [SLA 달성률] │
|
||||
│ 총 24건 긴급 2건 12대 98.7% │
|
||||
│ 진행중 8 ↗ 중 1건 실행중 10 ▲ 0.3% │
|
||||
└──────────────────────────────────────────────────────────────┘
|
||||
┌── SR 추이 차트 ────────┐ ┌── 서버 상태 그리드 ───────────────┐
|
||||
│ 7일 SR 생성/완료 추이 │ │ web-01 ● web-02 ● db-01 ● │
|
||||
│ [꺾은선 그래프] │ │ was-01 ● was-02 ● ksb-01 ● │
|
||||
└────────────────────────┘ └──────────────────────────────────┘
|
||||
┌── 최근 배포 이력 ──────────────────────────────────────────────┐
|
||||
│ [시간] [저장소] [커밋] [상태] [담당자] │
|
||||
│ 5분 전 zio/zioinfo-web abc1234 ✅ 성공 CI Bot │
|
||||
└──────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
## 환경변수 설정
|
||||
|
||||
```env
|
||||
# frontend/.env
|
||||
VITE_GUARDIA_API=http://zioinfo.co.kr:8001
|
||||
VITE_MANAGER_API=http://localhost:8002
|
||||
VITE_GITEA_API=http://zioinfo.co.kr:3000/api/v1
|
||||
VITE_APP_TITLE=GUARDiA Manager
|
||||
```
|
||||
297
.claude/skills/manager-ui/references/dashboard-charts.md
Normal file
297
.claude/skills/manager-ui/references/dashboard-charts.md
Normal file
@ -0,0 +1,297 @@
|
||||
# 메인 대시보드 차트 구성 가이드
|
||||
|
||||
> 관리자 시스템 메인화면은 대시보드 차트로 구성한다.
|
||||
> 네이버 클라우드 콘솔의 "서비스 사용 현황" 및 "리소스 모니터링" 화면을 참조한다.
|
||||
|
||||
## 메인 대시보드 레이아웃
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────────────────────┐
|
||||
│ GUARDiA Manager — 통합 운영 대시보드 마지막 갱신: 2분 전 [↺ 새로고침] │
|
||||
├─────────────────────────────────────────────────────────────────────────┤
|
||||
│ │
|
||||
│ ┌── 핵심 지표 카드 (4개) ─────────────────────────────────────────────┐ │
|
||||
│ │ [SR 현황] [인시던트] [서버 가용률] [SLA 달성률] │ │
|
||||
│ │ 24건 긴급 2건 91.7% 98.7% │ │
|
||||
│ │ 진행중 8↗ 해결중 3 11/12대 실행 ▲ 0.3% │ │
|
||||
│ └──────────────────────────────────────────────────────────────────┘ │
|
||||
│ │
|
||||
│ ┌── SR 추이 꺾은선 차트 ─────────┐ ┌── 서버 상태 도넛 차트 ──────────┐ │
|
||||
│ │ 7일간 SR 생성 vs 완료 │ │ │ │
|
||||
│ │ │ │ 실행중 10 │ │
|
||||
│ │ ╭──╮ │ │ ●────────── │ │
|
||||
│ │ │ ╰──╮ ╭─── │ │ ○ 중지 1 │ │
|
||||
│ │ │ ╰────╯ │ │ ● 오류 1 │ │
|
||||
│ │ └────────────────────────── │ │ │ │
|
||||
│ │ 월 화 수 목 금 토 일 │ │ 12대 서버 │ │
|
||||
│ └────────────────────────────────┘ └──────────────────────────────┘ │
|
||||
│ │
|
||||
│ ┌── 리소스 모니터링 막대 차트 ──────────────────────────────────────────┐ │
|
||||
│ │ CPU ██████████░░ 62% 메모리 ████████████░ 78% 디스크 ████░ 16% │ │
|
||||
│ └──────────────────────────────────────────────────────────────────┘ │
|
||||
│ │
|
||||
│ ┌── 최근 배포 이력 타임라인 ────────┐ ┌── AI/LLM 사용 현황 ───────────┐ │
|
||||
│ │ 5분전 ✅ zioinfo-web v1.2.1 │ │ llama3:8b 응답시간 2.3s │ │
|
||||
│ │ 1시간 ✅ guardia-itsm v2.0.4 │ │ 요청 124건/오늘 │ │
|
||||
│ │ 3시간 ❌ zioinfo-web (실패) │ │ [████████░░] 메모리 4.7GB │ │
|
||||
│ └────────────────────────────────┘ └──────────────────────────────┘ │
|
||||
└─────────────────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 차트 라이브러리 선택: Recharts
|
||||
|
||||
```bash
|
||||
npm install recharts
|
||||
```
|
||||
|
||||
**선택 이유:** NCloud 콘솔과 유사한 심플한 차트 스타일, React 친화적, 번들 크기 적당.
|
||||
|
||||
---
|
||||
|
||||
## 1. SR 추이 꺾은선 차트 (SRTrendChart)
|
||||
|
||||
```tsx
|
||||
// components/dashboard/SRTrendChart.tsx
|
||||
import { LineChart, Line, XAxis, YAxis, CartesianGrid, Tooltip, Legend, ResponsiveContainer } from 'recharts';
|
||||
|
||||
interface SRTrendData {
|
||||
date: string; // 'MM/DD'
|
||||
created: number;
|
||||
completed: number;
|
||||
}
|
||||
|
||||
export function SRTrendChart({ data }: { data: SRTrendData[] }) {
|
||||
return (
|
||||
<div style={{ background: '#fff', borderRadius: 10, padding: '20px 24px',
|
||||
border: '1px solid var(--border)' }}>
|
||||
<h3 style={{ margin: '0 0 16px', fontSize: 14, fontWeight: 600 }}>
|
||||
SR 생성/완료 추이 (7일)
|
||||
</h3>
|
||||
<ResponsiveContainer width="100%" height={200}>
|
||||
<LineChart data={data} margin={{ top: 5, right: 10, left: -20, bottom: 5 }}>
|
||||
<CartesianGrid strokeDasharray="3 3" stroke="#f0f2f5" />
|
||||
<XAxis dataKey="date" tick={{ fontSize: 11, fill: '#94a3b8' }} />
|
||||
<YAxis tick={{ fontSize: 11, fill: '#94a3b8' }} />
|
||||
<Tooltip contentStyle={{ borderRadius: 8, border: '1px solid #e2e8f0',
|
||||
fontSize: 12 }} />
|
||||
<Legend wrapperStyle={{ fontSize: 12 }} />
|
||||
<Line type="monotone" dataKey="created" name="신규 SR"
|
||||
stroke="#4f6ef7" strokeWidth={2} dot={{ r: 3 }} />
|
||||
<Line type="monotone" dataKey="completed" name="완료 SR"
|
||||
stroke="#22c55e" strokeWidth={2} dot={{ r: 3 }} />
|
||||
</LineChart>
|
||||
</ResponsiveContainer>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 2. 서버 상태 도넛 차트 (ServerStatusDonut)
|
||||
|
||||
```tsx
|
||||
// components/dashboard/ServerStatusDonut.tsx
|
||||
import { PieChart, Pie, Cell, Tooltip, Legend, ResponsiveContainer } from 'recharts';
|
||||
|
||||
const COLORS = {
|
||||
running: '#22c55e',
|
||||
stopped: '#94a3b8',
|
||||
error: '#ef4444',
|
||||
pending: '#f59e0b',
|
||||
};
|
||||
|
||||
export function ServerStatusDonut({ servers }: { servers: { status: string }[] }) {
|
||||
const counts = servers.reduce((acc, s) => {
|
||||
acc[s.status] = (acc[s.status] ?? 0) + 1; return acc;
|
||||
}, {} as Record<string, number>);
|
||||
|
||||
const data = Object.entries(counts).map(([status, value]) => ({
|
||||
name: { running: '실행중', stopped: '중지', error: '오류', pending: '진행중' }[status] ?? status,
|
||||
value,
|
||||
status,
|
||||
}));
|
||||
|
||||
return (
|
||||
<div style={{ background: '#fff', borderRadius: 10, padding: '20px 24px',
|
||||
border: '1px solid var(--border)' }}>
|
||||
<h3 style={{ margin: '0 0 8px', fontSize: 14, fontWeight: 600 }}>
|
||||
서버 상태 ({servers.length}대)
|
||||
</h3>
|
||||
<ResponsiveContainer width="100%" height={180}>
|
||||
<PieChart>
|
||||
<Pie data={data} cx="50%" cy="50%" innerRadius={50} outerRadius={75}
|
||||
paddingAngle={2} dataKey="value">
|
||||
{data.map((entry) => (
|
||||
<Cell key={entry.status}
|
||||
fill={COLORS[entry.status as keyof typeof COLORS] ?? '#94a3b8'} />
|
||||
))}
|
||||
</Pie>
|
||||
<Tooltip formatter={(value, name) => [`${value}대`, name]} />
|
||||
<Legend iconType="circle" iconSize={8} wrapperStyle={{ fontSize: 12 }} />
|
||||
</PieChart>
|
||||
</ResponsiveContainer>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 3. 리소스 모니터링 바 차트 (ResourceGauge)
|
||||
|
||||
```tsx
|
||||
// components/dashboard/ResourceGauge.tsx
|
||||
// NCloud 콘솔의 리소스 게이지 UI 참조
|
||||
|
||||
interface GaugeProps {
|
||||
label: string;
|
||||
percent: number;
|
||||
detail?: string;
|
||||
}
|
||||
|
||||
function Gauge({ label, percent, detail }: GaugeProps) {
|
||||
const color = percent > 90 ? '#ef4444' : percent > 70 ? '#f59e0b' : '#22c55e';
|
||||
return (
|
||||
<div style={{ flex: 1 }}>
|
||||
<div style={{ display: 'flex', justifyContent: 'space-between', marginBottom: 6,
|
||||
fontSize: 12 }}>
|
||||
<span style={{ fontWeight: 600 }}>{label}</span>
|
||||
<span style={{ color, fontWeight: 700 }}>{percent}%</span>
|
||||
</div>
|
||||
<div style={{ height: 8, background: '#f0f2f5', borderRadius: 4, overflow: 'hidden' }}>
|
||||
<div style={{ height: '100%', width: `${percent}%`,
|
||||
background: color, borderRadius: 4,
|
||||
transition: 'width 0.5s ease' }} />
|
||||
</div>
|
||||
{detail && <div style={{ fontSize: 11, color: '#94a3b8', marginTop: 4 }}>{detail}</div>}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export function ResourceGauge({ resources }: { resources: SystemResources }) {
|
||||
return (
|
||||
<div style={{ background: '#fff', borderRadius: 10, padding: '20px 24px',
|
||||
border: '1px solid var(--border)' }}>
|
||||
<h3 style={{ margin: '0 0 16px', fontSize: 14, fontWeight: 600 }}>
|
||||
서버 리소스
|
||||
</h3>
|
||||
<div style={{ display: 'flex', gap: 24 }}>
|
||||
<Gauge label="CPU" percent={resources.cpu_percent} />
|
||||
<Gauge label="메모리"
|
||||
percent={resources.memory.percent}
|
||||
detail={`${resources.memory.used_gb}GB / ${resources.memory.total_gb}GB`} />
|
||||
<Gauge label="디스크"
|
||||
percent={resources.disk.percent}
|
||||
detail={`${resources.disk.used_gb}GB / ${resources.disk.total_gb}GB`} />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 4. 배포 이력 타임라인 (DeployTimeline)
|
||||
|
||||
```tsx
|
||||
// components/dashboard/DeployTimeline.tsx
|
||||
import { useGuardiaApi } from '../../hooks/useGuardiaApi';
|
||||
|
||||
function timeAgo(ts: string) {
|
||||
const diff = Date.now() - new Date(ts).getTime();
|
||||
if (diff < 60000) return '방금 전';
|
||||
if (diff < 3600000) return `${Math.floor(diff/60000)}분 전`;
|
||||
return `${Math.floor(diff/3600000)}시간 전`;
|
||||
}
|
||||
|
||||
export function DeployTimeline() {
|
||||
const { data: logs } = useGuardiaApi<string[]>('/api/deploy/history');
|
||||
|
||||
const parsed = (logs ?? []).slice(-5).reverse().map(line => ({
|
||||
time: line.match(/\d{4}-\d{2}-\d{2} \d{2}:\d{2}/)?.[0] ?? '',
|
||||
ok: line.includes('완료') || line.includes('success'),
|
||||
msg: line.replace(/^\d{4}.*?INFO\s+/, '').trim().slice(0, 60),
|
||||
}));
|
||||
|
||||
return (
|
||||
<div style={{ background: '#fff', borderRadius: 10, padding: '20px 24px',
|
||||
border: '1px solid var(--border)' }}>
|
||||
<h3 style={{ margin: '0 0 14px', fontSize: 14, fontWeight: 600 }}>
|
||||
최근 배포 이력
|
||||
</h3>
|
||||
<ul style={{ listStyle: 'none', padding: 0, margin: 0 }}>
|
||||
{parsed.map((item, i) => (
|
||||
<li key={i} style={{ display: 'flex', gap: 10, padding: '8px 0',
|
||||
borderBottom: i < parsed.length - 1 ? '1px solid #f1f5f9' : 'none',
|
||||
fontSize: 13 }}>
|
||||
<span>{item.ok ? '✅' : '❌'}</span>
|
||||
<span style={{ flex: 1, color: '#1e293b' }}>{item.msg}</span>
|
||||
<span style={{ color: '#94a3b8', fontSize: 11, whiteSpace: 'nowrap' }}>
|
||||
{item.time}
|
||||
</span>
|
||||
</li>
|
||||
))}
|
||||
{!parsed.length && (
|
||||
<li style={{ color: '#94a3b8', fontSize: 13 }}>배포 이력이 없습니다.</li>
|
||||
)}
|
||||
</ul>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Dashboard.tsx 전체 조합
|
||||
|
||||
```tsx
|
||||
// pages/Dashboard.tsx
|
||||
import { StatCard } from '../components/common/StatCard';
|
||||
import { SRTrendChart } from '../components/dashboard/SRTrendChart';
|
||||
import { ServerStatusDonut } from '../components/dashboard/ServerStatusDonut';
|
||||
import { ResourceGauge } from '../components/dashboard/ResourceGauge';
|
||||
import { DeployTimeline } from '../components/dashboard/DeployTimeline';
|
||||
import { useGuardiaApi } from '../hooks/useGuardiaApi';
|
||||
import { useManagerApi } from '../hooks/useManagerApi';
|
||||
|
||||
export function Dashboard() {
|
||||
const { data: stats } = useGuardiaApi('/api/dashboard');
|
||||
const { data: resources } = useManagerApi('/api/system/resources');
|
||||
const { data: servers } = useGuardiaApi('/api/cmdb/servers?limit=100');
|
||||
|
||||
return (
|
||||
<div style={{ display: 'flex', flexDirection: 'column', gap: 20 }}>
|
||||
{/* 핵심 지표 카드 */}
|
||||
<div style={{ display: 'grid', gridTemplateColumns: 'repeat(4, 1fr)', gap: 16 }}>
|
||||
<StatCard title="전체 SR" value={stats?.total_sr ?? '-'}
|
||||
sub={`진행중 ${stats?.open_sr ?? 0}건`} icon="📋" color="#e8ecff" />
|
||||
<StatCard title="인시던트" value={stats?.critical_incidents ?? '-'}
|
||||
sub="긴급" icon="🚨" color="#fff1f2" />
|
||||
<StatCard title="서버 가용률"
|
||||
value={servers ? `${((servers.filter((s:any) => s.status==='running').length / servers.length)*100).toFixed(1)}%` : '-'}
|
||||
icon="🖥️" color="#f0fdf4" />
|
||||
<StatCard title="SLA 달성률" value={stats?.sla_achievement ? `${stats.sla_achievement}%` : '-'}
|
||||
icon="📈" color="#fff7ed" />
|
||||
</div>
|
||||
|
||||
{/* 차트 행 1: SR 추이 + 서버 상태 */}
|
||||
<div style={{ display: 'grid', gridTemplateColumns: '2fr 1fr', gap: 16 }}>
|
||||
<SRTrendChart data={stats?.sr_trend ?? []} />
|
||||
<ServerStatusDonut servers={servers ?? []} />
|
||||
</div>
|
||||
|
||||
{/* 리소스 게이지 */}
|
||||
{resources && <ResourceGauge resources={resources} />}
|
||||
|
||||
{/* 차트 행 2: 배포 이력 + LLM */}
|
||||
<div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 16 }}>
|
||||
<DeployTimeline />
|
||||
{/* LLM 상태 카드 추가 가능 */}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
```
|
||||
337
.claude/skills/manager-ui/references/ncloud-patterns.md
Normal file
337
.claude/skills/manager-ui/references/ncloud-patterns.md
Normal file
@ -0,0 +1,337 @@
|
||||
# 네이버 클라우드 콘솔 UI 패턴 참조
|
||||
|
||||
> GUARDiA Manager UI는 네이버 클라우드 콘솔(console.ncloud.com)의 디자인 패턴을 참조한다.
|
||||
> 아래 코드 스니펫은 해당 패턴을 GUARDiA Manager에 맞게 재구현한 것이다.
|
||||
|
||||
---
|
||||
|
||||
## 1. DataTable 컴포넌트 (NCloud 리소스 목록)
|
||||
|
||||
```tsx
|
||||
// components/common/DataTable.tsx
|
||||
import { useState } from 'react';
|
||||
import styles from './DataTable.module.css';
|
||||
|
||||
interface Column<T> {
|
||||
key: keyof T | string;
|
||||
header: string;
|
||||
width?: string;
|
||||
render?: (row: T) => React.ReactNode;
|
||||
sortable?: boolean;
|
||||
}
|
||||
|
||||
interface DataTableProps<T extends { id: string | number }> {
|
||||
columns: Column<T>[];
|
||||
data: T[];
|
||||
onRowClick?: (row: T) => void;
|
||||
actions?: React.ReactNode; // 상단 액션 버튼
|
||||
loading?: boolean;
|
||||
emptyMessage?: string;
|
||||
selectable?: boolean;
|
||||
onSelectionChange?: (selected: T[]) => void;
|
||||
}
|
||||
|
||||
export function DataTable<T extends { id: string | number }>({
|
||||
columns, data, onRowClick, actions, loading, emptyMessage,
|
||||
selectable = false, onSelectionChange,
|
||||
}: DataTableProps<T>) {
|
||||
const [selected, setSelected] = useState<Set<string | number>>(new Set());
|
||||
const [sortKey, setSortKey] = useState<string | null>(null);
|
||||
const [sortDir, setSortDir] = useState<'asc' | 'desc'>('asc');
|
||||
|
||||
const toggleAll = () => {
|
||||
const next = selected.size === data.length
|
||||
? new Set<string | number>()
|
||||
: new Set(data.map(r => r.id));
|
||||
setSelected(next);
|
||||
onSelectionChange?.(data.filter(r => next.has(r.id)));
|
||||
};
|
||||
|
||||
const toggleRow = (id: string | number) => {
|
||||
const next = new Set(selected);
|
||||
next.has(id) ? next.delete(id) : next.add(id);
|
||||
setSelected(next);
|
||||
onSelectionChange?.(data.filter(r => next.has(r.id)));
|
||||
};
|
||||
|
||||
return (
|
||||
<div className={styles.wrapper}>
|
||||
{/* 상단 액션 영역 */}
|
||||
{actions && (
|
||||
<div className={styles.toolbar}>
|
||||
{selected.size > 0 && (
|
||||
<span className={styles.selectionCount}>{selected.size}개 선택됨</span>
|
||||
)}
|
||||
<div className={styles.actions}>{actions}</div>
|
||||
</div>
|
||||
)}
|
||||
<table className={styles.table}>
|
||||
<thead>
|
||||
<tr>
|
||||
{selectable && (
|
||||
<th className={styles.checkboxCol}>
|
||||
<input type="checkbox"
|
||||
checked={selected.size === data.length && data.length > 0}
|
||||
onChange={toggleAll} />
|
||||
</th>
|
||||
)}
|
||||
{columns.map(col => (
|
||||
<th key={String(col.key)}
|
||||
style={{ width: col.width }}
|
||||
className={col.sortable ? styles.sortable : ''}
|
||||
onClick={() => col.sortable && (setSortKey(String(col.key)),
|
||||
setSortDir(d => d === 'asc' ? 'desc' : 'asc'))}>
|
||||
{col.header}
|
||||
{sortKey === String(col.key) && (
|
||||
<span>{sortDir === 'asc' ? ' ↑' : ' ↓'}</span>
|
||||
)}
|
||||
</th>
|
||||
))}
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{loading ? (
|
||||
<tr><td colSpan={columns.length + 1}
|
||||
className={styles.loading}>로딩 중...</td></tr>
|
||||
) : data.length === 0 ? (
|
||||
<tr><td colSpan={columns.length + 1}
|
||||
className={styles.empty}>{emptyMessage ?? '데이터가 없습니다.'}</td></tr>
|
||||
) : data.map(row => (
|
||||
<tr key={row.id}
|
||||
className={`${styles.row} ${onRowClick ? styles.clickable : ''}`}
|
||||
onClick={() => onRowClick?.(row)}>
|
||||
{selectable && (
|
||||
<td className={styles.checkboxCol} onClick={e => e.stopPropagation()}>
|
||||
<input type="checkbox"
|
||||
checked={selected.has(row.id)}
|
||||
onChange={() => toggleRow(row.id)} />
|
||||
</td>
|
||||
)}
|
||||
{columns.map(col => (
|
||||
<td key={String(col.key)}>
|
||||
{col.render ? col.render(row) : String((row as any)[col.key] ?? '')}
|
||||
</td>
|
||||
))}
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 2. ResourceCard 컴포넌트 (서버/서비스 카드)
|
||||
|
||||
```tsx
|
||||
// components/common/ResourceCard.tsx
|
||||
import { StatusBadge } from './StatusBadge';
|
||||
|
||||
interface ResourceCardProps {
|
||||
name: string;
|
||||
type: string; // '서버' | 'DB' | 'WAS' | 'API'
|
||||
status: 'running' | 'stopped' | 'error' | 'pending';
|
||||
spec?: string; // '2vCPU / 4GB' 등
|
||||
ip?: string;
|
||||
onClick?: () => void;
|
||||
}
|
||||
|
||||
export function ResourceCard({ name, type, status, spec, ip, onClick }: ResourceCardProps) {
|
||||
return (
|
||||
<div className="resource-card" onClick={onClick} style={{
|
||||
background: '#fff',
|
||||
border: '1px solid var(--border)',
|
||||
borderRadius: 8,
|
||||
padding: '16px',
|
||||
cursor: onClick ? 'pointer' : 'default',
|
||||
transition: 'box-shadow 0.15s',
|
||||
}}>
|
||||
<div style={{ display: 'flex', justifyContent: 'space-between', marginBottom: 8 }}>
|
||||
<span style={{ fontSize: 12, color: 'var(--text-muted)',
|
||||
background: 'var(--brand-light)', padding: '2px 8px', borderRadius: 4 }}>
|
||||
{type}
|
||||
</span>
|
||||
<StatusBadge status={status} />
|
||||
</div>
|
||||
<div style={{ fontWeight: 600, fontSize: 14, marginBottom: 4 }}>{name}</div>
|
||||
{spec && <div style={{ fontSize: 12, color: 'var(--text-secondary)' }}>{spec}</div>}
|
||||
{ip && <div style={{ fontSize: 12, color: 'var(--text-muted)', marginTop: 4 }}>{ip}</div>}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 3. SlidePanel 컴포넌트 (NCloud 상세 정보 패널)
|
||||
|
||||
NCloud 콘솔에서 리소스 클릭 시 우측에서 슬라이드하는 상세 패널.
|
||||
|
||||
```tsx
|
||||
// components/common/SlidePanel.tsx
|
||||
import { useEffect } from 'react';
|
||||
|
||||
interface SlidePanelProps {
|
||||
open: boolean;
|
||||
onClose: () => void;
|
||||
title: string;
|
||||
width?: number; // 기본 480px
|
||||
children: React.ReactNode;
|
||||
actions?: React.ReactNode;
|
||||
}
|
||||
|
||||
export function SlidePanel({ open, onClose, title, width = 480, children, actions }: SlidePanelProps) {
|
||||
useEffect(() => {
|
||||
const handler = (e: KeyboardEvent) => e.key === 'Escape' && onClose();
|
||||
document.addEventListener('keydown', handler);
|
||||
return () => document.removeEventListener('keydown', handler);
|
||||
}, [onClose]);
|
||||
|
||||
return (
|
||||
<>
|
||||
{/* 오버레이 */}
|
||||
{open && (
|
||||
<div onClick={onClose} style={{
|
||||
position: 'fixed', inset: 0,
|
||||
background: 'rgba(0,0,0,0.3)', zIndex: 200
|
||||
}} />
|
||||
)}
|
||||
{/* 패널 */}
|
||||
<div style={{
|
||||
position: 'fixed', top: 0, right: 0, bottom: 0,
|
||||
width, background: '#fff', zIndex: 201,
|
||||
transform: open ? 'translateX(0)' : `translateX(${width}px)`,
|
||||
transition: 'transform 0.25s ease',
|
||||
display: 'flex', flexDirection: 'column',
|
||||
boxShadow: '-4px 0 24px rgba(0,0,0,0.15)',
|
||||
}}>
|
||||
{/* 헤더 */}
|
||||
<div style={{ padding: '20px 24px 16px', borderBottom: '1px solid var(--border)',
|
||||
display: 'flex', alignItems: 'center', justifyContent: 'space-between' }}>
|
||||
<h3 style={{ margin: 0, fontSize: 16, fontWeight: 600 }}>{title}</h3>
|
||||
<button onClick={onClose} style={{ background: 'none', border: 'none',
|
||||
cursor: 'pointer', fontSize: 18, color: 'var(--text-muted)' }}>✕</button>
|
||||
</div>
|
||||
{/* 콘텐츠 */}
|
||||
<div style={{ flex: 1, overflow: 'auto', padding: '20px 24px' }}>{children}</div>
|
||||
{/* 푸터 액션 */}
|
||||
{actions && (
|
||||
<div style={{ padding: '16px 24px', borderTop: '1px solid var(--border)',
|
||||
display: 'flex', gap: 8, justifyContent: 'flex-end' }}>
|
||||
{actions}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 4. StatCard 컴포넌트 (대시보드 통계 카드)
|
||||
|
||||
```tsx
|
||||
// components/common/StatCard.tsx
|
||||
interface StatCardProps {
|
||||
title: string;
|
||||
value: string | number;
|
||||
sub?: string;
|
||||
trend?: { value: number; positive: boolean };
|
||||
icon?: string;
|
||||
color?: string; // 아이콘 배경색
|
||||
onClick?: () => void;
|
||||
}
|
||||
|
||||
export function StatCard({ title, value, sub, trend, icon, color = 'var(--brand-light)', onClick }: StatCardProps) {
|
||||
return (
|
||||
<div onClick={onClick} style={{
|
||||
background: '#fff', border: '1px solid var(--border)',
|
||||
borderRadius: 10, padding: '20px',
|
||||
cursor: onClick ? 'pointer' : 'default',
|
||||
}}>
|
||||
<div style={{ display: 'flex', alignItems: 'center', gap: 14 }}>
|
||||
{icon && (
|
||||
<div style={{ width: 44, height: 44, borderRadius: 10,
|
||||
background: color, display: 'flex', alignItems: 'center',
|
||||
justifyContent: 'center', fontSize: 20, flexShrink: 0 }}>
|
||||
{icon}
|
||||
</div>
|
||||
)}
|
||||
<div>
|
||||
<div style={{ fontSize: 24, fontWeight: 700, color: 'var(--text-primary)' }}>
|
||||
{value}
|
||||
</div>
|
||||
<div style={{ fontSize: 12, color: 'var(--text-muted)' }}>{title}</div>
|
||||
{sub && <div style={{ fontSize: 11, color: 'var(--text-muted)' }}>{sub}</div>}
|
||||
</div>
|
||||
</div>
|
||||
{trend && (
|
||||
<div style={{ marginTop: 10, fontSize: 11,
|
||||
color: trend.positive ? 'var(--status-running)' : 'var(--status-error)' }}>
|
||||
{trend.positive ? '▲' : '▼'} {Math.abs(trend.value)}% 전주 대비
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 5. 사이드바 네비게이션 (NCloud 서비스 트리 스타일)
|
||||
|
||||
```tsx
|
||||
const MENU = [
|
||||
{ label: '대시보드', icon: '📊', path: '/' },
|
||||
{
|
||||
label: '인프라 관리', icon: '🖥️',
|
||||
children: [
|
||||
{ label: '서버 목록', path: '/servers' },
|
||||
{ label: 'CMDB 현황', path: '/cmdb' },
|
||||
{ label: 'SSH 자격증명', path: '/credentials' },
|
||||
]
|
||||
},
|
||||
{
|
||||
label: '배포/CI-CD', icon: '🚀',
|
||||
children: [
|
||||
{ label: '배포 이력', path: '/deployments' },
|
||||
{ label: '저장소 목록', path: '/repos' },
|
||||
{ label: '서비스 상태', path: '/services' },
|
||||
]
|
||||
},
|
||||
{
|
||||
label: '사용자/테넌트', icon: '👥',
|
||||
children: [
|
||||
{ label: '사용자 관리', path: '/users' },
|
||||
{ label: '기관 관리', path: '/institutions' },
|
||||
{ label: '역할 설정', path: '/roles' },
|
||||
]
|
||||
},
|
||||
{
|
||||
label: '보안', icon: '🔒',
|
||||
children: [
|
||||
{ label: 'API Key 관리', path: '/api-keys' },
|
||||
{ label: '감사 로그', path: '/audit' },
|
||||
{ label: '취약점 현황', path: '/vulns' },
|
||||
]
|
||||
},
|
||||
{
|
||||
label: 'AI/LLM', icon: '🤖',
|
||||
children: [
|
||||
{ label: 'Ollama 모델', path: '/llm' },
|
||||
{ label: 'AI 에이전트', path: '/agents' },
|
||||
]
|
||||
},
|
||||
{
|
||||
label: '시스템 설정', icon: '⚙️',
|
||||
children: [
|
||||
{ label: '환경변수', path: '/config/env' },
|
||||
{ label: 'Nginx 설정', path: '/config/nginx' },
|
||||
{ label: '알림 설정', path: '/config/notify' },
|
||||
]
|
||||
},
|
||||
];
|
||||
```
|
||||
@ -18,7 +18,7 @@ GUARDiA ITSM·홈페이지·서버 인프라·CI/CD를 단일 화면에서 통
|
||||
| Frontend | React 18 + TypeScript + Vite | 독립 SPA |
|
||||
| Backend | Python FastAPI (경량) | 시스템 수준 작업 전용 |
|
||||
| 인증 | GUARDiA ITSM JWT 공유 | 별도 DB 없음 |
|
||||
| 연동 | GUARDiA ITSM REST API | http://101.79.17.164:8001 |
|
||||
| 연동 | GUARDiA ITSM REST API | http://zioinfo.co.kr:8001 |
|
||||
| 배포 | Gitea + Deploy Webhook | 포트 9999 |
|
||||
|
||||
---
|
||||
|
||||
21
backend/core/auth.py
Normal file
21
backend/core/auth.py
Normal file
@ -0,0 +1,21 @@
|
||||
import os
|
||||
from fastapi import Depends, HTTPException
|
||||
from fastapi.security import OAuth2PasswordBearer
|
||||
from jose import JWTError, jwt
|
||||
|
||||
SECRET = os.environ.get("GUARDIA_JWT_SECRET", "guardia-jwt-secret-2026-change-me!")
|
||||
ALGORITHM = "HS256"
|
||||
oauth2 = OAuth2PasswordBearer(tokenUrl="/guardia-api/api/auth/login", auto_error=False)
|
||||
|
||||
async def verify_token(token: str = Depends(oauth2)) -> dict:
|
||||
if not token:
|
||||
raise HTTPException(status_code=401, detail="인증이 필요합니다.")
|
||||
try:
|
||||
return jwt.decode(token, SECRET, algorithms=[ALGORITHM])
|
||||
except JWTError:
|
||||
raise HTTPException(status_code=401, detail="유효하지 않은 토큰입니다.")
|
||||
|
||||
async def require_admin(payload: dict = Depends(verify_token)) -> dict:
|
||||
if payload.get("role") not in ("admin",):
|
||||
raise HTTPException(status_code=403, detail="관리자 권한이 필요합니다.")
|
||||
return payload
|
||||
30
backend/main.py
Normal file
30
backend/main.py
Normal file
@ -0,0 +1,30 @@
|
||||
"""GUARDiA Manager 경량 백엔드 API — 포트 8002"""
|
||||
import os
|
||||
from fastapi import FastAPI
|
||||
from fastapi.middleware.cors import CORSMiddleware
|
||||
from dotenv import load_dotenv
|
||||
|
||||
load_dotenv()
|
||||
|
||||
app = FastAPI(title="GUARDiA Manager API", version="1.0.0")
|
||||
|
||||
app.add_middleware(
|
||||
CORSMiddleware,
|
||||
allow_origins=os.environ.get(
|
||||
"MANAGER_ALLOWED_ORIGINS",
|
||||
"http://localhost:5175,http://localhost:5173,http://zioinfo.co.kr:8090"
|
||||
).split(","),
|
||||
allow_methods=["*"],
|
||||
allow_headers=["*"],
|
||||
allow_credentials=True,
|
||||
)
|
||||
|
||||
from routers import system, deploy, config, llm
|
||||
app.include_router(system.router, prefix="/api/system", tags=["system"])
|
||||
app.include_router(deploy.router, prefix="/api/deploy", tags=["deploy"])
|
||||
app.include_router(config.router, prefix="/api/config", tags=["config"])
|
||||
app.include_router(llm.router, prefix="/api/llm", tags=["llm"])
|
||||
|
||||
@app.get("/health")
|
||||
async def health():
|
||||
return {"status": "ok", "service": "guardia-manager", "port": 8002}
|
||||
6
backend/requirements.txt
Normal file
6
backend/requirements.txt
Normal file
@ -0,0 +1,6 @@
|
||||
fastapi==0.115.0
|
||||
uvicorn[standard]==0.30.0
|
||||
httpx==0.27.0
|
||||
psutil==5.9.8
|
||||
python-jose[cryptography]==3.3.0
|
||||
python-dotenv==1.0.1
|
||||
36
backend/routers/config.py
Normal file
36
backend/routers/config.py
Normal file
@ -0,0 +1,36 @@
|
||||
import os, subprocess
|
||||
from fastapi import APIRouter, Depends, HTTPException
|
||||
from core.auth import require_admin
|
||||
|
||||
router = APIRouter()
|
||||
ENV_FILES = [
|
||||
"/opt/guardia/app/.env",
|
||||
"/opt/manager/backend/.env",
|
||||
]
|
||||
SENSITIVE = {"SECRET", "PASSWORD", "KEY", "TOKEN", "PASS", "PWD"}
|
||||
|
||||
def _mask(k: str, v: str) -> str:
|
||||
return "****" if any(s in k.upper() for s in SENSITIVE) else v
|
||||
|
||||
@router.get("/env")
|
||||
async def get_env(_=Depends(require_admin)):
|
||||
result: dict[str, str] = {}
|
||||
for path in ENV_FILES:
|
||||
try:
|
||||
with open(path) as f:
|
||||
for line in f:
|
||||
line = line.strip()
|
||||
if line and not line.startswith("#") and "=" in line:
|
||||
k, _, v = line.partition("=")
|
||||
result[k.strip()] = _mask(k.strip(), v.strip())
|
||||
except FileNotFoundError:
|
||||
pass
|
||||
return result
|
||||
|
||||
@router.post("/nginx/reload")
|
||||
async def nginx_reload(_=Depends(require_admin)):
|
||||
test = subprocess.run(["nginx", "-t"], capture_output=True, text=True)
|
||||
if test.returncode != 0:
|
||||
raise HTTPException(status_code=400, detail=f"설정 오류: {test.stderr[:300]}")
|
||||
subprocess.run(["systemctl", "reload", "nginx"])
|
||||
return {"message": "Nginx 리로드 완료"}
|
||||
27
backend/routers/deploy.py
Normal file
27
backend/routers/deploy.py
Normal file
@ -0,0 +1,27 @@
|
||||
import httpx, os
|
||||
from fastapi import APIRouter, Depends, HTTPException
|
||||
from core.auth import verify_token
|
||||
|
||||
router = APIRouter()
|
||||
WEBHOOK_URL = os.environ.get("DEPLOY_WEBHOOK", "http://localhost:9999/")
|
||||
DEPLOY_LOG = os.environ.get("DEPLOY_LOG", "/var/log/zioinfo/deploy.log")
|
||||
|
||||
@router.post("/trigger/{repo}")
|
||||
async def trigger(repo: str, user=Depends(verify_token)):
|
||||
async with httpx.AsyncClient() as c:
|
||||
try:
|
||||
r = await c.post(WEBHOOK_URL,
|
||||
json={"repo": repo, "triggered_by": user.get("sub", "manager")},
|
||||
timeout=10)
|
||||
return {"status": r.status_code, "message": f"{repo} 배포 트리거됨"}
|
||||
except Exception as e:
|
||||
raise HTTPException(status_code=502, detail=f"Deploy Webhook 연결 실패: {e}")
|
||||
|
||||
@router.get("/history")
|
||||
async def history(_=Depends(verify_token)):
|
||||
try:
|
||||
with open(DEPLOY_LOG, encoding="utf-8", errors="replace") as f:
|
||||
lines = f.readlines()[-200:]
|
||||
return {"lines": [l.rstrip() for l in lines]}
|
||||
except FileNotFoundError:
|
||||
return {"lines": []}
|
||||
15
backend/routers/llm.py
Normal file
15
backend/routers/llm.py
Normal file
@ -0,0 +1,15 @@
|
||||
import httpx, os
|
||||
from fastapi import APIRouter, Depends
|
||||
from core.auth import verify_token
|
||||
|
||||
router = APIRouter()
|
||||
OLLAMA = os.environ.get("OLLAMA_URL", "http://localhost:11434")
|
||||
|
||||
@router.get("/models")
|
||||
async def models(_=Depends(verify_token)):
|
||||
async with httpx.AsyncClient() as c:
|
||||
try:
|
||||
r = await c.get(f"{OLLAMA}/api/tags", timeout=5)
|
||||
return r.json()
|
||||
except Exception:
|
||||
return {"models": []}
|
||||
42
backend/routers/system.py
Normal file
42
backend/routers/system.py
Normal file
@ -0,0 +1,42 @@
|
||||
import subprocess
|
||||
from fastapi import APIRouter, Depends, HTTPException
|
||||
from core.auth import verify_token, require_admin
|
||||
|
||||
router = APIRouter()
|
||||
|
||||
ALLOWED_SVCS = {
|
||||
"nginx", "zioinfo", "zioinfo-deploy", "guardia", "guardia-manager",
|
||||
"gitea", "jenkins", "postgresql", "ollama",
|
||||
}
|
||||
|
||||
@router.get("/resources")
|
||||
async def resources(_=Depends(verify_token)):
|
||||
try:
|
||||
import psutil
|
||||
cpu = psutil.cpu_percent(interval=0.2)
|
||||
mem = psutil.virtual_memory()
|
||||
disk = psutil.disk_usage("/")
|
||||
return {
|
||||
"cpu_percent": round(cpu, 1),
|
||||
"memory": {"total_gb": round(mem.total/1e9,1), "used_gb": round(mem.used/1e9,1), "percent": mem.percent},
|
||||
"disk": {"total_gb": round(disk.total/1e9,1),"used_gb": round(disk.used/1e9,1), "percent": disk.percent},
|
||||
}
|
||||
except Exception as e:
|
||||
raise HTTPException(status_code=500, detail=str(e))
|
||||
|
||||
@router.get("/services")
|
||||
async def services(_=Depends(verify_token)):
|
||||
result: dict[str, str] = {}
|
||||
for svc in ALLOWED_SVCS:
|
||||
p = subprocess.run(["systemctl", "is-active", svc], capture_output=True, text=True)
|
||||
result[svc] = p.stdout.strip()
|
||||
return result
|
||||
|
||||
@router.post("/services/{name}/restart")
|
||||
async def restart(name: str, admin=Depends(require_admin)):
|
||||
if name not in ALLOWED_SVCS:
|
||||
raise HTTPException(status_code=400, detail="허용되지 않은 서비스")
|
||||
p = subprocess.run(["systemctl", "restart", name], capture_output=True, text=True)
|
||||
if p.returncode != 0:
|
||||
raise HTTPException(status_code=500, detail=p.stderr[:300])
|
||||
return {"message": f"{name} 재시작 완료"}
|
||||
156
deploy_server.py
Normal file
156
deploy_server.py
Normal file
@ -0,0 +1,156 @@
|
||||
#!/usr/bin/env python3
|
||||
"""GUARDiA Manager 서버 배포 스크립트"""
|
||||
import paramiko, time, sys, os, io, zipfile
|
||||
|
||||
HOST = 'zioinfo.co.kr'; USER = 'root'; PASS = '1q2w3e!Q'
|
||||
LOCAL_DIST = 'C:/GUARDiA/manager/dist'
|
||||
LOCAL_BACKEND = 'C:/GUARDiA/manager/backend'
|
||||
SEP = chr(92)
|
||||
|
||||
client = paramiko.SSHClient()
|
||||
client.set_missing_host_key_policy(paramiko.AutoAddPolicy())
|
||||
client.connect(HOST, username=USER, password=PASS, timeout=15)
|
||||
sftp = client.open_sftp()
|
||||
|
||||
def run(label, cmd, timeout=60):
|
||||
print(f'\n[{label}]')
|
||||
chan = client.get_transport().open_session()
|
||||
chan.set_combine_stderr(True)
|
||||
chan.exec_command(cmd)
|
||||
start = time.time()
|
||||
while not chan.exit_status_ready():
|
||||
if chan.recv_ready(): sys.stdout.buffer.write(chan.recv(4096)); sys.stdout.flush()
|
||||
if time.time() - start > timeout: print('[TIMEOUT]'); break
|
||||
time.sleep(0.2)
|
||||
while chan.recv_ready(): sys.stdout.buffer.write(chan.recv(4096))
|
||||
sys.stdout.flush()
|
||||
rc = chan.recv_exit_status()
|
||||
print(f'\n→ exit={rc}')
|
||||
return rc
|
||||
|
||||
# 1. 서버 디렉터리 생성
|
||||
run('디렉터리 생성',
|
||||
'mkdir -p /var/www/manager /opt/manager/backend /opt/manager/logs && echo ok')
|
||||
|
||||
# 2. React 빌드 결과 zip → 업로드
|
||||
print('\n[dist 파일 패키징...]')
|
||||
zip_buf = io.BytesIO()
|
||||
count = 0
|
||||
with zipfile.ZipFile(zip_buf, 'w', zipfile.ZIP_DEFLATED) as zf:
|
||||
for root, dirs, files in os.walk(LOCAL_DIST):
|
||||
rel = os.path.relpath(root, LOCAL_DIST).replace(SEP, '/')
|
||||
for f in files:
|
||||
arc = f if rel == '.' else f'{rel}/{f}'
|
||||
zf.write(os.path.join(root, f), arc); count += 1
|
||||
zip_buf.seek(0)
|
||||
with sftp.open('/tmp/manager-dist.zip', 'wb') as f: f.write(zip_buf.read())
|
||||
print(f'{count}개 파일 업로드')
|
||||
run('dist 배포', 'cd /var/www/manager && unzip -q -o /tmp/manager-dist.zip && echo "deployed" && ls | head -5')
|
||||
|
||||
# 3. 백엔드 소스 업로드
|
||||
print('\n[백엔드 소스 업로드...]')
|
||||
for item in os.listdir(LOCAL_BACKEND):
|
||||
lp = os.path.join(LOCAL_BACKEND, item)
|
||||
if os.path.isfile(lp) and item.endswith('.py'):
|
||||
sftp.put(lp, f'/opt/manager/backend/{item}')
|
||||
print(f' {item}')
|
||||
# routers / core
|
||||
for sub in ('routers', 'core'):
|
||||
sub_dir = os.path.join(LOCAL_BACKEND, sub)
|
||||
if not os.path.isdir(sub_dir): continue
|
||||
run(f'{sub} 디렉터리', f'mkdir -p /opt/manager/backend/{sub}')
|
||||
for fn in os.listdir(sub_dir):
|
||||
if fn.endswith('.py'):
|
||||
sftp.put(os.path.join(sub_dir, fn), f'/opt/manager/backend/{sub}/{fn}')
|
||||
print(f' {sub}/{fn}')
|
||||
# requirements + .env
|
||||
for fn in ('requirements.txt', '.env'):
|
||||
lp = os.path.join(LOCAL_BACKEND, fn)
|
||||
if os.path.exists(lp):
|
||||
sftp.put(lp, f'/opt/manager/backend/{fn}')
|
||||
print(f' {fn}')
|
||||
|
||||
# 4. Python venv + 패키지
|
||||
run('Python venv',
|
||||
'python3 -m venv /opt/manager/venv && '
|
||||
'/opt/manager/venv/bin/pip install -r /opt/manager/backend/requirements.txt -q && echo ok',
|
||||
120)
|
||||
|
||||
# 5. .env 서버 JWT secret 동기화
|
||||
run('.env JWT secret 동기화',
|
||||
"GUARDIA_SECRET=$(grep GUARDIA_JWT_SECRET /opt/guardia/app/.env 2>/dev/null | cut -d= -f2-) && "
|
||||
"if [ -n \"$GUARDIA_SECRET\" ]; then "
|
||||
" sed -i \"s|GUARDIA_JWT_SECRET=.*|GUARDIA_JWT_SECRET=$GUARDIA_SECRET|\" /opt/manager/backend/.env && "
|
||||
" echo 'JWT secret synced'; fi")
|
||||
|
||||
# 6. systemd 서비스
|
||||
svc = """[Unit]
|
||||
Description=GUARDiA Manager API Backend
|
||||
After=network.target guardia.service
|
||||
|
||||
[Service]
|
||||
User=root
|
||||
WorkingDirectory=/opt/manager/backend
|
||||
EnvironmentFile=-/opt/manager/backend/.env
|
||||
ExecStart=/opt/manager/venv/bin/uvicorn main:app --host 127.0.0.1 --port 8002 --workers 1
|
||||
Restart=on-failure
|
||||
RestartSec=5
|
||||
StandardOutput=append:/opt/manager/logs/backend.log
|
||||
StandardError=append:/opt/manager/logs/backend.log
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
"""
|
||||
with sftp.open('/etc/systemd/system/guardia-manager.service', 'w') as f: f.write(svc)
|
||||
|
||||
# 7. Nginx 설정
|
||||
nginx_conf = r"""server {
|
||||
listen 8090;
|
||||
server_name _;
|
||||
root /var/www/manager;
|
||||
index index.html;
|
||||
location / {
|
||||
try_files $uri $uri/ /index.html;
|
||||
add_header Cache-Control no-cache;
|
||||
}
|
||||
location /api/ {
|
||||
proxy_pass http://127.0.0.1:8002;
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_read_timeout 60s;
|
||||
}
|
||||
location /guardia-api/ {
|
||||
proxy_pass http://127.0.0.1:8001/;
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
}
|
||||
location ~* \.(js|css|png|ico|svg|woff2)$ {
|
||||
expires 7d;
|
||||
add_header Cache-Control "public, immutable";
|
||||
}
|
||||
gzip on;
|
||||
gzip_types text/plain text/css application/javascript application/json;
|
||||
}
|
||||
"""
|
||||
with sftp.open('/etc/nginx/sites-available/guardia-manager', 'w') as f: f.write(nginx_conf)
|
||||
|
||||
# 8. 서비스 시작
|
||||
run('UFW 8090 오픈', 'ufw allow 8090/tcp && ufw allow 8002/tcp && echo ok')
|
||||
run('systemd 등록 + 시작',
|
||||
'systemctl daemon-reload && '
|
||||
'systemctl enable guardia-manager && '
|
||||
'systemctl restart guardia-manager && '
|
||||
'sleep 5 && systemctl is-active guardia-manager')
|
||||
|
||||
run('Nginx 활성화',
|
||||
'ln -sf /etc/nginx/sites-available/guardia-manager /etc/nginx/sites-enabled/ && '
|
||||
'nginx -t && systemctl reload nginx && echo NGINX_OK')
|
||||
|
||||
# 9. 헬스체크
|
||||
run('헬스체크', 'curl -s http://localhost:8002/health && echo "" && '
|
||||
'curl -s -o /dev/null -w "Manager UI: HTTP %{http_code}" http://localhost:8090/')
|
||||
|
||||
sftp.close(); client.close()
|
||||
print('\n\n=== 배포 완료 ===')
|
||||
print('GUARDiA Manager: http://zioinfo.co.kr:8090')
|
||||
13
frontend/index.html
Normal file
13
frontend/index.html
Normal file
@ -0,0 +1,13 @@
|
||||
<!doctype html>
|
||||
<html lang="ko">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>GUARDiA Manager</title>
|
||||
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
|
||||
</head>
|
||||
<body>
|
||||
<div id="root"></div>
|
||||
<script type="module" src="/src/main.tsx"></script>
|
||||
</body>
|
||||
</html>
|
||||
16
frontend/node_modules/.bin/baseline-browser-mapping
generated
vendored
Normal file
16
frontend/node_modules/.bin/baseline-browser-mapping
generated
vendored
Normal file
@ -0,0 +1,16 @@
|
||||
#!/bin/sh
|
||||
basedir=$(dirname "$(echo "$0" | sed -e 's,\\,/,g')")
|
||||
|
||||
case `uname` in
|
||||
*CYGWIN*|*MINGW*|*MSYS*)
|
||||
if command -v cygpath > /dev/null 2>&1; then
|
||||
basedir=`cygpath -w "$basedir"`
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
|
||||
if [ -x "$basedir/node" ]; then
|
||||
exec "$basedir/node" "$basedir/../baseline-browser-mapping/dist/cli.cjs" "$@"
|
||||
else
|
||||
exec node "$basedir/../baseline-browser-mapping/dist/cli.cjs" "$@"
|
||||
fi
|
||||
17
frontend/node_modules/.bin/baseline-browser-mapping.cmd
generated
vendored
Normal file
17
frontend/node_modules/.bin/baseline-browser-mapping.cmd
generated
vendored
Normal file
@ -0,0 +1,17 @@
|
||||
@ECHO off
|
||||
GOTO start
|
||||
:find_dp0
|
||||
SET dp0=%~dp0
|
||||
EXIT /b
|
||||
:start
|
||||
SETLOCAL
|
||||
CALL :find_dp0
|
||||
|
||||
IF EXIST "%dp0%\node.exe" (
|
||||
SET "_prog=%dp0%\node.exe"
|
||||
) ELSE (
|
||||
SET "_prog=node"
|
||||
SET PATHEXT=%PATHEXT:;.JS;=;%
|
||||
)
|
||||
|
||||
endLocal & goto #_undefined_# 2>NUL || title %COMSPEC% & "%_prog%" "%dp0%\..\baseline-browser-mapping\dist\cli.cjs" %*
|
||||
28
frontend/node_modules/.bin/baseline-browser-mapping.ps1
generated
vendored
Normal file
28
frontend/node_modules/.bin/baseline-browser-mapping.ps1
generated
vendored
Normal file
@ -0,0 +1,28 @@
|
||||
#!/usr/bin/env pwsh
|
||||
$basedir=Split-Path $MyInvocation.MyCommand.Definition -Parent
|
||||
|
||||
$exe=""
|
||||
if ($PSVersionTable.PSVersion -lt "6.0" -or $IsWindows) {
|
||||
# Fix case when both the Windows and Linux builds of Node
|
||||
# are installed in the same directory
|
||||
$exe=".exe"
|
||||
}
|
||||
$ret=0
|
||||
if (Test-Path "$basedir/node$exe") {
|
||||
# Support pipeline input
|
||||
if ($MyInvocation.ExpectingInput) {
|
||||
$input | & "$basedir/node$exe" "$basedir/../baseline-browser-mapping/dist/cli.cjs" $args
|
||||
} else {
|
||||
& "$basedir/node$exe" "$basedir/../baseline-browser-mapping/dist/cli.cjs" $args
|
||||
}
|
||||
$ret=$LASTEXITCODE
|
||||
} else {
|
||||
# Support pipeline input
|
||||
if ($MyInvocation.ExpectingInput) {
|
||||
$input | & "node$exe" "$basedir/../baseline-browser-mapping/dist/cli.cjs" $args
|
||||
} else {
|
||||
& "node$exe" "$basedir/../baseline-browser-mapping/dist/cli.cjs" $args
|
||||
}
|
||||
$ret=$LASTEXITCODE
|
||||
}
|
||||
exit $ret
|
||||
16
frontend/node_modules/.bin/browserslist
generated
vendored
Normal file
16
frontend/node_modules/.bin/browserslist
generated
vendored
Normal file
@ -0,0 +1,16 @@
|
||||
#!/bin/sh
|
||||
basedir=$(dirname "$(echo "$0" | sed -e 's,\\,/,g')")
|
||||
|
||||
case `uname` in
|
||||
*CYGWIN*|*MINGW*|*MSYS*)
|
||||
if command -v cygpath > /dev/null 2>&1; then
|
||||
basedir=`cygpath -w "$basedir"`
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
|
||||
if [ -x "$basedir/node" ]; then
|
||||
exec "$basedir/node" "$basedir/../browserslist/cli.js" "$@"
|
||||
else
|
||||
exec node "$basedir/../browserslist/cli.js" "$@"
|
||||
fi
|
||||
17
frontend/node_modules/.bin/browserslist.cmd
generated
vendored
Normal file
17
frontend/node_modules/.bin/browserslist.cmd
generated
vendored
Normal file
@ -0,0 +1,17 @@
|
||||
@ECHO off
|
||||
GOTO start
|
||||
:find_dp0
|
||||
SET dp0=%~dp0
|
||||
EXIT /b
|
||||
:start
|
||||
SETLOCAL
|
||||
CALL :find_dp0
|
||||
|
||||
IF EXIST "%dp0%\node.exe" (
|
||||
SET "_prog=%dp0%\node.exe"
|
||||
) ELSE (
|
||||
SET "_prog=node"
|
||||
SET PATHEXT=%PATHEXT:;.JS;=;%
|
||||
)
|
||||
|
||||
endLocal & goto #_undefined_# 2>NUL || title %COMSPEC% & "%_prog%" "%dp0%\..\browserslist\cli.js" %*
|
||||
28
frontend/node_modules/.bin/browserslist.ps1
generated
vendored
Normal file
28
frontend/node_modules/.bin/browserslist.ps1
generated
vendored
Normal file
@ -0,0 +1,28 @@
|
||||
#!/usr/bin/env pwsh
|
||||
$basedir=Split-Path $MyInvocation.MyCommand.Definition -Parent
|
||||
|
||||
$exe=""
|
||||
if ($PSVersionTable.PSVersion -lt "6.0" -or $IsWindows) {
|
||||
# Fix case when both the Windows and Linux builds of Node
|
||||
# are installed in the same directory
|
||||
$exe=".exe"
|
||||
}
|
||||
$ret=0
|
||||
if (Test-Path "$basedir/node$exe") {
|
||||
# Support pipeline input
|
||||
if ($MyInvocation.ExpectingInput) {
|
||||
$input | & "$basedir/node$exe" "$basedir/../browserslist/cli.js" $args
|
||||
} else {
|
||||
& "$basedir/node$exe" "$basedir/../browserslist/cli.js" $args
|
||||
}
|
||||
$ret=$LASTEXITCODE
|
||||
} else {
|
||||
# Support pipeline input
|
||||
if ($MyInvocation.ExpectingInput) {
|
||||
$input | & "node$exe" "$basedir/../browserslist/cli.js" $args
|
||||
} else {
|
||||
& "node$exe" "$basedir/../browserslist/cli.js" $args
|
||||
}
|
||||
$ret=$LASTEXITCODE
|
||||
}
|
||||
exit $ret
|
||||
16
frontend/node_modules/.bin/esbuild
generated
vendored
Normal file
16
frontend/node_modules/.bin/esbuild
generated
vendored
Normal file
@ -0,0 +1,16 @@
|
||||
#!/bin/sh
|
||||
basedir=$(dirname "$(echo "$0" | sed -e 's,\\,/,g')")
|
||||
|
||||
case `uname` in
|
||||
*CYGWIN*|*MINGW*|*MSYS*)
|
||||
if command -v cygpath > /dev/null 2>&1; then
|
||||
basedir=`cygpath -w "$basedir"`
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
|
||||
if [ -x "$basedir/node" ]; then
|
||||
exec "$basedir/node" "$basedir/../esbuild/bin/esbuild" "$@"
|
||||
else
|
||||
exec node "$basedir/../esbuild/bin/esbuild" "$@"
|
||||
fi
|
||||
17
frontend/node_modules/.bin/esbuild.cmd
generated
vendored
Normal file
17
frontend/node_modules/.bin/esbuild.cmd
generated
vendored
Normal file
@ -0,0 +1,17 @@
|
||||
@ECHO off
|
||||
GOTO start
|
||||
:find_dp0
|
||||
SET dp0=%~dp0
|
||||
EXIT /b
|
||||
:start
|
||||
SETLOCAL
|
||||
CALL :find_dp0
|
||||
|
||||
IF EXIST "%dp0%\node.exe" (
|
||||
SET "_prog=%dp0%\node.exe"
|
||||
) ELSE (
|
||||
SET "_prog=node"
|
||||
SET PATHEXT=%PATHEXT:;.JS;=;%
|
||||
)
|
||||
|
||||
endLocal & goto #_undefined_# 2>NUL || title %COMSPEC% & "%_prog%" "%dp0%\..\esbuild\bin\esbuild" %*
|
||||
28
frontend/node_modules/.bin/esbuild.ps1
generated
vendored
Normal file
28
frontend/node_modules/.bin/esbuild.ps1
generated
vendored
Normal file
@ -0,0 +1,28 @@
|
||||
#!/usr/bin/env pwsh
|
||||
$basedir=Split-Path $MyInvocation.MyCommand.Definition -Parent
|
||||
|
||||
$exe=""
|
||||
if ($PSVersionTable.PSVersion -lt "6.0" -or $IsWindows) {
|
||||
# Fix case when both the Windows and Linux builds of Node
|
||||
# are installed in the same directory
|
||||
$exe=".exe"
|
||||
}
|
||||
$ret=0
|
||||
if (Test-Path "$basedir/node$exe") {
|
||||
# Support pipeline input
|
||||
if ($MyInvocation.ExpectingInput) {
|
||||
$input | & "$basedir/node$exe" "$basedir/../esbuild/bin/esbuild" $args
|
||||
} else {
|
||||
& "$basedir/node$exe" "$basedir/../esbuild/bin/esbuild" $args
|
||||
}
|
||||
$ret=$LASTEXITCODE
|
||||
} else {
|
||||
# Support pipeline input
|
||||
if ($MyInvocation.ExpectingInput) {
|
||||
$input | & "node$exe" "$basedir/../esbuild/bin/esbuild" $args
|
||||
} else {
|
||||
& "node$exe" "$basedir/../esbuild/bin/esbuild" $args
|
||||
}
|
||||
$ret=$LASTEXITCODE
|
||||
}
|
||||
exit $ret
|
||||
16
frontend/node_modules/.bin/jsesc
generated
vendored
Normal file
16
frontend/node_modules/.bin/jsesc
generated
vendored
Normal file
@ -0,0 +1,16 @@
|
||||
#!/bin/sh
|
||||
basedir=$(dirname "$(echo "$0" | sed -e 's,\\,/,g')")
|
||||
|
||||
case `uname` in
|
||||
*CYGWIN*|*MINGW*|*MSYS*)
|
||||
if command -v cygpath > /dev/null 2>&1; then
|
||||
basedir=`cygpath -w "$basedir"`
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
|
||||
if [ -x "$basedir/node" ]; then
|
||||
exec "$basedir/node" "$basedir/../jsesc/bin/jsesc" "$@"
|
||||
else
|
||||
exec node "$basedir/../jsesc/bin/jsesc" "$@"
|
||||
fi
|
||||
17
frontend/node_modules/.bin/jsesc.cmd
generated
vendored
Normal file
17
frontend/node_modules/.bin/jsesc.cmd
generated
vendored
Normal file
@ -0,0 +1,17 @@
|
||||
@ECHO off
|
||||
GOTO start
|
||||
:find_dp0
|
||||
SET dp0=%~dp0
|
||||
EXIT /b
|
||||
:start
|
||||
SETLOCAL
|
||||
CALL :find_dp0
|
||||
|
||||
IF EXIST "%dp0%\node.exe" (
|
||||
SET "_prog=%dp0%\node.exe"
|
||||
) ELSE (
|
||||
SET "_prog=node"
|
||||
SET PATHEXT=%PATHEXT:;.JS;=;%
|
||||
)
|
||||
|
||||
endLocal & goto #_undefined_# 2>NUL || title %COMSPEC% & "%_prog%" "%dp0%\..\jsesc\bin\jsesc" %*
|
||||
28
frontend/node_modules/.bin/jsesc.ps1
generated
vendored
Normal file
28
frontend/node_modules/.bin/jsesc.ps1
generated
vendored
Normal file
@ -0,0 +1,28 @@
|
||||
#!/usr/bin/env pwsh
|
||||
$basedir=Split-Path $MyInvocation.MyCommand.Definition -Parent
|
||||
|
||||
$exe=""
|
||||
if ($PSVersionTable.PSVersion -lt "6.0" -or $IsWindows) {
|
||||
# Fix case when both the Windows and Linux builds of Node
|
||||
# are installed in the same directory
|
||||
$exe=".exe"
|
||||
}
|
||||
$ret=0
|
||||
if (Test-Path "$basedir/node$exe") {
|
||||
# Support pipeline input
|
||||
if ($MyInvocation.ExpectingInput) {
|
||||
$input | & "$basedir/node$exe" "$basedir/../jsesc/bin/jsesc" $args
|
||||
} else {
|
||||
& "$basedir/node$exe" "$basedir/../jsesc/bin/jsesc" $args
|
||||
}
|
||||
$ret=$LASTEXITCODE
|
||||
} else {
|
||||
# Support pipeline input
|
||||
if ($MyInvocation.ExpectingInput) {
|
||||
$input | & "node$exe" "$basedir/../jsesc/bin/jsesc" $args
|
||||
} else {
|
||||
& "node$exe" "$basedir/../jsesc/bin/jsesc" $args
|
||||
}
|
||||
$ret=$LASTEXITCODE
|
||||
}
|
||||
exit $ret
|
||||
16
frontend/node_modules/.bin/json5
generated
vendored
Normal file
16
frontend/node_modules/.bin/json5
generated
vendored
Normal file
@ -0,0 +1,16 @@
|
||||
#!/bin/sh
|
||||
basedir=$(dirname "$(echo "$0" | sed -e 's,\\,/,g')")
|
||||
|
||||
case `uname` in
|
||||
*CYGWIN*|*MINGW*|*MSYS*)
|
||||
if command -v cygpath > /dev/null 2>&1; then
|
||||
basedir=`cygpath -w "$basedir"`
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
|
||||
if [ -x "$basedir/node" ]; then
|
||||
exec "$basedir/node" "$basedir/../json5/lib/cli.js" "$@"
|
||||
else
|
||||
exec node "$basedir/../json5/lib/cli.js" "$@"
|
||||
fi
|
||||
17
frontend/node_modules/.bin/json5.cmd
generated
vendored
Normal file
17
frontend/node_modules/.bin/json5.cmd
generated
vendored
Normal file
@ -0,0 +1,17 @@
|
||||
@ECHO off
|
||||
GOTO start
|
||||
:find_dp0
|
||||
SET dp0=%~dp0
|
||||
EXIT /b
|
||||
:start
|
||||
SETLOCAL
|
||||
CALL :find_dp0
|
||||
|
||||
IF EXIST "%dp0%\node.exe" (
|
||||
SET "_prog=%dp0%\node.exe"
|
||||
) ELSE (
|
||||
SET "_prog=node"
|
||||
SET PATHEXT=%PATHEXT:;.JS;=;%
|
||||
)
|
||||
|
||||
endLocal & goto #_undefined_# 2>NUL || title %COMSPEC% & "%_prog%" "%dp0%\..\json5\lib\cli.js" %*
|
||||
28
frontend/node_modules/.bin/json5.ps1
generated
vendored
Normal file
28
frontend/node_modules/.bin/json5.ps1
generated
vendored
Normal file
@ -0,0 +1,28 @@
|
||||
#!/usr/bin/env pwsh
|
||||
$basedir=Split-Path $MyInvocation.MyCommand.Definition -Parent
|
||||
|
||||
$exe=""
|
||||
if ($PSVersionTable.PSVersion -lt "6.0" -or $IsWindows) {
|
||||
# Fix case when both the Windows and Linux builds of Node
|
||||
# are installed in the same directory
|
||||
$exe=".exe"
|
||||
}
|
||||
$ret=0
|
||||
if (Test-Path "$basedir/node$exe") {
|
||||
# Support pipeline input
|
||||
if ($MyInvocation.ExpectingInput) {
|
||||
$input | & "$basedir/node$exe" "$basedir/../json5/lib/cli.js" $args
|
||||
} else {
|
||||
& "$basedir/node$exe" "$basedir/../json5/lib/cli.js" $args
|
||||
}
|
||||
$ret=$LASTEXITCODE
|
||||
} else {
|
||||
# Support pipeline input
|
||||
if ($MyInvocation.ExpectingInput) {
|
||||
$input | & "node$exe" "$basedir/../json5/lib/cli.js" $args
|
||||
} else {
|
||||
& "node$exe" "$basedir/../json5/lib/cli.js" $args
|
||||
}
|
||||
$ret=$LASTEXITCODE
|
||||
}
|
||||
exit $ret
|
||||
16
frontend/node_modules/.bin/loose-envify
generated
vendored
Normal file
16
frontend/node_modules/.bin/loose-envify
generated
vendored
Normal file
@ -0,0 +1,16 @@
|
||||
#!/bin/sh
|
||||
basedir=$(dirname "$(echo "$0" | sed -e 's,\\,/,g')")
|
||||
|
||||
case `uname` in
|
||||
*CYGWIN*|*MINGW*|*MSYS*)
|
||||
if command -v cygpath > /dev/null 2>&1; then
|
||||
basedir=`cygpath -w "$basedir"`
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
|
||||
if [ -x "$basedir/node" ]; then
|
||||
exec "$basedir/node" "$basedir/../loose-envify/cli.js" "$@"
|
||||
else
|
||||
exec node "$basedir/../loose-envify/cli.js" "$@"
|
||||
fi
|
||||
17
frontend/node_modules/.bin/loose-envify.cmd
generated
vendored
Normal file
17
frontend/node_modules/.bin/loose-envify.cmd
generated
vendored
Normal file
@ -0,0 +1,17 @@
|
||||
@ECHO off
|
||||
GOTO start
|
||||
:find_dp0
|
||||
SET dp0=%~dp0
|
||||
EXIT /b
|
||||
:start
|
||||
SETLOCAL
|
||||
CALL :find_dp0
|
||||
|
||||
IF EXIST "%dp0%\node.exe" (
|
||||
SET "_prog=%dp0%\node.exe"
|
||||
) ELSE (
|
||||
SET "_prog=node"
|
||||
SET PATHEXT=%PATHEXT:;.JS;=;%
|
||||
)
|
||||
|
||||
endLocal & goto #_undefined_# 2>NUL || title %COMSPEC% & "%_prog%" "%dp0%\..\loose-envify\cli.js" %*
|
||||
28
frontend/node_modules/.bin/loose-envify.ps1
generated
vendored
Normal file
28
frontend/node_modules/.bin/loose-envify.ps1
generated
vendored
Normal file
@ -0,0 +1,28 @@
|
||||
#!/usr/bin/env pwsh
|
||||
$basedir=Split-Path $MyInvocation.MyCommand.Definition -Parent
|
||||
|
||||
$exe=""
|
||||
if ($PSVersionTable.PSVersion -lt "6.0" -or $IsWindows) {
|
||||
# Fix case when both the Windows and Linux builds of Node
|
||||
# are installed in the same directory
|
||||
$exe=".exe"
|
||||
}
|
||||
$ret=0
|
||||
if (Test-Path "$basedir/node$exe") {
|
||||
# Support pipeline input
|
||||
if ($MyInvocation.ExpectingInput) {
|
||||
$input | & "$basedir/node$exe" "$basedir/../loose-envify/cli.js" $args
|
||||
} else {
|
||||
& "$basedir/node$exe" "$basedir/../loose-envify/cli.js" $args
|
||||
}
|
||||
$ret=$LASTEXITCODE
|
||||
} else {
|
||||
# Support pipeline input
|
||||
if ($MyInvocation.ExpectingInput) {
|
||||
$input | & "node$exe" "$basedir/../loose-envify/cli.js" $args
|
||||
} else {
|
||||
& "node$exe" "$basedir/../loose-envify/cli.js" $args
|
||||
}
|
||||
$ret=$LASTEXITCODE
|
||||
}
|
||||
exit $ret
|
||||
16
frontend/node_modules/.bin/nanoid
generated
vendored
Normal file
16
frontend/node_modules/.bin/nanoid
generated
vendored
Normal file
@ -0,0 +1,16 @@
|
||||
#!/bin/sh
|
||||
basedir=$(dirname "$(echo "$0" | sed -e 's,\\,/,g')")
|
||||
|
||||
case `uname` in
|
||||
*CYGWIN*|*MINGW*|*MSYS*)
|
||||
if command -v cygpath > /dev/null 2>&1; then
|
||||
basedir=`cygpath -w "$basedir"`
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
|
||||
if [ -x "$basedir/node" ]; then
|
||||
exec "$basedir/node" "$basedir/../nanoid/bin/nanoid.cjs" "$@"
|
||||
else
|
||||
exec node "$basedir/../nanoid/bin/nanoid.cjs" "$@"
|
||||
fi
|
||||
17
frontend/node_modules/.bin/nanoid.cmd
generated
vendored
Normal file
17
frontend/node_modules/.bin/nanoid.cmd
generated
vendored
Normal file
@ -0,0 +1,17 @@
|
||||
@ECHO off
|
||||
GOTO start
|
||||
:find_dp0
|
||||
SET dp0=%~dp0
|
||||
EXIT /b
|
||||
:start
|
||||
SETLOCAL
|
||||
CALL :find_dp0
|
||||
|
||||
IF EXIST "%dp0%\node.exe" (
|
||||
SET "_prog=%dp0%\node.exe"
|
||||
) ELSE (
|
||||
SET "_prog=node"
|
||||
SET PATHEXT=%PATHEXT:;.JS;=;%
|
||||
)
|
||||
|
||||
endLocal & goto #_undefined_# 2>NUL || title %COMSPEC% & "%_prog%" "%dp0%\..\nanoid\bin\nanoid.cjs" %*
|
||||
28
frontend/node_modules/.bin/nanoid.ps1
generated
vendored
Normal file
28
frontend/node_modules/.bin/nanoid.ps1
generated
vendored
Normal file
@ -0,0 +1,28 @@
|
||||
#!/usr/bin/env pwsh
|
||||
$basedir=Split-Path $MyInvocation.MyCommand.Definition -Parent
|
||||
|
||||
$exe=""
|
||||
if ($PSVersionTable.PSVersion -lt "6.0" -or $IsWindows) {
|
||||
# Fix case when both the Windows and Linux builds of Node
|
||||
# are installed in the same directory
|
||||
$exe=".exe"
|
||||
}
|
||||
$ret=0
|
||||
if (Test-Path "$basedir/node$exe") {
|
||||
# Support pipeline input
|
||||
if ($MyInvocation.ExpectingInput) {
|
||||
$input | & "$basedir/node$exe" "$basedir/../nanoid/bin/nanoid.cjs" $args
|
||||
} else {
|
||||
& "$basedir/node$exe" "$basedir/../nanoid/bin/nanoid.cjs" $args
|
||||
}
|
||||
$ret=$LASTEXITCODE
|
||||
} else {
|
||||
# Support pipeline input
|
||||
if ($MyInvocation.ExpectingInput) {
|
||||
$input | & "node$exe" "$basedir/../nanoid/bin/nanoid.cjs" $args
|
||||
} else {
|
||||
& "node$exe" "$basedir/../nanoid/bin/nanoid.cjs" $args
|
||||
}
|
||||
$ret=$LASTEXITCODE
|
||||
}
|
||||
exit $ret
|
||||
16
frontend/node_modules/.bin/parser
generated
vendored
Normal file
16
frontend/node_modules/.bin/parser
generated
vendored
Normal file
@ -0,0 +1,16 @@
|
||||
#!/bin/sh
|
||||
basedir=$(dirname "$(echo "$0" | sed -e 's,\\,/,g')")
|
||||
|
||||
case `uname` in
|
||||
*CYGWIN*|*MINGW*|*MSYS*)
|
||||
if command -v cygpath > /dev/null 2>&1; then
|
||||
basedir=`cygpath -w "$basedir"`
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
|
||||
if [ -x "$basedir/node" ]; then
|
||||
exec "$basedir/node" "$basedir/../@babel/parser/bin/babel-parser.js" "$@"
|
||||
else
|
||||
exec node "$basedir/../@babel/parser/bin/babel-parser.js" "$@"
|
||||
fi
|
||||
17
frontend/node_modules/.bin/parser.cmd
generated
vendored
Normal file
17
frontend/node_modules/.bin/parser.cmd
generated
vendored
Normal file
@ -0,0 +1,17 @@
|
||||
@ECHO off
|
||||
GOTO start
|
||||
:find_dp0
|
||||
SET dp0=%~dp0
|
||||
EXIT /b
|
||||
:start
|
||||
SETLOCAL
|
||||
CALL :find_dp0
|
||||
|
||||
IF EXIST "%dp0%\node.exe" (
|
||||
SET "_prog=%dp0%\node.exe"
|
||||
) ELSE (
|
||||
SET "_prog=node"
|
||||
SET PATHEXT=%PATHEXT:;.JS;=;%
|
||||
)
|
||||
|
||||
endLocal & goto #_undefined_# 2>NUL || title %COMSPEC% & "%_prog%" "%dp0%\..\@babel\parser\bin\babel-parser.js" %*
|
||||
28
frontend/node_modules/.bin/parser.ps1
generated
vendored
Normal file
28
frontend/node_modules/.bin/parser.ps1
generated
vendored
Normal file
@ -0,0 +1,28 @@
|
||||
#!/usr/bin/env pwsh
|
||||
$basedir=Split-Path $MyInvocation.MyCommand.Definition -Parent
|
||||
|
||||
$exe=""
|
||||
if ($PSVersionTable.PSVersion -lt "6.0" -or $IsWindows) {
|
||||
# Fix case when both the Windows and Linux builds of Node
|
||||
# are installed in the same directory
|
||||
$exe=".exe"
|
||||
}
|
||||
$ret=0
|
||||
if (Test-Path "$basedir/node$exe") {
|
||||
# Support pipeline input
|
||||
if ($MyInvocation.ExpectingInput) {
|
||||
$input | & "$basedir/node$exe" "$basedir/../@babel/parser/bin/babel-parser.js" $args
|
||||
} else {
|
||||
& "$basedir/node$exe" "$basedir/../@babel/parser/bin/babel-parser.js" $args
|
||||
}
|
||||
$ret=$LASTEXITCODE
|
||||
} else {
|
||||
# Support pipeline input
|
||||
if ($MyInvocation.ExpectingInput) {
|
||||
$input | & "node$exe" "$basedir/../@babel/parser/bin/babel-parser.js" $args
|
||||
} else {
|
||||
& "node$exe" "$basedir/../@babel/parser/bin/babel-parser.js" $args
|
||||
}
|
||||
$ret=$LASTEXITCODE
|
||||
}
|
||||
exit $ret
|
||||
16
frontend/node_modules/.bin/rollup
generated
vendored
Normal file
16
frontend/node_modules/.bin/rollup
generated
vendored
Normal file
@ -0,0 +1,16 @@
|
||||
#!/bin/sh
|
||||
basedir=$(dirname "$(echo "$0" | sed -e 's,\\,/,g')")
|
||||
|
||||
case `uname` in
|
||||
*CYGWIN*|*MINGW*|*MSYS*)
|
||||
if command -v cygpath > /dev/null 2>&1; then
|
||||
basedir=`cygpath -w "$basedir"`
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
|
||||
if [ -x "$basedir/node" ]; then
|
||||
exec "$basedir/node" "$basedir/../rollup/dist/bin/rollup" "$@"
|
||||
else
|
||||
exec node "$basedir/../rollup/dist/bin/rollup" "$@"
|
||||
fi
|
||||
17
frontend/node_modules/.bin/rollup.cmd
generated
vendored
Normal file
17
frontend/node_modules/.bin/rollup.cmd
generated
vendored
Normal file
@ -0,0 +1,17 @@
|
||||
@ECHO off
|
||||
GOTO start
|
||||
:find_dp0
|
||||
SET dp0=%~dp0
|
||||
EXIT /b
|
||||
:start
|
||||
SETLOCAL
|
||||
CALL :find_dp0
|
||||
|
||||
IF EXIST "%dp0%\node.exe" (
|
||||
SET "_prog=%dp0%\node.exe"
|
||||
) ELSE (
|
||||
SET "_prog=node"
|
||||
SET PATHEXT=%PATHEXT:;.JS;=;%
|
||||
)
|
||||
|
||||
endLocal & goto #_undefined_# 2>NUL || title %COMSPEC% & "%_prog%" "%dp0%\..\rollup\dist\bin\rollup" %*
|
||||
28
frontend/node_modules/.bin/rollup.ps1
generated
vendored
Normal file
28
frontend/node_modules/.bin/rollup.ps1
generated
vendored
Normal file
@ -0,0 +1,28 @@
|
||||
#!/usr/bin/env pwsh
|
||||
$basedir=Split-Path $MyInvocation.MyCommand.Definition -Parent
|
||||
|
||||
$exe=""
|
||||
if ($PSVersionTable.PSVersion -lt "6.0" -or $IsWindows) {
|
||||
# Fix case when both the Windows and Linux builds of Node
|
||||
# are installed in the same directory
|
||||
$exe=".exe"
|
||||
}
|
||||
$ret=0
|
||||
if (Test-Path "$basedir/node$exe") {
|
||||
# Support pipeline input
|
||||
if ($MyInvocation.ExpectingInput) {
|
||||
$input | & "$basedir/node$exe" "$basedir/../rollup/dist/bin/rollup" $args
|
||||
} else {
|
||||
& "$basedir/node$exe" "$basedir/../rollup/dist/bin/rollup" $args
|
||||
}
|
||||
$ret=$LASTEXITCODE
|
||||
} else {
|
||||
# Support pipeline input
|
||||
if ($MyInvocation.ExpectingInput) {
|
||||
$input | & "node$exe" "$basedir/../rollup/dist/bin/rollup" $args
|
||||
} else {
|
||||
& "node$exe" "$basedir/../rollup/dist/bin/rollup" $args
|
||||
}
|
||||
$ret=$LASTEXITCODE
|
||||
}
|
||||
exit $ret
|
||||
16
frontend/node_modules/.bin/semver
generated
vendored
Normal file
16
frontend/node_modules/.bin/semver
generated
vendored
Normal file
@ -0,0 +1,16 @@
|
||||
#!/bin/sh
|
||||
basedir=$(dirname "$(echo "$0" | sed -e 's,\\,/,g')")
|
||||
|
||||
case `uname` in
|
||||
*CYGWIN*|*MINGW*|*MSYS*)
|
||||
if command -v cygpath > /dev/null 2>&1; then
|
||||
basedir=`cygpath -w "$basedir"`
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
|
||||
if [ -x "$basedir/node" ]; then
|
||||
exec "$basedir/node" "$basedir/../semver/bin/semver.js" "$@"
|
||||
else
|
||||
exec node "$basedir/../semver/bin/semver.js" "$@"
|
||||
fi
|
||||
17
frontend/node_modules/.bin/semver.cmd
generated
vendored
Normal file
17
frontend/node_modules/.bin/semver.cmd
generated
vendored
Normal file
@ -0,0 +1,17 @@
|
||||
@ECHO off
|
||||
GOTO start
|
||||
:find_dp0
|
||||
SET dp0=%~dp0
|
||||
EXIT /b
|
||||
:start
|
||||
SETLOCAL
|
||||
CALL :find_dp0
|
||||
|
||||
IF EXIST "%dp0%\node.exe" (
|
||||
SET "_prog=%dp0%\node.exe"
|
||||
) ELSE (
|
||||
SET "_prog=node"
|
||||
SET PATHEXT=%PATHEXT:;.JS;=;%
|
||||
)
|
||||
|
||||
endLocal & goto #_undefined_# 2>NUL || title %COMSPEC% & "%_prog%" "%dp0%\..\semver\bin\semver.js" %*
|
||||
28
frontend/node_modules/.bin/semver.ps1
generated
vendored
Normal file
28
frontend/node_modules/.bin/semver.ps1
generated
vendored
Normal file
@ -0,0 +1,28 @@
|
||||
#!/usr/bin/env pwsh
|
||||
$basedir=Split-Path $MyInvocation.MyCommand.Definition -Parent
|
||||
|
||||
$exe=""
|
||||
if ($PSVersionTable.PSVersion -lt "6.0" -or $IsWindows) {
|
||||
# Fix case when both the Windows and Linux builds of Node
|
||||
# are installed in the same directory
|
||||
$exe=".exe"
|
||||
}
|
||||
$ret=0
|
||||
if (Test-Path "$basedir/node$exe") {
|
||||
# Support pipeline input
|
||||
if ($MyInvocation.ExpectingInput) {
|
||||
$input | & "$basedir/node$exe" "$basedir/../semver/bin/semver.js" $args
|
||||
} else {
|
||||
& "$basedir/node$exe" "$basedir/../semver/bin/semver.js" $args
|
||||
}
|
||||
$ret=$LASTEXITCODE
|
||||
} else {
|
||||
# Support pipeline input
|
||||
if ($MyInvocation.ExpectingInput) {
|
||||
$input | & "node$exe" "$basedir/../semver/bin/semver.js" $args
|
||||
} else {
|
||||
& "node$exe" "$basedir/../semver/bin/semver.js" $args
|
||||
}
|
||||
$ret=$LASTEXITCODE
|
||||
}
|
||||
exit $ret
|
||||
16
frontend/node_modules/.bin/tsc
generated
vendored
Normal file
16
frontend/node_modules/.bin/tsc
generated
vendored
Normal file
@ -0,0 +1,16 @@
|
||||
#!/bin/sh
|
||||
basedir=$(dirname "$(echo "$0" | sed -e 's,\\,/,g')")
|
||||
|
||||
case `uname` in
|
||||
*CYGWIN*|*MINGW*|*MSYS*)
|
||||
if command -v cygpath > /dev/null 2>&1; then
|
||||
basedir=`cygpath -w "$basedir"`
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
|
||||
if [ -x "$basedir/node" ]; then
|
||||
exec "$basedir/node" "$basedir/../typescript/bin/tsc" "$@"
|
||||
else
|
||||
exec node "$basedir/../typescript/bin/tsc" "$@"
|
||||
fi
|
||||
17
frontend/node_modules/.bin/tsc.cmd
generated
vendored
Normal file
17
frontend/node_modules/.bin/tsc.cmd
generated
vendored
Normal file
@ -0,0 +1,17 @@
|
||||
@ECHO off
|
||||
GOTO start
|
||||
:find_dp0
|
||||
SET dp0=%~dp0
|
||||
EXIT /b
|
||||
:start
|
||||
SETLOCAL
|
||||
CALL :find_dp0
|
||||
|
||||
IF EXIST "%dp0%\node.exe" (
|
||||
SET "_prog=%dp0%\node.exe"
|
||||
) ELSE (
|
||||
SET "_prog=node"
|
||||
SET PATHEXT=%PATHEXT:;.JS;=;%
|
||||
)
|
||||
|
||||
endLocal & goto #_undefined_# 2>NUL || title %COMSPEC% & "%_prog%" "%dp0%\..\typescript\bin\tsc" %*
|
||||
28
frontend/node_modules/.bin/tsc.ps1
generated
vendored
Normal file
28
frontend/node_modules/.bin/tsc.ps1
generated
vendored
Normal file
@ -0,0 +1,28 @@
|
||||
#!/usr/bin/env pwsh
|
||||
$basedir=Split-Path $MyInvocation.MyCommand.Definition -Parent
|
||||
|
||||
$exe=""
|
||||
if ($PSVersionTable.PSVersion -lt "6.0" -or $IsWindows) {
|
||||
# Fix case when both the Windows and Linux builds of Node
|
||||
# are installed in the same directory
|
||||
$exe=".exe"
|
||||
}
|
||||
$ret=0
|
||||
if (Test-Path "$basedir/node$exe") {
|
||||
# Support pipeline input
|
||||
if ($MyInvocation.ExpectingInput) {
|
||||
$input | & "$basedir/node$exe" "$basedir/../typescript/bin/tsc" $args
|
||||
} else {
|
||||
& "$basedir/node$exe" "$basedir/../typescript/bin/tsc" $args
|
||||
}
|
||||
$ret=$LASTEXITCODE
|
||||
} else {
|
||||
# Support pipeline input
|
||||
if ($MyInvocation.ExpectingInput) {
|
||||
$input | & "node$exe" "$basedir/../typescript/bin/tsc" $args
|
||||
} else {
|
||||
& "node$exe" "$basedir/../typescript/bin/tsc" $args
|
||||
}
|
||||
$ret=$LASTEXITCODE
|
||||
}
|
||||
exit $ret
|
||||
16
frontend/node_modules/.bin/tsserver
generated
vendored
Normal file
16
frontend/node_modules/.bin/tsserver
generated
vendored
Normal file
@ -0,0 +1,16 @@
|
||||
#!/bin/sh
|
||||
basedir=$(dirname "$(echo "$0" | sed -e 's,\\,/,g')")
|
||||
|
||||
case `uname` in
|
||||
*CYGWIN*|*MINGW*|*MSYS*)
|
||||
if command -v cygpath > /dev/null 2>&1; then
|
||||
basedir=`cygpath -w "$basedir"`
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
|
||||
if [ -x "$basedir/node" ]; then
|
||||
exec "$basedir/node" "$basedir/../typescript/bin/tsserver" "$@"
|
||||
else
|
||||
exec node "$basedir/../typescript/bin/tsserver" "$@"
|
||||
fi
|
||||
17
frontend/node_modules/.bin/tsserver.cmd
generated
vendored
Normal file
17
frontend/node_modules/.bin/tsserver.cmd
generated
vendored
Normal file
@ -0,0 +1,17 @@
|
||||
@ECHO off
|
||||
GOTO start
|
||||
:find_dp0
|
||||
SET dp0=%~dp0
|
||||
EXIT /b
|
||||
:start
|
||||
SETLOCAL
|
||||
CALL :find_dp0
|
||||
|
||||
IF EXIST "%dp0%\node.exe" (
|
||||
SET "_prog=%dp0%\node.exe"
|
||||
) ELSE (
|
||||
SET "_prog=node"
|
||||
SET PATHEXT=%PATHEXT:;.JS;=;%
|
||||
)
|
||||
|
||||
endLocal & goto #_undefined_# 2>NUL || title %COMSPEC% & "%_prog%" "%dp0%\..\typescript\bin\tsserver" %*
|
||||
28
frontend/node_modules/.bin/tsserver.ps1
generated
vendored
Normal file
28
frontend/node_modules/.bin/tsserver.ps1
generated
vendored
Normal file
@ -0,0 +1,28 @@
|
||||
#!/usr/bin/env pwsh
|
||||
$basedir=Split-Path $MyInvocation.MyCommand.Definition -Parent
|
||||
|
||||
$exe=""
|
||||
if ($PSVersionTable.PSVersion -lt "6.0" -or $IsWindows) {
|
||||
# Fix case when both the Windows and Linux builds of Node
|
||||
# are installed in the same directory
|
||||
$exe=".exe"
|
||||
}
|
||||
$ret=0
|
||||
if (Test-Path "$basedir/node$exe") {
|
||||
# Support pipeline input
|
||||
if ($MyInvocation.ExpectingInput) {
|
||||
$input | & "$basedir/node$exe" "$basedir/../typescript/bin/tsserver" $args
|
||||
} else {
|
||||
& "$basedir/node$exe" "$basedir/../typescript/bin/tsserver" $args
|
||||
}
|
||||
$ret=$LASTEXITCODE
|
||||
} else {
|
||||
# Support pipeline input
|
||||
if ($MyInvocation.ExpectingInput) {
|
||||
$input | & "node$exe" "$basedir/../typescript/bin/tsserver" $args
|
||||
} else {
|
||||
& "node$exe" "$basedir/../typescript/bin/tsserver" $args
|
||||
}
|
||||
$ret=$LASTEXITCODE
|
||||
}
|
||||
exit $ret
|
||||
16
frontend/node_modules/.bin/update-browserslist-db
generated
vendored
Normal file
16
frontend/node_modules/.bin/update-browserslist-db
generated
vendored
Normal file
@ -0,0 +1,16 @@
|
||||
#!/bin/sh
|
||||
basedir=$(dirname "$(echo "$0" | sed -e 's,\\,/,g')")
|
||||
|
||||
case `uname` in
|
||||
*CYGWIN*|*MINGW*|*MSYS*)
|
||||
if command -v cygpath > /dev/null 2>&1; then
|
||||
basedir=`cygpath -w "$basedir"`
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
|
||||
if [ -x "$basedir/node" ]; then
|
||||
exec "$basedir/node" "$basedir/../update-browserslist-db/cli.js" "$@"
|
||||
else
|
||||
exec node "$basedir/../update-browserslist-db/cli.js" "$@"
|
||||
fi
|
||||
17
frontend/node_modules/.bin/update-browserslist-db.cmd
generated
vendored
Normal file
17
frontend/node_modules/.bin/update-browserslist-db.cmd
generated
vendored
Normal file
@ -0,0 +1,17 @@
|
||||
@ECHO off
|
||||
GOTO start
|
||||
:find_dp0
|
||||
SET dp0=%~dp0
|
||||
EXIT /b
|
||||
:start
|
||||
SETLOCAL
|
||||
CALL :find_dp0
|
||||
|
||||
IF EXIST "%dp0%\node.exe" (
|
||||
SET "_prog=%dp0%\node.exe"
|
||||
) ELSE (
|
||||
SET "_prog=node"
|
||||
SET PATHEXT=%PATHEXT:;.JS;=;%
|
||||
)
|
||||
|
||||
endLocal & goto #_undefined_# 2>NUL || title %COMSPEC% & "%_prog%" "%dp0%\..\update-browserslist-db\cli.js" %*
|
||||
28
frontend/node_modules/.bin/update-browserslist-db.ps1
generated
vendored
Normal file
28
frontend/node_modules/.bin/update-browserslist-db.ps1
generated
vendored
Normal file
@ -0,0 +1,28 @@
|
||||
#!/usr/bin/env pwsh
|
||||
$basedir=Split-Path $MyInvocation.MyCommand.Definition -Parent
|
||||
|
||||
$exe=""
|
||||
if ($PSVersionTable.PSVersion -lt "6.0" -or $IsWindows) {
|
||||
# Fix case when both the Windows and Linux builds of Node
|
||||
# are installed in the same directory
|
||||
$exe=".exe"
|
||||
}
|
||||
$ret=0
|
||||
if (Test-Path "$basedir/node$exe") {
|
||||
# Support pipeline input
|
||||
if ($MyInvocation.ExpectingInput) {
|
||||
$input | & "$basedir/node$exe" "$basedir/../update-browserslist-db/cli.js" $args
|
||||
} else {
|
||||
& "$basedir/node$exe" "$basedir/../update-browserslist-db/cli.js" $args
|
||||
}
|
||||
$ret=$LASTEXITCODE
|
||||
} else {
|
||||
# Support pipeline input
|
||||
if ($MyInvocation.ExpectingInput) {
|
||||
$input | & "node$exe" "$basedir/../update-browserslist-db/cli.js" $args
|
||||
} else {
|
||||
& "node$exe" "$basedir/../update-browserslist-db/cli.js" $args
|
||||
}
|
||||
$ret=$LASTEXITCODE
|
||||
}
|
||||
exit $ret
|
||||
16
frontend/node_modules/.bin/vite
generated
vendored
Normal file
16
frontend/node_modules/.bin/vite
generated
vendored
Normal file
@ -0,0 +1,16 @@
|
||||
#!/bin/sh
|
||||
basedir=$(dirname "$(echo "$0" | sed -e 's,\\,/,g')")
|
||||
|
||||
case `uname` in
|
||||
*CYGWIN*|*MINGW*|*MSYS*)
|
||||
if command -v cygpath > /dev/null 2>&1; then
|
||||
basedir=`cygpath -w "$basedir"`
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
|
||||
if [ -x "$basedir/node" ]; then
|
||||
exec "$basedir/node" "$basedir/../vite/bin/vite.js" "$@"
|
||||
else
|
||||
exec node "$basedir/../vite/bin/vite.js" "$@"
|
||||
fi
|
||||
17
frontend/node_modules/.bin/vite.cmd
generated
vendored
Normal file
17
frontend/node_modules/.bin/vite.cmd
generated
vendored
Normal file
@ -0,0 +1,17 @@
|
||||
@ECHO off
|
||||
GOTO start
|
||||
:find_dp0
|
||||
SET dp0=%~dp0
|
||||
EXIT /b
|
||||
:start
|
||||
SETLOCAL
|
||||
CALL :find_dp0
|
||||
|
||||
IF EXIST "%dp0%\node.exe" (
|
||||
SET "_prog=%dp0%\node.exe"
|
||||
) ELSE (
|
||||
SET "_prog=node"
|
||||
SET PATHEXT=%PATHEXT:;.JS;=;%
|
||||
)
|
||||
|
||||
endLocal & goto #_undefined_# 2>NUL || title %COMSPEC% & "%_prog%" "%dp0%\..\vite\bin\vite.js" %*
|
||||
28
frontend/node_modules/.bin/vite.ps1
generated
vendored
Normal file
28
frontend/node_modules/.bin/vite.ps1
generated
vendored
Normal file
@ -0,0 +1,28 @@
|
||||
#!/usr/bin/env pwsh
|
||||
$basedir=Split-Path $MyInvocation.MyCommand.Definition -Parent
|
||||
|
||||
$exe=""
|
||||
if ($PSVersionTable.PSVersion -lt "6.0" -or $IsWindows) {
|
||||
# Fix case when both the Windows and Linux builds of Node
|
||||
# are installed in the same directory
|
||||
$exe=".exe"
|
||||
}
|
||||
$ret=0
|
||||
if (Test-Path "$basedir/node$exe") {
|
||||
# Support pipeline input
|
||||
if ($MyInvocation.ExpectingInput) {
|
||||
$input | & "$basedir/node$exe" "$basedir/../vite/bin/vite.js" $args
|
||||
} else {
|
||||
& "$basedir/node$exe" "$basedir/../vite/bin/vite.js" $args
|
||||
}
|
||||
$ret=$LASTEXITCODE
|
||||
} else {
|
||||
# Support pipeline input
|
||||
if ($MyInvocation.ExpectingInput) {
|
||||
$input | & "node$exe" "$basedir/../vite/bin/vite.js" $args
|
||||
} else {
|
||||
& "node$exe" "$basedir/../vite/bin/vite.js" $args
|
||||
}
|
||||
$ret=$LASTEXITCODE
|
||||
}
|
||||
exit $ret
|
||||
1733
frontend/node_modules/.package-lock.json
generated
vendored
Normal file
1733
frontend/node_modules/.package-lock.json
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
22
frontend/node_modules/@babel/code-frame/LICENSE
generated
vendored
Normal file
22
frontend/node_modules/@babel/code-frame/LICENSE
generated
vendored
Normal file
@ -0,0 +1,22 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2014-present Sebastian McKenzie and other contributors
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of this software and associated documentation files (the
|
||||
"Software"), to deal in the Software without restriction, including
|
||||
without limitation the rights to use, copy, modify, merge, publish,
|
||||
distribute, sublicense, and/or sell copies of the Software, and to
|
||||
permit persons to whom the Software is furnished to do so, subject to
|
||||
the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be
|
||||
included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
19
frontend/node_modules/@babel/code-frame/README.md
generated
vendored
Normal file
19
frontend/node_modules/@babel/code-frame/README.md
generated
vendored
Normal file
@ -0,0 +1,19 @@
|
||||
# @babel/code-frame
|
||||
|
||||
> Generate errors that contain a code frame that point to source locations.
|
||||
|
||||
See our website [@babel/code-frame](https://babeljs.io/docs/babel-code-frame) for more information.
|
||||
|
||||
## Install
|
||||
|
||||
Using npm:
|
||||
|
||||
```sh
|
||||
npm install --save-dev @babel/code-frame
|
||||
```
|
||||
|
||||
or using yarn:
|
||||
|
||||
```sh
|
||||
yarn add @babel/code-frame --dev
|
||||
```
|
||||
217
frontend/node_modules/@babel/code-frame/lib/index.js
generated
vendored
Normal file
217
frontend/node_modules/@babel/code-frame/lib/index.js
generated
vendored
Normal file
@ -0,0 +1,217 @@
|
||||
'use strict';
|
||||
|
||||
Object.defineProperty(exports, '__esModule', { value: true });
|
||||
|
||||
var picocolors = require('picocolors');
|
||||
var jsTokens = require('js-tokens');
|
||||
var helperValidatorIdentifier = require('@babel/helper-validator-identifier');
|
||||
|
||||
function isColorSupported() {
|
||||
return (typeof process === "object" && (process.env.FORCE_COLOR === "0" || process.env.FORCE_COLOR === "false") ? false : picocolors.isColorSupported
|
||||
);
|
||||
}
|
||||
const compose = (f, g) => v => f(g(v));
|
||||
function buildDefs(colors) {
|
||||
return {
|
||||
keyword: colors.cyan,
|
||||
capitalized: colors.yellow,
|
||||
jsxIdentifier: colors.yellow,
|
||||
punctuator: colors.yellow,
|
||||
number: colors.magenta,
|
||||
string: colors.green,
|
||||
regex: colors.magenta,
|
||||
comment: colors.gray,
|
||||
invalid: compose(compose(colors.white, colors.bgRed), colors.bold),
|
||||
gutter: colors.gray,
|
||||
marker: compose(colors.red, colors.bold),
|
||||
message: compose(colors.red, colors.bold),
|
||||
reset: colors.reset
|
||||
};
|
||||
}
|
||||
const defsOn = buildDefs(picocolors.createColors(true));
|
||||
const defsOff = buildDefs(picocolors.createColors(false));
|
||||
function getDefs(enabled) {
|
||||
return enabled ? defsOn : defsOff;
|
||||
}
|
||||
|
||||
const sometimesKeywords = new Set(["as", "async", "from", "get", "of", "set"]);
|
||||
const NEWLINE$1 = /\r\n|[\n\r\u2028\u2029]/;
|
||||
const BRACKET = /^[()[\]{}]$/;
|
||||
let tokenize;
|
||||
const JSX_TAG = /^[a-z][\w-]*$/i;
|
||||
const getTokenType = function (token, offset, text) {
|
||||
if (token.type === "name") {
|
||||
const tokenValue = token.value;
|
||||
if (helperValidatorIdentifier.isKeyword(tokenValue) || helperValidatorIdentifier.isStrictReservedWord(tokenValue, true) || sometimesKeywords.has(tokenValue)) {
|
||||
return "keyword";
|
||||
}
|
||||
if (JSX_TAG.test(tokenValue) && (text[offset - 1] === "<" || text.slice(offset - 2, offset) === "</")) {
|
||||
return "jsxIdentifier";
|
||||
}
|
||||
const firstChar = String.fromCodePoint(tokenValue.codePointAt(0));
|
||||
if (firstChar !== firstChar.toLowerCase()) {
|
||||
return "capitalized";
|
||||
}
|
||||
}
|
||||
if (token.type === "punctuator" && BRACKET.test(token.value)) {
|
||||
return "bracket";
|
||||
}
|
||||
if (token.type === "invalid" && (token.value === "@" || token.value === "#")) {
|
||||
return "punctuator";
|
||||
}
|
||||
return token.type;
|
||||
};
|
||||
tokenize = function* (text) {
|
||||
let match;
|
||||
while (match = jsTokens.default.exec(text)) {
|
||||
const token = jsTokens.matchToToken(match);
|
||||
yield {
|
||||
type: getTokenType(token, match.index, text),
|
||||
value: token.value
|
||||
};
|
||||
}
|
||||
};
|
||||
function highlight(text) {
|
||||
if (text === "") return "";
|
||||
const defs = getDefs(true);
|
||||
let highlighted = "";
|
||||
for (const {
|
||||
type,
|
||||
value
|
||||
} of tokenize(text)) {
|
||||
if (type in defs) {
|
||||
highlighted += value.split(NEWLINE$1).map(str => defs[type](str)).join("\n");
|
||||
} else {
|
||||
highlighted += value;
|
||||
}
|
||||
}
|
||||
return highlighted;
|
||||
}
|
||||
|
||||
let deprecationWarningShown = false;
|
||||
const NEWLINE = /\r\n|[\n\r\u2028\u2029]/;
|
||||
function getMarkerLines(loc, source, opts, startLineBaseZero) {
|
||||
const startLoc = Object.assign({
|
||||
column: 0,
|
||||
line: -1
|
||||
}, loc.start);
|
||||
const endLoc = Object.assign({}, startLoc, loc.end);
|
||||
const {
|
||||
linesAbove = 2,
|
||||
linesBelow = 3
|
||||
} = opts || {};
|
||||
const startLine = startLoc.line - startLineBaseZero;
|
||||
const startColumn = startLoc.column;
|
||||
const endLine = endLoc.line - startLineBaseZero;
|
||||
const endColumn = endLoc.column;
|
||||
let start = Math.max(startLine - (linesAbove + 1), 0);
|
||||
let end = Math.min(source.length, endLine + linesBelow);
|
||||
if (startLine === -1) {
|
||||
start = 0;
|
||||
}
|
||||
if (endLine === -1) {
|
||||
end = source.length;
|
||||
}
|
||||
const lineDiff = endLine - startLine;
|
||||
const markerLines = {};
|
||||
if (lineDiff) {
|
||||
for (let i = 0; i <= lineDiff; i++) {
|
||||
const lineNumber = i + startLine;
|
||||
if (!startColumn) {
|
||||
markerLines[lineNumber] = true;
|
||||
} else if (i === 0) {
|
||||
const sourceLength = source[lineNumber - 1].length;
|
||||
markerLines[lineNumber] = [startColumn, sourceLength - startColumn + 1];
|
||||
} else if (i === lineDiff) {
|
||||
markerLines[lineNumber] = [0, endColumn];
|
||||
} else {
|
||||
const sourceLength = source[lineNumber - i].length;
|
||||
markerLines[lineNumber] = [0, sourceLength];
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (startColumn === endColumn) {
|
||||
if (startColumn) {
|
||||
markerLines[startLine] = [startColumn, 0];
|
||||
} else {
|
||||
markerLines[startLine] = true;
|
||||
}
|
||||
} else {
|
||||
markerLines[startLine] = [startColumn, endColumn - startColumn];
|
||||
}
|
||||
}
|
||||
return {
|
||||
start,
|
||||
end,
|
||||
markerLines
|
||||
};
|
||||
}
|
||||
function codeFrameColumns(rawLines, loc, opts = {}) {
|
||||
const shouldHighlight = opts.forceColor || isColorSupported() && opts.highlightCode;
|
||||
const startLineBaseZero = (opts.startLine || 1) - 1;
|
||||
const defs = getDefs(shouldHighlight);
|
||||
const lines = rawLines.split(NEWLINE);
|
||||
const {
|
||||
start,
|
||||
end,
|
||||
markerLines
|
||||
} = getMarkerLines(loc, lines, opts, startLineBaseZero);
|
||||
const hasColumns = loc.start && typeof loc.start.column === "number";
|
||||
const numberMaxWidth = String(end + startLineBaseZero).length;
|
||||
const highlightedLines = shouldHighlight ? highlight(rawLines) : rawLines;
|
||||
let frame = highlightedLines.split(NEWLINE, end).slice(start, end).map((line, index) => {
|
||||
const number = start + 1 + index;
|
||||
const paddedNumber = ` ${number + startLineBaseZero}`.slice(-numberMaxWidth);
|
||||
const gutter = ` ${paddedNumber} |`;
|
||||
const hasMarker = markerLines[number];
|
||||
const lastMarkerLine = !markerLines[number + 1];
|
||||
if (hasMarker) {
|
||||
let markerLine = "";
|
||||
if (Array.isArray(hasMarker)) {
|
||||
const markerSpacing = line.slice(0, Math.max(hasMarker[0] - 1, 0)).replace(/[^\t]/g, " ");
|
||||
const numberOfMarkers = hasMarker[1] || 1;
|
||||
markerLine = ["\n ", defs.gutter(gutter.replace(/\d/g, " ")), " ", markerSpacing, defs.marker("^").repeat(numberOfMarkers)].join("");
|
||||
if (lastMarkerLine && opts.message) {
|
||||
markerLine += " " + defs.message(opts.message);
|
||||
}
|
||||
}
|
||||
return [defs.marker(">"), defs.gutter(gutter), line.length > 0 ? ` ${line}` : "", markerLine].join("");
|
||||
} else {
|
||||
return ` ${defs.gutter(gutter)}${line.length > 0 ? ` ${line}` : ""}`;
|
||||
}
|
||||
}).join("\n");
|
||||
if (opts.message && !hasColumns) {
|
||||
frame = `${" ".repeat(numberMaxWidth + 1)}${opts.message}\n${frame}`;
|
||||
}
|
||||
if (shouldHighlight) {
|
||||
return defs.reset(frame);
|
||||
} else {
|
||||
return frame;
|
||||
}
|
||||
}
|
||||
function index (rawLines, lineNumber, colNumber, opts = {}) {
|
||||
if (!deprecationWarningShown) {
|
||||
deprecationWarningShown = true;
|
||||
const message = "Passing lineNumber and colNumber is deprecated to @babel/code-frame. Please use `codeFrameColumns`.";
|
||||
if (process.emitWarning) {
|
||||
process.emitWarning(message, "DeprecationWarning");
|
||||
} else {
|
||||
const deprecationError = new Error(message);
|
||||
deprecationError.name = "DeprecationWarning";
|
||||
console.warn(new Error(message));
|
||||
}
|
||||
}
|
||||
colNumber = Math.max(colNumber, 0);
|
||||
const location = {
|
||||
start: {
|
||||
column: colNumber,
|
||||
line: lineNumber
|
||||
}
|
||||
};
|
||||
return codeFrameColumns(rawLines, location, opts);
|
||||
}
|
||||
|
||||
exports.codeFrameColumns = codeFrameColumns;
|
||||
exports.default = index;
|
||||
exports.highlight = highlight;
|
||||
//# sourceMappingURL=index.js.map
|
||||
1
frontend/node_modules/@babel/code-frame/lib/index.js.map
generated
vendored
Normal file
1
frontend/node_modules/@babel/code-frame/lib/index.js.map
generated
vendored
Normal file
File diff suppressed because one or more lines are too long
32
frontend/node_modules/@babel/code-frame/package.json
generated
vendored
Normal file
32
frontend/node_modules/@babel/code-frame/package.json
generated
vendored
Normal file
@ -0,0 +1,32 @@
|
||||
{
|
||||
"name": "@babel/code-frame",
|
||||
"version": "7.29.7",
|
||||
"description": "Generate errors that contain a code frame that point to source locations.",
|
||||
"author": "The Babel Team (https://babel.dev/team)",
|
||||
"homepage": "https://babel.dev/docs/en/next/babel-code-frame",
|
||||
"bugs": "https://github.com/babel/babel/issues?utf8=%E2%9C%93&q=is%3Aissue+is%3Aopen",
|
||||
"license": "MIT",
|
||||
"publishConfig": {
|
||||
"access": "public"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/babel/babel.git",
|
||||
"directory": "packages/babel-code-frame"
|
||||
},
|
||||
"main": "./lib/index.js",
|
||||
"dependencies": {
|
||||
"@babel/helper-validator-identifier": "^7.29.7",
|
||||
"js-tokens": "^4.0.0",
|
||||
"picocolors": "^1.1.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"charcodes": "^0.2.0",
|
||||
"import-meta-resolve": "^4.1.0",
|
||||
"strip-ansi": "^4.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=6.9.0"
|
||||
},
|
||||
"type": "commonjs"
|
||||
}
|
||||
22
frontend/node_modules/@babel/compat-data/LICENSE
generated
vendored
Normal file
22
frontend/node_modules/@babel/compat-data/LICENSE
generated
vendored
Normal file
@ -0,0 +1,22 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2014-present Sebastian McKenzie and other contributors
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of this software and associated documentation files (the
|
||||
"Software"), to deal in the Software without restriction, including
|
||||
without limitation the rights to use, copy, modify, merge, publish,
|
||||
distribute, sublicense, and/or sell copies of the Software, and to
|
||||
permit persons to whom the Software is furnished to do so, subject to
|
||||
the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be
|
||||
included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
19
frontend/node_modules/@babel/compat-data/README.md
generated
vendored
Normal file
19
frontend/node_modules/@babel/compat-data/README.md
generated
vendored
Normal file
@ -0,0 +1,19 @@
|
||||
# @babel/compat-data
|
||||
|
||||
> The compat-data to determine required Babel plugins
|
||||
|
||||
See our website [@babel/compat-data](https://babeljs.io/docs/babel-compat-data) for more information.
|
||||
|
||||
## Install
|
||||
|
||||
Using npm:
|
||||
|
||||
```sh
|
||||
npm install --save @babel/compat-data
|
||||
```
|
||||
|
||||
or using yarn:
|
||||
|
||||
```sh
|
||||
yarn add @babel/compat-data
|
||||
```
|
||||
2
frontend/node_modules/@babel/compat-data/corejs2-built-ins.js
generated
vendored
Normal file
2
frontend/node_modules/@babel/compat-data/corejs2-built-ins.js
generated
vendored
Normal file
@ -0,0 +1,2 @@
|
||||
// Todo (Babel 8): remove this file as Babel 8 drop support of core-js 2
|
||||
module.exports = require("./data/corejs2-built-ins.json");
|
||||
2
frontend/node_modules/@babel/compat-data/corejs3-shipped-proposals.js
generated
vendored
Normal file
2
frontend/node_modules/@babel/compat-data/corejs3-shipped-proposals.js
generated
vendored
Normal file
@ -0,0 +1,2 @@
|
||||
// Todo (Babel 8): remove this file now that it is included in babel-plugin-polyfill-corejs3
|
||||
module.exports = require("./data/corejs3-shipped-proposals.json");
|
||||
2120
frontend/node_modules/@babel/compat-data/data/corejs2-built-ins.json
generated
vendored
Normal file
2120
frontend/node_modules/@babel/compat-data/data/corejs2-built-ins.json
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
5
frontend/node_modules/@babel/compat-data/data/corejs3-shipped-proposals.json
generated
vendored
Normal file
5
frontend/node_modules/@babel/compat-data/data/corejs3-shipped-proposals.json
generated
vendored
Normal file
@ -0,0 +1,5 @@
|
||||
[
|
||||
"esnext.promise.all-settled",
|
||||
"esnext.string.match-all",
|
||||
"esnext.global-this"
|
||||
]
|
||||
18
frontend/node_modules/@babel/compat-data/data/native-modules.json
generated
vendored
Normal file
18
frontend/node_modules/@babel/compat-data/data/native-modules.json
generated
vendored
Normal file
@ -0,0 +1,18 @@
|
||||
{
|
||||
"es6.module": {
|
||||
"chrome": "61",
|
||||
"and_chr": "61",
|
||||
"edge": "16",
|
||||
"firefox": "60",
|
||||
"and_ff": "60",
|
||||
"node": "13.2.0",
|
||||
"opera": "48",
|
||||
"op_mob": "45",
|
||||
"safari": "10.1",
|
||||
"ios": "10.3",
|
||||
"samsung": "8.2",
|
||||
"android": "61",
|
||||
"electron": "2.0",
|
||||
"ios_saf": "10.3"
|
||||
}
|
||||
}
|
||||
38
frontend/node_modules/@babel/compat-data/data/overlapping-plugins.json
generated
vendored
Normal file
38
frontend/node_modules/@babel/compat-data/data/overlapping-plugins.json
generated
vendored
Normal file
@ -0,0 +1,38 @@
|
||||
{
|
||||
"transform-async-to-generator": [
|
||||
"bugfix/transform-async-arrows-in-class"
|
||||
],
|
||||
"transform-parameters": [
|
||||
"bugfix/transform-edge-default-parameters",
|
||||
"bugfix/transform-safari-id-destructuring-collision-in-function-expression"
|
||||
],
|
||||
"transform-function-name": [
|
||||
"bugfix/transform-edge-function-name"
|
||||
],
|
||||
"transform-block-scoping": [
|
||||
"bugfix/transform-safari-block-shadowing",
|
||||
"bugfix/transform-safari-for-shadowing"
|
||||
],
|
||||
"transform-destructuring": [
|
||||
"bugfix/transform-safari-rest-destructuring-rhs-array"
|
||||
],
|
||||
"transform-template-literals": [
|
||||
"bugfix/transform-tagged-template-caching"
|
||||
],
|
||||
"transform-optional-chaining": [
|
||||
"bugfix/transform-v8-spread-parameters-in-optional-chaining"
|
||||
],
|
||||
"proposal-optional-chaining": [
|
||||
"bugfix/transform-v8-spread-parameters-in-optional-chaining"
|
||||
],
|
||||
"transform-class-properties": [
|
||||
"bugfix/transform-v8-static-class-fields-redefine-readonly",
|
||||
"bugfix/transform-firefox-class-in-computed-class-key",
|
||||
"bugfix/transform-safari-class-field-initializer-scope"
|
||||
],
|
||||
"proposal-class-properties": [
|
||||
"bugfix/transform-v8-static-class-fields-redefine-readonly",
|
||||
"bugfix/transform-firefox-class-in-computed-class-key",
|
||||
"bugfix/transform-safari-class-field-initializer-scope"
|
||||
]
|
||||
}
|
||||
231
frontend/node_modules/@babel/compat-data/data/plugin-bugfixes.json
generated
vendored
Normal file
231
frontend/node_modules/@babel/compat-data/data/plugin-bugfixes.json
generated
vendored
Normal file
@ -0,0 +1,231 @@
|
||||
{
|
||||
"bugfix/transform-async-arrows-in-class": {
|
||||
"chrome": "55",
|
||||
"opera": "42",
|
||||
"edge": "15",
|
||||
"firefox": "52",
|
||||
"safari": "11",
|
||||
"node": "7.6",
|
||||
"deno": "1",
|
||||
"ios": "11",
|
||||
"samsung": "6",
|
||||
"opera_mobile": "42",
|
||||
"electron": "1.6"
|
||||
},
|
||||
"bugfix/transform-edge-default-parameters": {
|
||||
"chrome": "49",
|
||||
"opera": "36",
|
||||
"edge": "18",
|
||||
"firefox": "52",
|
||||
"safari": "10",
|
||||
"node": "6",
|
||||
"deno": "1",
|
||||
"ios": "10",
|
||||
"samsung": "5",
|
||||
"opera_mobile": "36",
|
||||
"electron": "0.37"
|
||||
},
|
||||
"bugfix/transform-edge-function-name": {
|
||||
"chrome": "51",
|
||||
"opera": "38",
|
||||
"edge": "79",
|
||||
"firefox": "53",
|
||||
"safari": "10",
|
||||
"node": "6.5",
|
||||
"deno": "1",
|
||||
"ios": "10",
|
||||
"samsung": "5",
|
||||
"rhino": "1.9",
|
||||
"opera_mobile": "41",
|
||||
"electron": "1.2"
|
||||
},
|
||||
"bugfix/transform-safari-block-shadowing": {
|
||||
"chrome": "49",
|
||||
"opera": "36",
|
||||
"edge": "12",
|
||||
"firefox": "44",
|
||||
"safari": "11",
|
||||
"node": "6",
|
||||
"deno": "1",
|
||||
"ie": "11",
|
||||
"ios": "11",
|
||||
"samsung": "5",
|
||||
"opera_mobile": "36",
|
||||
"electron": "0.37"
|
||||
},
|
||||
"bugfix/transform-safari-for-shadowing": {
|
||||
"chrome": "49",
|
||||
"opera": "36",
|
||||
"edge": "12",
|
||||
"firefox": "4",
|
||||
"safari": "11",
|
||||
"node": "6",
|
||||
"deno": "1",
|
||||
"ie": "11",
|
||||
"ios": "11",
|
||||
"samsung": "5",
|
||||
"rhino": "1.7.13",
|
||||
"opera_mobile": "36",
|
||||
"electron": "0.37"
|
||||
},
|
||||
"bugfix/transform-safari-id-destructuring-collision-in-function-expression": {
|
||||
"chrome": "49",
|
||||
"opera": "36",
|
||||
"edge": "14",
|
||||
"firefox": "2",
|
||||
"safari": "16.3",
|
||||
"node": "6",
|
||||
"deno": "1",
|
||||
"ios": "16.3",
|
||||
"samsung": "5",
|
||||
"opera_mobile": "36",
|
||||
"electron": "0.37"
|
||||
},
|
||||
"bugfix/transform-safari-rest-destructuring-rhs-array": {
|
||||
"chrome": "49",
|
||||
"opera": "36",
|
||||
"edge": "14",
|
||||
"firefox": "34",
|
||||
"safari": "14.1",
|
||||
"node": "6",
|
||||
"deno": "1",
|
||||
"ios": "14.5",
|
||||
"samsung": "5",
|
||||
"opera_mobile": "36",
|
||||
"electron": "0.37"
|
||||
},
|
||||
"bugfix/transform-tagged-template-caching": {
|
||||
"chrome": "41",
|
||||
"opera": "28",
|
||||
"edge": "12",
|
||||
"firefox": "34",
|
||||
"safari": "13",
|
||||
"node": "4",
|
||||
"deno": "1",
|
||||
"ios": "13",
|
||||
"samsung": "3.4",
|
||||
"rhino": "1.7.14",
|
||||
"opera_mobile": "28",
|
||||
"electron": "0.21"
|
||||
},
|
||||
"bugfix/transform-v8-spread-parameters-in-optional-chaining": {
|
||||
"chrome": "91",
|
||||
"opera": "77",
|
||||
"edge": "91",
|
||||
"firefox": "74",
|
||||
"safari": "13.1",
|
||||
"node": "16.9",
|
||||
"deno": "1.9",
|
||||
"ios": "13.4",
|
||||
"samsung": "16",
|
||||
"opera_mobile": "64",
|
||||
"electron": "13.0"
|
||||
},
|
||||
"transform-optional-chaining": {
|
||||
"chrome": "80",
|
||||
"opera": "67",
|
||||
"edge": "80",
|
||||
"firefox": "74",
|
||||
"safari": "13.1",
|
||||
"node": "14",
|
||||
"deno": "1",
|
||||
"ios": "13.4",
|
||||
"samsung": "13",
|
||||
"rhino": "1.8",
|
||||
"opera_mobile": "57",
|
||||
"electron": "8.0"
|
||||
},
|
||||
"proposal-optional-chaining": {
|
||||
"chrome": "80",
|
||||
"opera": "67",
|
||||
"edge": "80",
|
||||
"firefox": "74",
|
||||
"safari": "13.1",
|
||||
"node": "14",
|
||||
"deno": "1",
|
||||
"ios": "13.4",
|
||||
"samsung": "13",
|
||||
"rhino": "1.8",
|
||||
"opera_mobile": "57",
|
||||
"electron": "8.0"
|
||||
},
|
||||
"transform-parameters": {
|
||||
"chrome": "49",
|
||||
"opera": "36",
|
||||
"edge": "15",
|
||||
"firefox": "52",
|
||||
"safari": "10",
|
||||
"node": "6",
|
||||
"deno": "1",
|
||||
"ios": "10",
|
||||
"samsung": "5",
|
||||
"opera_mobile": "36",
|
||||
"electron": "0.37"
|
||||
},
|
||||
"transform-async-to-generator": {
|
||||
"chrome": "55",
|
||||
"opera": "42",
|
||||
"edge": "15",
|
||||
"firefox": "52",
|
||||
"safari": "10.1",
|
||||
"node": "7.6",
|
||||
"deno": "1",
|
||||
"ios": "10.3",
|
||||
"samsung": "6",
|
||||
"opera_mobile": "42",
|
||||
"electron": "1.6"
|
||||
},
|
||||
"transform-template-literals": {
|
||||
"chrome": "41",
|
||||
"opera": "28",
|
||||
"edge": "13",
|
||||
"firefox": "34",
|
||||
"safari": "9",
|
||||
"node": "4",
|
||||
"deno": "1",
|
||||
"ios": "9",
|
||||
"samsung": "3.4",
|
||||
"rhino": "1.9",
|
||||
"opera_mobile": "28",
|
||||
"electron": "0.21"
|
||||
},
|
||||
"transform-function-name": {
|
||||
"chrome": "51",
|
||||
"opera": "38",
|
||||
"edge": "14",
|
||||
"firefox": "53",
|
||||
"safari": "10",
|
||||
"node": "6.5",
|
||||
"deno": "1",
|
||||
"ios": "10",
|
||||
"samsung": "5",
|
||||
"opera_mobile": "41",
|
||||
"electron": "1.2"
|
||||
},
|
||||
"transform-destructuring": {
|
||||
"chrome": "51",
|
||||
"opera": "38",
|
||||
"edge": "15",
|
||||
"firefox": "53",
|
||||
"safari": "10",
|
||||
"node": "6.5",
|
||||
"deno": "1",
|
||||
"ios": "10",
|
||||
"samsung": "5",
|
||||
"opera_mobile": "41",
|
||||
"electron": "1.2"
|
||||
},
|
||||
"transform-block-scoping": {
|
||||
"chrome": "50",
|
||||
"opera": "37",
|
||||
"edge": "14",
|
||||
"firefox": "53",
|
||||
"safari": "10",
|
||||
"node": "6",
|
||||
"deno": "1",
|
||||
"ios": "10",
|
||||
"samsung": "5",
|
||||
"opera_mobile": "37",
|
||||
"electron": "1.1"
|
||||
}
|
||||
}
|
||||
843
frontend/node_modules/@babel/compat-data/data/plugins.json
generated
vendored
Normal file
843
frontend/node_modules/@babel/compat-data/data/plugins.json
generated
vendored
Normal file
@ -0,0 +1,843 @@
|
||||
{
|
||||
"transform-explicit-resource-management": {
|
||||
"chrome": "141",
|
||||
"edge": "141",
|
||||
"firefox": "141",
|
||||
"node": "25",
|
||||
"electron": "39.0"
|
||||
},
|
||||
"transform-duplicate-named-capturing-groups-regex": {
|
||||
"chrome": "126",
|
||||
"opera": "112",
|
||||
"edge": "126",
|
||||
"firefox": "129",
|
||||
"safari": "17.4",
|
||||
"node": "23",
|
||||
"ios": "17.4",
|
||||
"rhino": "1.9",
|
||||
"electron": "31.0"
|
||||
},
|
||||
"transform-regexp-modifiers": {
|
||||
"chrome": "125",
|
||||
"opera": "111",
|
||||
"edge": "125",
|
||||
"firefox": "132",
|
||||
"node": "23",
|
||||
"samsung": "27",
|
||||
"electron": "31.0"
|
||||
},
|
||||
"transform-unicode-sets-regex": {
|
||||
"chrome": "112",
|
||||
"opera": "98",
|
||||
"edge": "112",
|
||||
"firefox": "116",
|
||||
"safari": "17",
|
||||
"node": "20",
|
||||
"deno": "1.32",
|
||||
"ios": "17",
|
||||
"samsung": "23",
|
||||
"opera_mobile": "75",
|
||||
"electron": "24.0"
|
||||
},
|
||||
"bugfix/transform-v8-static-class-fields-redefine-readonly": {
|
||||
"chrome": "98",
|
||||
"opera": "84",
|
||||
"edge": "98",
|
||||
"firefox": "75",
|
||||
"safari": "15",
|
||||
"node": "12",
|
||||
"deno": "1.18",
|
||||
"ios": "15",
|
||||
"samsung": "11",
|
||||
"opera_mobile": "52",
|
||||
"electron": "17.0"
|
||||
},
|
||||
"bugfix/transform-firefox-class-in-computed-class-key": {
|
||||
"chrome": "74",
|
||||
"opera": "62",
|
||||
"edge": "79",
|
||||
"firefox": "126",
|
||||
"safari": "16",
|
||||
"node": "12",
|
||||
"deno": "1",
|
||||
"ios": "16",
|
||||
"samsung": "11",
|
||||
"opera_mobile": "53",
|
||||
"electron": "6.0"
|
||||
},
|
||||
"bugfix/transform-safari-class-field-initializer-scope": {
|
||||
"chrome": "74",
|
||||
"opera": "62",
|
||||
"edge": "79",
|
||||
"firefox": "69",
|
||||
"safari": "16",
|
||||
"node": "12",
|
||||
"deno": "1",
|
||||
"ios": "16",
|
||||
"samsung": "11",
|
||||
"opera_mobile": "53",
|
||||
"electron": "6.0"
|
||||
},
|
||||
"transform-class-static-block": {
|
||||
"chrome": "94",
|
||||
"opera": "80",
|
||||
"edge": "94",
|
||||
"firefox": "93",
|
||||
"safari": "16.4",
|
||||
"node": "16.11",
|
||||
"deno": "1.14",
|
||||
"ios": "16.4",
|
||||
"samsung": "17",
|
||||
"opera_mobile": "66",
|
||||
"electron": "15.0"
|
||||
},
|
||||
"proposal-class-static-block": {
|
||||
"chrome": "94",
|
||||
"opera": "80",
|
||||
"edge": "94",
|
||||
"firefox": "93",
|
||||
"safari": "16.4",
|
||||
"node": "16.11",
|
||||
"deno": "1.14",
|
||||
"ios": "16.4",
|
||||
"samsung": "17",
|
||||
"opera_mobile": "66",
|
||||
"electron": "15.0"
|
||||
},
|
||||
"transform-private-property-in-object": {
|
||||
"chrome": "91",
|
||||
"opera": "77",
|
||||
"edge": "91",
|
||||
"firefox": "90",
|
||||
"safari": "15",
|
||||
"node": "16.9",
|
||||
"deno": "1.9",
|
||||
"ios": "15",
|
||||
"samsung": "16",
|
||||
"opera_mobile": "64",
|
||||
"electron": "13.0"
|
||||
},
|
||||
"proposal-private-property-in-object": {
|
||||
"chrome": "91",
|
||||
"opera": "77",
|
||||
"edge": "91",
|
||||
"firefox": "90",
|
||||
"safari": "15",
|
||||
"node": "16.9",
|
||||
"deno": "1.9",
|
||||
"ios": "15",
|
||||
"samsung": "16",
|
||||
"opera_mobile": "64",
|
||||
"electron": "13.0"
|
||||
},
|
||||
"transform-class-properties": {
|
||||
"chrome": "74",
|
||||
"opera": "62",
|
||||
"edge": "79",
|
||||
"firefox": "90",
|
||||
"safari": "14.1",
|
||||
"node": "12",
|
||||
"deno": "1",
|
||||
"ios": "14.5",
|
||||
"samsung": "11",
|
||||
"opera_mobile": "53",
|
||||
"electron": "6.0"
|
||||
},
|
||||
"proposal-class-properties": {
|
||||
"chrome": "74",
|
||||
"opera": "62",
|
||||
"edge": "79",
|
||||
"firefox": "90",
|
||||
"safari": "14.1",
|
||||
"node": "12",
|
||||
"deno": "1",
|
||||
"ios": "14.5",
|
||||
"samsung": "11",
|
||||
"opera_mobile": "53",
|
||||
"electron": "6.0"
|
||||
},
|
||||
"transform-private-methods": {
|
||||
"chrome": "84",
|
||||
"opera": "70",
|
||||
"edge": "84",
|
||||
"firefox": "90",
|
||||
"safari": "15",
|
||||
"node": "14.6",
|
||||
"deno": "1",
|
||||
"ios": "15",
|
||||
"samsung": "14",
|
||||
"opera_mobile": "60",
|
||||
"electron": "10.0"
|
||||
},
|
||||
"proposal-private-methods": {
|
||||
"chrome": "84",
|
||||
"opera": "70",
|
||||
"edge": "84",
|
||||
"firefox": "90",
|
||||
"safari": "15",
|
||||
"node": "14.6",
|
||||
"deno": "1",
|
||||
"ios": "15",
|
||||
"samsung": "14",
|
||||
"opera_mobile": "60",
|
||||
"electron": "10.0"
|
||||
},
|
||||
"transform-numeric-separator": {
|
||||
"chrome": "75",
|
||||
"opera": "62",
|
||||
"edge": "79",
|
||||
"firefox": "70",
|
||||
"safari": "13",
|
||||
"node": "12.5",
|
||||
"deno": "1",
|
||||
"ios": "13",
|
||||
"samsung": "11",
|
||||
"rhino": "1.7.14",
|
||||
"opera_mobile": "54",
|
||||
"electron": "6.0"
|
||||
},
|
||||
"proposal-numeric-separator": {
|
||||
"chrome": "75",
|
||||
"opera": "62",
|
||||
"edge": "79",
|
||||
"firefox": "70",
|
||||
"safari": "13",
|
||||
"node": "12.5",
|
||||
"deno": "1",
|
||||
"ios": "13",
|
||||
"samsung": "11",
|
||||
"rhino": "1.7.14",
|
||||
"opera_mobile": "54",
|
||||
"electron": "6.0"
|
||||
},
|
||||
"transform-logical-assignment-operators": {
|
||||
"chrome": "85",
|
||||
"opera": "71",
|
||||
"edge": "85",
|
||||
"firefox": "79",
|
||||
"safari": "14",
|
||||
"node": "15",
|
||||
"deno": "1.2",
|
||||
"ios": "14",
|
||||
"samsung": "14",
|
||||
"opera_mobile": "60",
|
||||
"electron": "10.0"
|
||||
},
|
||||
"proposal-logical-assignment-operators": {
|
||||
"chrome": "85",
|
||||
"opera": "71",
|
||||
"edge": "85",
|
||||
"firefox": "79",
|
||||
"safari": "14",
|
||||
"node": "15",
|
||||
"deno": "1.2",
|
||||
"ios": "14",
|
||||
"samsung": "14",
|
||||
"opera_mobile": "60",
|
||||
"electron": "10.0"
|
||||
},
|
||||
"transform-nullish-coalescing-operator": {
|
||||
"chrome": "80",
|
||||
"opera": "67",
|
||||
"edge": "80",
|
||||
"firefox": "72",
|
||||
"safari": "13.1",
|
||||
"node": "14",
|
||||
"deno": "1",
|
||||
"ios": "13.4",
|
||||
"samsung": "13",
|
||||
"rhino": "1.8",
|
||||
"opera_mobile": "57",
|
||||
"electron": "8.0"
|
||||
},
|
||||
"proposal-nullish-coalescing-operator": {
|
||||
"chrome": "80",
|
||||
"opera": "67",
|
||||
"edge": "80",
|
||||
"firefox": "72",
|
||||
"safari": "13.1",
|
||||
"node": "14",
|
||||
"deno": "1",
|
||||
"ios": "13.4",
|
||||
"samsung": "13",
|
||||
"rhino": "1.8",
|
||||
"opera_mobile": "57",
|
||||
"electron": "8.0"
|
||||
},
|
||||
"transform-optional-chaining": {
|
||||
"chrome": "91",
|
||||
"opera": "77",
|
||||
"edge": "91",
|
||||
"firefox": "74",
|
||||
"safari": "13.1",
|
||||
"node": "16.9",
|
||||
"deno": "1.9",
|
||||
"ios": "13.4",
|
||||
"samsung": "16",
|
||||
"opera_mobile": "64",
|
||||
"electron": "13.0"
|
||||
},
|
||||
"proposal-optional-chaining": {
|
||||
"chrome": "91",
|
||||
"opera": "77",
|
||||
"edge": "91",
|
||||
"firefox": "74",
|
||||
"safari": "13.1",
|
||||
"node": "16.9",
|
||||
"deno": "1.9",
|
||||
"ios": "13.4",
|
||||
"samsung": "16",
|
||||
"opera_mobile": "64",
|
||||
"electron": "13.0"
|
||||
},
|
||||
"transform-json-strings": {
|
||||
"chrome": "66",
|
||||
"opera": "53",
|
||||
"edge": "79",
|
||||
"firefox": "62",
|
||||
"safari": "12",
|
||||
"node": "10",
|
||||
"deno": "1",
|
||||
"ios": "12",
|
||||
"samsung": "9",
|
||||
"rhino": "1.7.14",
|
||||
"opera_mobile": "47",
|
||||
"electron": "3.0"
|
||||
},
|
||||
"proposal-json-strings": {
|
||||
"chrome": "66",
|
||||
"opera": "53",
|
||||
"edge": "79",
|
||||
"firefox": "62",
|
||||
"safari": "12",
|
||||
"node": "10",
|
||||
"deno": "1",
|
||||
"ios": "12",
|
||||
"samsung": "9",
|
||||
"rhino": "1.7.14",
|
||||
"opera_mobile": "47",
|
||||
"electron": "3.0"
|
||||
},
|
||||
"transform-optional-catch-binding": {
|
||||
"chrome": "66",
|
||||
"opera": "53",
|
||||
"edge": "79",
|
||||
"firefox": "58",
|
||||
"safari": "11.1",
|
||||
"node": "10",
|
||||
"deno": "1",
|
||||
"ios": "11.3",
|
||||
"samsung": "9",
|
||||
"opera_mobile": "47",
|
||||
"electron": "3.0"
|
||||
},
|
||||
"proposal-optional-catch-binding": {
|
||||
"chrome": "66",
|
||||
"opera": "53",
|
||||
"edge": "79",
|
||||
"firefox": "58",
|
||||
"safari": "11.1",
|
||||
"node": "10",
|
||||
"deno": "1",
|
||||
"ios": "11.3",
|
||||
"samsung": "9",
|
||||
"opera_mobile": "47",
|
||||
"electron": "3.0"
|
||||
},
|
||||
"transform-parameters": {
|
||||
"chrome": "49",
|
||||
"opera": "36",
|
||||
"edge": "18",
|
||||
"firefox": "52",
|
||||
"safari": "16.3",
|
||||
"node": "6",
|
||||
"deno": "1",
|
||||
"ios": "16.3",
|
||||
"samsung": "5",
|
||||
"opera_mobile": "36",
|
||||
"electron": "0.37"
|
||||
},
|
||||
"transform-async-generator-functions": {
|
||||
"chrome": "63",
|
||||
"opera": "50",
|
||||
"edge": "79",
|
||||
"firefox": "57",
|
||||
"safari": "12",
|
||||
"node": "10",
|
||||
"deno": "1",
|
||||
"ios": "12",
|
||||
"samsung": "8",
|
||||
"opera_mobile": "46",
|
||||
"electron": "3.0"
|
||||
},
|
||||
"proposal-async-generator-functions": {
|
||||
"chrome": "63",
|
||||
"opera": "50",
|
||||
"edge": "79",
|
||||
"firefox": "57",
|
||||
"safari": "12",
|
||||
"node": "10",
|
||||
"deno": "1",
|
||||
"ios": "12",
|
||||
"samsung": "8",
|
||||
"opera_mobile": "46",
|
||||
"electron": "3.0"
|
||||
},
|
||||
"transform-object-rest-spread": {
|
||||
"chrome": "60",
|
||||
"opera": "47",
|
||||
"edge": "79",
|
||||
"firefox": "55",
|
||||
"safari": "11.1",
|
||||
"node": "8.3",
|
||||
"deno": "1",
|
||||
"ios": "11.3",
|
||||
"samsung": "8",
|
||||
"opera_mobile": "44",
|
||||
"electron": "2.0"
|
||||
},
|
||||
"proposal-object-rest-spread": {
|
||||
"chrome": "60",
|
||||
"opera": "47",
|
||||
"edge": "79",
|
||||
"firefox": "55",
|
||||
"safari": "11.1",
|
||||
"node": "8.3",
|
||||
"deno": "1",
|
||||
"ios": "11.3",
|
||||
"samsung": "8",
|
||||
"opera_mobile": "44",
|
||||
"electron": "2.0"
|
||||
},
|
||||
"transform-dotall-regex": {
|
||||
"chrome": "62",
|
||||
"opera": "49",
|
||||
"edge": "79",
|
||||
"firefox": "78",
|
||||
"safari": "11.1",
|
||||
"node": "8.10",
|
||||
"deno": "1",
|
||||
"ios": "11.3",
|
||||
"samsung": "8",
|
||||
"rhino": "1.7.15",
|
||||
"opera_mobile": "46",
|
||||
"electron": "3.0"
|
||||
},
|
||||
"transform-unicode-property-regex": {
|
||||
"chrome": "64",
|
||||
"opera": "51",
|
||||
"edge": "79",
|
||||
"firefox": "78",
|
||||
"safari": "11.1",
|
||||
"node": "10",
|
||||
"deno": "1",
|
||||
"ios": "11.3",
|
||||
"samsung": "9",
|
||||
"rhino": "1.9",
|
||||
"opera_mobile": "47",
|
||||
"electron": "3.0"
|
||||
},
|
||||
"proposal-unicode-property-regex": {
|
||||
"chrome": "64",
|
||||
"opera": "51",
|
||||
"edge": "79",
|
||||
"firefox": "78",
|
||||
"safari": "11.1",
|
||||
"node": "10",
|
||||
"deno": "1",
|
||||
"ios": "11.3",
|
||||
"samsung": "9",
|
||||
"rhino": "1.9",
|
||||
"opera_mobile": "47",
|
||||
"electron": "3.0"
|
||||
},
|
||||
"transform-named-capturing-groups-regex": {
|
||||
"chrome": "64",
|
||||
"opera": "51",
|
||||
"edge": "79",
|
||||
"firefox": "78",
|
||||
"safari": "11.1",
|
||||
"node": "10",
|
||||
"deno": "1",
|
||||
"ios": "11.3",
|
||||
"samsung": "9",
|
||||
"rhino": "1.9",
|
||||
"opera_mobile": "47",
|
||||
"electron": "3.0"
|
||||
},
|
||||
"transform-async-to-generator": {
|
||||
"chrome": "55",
|
||||
"opera": "42",
|
||||
"edge": "15",
|
||||
"firefox": "52",
|
||||
"safari": "11",
|
||||
"node": "7.6",
|
||||
"deno": "1",
|
||||
"ios": "11",
|
||||
"samsung": "6",
|
||||
"opera_mobile": "42",
|
||||
"electron": "1.6"
|
||||
},
|
||||
"transform-exponentiation-operator": {
|
||||
"chrome": "52",
|
||||
"opera": "39",
|
||||
"edge": "14",
|
||||
"firefox": "52",
|
||||
"safari": "10.1",
|
||||
"node": "7",
|
||||
"deno": "1",
|
||||
"ios": "10.3",
|
||||
"samsung": "6",
|
||||
"rhino": "1.7.14",
|
||||
"opera_mobile": "41",
|
||||
"electron": "1.3"
|
||||
},
|
||||
"transform-template-literals": {
|
||||
"chrome": "41",
|
||||
"opera": "28",
|
||||
"edge": "13",
|
||||
"firefox": "34",
|
||||
"safari": "13",
|
||||
"node": "4",
|
||||
"deno": "1",
|
||||
"ios": "13",
|
||||
"samsung": "3.4",
|
||||
"rhino": "1.9",
|
||||
"opera_mobile": "28",
|
||||
"electron": "0.21"
|
||||
},
|
||||
"transform-literals": {
|
||||
"chrome": "44",
|
||||
"opera": "31",
|
||||
"edge": "12",
|
||||
"firefox": "53",
|
||||
"safari": "9",
|
||||
"node": "4",
|
||||
"deno": "1",
|
||||
"ios": "9",
|
||||
"samsung": "4",
|
||||
"rhino": "1.7.15",
|
||||
"opera_mobile": "32",
|
||||
"electron": "0.30"
|
||||
},
|
||||
"transform-function-name": {
|
||||
"chrome": "51",
|
||||
"opera": "38",
|
||||
"edge": "79",
|
||||
"firefox": "53",
|
||||
"safari": "10",
|
||||
"node": "6.5",
|
||||
"deno": "1",
|
||||
"ios": "10",
|
||||
"samsung": "5",
|
||||
"opera_mobile": "41",
|
||||
"electron": "1.2"
|
||||
},
|
||||
"transform-arrow-functions": {
|
||||
"chrome": "47",
|
||||
"opera": "34",
|
||||
"edge": "13",
|
||||
"firefox": "43",
|
||||
"safari": "10",
|
||||
"node": "6",
|
||||
"deno": "1",
|
||||
"ios": "10",
|
||||
"samsung": "5",
|
||||
"rhino": "1.7.13",
|
||||
"opera_mobile": "34",
|
||||
"electron": "0.36"
|
||||
},
|
||||
"transform-block-scoped-functions": {
|
||||
"chrome": "41",
|
||||
"opera": "28",
|
||||
"edge": "12",
|
||||
"firefox": "46",
|
||||
"safari": "10",
|
||||
"node": "4",
|
||||
"deno": "1",
|
||||
"ie": "11",
|
||||
"ios": "10",
|
||||
"samsung": "3.4",
|
||||
"opera_mobile": "28",
|
||||
"electron": "0.21"
|
||||
},
|
||||
"transform-classes": {
|
||||
"chrome": "46",
|
||||
"opera": "33",
|
||||
"edge": "13",
|
||||
"firefox": "45",
|
||||
"safari": "10",
|
||||
"node": "5",
|
||||
"deno": "1",
|
||||
"ios": "10",
|
||||
"samsung": "5",
|
||||
"opera_mobile": "33",
|
||||
"electron": "0.36"
|
||||
},
|
||||
"transform-object-super": {
|
||||
"chrome": "46",
|
||||
"opera": "33",
|
||||
"edge": "13",
|
||||
"firefox": "45",
|
||||
"safari": "10",
|
||||
"node": "5",
|
||||
"deno": "1",
|
||||
"ios": "10",
|
||||
"samsung": "5",
|
||||
"opera_mobile": "33",
|
||||
"electron": "0.36"
|
||||
},
|
||||
"transform-shorthand-properties": {
|
||||
"chrome": "43",
|
||||
"opera": "30",
|
||||
"edge": "12",
|
||||
"firefox": "33",
|
||||
"safari": "9",
|
||||
"node": "4",
|
||||
"deno": "1",
|
||||
"ios": "9",
|
||||
"samsung": "4",
|
||||
"rhino": "1.7.14",
|
||||
"opera_mobile": "30",
|
||||
"electron": "0.27"
|
||||
},
|
||||
"transform-duplicate-keys": {
|
||||
"chrome": "42",
|
||||
"opera": "29",
|
||||
"edge": "12",
|
||||
"firefox": "34",
|
||||
"safari": "9",
|
||||
"node": "4",
|
||||
"deno": "1",
|
||||
"ios": "9",
|
||||
"samsung": "3.4",
|
||||
"opera_mobile": "29",
|
||||
"electron": "0.25"
|
||||
},
|
||||
"transform-computed-properties": {
|
||||
"chrome": "44",
|
||||
"opera": "31",
|
||||
"edge": "12",
|
||||
"firefox": "34",
|
||||
"safari": "7.1",
|
||||
"node": "4",
|
||||
"deno": "1",
|
||||
"ios": "8",
|
||||
"samsung": "4",
|
||||
"rhino": "1.8",
|
||||
"opera_mobile": "32",
|
||||
"electron": "0.30"
|
||||
},
|
||||
"transform-for-of": {
|
||||
"chrome": "51",
|
||||
"opera": "38",
|
||||
"edge": "15",
|
||||
"firefox": "53",
|
||||
"safari": "10",
|
||||
"node": "6.5",
|
||||
"deno": "1",
|
||||
"ios": "10",
|
||||
"samsung": "5",
|
||||
"opera_mobile": "41",
|
||||
"electron": "1.2"
|
||||
},
|
||||
"transform-sticky-regex": {
|
||||
"chrome": "49",
|
||||
"opera": "36",
|
||||
"edge": "13",
|
||||
"firefox": "3",
|
||||
"safari": "10",
|
||||
"node": "6",
|
||||
"deno": "1",
|
||||
"ios": "10",
|
||||
"samsung": "5",
|
||||
"rhino": "1.7.15",
|
||||
"opera_mobile": "36",
|
||||
"electron": "0.37"
|
||||
},
|
||||
"transform-unicode-escapes": {
|
||||
"chrome": "44",
|
||||
"opera": "31",
|
||||
"edge": "12",
|
||||
"firefox": "53",
|
||||
"safari": "9",
|
||||
"node": "4",
|
||||
"deno": "1",
|
||||
"ios": "9",
|
||||
"samsung": "4",
|
||||
"rhino": "1.7.15",
|
||||
"opera_mobile": "32",
|
||||
"electron": "0.30"
|
||||
},
|
||||
"transform-unicode-regex": {
|
||||
"chrome": "50",
|
||||
"opera": "37",
|
||||
"edge": "13",
|
||||
"firefox": "46",
|
||||
"safari": "12",
|
||||
"node": "6",
|
||||
"deno": "1",
|
||||
"ios": "12",
|
||||
"samsung": "5",
|
||||
"opera_mobile": "37",
|
||||
"electron": "1.1"
|
||||
},
|
||||
"transform-spread": {
|
||||
"chrome": "46",
|
||||
"opera": "33",
|
||||
"edge": "13",
|
||||
"firefox": "45",
|
||||
"safari": "10",
|
||||
"node": "5",
|
||||
"deno": "1",
|
||||
"ios": "10",
|
||||
"samsung": "5",
|
||||
"opera_mobile": "33",
|
||||
"electron": "0.36"
|
||||
},
|
||||
"transform-destructuring": {
|
||||
"chrome": "51",
|
||||
"opera": "38",
|
||||
"edge": "15",
|
||||
"firefox": "53",
|
||||
"safari": "14.1",
|
||||
"node": "6.5",
|
||||
"deno": "1",
|
||||
"ios": "14.5",
|
||||
"samsung": "5",
|
||||
"opera_mobile": "41",
|
||||
"electron": "1.2"
|
||||
},
|
||||
"transform-block-scoping": {
|
||||
"chrome": "50",
|
||||
"opera": "37",
|
||||
"edge": "14",
|
||||
"firefox": "53",
|
||||
"safari": "11",
|
||||
"node": "6",
|
||||
"deno": "1",
|
||||
"ios": "11",
|
||||
"samsung": "5",
|
||||
"opera_mobile": "37",
|
||||
"electron": "1.1"
|
||||
},
|
||||
"transform-typeof-symbol": {
|
||||
"chrome": "48",
|
||||
"opera": "35",
|
||||
"edge": "12",
|
||||
"firefox": "36",
|
||||
"safari": "9",
|
||||
"node": "6",
|
||||
"deno": "1",
|
||||
"ios": "9",
|
||||
"samsung": "5",
|
||||
"rhino": "1.8",
|
||||
"opera_mobile": "35",
|
||||
"electron": "0.37"
|
||||
},
|
||||
"transform-new-target": {
|
||||
"chrome": "46",
|
||||
"opera": "33",
|
||||
"edge": "14",
|
||||
"firefox": "41",
|
||||
"safari": "10",
|
||||
"node": "5",
|
||||
"deno": "1",
|
||||
"ios": "10",
|
||||
"samsung": "5",
|
||||
"opera_mobile": "33",
|
||||
"electron": "0.36"
|
||||
},
|
||||
"transform-regenerator": {
|
||||
"chrome": "50",
|
||||
"opera": "37",
|
||||
"edge": "13",
|
||||
"firefox": "53",
|
||||
"safari": "10",
|
||||
"node": "6",
|
||||
"deno": "1",
|
||||
"ios": "10",
|
||||
"samsung": "5",
|
||||
"opera_mobile": "37",
|
||||
"electron": "1.1"
|
||||
},
|
||||
"transform-member-expression-literals": {
|
||||
"chrome": "7",
|
||||
"opera": "12",
|
||||
"edge": "12",
|
||||
"firefox": "2",
|
||||
"safari": "5.1",
|
||||
"node": "0.4",
|
||||
"deno": "1",
|
||||
"ie": "9",
|
||||
"android": "4",
|
||||
"ios": "6",
|
||||
"phantom": "1.9",
|
||||
"samsung": "1",
|
||||
"rhino": "1.7.13",
|
||||
"opera_mobile": "12",
|
||||
"electron": "0.20"
|
||||
},
|
||||
"transform-property-literals": {
|
||||
"chrome": "7",
|
||||
"opera": "12",
|
||||
"edge": "12",
|
||||
"firefox": "2",
|
||||
"safari": "5.1",
|
||||
"node": "0.4",
|
||||
"deno": "1",
|
||||
"ie": "9",
|
||||
"android": "4",
|
||||
"ios": "6",
|
||||
"phantom": "1.9",
|
||||
"samsung": "1",
|
||||
"rhino": "1.7.13",
|
||||
"opera_mobile": "12",
|
||||
"electron": "0.20"
|
||||
},
|
||||
"transform-reserved-words": {
|
||||
"chrome": "13",
|
||||
"opera": "10.50",
|
||||
"edge": "12",
|
||||
"firefox": "2",
|
||||
"safari": "3.1",
|
||||
"node": "0.6",
|
||||
"deno": "1",
|
||||
"ie": "9",
|
||||
"android": "4.4",
|
||||
"ios": "6",
|
||||
"phantom": "1.9",
|
||||
"samsung": "1",
|
||||
"rhino": "1.7.13",
|
||||
"opera_mobile": "10.1",
|
||||
"electron": "0.20"
|
||||
},
|
||||
"transform-export-namespace-from": {
|
||||
"chrome": "72",
|
||||
"deno": "1.0",
|
||||
"edge": "79",
|
||||
"firefox": "80",
|
||||
"node": "13.2.0",
|
||||
"opera": "60",
|
||||
"opera_mobile": "51",
|
||||
"safari": "14.1",
|
||||
"ios": "14.5",
|
||||
"samsung": "11.0",
|
||||
"android": "72",
|
||||
"electron": "5.0"
|
||||
},
|
||||
"proposal-export-namespace-from": {
|
||||
"chrome": "72",
|
||||
"deno": "1.0",
|
||||
"edge": "79",
|
||||
"firefox": "80",
|
||||
"node": "13.2.0",
|
||||
"opera": "60",
|
||||
"opera_mobile": "51",
|
||||
"safari": "14.1",
|
||||
"ios": "14.5",
|
||||
"samsung": "11.0",
|
||||
"android": "72",
|
||||
"electron": "5.0"
|
||||
}
|
||||
}
|
||||
2
frontend/node_modules/@babel/compat-data/native-modules.js
generated
vendored
Normal file
2
frontend/node_modules/@babel/compat-data/native-modules.js
generated
vendored
Normal file
@ -0,0 +1,2 @@
|
||||
// Todo (Babel 8): remove this file, in Babel 8 users import the .json directly
|
||||
module.exports = require("./data/native-modules.json");
|
||||
2
frontend/node_modules/@babel/compat-data/overlapping-plugins.js
generated
vendored
Normal file
2
frontend/node_modules/@babel/compat-data/overlapping-plugins.js
generated
vendored
Normal file
@ -0,0 +1,2 @@
|
||||
// Todo (Babel 8): remove this file, in Babel 8 users import the .json directly
|
||||
module.exports = require("./data/overlapping-plugins.json");
|
||||
40
frontend/node_modules/@babel/compat-data/package.json
generated
vendored
Normal file
40
frontend/node_modules/@babel/compat-data/package.json
generated
vendored
Normal file
@ -0,0 +1,40 @@
|
||||
{
|
||||
"name": "@babel/compat-data",
|
||||
"version": "7.29.7",
|
||||
"author": "The Babel Team (https://babel.dev/team)",
|
||||
"license": "MIT",
|
||||
"description": "The compat-data to determine required Babel plugins",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/babel/babel.git",
|
||||
"directory": "packages/babel-compat-data"
|
||||
},
|
||||
"publishConfig": {
|
||||
"access": "public"
|
||||
},
|
||||
"exports": {
|
||||
"./plugins": "./plugins.js",
|
||||
"./native-modules": "./native-modules.js",
|
||||
"./corejs2-built-ins": "./corejs2-built-ins.js",
|
||||
"./corejs3-shipped-proposals": "./corejs3-shipped-proposals.js",
|
||||
"./overlapping-plugins": "./overlapping-plugins.js",
|
||||
"./plugin-bugfixes": "./plugin-bugfixes.js"
|
||||
},
|
||||
"scripts": {
|
||||
"build-data": "./scripts/download-compat-table.sh && node ./scripts/build-data.mjs && node ./scripts/build-modules-support.mjs && node ./scripts/build-bugfixes-targets.mjs"
|
||||
},
|
||||
"keywords": [
|
||||
"babel",
|
||||
"compat-table",
|
||||
"compat-data"
|
||||
],
|
||||
"devDependencies": {
|
||||
"@mdn/browser-compat-data": "^6.0.8",
|
||||
"core-js-compat": "^3.48.0",
|
||||
"electron-to-chromium": "^1.5.278"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=6.9.0"
|
||||
},
|
||||
"type": "commonjs"
|
||||
}
|
||||
2
frontend/node_modules/@babel/compat-data/plugin-bugfixes.js
generated
vendored
Normal file
2
frontend/node_modules/@babel/compat-data/plugin-bugfixes.js
generated
vendored
Normal file
@ -0,0 +1,2 @@
|
||||
// Todo (Babel 8): remove this file, in Babel 8 users import the .json directly
|
||||
module.exports = require("./data/plugin-bugfixes.json");
|
||||
2
frontend/node_modules/@babel/compat-data/plugins.js
generated
vendored
Normal file
2
frontend/node_modules/@babel/compat-data/plugins.js
generated
vendored
Normal file
@ -0,0 +1,2 @@
|
||||
// Todo (Babel 8): remove this file, in Babel 8 users import the .json directly
|
||||
module.exports = require("./data/plugins.json");
|
||||
22
frontend/node_modules/@babel/core/LICENSE
generated
vendored
Normal file
22
frontend/node_modules/@babel/core/LICENSE
generated
vendored
Normal file
@ -0,0 +1,22 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2014-present Sebastian McKenzie and other contributors
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of this software and associated documentation files (the
|
||||
"Software"), to deal in the Software without restriction, including
|
||||
without limitation the rights to use, copy, modify, merge, publish,
|
||||
distribute, sublicense, and/or sell copies of the Software, and to
|
||||
permit persons to whom the Software is furnished to do so, subject to
|
||||
the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be
|
||||
included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
19
frontend/node_modules/@babel/core/README.md
generated
vendored
Normal file
19
frontend/node_modules/@babel/core/README.md
generated
vendored
Normal file
@ -0,0 +1,19 @@
|
||||
# @babel/core
|
||||
|
||||
> Babel compiler core.
|
||||
|
||||
See our website [@babel/core](https://babeljs.io/docs/babel-core) for more information or the [issues](https://github.com/babel/babel/issues?utf8=%E2%9C%93&q=is%3Aissue+label%3A%22pkg%3A%20core%22+is%3Aopen) associated with this package.
|
||||
|
||||
## Install
|
||||
|
||||
Using npm:
|
||||
|
||||
```sh
|
||||
npm install --save-dev @babel/core
|
||||
```
|
||||
|
||||
or using yarn:
|
||||
|
||||
```sh
|
||||
yarn add @babel/core --dev
|
||||
```
|
||||
5
frontend/node_modules/@babel/core/lib/config/cache-contexts.js
generated
vendored
Normal file
5
frontend/node_modules/@babel/core/lib/config/cache-contexts.js
generated
vendored
Normal file
@ -0,0 +1,5 @@
|
||||
"use strict";
|
||||
|
||||
0 && 0;
|
||||
|
||||
//# sourceMappingURL=cache-contexts.js.map
|
||||
1
frontend/node_modules/@babel/core/lib/config/cache-contexts.js.map
generated
vendored
Normal file
1
frontend/node_modules/@babel/core/lib/config/cache-contexts.js.map
generated
vendored
Normal file
@ -0,0 +1 @@
|
||||
{"version":3,"names":[],"sources":["../../src/config/cache-contexts.ts"],"sourcesContent":["import type { ConfigContext } from \"./config-chain.ts\";\nimport type {\n CallerMetadata,\n TargetsListOrObject,\n} from \"./validation/options.ts\";\n\nexport type { ConfigContext as FullConfig };\n\nexport type FullPreset = {\n targets: TargetsListOrObject;\n} & ConfigContext;\nexport type FullPlugin = {\n assumptions: Record<string, boolean>;\n} & FullPreset;\n\n// Context not including filename since it is used in places that cannot\n// process 'ignore'/'only' and other filename-based logic.\nexport type SimpleConfig = {\n envName: string;\n caller: CallerMetadata | undefined;\n};\nexport type SimplePreset = {\n targets: TargetsListOrObject;\n} & SimpleConfig;\nexport type SimplePlugin = {\n assumptions: Record<string, boolean>;\n} & SimplePreset;\n"],"mappings":"","ignoreList":[]}
|
||||
261
frontend/node_modules/@babel/core/lib/config/caching.js
generated
vendored
Normal file
261
frontend/node_modules/@babel/core/lib/config/caching.js
generated
vendored
Normal file
@ -0,0 +1,261 @@
|
||||
"use strict";
|
||||
|
||||
Object.defineProperty(exports, "__esModule", {
|
||||
value: true
|
||||
});
|
||||
exports.assertSimpleType = assertSimpleType;
|
||||
exports.makeStrongCache = makeStrongCache;
|
||||
exports.makeStrongCacheSync = makeStrongCacheSync;
|
||||
exports.makeWeakCache = makeWeakCache;
|
||||
exports.makeWeakCacheSync = makeWeakCacheSync;
|
||||
function _gensync() {
|
||||
const data = require("gensync");
|
||||
_gensync = function () {
|
||||
return data;
|
||||
};
|
||||
return data;
|
||||
}
|
||||
var _async = require("../gensync-utils/async.js");
|
||||
var _util = require("./util.js");
|
||||
const synchronize = gen => {
|
||||
return _gensync()(gen).sync;
|
||||
};
|
||||
function* genTrue() {
|
||||
return true;
|
||||
}
|
||||
function makeWeakCache(handler) {
|
||||
return makeCachedFunction(WeakMap, handler);
|
||||
}
|
||||
function makeWeakCacheSync(handler) {
|
||||
return synchronize(makeWeakCache(handler));
|
||||
}
|
||||
function makeStrongCache(handler) {
|
||||
return makeCachedFunction(Map, handler);
|
||||
}
|
||||
function makeStrongCacheSync(handler) {
|
||||
return synchronize(makeStrongCache(handler));
|
||||
}
|
||||
function makeCachedFunction(CallCache, handler) {
|
||||
const callCacheSync = new CallCache();
|
||||
const callCacheAsync = new CallCache();
|
||||
const futureCache = new CallCache();
|
||||
return function* cachedFunction(arg, data) {
|
||||
const asyncContext = yield* (0, _async.isAsync)();
|
||||
const callCache = asyncContext ? callCacheAsync : callCacheSync;
|
||||
const cached = yield* getCachedValueOrWait(asyncContext, callCache, futureCache, arg, data);
|
||||
if (cached.valid) return cached.value;
|
||||
const cache = new CacheConfigurator(data);
|
||||
const handlerResult = handler(arg, cache);
|
||||
let finishLock;
|
||||
let value;
|
||||
if ((0, _util.isIterableIterator)(handlerResult)) {
|
||||
value = yield* (0, _async.onFirstPause)(handlerResult, () => {
|
||||
finishLock = setupAsyncLocks(cache, futureCache, arg);
|
||||
});
|
||||
} else {
|
||||
value = handlerResult;
|
||||
}
|
||||
updateFunctionCache(callCache, cache, arg, value);
|
||||
if (finishLock) {
|
||||
futureCache.delete(arg);
|
||||
finishLock.release(value);
|
||||
}
|
||||
return value;
|
||||
};
|
||||
}
|
||||
function* getCachedValue(cache, arg, data) {
|
||||
const cachedValue = cache.get(arg);
|
||||
if (cachedValue) {
|
||||
for (const {
|
||||
value,
|
||||
valid
|
||||
} of cachedValue) {
|
||||
if (yield* valid(data)) return {
|
||||
valid: true,
|
||||
value
|
||||
};
|
||||
}
|
||||
}
|
||||
return {
|
||||
valid: false,
|
||||
value: null
|
||||
};
|
||||
}
|
||||
function* getCachedValueOrWait(asyncContext, callCache, futureCache, arg, data) {
|
||||
const cached = yield* getCachedValue(callCache, arg, data);
|
||||
if (cached.valid) {
|
||||
return cached;
|
||||
}
|
||||
if (asyncContext) {
|
||||
const cached = yield* getCachedValue(futureCache, arg, data);
|
||||
if (cached.valid) {
|
||||
const value = yield* (0, _async.waitFor)(cached.value.promise);
|
||||
return {
|
||||
valid: true,
|
||||
value
|
||||
};
|
||||
}
|
||||
}
|
||||
return {
|
||||
valid: false,
|
||||
value: null
|
||||
};
|
||||
}
|
||||
function setupAsyncLocks(config, futureCache, arg) {
|
||||
const finishLock = new Lock();
|
||||
updateFunctionCache(futureCache, config, arg, finishLock);
|
||||
return finishLock;
|
||||
}
|
||||
function updateFunctionCache(cache, config, arg, value) {
|
||||
if (!config.configured()) config.forever();
|
||||
let cachedValue = cache.get(arg);
|
||||
config.deactivate();
|
||||
switch (config.mode()) {
|
||||
case "forever":
|
||||
cachedValue = [{
|
||||
value,
|
||||
valid: genTrue
|
||||
}];
|
||||
cache.set(arg, cachedValue);
|
||||
break;
|
||||
case "invalidate":
|
||||
cachedValue = [{
|
||||
value,
|
||||
valid: config.validator()
|
||||
}];
|
||||
cache.set(arg, cachedValue);
|
||||
break;
|
||||
case "valid":
|
||||
if (cachedValue) {
|
||||
cachedValue.push({
|
||||
value,
|
||||
valid: config.validator()
|
||||
});
|
||||
} else {
|
||||
cachedValue = [{
|
||||
value,
|
||||
valid: config.validator()
|
||||
}];
|
||||
cache.set(arg, cachedValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
class CacheConfigurator {
|
||||
constructor(data) {
|
||||
this._active = true;
|
||||
this._never = false;
|
||||
this._forever = false;
|
||||
this._invalidate = false;
|
||||
this._configured = false;
|
||||
this._pairs = [];
|
||||
this._data = void 0;
|
||||
this._data = data;
|
||||
}
|
||||
simple() {
|
||||
return makeSimpleConfigurator(this);
|
||||
}
|
||||
mode() {
|
||||
if (this._never) return "never";
|
||||
if (this._forever) return "forever";
|
||||
if (this._invalidate) return "invalidate";
|
||||
return "valid";
|
||||
}
|
||||
forever() {
|
||||
if (!this._active) {
|
||||
throw new Error("Cannot change caching after evaluation has completed.");
|
||||
}
|
||||
if (this._never) {
|
||||
throw new Error("Caching has already been configured with .never()");
|
||||
}
|
||||
this._forever = true;
|
||||
this._configured = true;
|
||||
}
|
||||
never() {
|
||||
if (!this._active) {
|
||||
throw new Error("Cannot change caching after evaluation has completed.");
|
||||
}
|
||||
if (this._forever) {
|
||||
throw new Error("Caching has already been configured with .forever()");
|
||||
}
|
||||
this._never = true;
|
||||
this._configured = true;
|
||||
}
|
||||
using(handler) {
|
||||
if (!this._active) {
|
||||
throw new Error("Cannot change caching after evaluation has completed.");
|
||||
}
|
||||
if (this._never || this._forever) {
|
||||
throw new Error("Caching has already been configured with .never or .forever()");
|
||||
}
|
||||
this._configured = true;
|
||||
const key = handler(this._data);
|
||||
const fn = (0, _async.maybeAsync)(handler, `You appear to be using an async cache handler, but Babel has been called synchronously`);
|
||||
if ((0, _async.isThenable)(key)) {
|
||||
return key.then(key => {
|
||||
this._pairs.push([key, fn]);
|
||||
return key;
|
||||
});
|
||||
}
|
||||
this._pairs.push([key, fn]);
|
||||
return key;
|
||||
}
|
||||
invalidate(handler) {
|
||||
this._invalidate = true;
|
||||
return this.using(handler);
|
||||
}
|
||||
validator() {
|
||||
const pairs = this._pairs;
|
||||
return function* (data) {
|
||||
for (const [key, fn] of pairs) {
|
||||
if (key !== (yield* fn(data))) return false;
|
||||
}
|
||||
return true;
|
||||
};
|
||||
}
|
||||
deactivate() {
|
||||
this._active = false;
|
||||
}
|
||||
configured() {
|
||||
return this._configured;
|
||||
}
|
||||
}
|
||||
function makeSimpleConfigurator(cache) {
|
||||
function cacheFn(val) {
|
||||
if (typeof val === "boolean") {
|
||||
if (val) cache.forever();else cache.never();
|
||||
return;
|
||||
}
|
||||
return cache.using(() => assertSimpleType(val()));
|
||||
}
|
||||
cacheFn.forever = () => cache.forever();
|
||||
cacheFn.never = () => cache.never();
|
||||
cacheFn.using = cb => cache.using(() => assertSimpleType(cb()));
|
||||
cacheFn.invalidate = cb => cache.invalidate(() => assertSimpleType(cb()));
|
||||
return cacheFn;
|
||||
}
|
||||
function assertSimpleType(value) {
|
||||
if ((0, _async.isThenable)(value)) {
|
||||
throw new Error(`You appear to be using an async cache handler, ` + `which your current version of Babel does not support. ` + `We may add support for this in the future, ` + `but if you're on the most recent version of @babel/core and still ` + `seeing this error, then you'll need to synchronously handle your caching logic.`);
|
||||
}
|
||||
if (value != null && typeof value !== "string" && typeof value !== "boolean" && typeof value !== "number") {
|
||||
throw new Error("Cache keys must be either string, boolean, number, null, or undefined.");
|
||||
}
|
||||
return value;
|
||||
}
|
||||
class Lock {
|
||||
constructor() {
|
||||
this.released = false;
|
||||
this.promise = void 0;
|
||||
this._resolve = void 0;
|
||||
this.promise = new Promise(resolve => {
|
||||
this._resolve = resolve;
|
||||
});
|
||||
}
|
||||
release(value) {
|
||||
this.released = true;
|
||||
this._resolve(value);
|
||||
}
|
||||
}
|
||||
0 && 0;
|
||||
|
||||
//# sourceMappingURL=caching.js.map
|
||||
1
frontend/node_modules/@babel/core/lib/config/caching.js.map
generated
vendored
Normal file
1
frontend/node_modules/@babel/core/lib/config/caching.js.map
generated
vendored
Normal file
File diff suppressed because one or more lines are too long
469
frontend/node_modules/@babel/core/lib/config/config-chain.js
generated
vendored
Normal file
469
frontend/node_modules/@babel/core/lib/config/config-chain.js
generated
vendored
Normal file
@ -0,0 +1,469 @@
|
||||
"use strict";
|
||||
|
||||
Object.defineProperty(exports, "__esModule", {
|
||||
value: true
|
||||
});
|
||||
exports.buildPresetChain = buildPresetChain;
|
||||
exports.buildPresetChainWalker = void 0;
|
||||
exports.buildRootChain = buildRootChain;
|
||||
function _path() {
|
||||
const data = require("path");
|
||||
_path = function () {
|
||||
return data;
|
||||
};
|
||||
return data;
|
||||
}
|
||||
function _debug() {
|
||||
const data = require("debug");
|
||||
_debug = function () {
|
||||
return data;
|
||||
};
|
||||
return data;
|
||||
}
|
||||
var _options = require("./validation/options.js");
|
||||
var _patternToRegex = require("./pattern-to-regex.js");
|
||||
var _printer = require("./printer.js");
|
||||
var _rewriteStackTrace = require("../errors/rewrite-stack-trace.js");
|
||||
var _configError = require("../errors/config-error.js");
|
||||
var _index = require("./files/index.js");
|
||||
var _caching = require("./caching.js");
|
||||
var _configDescriptors = require("./config-descriptors.js");
|
||||
const debug = _debug()("babel:config:config-chain");
|
||||
function* buildPresetChain(arg, context) {
|
||||
const chain = yield* buildPresetChainWalker(arg, context);
|
||||
if (!chain) return null;
|
||||
return {
|
||||
plugins: dedupDescriptors(chain.plugins),
|
||||
presets: dedupDescriptors(chain.presets),
|
||||
options: chain.options.map(o => createConfigChainOptions(o)),
|
||||
files: new Set()
|
||||
};
|
||||
}
|
||||
const buildPresetChainWalker = exports.buildPresetChainWalker = makeChainWalker({
|
||||
root: preset => loadPresetDescriptors(preset),
|
||||
env: (preset, envName) => loadPresetEnvDescriptors(preset)(envName),
|
||||
overrides: (preset, index) => loadPresetOverridesDescriptors(preset)(index),
|
||||
overridesEnv: (preset, index, envName) => loadPresetOverridesEnvDescriptors(preset)(index)(envName),
|
||||
createLogger: () => () => {}
|
||||
});
|
||||
const loadPresetDescriptors = (0, _caching.makeWeakCacheSync)(preset => buildRootDescriptors(preset, preset.alias, _configDescriptors.createUncachedDescriptors));
|
||||
const loadPresetEnvDescriptors = (0, _caching.makeWeakCacheSync)(preset => (0, _caching.makeStrongCacheSync)(envName => buildEnvDescriptors(preset, preset.alias, _configDescriptors.createUncachedDescriptors, envName)));
|
||||
const loadPresetOverridesDescriptors = (0, _caching.makeWeakCacheSync)(preset => (0, _caching.makeStrongCacheSync)(index => buildOverrideDescriptors(preset, preset.alias, _configDescriptors.createUncachedDescriptors, index)));
|
||||
const loadPresetOverridesEnvDescriptors = (0, _caching.makeWeakCacheSync)(preset => (0, _caching.makeStrongCacheSync)(index => (0, _caching.makeStrongCacheSync)(envName => buildOverrideEnvDescriptors(preset, preset.alias, _configDescriptors.createUncachedDescriptors, index, envName))));
|
||||
function* buildRootChain(opts, context) {
|
||||
let configReport, babelRcReport;
|
||||
const programmaticLogger = new _printer.ConfigPrinter();
|
||||
const programmaticChain = yield* loadProgrammaticChain({
|
||||
options: opts,
|
||||
dirname: context.cwd
|
||||
}, context, undefined, programmaticLogger);
|
||||
if (!programmaticChain) return null;
|
||||
const programmaticReport = yield* programmaticLogger.output();
|
||||
let configFile;
|
||||
if (typeof opts.configFile === "string") {
|
||||
configFile = yield* (0, _index.loadConfig)(opts.configFile, context.cwd, context.envName, context.caller);
|
||||
} else if (opts.configFile !== false) {
|
||||
configFile = yield* (0, _index.findRootConfig)(context.root, context.envName, context.caller);
|
||||
}
|
||||
let {
|
||||
babelrc,
|
||||
babelrcRoots
|
||||
} = opts;
|
||||
let babelrcRootsDirectory = context.cwd;
|
||||
const configFileChain = emptyChain();
|
||||
const configFileLogger = new _printer.ConfigPrinter();
|
||||
if (configFile) {
|
||||
const validatedFile = validateConfigFile(configFile);
|
||||
const result = yield* loadFileChain(validatedFile, context, undefined, configFileLogger);
|
||||
if (!result) return null;
|
||||
configReport = yield* configFileLogger.output();
|
||||
if (babelrc === undefined) {
|
||||
babelrc = validatedFile.options.babelrc;
|
||||
}
|
||||
if (babelrcRoots === undefined) {
|
||||
babelrcRootsDirectory = validatedFile.dirname;
|
||||
babelrcRoots = validatedFile.options.babelrcRoots;
|
||||
}
|
||||
mergeChain(configFileChain, result);
|
||||
}
|
||||
let ignoreFile, babelrcFile;
|
||||
let isIgnored = false;
|
||||
const fileChain = emptyChain();
|
||||
if ((babelrc === true || babelrc === undefined) && typeof context.filename === "string") {
|
||||
const pkgData = yield* (0, _index.findPackageData)(context.filename);
|
||||
if (pkgData && babelrcLoadEnabled(context, pkgData, babelrcRoots, babelrcRootsDirectory)) {
|
||||
({
|
||||
ignore: ignoreFile,
|
||||
config: babelrcFile
|
||||
} = yield* (0, _index.findRelativeConfig)(pkgData, context.envName, context.caller));
|
||||
if (ignoreFile) {
|
||||
fileChain.files.add(ignoreFile.filepath);
|
||||
}
|
||||
if (ignoreFile && shouldIgnore(context, ignoreFile.ignore, null, ignoreFile.dirname)) {
|
||||
isIgnored = true;
|
||||
}
|
||||
if (babelrcFile && !isIgnored) {
|
||||
const validatedFile = validateBabelrcFile(babelrcFile);
|
||||
const babelrcLogger = new _printer.ConfigPrinter();
|
||||
const result = yield* loadFileChain(validatedFile, context, undefined, babelrcLogger);
|
||||
if (!result) {
|
||||
isIgnored = true;
|
||||
} else {
|
||||
babelRcReport = yield* babelrcLogger.output();
|
||||
mergeChain(fileChain, result);
|
||||
}
|
||||
}
|
||||
if (babelrcFile && isIgnored) {
|
||||
fileChain.files.add(babelrcFile.filepath);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (context.showConfig) {
|
||||
console.log(`Babel configs on "${context.filename}" (ascending priority):\n` + [configReport, babelRcReport, programmaticReport].filter(x => !!x).join("\n\n") + "\n-----End Babel configs-----");
|
||||
}
|
||||
const chain = mergeChain(mergeChain(mergeChain(emptyChain(), configFileChain), fileChain), programmaticChain);
|
||||
return {
|
||||
plugins: isIgnored ? [] : dedupDescriptors(chain.plugins),
|
||||
presets: isIgnored ? [] : dedupDescriptors(chain.presets),
|
||||
options: isIgnored ? [] : chain.options.map(o => createConfigChainOptions(o)),
|
||||
fileHandling: isIgnored ? "ignored" : "transpile",
|
||||
ignore: ignoreFile || undefined,
|
||||
babelrc: babelrcFile || undefined,
|
||||
config: configFile || undefined,
|
||||
files: chain.files
|
||||
};
|
||||
}
|
||||
function babelrcLoadEnabled(context, pkgData, babelrcRoots, babelrcRootsDirectory) {
|
||||
if (typeof babelrcRoots === "boolean") return babelrcRoots;
|
||||
const absoluteRoot = context.root;
|
||||
if (babelrcRoots === undefined) {
|
||||
return pkgData.directories.includes(absoluteRoot);
|
||||
}
|
||||
let babelrcPatterns = babelrcRoots;
|
||||
if (!Array.isArray(babelrcPatterns)) {
|
||||
babelrcPatterns = [babelrcPatterns];
|
||||
}
|
||||
babelrcPatterns = babelrcPatterns.map(pat => {
|
||||
return typeof pat === "string" ? _path().resolve(babelrcRootsDirectory, pat) : pat;
|
||||
});
|
||||
if (babelrcPatterns.length === 1 && babelrcPatterns[0] === absoluteRoot) {
|
||||
return pkgData.directories.includes(absoluteRoot);
|
||||
}
|
||||
return babelrcPatterns.some(pat => {
|
||||
if (typeof pat === "string") {
|
||||
pat = (0, _patternToRegex.default)(pat, babelrcRootsDirectory);
|
||||
}
|
||||
return pkgData.directories.some(directory => {
|
||||
return matchPattern(pat, babelrcRootsDirectory, directory, context);
|
||||
});
|
||||
});
|
||||
}
|
||||
const validateConfigFile = (0, _caching.makeWeakCacheSync)(file => ({
|
||||
filepath: file.filepath,
|
||||
dirname: file.dirname,
|
||||
options: (0, _options.validate)("configfile", file.options, file.filepath)
|
||||
}));
|
||||
const validateBabelrcFile = (0, _caching.makeWeakCacheSync)(file => ({
|
||||
filepath: file.filepath,
|
||||
dirname: file.dirname,
|
||||
options: (0, _options.validate)("babelrcfile", file.options, file.filepath)
|
||||
}));
|
||||
const validateExtendFile = (0, _caching.makeWeakCacheSync)(file => ({
|
||||
filepath: file.filepath,
|
||||
dirname: file.dirname,
|
||||
options: (0, _options.validate)("extendsfile", file.options, file.filepath)
|
||||
}));
|
||||
const loadProgrammaticChain = makeChainWalker({
|
||||
root: input => buildRootDescriptors(input, "base", _configDescriptors.createCachedDescriptors),
|
||||
env: (input, envName) => buildEnvDescriptors(input, "base", _configDescriptors.createCachedDescriptors, envName),
|
||||
overrides: (input, index) => buildOverrideDescriptors(input, "base", _configDescriptors.createCachedDescriptors, index),
|
||||
overridesEnv: (input, index, envName) => buildOverrideEnvDescriptors(input, "base", _configDescriptors.createCachedDescriptors, index, envName),
|
||||
createLogger: (input, context, baseLogger) => buildProgrammaticLogger(input, context, baseLogger)
|
||||
});
|
||||
const loadFileChainWalker = makeChainWalker({
|
||||
root: file => loadFileDescriptors(file),
|
||||
env: (file, envName) => loadFileEnvDescriptors(file)(envName),
|
||||
overrides: (file, index) => loadFileOverridesDescriptors(file)(index),
|
||||
overridesEnv: (file, index, envName) => loadFileOverridesEnvDescriptors(file)(index)(envName),
|
||||
createLogger: (file, context, baseLogger) => buildFileLogger(file.filepath, context, baseLogger)
|
||||
});
|
||||
function* loadFileChain(input, context, files, baseLogger) {
|
||||
const chain = yield* loadFileChainWalker(input, context, files, baseLogger);
|
||||
chain == null || chain.files.add(input.filepath);
|
||||
return chain;
|
||||
}
|
||||
const loadFileDescriptors = (0, _caching.makeWeakCacheSync)(file => buildRootDescriptors(file, file.filepath, _configDescriptors.createUncachedDescriptors));
|
||||
const loadFileEnvDescriptors = (0, _caching.makeWeakCacheSync)(file => (0, _caching.makeStrongCacheSync)(envName => buildEnvDescriptors(file, file.filepath, _configDescriptors.createUncachedDescriptors, envName)));
|
||||
const loadFileOverridesDescriptors = (0, _caching.makeWeakCacheSync)(file => (0, _caching.makeStrongCacheSync)(index => buildOverrideDescriptors(file, file.filepath, _configDescriptors.createUncachedDescriptors, index)));
|
||||
const loadFileOverridesEnvDescriptors = (0, _caching.makeWeakCacheSync)(file => (0, _caching.makeStrongCacheSync)(index => (0, _caching.makeStrongCacheSync)(envName => buildOverrideEnvDescriptors(file, file.filepath, _configDescriptors.createUncachedDescriptors, index, envName))));
|
||||
function buildFileLogger(filepath, context, baseLogger) {
|
||||
if (!baseLogger) {
|
||||
return () => {};
|
||||
}
|
||||
return baseLogger.configure(context.showConfig, _printer.ChainFormatter.Config, {
|
||||
filepath
|
||||
});
|
||||
}
|
||||
function buildRootDescriptors({
|
||||
dirname,
|
||||
options
|
||||
}, alias, descriptors) {
|
||||
return descriptors(dirname, options, alias);
|
||||
}
|
||||
function buildProgrammaticLogger(_, context, baseLogger) {
|
||||
var _context$caller;
|
||||
if (!baseLogger) {
|
||||
return () => {};
|
||||
}
|
||||
return baseLogger.configure(context.showConfig, _printer.ChainFormatter.Programmatic, {
|
||||
callerName: (_context$caller = context.caller) == null ? void 0 : _context$caller.name
|
||||
});
|
||||
}
|
||||
function buildEnvDescriptors({
|
||||
dirname,
|
||||
options
|
||||
}, alias, descriptors, envName) {
|
||||
var _options$env;
|
||||
const opts = (_options$env = options.env) == null ? void 0 : _options$env[envName];
|
||||
return opts ? descriptors(dirname, opts, `${alias}.env["${envName}"]`) : null;
|
||||
}
|
||||
function buildOverrideDescriptors({
|
||||
dirname,
|
||||
options
|
||||
}, alias, descriptors, index) {
|
||||
var _options$overrides;
|
||||
const opts = (_options$overrides = options.overrides) == null ? void 0 : _options$overrides[index];
|
||||
if (!opts) throw new Error("Assertion failure - missing override");
|
||||
return descriptors(dirname, opts, `${alias}.overrides[${index}]`);
|
||||
}
|
||||
function buildOverrideEnvDescriptors({
|
||||
dirname,
|
||||
options
|
||||
}, alias, descriptors, index, envName) {
|
||||
var _options$overrides2, _override$env;
|
||||
const override = (_options$overrides2 = options.overrides) == null ? void 0 : _options$overrides2[index];
|
||||
if (!override) throw new Error("Assertion failure - missing override");
|
||||
const opts = (_override$env = override.env) == null ? void 0 : _override$env[envName];
|
||||
return opts ? descriptors(dirname, opts, `${alias}.overrides[${index}].env["${envName}"]`) : null;
|
||||
}
|
||||
function makeChainWalker({
|
||||
root,
|
||||
env,
|
||||
overrides,
|
||||
overridesEnv,
|
||||
createLogger
|
||||
}) {
|
||||
return function* chainWalker(input, context, files = new Set(), baseLogger) {
|
||||
const {
|
||||
dirname
|
||||
} = input;
|
||||
const flattenedConfigs = [];
|
||||
const rootOpts = root(input);
|
||||
if (configIsApplicable(rootOpts, dirname, context, input.filepath)) {
|
||||
flattenedConfigs.push({
|
||||
config: rootOpts,
|
||||
envName: undefined,
|
||||
index: undefined
|
||||
});
|
||||
const envOpts = env(input, context.envName);
|
||||
if (envOpts && configIsApplicable(envOpts, dirname, context, input.filepath)) {
|
||||
flattenedConfigs.push({
|
||||
config: envOpts,
|
||||
envName: context.envName,
|
||||
index: undefined
|
||||
});
|
||||
}
|
||||
(rootOpts.options.overrides || []).forEach((_, index) => {
|
||||
const overrideOps = overrides(input, index);
|
||||
if (configIsApplicable(overrideOps, dirname, context, input.filepath)) {
|
||||
flattenedConfigs.push({
|
||||
config: overrideOps,
|
||||
index,
|
||||
envName: undefined
|
||||
});
|
||||
const overrideEnvOpts = overridesEnv(input, index, context.envName);
|
||||
if (overrideEnvOpts && configIsApplicable(overrideEnvOpts, dirname, context, input.filepath)) {
|
||||
flattenedConfigs.push({
|
||||
config: overrideEnvOpts,
|
||||
index,
|
||||
envName: context.envName
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
if (flattenedConfigs.some(({
|
||||
config: {
|
||||
options: {
|
||||
ignore,
|
||||
only
|
||||
}
|
||||
}
|
||||
}) => shouldIgnore(context, ignore, only, dirname))) {
|
||||
return null;
|
||||
}
|
||||
const chain = emptyChain();
|
||||
const logger = createLogger(input, context, baseLogger);
|
||||
for (const {
|
||||
config,
|
||||
index,
|
||||
envName
|
||||
} of flattenedConfigs) {
|
||||
if (!(yield* mergeExtendsChain(chain, config.options, dirname, context, files, baseLogger))) {
|
||||
return null;
|
||||
}
|
||||
logger(config, index, envName);
|
||||
yield* mergeChainOpts(chain, config);
|
||||
}
|
||||
return chain;
|
||||
};
|
||||
}
|
||||
function* mergeExtendsChain(chain, opts, dirname, context, files, baseLogger) {
|
||||
if (opts.extends === undefined) return true;
|
||||
const file = yield* (0, _index.loadConfig)(opts.extends, dirname, context.envName, context.caller);
|
||||
if (files.has(file)) {
|
||||
throw new Error(`Configuration cycle detected loading ${file.filepath}.\n` + `File already loaded following the config chain:\n` + Array.from(files, file => ` - ${file.filepath}`).join("\n"));
|
||||
}
|
||||
files.add(file);
|
||||
const fileChain = yield* loadFileChain(validateExtendFile(file), context, files, baseLogger);
|
||||
files.delete(file);
|
||||
if (!fileChain) return false;
|
||||
mergeChain(chain, fileChain);
|
||||
return true;
|
||||
}
|
||||
function mergeChain(target, source) {
|
||||
target.options.push(...source.options);
|
||||
target.plugins.push(...source.plugins);
|
||||
target.presets.push(...source.presets);
|
||||
for (const file of source.files) {
|
||||
target.files.add(file);
|
||||
}
|
||||
return target;
|
||||
}
|
||||
function* mergeChainOpts(target, {
|
||||
options,
|
||||
plugins,
|
||||
presets
|
||||
}) {
|
||||
target.options.push(options);
|
||||
target.plugins.push(...(yield* plugins()));
|
||||
target.presets.push(...(yield* presets()));
|
||||
return target;
|
||||
}
|
||||
function emptyChain() {
|
||||
return {
|
||||
options: [],
|
||||
presets: [],
|
||||
plugins: [],
|
||||
files: new Set()
|
||||
};
|
||||
}
|
||||
function createConfigChainOptions(opts) {
|
||||
const options = Object.assign({}, opts);
|
||||
delete options.extends;
|
||||
delete options.env;
|
||||
delete options.overrides;
|
||||
delete options.plugins;
|
||||
delete options.presets;
|
||||
delete options.passPerPreset;
|
||||
delete options.ignore;
|
||||
delete options.only;
|
||||
delete options.test;
|
||||
delete options.include;
|
||||
delete options.exclude;
|
||||
if (hasOwnProperty.call(options, "sourceMap")) {
|
||||
options.sourceMaps = options.sourceMap;
|
||||
delete options.sourceMap;
|
||||
}
|
||||
return options;
|
||||
}
|
||||
function dedupDescriptors(items) {
|
||||
const map = new Map();
|
||||
const descriptors = [];
|
||||
for (const item of items) {
|
||||
if (typeof item.value === "function") {
|
||||
const fnKey = item.value;
|
||||
let nameMap = map.get(fnKey);
|
||||
if (!nameMap) {
|
||||
nameMap = new Map();
|
||||
map.set(fnKey, nameMap);
|
||||
}
|
||||
let desc = nameMap.get(item.name);
|
||||
if (!desc) {
|
||||
desc = {
|
||||
value: item
|
||||
};
|
||||
descriptors.push(desc);
|
||||
if (!item.ownPass) nameMap.set(item.name, desc);
|
||||
} else {
|
||||
desc.value = item;
|
||||
}
|
||||
} else {
|
||||
descriptors.push({
|
||||
value: item
|
||||
});
|
||||
}
|
||||
}
|
||||
return descriptors.reduce((acc, desc) => {
|
||||
acc.push(desc.value);
|
||||
return acc;
|
||||
}, []);
|
||||
}
|
||||
function configIsApplicable({
|
||||
options
|
||||
}, dirname, context, configName) {
|
||||
return (options.test === undefined || configFieldIsApplicable(context, options.test, dirname, configName)) && (options.include === undefined || configFieldIsApplicable(context, options.include, dirname, configName)) && (options.exclude === undefined || !configFieldIsApplicable(context, options.exclude, dirname, configName));
|
||||
}
|
||||
function configFieldIsApplicable(context, test, dirname, configName) {
|
||||
const patterns = Array.isArray(test) ? test : [test];
|
||||
return matchesPatterns(context, patterns, dirname, configName);
|
||||
}
|
||||
function ignoreListReplacer(_key, value) {
|
||||
if (value instanceof RegExp) {
|
||||
return String(value);
|
||||
}
|
||||
return value;
|
||||
}
|
||||
function shouldIgnore(context, ignore, only, dirname) {
|
||||
if (ignore && matchesPatterns(context, ignore, dirname)) {
|
||||
var _context$filename;
|
||||
const message = `No config is applied to "${(_context$filename = context.filename) != null ? _context$filename : "(unknown)"}" because it matches one of \`ignore: ${JSON.stringify(ignore, ignoreListReplacer)}\` from "${dirname}"`;
|
||||
debug(message);
|
||||
if (context.showConfig) {
|
||||
console.log(message);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
if (only && !matchesPatterns(context, only, dirname)) {
|
||||
var _context$filename2;
|
||||
const message = `No config is applied to "${(_context$filename2 = context.filename) != null ? _context$filename2 : "(unknown)"}" because it fails to match one of \`only: ${JSON.stringify(only, ignoreListReplacer)}\` from "${dirname}"`;
|
||||
debug(message);
|
||||
if (context.showConfig) {
|
||||
console.log(message);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
function matchesPatterns(context, patterns, dirname, configName) {
|
||||
return patterns.some(pattern => matchPattern(pattern, dirname, context.filename, context, configName));
|
||||
}
|
||||
function matchPattern(pattern, dirname, pathToTest, context, configName) {
|
||||
if (typeof pattern === "function") {
|
||||
return !!(0, _rewriteStackTrace.endHiddenCallStack)(pattern)(pathToTest, {
|
||||
dirname,
|
||||
envName: context.envName,
|
||||
caller: context.caller
|
||||
});
|
||||
}
|
||||
if (typeof pathToTest !== "string") {
|
||||
throw new _configError.default(`Configuration contains string/RegExp pattern, but no filename was passed to Babel`, configName);
|
||||
}
|
||||
if (typeof pattern === "string") {
|
||||
pattern = (0, _patternToRegex.default)(pattern, dirname);
|
||||
}
|
||||
return pattern.test(pathToTest);
|
||||
}
|
||||
0 && 0;
|
||||
|
||||
//# sourceMappingURL=config-chain.js.map
|
||||
1
frontend/node_modules/@babel/core/lib/config/config-chain.js.map
generated
vendored
Normal file
1
frontend/node_modules/@babel/core/lib/config/config-chain.js.map
generated
vendored
Normal file
File diff suppressed because one or more lines are too long
190
frontend/node_modules/@babel/core/lib/config/config-descriptors.js
generated
vendored
Normal file
190
frontend/node_modules/@babel/core/lib/config/config-descriptors.js
generated
vendored
Normal file
@ -0,0 +1,190 @@
|
||||
"use strict";
|
||||
|
||||
Object.defineProperty(exports, "__esModule", {
|
||||
value: true
|
||||
});
|
||||
exports.createCachedDescriptors = createCachedDescriptors;
|
||||
exports.createDescriptor = createDescriptor;
|
||||
exports.createUncachedDescriptors = createUncachedDescriptors;
|
||||
function _gensync() {
|
||||
const data = require("gensync");
|
||||
_gensync = function () {
|
||||
return data;
|
||||
};
|
||||
return data;
|
||||
}
|
||||
var _functional = require("../gensync-utils/functional.js");
|
||||
var _index = require("./files/index.js");
|
||||
var _item = require("./item.js");
|
||||
var _caching = require("./caching.js");
|
||||
var _resolveTargets = require("./resolve-targets.js");
|
||||
function isEqualDescriptor(a, b) {
|
||||
var _a$file, _b$file, _a$file2, _b$file2;
|
||||
return a.name === b.name && a.value === b.value && a.options === b.options && a.dirname === b.dirname && a.alias === b.alias && a.ownPass === b.ownPass && ((_a$file = a.file) == null ? void 0 : _a$file.request) === ((_b$file = b.file) == null ? void 0 : _b$file.request) && ((_a$file2 = a.file) == null ? void 0 : _a$file2.resolved) === ((_b$file2 = b.file) == null ? void 0 : _b$file2.resolved);
|
||||
}
|
||||
function* handlerOf(value) {
|
||||
return value;
|
||||
}
|
||||
function optionsWithResolvedBrowserslistConfigFile(options, dirname) {
|
||||
if (typeof options.browserslistConfigFile === "string") {
|
||||
options.browserslistConfigFile = (0, _resolveTargets.resolveBrowserslistConfigFile)(options.browserslistConfigFile, dirname);
|
||||
}
|
||||
return options;
|
||||
}
|
||||
function createCachedDescriptors(dirname, options, alias) {
|
||||
const {
|
||||
plugins,
|
||||
presets,
|
||||
passPerPreset
|
||||
} = options;
|
||||
return {
|
||||
options: optionsWithResolvedBrowserslistConfigFile(options, dirname),
|
||||
plugins: plugins ? () => createCachedPluginDescriptors(plugins, dirname)(alias) : () => handlerOf([]),
|
||||
presets: presets ? () => createCachedPresetDescriptors(presets, dirname)(alias)(!!passPerPreset) : () => handlerOf([])
|
||||
};
|
||||
}
|
||||
function createUncachedDescriptors(dirname, options, alias) {
|
||||
return {
|
||||
options: optionsWithResolvedBrowserslistConfigFile(options, dirname),
|
||||
plugins: (0, _functional.once)(() => createPluginDescriptors(options.plugins || [], dirname, alias)),
|
||||
presets: (0, _functional.once)(() => createPresetDescriptors(options.presets || [], dirname, alias, !!options.passPerPreset))
|
||||
};
|
||||
}
|
||||
const PRESET_DESCRIPTOR_CACHE = new WeakMap();
|
||||
const createCachedPresetDescriptors = (0, _caching.makeWeakCacheSync)((items, cache) => {
|
||||
const dirname = cache.using(dir => dir);
|
||||
return (0, _caching.makeStrongCacheSync)(alias => (0, _caching.makeStrongCache)(function* (passPerPreset) {
|
||||
const descriptors = yield* createPresetDescriptors(items, dirname, alias, passPerPreset);
|
||||
return descriptors.map(desc => loadCachedDescriptor(PRESET_DESCRIPTOR_CACHE, desc));
|
||||
}));
|
||||
});
|
||||
const PLUGIN_DESCRIPTOR_CACHE = new WeakMap();
|
||||
const createCachedPluginDescriptors = (0, _caching.makeWeakCacheSync)((items, cache) => {
|
||||
const dirname = cache.using(dir => dir);
|
||||
return (0, _caching.makeStrongCache)(function* (alias) {
|
||||
const descriptors = yield* createPluginDescriptors(items, dirname, alias);
|
||||
return descriptors.map(desc => loadCachedDescriptor(PLUGIN_DESCRIPTOR_CACHE, desc));
|
||||
});
|
||||
});
|
||||
const DEFAULT_OPTIONS = {};
|
||||
function loadCachedDescriptor(cache, desc) {
|
||||
const {
|
||||
value,
|
||||
options = DEFAULT_OPTIONS
|
||||
} = desc;
|
||||
if (options === false) return desc;
|
||||
let cacheByOptions = cache.get(value);
|
||||
if (!cacheByOptions) {
|
||||
cacheByOptions = new WeakMap();
|
||||
cache.set(value, cacheByOptions);
|
||||
}
|
||||
let possibilities = cacheByOptions.get(options);
|
||||
if (!possibilities) {
|
||||
possibilities = [];
|
||||
cacheByOptions.set(options, possibilities);
|
||||
}
|
||||
if (!possibilities.includes(desc)) {
|
||||
const matches = possibilities.filter(possibility => isEqualDescriptor(possibility, desc));
|
||||
if (matches.length > 0) {
|
||||
return matches[0];
|
||||
}
|
||||
possibilities.push(desc);
|
||||
}
|
||||
return desc;
|
||||
}
|
||||
function* createPresetDescriptors(items, dirname, alias, passPerPreset) {
|
||||
return yield* createDescriptors("preset", items, dirname, alias, passPerPreset);
|
||||
}
|
||||
function* createPluginDescriptors(items, dirname, alias) {
|
||||
return yield* createDescriptors("plugin", items, dirname, alias);
|
||||
}
|
||||
function* createDescriptors(type, items, dirname, alias, ownPass) {
|
||||
const descriptors = yield* _gensync().all(items.map((item, index) => createDescriptor(item, dirname, {
|
||||
type,
|
||||
alias: `${alias}$${index}`,
|
||||
ownPass: !!ownPass
|
||||
})));
|
||||
assertNoDuplicates(descriptors);
|
||||
return descriptors;
|
||||
}
|
||||
function* createDescriptor(pair, dirname, {
|
||||
type,
|
||||
alias,
|
||||
ownPass
|
||||
}) {
|
||||
const desc = (0, _item.getItemDescriptor)(pair);
|
||||
if (desc) {
|
||||
return desc;
|
||||
}
|
||||
let name;
|
||||
let options;
|
||||
let value = pair;
|
||||
if (Array.isArray(value)) {
|
||||
if (value.length === 3) {
|
||||
[value, options, name] = value;
|
||||
} else {
|
||||
[value, options] = value;
|
||||
}
|
||||
}
|
||||
let file = undefined;
|
||||
let filepath = null;
|
||||
if (typeof value === "string") {
|
||||
if (typeof type !== "string") {
|
||||
throw new Error("To resolve a string-based item, the type of item must be given");
|
||||
}
|
||||
const resolver = type === "plugin" ? _index.loadPlugin : _index.loadPreset;
|
||||
const request = value;
|
||||
({
|
||||
filepath,
|
||||
value
|
||||
} = yield* resolver(value, dirname));
|
||||
file = {
|
||||
request,
|
||||
resolved: filepath
|
||||
};
|
||||
}
|
||||
if (!value) {
|
||||
throw new Error(`Unexpected falsy value: ${String(value)}`);
|
||||
}
|
||||
if (typeof value === "object" && value.__esModule) {
|
||||
if (value.default) {
|
||||
value = value.default;
|
||||
} else {
|
||||
throw new Error("Must export a default export when using ES6 modules.");
|
||||
}
|
||||
}
|
||||
if (typeof value !== "object" && typeof value !== "function") {
|
||||
throw new Error(`Unsupported format: ${typeof value}. Expected an object or a function.`);
|
||||
}
|
||||
if (filepath !== null && typeof value === "object" && value) {
|
||||
throw new Error(`Plugin/Preset files are not allowed to export objects, only functions. In ${filepath}`);
|
||||
}
|
||||
return {
|
||||
name,
|
||||
alias: filepath || alias,
|
||||
value,
|
||||
options,
|
||||
dirname,
|
||||
ownPass,
|
||||
file
|
||||
};
|
||||
}
|
||||
function assertNoDuplicates(items) {
|
||||
const map = new Map();
|
||||
for (const item of items) {
|
||||
if (typeof item.value !== "function") continue;
|
||||
let nameMap = map.get(item.value);
|
||||
if (!nameMap) {
|
||||
nameMap = new Set();
|
||||
map.set(item.value, nameMap);
|
||||
}
|
||||
if (nameMap.has(item.name)) {
|
||||
const conflicts = items.filter(i => i.value === item.value);
|
||||
throw new Error([`Duplicate plugin/preset detected.`, `If you'd like to use two separate instances of a plugin,`, `they need separate names, e.g.`, ``, ` plugins: [`, ` ['some-plugin', {}],`, ` ['some-plugin', {}, 'some unique name'],`, ` ]`, ``, `Duplicates detected are:`, `${JSON.stringify(conflicts, null, 2)}`].join("\n"));
|
||||
}
|
||||
nameMap.add(item.name);
|
||||
}
|
||||
}
|
||||
0 && 0;
|
||||
|
||||
//# sourceMappingURL=config-descriptors.js.map
|
||||
1
frontend/node_modules/@babel/core/lib/config/config-descriptors.js.map
generated
vendored
Normal file
1
frontend/node_modules/@babel/core/lib/config/config-descriptors.js.map
generated
vendored
Normal file
File diff suppressed because one or more lines are too long
290
frontend/node_modules/@babel/core/lib/config/files/configuration.js
generated
vendored
Normal file
290
frontend/node_modules/@babel/core/lib/config/files/configuration.js
generated
vendored
Normal file
@ -0,0 +1,290 @@
|
||||
"use strict";
|
||||
|
||||
Object.defineProperty(exports, "__esModule", {
|
||||
value: true
|
||||
});
|
||||
exports.ROOT_CONFIG_FILENAMES = void 0;
|
||||
exports.findConfigUpwards = findConfigUpwards;
|
||||
exports.findRelativeConfig = findRelativeConfig;
|
||||
exports.findRootConfig = findRootConfig;
|
||||
exports.loadConfig = loadConfig;
|
||||
exports.resolveShowConfigPath = resolveShowConfigPath;
|
||||
function _debug() {
|
||||
const data = require("debug");
|
||||
_debug = function () {
|
||||
return data;
|
||||
};
|
||||
return data;
|
||||
}
|
||||
function _fs() {
|
||||
const data = require("fs");
|
||||
_fs = function () {
|
||||
return data;
|
||||
};
|
||||
return data;
|
||||
}
|
||||
function _path() {
|
||||
const data = require("path");
|
||||
_path = function () {
|
||||
return data;
|
||||
};
|
||||
return data;
|
||||
}
|
||||
function _json() {
|
||||
const data = require("json5");
|
||||
_json = function () {
|
||||
return data;
|
||||
};
|
||||
return data;
|
||||
}
|
||||
function _gensync() {
|
||||
const data = require("gensync");
|
||||
_gensync = function () {
|
||||
return data;
|
||||
};
|
||||
return data;
|
||||
}
|
||||
var _caching = require("../caching.js");
|
||||
var _configApi = require("../helpers/config-api.js");
|
||||
var _utils = require("./utils.js");
|
||||
var _moduleTypes = require("./module-types.js");
|
||||
var _patternToRegex = require("../pattern-to-regex.js");
|
||||
var _configError = require("../../errors/config-error.js");
|
||||
var fs = require("../../gensync-utils/fs.js");
|
||||
require("module");
|
||||
var _rewriteStackTrace = require("../../errors/rewrite-stack-trace.js");
|
||||
var _async = require("../../gensync-utils/async.js");
|
||||
const debug = _debug()("babel:config:loading:files:configuration");
|
||||
const ROOT_CONFIG_FILENAMES = exports.ROOT_CONFIG_FILENAMES = ["babel.config.js", "babel.config.cjs", "babel.config.mjs", "babel.config.json", "babel.config.cts", "babel.config.ts", "babel.config.mts"];
|
||||
const RELATIVE_CONFIG_FILENAMES = [".babelrc", ".babelrc.js", ".babelrc.cjs", ".babelrc.mjs", ".babelrc.json", ".babelrc.cts"];
|
||||
const BABELIGNORE_FILENAME = ".babelignore";
|
||||
const runConfig = (0, _caching.makeWeakCache)(function* runConfig(options, cache) {
|
||||
yield* [];
|
||||
return {
|
||||
options: (0, _rewriteStackTrace.endHiddenCallStack)(options)((0, _configApi.makeConfigAPI)(cache)),
|
||||
cacheNeedsConfiguration: !cache.configured()
|
||||
};
|
||||
});
|
||||
function* readConfigCode(filepath, data) {
|
||||
if (!_fs().existsSync(filepath)) return null;
|
||||
let options = yield* (0, _moduleTypes.default)(filepath, (yield* (0, _async.isAsync)()) ? "auto" : "require", "You appear to be using a native ECMAScript module configuration " + "file, which is only supported when running Babel asynchronously " + "or when using the Node.js `--experimental-require-module` flag.", "You appear to be using a configuration file that contains top-level " + "await, which is only supported when running Babel asynchronously.");
|
||||
let cacheNeedsConfiguration = false;
|
||||
if (typeof options === "function") {
|
||||
({
|
||||
options,
|
||||
cacheNeedsConfiguration
|
||||
} = yield* runConfig(options, data));
|
||||
}
|
||||
if (!options || typeof options !== "object" || Array.isArray(options)) {
|
||||
throw new _configError.default(`Configuration should be an exported JavaScript object.`, filepath);
|
||||
}
|
||||
if (typeof options.then === "function") {
|
||||
options.catch == null || options.catch(() => {});
|
||||
throw new _configError.default(`You appear to be using an async configuration, ` + `which your current version of Babel does not support. ` + `We may add support for this in the future, ` + `but if you're on the most recent version of @babel/core and still ` + `seeing this error, then you'll need to synchronously return your config.`, filepath);
|
||||
}
|
||||
if (cacheNeedsConfiguration) throwConfigError(filepath);
|
||||
return buildConfigFileObject(options, filepath);
|
||||
}
|
||||
const cfboaf = new WeakMap();
|
||||
function buildConfigFileObject(options, filepath) {
|
||||
let configFilesByFilepath = cfboaf.get(options);
|
||||
if (!configFilesByFilepath) {
|
||||
cfboaf.set(options, configFilesByFilepath = new Map());
|
||||
}
|
||||
let configFile = configFilesByFilepath.get(filepath);
|
||||
if (!configFile) {
|
||||
configFile = {
|
||||
filepath,
|
||||
dirname: _path().dirname(filepath),
|
||||
options
|
||||
};
|
||||
configFilesByFilepath.set(filepath, configFile);
|
||||
}
|
||||
return configFile;
|
||||
}
|
||||
const packageToBabelConfig = (0, _caching.makeWeakCacheSync)(file => {
|
||||
const babel = file.options.babel;
|
||||
if (babel === undefined) return null;
|
||||
if (typeof babel !== "object" || Array.isArray(babel) || babel === null) {
|
||||
throw new _configError.default(`.babel property must be an object`, file.filepath);
|
||||
}
|
||||
return {
|
||||
filepath: file.filepath,
|
||||
dirname: file.dirname,
|
||||
options: babel
|
||||
};
|
||||
});
|
||||
const readConfigJSON5 = (0, _utils.makeStaticFileCache)((filepath, content) => {
|
||||
let options;
|
||||
try {
|
||||
options = _json().parse(content);
|
||||
} catch (err) {
|
||||
throw new _configError.default(`Error while parsing config - ${err.message}`, filepath);
|
||||
}
|
||||
if (!options) throw new _configError.default(`No config detected`, filepath);
|
||||
if (typeof options !== "object") {
|
||||
throw new _configError.default(`Config returned typeof ${typeof options}`, filepath);
|
||||
}
|
||||
if (Array.isArray(options)) {
|
||||
throw new _configError.default(`Expected config object but found array`, filepath);
|
||||
}
|
||||
delete options.$schema;
|
||||
return {
|
||||
filepath,
|
||||
dirname: _path().dirname(filepath),
|
||||
options
|
||||
};
|
||||
});
|
||||
const readIgnoreConfig = (0, _utils.makeStaticFileCache)((filepath, content) => {
|
||||
const ignoreDir = _path().dirname(filepath);
|
||||
const ignorePatterns = content.split("\n").map(line => line.replace(/#.*$/, "").trim()).filter(Boolean);
|
||||
for (const pattern of ignorePatterns) {
|
||||
if (pattern.startsWith("!")) {
|
||||
throw new _configError.default(`Negation of file paths is not supported.`, filepath);
|
||||
}
|
||||
}
|
||||
return {
|
||||
filepath,
|
||||
dirname: _path().dirname(filepath),
|
||||
ignore: ignorePatterns.map(pattern => (0, _patternToRegex.default)(pattern, ignoreDir))
|
||||
};
|
||||
});
|
||||
function findConfigUpwards(rootDir) {
|
||||
let dirname = rootDir;
|
||||
for (;;) {
|
||||
for (const filename of ROOT_CONFIG_FILENAMES) {
|
||||
if (_fs().existsSync(_path().join(dirname, filename))) {
|
||||
return dirname;
|
||||
}
|
||||
}
|
||||
const nextDir = _path().dirname(dirname);
|
||||
if (dirname === nextDir) break;
|
||||
dirname = nextDir;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
function* findRelativeConfig(packageData, envName, caller) {
|
||||
let config = null;
|
||||
let ignore = null;
|
||||
const dirname = _path().dirname(packageData.filepath);
|
||||
for (const loc of packageData.directories) {
|
||||
if (!config) {
|
||||
var _packageData$pkg;
|
||||
config = yield* loadOneConfig(RELATIVE_CONFIG_FILENAMES, loc, envName, caller, ((_packageData$pkg = packageData.pkg) == null ? void 0 : _packageData$pkg.dirname) === loc ? packageToBabelConfig(packageData.pkg) : null);
|
||||
}
|
||||
if (!ignore) {
|
||||
const ignoreLoc = _path().join(loc, BABELIGNORE_FILENAME);
|
||||
ignore = yield* readIgnoreConfig(ignoreLoc);
|
||||
if (ignore) {
|
||||
debug("Found ignore %o from %o.", ignore.filepath, dirname);
|
||||
}
|
||||
}
|
||||
}
|
||||
return {
|
||||
config,
|
||||
ignore
|
||||
};
|
||||
}
|
||||
function findRootConfig(dirname, envName, caller) {
|
||||
return loadOneConfig(ROOT_CONFIG_FILENAMES, dirname, envName, caller);
|
||||
}
|
||||
function* loadOneConfig(names, dirname, envName, caller, previousConfig = null) {
|
||||
const configs = yield* _gensync().all(names.map(filename => readConfig(_path().join(dirname, filename), envName, caller)));
|
||||
const config = configs.reduce((previousConfig, config) => {
|
||||
if (config && previousConfig) {
|
||||
throw new _configError.default(`Multiple configuration files found. Please remove one:\n` + ` - ${_path().basename(previousConfig.filepath)}\n` + ` - ${config.filepath}\n` + `from ${dirname}`);
|
||||
}
|
||||
return config || previousConfig;
|
||||
}, previousConfig);
|
||||
if (config) {
|
||||
debug("Found configuration %o from %o.", config.filepath, dirname);
|
||||
}
|
||||
return config;
|
||||
}
|
||||
function* loadConfig(name, dirname, envName, caller) {
|
||||
const filepath = (((v, w) => (v = v.split("."), w = w.split("."), +v[0] > +w[0] || v[0] == w[0] && +v[1] >= +w[1]))(process.versions.node, "8.9") ? require.resolve : (r, {
|
||||
paths: [b]
|
||||
}, M = require("module")) => {
|
||||
let f = M._findPath(r, M._nodeModulePaths(b).concat(b));
|
||||
if (f) return f;
|
||||
f = new Error(`Cannot resolve module '${r}'`);
|
||||
f.code = "MODULE_NOT_FOUND";
|
||||
throw f;
|
||||
})(name, {
|
||||
paths: [dirname]
|
||||
});
|
||||
const conf = yield* readConfig(filepath, envName, caller);
|
||||
if (!conf) {
|
||||
throw new _configError.default(`Config file contains no configuration data`, filepath);
|
||||
}
|
||||
debug("Loaded config %o from %o.", name, dirname);
|
||||
return conf;
|
||||
}
|
||||
function readConfig(filepath, envName, caller) {
|
||||
const ext = _path().extname(filepath);
|
||||
switch (ext) {
|
||||
case ".js":
|
||||
case ".cjs":
|
||||
case ".mjs":
|
||||
case ".ts":
|
||||
case ".cts":
|
||||
case ".mts":
|
||||
return readConfigCode(filepath, {
|
||||
envName,
|
||||
caller
|
||||
});
|
||||
default:
|
||||
return readConfigJSON5(filepath);
|
||||
}
|
||||
}
|
||||
function* resolveShowConfigPath(dirname) {
|
||||
const targetPath = process.env.BABEL_SHOW_CONFIG_FOR;
|
||||
if (targetPath != null) {
|
||||
const absolutePath = _path().resolve(dirname, targetPath);
|
||||
const stats = yield* fs.stat(absolutePath);
|
||||
if (!stats.isFile()) {
|
||||
throw new Error(`${absolutePath}: BABEL_SHOW_CONFIG_FOR must refer to a regular file, directories are not supported.`);
|
||||
}
|
||||
return absolutePath;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
function throwConfigError(filepath) {
|
||||
throw new _configError.default(`\
|
||||
Caching was left unconfigured. Babel's plugins, presets, and .babelrc.js files can be configured
|
||||
for various types of caching, using the first param of their handler functions:
|
||||
|
||||
module.exports = function(api) {
|
||||
// The API exposes the following:
|
||||
|
||||
// Cache the returned value forever and don't call this function again.
|
||||
api.cache(true);
|
||||
|
||||
// Don't cache at all. Not recommended because it will be very slow.
|
||||
api.cache(false);
|
||||
|
||||
// Cached based on the value of some function. If this function returns a value different from
|
||||
// a previously-encountered value, the plugins will re-evaluate.
|
||||
var env = api.cache(() => process.env.NODE_ENV);
|
||||
|
||||
// If testing for a specific env, we recommend specifics to avoid instantiating a plugin for
|
||||
// any possible NODE_ENV value that might come up during plugin execution.
|
||||
var isProd = api.cache(() => process.env.NODE_ENV === "production");
|
||||
|
||||
// .cache(fn) will perform a linear search though instances to find the matching plugin based
|
||||
// based on previous instantiated plugins. If you want to recreate the plugin and discard the
|
||||
// previous instance whenever something changes, you may use:
|
||||
var isProd = api.cache.invalidate(() => process.env.NODE_ENV === "production");
|
||||
|
||||
// Note, we also expose the following more-verbose versions of the above examples:
|
||||
api.cache.forever(); // api.cache(true)
|
||||
api.cache.never(); // api.cache(false)
|
||||
api.cache.using(fn); // api.cache(fn)
|
||||
|
||||
// Return the value that will be cached.
|
||||
return { };
|
||||
};`, filepath);
|
||||
}
|
||||
0 && 0;
|
||||
|
||||
//# sourceMappingURL=configuration.js.map
|
||||
1
frontend/node_modules/@babel/core/lib/config/files/configuration.js.map
generated
vendored
Normal file
1
frontend/node_modules/@babel/core/lib/config/files/configuration.js.map
generated
vendored
Normal file
File diff suppressed because one or more lines are too long
6
frontend/node_modules/@babel/core/lib/config/files/import.cjs
generated
vendored
Normal file
6
frontend/node_modules/@babel/core/lib/config/files/import.cjs
generated
vendored
Normal file
@ -0,0 +1,6 @@
|
||||
module.exports = function import_(filepath) {
|
||||
return import(filepath);
|
||||
};
|
||||
0 && 0;
|
||||
|
||||
//# sourceMappingURL=import.cjs.map
|
||||
1
frontend/node_modules/@babel/core/lib/config/files/import.cjs.map
generated
vendored
Normal file
1
frontend/node_modules/@babel/core/lib/config/files/import.cjs.map
generated
vendored
Normal file
@ -0,0 +1 @@
|
||||
{"version":3,"names":["module","exports","import_","filepath"],"sources":["../../../src/config/files/import.cjs"],"sourcesContent":["// We keep this in a separate file so that in older node versions, where\n// import() isn't supported, we can try/catch around the require() call\n// when loading this file.\n\nmodule.exports = function import_(filepath) {\n return import(filepath);\n};\n"],"mappings":"AAIAA,MAAM,CAACC,OAAO,GAAG,SAASC,OAAOA,CAACC,QAAQ,EAAE;EAC1C,OAAO,OAAOA,QAAQ,CAAC;AACzB,CAAC;AAAC","ignoreList":[]}
|
||||
58
frontend/node_modules/@babel/core/lib/config/files/index-browser.js
generated
vendored
Normal file
58
frontend/node_modules/@babel/core/lib/config/files/index-browser.js
generated
vendored
Normal file
@ -0,0 +1,58 @@
|
||||
"use strict";
|
||||
|
||||
Object.defineProperty(exports, "__esModule", {
|
||||
value: true
|
||||
});
|
||||
exports.ROOT_CONFIG_FILENAMES = void 0;
|
||||
exports.findConfigUpwards = findConfigUpwards;
|
||||
exports.findPackageData = findPackageData;
|
||||
exports.findRelativeConfig = findRelativeConfig;
|
||||
exports.findRootConfig = findRootConfig;
|
||||
exports.loadConfig = loadConfig;
|
||||
exports.loadPlugin = loadPlugin;
|
||||
exports.loadPreset = loadPreset;
|
||||
exports.resolvePlugin = resolvePlugin;
|
||||
exports.resolvePreset = resolvePreset;
|
||||
exports.resolveShowConfigPath = resolveShowConfigPath;
|
||||
function findConfigUpwards(rootDir) {
|
||||
return null;
|
||||
}
|
||||
function* findPackageData(filepath) {
|
||||
return {
|
||||
filepath,
|
||||
directories: [],
|
||||
pkg: null,
|
||||
isPackage: false
|
||||
};
|
||||
}
|
||||
function* findRelativeConfig(pkgData, envName, caller) {
|
||||
return {
|
||||
config: null,
|
||||
ignore: null
|
||||
};
|
||||
}
|
||||
function* findRootConfig(dirname, envName, caller) {
|
||||
return null;
|
||||
}
|
||||
function* loadConfig(name, dirname, envName, caller) {
|
||||
throw new Error(`Cannot load ${name} relative to ${dirname} in a browser`);
|
||||
}
|
||||
function* resolveShowConfigPath(dirname) {
|
||||
return null;
|
||||
}
|
||||
const ROOT_CONFIG_FILENAMES = exports.ROOT_CONFIG_FILENAMES = [];
|
||||
function resolvePlugin(name, dirname) {
|
||||
return null;
|
||||
}
|
||||
function resolvePreset(name, dirname) {
|
||||
return null;
|
||||
}
|
||||
function loadPlugin(name, dirname) {
|
||||
throw new Error(`Cannot load plugin ${name} relative to ${dirname} in a browser`);
|
||||
}
|
||||
function loadPreset(name, dirname) {
|
||||
throw new Error(`Cannot load preset ${name} relative to ${dirname} in a browser`);
|
||||
}
|
||||
0 && 0;
|
||||
|
||||
//# sourceMappingURL=index-browser.js.map
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user