diff --git a/.gitea/workflows/deploy.yml b/.gitea/workflows/deploy.yml
index ea5826b..4bf64e4 100644
--- a/.gitea/workflows/deploy.yml
+++ b/.gitea/workflows/deploy.yml
@@ -2,14 +2,14 @@ name: Build & Deploy
on:
push:
- branches:
- - master
- - main
+ branches: [master, main]
workflow_dispatch:
env:
REGISTRY: git.junggomoa.com
- IMAGE_NAME: chpark/insurance
+ WEB_IMAGE: chpark/insurance
+ API_IMAGE: chpark/insurance-api
+ API_BASE_URL: https://api.insurance.junggomoa.com
jobs:
build-and-deploy:
@@ -31,16 +31,31 @@ jobs:
username: ${{ secrets.REGISTRY_USER }}
password: ${{ secrets.REGISTRY_TOKEN }}
- - name: Build and push image
+ - name: Build & push WEB image
uses: docker/build-push-action@v5
with:
context: .
+ file: ./Dockerfile
+ build-args: |
+ EXPO_PUBLIC_API_BASE=${{ env.API_BASE_URL }}
push: true
tags: |
- ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:latest
- ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ env.SHORT_SHA }}
- cache-from: type=registry,ref=${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:buildcache
- cache-to: type=registry,ref=${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:buildcache,mode=max
+ ${{ env.REGISTRY }}/${{ env.WEB_IMAGE }}:latest
+ ${{ env.REGISTRY }}/${{ env.WEB_IMAGE }}:${{ env.SHORT_SHA }}
+ cache-from: type=registry,ref=${{ env.REGISTRY }}/${{ env.WEB_IMAGE }}:buildcache
+ cache-to: type=registry,ref=${{ env.REGISTRY }}/${{ env.WEB_IMAGE }}:buildcache,mode=max
+
+ - name: Build & push API image
+ uses: docker/build-push-action@v5
+ with:
+ context: ./server
+ file: ./server/Dockerfile
+ push: true
+ tags: |
+ ${{ env.REGISTRY }}/${{ env.API_IMAGE }}:latest
+ ${{ env.REGISTRY }}/${{ env.API_IMAGE }}:${{ env.SHORT_SHA }}
+ cache-from: type=registry,ref=${{ env.REGISTRY }}/${{ env.API_IMAGE }}:buildcache
+ cache-to: type=registry,ref=${{ env.REGISTRY }}/${{ env.API_IMAGE }}:buildcache,mode=max
- name: Set up kubectl
uses: azure/setup-kubectl@v4
@@ -53,16 +68,40 @@ jobs:
echo "${{ secrets.KUBE_CONFIG }}" | base64 -d > $HOME/.kube/config
chmod 600 $HOME/.kube/config
- - name: Ensure namespace & registry secret
+ - name: Ensure namespace, registry & DB secrets
run: |
kubectl apply -f deploy/k8s/namespace.yaml
+
kubectl -n insurance create secret docker-registry gitea-registry \
--docker-server=${{ env.REGISTRY }} \
--docker-username=${{ secrets.REGISTRY_USER }} \
--docker-password=${{ secrets.REGISTRY_TOKEN }} \
--dry-run=client -o yaml | kubectl apply -f -
- - name: Apply manifests
+ kubectl -n insurance create secret generic postgres-credentials \
+ --from-literal=username=insurance \
+ --from-literal=password='${{ secrets.POSTGRES_PASSWORD }}' \
+ --dry-run=client -o yaml | kubectl apply -f -
+
+ kubectl -n insurance create secret generic api-secrets \
+ --from-literal=jwtSecret='${{ secrets.JWT_SECRET }}' \
+ --from-literal=databaseUrl="postgresql://insurance:${{ secrets.POSTGRES_PASSWORD }}@postgres:5432/insurance?schema=public" \
+ --dry-run=client -o yaml | kubectl apply -f -
+
+ - name: Deploy Postgres
+ run: kubectl apply -f deploy/k8s/postgres.yaml
+
+ - name: Wait for Postgres
+ run: kubectl -n insurance rollout status statefulset/postgres --timeout=180s
+
+ - name: Deploy API
+ run: |
+ kubectl apply -f deploy/k8s/api.yaml
+ kubectl -n insurance set image deployment/insurance-api \
+ api=${{ env.REGISTRY }}/${{ env.API_IMAGE }}:${{ env.SHORT_SHA }}
+ kubectl -n insurance rollout status deployment/insurance-api --timeout=240s
+
+ - name: Deploy Web
run: |
kubectl apply -f deploy/k8s/deployment.yaml
kubectl apply -f deploy/k8s/service.yaml
@@ -71,15 +110,13 @@ jobs:
else
kubectl apply -f deploy/k8s/ingress.yaml
fi
-
- - name: Update deployment image & restart
- run: |
kubectl -n insurance set image deployment/insurance-web \
- web=${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ env.SHORT_SHA }}
+ web=${{ env.REGISTRY }}/${{ env.WEB_IMAGE }}:${{ env.SHORT_SHA }}
kubectl -n insurance rollout status deployment/insurance-web --timeout=180s
- name: Show deployment info
run: |
- kubectl -n insurance get deployment,svc,ingress
+ kubectl -n insurance get deployment,statefulset,svc,ingress,pvc
echo ""
- echo "π Deployed: https://insurance.junggomoa.com"
+ echo "π Web: https://insurance.junggomoa.com"
+ echo "π API: https://api.insurance.junggomoa.com"
diff --git a/.gitignore b/.gitignore
index 0431d4e..c6af005 100644
--- a/.gitignore
+++ b/.gitignore
@@ -14,3 +14,4 @@ npm-debug.*
.env
.env.local
*.log
+.claude/
diff --git a/App.tsx b/App.tsx
index 183c0b2..917d1bb 100644
--- a/App.tsx
+++ b/App.tsx
@@ -1,16 +1,49 @@
import React from 'react';
+import { View, StyleSheet, Platform } from 'react-native';
import { StatusBar } from 'expo-status-bar';
import { SafeAreaProvider } from 'react-native-safe-area-context';
import { NavigationContainer } from '@react-navigation/native';
import RootNavigator from './src/navigation/RootNavigator';
+import ErrorBoundary from './src/components/ErrorBoundary';
export default function App() {
+ const isWeb = Platform.OS === 'web';
return (
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
);
}
+
+const styles = StyleSheet.create({
+ outer: { flex: 1 },
+ webOuter: {
+ flex: 1,
+ backgroundColor: '#E5E7EB',
+ alignItems: 'center',
+ justifyContent: 'center',
+ minHeight: '100%' as any,
+ },
+ inner: { flex: 1 },
+ webInner: {
+ width: '100%',
+ maxWidth: 480,
+ height: '100%' as any,
+ minHeight: 800,
+ backgroundColor: '#F9FAFB',
+ shadowColor: '#000',
+ shadowOffset: { width: 0, height: 4 },
+ shadowOpacity: 0.15,
+ shadowRadius: 20,
+ elevation: 10,
+ },
+});
diff --git a/DEPLOY.md b/DEPLOY.md
index 6b9f41d..6391c07 100644
--- a/DEPLOY.md
+++ b/DEPLOY.md
@@ -1,78 +1,40 @@
-# π λ°°ν¬ κ°μ΄λ
-
-## μλ λ°°ν¬ νμ΄νλΌμΈ
+# π λ°°ν¬ κ°μ΄λ (Full Stack)
```
-git push β Gitea Actions β Docker λΉλ β Container Registry β Kubernetes rollout
- β
- insurance.junggomoa.com
+git push β Gitea Actions
+ ββ Web Docker build β insurance.junggomoa.com (nginx)
+ ββ API Docker build β api.insurance.junggomoa.com (Fastify)
+ ββ Postgres StatefulSet (10Gi PVC)
```
----
+## β ν λ²λ§ μ€μ (Gitea Repo Secrets)
-## π ν λ²λ§ ν΄μΌ νλ μ΄κΈ° μ€μ
+[https://git.junggomoa.com/chpark/insurance/settings/actions/secrets](https://git.junggomoa.com/chpark/insurance/settings/actions/secrets)
-### 1λ¨κ³ β Gitea μΈ‘ μ€μ (μΉ UI)
-
-#### β Gitea Actions Runner νμ±ν
-1. [https://git.junggomoa.com/chpark/insurance](https://git.junggomoa.com/chpark/insurance) β **Settings**
-2. μ’μΈ‘ **Actions** λ©λ΄ β **Enable Actions** 체ν¬
-3. μ‘°μ§/μΈμ€ν΄μ€ κ΄λ¦¬μκ° **Runner**λ₯Ό νλ λ±λ‘ν΄λ¬μΌ ν©λλ€
- - Runner μλ€λ©΄ κ΄λ¦¬μμκ² μμ²
- - λλ Kubernetesμ `act-runner` Helm μ°¨νΈλ‘ μ§μ μ€μΉ (μλ μ€ν¬λ¦½νΈ μ°Έκ³ )
-
-#### β‘ Container Registry μ κ·Ό ν ν° λ°κΈ
-1. Gitea β μ°μΈ‘ μλ¨ νλ‘ν β **Settings** β **Applications**
-2. **Generate New Token** β κΆν `write:package`, `read:package` 체ν¬
-3. λ°κΈλ ν ν° λ³΅μ¬ (ν λ²λ§ 보μ)
-
-#### β’ Repository Secrets λ±λ‘
-Repo β **Settings** β **Secrets and Variables** β **Actions** β **Add Secret**:
-
-| Name | Value | μ€λͺ
|
+| Secret | κ° | λΉκ³ |
|---|---|---|
| `REGISTRY_USER` | `chpark` | Gitea μ¬μ©μλͺ
|
-| `REGISTRY_TOKEN` | (μμμ λ°κΈν ν ν°) | Container Registry μΈμ¦ |
-| `KUBE_CONFIG` | (μλ 2λ¨κ³μμ μμ±) | base64 μΈμ½λ©λ kubeconfig |
-| `INGRESS_MODE` | `ingress` λλ `ingressroute` | Traefik μ€μΉ λ°©μ (μλ νμΈ) |
+| `REGISTRY_TOKEN` | (Gitea β Settings β Applications β Generate Token, `write:package` 체ν¬) | |
+| `KUBE_CONFIG` | μλ²μμ μμ±ν base64 kubeconfig | μλ μ€ν¬λ¦½νΈ μ°Έκ³ |
+| `POSTGRES_PASSWORD` | μμμ κ°ν λΉλ°λ²νΈ (μ: `openssl rand -hex 24`) | DB λΉλ² |
+| `JWT_SECRET` | μμμ 32μ μ΄μ λλ€ λ¬Έμμ΄ (`openssl rand -hex 32`) | JWT μλͺ
ν€ |
+| `INGRESS_MODE` | `ingress` λλ `ingressroute` | Traefik λ²μ |
----
+## π kubeconfig μμ± (μλ²μμ ν λ²λ§)
-### 2λ¨κ³ β Kubernetes μΈ‘ μ€μ (μλ² SSH 1ν)
-
-SSH μ μ:
```bash
ssh chpark@183.99.177.40
-```
-
-#### β Traefik μ€μΉ λ°©μ νμΈ
-```bash
-kubectl api-resources | grep -i traefik
-```
-- κ²°κ³Όμ `ingressroutes.traefik.io` λμ€λ©΄ β `INGRESS_MODE=ingressroute`
-- μ무κ²λ μ λμ€λ©΄ β `INGRESS_MODE=ingress` (νμ€ Ingress μ¬μ©)
-
-#### β‘ CIμ© kubeconfig μμ± (μλΉμ€ μ΄μΉ΄μ΄νΈ λ°©μ, κΆμ₯)
-```bash
-# λ€μμ€νμ΄μ€Β·μλΉμ€μ΄μΉ΄μ΄νΈ μ μμ±
kubectl create namespace insurance 2>/dev/null || true
kubectl -n insurance create serviceaccount gitea-deployer
kubectl create clusterrolebinding gitea-deployer \
--clusterrole=cluster-admin \
--serviceaccount=insurance:gitea-deployer
-# ν ν° μμ± (24μκ° κΈ°λ³Έ, νμ μ --duration=8760h λ‘ 1λ
)
TOKEN=$(kubectl -n insurance create token gitea-deployer --duration=8760h)
-
-# νμ¬ API μλ² μ£Όμ
SERVER=$(kubectl config view --minify -o jsonpath='{.clusters[0].cluster.server}')
-CA=$(kubectl -n insurance get secret \
- $(kubectl -n insurance get sa gitea-deployer -o jsonpath='{.secrets[0].name}' 2>/dev/null || echo "") \
- -o jsonpath='{.data.ca\.crt}' 2>/dev/null || \
- kubectl config view --minify --raw -o jsonpath='{.clusters[0].cluster.certificate-authority-data}')
+CA=$(kubectl config view --minify --raw -o jsonpath='{.clusters[0].cluster.certificate-authority-data}')
-# kubeconfig μμ±
-cat < /tmp/gitea-kubeconfig
+cat > /tmp/gitea-kubeconfig <`
----
+## π§ͺ API μ€λͺ¨ν¬ ν
μ€νΈ
-## β
μ΄νλΆν° β μλ λ°°ν¬
-
-μ½λ μμ ν:
```bash
-git add .
-git commit -m "μμ λ΄μ©"
-git push
+# νμκ°μ
+curl -X POST http://localhost:4000/auth/register \
+ -H "Content-Type: application/json" \
+ -d '{"email":"test@test.com","password":"password123","name":"λ°μ² ν","age":34,"gender":"MALE","job":"μ¬λ¬΄μ§"}'
+
+# λ‘κ·ΈμΈ
+curl -X POST http://localhost:4000/auth/login \
+ -H "Content-Type: application/json" \
+ -d '{"email":"test@test.com","password":"password123"}'
+
+# μΉ΄μΉ΄μ€ λ‘κ·ΈμΈ (μΉ΄μΉ΄μ€ SDKμμ λ°μ access_token νμ)
+curl -X POST http://localhost:4000/auth/kakao \
+ -H "Content-Type: application/json" \
+ -d '{"accessToken":"YOUR_KAKAO_ACCESS_TOKEN"}'
```
-Gitea Actions νμμ μ§ν μν© νμΈ:
-- [https://git.junggomoa.com/chpark/insurance/actions](https://git.junggomoa.com/chpark/insurance/actions)
+## π¦ μΉ΄μΉ΄μ€ λ‘κ·ΈμΈ μ°λ
-**μ½ 3~5λΆ ν** `https://insurance.junggomoa.com` μ μ λ²μ λ°μλ©λλ€.
+### λ°±μλ
+- `POST /auth/kakao` μλν¬μΈνΈκ° `access_token`μ λ°μ `https://kapi.kakao.com/v2/user/me` νΈμΆ β νλ‘ν λ§€ν β JWT λ°κΈ
----
+### ν΄λΌμ΄μΈνΈ
+νμ¬ μΉμμλ κ°λ°μ© `window.prompt`λ‘ ν ν° λΆμ¬λ£κΈ° μ§μ. μ€μ μ΄μμμλ:
+- **Web**: Kakao JS SDK λ‘ `Kakao.Auth.login()` νΈμΆ β `access_token` νλ β `/auth/kakao`
+- **Native (iOS/Android)**: `@react-native-seoul/kakao-login` β `bare workflow`λ‘ prebuild ν μ°λ
-## π§ͺ μλ 1ν λ°°ν¬ (CI μΈν
μ ν
μ€νΈμ©)
+### νμν Kakao Developer μ€μ
+1. https://developers.kakao.com/ μ± μμ±
+2. νλ«νΌ: Web (λλ©μΈ λ±λ‘: `https://insurance.junggomoa.com`), Android/iOS λ³λ
+3. μΉ΄μΉ΄μ€ λ‘κ·ΈμΈ β Redirect URI: `https://insurance.junggomoa.com/auth/kakao/callback`
+4. λμνλͺ©: λλ€μ(νμ), νλ‘ν μ΄λ―Έμ§(μ ν), μ΄λ©μΌ(μ ν)
+
+## π νΈλ¬λΈμν
-SSH μ μ ν:
```bash
-# μ½λ λ°κΈ°
-git clone https://git.junggomoa.com/chpark/insurance.git
-cd insurance
+# Pod λ‘κ·Έ
+kubectl -n insurance logs -l app.kubernetes.io/name=insurance-api --tail=100
-# λ컀 μ΄λ―Έμ§ λΉλ
-docker build -t git.junggomoa.com/chpark/insurance:latest .
+# DB μ μ
+kubectl -n insurance exec -it postgres-0 -- psql -U insurance
-# λ μ§μ€νΈλ¦¬ λ‘κ·ΈμΈ & push
-docker login git.junggomoa.com -u chpark -p <ν ν°>
-docker push git.junggomoa.com/chpark/insurance:latest
+# λ§μ΄κ·Έλ μ΄μ
μλ μ€ν
+kubectl -n insurance exec -it deploy/insurance-api -- npx prisma migrate deploy
-# K8s μν¬λ¦Ώ & λ§€λνμ€νΈ μ μ©
-kubectl apply -f deploy/k8s/namespace.yaml
-kubectl -n insurance create secret docker-registry gitea-registry \
- --docker-server=git.junggomoa.com \
- --docker-username=chpark \
- --docker-password=<ν ν°>
-kubectl apply -f deploy/k8s/deployment.yaml
-kubectl apply -f deploy/k8s/service.yaml
-kubectl apply -f deploy/k8s/ingress.yaml # λλ ingressroute-traefik.yaml
-
-# μν νμΈ
-kubectl -n insurance get all
-kubectl -n insurance get ingress
-```
-
----
-
-## π§ νΈλ¬λΈμν
-
-| μ¦μ | μμΈ | ν΄κ²° |
-|---|---|---|
-| `ImagePullBackOff` | Registry μΈμ¦ μ€ν¨ | `gitea-registry` secret μ¬μμ± |
-| `404 page not found` (Traefik) | Ingress host λΆμΌμΉ | `insurance.junggomoa.com` μ² μ νμΈ |
-| TLS μΈμ¦μ μμ | cert-manager λ―Έμ€μ | κ΄λ¦¬μμκ² μμΌλμΉ΄λ μΈμ¦μ μμ² λλ cert-manager μ€μΉ |
-| Actions μ€ν¨: `no runner` | Runner λ―Έλ±λ‘ | μ‘°μ§μ act-runner λ±λ‘ νμ |
-
-λ‘κ·Έ νμΈ:
-```bash
-kubectl -n insurance logs -l app.kubernetes.io/name=insurance-web --tail=100
-kubectl -n insurance describe deployment insurance-web
-kubectl -n insurance describe ingress insurance-web
-```
-
----
-
-## π λ°°ν¬ κ΄λ ¨ νμΌ
-
-```
-Dockerfile # Multi-stage λΉλ (Expo β nginx)
-.dockerignore
-.gitea/workflows/deploy.yml # CI/CD νμ΄νλΌμΈ
-deploy/
- βββ nginx.conf # SPA fallback + gzip + health
- βββ k8s/
- βββ namespace.yaml
- βββ deployment.yaml # 2 replicas, rolling update
- βββ service.yaml # ClusterIP :80
- βββ ingress.yaml # νμ€ Ingress (Traefik ingressClass)
- βββ ingressroute-traefik.yaml # Traefik CRD λ²μ (μ ν)
+# μ΄λ―Έμ§ μ¬λ°°ν¬
+kubectl -n insurance rollout restart deployment/insurance-api deployment/insurance-web
```
diff --git a/Dockerfile b/Dockerfile
index a35b5a7..95b675b 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -1,6 +1,9 @@
FROM node:20-alpine AS builder
WORKDIR /app
+ARG EXPO_PUBLIC_API_BASE=https://api.insurance.junggomoa.com
+ENV EXPO_PUBLIC_API_BASE=${EXPO_PUBLIC_API_BASE}
+
COPY package*.json ./
RUN npm ci --legacy-peer-deps --no-audit --no-fund
diff --git a/app.json b/app.json
index 8a188d0..5b6bbe3 100644
--- a/app.json
+++ b/app.json
@@ -9,7 +9,9 @@
"resizeMode": "contain",
"backgroundColor": "#3B82F6"
},
- "assetBundlePatterns": ["**/*"],
+ "assetBundlePatterns": [
+ "**/*"
+ ],
"ios": {
"supportsTablet": true,
"bundleIdentifier": "com.insurancecare.app",
@@ -38,7 +40,8 @@
"photosPermission": "보νκΈ μ²κ΅¬ μλ₯ 첨λΆλ₯Ό μν΄ μ¬μ§ μ κ·Όμ΄ νμν©λλ€."
}
],
- "expo-notifications"
+ "expo-notifications",
+ "expo-font"
]
}
}
diff --git a/deploy/k8s/api.yaml b/deploy/k8s/api.yaml
new file mode 100644
index 0000000..c6ccfa7
--- /dev/null
+++ b/deploy/k8s/api.yaml
@@ -0,0 +1,130 @@
+apiVersion: v1
+kind: PersistentVolumeClaim
+metadata:
+ name: uploads
+ namespace: insurance
+spec:
+ accessModes: ["ReadWriteOnce"]
+ resources:
+ requests:
+ storage: 20Gi
+---
+apiVersion: apps/v1
+kind: Deployment
+metadata:
+ name: insurance-api
+ namespace: insurance
+ labels:
+ app.kubernetes.io/name: insurance-api
+spec:
+ replicas: 2
+ revisionHistoryLimit: 3
+ strategy:
+ type: RollingUpdate
+ rollingUpdate:
+ maxSurge: 1
+ maxUnavailable: 0
+ selector:
+ matchLabels:
+ app.kubernetes.io/name: insurance-api
+ template:
+ metadata:
+ labels:
+ app.kubernetes.io/name: insurance-api
+ spec:
+ imagePullSecrets:
+ - name: gitea-registry
+ containers:
+ - name: api
+ image: git.junggomoa.com/chpark/insurance-api:latest
+ imagePullPolicy: Always
+ ports:
+ - name: http
+ containerPort: 4000
+ env:
+ - name: PORT
+ value: "4000"
+ - name: HOST
+ value: "0.0.0.0"
+ - name: NODE_ENV
+ value: production
+ - name: UPLOAD_DIR
+ value: /data/uploads
+ - name: JWT_SECRET
+ valueFrom:
+ secretKeyRef:
+ name: api-secrets
+ key: jwtSecret
+ - name: DATABASE_URL
+ valueFrom:
+ secretKeyRef:
+ name: api-secrets
+ key: databaseUrl
+ readinessProbe:
+ httpGet:
+ path: /health
+ port: http
+ initialDelaySeconds: 5
+ periodSeconds: 10
+ livenessProbe:
+ httpGet:
+ path: /health
+ port: http
+ initialDelaySeconds: 30
+ periodSeconds: 20
+ resources:
+ requests:
+ cpu: 100m
+ memory: 128Mi
+ limits:
+ cpu: 500m
+ memory: 512Mi
+ volumeMounts:
+ - name: uploads
+ mountPath: /data/uploads
+ volumes:
+ - name: uploads
+ persistentVolumeClaim:
+ claimName: uploads
+---
+apiVersion: v1
+kind: Service
+metadata:
+ name: insurance-api
+ namespace: insurance
+ labels:
+ app.kubernetes.io/name: insurance-api
+spec:
+ type: ClusterIP
+ selector:
+ app.kubernetes.io/name: insurance-api
+ ports:
+ - name: http
+ port: 4000
+ targetPort: http
+---
+apiVersion: networking.k8s.io/v1
+kind: Ingress
+metadata:
+ name: insurance-api
+ namespace: insurance
+ annotations:
+ traefik.ingress.kubernetes.io/router.entrypoints: websecure
+ traefik.ingress.kubernetes.io/router.tls: "true"
+spec:
+ ingressClassName: traefik
+ tls:
+ - hosts:
+ - api.insurance.junggomoa.com
+ secretName: insurance-api-tls
+ rules:
+ - host: api.insurance.junggomoa.com
+ http:
+ paths:
+ - path: /
+ pathType: Prefix
+ backend:
+ service:
+ name: insurance-api
+ port:
+ number: 4000
diff --git a/deploy/k8s/postgres.yaml b/deploy/k8s/postgres.yaml
new file mode 100644
index 0000000..c40adf6
--- /dev/null
+++ b/deploy/k8s/postgres.yaml
@@ -0,0 +1,81 @@
+apiVersion: v1
+kind: Service
+metadata:
+ name: postgres
+ namespace: insurance
+ labels:
+ app.kubernetes.io/name: postgres
+spec:
+ clusterIP: None
+ selector:
+ app.kubernetes.io/name: postgres
+ ports:
+ - name: postgres
+ port: 5432
+ targetPort: 5432
+---
+apiVersion: apps/v1
+kind: StatefulSet
+metadata:
+ name: postgres
+ namespace: insurance
+spec:
+ serviceName: postgres
+ replicas: 1
+ selector:
+ matchLabels:
+ app.kubernetes.io/name: postgres
+ template:
+ metadata:
+ labels:
+ app.kubernetes.io/name: postgres
+ spec:
+ containers:
+ - name: postgres
+ image: postgres:16-alpine
+ ports:
+ - containerPort: 5432
+ name: postgres
+ env:
+ - name: POSTGRES_USER
+ valueFrom:
+ secretKeyRef:
+ name: postgres-credentials
+ key: username
+ - name: POSTGRES_PASSWORD
+ valueFrom:
+ secretKeyRef:
+ name: postgres-credentials
+ key: password
+ - name: POSTGRES_DB
+ value: insurance
+ - name: PGDATA
+ value: /var/lib/postgresql/data/pgdata
+ volumeMounts:
+ - name: data
+ mountPath: /var/lib/postgresql/data
+ readinessProbe:
+ exec:
+ command: ["pg_isready", "-U", "insurance"]
+ initialDelaySeconds: 5
+ periodSeconds: 10
+ livenessProbe:
+ exec:
+ command: ["pg_isready", "-U", "insurance"]
+ initialDelaySeconds: 30
+ periodSeconds: 20
+ resources:
+ requests:
+ cpu: 100m
+ memory: 256Mi
+ limits:
+ cpu: 1000m
+ memory: 1Gi
+ volumeClaimTemplates:
+ - metadata:
+ name: data
+ spec:
+ accessModes: ["ReadWriteOnce"]
+ resources:
+ requests:
+ storage: 10Gi
diff --git a/docker-compose.yml b/docker-compose.yml
new file mode 100644
index 0000000..6c2a21b
--- /dev/null
+++ b/docker-compose.yml
@@ -0,0 +1,41 @@
+version: "3.9"
+
+services:
+ postgres:
+ image: postgres:16-alpine
+ environment:
+ POSTGRES_USER: insurance
+ POSTGRES_PASSWORD: insurance_dev
+ POSTGRES_DB: insurance
+ ports:
+ - "5432:5432"
+ volumes:
+ - postgres_data:/var/lib/postgresql/data
+ healthcheck:
+ test: ["CMD-SHELL", "pg_isready -U insurance"]
+ interval: 5s
+ timeout: 3s
+ retries: 10
+
+ api:
+ build:
+ context: ./server
+ dockerfile: Dockerfile
+ depends_on:
+ postgres:
+ condition: service_healthy
+ environment:
+ DATABASE_URL: postgresql://insurance:insurance_dev@postgres:5432/insurance?schema=public
+ JWT_SECRET: dev-change-me
+ PORT: "4000"
+ HOST: "0.0.0.0"
+ NODE_ENV: development
+ UPLOAD_DIR: /data/uploads
+ ports:
+ - "4000:4000"
+ volumes:
+ - uploads:/data/uploads
+
+volumes:
+ postgres_data:
+ uploads:
diff --git a/package-lock.json b/package-lock.json
index 8007771..61e4590 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -16,14 +16,14 @@
"@react-navigation/native-stack": "^6.9.26",
"expo": "~51.0.0",
"expo-device": "~6.0.2",
- "expo-font": "^55.0.6",
- "expo-image-picker": "~15.0.5",
+ "expo-font": "~12.0.10",
+ "expo-image-picker": "~15.1.0",
"expo-linear-gradient": "~13.0.2",
"expo-notifications": "~0.28.0",
"expo-status-bar": "~1.12.1",
"react": "18.2.0",
"react-dom": "^18.2.0",
- "react-native": "0.74.3",
+ "react-native": "0.74.5",
"react-native-chart-kit": "^6.12.0",
"react-native-gesture-handler": "~2.16.1",
"react-native-reanimated": "~3.10.1",
@@ -478,6 +478,90 @@
"node": ">=6.0.0"
}
},
+ "node_modules/@babel/plugin-bugfix-firefox-class-in-computed-class-key": {
+ "version": "7.28.5",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-firefox-class-in-computed-class-key/-/plugin-bugfix-firefox-class-in-computed-class-key-7.28.5.tgz",
+ "integrity": "sha512-87GDMS3tsmMSi/3bWOte1UblL+YUTFMV8SZPZ2eSEL17s74Cw/l63rR6NmGVKMYW2GYi85nE+/d6Hw5N0bEk2Q==",
+ "license": "MIT",
+ "peer": true,
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.27.1",
+ "@babel/traverse": "^7.28.5"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0"
+ }
+ },
+ "node_modules/@babel/plugin-bugfix-safari-class-field-initializer-scope": {
+ "version": "7.27.1",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-safari-class-field-initializer-scope/-/plugin-bugfix-safari-class-field-initializer-scope-7.27.1.tgz",
+ "integrity": "sha512-qNeq3bCKnGgLkEXUuFry6dPlGfCdQNZbn7yUAPCInwAJHMU7THJfrBSozkcWq5sNM6RcF3S8XyQL2A52KNR9IA==",
+ "license": "MIT",
+ "peer": true,
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.27.1"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0"
+ }
+ },
+ "node_modules/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": {
+ "version": "7.27.1",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression/-/plugin-bugfix-safari-id-destructuring-collision-in-function-expression-7.27.1.tgz",
+ "integrity": "sha512-g4L7OYun04N1WyqMNjldFwlfPCLVkgB54A/YCXICZYBsvJJE3kByKv9c9+R/nAfmIfjl2rKYLNyMHboYbZaWaA==",
+ "license": "MIT",
+ "peer": true,
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.27.1"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0"
+ }
+ },
+ "node_modules/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": {
+ "version": "7.27.1",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining/-/plugin-bugfix-v8-spread-parameters-in-optional-chaining-7.27.1.tgz",
+ "integrity": "sha512-oO02gcONcD5O1iTLi/6frMJBIwWEHceWGSGqrpCmEL8nogiS6J9PBlE48CaK20/Jx1LuRml9aDftLgdjXT8+Cw==",
+ "license": "MIT",
+ "peer": true,
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.27.1",
+ "@babel/helper-skip-transparent-expression-wrappers": "^7.27.1",
+ "@babel/plugin-transform-optional-chaining": "^7.27.1"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.13.0"
+ }
+ },
+ "node_modules/@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly": {
+ "version": "7.28.6",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly/-/plugin-bugfix-v8-static-class-fields-redefine-readonly-7.28.6.tgz",
+ "integrity": "sha512-a0aBScVTlNaiUe35UtfxAN7A/tehvvG4/ByO6+46VPKTRSlfnAFsgKy0FUh+qAkQrDTmhDkT+IBOKlOoMUxQ0g==",
+ "license": "MIT",
+ "peer": true,
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.28.6",
+ "@babel/traverse": "^7.28.6"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0"
+ }
+ },
"node_modules/@babel/plugin-proposal-async-generator-functions": {
"version": "7.20.7",
"resolved": "https://registry.npmjs.org/@babel/plugin-proposal-async-generator-functions/-/plugin-proposal-async-generator-functions-7.20.7.tgz",
@@ -652,6 +736,19 @@
"@babel/core": "^7.0.0-0"
}
},
+ "node_modules/@babel/plugin-proposal-private-property-in-object": {
+ "version": "7.21.0-placeholder-for-preset-env.2",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-private-property-in-object/-/plugin-proposal-private-property-in-object-7.21.0-placeholder-for-preset-env.2.tgz",
+ "integrity": "sha512-SOSkfJDddaM7mak6cPEpswyTRnuRltl429hMraQEglW+OkovnCzsiszTmsrlY//qLFjCpQDFRvjdm2wA5pPm9w==",
+ "license": "MIT",
+ "peer": true,
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
"node_modules/@babel/plugin-syntax-async-generators": {
"version": "7.8.4",
"resolved": "https://registry.npmjs.org/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz",
@@ -721,6 +818,38 @@
"@babel/core": "^7.0.0-0"
}
},
+ "node_modules/@babel/plugin-syntax-import-assertions": {
+ "version": "7.28.6",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-assertions/-/plugin-syntax-import-assertions-7.28.6.tgz",
+ "integrity": "sha512-pSJUpFHdx9z5nqTSirOCMtYVP2wFgoWhP0p3g8ONK/4IHhLIBd0B9NYqAvIUAhq+OkhO4VM1tENCt0cjlsNShw==",
+ "license": "MIT",
+ "peer": true,
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.28.6"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-syntax-import-attributes": {
+ "version": "7.28.6",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-attributes/-/plugin-syntax-import-attributes-7.28.6.tgz",
+ "integrity": "sha512-jiLC0ma9XkQT3TKJ9uYvlakm66Pamywo+qwL+oL8HJOvc6TWdZXVfhqJr8CCzbSGUAbDOzlGHJC1U+vRfLQDvw==",
+ "license": "MIT",
+ "peer": true,
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.28.6"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
"node_modules/@babel/plugin-syntax-jsx": {
"version": "7.28.6",
"resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.28.6.tgz",
@@ -823,6 +952,23 @@
"@babel/core": "^7.0.0-0"
}
},
+ "node_modules/@babel/plugin-syntax-unicode-sets-regex": {
+ "version": "7.18.6",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-unicode-sets-regex/-/plugin-syntax-unicode-sets-regex-7.18.6.tgz",
+ "integrity": "sha512-727YkEAPwSIQTv5im8QHz3upqp92JTWhidIC81Tdx4VJYIte/VndKf1qKrfnnhPLiPghStWfvC/iFaMCQu7Nqg==",
+ "license": "MIT",
+ "peer": true,
+ "dependencies": {
+ "@babel/helper-create-regexp-features-plugin": "^7.18.6",
+ "@babel/helper-plugin-utils": "^7.18.6"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0"
+ }
+ },
"node_modules/@babel/plugin-transform-arrow-functions": {
"version": "7.27.1",
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.27.1.tgz",
@@ -838,6 +984,24 @@
"@babel/core": "^7.0.0-0"
}
},
+ "node_modules/@babel/plugin-transform-async-generator-functions": {
+ "version": "7.29.0",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-generator-functions/-/plugin-transform-async-generator-functions-7.29.0.tgz",
+ "integrity": "sha512-va0VdWro4zlBr2JsXC+ofCPB2iG12wPtVGTWFx2WLDOM3nYQZZIGP82qku2eW/JR83sD+k2k+CsNtyEbUqhU6w==",
+ "license": "MIT",
+ "peer": true,
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.28.6",
+ "@babel/helper-remap-async-to-generator": "^7.27.1",
+ "@babel/traverse": "^7.29.0"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
"node_modules/@babel/plugin-transform-async-to-generator": {
"version": "7.28.6",
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.28.6.tgz",
@@ -855,6 +1019,22 @@
"@babel/core": "^7.0.0-0"
}
},
+ "node_modules/@babel/plugin-transform-block-scoped-functions": {
+ "version": "7.27.1",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.27.1.tgz",
+ "integrity": "sha512-cnqkuOtZLapWYZUYM5rVIdv1nXYuFVIltZ6ZJ7nIj585QsjKM5dhL2Fu/lICXZ1OyIAFc7Qy+bvDAtTXqGrlhg==",
+ "license": "MIT",
+ "peer": true,
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.27.1"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
"node_modules/@babel/plugin-transform-block-scoping": {
"version": "7.28.6",
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.28.6.tgz",
@@ -870,6 +1050,40 @@
"@babel/core": "^7.0.0-0"
}
},
+ "node_modules/@babel/plugin-transform-class-properties": {
+ "version": "7.28.6",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-class-properties/-/plugin-transform-class-properties-7.28.6.tgz",
+ "integrity": "sha512-dY2wS3I2G7D697VHndN91TJr8/AAfXQNt5ynCTI/MpxMsSzHp+52uNivYT5wCPax3whc47DR8Ba7cmlQMg24bw==",
+ "license": "MIT",
+ "peer": true,
+ "dependencies": {
+ "@babel/helper-create-class-features-plugin": "^7.28.6",
+ "@babel/helper-plugin-utils": "^7.28.6"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-transform-class-static-block": {
+ "version": "7.28.6",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-class-static-block/-/plugin-transform-class-static-block-7.28.6.tgz",
+ "integrity": "sha512-rfQ++ghVwTWTqQ7w8qyDxL1XGihjBss4CmTgGRCTAC9RIbhVpyp4fOeZtta0Lbf+dTNIVJer6ych2ibHwkZqsQ==",
+ "license": "MIT",
+ "peer": true,
+ "dependencies": {
+ "@babel/helper-create-class-features-plugin": "^7.28.6",
+ "@babel/helper-plugin-utils": "^7.28.6"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.12.0"
+ }
+ },
"node_modules/@babel/plugin-transform-classes": {
"version": "7.28.6",
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.28.6.tgz",
@@ -922,6 +1136,105 @@
"@babel/core": "^7.0.0-0"
}
},
+ "node_modules/@babel/plugin-transform-dotall-regex": {
+ "version": "7.28.6",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.28.6.tgz",
+ "integrity": "sha512-SljjowuNKB7q5Oayv4FoPzeB74g3QgLt8IVJw9ADvWy3QnUb/01aw8I4AVv8wYnPvQz2GDDZ/g3GhcNyDBI4Bg==",
+ "license": "MIT",
+ "peer": true,
+ "dependencies": {
+ "@babel/helper-create-regexp-features-plugin": "^7.28.5",
+ "@babel/helper-plugin-utils": "^7.28.6"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-transform-duplicate-keys": {
+ "version": "7.27.1",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.27.1.tgz",
+ "integrity": "sha512-MTyJk98sHvSs+cvZ4nOauwTTG1JeonDjSGvGGUNHreGQns+Mpt6WX/dVzWBHgg+dYZhkC4X+zTDfkTU+Vy9y7Q==",
+ "license": "MIT",
+ "peer": true,
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.27.1"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-transform-duplicate-named-capturing-groups-regex": {
+ "version": "7.29.0",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-duplicate-named-capturing-groups-regex/-/plugin-transform-duplicate-named-capturing-groups-regex-7.29.0.tgz",
+ "integrity": "sha512-zBPcW2lFGxdiD8PUnPwJjag2J9otbcLQzvbiOzDxpYXyCuYX9agOwMPGn1prVH0a4qzhCKu24rlH4c1f7yA8rw==",
+ "license": "MIT",
+ "peer": true,
+ "dependencies": {
+ "@babel/helper-create-regexp-features-plugin": "^7.28.5",
+ "@babel/helper-plugin-utils": "^7.28.6"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0"
+ }
+ },
+ "node_modules/@babel/plugin-transform-dynamic-import": {
+ "version": "7.27.1",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dynamic-import/-/plugin-transform-dynamic-import-7.27.1.tgz",
+ "integrity": "sha512-MHzkWQcEmjzzVW9j2q8LGjwGWpG2mjwaaB0BNQwst3FIjqsg8Ct/mIZlvSPJvfi9y2AC8mi/ktxbFVL9pZ1I4A==",
+ "license": "MIT",
+ "peer": true,
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.27.1"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-transform-explicit-resource-management": {
+ "version": "7.28.6",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-explicit-resource-management/-/plugin-transform-explicit-resource-management-7.28.6.tgz",
+ "integrity": "sha512-Iao5Konzx2b6g7EPqTy40UZbcdXE126tTxVFr/nAIj+WItNxjKSYTEw3RC+A2/ZetmdJsgueL1KhaMCQHkLPIg==",
+ "license": "MIT",
+ "peer": true,
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.28.6",
+ "@babel/plugin-transform-destructuring": "^7.28.5"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-transform-exponentiation-operator": {
+ "version": "7.28.6",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.28.6.tgz",
+ "integrity": "sha512-WitabqiGjV/vJ0aPOLSFfNY1u9U3R7W36B03r5I2KoNix+a3sOhJ3pKFB3R5It9/UiK78NiO0KE9P21cMhlPkw==",
+ "license": "MIT",
+ "peer": true,
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.28.6"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
"node_modules/@babel/plugin-transform-export-namespace-from": {
"version": "7.27.1",
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-export-namespace-from/-/plugin-transform-export-namespace-from-7.27.1.tgz",
@@ -953,6 +1266,23 @@
"@babel/core": "^7.0.0-0"
}
},
+ "node_modules/@babel/plugin-transform-for-of": {
+ "version": "7.27.1",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.27.1.tgz",
+ "integrity": "sha512-BfbWFFEJFQzLCQ5N8VocnCtA8J1CLkNTe2Ms2wocj75dd6VpiqS5Z5quTYcUoo4Yq+DN0rtikODccuv7RU81sw==",
+ "license": "MIT",
+ "peer": true,
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.27.1",
+ "@babel/helper-skip-transparent-expression-wrappers": "^7.27.1"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
"node_modules/@babel/plugin-transform-function-name": {
"version": "7.27.1",
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.27.1.tgz",
@@ -970,6 +1300,22 @@
"@babel/core": "^7.0.0-0"
}
},
+ "node_modules/@babel/plugin-transform-json-strings": {
+ "version": "7.28.6",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-json-strings/-/plugin-transform-json-strings-7.28.6.tgz",
+ "integrity": "sha512-Nr+hEN+0geQkzhbdgQVPoqr47lZbm+5fCUmO70722xJZd0Mvb59+33QLImGj6F+DkK3xgDi1YVysP8whD6FQAw==",
+ "license": "MIT",
+ "peer": true,
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.28.6"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
"node_modules/@babel/plugin-transform-literals": {
"version": "7.27.1",
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-literals/-/plugin-transform-literals-7.27.1.tgz",
@@ -985,6 +1331,55 @@
"@babel/core": "^7.0.0-0"
}
},
+ "node_modules/@babel/plugin-transform-logical-assignment-operators": {
+ "version": "7.28.6",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-logical-assignment-operators/-/plugin-transform-logical-assignment-operators-7.28.6.tgz",
+ "integrity": "sha512-+anKKair6gpi8VsM/95kmomGNMD0eLz1NQ8+Pfw5sAwWH9fGYXT50E55ZpV0pHUHWf6IUTWPM+f/7AAff+wr9A==",
+ "license": "MIT",
+ "peer": true,
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.28.6"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-transform-member-expression-literals": {
+ "version": "7.27.1",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.27.1.tgz",
+ "integrity": "sha512-hqoBX4dcZ1I33jCSWcXrP+1Ku7kdqXf1oeah7ooKOIiAdKQ+uqftgCFNOSzA5AMS2XIHEYeGFg4cKRCdpxzVOQ==",
+ "license": "MIT",
+ "peer": true,
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.27.1"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-transform-modules-amd": {
+ "version": "7.27.1",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.27.1.tgz",
+ "integrity": "sha512-iCsytMg/N9/oFq6n+gFTvUYDZQOMK5kEdeYxmxt91fcJGycfxVP9CnrxoliM0oumFERba2i8ZtwRUCMhvP1LnA==",
+ "license": "MIT",
+ "peer": true,
+ "dependencies": {
+ "@babel/helper-module-transforms": "^7.27.1",
+ "@babel/helper-plugin-utils": "^7.27.1"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
"node_modules/@babel/plugin-transform-modules-commonjs": {
"version": "7.28.6",
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.28.6.tgz",
@@ -1001,6 +1396,42 @@
"@babel/core": "^7.0.0-0"
}
},
+ "node_modules/@babel/plugin-transform-modules-systemjs": {
+ "version": "7.29.0",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.29.0.tgz",
+ "integrity": "sha512-PrujnVFbOdUpw4UHiVwKvKRLMMic8+eC0CuNlxjsyZUiBjhFdPsewdXCkveh2KqBA9/waD0W1b4hXSOBQJezpQ==",
+ "license": "MIT",
+ "peer": true,
+ "dependencies": {
+ "@babel/helper-module-transforms": "^7.28.6",
+ "@babel/helper-plugin-utils": "^7.28.6",
+ "@babel/helper-validator-identifier": "^7.28.5",
+ "@babel/traverse": "^7.29.0"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-transform-modules-umd": {
+ "version": "7.27.1",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.27.1.tgz",
+ "integrity": "sha512-iQBE/xC5BV1OxJbp6WG7jq9IWiD+xxlZhLrdwpPkTX3ydmXdvoCpyfJN7acaIBZaOqTfr76pgzqBJflNbeRK+w==",
+ "license": "MIT",
+ "peer": true,
+ "dependencies": {
+ "@babel/helper-module-transforms": "^7.27.1",
+ "@babel/helper-plugin-utils": "^7.27.1"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
"node_modules/@babel/plugin-transform-named-capturing-groups-regex": {
"version": "7.29.0",
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.29.0.tgz",
@@ -1017,6 +1448,22 @@
"@babel/core": "^7.0.0"
}
},
+ "node_modules/@babel/plugin-transform-new-target": {
+ "version": "7.27.1",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.27.1.tgz",
+ "integrity": "sha512-f6PiYeqXQ05lYq3TIfIDu/MtliKUbNwkGApPUvyo6+tc7uaR4cPjPe7DFPr15Uyycg2lZU6btZ575CuQoYh7MQ==",
+ "license": "MIT",
+ "peer": true,
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.27.1"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
"node_modules/@babel/plugin-transform-nullish-coalescing-operator": {
"version": "7.28.6",
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-nullish-coalescing-operator/-/plugin-transform-nullish-coalescing-operator-7.28.6.tgz",
@@ -1032,6 +1479,22 @@
"@babel/core": "^7.0.0-0"
}
},
+ "node_modules/@babel/plugin-transform-numeric-separator": {
+ "version": "7.28.6",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-numeric-separator/-/plugin-transform-numeric-separator-7.28.6.tgz",
+ "integrity": "sha512-SJR8hPynj8outz+SlStQSwvziMN4+Bq99it4tMIf5/Caq+3iOc0JtKyse8puvyXkk3eFRIA5ID/XfunGgO5i6w==",
+ "license": "MIT",
+ "peer": true,
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.28.6"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
"node_modules/@babel/plugin-transform-object-rest-spread": {
"version": "7.28.6",
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-rest-spread/-/plugin-transform-object-rest-spread-7.28.6.tgz",
@@ -1051,6 +1514,39 @@
"@babel/core": "^7.0.0-0"
}
},
+ "node_modules/@babel/plugin-transform-object-super": {
+ "version": "7.27.1",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.27.1.tgz",
+ "integrity": "sha512-SFy8S9plRPbIcxlJ8A6mT/CxFdJx/c04JEctz4jf8YZaVS2px34j7NXRrlGlHkN/M2gnpL37ZpGRGVFLd3l8Ng==",
+ "license": "MIT",
+ "peer": true,
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.27.1",
+ "@babel/helper-replace-supers": "^7.27.1"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-transform-optional-catch-binding": {
+ "version": "7.28.6",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-optional-catch-binding/-/plugin-transform-optional-catch-binding-7.28.6.tgz",
+ "integrity": "sha512-R8ja/Pyrv0OGAvAXQhSTmWyPJPml+0TMqXlO5w+AsMEiwb2fg3WkOvob7UxFSL3OIttFSGSRFKQsOhJ/X6HQdQ==",
+ "license": "MIT",
+ "peer": true,
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.28.6"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
"node_modules/@babel/plugin-transform-optional-chaining": {
"version": "7.28.6",
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-optional-chaining/-/plugin-transform-optional-chaining-7.28.6.tgz",
@@ -1115,6 +1611,22 @@
"@babel/core": "^7.0.0-0"
}
},
+ "node_modules/@babel/plugin-transform-property-literals": {
+ "version": "7.27.1",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.27.1.tgz",
+ "integrity": "sha512-oThy3BCuCha8kDZ8ZkgOg2exvPYUlprMukKQXI1r1pJ47NCvxfkEy8vK+r/hT9nF0Aa4H1WUPZZjHTFtAhGfmQ==",
+ "license": "MIT",
+ "peer": true,
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.27.1"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
"node_modules/@babel/plugin-transform-react-display-name": {
"version": "7.28.0",
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-display-name/-/plugin-transform-react-display-name-7.28.0.tgz",
@@ -1210,6 +1722,55 @@
"@babel/core": "^7.0.0-0"
}
},
+ "node_modules/@babel/plugin-transform-regenerator": {
+ "version": "7.29.0",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.29.0.tgz",
+ "integrity": "sha512-FijqlqMA7DmRdg/aINBSs04y8XNTYw/lr1gJ2WsmBnnaNw1iS43EPkJW+zK7z65auG3AWRFXWj+NcTQwYptUog==",
+ "license": "MIT",
+ "peer": true,
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.28.6"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-transform-regexp-modifiers": {
+ "version": "7.28.6",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regexp-modifiers/-/plugin-transform-regexp-modifiers-7.28.6.tgz",
+ "integrity": "sha512-QGWAepm9qxpaIs7UM9FvUSnCGlb8Ua1RhyM4/veAxLwt3gMat/LSGrZixyuj4I6+Kn9iwvqCyPTtbdxanYoWYg==",
+ "license": "MIT",
+ "peer": true,
+ "dependencies": {
+ "@babel/helper-create-regexp-features-plugin": "^7.28.5",
+ "@babel/helper-plugin-utils": "^7.28.6"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0"
+ }
+ },
+ "node_modules/@babel/plugin-transform-reserved-words": {
+ "version": "7.27.1",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.27.1.tgz",
+ "integrity": "sha512-V2ABPHIJX4kC7HegLkYoDpfg9PVmuWy/i6vUM5eGK22bx4YVFD3M5F0QQnWQoDs6AGsUWTVOopBiMFQgHaSkVw==",
+ "license": "MIT",
+ "peer": true,
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.27.1"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
"node_modules/@babel/plugin-transform-runtime": {
"version": "7.29.0",
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.29.0.tgz",
@@ -1291,6 +1852,22 @@
"@babel/core": "^7.0.0-0"
}
},
+ "node_modules/@babel/plugin-transform-typeof-symbol": {
+ "version": "7.27.1",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.27.1.tgz",
+ "integrity": "sha512-RiSILC+nRJM7FY5srIyc4/fGIwUhyDuuBSdWn4y6yT6gm652DpCHZjIipgn6B7MQ1ITOUnAKWixEUjQRIBIcLw==",
+ "license": "MIT",
+ "peer": true,
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.27.1"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
"node_modules/@babel/plugin-transform-typescript": {
"version": "7.28.6",
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.28.6.tgz",
@@ -1310,6 +1887,39 @@
"@babel/core": "^7.0.0-0"
}
},
+ "node_modules/@babel/plugin-transform-unicode-escapes": {
+ "version": "7.27.1",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.27.1.tgz",
+ "integrity": "sha512-Ysg4v6AmF26k9vpfFuTZg8HRfVWzsh1kVfowA23y9j/Gu6dOuahdUVhkLqpObp3JIv27MLSii6noRnuKN8H0Mg==",
+ "license": "MIT",
+ "peer": true,
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.27.1"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-transform-unicode-property-regex": {
+ "version": "7.28.6",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-property-regex/-/plugin-transform-unicode-property-regex-7.28.6.tgz",
+ "integrity": "sha512-4Wlbdl/sIZjzi/8St0evF0gEZrgOswVO6aOzqxh1kDZOl9WmLrHq2HtGhnOJZmHZYKP8WZ1MDLCt5DAWwRo57A==",
+ "license": "MIT",
+ "peer": true,
+ "dependencies": {
+ "@babel/helper-create-regexp-features-plugin": "^7.28.5",
+ "@babel/helper-plugin-utils": "^7.28.6"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
"node_modules/@babel/plugin-transform-unicode-regex": {
"version": "7.27.1",
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.27.1.tgz",
@@ -1326,6 +1936,122 @@
"@babel/core": "^7.0.0-0"
}
},
+ "node_modules/@babel/plugin-transform-unicode-sets-regex": {
+ "version": "7.28.6",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-sets-regex/-/plugin-transform-unicode-sets-regex-7.28.6.tgz",
+ "integrity": "sha512-/wHc/paTUmsDYN7SZkpWxogTOBNnlx7nBQYfy6JJlCT7G3mVhltk3e++N7zV0XfgGsrqBxd4rJQt9H16I21Y1Q==",
+ "license": "MIT",
+ "peer": true,
+ "dependencies": {
+ "@babel/helper-create-regexp-features-plugin": "^7.28.5",
+ "@babel/helper-plugin-utils": "^7.28.6"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0"
+ }
+ },
+ "node_modules/@babel/preset-env": {
+ "version": "7.29.2",
+ "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.29.2.tgz",
+ "integrity": "sha512-DYD23veRYGvBFhcTY1iUvJnDNpuqNd/BzBwCvzOTKUnJjKg5kpUBh3/u9585Agdkgj+QuygG7jLfOPWMa2KVNw==",
+ "license": "MIT",
+ "peer": true,
+ "dependencies": {
+ "@babel/compat-data": "^7.29.0",
+ "@babel/helper-compilation-targets": "^7.28.6",
+ "@babel/helper-plugin-utils": "^7.28.6",
+ "@babel/helper-validator-option": "^7.27.1",
+ "@babel/plugin-bugfix-firefox-class-in-computed-class-key": "^7.28.5",
+ "@babel/plugin-bugfix-safari-class-field-initializer-scope": "^7.27.1",
+ "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": "^7.27.1",
+ "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": "^7.27.1",
+ "@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly": "^7.28.6",
+ "@babel/plugin-proposal-private-property-in-object": "7.21.0-placeholder-for-preset-env.2",
+ "@babel/plugin-syntax-import-assertions": "^7.28.6",
+ "@babel/plugin-syntax-import-attributes": "^7.28.6",
+ "@babel/plugin-syntax-unicode-sets-regex": "^7.18.6",
+ "@babel/plugin-transform-arrow-functions": "^7.27.1",
+ "@babel/plugin-transform-async-generator-functions": "^7.29.0",
+ "@babel/plugin-transform-async-to-generator": "^7.28.6",
+ "@babel/plugin-transform-block-scoped-functions": "^7.27.1",
+ "@babel/plugin-transform-block-scoping": "^7.28.6",
+ "@babel/plugin-transform-class-properties": "^7.28.6",
+ "@babel/plugin-transform-class-static-block": "^7.28.6",
+ "@babel/plugin-transform-classes": "^7.28.6",
+ "@babel/plugin-transform-computed-properties": "^7.28.6",
+ "@babel/plugin-transform-destructuring": "^7.28.5",
+ "@babel/plugin-transform-dotall-regex": "^7.28.6",
+ "@babel/plugin-transform-duplicate-keys": "^7.27.1",
+ "@babel/plugin-transform-duplicate-named-capturing-groups-regex": "^7.29.0",
+ "@babel/plugin-transform-dynamic-import": "^7.27.1",
+ "@babel/plugin-transform-explicit-resource-management": "^7.28.6",
+ "@babel/plugin-transform-exponentiation-operator": "^7.28.6",
+ "@babel/plugin-transform-export-namespace-from": "^7.27.1",
+ "@babel/plugin-transform-for-of": "^7.27.1",
+ "@babel/plugin-transform-function-name": "^7.27.1",
+ "@babel/plugin-transform-json-strings": "^7.28.6",
+ "@babel/plugin-transform-literals": "^7.27.1",
+ "@babel/plugin-transform-logical-assignment-operators": "^7.28.6",
+ "@babel/plugin-transform-member-expression-literals": "^7.27.1",
+ "@babel/plugin-transform-modules-amd": "^7.27.1",
+ "@babel/plugin-transform-modules-commonjs": "^7.28.6",
+ "@babel/plugin-transform-modules-systemjs": "^7.29.0",
+ "@babel/plugin-transform-modules-umd": "^7.27.1",
+ "@babel/plugin-transform-named-capturing-groups-regex": "^7.29.0",
+ "@babel/plugin-transform-new-target": "^7.27.1",
+ "@babel/plugin-transform-nullish-coalescing-operator": "^7.28.6",
+ "@babel/plugin-transform-numeric-separator": "^7.28.6",
+ "@babel/plugin-transform-object-rest-spread": "^7.28.6",
+ "@babel/plugin-transform-object-super": "^7.27.1",
+ "@babel/plugin-transform-optional-catch-binding": "^7.28.6",
+ "@babel/plugin-transform-optional-chaining": "^7.28.6",
+ "@babel/plugin-transform-parameters": "^7.27.7",
+ "@babel/plugin-transform-private-methods": "^7.28.6",
+ "@babel/plugin-transform-private-property-in-object": "^7.28.6",
+ "@babel/plugin-transform-property-literals": "^7.27.1",
+ "@babel/plugin-transform-regenerator": "^7.29.0",
+ "@babel/plugin-transform-regexp-modifiers": "^7.28.6",
+ "@babel/plugin-transform-reserved-words": "^7.27.1",
+ "@babel/plugin-transform-shorthand-properties": "^7.27.1",
+ "@babel/plugin-transform-spread": "^7.28.6",
+ "@babel/plugin-transform-sticky-regex": "^7.27.1",
+ "@babel/plugin-transform-template-literals": "^7.27.1",
+ "@babel/plugin-transform-typeof-symbol": "^7.27.1",
+ "@babel/plugin-transform-unicode-escapes": "^7.27.1",
+ "@babel/plugin-transform-unicode-property-regex": "^7.28.6",
+ "@babel/plugin-transform-unicode-regex": "^7.27.1",
+ "@babel/plugin-transform-unicode-sets-regex": "^7.28.6",
+ "@babel/preset-modules": "0.1.6-no-external-plugins",
+ "babel-plugin-polyfill-corejs2": "^0.4.15",
+ "babel-plugin-polyfill-corejs3": "^0.14.0",
+ "babel-plugin-polyfill-regenerator": "^0.6.6",
+ "core-js-compat": "^3.48.0",
+ "semver": "^6.3.1"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/preset-env/node_modules/babel-plugin-polyfill-corejs3": {
+ "version": "0.14.2",
+ "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.14.2.tgz",
+ "integrity": "sha512-coWpDLJ410R781Npmn/SIBZEsAetR4xVi0SxLMXPaMO4lSf1MwnkGYMtkFxew0Dn8B3/CpbpYxN0JCgg8mn67g==",
+ "license": "MIT",
+ "peer": true,
+ "dependencies": {
+ "@babel/helper-define-polyfill-provider": "^0.6.8",
+ "core-js-compat": "^3.48.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0"
+ }
+ },
"node_modules/@babel/preset-flow": {
"version": "7.27.1",
"resolved": "https://registry.npmjs.org/@babel/preset-flow/-/preset-flow-7.27.1.tgz",
@@ -1343,6 +2069,21 @@
"@babel/core": "^7.0.0-0"
}
},
+ "node_modules/@babel/preset-modules": {
+ "version": "0.1.6-no-external-plugins",
+ "resolved": "https://registry.npmjs.org/@babel/preset-modules/-/preset-modules-0.1.6-no-external-plugins.tgz",
+ "integrity": "sha512-HrcgcIESLm9aIR842yhJ5RWan/gebQUJ6E/E5+rf0y9o6oj7w0Br+sWuL6kEQ/o/AdfvR1Je9jG18/gnpwjEyA==",
+ "license": "MIT",
+ "peer": true,
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.0.0",
+ "@babel/types": "^7.4.4",
+ "esutils": "^2.0.2"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0 || ^8.0.0-0 <8.0.0"
+ }
+ },
"node_modules/@babel/preset-react": {
"version": "7.28.5",
"resolved": "https://registry.npmjs.org/@babel/preset-react/-/preset-react-7.28.5.tgz",
@@ -3837,9 +4578,9 @@
}
},
"node_modules/@react-native/assets-registry": {
- "version": "0.74.85",
- "resolved": "https://registry.npmjs.org/@react-native/assets-registry/-/assets-registry-0.74.85.tgz",
- "integrity": "sha512-59YmIQxfGDw4aP9S/nAM+sjSFdW8fUP6fsqczCcXgL2YVEjyER9XCaUT0J1K+PdHep8pi05KUgIKUds8P3jbmA==",
+ "version": "0.74.87",
+ "resolved": "https://registry.npmjs.org/@react-native/assets-registry/-/assets-registry-0.74.87.tgz",
+ "integrity": "sha512-1XmRhqQchN+pXPKEKYdpJlwESxVomJOxtEnIkbo7GAlaN2sym84fHEGDXAjLilih5GVPpcpSmFzTy8jx3LtaFg==",
"license": "MIT",
"engines": {
"node": ">=18"
@@ -3969,15 +4710,15 @@
}
},
"node_modules/@react-native/community-cli-plugin": {
- "version": "0.74.85",
- "resolved": "https://registry.npmjs.org/@react-native/community-cli-plugin/-/community-cli-plugin-0.74.85.tgz",
- "integrity": "sha512-ODzND33eA2owAY3g9jgCdqB+BjAh8qJ7dvmSotXgrgDYr3MJMpd8gvHTIPe2fg4Kab+wk8uipRhrE0i0RYMwtQ==",
+ "version": "0.74.87",
+ "resolved": "https://registry.npmjs.org/@react-native/community-cli-plugin/-/community-cli-plugin-0.74.87.tgz",
+ "integrity": "sha512-EgJG9lSr8x3X67dHQKQvU6EkO+3ksVlJHYIVv6U/AmW9dN80BEFxgYbSJ7icXS4wri7m4kHdgeq2PQ7/3vvrTQ==",
"license": "MIT",
"dependencies": {
"@react-native-community/cli-server-api": "13.6.9",
"@react-native-community/cli-tools": "13.6.9",
- "@react-native/dev-middleware": "0.74.85",
- "@react-native/metro-babel-transformer": "0.74.85",
+ "@react-native/dev-middleware": "0.74.87",
+ "@react-native/metro-babel-transformer": "0.74.87",
"chalk": "^4.0.0",
"execa": "^5.1.1",
"metro": "^0.80.3",
@@ -3991,6 +4732,48 @@
"node": ">=18"
}
},
+ "node_modules/@react-native/community-cli-plugin/node_modules/@react-native/debugger-frontend": {
+ "version": "0.74.87",
+ "resolved": "https://registry.npmjs.org/@react-native/debugger-frontend/-/debugger-frontend-0.74.87.tgz",
+ "integrity": "sha512-MN95DJLYTv4EqJc+9JajA3AJZSBYJz2QEJ3uWlHrOky2vKrbbRVaW1ityTmaZa2OXIvNc6CZwSRSE7xCoHbXhQ==",
+ "license": "BSD-3-Clause",
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@react-native/community-cli-plugin/node_modules/@react-native/dev-middleware": {
+ "version": "0.74.87",
+ "resolved": "https://registry.npmjs.org/@react-native/dev-middleware/-/dev-middleware-0.74.87.tgz",
+ "integrity": "sha512-7TmZ3hTHwooYgIHqc/z87BMe1ryrIqAUi+AF7vsD+EHCGxHFdMjSpf1BZ2SUPXuLnF2cTiTfV2RwhbPzx0tYIA==",
+ "license": "MIT",
+ "dependencies": {
+ "@isaacs/ttlcache": "^1.4.1",
+ "@react-native/debugger-frontend": "0.74.87",
+ "@rnx-kit/chromium-edge-launcher": "^1.0.0",
+ "chrome-launcher": "^0.15.2",
+ "connect": "^3.6.5",
+ "debug": "^2.2.0",
+ "node-fetch": "^2.2.0",
+ "nullthrows": "^1.1.1",
+ "open": "^7.0.3",
+ "selfsigned": "^2.4.1",
+ "serve-static": "^1.13.1",
+ "temp-dir": "^2.0.0",
+ "ws": "^6.2.2"
+ },
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@react-native/community-cli-plugin/node_modules/debug": {
+ "version": "2.6.9",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
+ "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
+ "license": "MIT",
+ "dependencies": {
+ "ms": "2.0.0"
+ }
+ },
"node_modules/@react-native/community-cli-plugin/node_modules/execa": {
"version": "5.1.1",
"resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz",
@@ -4047,6 +4830,12 @@
"node": ">=6"
}
},
+ "node_modules/@react-native/community-cli-plugin/node_modules/ms": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
+ "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==",
+ "license": "MIT"
+ },
"node_modules/@react-native/community-cli-plugin/node_modules/npm-run-path": {
"version": "4.0.1",
"resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz",
@@ -4074,6 +4863,31 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
+ "node_modules/@react-native/community-cli-plugin/node_modules/open": {
+ "version": "7.4.2",
+ "resolved": "https://registry.npmjs.org/open/-/open-7.4.2.tgz",
+ "integrity": "sha512-MVHddDVweXZF3awtlAS+6pgKLlm/JgxZ90+/NBurBoQctVOOB/zDdVjcyPzQ+0laDGbsWgrRkflI65sQeOgT9Q==",
+ "license": "MIT",
+ "dependencies": {
+ "is-docker": "^2.0.0",
+ "is-wsl": "^2.1.1"
+ },
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/@react-native/community-cli-plugin/node_modules/ws": {
+ "version": "6.2.3",
+ "resolved": "https://registry.npmjs.org/ws/-/ws-6.2.3.tgz",
+ "integrity": "sha512-jmTjYU0j60B+vHey6TfR3Z7RD61z/hmxBS3VMSGIrroOWXQEneK1zNuotOUrGyBHQj0yrpsLHPWtigEFd13ndA==",
+ "license": "MIT",
+ "dependencies": {
+ "async-limiter": "~1.0.0"
+ }
+ },
"node_modules/@react-native/debugger-frontend": {
"version": "0.74.85",
"resolved": "https://registry.npmjs.org/@react-native/debugger-frontend/-/debugger-frontend-0.74.85.tgz",
@@ -4148,31 +4962,31 @@
}
},
"node_modules/@react-native/gradle-plugin": {
- "version": "0.74.85",
- "resolved": "https://registry.npmjs.org/@react-native/gradle-plugin/-/gradle-plugin-0.74.85.tgz",
- "integrity": "sha512-1VQSLukJzaVMn1MYcs8Weo1nUW8xCas2XU1KuoV7OJPk6xPnEBFJmapmEGP5mWeEy7kcTXJmddEgy1wwW0tcig==",
+ "version": "0.74.87",
+ "resolved": "https://registry.npmjs.org/@react-native/gradle-plugin/-/gradle-plugin-0.74.87.tgz",
+ "integrity": "sha512-T+VX0N1qP+U9V4oAtn7FTX7pfsoVkd1ocyw9swYXgJqU2fK7hC9famW7b3s3ZiufPGPr1VPJe2TVGtSopBjL6A==",
"license": "MIT",
"engines": {
"node": ">=18"
}
},
"node_modules/@react-native/js-polyfills": {
- "version": "0.74.85",
- "resolved": "https://registry.npmjs.org/@react-native/js-polyfills/-/js-polyfills-0.74.85.tgz",
- "integrity": "sha512-gp4Rg9le3lVZeW7Cie6qLfekvRKZuhJ3LKgi1SFB4N154z1wIclypAJXVXgWBsy8JKJfTwRI+sffC4qZDlvzrg==",
+ "version": "0.74.87",
+ "resolved": "https://registry.npmjs.org/@react-native/js-polyfills/-/js-polyfills-0.74.87.tgz",
+ "integrity": "sha512-M5Evdn76CuVEF0GsaXiGi95CBZ4IWubHqwXxV9vG9CC9kq0PSkoM2Pn7Lx7dgyp4vT7ccJ8a3IwHbe+5KJRnpw==",
"license": "MIT",
"engines": {
"node": ">=18"
}
},
"node_modules/@react-native/metro-babel-transformer": {
- "version": "0.74.85",
- "resolved": "https://registry.npmjs.org/@react-native/metro-babel-transformer/-/metro-babel-transformer-0.74.85.tgz",
- "integrity": "sha512-JIrXqEwhTvWPtGArgMptIPGstMdXQIkwSjKVYt+7VC4a9Pw1GurIWanIJheEW6ZuCVvTc0VZkwglFz9JVjzDjA==",
+ "version": "0.74.87",
+ "resolved": "https://registry.npmjs.org/@react-native/metro-babel-transformer/-/metro-babel-transformer-0.74.87.tgz",
+ "integrity": "sha512-UsJCO24sNax2NSPBmV1zLEVVNkS88kcgAiYrZHtYSwSjpl4WZ656tIeedBfiySdJ94Hr3kQmBYLipV5zk0NI1A==",
"license": "MIT",
"dependencies": {
"@babel/core": "^7.20.0",
- "@react-native/babel-preset": "0.74.85",
+ "@react-native/babel-preset": "0.74.87",
"hermes-parser": "0.19.1",
"nullthrows": "^1.1.1"
},
@@ -4183,129 +4997,6 @@
"@babel/core": "*"
}
},
- "node_modules/@react-native/metro-babel-transformer/node_modules/@react-native/babel-plugin-codegen": {
- "version": "0.74.85",
- "resolved": "https://registry.npmjs.org/@react-native/babel-plugin-codegen/-/babel-plugin-codegen-0.74.85.tgz",
- "integrity": "sha512-48TSDclRB5OMXiImiJkLxyCfRyLsqkCgI8buugCZzvXcYslfV7gCvcyFyQldtcOmerV+CK4RAj7QS4hmB5Mr8Q==",
- "license": "MIT",
- "dependencies": {
- "@react-native/codegen": "0.74.85"
- },
- "engines": {
- "node": ">=18"
- }
- },
- "node_modules/@react-native/metro-babel-transformer/node_modules/@react-native/babel-preset": {
- "version": "0.74.85",
- "resolved": "https://registry.npmjs.org/@react-native/babel-preset/-/babel-preset-0.74.85.tgz",
- "integrity": "sha512-yMHUlN8INbK5BBwiBuQMftdWkpm1IgCsoJTKcGD2OpSgZhwwm8RUSvGhdRMzB2w7bsqqBmaEMleGtW6aCR7B9w==",
- "license": "MIT",
- "dependencies": {
- "@babel/core": "^7.20.0",
- "@babel/plugin-proposal-async-generator-functions": "^7.0.0",
- "@babel/plugin-proposal-class-properties": "^7.18.0",
- "@babel/plugin-proposal-export-default-from": "^7.0.0",
- "@babel/plugin-proposal-logical-assignment-operators": "^7.18.0",
- "@babel/plugin-proposal-nullish-coalescing-operator": "^7.18.0",
- "@babel/plugin-proposal-numeric-separator": "^7.0.0",
- "@babel/plugin-proposal-object-rest-spread": "^7.20.0",
- "@babel/plugin-proposal-optional-catch-binding": "^7.0.0",
- "@babel/plugin-proposal-optional-chaining": "^7.20.0",
- "@babel/plugin-syntax-dynamic-import": "^7.8.0",
- "@babel/plugin-syntax-export-default-from": "^7.0.0",
- "@babel/plugin-syntax-flow": "^7.18.0",
- "@babel/plugin-syntax-nullish-coalescing-operator": "^7.0.0",
- "@babel/plugin-syntax-optional-chaining": "^7.0.0",
- "@babel/plugin-transform-arrow-functions": "^7.0.0",
- "@babel/plugin-transform-async-to-generator": "^7.20.0",
- "@babel/plugin-transform-block-scoping": "^7.0.0",
- "@babel/plugin-transform-classes": "^7.0.0",
- "@babel/plugin-transform-computed-properties": "^7.0.0",
- "@babel/plugin-transform-destructuring": "^7.20.0",
- "@babel/plugin-transform-flow-strip-types": "^7.20.0",
- "@babel/plugin-transform-function-name": "^7.0.0",
- "@babel/plugin-transform-literals": "^7.0.0",
- "@babel/plugin-transform-modules-commonjs": "^7.0.0",
- "@babel/plugin-transform-named-capturing-groups-regex": "^7.0.0",
- "@babel/plugin-transform-parameters": "^7.0.0",
- "@babel/plugin-transform-private-methods": "^7.22.5",
- "@babel/plugin-transform-private-property-in-object": "^7.22.11",
- "@babel/plugin-transform-react-display-name": "^7.0.0",
- "@babel/plugin-transform-react-jsx": "^7.0.0",
- "@babel/plugin-transform-react-jsx-self": "^7.0.0",
- "@babel/plugin-transform-react-jsx-source": "^7.0.0",
- "@babel/plugin-transform-runtime": "^7.0.0",
- "@babel/plugin-transform-shorthand-properties": "^7.0.0",
- "@babel/plugin-transform-spread": "^7.0.0",
- "@babel/plugin-transform-sticky-regex": "^7.0.0",
- "@babel/plugin-transform-typescript": "^7.5.0",
- "@babel/plugin-transform-unicode-regex": "^7.0.0",
- "@babel/template": "^7.0.0",
- "@react-native/babel-plugin-codegen": "0.74.85",
- "babel-plugin-transform-flow-enums": "^0.0.2",
- "react-refresh": "^0.14.0"
- },
- "engines": {
- "node": ">=18"
- },
- "peerDependencies": {
- "@babel/core": "*"
- }
- },
- "node_modules/@react-native/metro-babel-transformer/node_modules/@react-native/codegen": {
- "version": "0.74.85",
- "resolved": "https://registry.npmjs.org/@react-native/codegen/-/codegen-0.74.85.tgz",
- "integrity": "sha512-N7QwoS4Hq/uQmoH83Ewedy6D0M7xbQsOU3OMcQf0eY3ltQ7S2hd9/R4UTalQWRn1OUJfXR6OG12QJ4FStKgV6Q==",
- "license": "MIT",
- "dependencies": {
- "@babel/parser": "^7.20.0",
- "glob": "^7.1.1",
- "hermes-parser": "0.19.1",
- "invariant": "^2.2.4",
- "jscodeshift": "^0.14.0",
- "mkdirp": "^0.5.1",
- "nullthrows": "^1.1.1"
- },
- "engines": {
- "node": ">=18"
- },
- "peerDependencies": {
- "@babel/preset-env": "^7.1.6"
- }
- },
- "node_modules/@react-native/metro-babel-transformer/node_modules/glob": {
- "version": "7.2.3",
- "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz",
- "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==",
- "deprecated": "Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me",
- "license": "ISC",
- "dependencies": {
- "fs.realpath": "^1.0.0",
- "inflight": "^1.0.4",
- "inherits": "2",
- "minimatch": "^3.1.1",
- "once": "^1.3.0",
- "path-is-absolute": "^1.0.0"
- },
- "engines": {
- "node": "*"
- },
- "funding": {
- "url": "https://github.com/sponsors/isaacs"
- }
- },
- "node_modules/@react-native/metro-babel-transformer/node_modules/mkdirp": {
- "version": "0.5.6",
- "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz",
- "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==",
- "license": "MIT",
- "dependencies": {
- "minimist": "^1.2.6"
- },
- "bin": {
- "mkdirp": "bin/cmd.js"
- }
- },
"node_modules/@react-native/normalize-colors": {
"version": "0.74.85",
"resolved": "https://registry.npmjs.org/@react-native/normalize-colors/-/normalize-colors-0.74.85.tgz",
@@ -4525,14 +5216,14 @@
"version": "15.7.15",
"resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.15.tgz",
"integrity": "sha512-F6bEyamV9jKGAFBEmlQnesRPGOQqS2+Uwi0Em15xenOxHaf2hv6L8YCVn3rPdPJOiJfPiCnLIRyvwVaqMY3MIw==",
- "dev": true,
+ "devOptional": true,
"license": "MIT"
},
"node_modules/@types/react": {
"version": "18.2.79",
"resolved": "https://registry.npmjs.org/@types/react/-/react-18.2.79.tgz",
"integrity": "sha512-RwGAGXPl9kSXwdNTafkOEuFrTBD5SA2B3iEB96xi8+xu5ddUa/cpvyVCSNn+asgLCTHkb5ZxN8gbuibYJi4s1w==",
- "dev": true,
+ "devOptional": true,
"license": "MIT",
"dependencies": {
"@types/prop-types": "*",
@@ -5968,7 +6659,7 @@
"version": "3.2.3",
"resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz",
"integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==",
- "dev": true,
+ "devOptional": true,
"license": "MIT"
},
"node_modules/dag-map": {
@@ -6620,6 +7311,16 @@
"node": ">=4"
}
},
+ "node_modules/esutils": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz",
+ "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==",
+ "license": "BSD-2-Clause",
+ "peer": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
"node_modules/etag": {
"version": "1.8.1",
"resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz",
@@ -6807,17 +7508,15 @@
}
},
"node_modules/expo-font": {
- "version": "55.0.6",
- "resolved": "https://registry.npmjs.org/expo-font/-/expo-font-55.0.6.tgz",
- "integrity": "sha512-x9czUA3UQWjIwa0ZUEs/eWJNqB4mAue/m4ltESlNPLZhHL0nWWqIfsyHmklTLFH7mVfcHSJvew6k+pR2FE1zVw==",
+ "version": "12.0.10",
+ "resolved": "https://registry.npmjs.org/expo-font/-/expo-font-12.0.10.tgz",
+ "integrity": "sha512-Q1i2NuYri3jy32zdnBaHHCya1wH1yMAsI+3CCmj9zlQzlhsS9Bdwcj2W3c5eU5FvH2hsNQy4O+O1NnM6o/pDaQ==",
"license": "MIT",
"dependencies": {
"fontfaceobserver": "^2.1.0"
},
"peerDependencies": {
- "expo": "*",
- "react": "*",
- "react-native": "*"
+ "expo": "*"
}
},
"node_modules/expo-image-loader": {
@@ -6830,9 +7529,9 @@
}
},
"node_modules/expo-image-picker": {
- "version": "15.0.7",
- "resolved": "https://registry.npmjs.org/expo-image-picker/-/expo-image-picker-15.0.7.tgz",
- "integrity": "sha512-u8qiPZNfDb+ap6PJ8pq2iTO7JKX+ikAUQ0K0c7gXGliKLxoXgDdDmXxz9/6QdICTshJBJlBvI0MwY5NWu7A/uw==",
+ "version": "15.1.0",
+ "resolved": "https://registry.npmjs.org/expo-image-picker/-/expo-image-picker-15.1.0.tgz",
+ "integrity": "sha512-6cE10S7d0qG7+pcHtsYukRKuFL44ssOM0/v+5JIBzRwxpIgGA6qokr8PxasFEpxOA9NpN0gwry33jmq1qLXJTg==",
"license": "MIT",
"dependencies": {
"expo-image-loader": "~4.7.0"
@@ -6983,18 +7682,6 @@
"integrity": "sha512-/t3xdbS8KB0prj5KG5w7z+wZPFlPtkgs95BsmrP/E7Q0xHXTcDcQ6Cu2FkFuRM+PKTb17cJDnLkawyS5vDLxMA==",
"license": "MIT"
},
- "node_modules/expo/node_modules/expo-font": {
- "version": "12.0.10",
- "resolved": "https://registry.npmjs.org/expo-font/-/expo-font-12.0.10.tgz",
- "integrity": "sha512-Q1i2NuYri3jy32zdnBaHHCya1wH1yMAsI+3CCmj9zlQzlhsS9Bdwcj2W3c5eU5FvH2hsNQy4O+O1NnM6o/pDaQ==",
- "license": "MIT",
- "dependencies": {
- "fontfaceobserver": "^2.1.0"
- },
- "peerDependencies": {
- "expo": "*"
- }
- },
"node_modules/exponential-backoff": {
"version": "3.1.3",
"resolved": "https://registry.npmjs.org/exponential-backoff/-/exponential-backoff-3.1.3.tgz",
@@ -11716,22 +12403,22 @@
"license": "MIT"
},
"node_modules/react-native": {
- "version": "0.74.3",
- "resolved": "https://registry.npmjs.org/react-native/-/react-native-0.74.3.tgz",
- "integrity": "sha512-UFutCC6WEw6HkxlcpQ2BemKqi0JkwrgDchYB5Svi8Sp4Xwt4HA6LGEjNQgZ+3KM44bjyFRpofQym0uh0jACGng==",
+ "version": "0.74.5",
+ "resolved": "https://registry.npmjs.org/react-native/-/react-native-0.74.5.tgz",
+ "integrity": "sha512-Bgg2WvxaGODukJMTZFTZBNMKVaROHLwSb8VAGEdrlvKwfb1hHg/3aXTUICYk7dwgAnb+INbGMwnF8yeAgIUmqw==",
"license": "MIT",
"dependencies": {
"@jest/create-cache-key-function": "^29.6.3",
"@react-native-community/cli": "13.6.9",
"@react-native-community/cli-platform-android": "13.6.9",
"@react-native-community/cli-platform-ios": "13.6.9",
- "@react-native/assets-registry": "0.74.85",
- "@react-native/codegen": "0.74.85",
- "@react-native/community-cli-plugin": "0.74.85",
- "@react-native/gradle-plugin": "0.74.85",
- "@react-native/js-polyfills": "0.74.85",
- "@react-native/normalize-colors": "0.74.85",
- "@react-native/virtualized-lists": "0.74.85",
+ "@react-native/assets-registry": "0.74.87",
+ "@react-native/codegen": "0.74.87",
+ "@react-native/community-cli-plugin": "0.74.87",
+ "@react-native/gradle-plugin": "0.74.87",
+ "@react-native/js-polyfills": "0.74.87",
+ "@react-native/normalize-colors": "0.74.87",
+ "@react-native/virtualized-lists": "0.74.87",
"abort-controller": "^3.0.0",
"anser": "^1.4.9",
"ansi-regex": "^5.0.0",
@@ -11909,31 +12596,16 @@
"node": ">= 10.14.2"
}
},
- "node_modules/react-native/node_modules/@react-native/codegen": {
- "version": "0.74.85",
- "resolved": "https://registry.npmjs.org/@react-native/codegen/-/codegen-0.74.85.tgz",
- "integrity": "sha512-N7QwoS4Hq/uQmoH83Ewedy6D0M7xbQsOU3OMcQf0eY3ltQ7S2hd9/R4UTalQWRn1OUJfXR6OG12QJ4FStKgV6Q==",
- "license": "MIT",
- "dependencies": {
- "@babel/parser": "^7.20.0",
- "glob": "^7.1.1",
- "hermes-parser": "0.19.1",
- "invariant": "^2.2.4",
- "jscodeshift": "^0.14.0",
- "mkdirp": "^0.5.1",
- "nullthrows": "^1.1.1"
- },
- "engines": {
- "node": ">=18"
- },
- "peerDependencies": {
- "@babel/preset-env": "^7.1.6"
- }
+ "node_modules/react-native/node_modules/@react-native/normalize-colors": {
+ "version": "0.74.87",
+ "resolved": "https://registry.npmjs.org/@react-native/normalize-colors/-/normalize-colors-0.74.87.tgz",
+ "integrity": "sha512-Xh7Nyk/MPefkb0Itl5Z+3oOobeG9lfLb7ZOY2DKpFnoCE1TzBmib9vMNdFaLdSxLIP+Ec6icgKtdzYg8QUPYzA==",
+ "license": "MIT"
},
"node_modules/react-native/node_modules/@react-native/virtualized-lists": {
- "version": "0.74.85",
- "resolved": "https://registry.npmjs.org/@react-native/virtualized-lists/-/virtualized-lists-0.74.85.tgz",
- "integrity": "sha512-jx2Zw0qlZteoQ+0KxRc7s4drsljLBEP534FaNZ950e9+CN9nVkLsV6rigcTjDR8wjKMSBWhKf0C0C3egYz7Ehg==",
+ "version": "0.74.87",
+ "resolved": "https://registry.npmjs.org/@react-native/virtualized-lists/-/virtualized-lists-0.74.87.tgz",
+ "integrity": "sha512-lsGxoFMb0lyK/MiplNKJpD+A1EoEUumkLrCjH4Ht+ZlG8S0BfCxmskLZ6qXn3BiDSkLjfjI/qyZ3pnxNBvkXpQ==",
"license": "MIT",
"dependencies": {
"invariant": "^2.2.4",
@@ -11980,27 +12652,6 @@
"node": ">=8"
}
},
- "node_modules/react-native/node_modules/glob": {
- "version": "7.2.3",
- "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz",
- "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==",
- "deprecated": "Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me",
- "license": "ISC",
- "dependencies": {
- "fs.realpath": "^1.0.0",
- "inflight": "^1.0.4",
- "inherits": "2",
- "minimatch": "^3.1.1",
- "once": "^1.3.0",
- "path-is-absolute": "^1.0.0"
- },
- "engines": {
- "node": "*"
- },
- "funding": {
- "url": "https://github.com/sponsors/isaacs"
- }
- },
"node_modules/react-native/node_modules/mkdirp": {
"version": "0.5.6",
"resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz",
diff --git a/package.json b/package.json
index bd52cc5..3a456e0 100644
--- a/package.json
+++ b/package.json
@@ -17,14 +17,14 @@
"@react-navigation/native-stack": "^6.9.26",
"expo": "~51.0.0",
"expo-device": "~6.0.2",
- "expo-font": "^55.0.6",
- "expo-image-picker": "~15.0.5",
+ "expo-font": "~12.0.10",
+ "expo-image-picker": "~15.1.0",
"expo-linear-gradient": "~13.0.2",
"expo-notifications": "~0.28.0",
"expo-status-bar": "~1.12.1",
"react": "18.2.0",
"react-dom": "^18.2.0",
- "react-native": "0.74.3",
+ "react-native": "0.74.5",
"react-native-chart-kit": "^6.12.0",
"react-native-gesture-handler": "~2.16.1",
"react-native-reanimated": "~3.10.1",
diff --git a/server/.dockerignore b/server/.dockerignore
new file mode 100644
index 0000000..b8e616f
--- /dev/null
+++ b/server/.dockerignore
@@ -0,0 +1,5 @@
+node_modules
+dist
+.env*
+*.log
+.git
diff --git a/server/.env.example b/server/.env.example
new file mode 100644
index 0000000..4cdb00b
--- /dev/null
+++ b/server/.env.example
@@ -0,0 +1,6 @@
+DATABASE_URL=postgresql://insurance:insurance_dev@localhost:5432/insurance?schema=public
+JWT_SECRET=change-me-in-prod
+PORT=4000
+HOST=0.0.0.0
+NODE_ENV=development
+UPLOAD_DIR=./uploads
diff --git a/server/Dockerfile b/server/Dockerfile
new file mode 100644
index 0000000..58c23dc
--- /dev/null
+++ b/server/Dockerfile
@@ -0,0 +1,31 @@
+FROM node:20-alpine AS builder
+WORKDIR /app
+
+COPY package*.json ./
+RUN npm ci --no-audit --no-fund
+
+COPY prisma ./prisma
+RUN npx prisma generate
+
+COPY tsconfig.json ./
+COPY src ./src
+RUN npm run build
+
+FROM node:20-alpine AS runner
+WORKDIR /app
+ENV NODE_ENV=production
+
+COPY package*.json ./
+RUN npm ci --omit=dev --no-audit --no-fund
+
+COPY --from=builder /app/node_modules/.prisma ./node_modules/.prisma
+COPY --from=builder /app/node_modules/@prisma ./node_modules/@prisma
+COPY --from=builder /app/prisma ./prisma
+COPY --from=builder /app/dist ./dist
+
+EXPOSE 4000
+
+HEALTHCHECK --interval=30s --timeout=3s --start-period=15s --retries=3 \
+ CMD wget -q --spider http://localhost:4000/health || exit 1
+
+CMD ["sh", "-c", "npx prisma migrate deploy && node dist/main.js"]
diff --git a/server/package-lock.json b/server/package-lock.json
new file mode 100644
index 0000000..6b91d95
--- /dev/null
+++ b/server/package-lock.json
@@ -0,0 +1,2968 @@
+{
+ "name": "insurance-api",
+ "version": "1.0.0",
+ "lockfileVersion": 3,
+ "requires": true,
+ "packages": {
+ "": {
+ "name": "insurance-api",
+ "version": "1.0.0",
+ "dependencies": {
+ "@fastify/cors": "^9.0.1",
+ "@fastify/jwt": "^8.0.1",
+ "@fastify/multipart": "^8.3.0",
+ "@fastify/swagger": "^8.15.0",
+ "@fastify/swagger-ui": "^4.1.0",
+ "@prisma/client": "^5.22.0",
+ "bcrypt": "^5.1.1",
+ "fastify": "^4.28.1",
+ "fastify-plugin": "^4.5.1",
+ "pino-pretty": "^11.2.2",
+ "zod": "^3.23.8"
+ },
+ "devDependencies": {
+ "@types/bcrypt": "^5.0.2",
+ "@types/node": "^20.14.11",
+ "prisma": "^5.22.0",
+ "tsx": "^4.19.2",
+ "typescript": "^5.5.4"
+ }
+ },
+ "node_modules/@esbuild/aix-ppc64": {
+ "version": "0.27.7",
+ "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.27.7.tgz",
+ "integrity": "sha512-EKX3Qwmhz1eMdEJokhALr0YiD0lhQNwDqkPYyPhiSwKrh7/4KRjQc04sZ8db+5DVVnZ1LmbNDI1uAMPEUBnQPg==",
+ "cpu": [
+ "ppc64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "aix"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/android-arm": {
+ "version": "0.27.7",
+ "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.27.7.tgz",
+ "integrity": "sha512-jbPXvB4Yj2yBV7HUfE2KHe4GJX51QplCN1pGbYjvsyCZbQmies29EoJbkEc+vYuU5o45AfQn37vZlyXy4YJ8RQ==",
+ "cpu": [
+ "arm"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "android"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/android-arm64": {
+ "version": "0.27.7",
+ "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.27.7.tgz",
+ "integrity": "sha512-62dPZHpIXzvChfvfLJow3q5dDtiNMkwiRzPylSCfriLvZeq0a1bWChrGx/BbUbPwOrsWKMn8idSllklzBy+dgQ==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "android"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/android-x64": {
+ "version": "0.27.7",
+ "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.27.7.tgz",
+ "integrity": "sha512-x5VpMODneVDb70PYV2VQOmIUUiBtY3D3mPBG8NxVk5CogneYhkR7MmM3yR/uMdITLrC1ml/NV1rj4bMJuy9MCg==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "android"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/darwin-arm64": {
+ "version": "0.27.7",
+ "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.27.7.tgz",
+ "integrity": "sha512-5lckdqeuBPlKUwvoCXIgI2D9/ABmPq3Rdp7IfL70393YgaASt7tbju3Ac+ePVi3KDH6N2RqePfHnXkaDtY9fkw==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/darwin-x64": {
+ "version": "0.27.7",
+ "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.27.7.tgz",
+ "integrity": "sha512-rYnXrKcXuT7Z+WL5K980jVFdvVKhCHhUwid+dDYQpH+qu+TefcomiMAJpIiC2EM3Rjtq0sO3StMV/+3w3MyyqQ==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/freebsd-arm64": {
+ "version": "0.27.7",
+ "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.27.7.tgz",
+ "integrity": "sha512-B48PqeCsEgOtzME2GbNM2roU29AMTuOIN91dsMO30t+Ydis3z/3Ngoj5hhnsOSSwNzS+6JppqWsuhTp6E82l2w==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "freebsd"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/freebsd-x64": {
+ "version": "0.27.7",
+ "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.27.7.tgz",
+ "integrity": "sha512-jOBDK5XEjA4m5IJK3bpAQF9/Lelu/Z9ZcdhTRLf4cajlB+8VEhFFRjWgfy3M1O4rO2GQ/b2dLwCUGpiF/eATNQ==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "freebsd"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/linux-arm": {
+ "version": "0.27.7",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.27.7.tgz",
+ "integrity": "sha512-RkT/YXYBTSULo3+af8Ib0ykH8u2MBh57o7q/DAs3lTJlyVQkgQvlrPTnjIzzRPQyavxtPtfg0EopvDyIt0j1rA==",
+ "cpu": [
+ "arm"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/linux-arm64": {
+ "version": "0.27.7",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.27.7.tgz",
+ "integrity": "sha512-RZPHBoxXuNnPQO9rvjh5jdkRmVizktkT7TCDkDmQ0W2SwHInKCAV95GRuvdSvA7w4VMwfCjUiPwDi0ZO6Nfe9A==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/linux-ia32": {
+ "version": "0.27.7",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.27.7.tgz",
+ "integrity": "sha512-GA48aKNkyQDbd3KtkplYWT102C5sn/EZTY4XROkxONgruHPU72l+gW+FfF8tf2cFjeHaRbWpOYa/uRBz/Xq1Pg==",
+ "cpu": [
+ "ia32"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/linux-loong64": {
+ "version": "0.27.7",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.27.7.tgz",
+ "integrity": "sha512-a4POruNM2oWsD4WKvBSEKGIiWQF8fZOAsycHOt6JBpZ+JN2n2JH9WAv56SOyu9X5IqAjqSIPTaJkqN8F7XOQ5Q==",
+ "cpu": [
+ "loong64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/linux-mips64el": {
+ "version": "0.27.7",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.27.7.tgz",
+ "integrity": "sha512-KabT5I6StirGfIz0FMgl1I+R1H73Gp0ofL9A3nG3i/cYFJzKHhouBV5VWK1CSgKvVaG4q1RNpCTR2LuTVB3fIw==",
+ "cpu": [
+ "mips64el"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/linux-ppc64": {
+ "version": "0.27.7",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.27.7.tgz",
+ "integrity": "sha512-gRsL4x6wsGHGRqhtI+ifpN/vpOFTQtnbsupUF5R5YTAg+y/lKelYR1hXbnBdzDjGbMYjVJLJTd2OFmMewAgwlQ==",
+ "cpu": [
+ "ppc64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/linux-riscv64": {
+ "version": "0.27.7",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.27.7.tgz",
+ "integrity": "sha512-hL25LbxO1QOngGzu2U5xeXtxXcW+/GvMN3ejANqXkxZ/opySAZMrc+9LY/WyjAan41unrR3YrmtTsUpwT66InQ==",
+ "cpu": [
+ "riscv64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/linux-s390x": {
+ "version": "0.27.7",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.27.7.tgz",
+ "integrity": "sha512-2k8go8Ycu1Kb46vEelhu1vqEP+UeRVj2zY1pSuPdgvbd5ykAw82Lrro28vXUrRmzEsUV0NzCf54yARIK8r0fdw==",
+ "cpu": [
+ "s390x"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/linux-x64": {
+ "version": "0.27.7",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.27.7.tgz",
+ "integrity": "sha512-hzznmADPt+OmsYzw1EE33ccA+HPdIqiCRq7cQeL1Jlq2gb1+OyWBkMCrYGBJ+sxVzve2ZJEVeePbLM2iEIZSxA==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/netbsd-arm64": {
+ "version": "0.27.7",
+ "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.27.7.tgz",
+ "integrity": "sha512-b6pqtrQdigZBwZxAn1UpazEisvwaIDvdbMbmrly7cDTMFnw/+3lVxxCTGOrkPVnsYIosJJXAsILG9XcQS+Yu6w==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "netbsd"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/netbsd-x64": {
+ "version": "0.27.7",
+ "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.27.7.tgz",
+ "integrity": "sha512-OfatkLojr6U+WN5EDYuoQhtM+1xco+/6FSzJJnuWiUw5eVcicbyK3dq5EeV/QHT1uy6GoDhGbFpprUiHUYggrw==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "netbsd"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/openbsd-arm64": {
+ "version": "0.27.7",
+ "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.27.7.tgz",
+ "integrity": "sha512-AFuojMQTxAz75Fo8idVcqoQWEHIXFRbOc1TrVcFSgCZtQfSdc1RXgB3tjOn/krRHENUB4j00bfGjyl2mJrU37A==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "openbsd"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/openbsd-x64": {
+ "version": "0.27.7",
+ "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.27.7.tgz",
+ "integrity": "sha512-+A1NJmfM8WNDv5CLVQYJ5PshuRm/4cI6WMZRg1by1GwPIQPCTs1GLEUHwiiQGT5zDdyLiRM/l1G0Pv54gvtKIg==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "openbsd"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/openharmony-arm64": {
+ "version": "0.27.7",
+ "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.27.7.tgz",
+ "integrity": "sha512-+KrvYb/C8zA9CU/g0sR6w2RBw7IGc5J2BPnc3dYc5VJxHCSF1yNMxTV5LQ7GuKteQXZtspjFbiuW5/dOj7H4Yw==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "openharmony"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/sunos-x64": {
+ "version": "0.27.7",
+ "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.27.7.tgz",
+ "integrity": "sha512-ikktIhFBzQNt/QDyOL580ti9+5mL/YZeUPKU2ivGtGjdTYoqz6jObj6nOMfhASpS4GU4Q/Clh1QtxWAvcYKamA==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "sunos"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/win32-arm64": {
+ "version": "0.27.7",
+ "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.27.7.tgz",
+ "integrity": "sha512-7yRhbHvPqSpRUV7Q20VuDwbjW5kIMwTHpptuUzV+AA46kiPze5Z7qgt6CLCK3pWFrHeNfDd1VKgyP4O+ng17CA==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/win32-ia32": {
+ "version": "0.27.7",
+ "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.27.7.tgz",
+ "integrity": "sha512-SmwKXe6VHIyZYbBLJrhOoCJRB/Z1tckzmgTLfFYOfpMAx63BJEaL9ExI8x7v0oAO3Zh6D/Oi1gVxEYr5oUCFhw==",
+ "cpu": [
+ "ia32"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/win32-x64": {
+ "version": "0.27.7",
+ "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.27.7.tgz",
+ "integrity": "sha512-56hiAJPhwQ1R4i+21FVF7V8kSD5zZTdHcVuRFMW0hn753vVfQN8xlx4uOPT4xoGH0Z/oVATuR82AiqSTDIpaHg==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@fastify/accept-negotiator": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/@fastify/accept-negotiator/-/accept-negotiator-1.1.0.tgz",
+ "integrity": "sha512-OIHZrb2ImZ7XG85HXOONLcJWGosv7sIvM2ifAPQVhg9Lv7qdmMBNVaai4QTdyuaqbKM5eO6sLSQOYI7wEQeCJQ==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=14"
+ }
+ },
+ "node_modules/@fastify/ajv-compiler": {
+ "version": "3.6.0",
+ "resolved": "https://registry.npmjs.org/@fastify/ajv-compiler/-/ajv-compiler-3.6.0.tgz",
+ "integrity": "sha512-LwdXQJjmMD+GwLOkP7TVC68qa+pSSogeWWmznRJ/coyTcfe9qA05AHFSe1eZFwK6q+xVRpChnvFUkf1iYaSZsQ==",
+ "license": "MIT",
+ "dependencies": {
+ "ajv": "^8.11.0",
+ "ajv-formats": "^2.1.1",
+ "fast-uri": "^2.0.0"
+ }
+ },
+ "node_modules/@fastify/busboy": {
+ "version": "3.2.0",
+ "resolved": "https://registry.npmjs.org/@fastify/busboy/-/busboy-3.2.0.tgz",
+ "integrity": "sha512-m9FVDXU3GT2ITSe0UaMA5rU3QkfC/UXtCU8y0gSN/GugTqtVldOBWIB5V6V3sbmenVZUIpU6f+mPEO2+m5iTaA==",
+ "license": "MIT"
+ },
+ "node_modules/@fastify/cors": {
+ "version": "9.0.1",
+ "resolved": "https://registry.npmjs.org/@fastify/cors/-/cors-9.0.1.tgz",
+ "integrity": "sha512-YY9Ho3ovI+QHIL2hW+9X4XqQjXLjJqsU+sMV/xFsxZkE8p3GNnYVFpoOxF7SsP5ZL76gwvbo3V9L+FIekBGU4Q==",
+ "license": "MIT",
+ "dependencies": {
+ "fastify-plugin": "^4.0.0",
+ "mnemonist": "0.39.6"
+ }
+ },
+ "node_modules/@fastify/deepmerge": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/@fastify/deepmerge/-/deepmerge-2.0.2.tgz",
+ "integrity": "sha512-3wuLdX5iiiYeZWP6bQrjqhrcvBIf0NHbQH1Ur1WbHvoiuTYUEItgygea3zs8aHpiitn0lOB8gX20u1qO+FDm7Q==",
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/fastify"
+ },
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/fastify"
+ }
+ ],
+ "license": "MIT"
+ },
+ "node_modules/@fastify/error": {
+ "version": "3.4.1",
+ "resolved": "https://registry.npmjs.org/@fastify/error/-/error-3.4.1.tgz",
+ "integrity": "sha512-wWSvph+29GR783IhmvdwWnN4bUxTD01Vm5Xad4i7i1VuAOItLvbPAb69sb0IQ2N57yprvhNIwAP5B6xfKTmjmQ==",
+ "license": "MIT"
+ },
+ "node_modules/@fastify/fast-json-stringify-compiler": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/@fastify/fast-json-stringify-compiler/-/fast-json-stringify-compiler-4.3.0.tgz",
+ "integrity": "sha512-aZAXGYo6m22Fk1zZzEUKBvut/CIIQe/BapEORnxiD5Qr0kPHqqI69NtEMCme74h+at72sPhbkb4ZrLd1W3KRLA==",
+ "license": "MIT",
+ "dependencies": {
+ "fast-json-stringify": "^5.7.0"
+ }
+ },
+ "node_modules/@fastify/jwt": {
+ "version": "8.0.1",
+ "resolved": "https://registry.npmjs.org/@fastify/jwt/-/jwt-8.0.1.tgz",
+ "integrity": "sha512-295bd7V6bDCnZOu8MAQgM6r7V1KILB+kdEq1q6nbHfXCnML569n7NSo3WzeLDG6IAqDl+Rhzi1vjxwaNHhRCBA==",
+ "license": "MIT",
+ "dependencies": {
+ "@fastify/error": "^3.0.0",
+ "@lukeed/ms": "^2.0.0",
+ "fast-jwt": "^4.0.0",
+ "fastify-plugin": "^4.0.0",
+ "steed": "^1.1.3"
+ }
+ },
+ "node_modules/@fastify/merge-json-schemas": {
+ "version": "0.1.1",
+ "resolved": "https://registry.npmjs.org/@fastify/merge-json-schemas/-/merge-json-schemas-0.1.1.tgz",
+ "integrity": "sha512-fERDVz7topgNjtXsJTTW1JKLy0rhuLRcquYqNR9rF7OcVpCa2OVW49ZPDIhaRRCaUuvVxI+N416xUoF76HNSXA==",
+ "license": "MIT",
+ "dependencies": {
+ "fast-deep-equal": "^3.1.3"
+ }
+ },
+ "node_modules/@fastify/multipart": {
+ "version": "8.3.1",
+ "resolved": "https://registry.npmjs.org/@fastify/multipart/-/multipart-8.3.1.tgz",
+ "integrity": "sha512-pncbnG28S6MIskFSVRtzTKE9dK+GrKAJl0NbaQ/CG8ded80okWFsYKzSlP9haaLNQhNRDOoHqmGQNvgbiPVpWQ==",
+ "license": "MIT",
+ "dependencies": {
+ "@fastify/busboy": "^3.0.0",
+ "@fastify/deepmerge": "^2.0.0",
+ "@fastify/error": "^4.0.0",
+ "fastify-plugin": "^4.0.0",
+ "secure-json-parse": "^2.4.0",
+ "stream-wormhole": "^1.1.0"
+ }
+ },
+ "node_modules/@fastify/multipart/node_modules/@fastify/error": {
+ "version": "4.2.0",
+ "resolved": "https://registry.npmjs.org/@fastify/error/-/error-4.2.0.tgz",
+ "integrity": "sha512-RSo3sVDXfHskiBZKBPRgnQTtIqpi/7zhJOEmAxCiBcM7d0uwdGdxLlsCaLzGs8v8NnxIRlfG0N51p5yFaOentQ==",
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/fastify"
+ },
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/fastify"
+ }
+ ],
+ "license": "MIT"
+ },
+ "node_modules/@fastify/send": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/@fastify/send/-/send-2.1.0.tgz",
+ "integrity": "sha512-yNYiY6sDkexoJR0D8IDy3aRP3+L4wdqCpvx5WP+VtEU58sn7USmKynBzDQex5X42Zzvw2gNzzYgP90UfWShLFA==",
+ "license": "MIT",
+ "dependencies": {
+ "@lukeed/ms": "^2.0.1",
+ "escape-html": "~1.0.3",
+ "fast-decode-uri-component": "^1.0.1",
+ "http-errors": "2.0.0",
+ "mime": "^3.0.0"
+ }
+ },
+ "node_modules/@fastify/static": {
+ "version": "7.0.4",
+ "resolved": "https://registry.npmjs.org/@fastify/static/-/static-7.0.4.tgz",
+ "integrity": "sha512-p2uKtaf8BMOZWLs6wu+Ihg7bWNBdjNgCwDza4MJtTqg+5ovKmcbgbR9Xs5/smZ1YISfzKOCNYmZV8LaCj+eJ1Q==",
+ "license": "MIT",
+ "dependencies": {
+ "@fastify/accept-negotiator": "^1.0.0",
+ "@fastify/send": "^2.0.0",
+ "content-disposition": "^0.5.3",
+ "fastify-plugin": "^4.0.0",
+ "fastq": "^1.17.0",
+ "glob": "^10.3.4"
+ }
+ },
+ "node_modules/@fastify/swagger": {
+ "version": "8.15.0",
+ "resolved": "https://registry.npmjs.org/@fastify/swagger/-/swagger-8.15.0.tgz",
+ "integrity": "sha512-zy+HEEKFqPMS2sFUsQU5X0MHplhKJvWeohBwTCkBAJA/GDYGLGUWQaETEhptiqxK7Hs0fQB9B4MDb3pbwIiCwA==",
+ "license": "MIT",
+ "dependencies": {
+ "fastify-plugin": "^4.0.0",
+ "json-schema-resolver": "^2.0.0",
+ "openapi-types": "^12.0.0",
+ "rfdc": "^1.3.0",
+ "yaml": "^2.2.2"
+ }
+ },
+ "node_modules/@fastify/swagger-ui": {
+ "version": "4.2.0",
+ "resolved": "https://registry.npmjs.org/@fastify/swagger-ui/-/swagger-ui-4.2.0.tgz",
+ "integrity": "sha512-pVutmTm49Pn98FS01E2m+eUH0WGhsHlImowWr9PXQt3rQPArSsocON8qF/8mm0dNLmilwtJZJqdsvFTnCUcapw==",
+ "license": "MIT",
+ "dependencies": {
+ "@fastify/static": "^7.0.0",
+ "fastify-plugin": "^4.0.0",
+ "openapi-types": "^12.0.2",
+ "rfdc": "^1.3.0",
+ "yaml": "^2.2.2"
+ }
+ },
+ "node_modules/@isaacs/cliui": {
+ "version": "8.0.2",
+ "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz",
+ "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==",
+ "license": "ISC",
+ "dependencies": {
+ "string-width": "^5.1.2",
+ "string-width-cjs": "npm:string-width@^4.2.0",
+ "strip-ansi": "^7.0.1",
+ "strip-ansi-cjs": "npm:strip-ansi@^6.0.1",
+ "wrap-ansi": "^8.1.0",
+ "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0"
+ },
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@lukeed/ms": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/@lukeed/ms/-/ms-2.0.2.tgz",
+ "integrity": "sha512-9I2Zn6+NJLfaGoz9jN3lpwDgAYvfGeNYdbAIjJOqzs4Tpc+VU3Jqq4IofSUBKajiDS8k9fZIg18/z13mpk1bsA==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/@mapbox/node-pre-gyp": {
+ "version": "1.0.11",
+ "resolved": "https://registry.npmjs.org/@mapbox/node-pre-gyp/-/node-pre-gyp-1.0.11.tgz",
+ "integrity": "sha512-Yhlar6v9WQgUp/He7BdgzOz8lqMQ8sU+jkCq7Wx8Myc5YFJLbEe7lgui/V7G1qB1DJykHSGwreceSaD60Y0PUQ==",
+ "license": "BSD-3-Clause",
+ "dependencies": {
+ "detect-libc": "^2.0.0",
+ "https-proxy-agent": "^5.0.0",
+ "make-dir": "^3.1.0",
+ "node-fetch": "^2.6.7",
+ "nopt": "^5.0.0",
+ "npmlog": "^5.0.1",
+ "rimraf": "^3.0.2",
+ "semver": "^7.3.5",
+ "tar": "^6.1.11"
+ },
+ "bin": {
+ "node-pre-gyp": "bin/node-pre-gyp"
+ }
+ },
+ "node_modules/@pinojs/redact": {
+ "version": "0.4.0",
+ "resolved": "https://registry.npmjs.org/@pinojs/redact/-/redact-0.4.0.tgz",
+ "integrity": "sha512-k2ENnmBugE/rzQfEcdWHcCY+/FM3VLzH9cYEsbdsoqrvzAKRhUZeRNhAZvB8OitQJ1TBed3yqWtdjzS6wJKBwg==",
+ "license": "MIT"
+ },
+ "node_modules/@pkgjs/parseargs": {
+ "version": "0.11.0",
+ "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz",
+ "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==",
+ "license": "MIT",
+ "optional": true,
+ "engines": {
+ "node": ">=14"
+ }
+ },
+ "node_modules/@prisma/client": {
+ "version": "5.22.0",
+ "resolved": "https://registry.npmjs.org/@prisma/client/-/client-5.22.0.tgz",
+ "integrity": "sha512-M0SVXfyHnQREBKxCgyo7sffrKttwE6R8PMq330MIUF0pTwjUhLbW84pFDlf06B27XyCR++VtjugEnIHdr07SVA==",
+ "hasInstallScript": true,
+ "license": "Apache-2.0",
+ "engines": {
+ "node": ">=16.13"
+ },
+ "peerDependencies": {
+ "prisma": "*"
+ },
+ "peerDependenciesMeta": {
+ "prisma": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@prisma/debug": {
+ "version": "5.22.0",
+ "resolved": "https://registry.npmjs.org/@prisma/debug/-/debug-5.22.0.tgz",
+ "integrity": "sha512-AUt44v3YJeggO2ZU5BkXI7M4hu9BF2zzH2iF2V5pyXT/lRTyWiElZ7It+bRH1EshoMRxHgpYg4VB6rCM+mG5jQ==",
+ "devOptional": true,
+ "license": "Apache-2.0"
+ },
+ "node_modules/@prisma/engines": {
+ "version": "5.22.0",
+ "resolved": "https://registry.npmjs.org/@prisma/engines/-/engines-5.22.0.tgz",
+ "integrity": "sha512-UNjfslWhAt06kVL3CjkuYpHAWSO6L4kDCVPegV6itt7nD1kSJavd3vhgAEhjglLJJKEdJ7oIqDJ+yHk6qO8gPA==",
+ "devOptional": true,
+ "hasInstallScript": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@prisma/debug": "5.22.0",
+ "@prisma/engines-version": "5.22.0-44.605197351a3c8bdd595af2d2a9bc3025bca48ea2",
+ "@prisma/fetch-engine": "5.22.0",
+ "@prisma/get-platform": "5.22.0"
+ }
+ },
+ "node_modules/@prisma/engines-version": {
+ "version": "5.22.0-44.605197351a3c8bdd595af2d2a9bc3025bca48ea2",
+ "resolved": "https://registry.npmjs.org/@prisma/engines-version/-/engines-version-5.22.0-44.605197351a3c8bdd595af2d2a9bc3025bca48ea2.tgz",
+ "integrity": "sha512-2PTmxFR2yHW/eB3uqWtcgRcgAbG1rwG9ZriSvQw+nnb7c4uCr3RAcGMb6/zfE88SKlC1Nj2ziUvc96Z379mHgQ==",
+ "devOptional": true,
+ "license": "Apache-2.0"
+ },
+ "node_modules/@prisma/fetch-engine": {
+ "version": "5.22.0",
+ "resolved": "https://registry.npmjs.org/@prisma/fetch-engine/-/fetch-engine-5.22.0.tgz",
+ "integrity": "sha512-bkrD/Mc2fSvkQBV5EpoFcZ87AvOgDxbG99488a5cexp5Ccny+UM6MAe/UFkUC0wLYD9+9befNOqGiIJhhq+HbA==",
+ "devOptional": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@prisma/debug": "5.22.0",
+ "@prisma/engines-version": "5.22.0-44.605197351a3c8bdd595af2d2a9bc3025bca48ea2",
+ "@prisma/get-platform": "5.22.0"
+ }
+ },
+ "node_modules/@prisma/get-platform": {
+ "version": "5.22.0",
+ "resolved": "https://registry.npmjs.org/@prisma/get-platform/-/get-platform-5.22.0.tgz",
+ "integrity": "sha512-pHhpQdr1UPFpt+zFfnPazhulaZYCUqeIcPpJViYoq9R+D/yw4fjE+CtnsnKzPYm0ddUbeXUzjGVGIRVgPDCk4Q==",
+ "devOptional": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@prisma/debug": "5.22.0"
+ }
+ },
+ "node_modules/@types/bcrypt": {
+ "version": "5.0.2",
+ "resolved": "https://registry.npmjs.org/@types/bcrypt/-/bcrypt-5.0.2.tgz",
+ "integrity": "sha512-6atioO8Y75fNcbmj0G7UjI9lXN2pQ/IGJ2FWT4a/btd0Lk9lQalHLKhkgKVZ3r+spnmWUKfbMi1GEe9wyHQfNQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@types/node": "*"
+ }
+ },
+ "node_modules/@types/node": {
+ "version": "20.19.39",
+ "resolved": "https://registry.npmjs.org/@types/node/-/node-20.19.39.tgz",
+ "integrity": "sha512-orrrD74MBUyK8jOAD/r0+lfa1I2MO6I+vAkmAWzMYbCcgrN4lCrmK52gRFQq/JRxfYPfonkr4b0jcY7Olqdqbw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "undici-types": "~6.21.0"
+ }
+ },
+ "node_modules/abbrev": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz",
+ "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==",
+ "license": "ISC"
+ },
+ "node_modules/abort-controller": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz",
+ "integrity": "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==",
+ "license": "MIT",
+ "dependencies": {
+ "event-target-shim": "^5.0.0"
+ },
+ "engines": {
+ "node": ">=6.5"
+ }
+ },
+ "node_modules/abstract-logging": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/abstract-logging/-/abstract-logging-2.0.1.tgz",
+ "integrity": "sha512-2BjRTZxTPvheOvGbBslFSYOUkr+SjPtOnrLP33f+VIWLzezQpZcqVg7ja3L4dBXmzzgwT+a029jRx5PCi3JuiA==",
+ "license": "MIT"
+ },
+ "node_modules/agent-base": {
+ "version": "6.0.2",
+ "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz",
+ "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==",
+ "license": "MIT",
+ "dependencies": {
+ "debug": "4"
+ },
+ "engines": {
+ "node": ">= 6.0.0"
+ }
+ },
+ "node_modules/ajv": {
+ "version": "8.18.0",
+ "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.18.0.tgz",
+ "integrity": "sha512-PlXPeEWMXMZ7sPYOHqmDyCJzcfNrUr3fGNKtezX14ykXOEIvyK81d+qydx89KY5O71FKMPaQ2vBfBFI5NHR63A==",
+ "license": "MIT",
+ "dependencies": {
+ "fast-deep-equal": "^3.1.3",
+ "fast-uri": "^3.0.1",
+ "json-schema-traverse": "^1.0.0",
+ "require-from-string": "^2.0.2"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/epoberezkin"
+ }
+ },
+ "node_modules/ajv-formats": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-2.1.1.tgz",
+ "integrity": "sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==",
+ "license": "MIT",
+ "dependencies": {
+ "ajv": "^8.0.0"
+ },
+ "peerDependencies": {
+ "ajv": "^8.0.0"
+ },
+ "peerDependenciesMeta": {
+ "ajv": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/ajv/node_modules/fast-uri": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.0.tgz",
+ "integrity": "sha512-iPeeDKJSWf4IEOasVVrknXpaBV0IApz/gp7S2bb7Z4Lljbl2MGJRqInZiUrQwV16cpzw/D3S5j5Julj/gT52AA==",
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/fastify"
+ },
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/fastify"
+ }
+ ],
+ "license": "BSD-3-Clause"
+ },
+ "node_modules/ansi-regex": {
+ "version": "6.2.2",
+ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.2.2.tgz",
+ "integrity": "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/ansi-regex?sponsor=1"
+ }
+ },
+ "node_modules/ansi-styles": {
+ "version": "6.2.3",
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.3.tgz",
+ "integrity": "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/ansi-styles?sponsor=1"
+ }
+ },
+ "node_modules/aproba": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/aproba/-/aproba-2.1.0.tgz",
+ "integrity": "sha512-tLIEcj5GuR2RSTnxNKdkK0dJ/GrC7P38sUkiDmDuHfsHmbagTFAxDVIBltoklXEVIQ/f14IL8IMJ5pn9Hez1Ew==",
+ "license": "ISC"
+ },
+ "node_modules/are-we-there-yet": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-2.0.0.tgz",
+ "integrity": "sha512-Ci/qENmwHnsYo9xKIcUJN5LeDKdJ6R1Z1j9V/J5wyq8nh/mYPEpIKJbBZXtZjG04HiK7zV/p6Vs9952MrMeUIw==",
+ "deprecated": "This package is no longer supported.",
+ "license": "ISC",
+ "dependencies": {
+ "delegates": "^1.0.0",
+ "readable-stream": "^3.6.0"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/asn1.js": {
+ "version": "5.4.1",
+ "resolved": "https://registry.npmjs.org/asn1.js/-/asn1.js-5.4.1.tgz",
+ "integrity": "sha512-+I//4cYPccV8LdmBLiX8CYvf9Sp3vQsrqu2QNXRcrbiWvcx/UdlFiqUJJzxRQxgsZmvhXhn4cSKeSmoFjVdupA==",
+ "license": "MIT",
+ "dependencies": {
+ "bn.js": "^4.0.0",
+ "inherits": "^2.0.1",
+ "minimalistic-assert": "^1.0.0",
+ "safer-buffer": "^2.1.0"
+ }
+ },
+ "node_modules/atomic-sleep": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/atomic-sleep/-/atomic-sleep-1.0.0.tgz",
+ "integrity": "sha512-kNOjDqAh7px0XWNI+4QbzoiR/nTkHAWNud2uvnJquD1/x5a7EQZMJT0AczqK0Qn67oY/TTQ1LbUKajZpp3I9tQ==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=8.0.0"
+ }
+ },
+ "node_modules/avvio": {
+ "version": "8.4.0",
+ "resolved": "https://registry.npmjs.org/avvio/-/avvio-8.4.0.tgz",
+ "integrity": "sha512-CDSwaxINFy59iNwhYnkvALBwZiTydGkOecZyPkqBpABYR1KqGEsET0VOOYDwtleZSUIdeY36DC2bSZ24CO1igA==",
+ "license": "MIT",
+ "dependencies": {
+ "@fastify/error": "^3.3.0",
+ "fastq": "^1.17.1"
+ }
+ },
+ "node_modules/balanced-match": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
+ "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==",
+ "license": "MIT"
+ },
+ "node_modules/base64-js": {
+ "version": "1.5.1",
+ "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz",
+ "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==",
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/feross"
+ },
+ {
+ "type": "patreon",
+ "url": "https://www.patreon.com/feross"
+ },
+ {
+ "type": "consulting",
+ "url": "https://feross.org/support"
+ }
+ ],
+ "license": "MIT"
+ },
+ "node_modules/bcrypt": {
+ "version": "5.1.1",
+ "resolved": "https://registry.npmjs.org/bcrypt/-/bcrypt-5.1.1.tgz",
+ "integrity": "sha512-AGBHOG5hPYZ5Xl9KXzU5iKq9516yEmvCKDg3ecP5kX2aB6UqTeXZxk2ELnDgDm6BQSMlLt9rDB4LoSMx0rYwww==",
+ "hasInstallScript": true,
+ "license": "MIT",
+ "dependencies": {
+ "@mapbox/node-pre-gyp": "^1.0.11",
+ "node-addon-api": "^5.0.0"
+ },
+ "engines": {
+ "node": ">= 10.0.0"
+ }
+ },
+ "node_modules/bn.js": {
+ "version": "4.12.3",
+ "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.3.tgz",
+ "integrity": "sha512-fGTi3gxV/23FTYdAoUtLYp6qySe2KE3teyZitipKNRuVYcBkoP/bB3guXN/XVKUe9mxCHXnc9C4ocyz8OmgN0g==",
+ "license": "MIT"
+ },
+ "node_modules/brace-expansion": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.1.0.tgz",
+ "integrity": "sha512-TN1kCZAgdgweJhWWpgKYrQaMNHcDULHkWwQIspdtjV4Y5aurRdZpjAqn6yX3FPqTA9ngHCc4hJxMAMgGfve85w==",
+ "license": "MIT",
+ "dependencies": {
+ "balanced-match": "^1.0.0"
+ }
+ },
+ "node_modules/buffer": {
+ "version": "6.0.3",
+ "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz",
+ "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==",
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/feross"
+ },
+ {
+ "type": "patreon",
+ "url": "https://www.patreon.com/feross"
+ },
+ {
+ "type": "consulting",
+ "url": "https://feross.org/support"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "base64-js": "^1.3.1",
+ "ieee754": "^1.2.1"
+ }
+ },
+ "node_modules/chownr": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/chownr/-/chownr-2.0.0.tgz",
+ "integrity": "sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==",
+ "license": "ISC",
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/color-convert": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
+ "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
+ "license": "MIT",
+ "dependencies": {
+ "color-name": "~1.1.4"
+ },
+ "engines": {
+ "node": ">=7.0.0"
+ }
+ },
+ "node_modules/color-name": {
+ "version": "1.1.4",
+ "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
+ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
+ "license": "MIT"
+ },
+ "node_modules/color-support": {
+ "version": "1.1.3",
+ "resolved": "https://registry.npmjs.org/color-support/-/color-support-1.1.3.tgz",
+ "integrity": "sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg==",
+ "license": "ISC",
+ "bin": {
+ "color-support": "bin.js"
+ }
+ },
+ "node_modules/colorette": {
+ "version": "2.0.20",
+ "resolved": "https://registry.npmjs.org/colorette/-/colorette-2.0.20.tgz",
+ "integrity": "sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==",
+ "license": "MIT"
+ },
+ "node_modules/concat-map": {
+ "version": "0.0.1",
+ "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
+ "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==",
+ "license": "MIT"
+ },
+ "node_modules/console-control-strings": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz",
+ "integrity": "sha512-ty/fTekppD2fIwRvnZAVdeOiGd1c7YXEixbgJTNzqcxJWKQnjJ/V1bNEEE6hygpM3WjwHFUVK6HTjWSzV4a8sQ==",
+ "license": "ISC"
+ },
+ "node_modules/content-disposition": {
+ "version": "0.5.4",
+ "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz",
+ "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==",
+ "license": "MIT",
+ "dependencies": {
+ "safe-buffer": "5.2.1"
+ },
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/cookie": {
+ "version": "0.7.2",
+ "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.2.tgz",
+ "integrity": "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/cross-spawn": {
+ "version": "7.0.6",
+ "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz",
+ "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==",
+ "license": "MIT",
+ "dependencies": {
+ "path-key": "^3.1.0",
+ "shebang-command": "^2.0.0",
+ "which": "^2.0.1"
+ },
+ "engines": {
+ "node": ">= 8"
+ }
+ },
+ "node_modules/dateformat": {
+ "version": "4.6.3",
+ "resolved": "https://registry.npmjs.org/dateformat/-/dateformat-4.6.3.tgz",
+ "integrity": "sha512-2P0p0pFGzHS5EMnhdxQi7aJN+iMheud0UhG4dlE1DLAlvL8JHjJJTX/CSm4JXwV0Ka5nGk3zC5mcb5bUQUxxMA==",
+ "license": "MIT",
+ "engines": {
+ "node": "*"
+ }
+ },
+ "node_modules/debug": {
+ "version": "4.4.3",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz",
+ "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==",
+ "license": "MIT",
+ "dependencies": {
+ "ms": "^2.1.3"
+ },
+ "engines": {
+ "node": ">=6.0"
+ },
+ "peerDependenciesMeta": {
+ "supports-color": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/delegates": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz",
+ "integrity": "sha512-bd2L678uiWATM6m5Z1VzNCErI3jiGzt6HGY8OVICs40JQq/HALfbyNJmp0UDakEY4pMMaN0Ly5om/B1VI/+xfQ==",
+ "license": "MIT"
+ },
+ "node_modules/depd": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz",
+ "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
+ "node_modules/detect-libc": {
+ "version": "2.1.2",
+ "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz",
+ "integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==",
+ "license": "Apache-2.0",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/eastasianwidth": {
+ "version": "0.2.0",
+ "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz",
+ "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==",
+ "license": "MIT"
+ },
+ "node_modules/ecdsa-sig-formatter": {
+ "version": "1.0.11",
+ "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz",
+ "integrity": "sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "safe-buffer": "^5.0.1"
+ }
+ },
+ "node_modules/emoji-regex": {
+ "version": "9.2.2",
+ "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz",
+ "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==",
+ "license": "MIT"
+ },
+ "node_modules/end-of-stream": {
+ "version": "1.4.5",
+ "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.5.tgz",
+ "integrity": "sha512-ooEGc6HP26xXq/N+GCGOT0JKCLDGrq2bQUZrQ7gyrJiZANJ/8YDTxTpQBXGMn+WbIQXNVpyWymm7KYVICQnyOg==",
+ "license": "MIT",
+ "dependencies": {
+ "once": "^1.4.0"
+ }
+ },
+ "node_modules/esbuild": {
+ "version": "0.27.7",
+ "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.27.7.tgz",
+ "integrity": "sha512-IxpibTjyVnmrIQo5aqNpCgoACA/dTKLTlhMHihVHhdkxKyPO1uBBthumT0rdHmcsk9uMonIWS0m4FljWzILh3w==",
+ "dev": true,
+ "hasInstallScript": true,
+ "license": "MIT",
+ "bin": {
+ "esbuild": "bin/esbuild"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "optionalDependencies": {
+ "@esbuild/aix-ppc64": "0.27.7",
+ "@esbuild/android-arm": "0.27.7",
+ "@esbuild/android-arm64": "0.27.7",
+ "@esbuild/android-x64": "0.27.7",
+ "@esbuild/darwin-arm64": "0.27.7",
+ "@esbuild/darwin-x64": "0.27.7",
+ "@esbuild/freebsd-arm64": "0.27.7",
+ "@esbuild/freebsd-x64": "0.27.7",
+ "@esbuild/linux-arm": "0.27.7",
+ "@esbuild/linux-arm64": "0.27.7",
+ "@esbuild/linux-ia32": "0.27.7",
+ "@esbuild/linux-loong64": "0.27.7",
+ "@esbuild/linux-mips64el": "0.27.7",
+ "@esbuild/linux-ppc64": "0.27.7",
+ "@esbuild/linux-riscv64": "0.27.7",
+ "@esbuild/linux-s390x": "0.27.7",
+ "@esbuild/linux-x64": "0.27.7",
+ "@esbuild/netbsd-arm64": "0.27.7",
+ "@esbuild/netbsd-x64": "0.27.7",
+ "@esbuild/openbsd-arm64": "0.27.7",
+ "@esbuild/openbsd-x64": "0.27.7",
+ "@esbuild/openharmony-arm64": "0.27.7",
+ "@esbuild/sunos-x64": "0.27.7",
+ "@esbuild/win32-arm64": "0.27.7",
+ "@esbuild/win32-ia32": "0.27.7",
+ "@esbuild/win32-x64": "0.27.7"
+ }
+ },
+ "node_modules/escape-html": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz",
+ "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==",
+ "license": "MIT"
+ },
+ "node_modules/event-target-shim": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/event-target-shim/-/event-target-shim-5.0.1.tgz",
+ "integrity": "sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/events": {
+ "version": "3.3.0",
+ "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz",
+ "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.8.x"
+ }
+ },
+ "node_modules/fast-content-type-parse": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/fast-content-type-parse/-/fast-content-type-parse-1.1.0.tgz",
+ "integrity": "sha512-fBHHqSTFLVnR61C+gltJuE5GkVQMV0S2nqUO8TJ+5Z3qAKG8vAx4FKai1s5jq/inV1+sREynIWSuQ6HgoSXpDQ==",
+ "license": "MIT"
+ },
+ "node_modules/fast-copy": {
+ "version": "3.0.2",
+ "resolved": "https://registry.npmjs.org/fast-copy/-/fast-copy-3.0.2.tgz",
+ "integrity": "sha512-dl0O9Vhju8IrcLndv2eU4ldt1ftXMqqfgN4H1cpmGV7P6jeB9FwpN9a2c8DPGE1Ys88rNUJVYDHq73CGAGOPfQ==",
+ "license": "MIT"
+ },
+ "node_modules/fast-decode-uri-component": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/fast-decode-uri-component/-/fast-decode-uri-component-1.0.1.tgz",
+ "integrity": "sha512-WKgKWg5eUxvRZGwW8FvfbaH7AXSh2cL+3j5fMGzUMCxWBJ3dV3a7Wz8y2f/uQ0e3B6WmodD3oS54jTQ9HVTIIg==",
+ "license": "MIT"
+ },
+ "node_modules/fast-deep-equal": {
+ "version": "3.1.3",
+ "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
+ "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==",
+ "license": "MIT"
+ },
+ "node_modules/fast-json-stringify": {
+ "version": "5.16.1",
+ "resolved": "https://registry.npmjs.org/fast-json-stringify/-/fast-json-stringify-5.16.1.tgz",
+ "integrity": "sha512-KAdnLvy1yu/XrRtP+LJnxbBGrhN+xXu+gt3EUvZhYGKCr3lFHq/7UFJHHFgmJKoqlh6B40bZLEv7w46B0mqn1g==",
+ "license": "MIT",
+ "dependencies": {
+ "@fastify/merge-json-schemas": "^0.1.0",
+ "ajv": "^8.10.0",
+ "ajv-formats": "^3.0.1",
+ "fast-deep-equal": "^3.1.3",
+ "fast-uri": "^2.1.0",
+ "json-schema-ref-resolver": "^1.0.1",
+ "rfdc": "^1.2.0"
+ }
+ },
+ "node_modules/fast-json-stringify/node_modules/ajv-formats": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-3.0.1.tgz",
+ "integrity": "sha512-8iUql50EUR+uUcdRQ3HDqa6EVyo3docL8g5WJ3FNcWmu62IbkGUue/pEyLBW8VGKKucTPgqeks4fIU1DA4yowQ==",
+ "license": "MIT",
+ "dependencies": {
+ "ajv": "^8.0.0"
+ },
+ "peerDependencies": {
+ "ajv": "^8.0.0"
+ },
+ "peerDependenciesMeta": {
+ "ajv": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/fast-jwt": {
+ "version": "4.0.5",
+ "resolved": "https://registry.npmjs.org/fast-jwt/-/fast-jwt-4.0.5.tgz",
+ "integrity": "sha512-QnpNdn0955GT7SlT8iMgYfhTsityUWysrQjM+Q7bGFijLp6+TNWzlbSMPvgalbrQGRg4ZaHZgMcns5fYOm5avg==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@lukeed/ms": "^2.0.1",
+ "asn1.js": "^5.4.1",
+ "ecdsa-sig-formatter": "^1.0.11",
+ "mnemonist": "^0.39.5"
+ },
+ "engines": {
+ "node": ">=16"
+ }
+ },
+ "node_modules/fast-querystring": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/fast-querystring/-/fast-querystring-1.1.2.tgz",
+ "integrity": "sha512-g6KuKWmFXc0fID8WWH0jit4g0AGBoJhCkJMb1RmbsSEUNvQ+ZC8D6CUZ+GtF8nMzSPXnhiePyyqqipzNNEnHjg==",
+ "license": "MIT",
+ "dependencies": {
+ "fast-decode-uri-component": "^1.0.1"
+ }
+ },
+ "node_modules/fast-safe-stringify": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/fast-safe-stringify/-/fast-safe-stringify-2.1.1.tgz",
+ "integrity": "sha512-W+KJc2dmILlPplD/H4K9l9LcAHAfPtP6BY84uVLXQ6Evcz9Lcg33Y2z1IVblT6xdY54PXYVHEv+0Wpq8Io6zkA==",
+ "license": "MIT"
+ },
+ "node_modules/fast-uri": {
+ "version": "2.4.0",
+ "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-2.4.0.tgz",
+ "integrity": "sha512-ypuAmmMKInk5q7XcepxlnUWDLWv4GFtaJqAzWKqn62IpQ3pejtr5dTVbt3vwqVaMKmkNR55sTT+CqUKIaT21BA==",
+ "license": "MIT"
+ },
+ "node_modules/fastfall": {
+ "version": "1.5.1",
+ "resolved": "https://registry.npmjs.org/fastfall/-/fastfall-1.5.1.tgz",
+ "integrity": "sha512-KH6p+Z8AKPXnmA7+Iz2Lh8ARCMr+8WNPVludm1LGkZoD2MjY6LVnRMtTKhkdzI+jr0RzQWXKzKyBJm1zoHEL4Q==",
+ "license": "MIT",
+ "dependencies": {
+ "reusify": "^1.0.0"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/fastify": {
+ "version": "4.29.1",
+ "resolved": "https://registry.npmjs.org/fastify/-/fastify-4.29.1.tgz",
+ "integrity": "sha512-m2kMNHIG92tSNWv+Z3UeTR9AWLLuo7KctC7mlFPtMEVrfjIhmQhkQnT9v15qA/BfVq3vvj134Y0jl9SBje3jXQ==",
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/fastify"
+ },
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/fastify"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "@fastify/ajv-compiler": "^3.5.0",
+ "@fastify/error": "^3.4.0",
+ "@fastify/fast-json-stringify-compiler": "^4.3.0",
+ "abstract-logging": "^2.0.1",
+ "avvio": "^8.3.0",
+ "fast-content-type-parse": "^1.1.0",
+ "fast-json-stringify": "^5.8.0",
+ "find-my-way": "^8.0.0",
+ "light-my-request": "^5.11.0",
+ "pino": "^9.0.0",
+ "process-warning": "^3.0.0",
+ "proxy-addr": "^2.0.7",
+ "rfdc": "^1.3.0",
+ "secure-json-parse": "^2.7.0",
+ "semver": "^7.5.4",
+ "toad-cache": "^3.3.0"
+ }
+ },
+ "node_modules/fastify-plugin": {
+ "version": "4.5.1",
+ "resolved": "https://registry.npmjs.org/fastify-plugin/-/fastify-plugin-4.5.1.tgz",
+ "integrity": "sha512-stRHYGeuqpEZTL1Ef0Ovr2ltazUT9g844X5z/zEBFLG8RYlpDiOCIG+ATvYEp+/zmc7sN29mcIMp8gvYplYPIQ==",
+ "license": "MIT"
+ },
+ "node_modules/fastparallel": {
+ "version": "2.4.1",
+ "resolved": "https://registry.npmjs.org/fastparallel/-/fastparallel-2.4.1.tgz",
+ "integrity": "sha512-qUmhxPgNHmvRjZKBFUNI0oZuuH9OlSIOXmJ98lhKPxMZZ7zS/Fi0wRHOihDSz0R1YiIOjxzOY4bq65YTcdBi2Q==",
+ "license": "ISC",
+ "dependencies": {
+ "reusify": "^1.0.4",
+ "xtend": "^4.0.2"
+ }
+ },
+ "node_modules/fastq": {
+ "version": "1.20.1",
+ "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.20.1.tgz",
+ "integrity": "sha512-GGToxJ/w1x32s/D2EKND7kTil4n8OVk/9mycTc4VDza13lOvpUZTGX3mFSCtV9ksdGBVzvsyAVLM6mHFThxXxw==",
+ "license": "ISC",
+ "dependencies": {
+ "reusify": "^1.0.4"
+ }
+ },
+ "node_modules/fastseries": {
+ "version": "1.7.2",
+ "resolved": "https://registry.npmjs.org/fastseries/-/fastseries-1.7.2.tgz",
+ "integrity": "sha512-dTPFrPGS8SNSzAt7u/CbMKCJ3s01N04s4JFbORHcmyvVfVKmbhMD1VtRbh5enGHxkaQDqWyLefiKOGGmohGDDQ==",
+ "license": "ISC",
+ "dependencies": {
+ "reusify": "^1.0.0",
+ "xtend": "^4.0.0"
+ }
+ },
+ "node_modules/find-my-way": {
+ "version": "8.2.2",
+ "resolved": "https://registry.npmjs.org/find-my-way/-/find-my-way-8.2.2.tgz",
+ "integrity": "sha512-Dobi7gcTEq8yszimcfp/R7+owiT4WncAJ7VTTgFH1jYJ5GaG1FbhjwDG820hptN0QDFvzVY3RfCzdInvGPGzjA==",
+ "license": "MIT",
+ "dependencies": {
+ "fast-deep-equal": "^3.1.3",
+ "fast-querystring": "^1.0.0",
+ "safe-regex2": "^3.1.0"
+ },
+ "engines": {
+ "node": ">=14"
+ }
+ },
+ "node_modules/foreground-child": {
+ "version": "3.3.1",
+ "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.1.tgz",
+ "integrity": "sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==",
+ "license": "ISC",
+ "dependencies": {
+ "cross-spawn": "^7.0.6",
+ "signal-exit": "^4.0.1"
+ },
+ "engines": {
+ "node": ">=14"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ }
+ },
+ "node_modules/forwarded": {
+ "version": "0.2.0",
+ "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz",
+ "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/fs-minipass": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-2.1.0.tgz",
+ "integrity": "sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==",
+ "license": "ISC",
+ "dependencies": {
+ "minipass": "^3.0.0"
+ },
+ "engines": {
+ "node": ">= 8"
+ }
+ },
+ "node_modules/fs-minipass/node_modules/minipass": {
+ "version": "3.3.6",
+ "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz",
+ "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==",
+ "license": "ISC",
+ "dependencies": {
+ "yallist": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/fs.realpath": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
+ "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==",
+ "license": "ISC"
+ },
+ "node_modules/fsevents": {
+ "version": "2.3.3",
+ "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz",
+ "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==",
+ "hasInstallScript": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": "^8.16.0 || ^10.6.0 || >=11.0.0"
+ }
+ },
+ "node_modules/gauge": {
+ "version": "3.0.2",
+ "resolved": "https://registry.npmjs.org/gauge/-/gauge-3.0.2.tgz",
+ "integrity": "sha512-+5J6MS/5XksCuXq++uFRsnUd7Ovu1XenbeuIuNRJxYWjgQbPuFhT14lAvsWfqfAmnwluf1OwMjz39HjfLPci0Q==",
+ "deprecated": "This package is no longer supported.",
+ "license": "ISC",
+ "dependencies": {
+ "aproba": "^1.0.3 || ^2.0.0",
+ "color-support": "^1.1.2",
+ "console-control-strings": "^1.0.0",
+ "has-unicode": "^2.0.1",
+ "object-assign": "^4.1.1",
+ "signal-exit": "^3.0.0",
+ "string-width": "^4.2.3",
+ "strip-ansi": "^6.0.1",
+ "wide-align": "^1.1.2"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/gauge/node_modules/ansi-regex": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
+ "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/gauge/node_modules/emoji-regex": {
+ "version": "8.0.0",
+ "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
+ "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==",
+ "license": "MIT"
+ },
+ "node_modules/gauge/node_modules/signal-exit": {
+ "version": "3.0.7",
+ "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz",
+ "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==",
+ "license": "ISC"
+ },
+ "node_modules/gauge/node_modules/string-width": {
+ "version": "4.2.3",
+ "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
+ "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
+ "license": "MIT",
+ "dependencies": {
+ "emoji-regex": "^8.0.0",
+ "is-fullwidth-code-point": "^3.0.0",
+ "strip-ansi": "^6.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/gauge/node_modules/strip-ansi": {
+ "version": "6.0.1",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
+ "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
+ "license": "MIT",
+ "dependencies": {
+ "ansi-regex": "^5.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/get-tsconfig": {
+ "version": "4.14.0",
+ "resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.14.0.tgz",
+ "integrity": "sha512-yTb+8DXzDREzgvYmh6s9vHsSVCHeC0G3PI5bEXNBHtmshPnO+S5O7qgLEOn0I5QvMy6kpZN8K1NKGyilLb93wA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "resolve-pkg-maps": "^1.0.0"
+ },
+ "funding": {
+ "url": "https://github.com/privatenumber/get-tsconfig?sponsor=1"
+ }
+ },
+ "node_modules/glob": {
+ "version": "10.5.0",
+ "resolved": "https://registry.npmjs.org/glob/-/glob-10.5.0.tgz",
+ "integrity": "sha512-DfXN8DfhJ7NH3Oe7cFmu3NCu1wKbkReJ8TorzSAFbSKrlNaQSKfIzqYqVY8zlbs2NLBbWpRiU52GX2PbaBVNkg==",
+ "deprecated": "Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me",
+ "license": "ISC",
+ "dependencies": {
+ "foreground-child": "^3.1.0",
+ "jackspeak": "^3.1.2",
+ "minimatch": "^9.0.4",
+ "minipass": "^7.1.2",
+ "package-json-from-dist": "^1.0.0",
+ "path-scurry": "^1.11.1"
+ },
+ "bin": {
+ "glob": "dist/esm/bin.mjs"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ }
+ },
+ "node_modules/has-unicode": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz",
+ "integrity": "sha512-8Rf9Y83NBReMnx0gFzA8JImQACstCYWUplepDa9xprwwtmgEZUF0h/i5xSA625zB/I37EtrswSST6OXxwaaIJQ==",
+ "license": "ISC"
+ },
+ "node_modules/help-me": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/help-me/-/help-me-5.0.0.tgz",
+ "integrity": "sha512-7xgomUX6ADmcYzFik0HzAxh/73YlKR9bmFzf51CZwR+b6YtzU2m0u49hQCqV6SvlqIqsaxovfwdvbnsw3b/zpg==",
+ "license": "MIT"
+ },
+ "node_modules/http-errors": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz",
+ "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==",
+ "license": "MIT",
+ "dependencies": {
+ "depd": "2.0.0",
+ "inherits": "2.0.4",
+ "setprototypeof": "1.2.0",
+ "statuses": "2.0.1",
+ "toidentifier": "1.0.1"
+ },
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
+ "node_modules/https-proxy-agent": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz",
+ "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==",
+ "license": "MIT",
+ "dependencies": {
+ "agent-base": "6",
+ "debug": "4"
+ },
+ "engines": {
+ "node": ">= 6"
+ }
+ },
+ "node_modules/ieee754": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz",
+ "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==",
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/feross"
+ },
+ {
+ "type": "patreon",
+ "url": "https://www.patreon.com/feross"
+ },
+ {
+ "type": "consulting",
+ "url": "https://feross.org/support"
+ }
+ ],
+ "license": "BSD-3-Clause"
+ },
+ "node_modules/inflight": {
+ "version": "1.0.6",
+ "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz",
+ "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==",
+ "deprecated": "This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.",
+ "license": "ISC",
+ "dependencies": {
+ "once": "^1.3.0",
+ "wrappy": "1"
+ }
+ },
+ "node_modules/inherits": {
+ "version": "2.0.4",
+ "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
+ "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==",
+ "license": "ISC"
+ },
+ "node_modules/ipaddr.js": {
+ "version": "1.9.1",
+ "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz",
+ "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.10"
+ }
+ },
+ "node_modules/is-fullwidth-code-point": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
+ "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/isexe": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz",
+ "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==",
+ "license": "ISC"
+ },
+ "node_modules/jackspeak": {
+ "version": "3.4.3",
+ "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.3.tgz",
+ "integrity": "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==",
+ "license": "BlueOak-1.0.0",
+ "dependencies": {
+ "@isaacs/cliui": "^8.0.2"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ },
+ "optionalDependencies": {
+ "@pkgjs/parseargs": "^0.11.0"
+ }
+ },
+ "node_modules/joycon": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/joycon/-/joycon-3.1.1.tgz",
+ "integrity": "sha512-34wB/Y7MW7bzjKRjUKTa46I2Z7eV62Rkhva+KkopW7Qvv/OSWBqvkSY7vusOPrNuZcUG3tApvdVgNB8POj3SPw==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/json-schema-ref-resolver": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/json-schema-ref-resolver/-/json-schema-ref-resolver-1.0.1.tgz",
+ "integrity": "sha512-EJAj1pgHc1hxF6vo2Z3s69fMjO1INq6eGHXZ8Z6wCQeldCuwxGK9Sxf4/cScGn3FZubCVUehfWtcDM/PLteCQw==",
+ "license": "MIT",
+ "dependencies": {
+ "fast-deep-equal": "^3.1.3"
+ }
+ },
+ "node_modules/json-schema-resolver": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/json-schema-resolver/-/json-schema-resolver-2.0.0.tgz",
+ "integrity": "sha512-pJ4XLQP4Q9HTxl6RVDLJ8Cyh1uitSs0CzDBAz1uoJ4sRD/Bk7cFSXL1FUXDW3zJ7YnfliJx6eu8Jn283bpZ4Yg==",
+ "license": "MIT",
+ "dependencies": {
+ "debug": "^4.1.1",
+ "rfdc": "^1.1.4",
+ "uri-js": "^4.2.2"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/Eomm/json-schema-resolver?sponsor=1"
+ }
+ },
+ "node_modules/json-schema-traverse": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz",
+ "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==",
+ "license": "MIT"
+ },
+ "node_modules/light-my-request": {
+ "version": "5.14.0",
+ "resolved": "https://registry.npmjs.org/light-my-request/-/light-my-request-5.14.0.tgz",
+ "integrity": "sha512-aORPWntbpH5esaYpGOOmri0OHDOe3wC5M2MQxZ9dvMLZm6DnaAn0kJlcbU9hwsQgLzmZyReKwFwwPkR+nHu5kA==",
+ "license": "BSD-3-Clause",
+ "dependencies": {
+ "cookie": "^0.7.0",
+ "process-warning": "^3.0.0",
+ "set-cookie-parser": "^2.4.1"
+ }
+ },
+ "node_modules/lru-cache": {
+ "version": "10.4.3",
+ "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz",
+ "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==",
+ "license": "ISC"
+ },
+ "node_modules/make-dir": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz",
+ "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==",
+ "license": "MIT",
+ "dependencies": {
+ "semver": "^6.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/make-dir/node_modules/semver": {
+ "version": "6.3.1",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz",
+ "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==",
+ "license": "ISC",
+ "bin": {
+ "semver": "bin/semver.js"
+ }
+ },
+ "node_modules/mime": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/mime/-/mime-3.0.0.tgz",
+ "integrity": "sha512-jSCU7/VB1loIWBZe14aEYHU/+1UMEHoaO7qxCOVJOw9GgH72VAWppxNcjU+x9a2k3GSIBXNKxXQFqRvvZ7vr3A==",
+ "license": "MIT",
+ "bin": {
+ "mime": "cli.js"
+ },
+ "engines": {
+ "node": ">=10.0.0"
+ }
+ },
+ "node_modules/minimalistic-assert": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz",
+ "integrity": "sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==",
+ "license": "ISC"
+ },
+ "node_modules/minimatch": {
+ "version": "9.0.9",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.9.tgz",
+ "integrity": "sha512-OBwBN9AL4dqmETlpS2zasx+vTeWclWzkblfZk7KTA5j3jeOONz/tRCnZomUyvNg83wL5Zv9Ss6HMJXAgL8R2Yg==",
+ "license": "ISC",
+ "dependencies": {
+ "brace-expansion": "^2.0.2"
+ },
+ "engines": {
+ "node": ">=16 || 14 >=14.17"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ }
+ },
+ "node_modules/minimist": {
+ "version": "1.2.8",
+ "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz",
+ "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==",
+ "license": "MIT",
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/minipass": {
+ "version": "7.1.3",
+ "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.3.tgz",
+ "integrity": "sha512-tEBHqDnIoM/1rXME1zgka9g6Q2lcoCkxHLuc7ODJ5BxbP5d4c2Z5cGgtXAku59200Cx7diuHTOYfSBD8n6mm8A==",
+ "license": "BlueOak-1.0.0",
+ "engines": {
+ "node": ">=16 || 14 >=14.17"
+ }
+ },
+ "node_modules/minizlib": {
+ "version": "2.1.2",
+ "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-2.1.2.tgz",
+ "integrity": "sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==",
+ "license": "MIT",
+ "dependencies": {
+ "minipass": "^3.0.0",
+ "yallist": "^4.0.0"
+ },
+ "engines": {
+ "node": ">= 8"
+ }
+ },
+ "node_modules/minizlib/node_modules/minipass": {
+ "version": "3.3.6",
+ "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz",
+ "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==",
+ "license": "ISC",
+ "dependencies": {
+ "yallist": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/mkdirp": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz",
+ "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==",
+ "license": "MIT",
+ "bin": {
+ "mkdirp": "bin/cmd.js"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/mnemonist": {
+ "version": "0.39.6",
+ "resolved": "https://registry.npmjs.org/mnemonist/-/mnemonist-0.39.6.tgz",
+ "integrity": "sha512-A/0v5Z59y63US00cRSLiloEIw3t5G+MiKz4BhX21FI+YBJXBOGW0ohFxTxO08dsOYlzxo87T7vGfZKYp2bcAWA==",
+ "license": "MIT",
+ "dependencies": {
+ "obliterator": "^2.0.1"
+ }
+ },
+ "node_modules/ms": {
+ "version": "2.1.3",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
+ "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
+ "license": "MIT"
+ },
+ "node_modules/node-addon-api": {
+ "version": "5.1.0",
+ "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-5.1.0.tgz",
+ "integrity": "sha512-eh0GgfEkpnoWDq+VY8OyvYhFEzBk6jIYbRKdIlyTiAXIVJ8PyBaKb0rp7oDtoddbdoHWhq8wwr+XZ81F1rpNdA==",
+ "license": "MIT"
+ },
+ "node_modules/node-fetch": {
+ "version": "2.7.0",
+ "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz",
+ "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==",
+ "license": "MIT",
+ "dependencies": {
+ "whatwg-url": "^5.0.0"
+ },
+ "engines": {
+ "node": "4.x || >=6.0.0"
+ },
+ "peerDependencies": {
+ "encoding": "^0.1.0"
+ },
+ "peerDependenciesMeta": {
+ "encoding": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/nopt": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/nopt/-/nopt-5.0.0.tgz",
+ "integrity": "sha512-Tbj67rffqceeLpcRXrT7vKAN8CwfPeIBgM7E6iBkmKLV7bEMwpGgYLGv0jACUsECaa/vuxP0IjEont6umdMgtQ==",
+ "license": "ISC",
+ "dependencies": {
+ "abbrev": "1"
+ },
+ "bin": {
+ "nopt": "bin/nopt.js"
+ },
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/npmlog": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-5.0.1.tgz",
+ "integrity": "sha512-AqZtDUWOMKs1G/8lwylVjrdYgqA4d9nu8hc+0gzRxlDb1I10+FHBGMXs6aiQHFdCUUlqH99MUMuLfzWDNDtfxw==",
+ "deprecated": "This package is no longer supported.",
+ "license": "ISC",
+ "dependencies": {
+ "are-we-there-yet": "^2.0.0",
+ "console-control-strings": "^1.1.0",
+ "gauge": "^3.0.0",
+ "set-blocking": "^2.0.0"
+ }
+ },
+ "node_modules/object-assign": {
+ "version": "4.1.1",
+ "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
+ "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/obliterator": {
+ "version": "2.0.5",
+ "resolved": "https://registry.npmjs.org/obliterator/-/obliterator-2.0.5.tgz",
+ "integrity": "sha512-42CPE9AhahZRsMNslczq0ctAEtqk8Eka26QofnqC346BZdHDySk3LWka23LI7ULIw11NmltpiLagIq8gBozxTw==",
+ "license": "MIT"
+ },
+ "node_modules/on-exit-leak-free": {
+ "version": "2.1.2",
+ "resolved": "https://registry.npmjs.org/on-exit-leak-free/-/on-exit-leak-free-2.1.2.tgz",
+ "integrity": "sha512-0eJJY6hXLGf1udHwfNftBqH+g73EU4B504nZeKpz1sYRKafAghwxEJunB2O7rDZkL4PGfsMVnTXZ2EjibbqcsA==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=14.0.0"
+ }
+ },
+ "node_modules/once": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
+ "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==",
+ "license": "ISC",
+ "dependencies": {
+ "wrappy": "1"
+ }
+ },
+ "node_modules/openapi-types": {
+ "version": "12.1.3",
+ "resolved": "https://registry.npmjs.org/openapi-types/-/openapi-types-12.1.3.tgz",
+ "integrity": "sha512-N4YtSYJqghVu4iek2ZUvcN/0aqH1kRDuNqzcycDxhOUpg7GdvLa2F3DgS6yBNhInhv2r/6I0Flkn7CqL8+nIcw==",
+ "license": "MIT"
+ },
+ "node_modules/package-json-from-dist": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz",
+ "integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==",
+ "license": "BlueOak-1.0.0"
+ },
+ "node_modules/path-is-absolute": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
+ "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/path-key": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz",
+ "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/path-scurry": {
+ "version": "1.11.1",
+ "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz",
+ "integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==",
+ "license": "BlueOak-1.0.0",
+ "dependencies": {
+ "lru-cache": "^10.2.0",
+ "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0"
+ },
+ "engines": {
+ "node": ">=16 || 14 >=14.18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ }
+ },
+ "node_modules/pino": {
+ "version": "9.14.0",
+ "resolved": "https://registry.npmjs.org/pino/-/pino-9.14.0.tgz",
+ "integrity": "sha512-8OEwKp5juEvb/MjpIc4hjqfgCNysrS94RIOMXYvpYCdm/jglrKEiAYmiumbmGhCvs+IcInsphYDFwqrjr7398w==",
+ "license": "MIT",
+ "dependencies": {
+ "@pinojs/redact": "^0.4.0",
+ "atomic-sleep": "^1.0.0",
+ "on-exit-leak-free": "^2.1.0",
+ "pino-abstract-transport": "^2.0.0",
+ "pino-std-serializers": "^7.0.0",
+ "process-warning": "^5.0.0",
+ "quick-format-unescaped": "^4.0.3",
+ "real-require": "^0.2.0",
+ "safe-stable-stringify": "^2.3.1",
+ "sonic-boom": "^4.0.1",
+ "thread-stream": "^3.0.0"
+ },
+ "bin": {
+ "pino": "bin.js"
+ }
+ },
+ "node_modules/pino-abstract-transport": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/pino-abstract-transport/-/pino-abstract-transport-2.0.0.tgz",
+ "integrity": "sha512-F63x5tizV6WCh4R6RHyi2Ml+M70DNRXt/+HANowMflpgGFMAym/VKm6G7ZOQRjqN7XbGxK1Lg9t6ZrtzOaivMw==",
+ "license": "MIT",
+ "dependencies": {
+ "split2": "^4.0.0"
+ }
+ },
+ "node_modules/pino-pretty": {
+ "version": "11.3.0",
+ "resolved": "https://registry.npmjs.org/pino-pretty/-/pino-pretty-11.3.0.tgz",
+ "integrity": "sha512-oXwn7ICywaZPHmu3epHGU2oJX4nPmKvHvB/bwrJHlGcbEWaVcotkpyVHMKLKmiVryWYByNp0jpgAcXpFJDXJzA==",
+ "license": "MIT",
+ "dependencies": {
+ "colorette": "^2.0.7",
+ "dateformat": "^4.6.3",
+ "fast-copy": "^3.0.2",
+ "fast-safe-stringify": "^2.1.1",
+ "help-me": "^5.0.0",
+ "joycon": "^3.1.1",
+ "minimist": "^1.2.6",
+ "on-exit-leak-free": "^2.1.0",
+ "pino-abstract-transport": "^2.0.0",
+ "pump": "^3.0.0",
+ "readable-stream": "^4.0.0",
+ "secure-json-parse": "^2.4.0",
+ "sonic-boom": "^4.0.1",
+ "strip-json-comments": "^3.1.1"
+ },
+ "bin": {
+ "pino-pretty": "bin.js"
+ }
+ },
+ "node_modules/pino-pretty/node_modules/readable-stream": {
+ "version": "4.7.0",
+ "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-4.7.0.tgz",
+ "integrity": "sha512-oIGGmcpTLwPga8Bn6/Z75SVaH1z5dUut2ibSyAMVhmUggWpmDn2dapB0n7f8nwaSiRtepAsfJyfXIO5DCVAODg==",
+ "license": "MIT",
+ "dependencies": {
+ "abort-controller": "^3.0.0",
+ "buffer": "^6.0.3",
+ "events": "^3.3.0",
+ "process": "^0.11.10",
+ "string_decoder": "^1.3.0"
+ },
+ "engines": {
+ "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+ }
+ },
+ "node_modules/pino-std-serializers": {
+ "version": "7.1.0",
+ "resolved": "https://registry.npmjs.org/pino-std-serializers/-/pino-std-serializers-7.1.0.tgz",
+ "integrity": "sha512-BndPH67/JxGExRgiX1dX0w1FvZck5Wa4aal9198SrRhZjH3GxKQUKIBnYJTdj2HDN3UQAS06HlfcSbQj2OHmaw==",
+ "license": "MIT"
+ },
+ "node_modules/pino/node_modules/process-warning": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/process-warning/-/process-warning-5.0.0.tgz",
+ "integrity": "sha512-a39t9ApHNx2L4+HBnQKqxxHNs1r7KF+Intd8Q/g1bUh6q0WIp9voPXJ/x0j+ZL45KF1pJd9+q2jLIRMfvEshkA==",
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/fastify"
+ },
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/fastify"
+ }
+ ],
+ "license": "MIT"
+ },
+ "node_modules/prisma": {
+ "version": "5.22.0",
+ "resolved": "https://registry.npmjs.org/prisma/-/prisma-5.22.0.tgz",
+ "integrity": "sha512-vtpjW3XuYCSnMsNVBjLMNkTj6OZbudcPPTPYHqX0CJfpcdWciI1dM8uHETwmDxxiqEwCIE6WvXucWUetJgfu/A==",
+ "devOptional": true,
+ "hasInstallScript": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@prisma/engines": "5.22.0"
+ },
+ "bin": {
+ "prisma": "build/index.js"
+ },
+ "engines": {
+ "node": ">=16.13"
+ },
+ "optionalDependencies": {
+ "fsevents": "2.3.3"
+ }
+ },
+ "node_modules/process": {
+ "version": "0.11.10",
+ "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz",
+ "integrity": "sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.6.0"
+ }
+ },
+ "node_modules/process-warning": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/process-warning/-/process-warning-3.0.0.tgz",
+ "integrity": "sha512-mqn0kFRl0EoqhnL0GQ0veqFHyIN1yig9RHh/InzORTUiZHFRAur+aMtRkELNwGs9aNwKS6tg/An4NYBPGwvtzQ==",
+ "license": "MIT"
+ },
+ "node_modules/proxy-addr": {
+ "version": "2.0.7",
+ "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz",
+ "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==",
+ "license": "MIT",
+ "dependencies": {
+ "forwarded": "0.2.0",
+ "ipaddr.js": "1.9.1"
+ },
+ "engines": {
+ "node": ">= 0.10"
+ }
+ },
+ "node_modules/pump": {
+ "version": "3.0.4",
+ "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.4.tgz",
+ "integrity": "sha512-VS7sjc6KR7e1ukRFhQSY5LM2uBWAUPiOPa/A3mkKmiMwSmRFUITt0xuj+/lesgnCv+dPIEYlkzrcyXgquIHMcA==",
+ "license": "MIT",
+ "dependencies": {
+ "end-of-stream": "^1.1.0",
+ "once": "^1.3.1"
+ }
+ },
+ "node_modules/punycode": {
+ "version": "2.3.1",
+ "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz",
+ "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/quick-format-unescaped": {
+ "version": "4.0.4",
+ "resolved": "https://registry.npmjs.org/quick-format-unescaped/-/quick-format-unescaped-4.0.4.tgz",
+ "integrity": "sha512-tYC1Q1hgyRuHgloV/YXs2w15unPVh8qfu/qCTfhTYamaw7fyhumKa2yGpdSo87vY32rIclj+4fWYQXUMs9EHvg==",
+ "license": "MIT"
+ },
+ "node_modules/readable-stream": {
+ "version": "3.6.2",
+ "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz",
+ "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==",
+ "license": "MIT",
+ "dependencies": {
+ "inherits": "^2.0.3",
+ "string_decoder": "^1.1.1",
+ "util-deprecate": "^1.0.1"
+ },
+ "engines": {
+ "node": ">= 6"
+ }
+ },
+ "node_modules/real-require": {
+ "version": "0.2.0",
+ "resolved": "https://registry.npmjs.org/real-require/-/real-require-0.2.0.tgz",
+ "integrity": "sha512-57frrGM/OCTLqLOAh0mhVA9VBMHd+9U7Zb2THMGdBUoZVOtGbJzjxsYGDJ3A9AYYCP4hn6y1TVbaOfzWtm5GFg==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 12.13.0"
+ }
+ },
+ "node_modules/require-from-string": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz",
+ "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/resolve-pkg-maps": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/resolve-pkg-maps/-/resolve-pkg-maps-1.0.0.tgz",
+ "integrity": "sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==",
+ "dev": true,
+ "license": "MIT",
+ "funding": {
+ "url": "https://github.com/privatenumber/resolve-pkg-maps?sponsor=1"
+ }
+ },
+ "node_modules/ret": {
+ "version": "0.4.3",
+ "resolved": "https://registry.npmjs.org/ret/-/ret-0.4.3.tgz",
+ "integrity": "sha512-0f4Memo5QP7WQyUEAYUO3esD/XjOc3Zjjg5CPsAq1p8sIu0XPeMbHJemKA0BO7tV0X7+A0FoEpbmHXWxPyD3wQ==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/reusify": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.1.0.tgz",
+ "integrity": "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==",
+ "license": "MIT",
+ "engines": {
+ "iojs": ">=1.0.0",
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/rfdc": {
+ "version": "1.4.1",
+ "resolved": "https://registry.npmjs.org/rfdc/-/rfdc-1.4.1.tgz",
+ "integrity": "sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA==",
+ "license": "MIT"
+ },
+ "node_modules/rimraf": {
+ "version": "3.0.2",
+ "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz",
+ "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==",
+ "deprecated": "Rimraf versions prior to v4 are no longer supported",
+ "license": "ISC",
+ "dependencies": {
+ "glob": "^7.1.3"
+ },
+ "bin": {
+ "rimraf": "bin.js"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ }
+ },
+ "node_modules/rimraf/node_modules/brace-expansion": {
+ "version": "1.1.14",
+ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.14.tgz",
+ "integrity": "sha512-MWPGfDxnyzKU7rNOW9SP/c50vi3xrmrua/+6hfPbCS2ABNWfx24vPidzvC7krjU/RTo235sV776ymlsMtGKj8g==",
+ "license": "MIT",
+ "dependencies": {
+ "balanced-match": "^1.0.0",
+ "concat-map": "0.0.1"
+ }
+ },
+ "node_modules/rimraf/node_modules/glob": {
+ "version": "7.2.3",
+ "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz",
+ "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==",
+ "deprecated": "Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me",
+ "license": "ISC",
+ "dependencies": {
+ "fs.realpath": "^1.0.0",
+ "inflight": "^1.0.4",
+ "inherits": "2",
+ "minimatch": "^3.1.1",
+ "once": "^1.3.0",
+ "path-is-absolute": "^1.0.0"
+ },
+ "engines": {
+ "node": "*"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ }
+ },
+ "node_modules/rimraf/node_modules/minimatch": {
+ "version": "3.1.5",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz",
+ "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==",
+ "license": "ISC",
+ "dependencies": {
+ "brace-expansion": "^1.1.7"
+ },
+ "engines": {
+ "node": "*"
+ }
+ },
+ "node_modules/safe-buffer": {
+ "version": "5.2.1",
+ "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
+ "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==",
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/feross"
+ },
+ {
+ "type": "patreon",
+ "url": "https://www.patreon.com/feross"
+ },
+ {
+ "type": "consulting",
+ "url": "https://feross.org/support"
+ }
+ ],
+ "license": "MIT"
+ },
+ "node_modules/safe-regex2": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/safe-regex2/-/safe-regex2-3.1.0.tgz",
+ "integrity": "sha512-RAAZAGbap2kBfbVhvmnTFv73NWLMvDGOITFYTZBAaY8eR+Ir4ef7Up/e7amo+y1+AH+3PtLkrt9mvcTsG9LXug==",
+ "license": "MIT",
+ "dependencies": {
+ "ret": "~0.4.0"
+ }
+ },
+ "node_modules/safe-stable-stringify": {
+ "version": "2.5.0",
+ "resolved": "https://registry.npmjs.org/safe-stable-stringify/-/safe-stable-stringify-2.5.0.tgz",
+ "integrity": "sha512-b3rppTKm9T+PsVCBEOUR46GWI7fdOs00VKZ1+9c1EWDaDMvjQc6tUwuFyIprgGgTcWoVHSKrU8H31ZHA2e0RHA==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/safer-buffer": {
+ "version": "2.1.2",
+ "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
+ "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==",
+ "license": "MIT"
+ },
+ "node_modules/secure-json-parse": {
+ "version": "2.7.0",
+ "resolved": "https://registry.npmjs.org/secure-json-parse/-/secure-json-parse-2.7.0.tgz",
+ "integrity": "sha512-6aU+Rwsezw7VR8/nyvKTx8QpWH9FrcYiXXlqC4z5d5XQBDRqtbfsRjnwGyqbi3gddNtWHuEk9OANUotL26qKUw==",
+ "license": "BSD-3-Clause"
+ },
+ "node_modules/semver": {
+ "version": "7.7.4",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz",
+ "integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==",
+ "license": "ISC",
+ "bin": {
+ "semver": "bin/semver.js"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/set-blocking": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz",
+ "integrity": "sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==",
+ "license": "ISC"
+ },
+ "node_modules/set-cookie-parser": {
+ "version": "2.7.2",
+ "resolved": "https://registry.npmjs.org/set-cookie-parser/-/set-cookie-parser-2.7.2.tgz",
+ "integrity": "sha512-oeM1lpU/UvhTxw+g3cIfxXHyJRc/uidd3yK1P242gzHds0udQBYzs3y8j4gCCW+ZJ7ad0yctld8RYO+bdurlvw==",
+ "license": "MIT"
+ },
+ "node_modules/setprototypeof": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz",
+ "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==",
+ "license": "ISC"
+ },
+ "node_modules/shebang-command": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz",
+ "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==",
+ "license": "MIT",
+ "dependencies": {
+ "shebang-regex": "^3.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/shebang-regex": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz",
+ "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/signal-exit": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz",
+ "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==",
+ "license": "ISC",
+ "engines": {
+ "node": ">=14"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ }
+ },
+ "node_modules/sonic-boom": {
+ "version": "4.2.1",
+ "resolved": "https://registry.npmjs.org/sonic-boom/-/sonic-boom-4.2.1.tgz",
+ "integrity": "sha512-w6AxtubXa2wTXAUsZMMWERrsIRAdrK0Sc+FUytWvYAhBJLyuI4llrMIC1DtlNSdI99EI86KZum2MMq3EAZlF9Q==",
+ "license": "MIT",
+ "dependencies": {
+ "atomic-sleep": "^1.0.0"
+ }
+ },
+ "node_modules/split2": {
+ "version": "4.2.0",
+ "resolved": "https://registry.npmjs.org/split2/-/split2-4.2.0.tgz",
+ "integrity": "sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==",
+ "license": "ISC",
+ "engines": {
+ "node": ">= 10.x"
+ }
+ },
+ "node_modules/statuses": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz",
+ "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
+ "node_modules/steed": {
+ "version": "1.1.3",
+ "resolved": "https://registry.npmjs.org/steed/-/steed-1.1.3.tgz",
+ "integrity": "sha512-EUkci0FAUiE4IvGTSKcDJIQ/eRUP2JJb56+fvZ4sdnguLTqIdKjSxUe138poW8mkvKWXW2sFPrgTsxqoISnmoA==",
+ "license": "MIT",
+ "dependencies": {
+ "fastfall": "^1.5.0",
+ "fastparallel": "^2.2.0",
+ "fastq": "^1.3.0",
+ "fastseries": "^1.7.0",
+ "reusify": "^1.0.0"
+ }
+ },
+ "node_modules/stream-wormhole": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/stream-wormhole/-/stream-wormhole-1.1.0.tgz",
+ "integrity": "sha512-gHFfL3px0Kctd6Po0M8TzEvt3De/xu6cnRrjlfYNhwbhLPLwigI2t1nc6jrzNuaYg5C4YF78PPFuQPzRiqn9ew==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=4.0.0"
+ }
+ },
+ "node_modules/string_decoder": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz",
+ "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==",
+ "license": "MIT",
+ "dependencies": {
+ "safe-buffer": "~5.2.0"
+ }
+ },
+ "node_modules/string-width": {
+ "version": "5.1.2",
+ "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz",
+ "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==",
+ "license": "MIT",
+ "dependencies": {
+ "eastasianwidth": "^0.2.0",
+ "emoji-regex": "^9.2.2",
+ "strip-ansi": "^7.0.1"
+ },
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/string-width-cjs": {
+ "name": "string-width",
+ "version": "4.2.3",
+ "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
+ "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
+ "license": "MIT",
+ "dependencies": {
+ "emoji-regex": "^8.0.0",
+ "is-fullwidth-code-point": "^3.0.0",
+ "strip-ansi": "^6.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/string-width-cjs/node_modules/ansi-regex": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
+ "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/string-width-cjs/node_modules/emoji-regex": {
+ "version": "8.0.0",
+ "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
+ "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==",
+ "license": "MIT"
+ },
+ "node_modules/string-width-cjs/node_modules/strip-ansi": {
+ "version": "6.0.1",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
+ "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
+ "license": "MIT",
+ "dependencies": {
+ "ansi-regex": "^5.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/strip-ansi": {
+ "version": "7.2.0",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.2.0.tgz",
+ "integrity": "sha512-yDPMNjp4WyfYBkHnjIRLfca1i6KMyGCtsVgoKe/z1+6vukgaENdgGBZt+ZmKPc4gavvEZ5OgHfHdrazhgNyG7w==",
+ "license": "MIT",
+ "dependencies": {
+ "ansi-regex": "^6.2.2"
+ },
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/strip-ansi?sponsor=1"
+ }
+ },
+ "node_modules/strip-ansi-cjs": {
+ "name": "strip-ansi",
+ "version": "6.0.1",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
+ "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
+ "license": "MIT",
+ "dependencies": {
+ "ansi-regex": "^5.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/strip-ansi-cjs/node_modules/ansi-regex": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
+ "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/strip-json-comments": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz",
+ "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/tar": {
+ "version": "6.2.1",
+ "resolved": "https://registry.npmjs.org/tar/-/tar-6.2.1.tgz",
+ "integrity": "sha512-DZ4yORTwrbTj/7MZYq2w+/ZFdI6OZ/f9SFHR+71gIVUZhOQPHzVCLpvRnPgyaMpfWxxk/4ONva3GQSyNIKRv6A==",
+ "deprecated": "Old versions of tar are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me",
+ "license": "ISC",
+ "dependencies": {
+ "chownr": "^2.0.0",
+ "fs-minipass": "^2.0.0",
+ "minipass": "^5.0.0",
+ "minizlib": "^2.1.1",
+ "mkdirp": "^1.0.3",
+ "yallist": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/tar/node_modules/minipass": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/minipass/-/minipass-5.0.0.tgz",
+ "integrity": "sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ==",
+ "license": "ISC",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/thread-stream": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/thread-stream/-/thread-stream-3.1.0.tgz",
+ "integrity": "sha512-OqyPZ9u96VohAyMfJykzmivOrY2wfMSf3C5TtFJVgN+Hm6aj+voFhlK+kZEIv2FBh1X6Xp3DlnCOfEQ3B2J86A==",
+ "license": "MIT",
+ "dependencies": {
+ "real-require": "^0.2.0"
+ }
+ },
+ "node_modules/toad-cache": {
+ "version": "3.7.0",
+ "resolved": "https://registry.npmjs.org/toad-cache/-/toad-cache-3.7.0.tgz",
+ "integrity": "sha512-/m8M+2BJUpoJdgAHoG+baCwBT+tf2VraSfkBgl0Y00qIWt41DJ8R5B8nsEw0I58YwF5IZH6z24/2TobDKnqSWw==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/toidentifier": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz",
+ "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.6"
+ }
+ },
+ "node_modules/tr46": {
+ "version": "0.0.3",
+ "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz",
+ "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==",
+ "license": "MIT"
+ },
+ "node_modules/tsx": {
+ "version": "4.21.0",
+ "resolved": "https://registry.npmjs.org/tsx/-/tsx-4.21.0.tgz",
+ "integrity": "sha512-5C1sg4USs1lfG0GFb2RLXsdpXqBSEhAaA/0kPL01wxzpMqLILNxIxIOKiILz+cdg/pLnOUxFYOR5yhHU666wbw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "esbuild": "~0.27.0",
+ "get-tsconfig": "^4.7.5"
+ },
+ "bin": {
+ "tsx": "dist/cli.mjs"
+ },
+ "engines": {
+ "node": ">=18.0.0"
+ },
+ "optionalDependencies": {
+ "fsevents": "~2.3.3"
+ }
+ },
+ "node_modules/typescript": {
+ "version": "5.9.3",
+ "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz",
+ "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "bin": {
+ "tsc": "bin/tsc",
+ "tsserver": "bin/tsserver"
+ },
+ "engines": {
+ "node": ">=14.17"
+ }
+ },
+ "node_modules/undici-types": {
+ "version": "6.21.0",
+ "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz",
+ "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/uri-js": {
+ "version": "4.4.1",
+ "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz",
+ "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==",
+ "license": "BSD-2-Clause",
+ "dependencies": {
+ "punycode": "^2.1.0"
+ }
+ },
+ "node_modules/util-deprecate": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
+ "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==",
+ "license": "MIT"
+ },
+ "node_modules/webidl-conversions": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz",
+ "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==",
+ "license": "BSD-2-Clause"
+ },
+ "node_modules/whatwg-url": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz",
+ "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==",
+ "license": "MIT",
+ "dependencies": {
+ "tr46": "~0.0.3",
+ "webidl-conversions": "^3.0.0"
+ }
+ },
+ "node_modules/which": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
+ "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==",
+ "license": "ISC",
+ "dependencies": {
+ "isexe": "^2.0.0"
+ },
+ "bin": {
+ "node-which": "bin/node-which"
+ },
+ "engines": {
+ "node": ">= 8"
+ }
+ },
+ "node_modules/wide-align": {
+ "version": "1.1.5",
+ "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.5.tgz",
+ "integrity": "sha512-eDMORYaPNZ4sQIuuYPDHdQvf4gyCF9rEEV/yPxGfwPkRodwEgiMUUXTx/dex+Me0wxx53S+NgUHaP7y3MGlDmg==",
+ "license": "ISC",
+ "dependencies": {
+ "string-width": "^1.0.2 || 2 || 3 || 4"
+ }
+ },
+ "node_modules/wide-align/node_modules/ansi-regex": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
+ "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/wide-align/node_modules/emoji-regex": {
+ "version": "8.0.0",
+ "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
+ "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==",
+ "license": "MIT"
+ },
+ "node_modules/wide-align/node_modules/string-width": {
+ "version": "4.2.3",
+ "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
+ "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
+ "license": "MIT",
+ "dependencies": {
+ "emoji-regex": "^8.0.0",
+ "is-fullwidth-code-point": "^3.0.0",
+ "strip-ansi": "^6.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/wide-align/node_modules/strip-ansi": {
+ "version": "6.0.1",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
+ "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
+ "license": "MIT",
+ "dependencies": {
+ "ansi-regex": "^5.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/wrap-ansi": {
+ "version": "8.1.0",
+ "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz",
+ "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==",
+ "license": "MIT",
+ "dependencies": {
+ "ansi-styles": "^6.1.0",
+ "string-width": "^5.0.1",
+ "strip-ansi": "^7.0.1"
+ },
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/wrap-ansi?sponsor=1"
+ }
+ },
+ "node_modules/wrap-ansi-cjs": {
+ "name": "wrap-ansi",
+ "version": "7.0.0",
+ "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz",
+ "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==",
+ "license": "MIT",
+ "dependencies": {
+ "ansi-styles": "^4.0.0",
+ "string-width": "^4.1.0",
+ "strip-ansi": "^6.0.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/wrap-ansi?sponsor=1"
+ }
+ },
+ "node_modules/wrap-ansi-cjs/node_modules/ansi-regex": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
+ "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/wrap-ansi-cjs/node_modules/ansi-styles": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
+ "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
+ "license": "MIT",
+ "dependencies": {
+ "color-convert": "^2.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/ansi-styles?sponsor=1"
+ }
+ },
+ "node_modules/wrap-ansi-cjs/node_modules/emoji-regex": {
+ "version": "8.0.0",
+ "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
+ "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==",
+ "license": "MIT"
+ },
+ "node_modules/wrap-ansi-cjs/node_modules/string-width": {
+ "version": "4.2.3",
+ "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
+ "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
+ "license": "MIT",
+ "dependencies": {
+ "emoji-regex": "^8.0.0",
+ "is-fullwidth-code-point": "^3.0.0",
+ "strip-ansi": "^6.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/wrap-ansi-cjs/node_modules/strip-ansi": {
+ "version": "6.0.1",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
+ "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
+ "license": "MIT",
+ "dependencies": {
+ "ansi-regex": "^5.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/wrappy": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
+ "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==",
+ "license": "ISC"
+ },
+ "node_modules/xtend": {
+ "version": "4.0.2",
+ "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz",
+ "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.4"
+ }
+ },
+ "node_modules/yallist": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",
+ "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==",
+ "license": "ISC"
+ },
+ "node_modules/yaml": {
+ "version": "2.8.3",
+ "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.8.3.tgz",
+ "integrity": "sha512-AvbaCLOO2Otw/lW5bmh9d/WEdcDFdQp2Z2ZUH3pX9U2ihyUY0nvLv7J6TrWowklRGPYbB/IuIMfYgxaCPg5Bpg==",
+ "license": "ISC",
+ "bin": {
+ "yaml": "bin.mjs"
+ },
+ "engines": {
+ "node": ">= 14.6"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/eemeli"
+ }
+ },
+ "node_modules/zod": {
+ "version": "3.25.76",
+ "resolved": "https://registry.npmjs.org/zod/-/zod-3.25.76.tgz",
+ "integrity": "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==",
+ "license": "MIT",
+ "funding": {
+ "url": "https://github.com/sponsors/colinhacks"
+ }
+ }
+ }
+}
diff --git a/server/package.json b/server/package.json
new file mode 100644
index 0000000..bfeb3a4
--- /dev/null
+++ b/server/package.json
@@ -0,0 +1,35 @@
+{
+ "name": "insurance-api",
+ "version": "1.0.0",
+ "private": true,
+ "main": "dist/main.js",
+ "scripts": {
+ "dev": "tsx watch src/main.ts",
+ "build": "tsc -p tsconfig.json",
+ "start": "node dist/main.js",
+ "prisma:generate": "prisma generate",
+ "prisma:migrate": "prisma migrate deploy",
+ "prisma:migrate:dev": "prisma migrate dev",
+ "prisma:seed": "tsx prisma/seed.ts"
+ },
+ "dependencies": {
+ "@fastify/cors": "^9.0.1",
+ "@fastify/jwt": "^8.0.1",
+ "@fastify/multipart": "^8.3.0",
+ "@fastify/swagger": "^8.15.0",
+ "@fastify/swagger-ui": "^4.1.0",
+ "@prisma/client": "^5.22.0",
+ "bcrypt": "^5.1.1",
+ "fastify": "^4.28.1",
+ "fastify-plugin": "^4.5.1",
+ "pino-pretty": "^11.2.2",
+ "zod": "^3.23.8"
+ },
+ "devDependencies": {
+ "@types/bcrypt": "^5.0.2",
+ "@types/node": "^20.14.11",
+ "prisma": "^5.22.0",
+ "tsx": "^4.19.2",
+ "typescript": "^5.5.4"
+ }
+}
diff --git a/server/prisma/schema.prisma b/server/prisma/schema.prisma
new file mode 100644
index 0000000..0ca0aac
--- /dev/null
+++ b/server/prisma/schema.prisma
@@ -0,0 +1,253 @@
+generator client {
+ provider = "prisma-client-js"
+}
+
+datasource db {
+ provider = "postgresql"
+ url = env("DATABASE_URL")
+}
+
+enum AuthProvider {
+ EMAIL
+ KAKAO
+ APPLE
+ NAVER
+ GOOGLE
+}
+
+model User {
+ id String @id @default(cuid())
+ email String? @unique
+ passwordHash String?
+ name String
+ phone String?
+ provider AuthProvider @default(EMAIL)
+ kakaoId String? @unique
+ appleSub String? @unique
+ naverId String? @unique
+ googleId String? @unique
+ profileImage String?
+ createdAt DateTime @default(now())
+ updatedAt DateTime @updatedAt
+
+ profile Profile?
+ family FamilyMember[]
+ policies Policy[]
+ claims Claim[]
+ notifications Notification[]
+ diagnoses Diagnosis[]
+ healthChecks HealthCheck[]
+ consults Consult[]
+}
+
+model Profile {
+ id String @id @default(cuid())
+ userId String @unique
+ age Int
+ gender Gender
+ job String?
+ monthlyPremium Int @default(0)
+ score Int @default(0)
+
+ user User @relation(fields: [userId], references: [id], onDelete: Cascade)
+}
+
+enum Gender {
+ MALE
+ FEMALE
+}
+
+enum Relation {
+ SELF
+ SPOUSE
+ CHILD
+ PARENT
+ SIBLING
+}
+
+model FamilyMember {
+ id String @id @default(cuid())
+ userId String
+ relation Relation
+ name String
+ age Int
+ gender Gender
+
+ user User @relation(fields: [userId], references: [id], onDelete: Cascade)
+ policies Policy[]
+
+ @@index([userId])
+}
+
+enum PolicyType {
+ SILSON
+ CANCER
+ LIFE
+ ACCIDENT
+ CHILD
+ NURSING
+ FEMALE
+ DENTAL
+ DRIVER
+ CAR
+}
+
+model Policy {
+ id String @id @default(cuid())
+ userId String
+ familyMemberId String?
+ name String
+ insurer String
+ type PolicyType
+ monthlyPremium Int
+ coverage BigInt
+ joinDate DateTime
+ maturityDate DateTime?
+ renewalDate DateTime?
+ silsonGen Int?
+ isGroup Boolean @default(false)
+ createdAt DateTime @default(now())
+ updatedAt DateTime @updatedAt
+
+ user User @relation(fields: [userId], references: [id], onDelete: Cascade)
+ familyMember FamilyMember? @relation(fields: [familyMemberId], references: [id], onDelete: SetNull)
+
+ @@index([userId])
+ @@index([familyMemberId])
+}
+
+enum ClaimStatus {
+ SUBMITTED
+ REVIEWING
+ ADDITIONAL_DOCS
+ APPROVED
+ PAID
+ REJECTED
+}
+
+model Claim {
+ id String @id @default(cuid())
+ userId String
+ title String
+ hospital String?
+ visitDate DateTime?
+ status ClaimStatus @default(SUBMITTED)
+ amount Int?
+ aiEstimated String?
+ createdAt DateTime @default(now())
+ updatedAt DateTime @updatedAt
+
+ user User @relation(fields: [userId], references: [id], onDelete: Cascade)
+ attachments ClaimAttachment[]
+ events ClaimEvent[]
+
+ @@index([userId, status])
+}
+
+enum AttachmentType {
+ RECEIPT
+ DIAGNOSIS
+ DETAIL
+ OTHER
+}
+
+model ClaimAttachment {
+ id String @id @default(cuid())
+ claimId String
+ type AttachmentType
+ objectKey String
+ mimeType String?
+ size Int?
+ createdAt DateTime @default(now())
+
+ claim Claim @relation(fields: [claimId], references: [id], onDelete: Cascade)
+
+ @@index([claimId])
+}
+
+model ClaimEvent {
+ id String @id @default(cuid())
+ claimId String
+ status ClaimStatus
+ note String?
+ createdAt DateTime @default(now())
+
+ claim Claim @relation(fields: [claimId], references: [id], onDelete: Cascade)
+
+ @@index([claimId])
+}
+
+enum NotificationTone {
+ INFO
+ WARN
+ DANGER
+}
+
+model Notification {
+ id String @id @default(cuid())
+ userId String
+ title String
+ body String
+ tone NotificationTone @default(INFO)
+ scheduled DateTime
+ readAt DateTime?
+ createdAt DateTime @default(now())
+
+ user User @relation(fields: [userId], references: [id], onDelete: Cascade)
+
+ @@index([userId, scheduled])
+}
+
+model Diagnosis {
+ id String @id @default(cuid())
+ userId String
+ answers Json
+ score Int
+ breakdown Json
+ createdAt DateTime @default(now())
+
+ user User @relation(fields: [userId], references: [id], onDelete: Cascade)
+
+ @@index([userId])
+}
+
+model HealthCheck {
+ id String @id @default(cuid())
+ userId String
+ date DateTime
+ metrics Json
+ summary String?
+ createdAt DateTime @default(now())
+
+ user User @relation(fields: [userId], references: [id], onDelete: Cascade)
+
+ @@index([userId, date])
+}
+
+enum ConsultMethod {
+ KAKAO
+ PHONE
+ VISIT
+}
+
+enum ConsultStatus {
+ REQUESTED
+ SCHEDULED
+ DONE
+ CANCELED
+}
+
+model Consult {
+ id String @id @default(cuid())
+ userId String
+ method ConsultMethod
+ phone String?
+ preferredAt DateTime?
+ memo String?
+ status ConsultStatus @default(REQUESTED)
+ createdAt DateTime @default(now())
+
+ user User @relation(fields: [userId], references: [id], onDelete: Cascade)
+
+ @@index([userId, status])
+}
diff --git a/server/src/app.ts b/server/src/app.ts
new file mode 100644
index 0000000..dda810d
--- /dev/null
+++ b/server/src/app.ts
@@ -0,0 +1,53 @@
+import Fastify from 'fastify';
+import cors from '@fastify/cors';
+import jwt from '@fastify/jwt';
+import multipart from '@fastify/multipart';
+import swagger from '@fastify/swagger';
+import swaggerUi from '@fastify/swagger-ui';
+
+import { prismaPlugin } from './plugins/prisma';
+import { authPlugin } from './plugins/auth';
+import { registerRoutes } from './routes';
+
+export async function buildApp() {
+ const app = Fastify({
+ logger: {
+ transport: process.env.NODE_ENV !== 'production' ? { target: 'pino-pretty', options: { colorize: true } } : undefined,
+ },
+ });
+
+ await app.register(cors, {
+ origin: (origin, cb) => cb(null, true),
+ credentials: true,
+ });
+
+ await app.register(jwt, {
+ secret: process.env.JWT_SECRET ?? 'dev-secret-change-me-in-prod',
+ sign: { expiresIn: '7d' },
+ });
+
+ await app.register(multipart, {
+ limits: { fileSize: 10 * 1024 * 1024 },
+ });
+
+ await app.register(swagger, {
+ openapi: {
+ info: { title: 'Insurance API', version: '1.0.0' },
+ components: {
+ securitySchemes: {
+ bearerAuth: { type: 'http', scheme: 'bearer', bearerFormat: 'JWT' },
+ },
+ },
+ },
+ });
+ await app.register(swaggerUi, { routePrefix: '/docs' });
+
+ await app.register(prismaPlugin);
+ await app.register(authPlugin);
+
+ app.get('/health', async () => ({ ok: true, ts: Date.now() }));
+
+ await registerRoutes(app);
+
+ return app;
+}
diff --git a/server/src/main.ts b/server/src/main.ts
new file mode 100644
index 0000000..c9f4ef8
--- /dev/null
+++ b/server/src/main.ts
@@ -0,0 +1,18 @@
+import 'dotenv/config';
+import { buildApp } from './app';
+
+const PORT = Number(process.env.PORT ?? 4000);
+const HOST = process.env.HOST ?? '0.0.0.0';
+
+async function main() {
+ const app = await buildApp();
+ try {
+ await app.listen({ port: PORT, host: HOST });
+ app.log.info(`API up on http://${HOST}:${PORT}`);
+ } catch (err) {
+ app.log.error(err);
+ process.exit(1);
+ }
+}
+
+main();
diff --git a/server/src/plugins/auth.ts b/server/src/plugins/auth.ts
new file mode 100644
index 0000000..4f24f82
--- /dev/null
+++ b/server/src/plugins/auth.ts
@@ -0,0 +1,25 @@
+import fp from 'fastify-plugin';
+import type { FastifyReply, FastifyRequest } from 'fastify';
+
+declare module 'fastify' {
+ interface FastifyInstance {
+ authenticate: (req: FastifyRequest, reply: FastifyReply) => Promise;
+ }
+}
+
+declare module '@fastify/jwt' {
+ interface FastifyJWT {
+ payload: { sub: string; email?: string };
+ user: { sub: string; email?: string };
+ }
+}
+
+export const authPlugin = fp(async (app) => {
+ app.decorate('authenticate', async function (req: FastifyRequest, reply: FastifyReply) {
+ try {
+ await req.jwtVerify();
+ } catch (err) {
+ reply.code(401).send({ message: 'μΈμ¦μ΄ νμν©λλ€' });
+ }
+ });
+});
diff --git a/server/src/plugins/prisma.ts b/server/src/plugins/prisma.ts
new file mode 100644
index 0000000..8dea372
--- /dev/null
+++ b/server/src/plugins/prisma.ts
@@ -0,0 +1,19 @@
+import fp from 'fastify-plugin';
+import { PrismaClient } from '@prisma/client';
+
+declare module 'fastify' {
+ interface FastifyInstance {
+ prisma: PrismaClient;
+ }
+}
+
+export const prismaPlugin = fp(async (app) => {
+ const prisma = new PrismaClient({
+ log: process.env.NODE_ENV === 'production' ? ['error'] : ['warn', 'error'],
+ });
+ await prisma.$connect();
+ app.decorate('prisma', prisma);
+ app.addHook('onClose', async () => {
+ await prisma.$disconnect();
+ });
+});
diff --git a/server/src/routes/auth.ts b/server/src/routes/auth.ts
new file mode 100644
index 0000000..b3d26fa
--- /dev/null
+++ b/server/src/routes/auth.ts
@@ -0,0 +1,159 @@
+import type { FastifyInstance } from 'fastify';
+import bcrypt from 'bcrypt';
+import { z } from 'zod';
+
+const RegisterBody = z.object({
+ email: z.string().email(),
+ password: z.string().min(8),
+ name: z.string().min(1),
+ phone: z.string().optional(),
+ age: z.number().int().min(0).max(120),
+ gender: z.enum(['MALE', 'FEMALE']),
+ job: z.string().optional(),
+});
+
+const LoginBody = z.object({
+ email: z.string().email(),
+ password: z.string().min(1),
+});
+
+const KakaoBody = z.object({
+ accessToken: z.string().min(10),
+});
+
+type KakaoMe = {
+ id: number;
+ kakao_account?: {
+ email?: string;
+ profile?: { nickname?: string; profile_image_url?: string };
+ phone_number?: string;
+ };
+ properties?: { nickname?: string; profile_image?: string };
+};
+
+async function fetchKakaoProfile(accessToken: string): Promise {
+ const res = await fetch('https://kapi.kakao.com/v2/user/me', {
+ method: 'GET',
+ headers: { Authorization: `Bearer ${accessToken}` },
+ });
+ if (!res.ok) throw new Error(`Kakao profile fetch failed: ${res.status}`);
+ return (await res.json()) as KakaoMe;
+}
+
+export async function authRoutes(app: FastifyInstance) {
+ app.post('/register', async (req, reply) => {
+ const body = RegisterBody.parse(req.body);
+ const exists = await app.prisma.user.findUnique({ where: { email: body.email } });
+ if (exists) return reply.code(409).send({ message: 'μ΄λ―Έ κ°μ
λ μ΄λ©μΌμ
λλ€' });
+
+ const passwordHash = await bcrypt.hash(body.password, 10);
+ const user = await app.prisma.user.create({
+ data: {
+ email: body.email,
+ passwordHash,
+ name: body.name,
+ phone: body.phone,
+ provider: 'EMAIL',
+ profile: {
+ create: {
+ age: body.age,
+ gender: body.gender,
+ job: body.job ?? 'κΈ°ν',
+ },
+ },
+ },
+ include: { profile: true },
+ });
+
+ const token = await reply.jwtSign({ sub: user.id, email: user.email ?? undefined });
+ return { token, user: publicUser(user) };
+ });
+
+ app.post('/login', async (req, reply) => {
+ const body = LoginBody.parse(req.body);
+ const user = await app.prisma.user.findUnique({
+ where: { email: body.email },
+ include: { profile: true },
+ });
+ if (!user || !user.passwordHash) return reply.code(401).send({ message: 'μ΄λ©μΌ λλ λΉλ°λ²νΈκ° νλ Έμ΅λλ€' });
+
+ const ok = await bcrypt.compare(body.password, user.passwordHash);
+ if (!ok) return reply.code(401).send({ message: 'μ΄λ©μΌ λλ λΉλ°λ²νΈκ° νλ Έμ΅λλ€' });
+
+ const token = await reply.jwtSign({ sub: user.id, email: user.email ?? undefined });
+ return { token, user: publicUser(user) };
+ });
+
+ app.post('/kakao', async (req, reply) => {
+ const body = KakaoBody.parse(req.body);
+ let me: KakaoMe;
+ try {
+ me = await fetchKakaoProfile(body.accessToken);
+ } catch (e) {
+ return reply.code(401).send({ message: 'μΉ΄μΉ΄μ€ μΈμ¦ μ€ν¨' });
+ }
+
+ const kakaoId = String(me.id);
+ const email = me.kakao_account?.email;
+ const name = me.kakao_account?.profile?.nickname ?? me.properties?.nickname ?? 'μΉ΄μΉ΄μ€μ¬μ©μ';
+ const profileImage = me.kakao_account?.profile?.profile_image_url ?? me.properties?.profile_image;
+
+ let user = await app.prisma.user.findUnique({
+ where: { kakaoId },
+ include: { profile: true },
+ });
+
+ if (!user) {
+ user = await app.prisma.user.create({
+ data: {
+ kakaoId,
+ email: email ?? null,
+ name,
+ phone: me.kakao_account?.phone_number,
+ provider: 'KAKAO',
+ profileImage,
+ profile: {
+ create: {
+ age: 30,
+ gender: 'MALE',
+ job: 'κΈ°ν',
+ },
+ },
+ },
+ include: { profile: true },
+ });
+ }
+
+ const token = await reply.jwtSign({ sub: user.id, email: user.email ?? undefined });
+ return { token, user: publicUser(user) };
+ });
+
+ app.get('/me', { onRequest: [app.authenticate] }, async (req) => {
+ const user = await app.prisma.user.findUnique({
+ where: { id: req.user.sub },
+ include: { profile: true },
+ });
+ if (!user) throw new Error('User not found');
+ return publicUser(user);
+ });
+}
+
+function publicUser(u: any) {
+ return {
+ id: u.id,
+ email: u.email,
+ name: u.name,
+ phone: u.phone,
+ provider: u.provider,
+ profileImage: u.profileImage,
+ profile: u.profile
+ ? {
+ age: u.profile.age,
+ gender: u.profile.gender,
+ job: u.profile.job,
+ monthlyPremium: u.profile.monthlyPremium,
+ score: u.profile.score,
+ }
+ : null,
+ };
+}
diff --git a/server/src/routes/claims.ts b/server/src/routes/claims.ts
new file mode 100644
index 0000000..e3dbd89
--- /dev/null
+++ b/server/src/routes/claims.ts
@@ -0,0 +1,95 @@
+import type { FastifyInstance } from 'fastify';
+import { z } from 'zod';
+import fs from 'node:fs/promises';
+import path from 'node:path';
+import crypto from 'node:crypto';
+
+const CreateClaim = z.object({
+ title: z.string().min(1),
+ hospital: z.string().optional(),
+ visitDate: z.string().optional(),
+});
+
+const UPLOAD_DIR = process.env.UPLOAD_DIR ?? './uploads';
+
+export async function claimRoutes(app: FastifyInstance) {
+ app.addHook('onRequest', app.authenticate);
+
+ await fs.mkdir(UPLOAD_DIR, { recursive: true });
+
+ app.get('/', async (req) => {
+ const claims = await app.prisma.claim.findMany({
+ where: { userId: req.user.sub },
+ include: { attachments: true, events: { orderBy: { createdAt: 'desc' } } },
+ orderBy: { createdAt: 'desc' },
+ });
+ return claims;
+ });
+
+ app.post('/', async (req, reply) => {
+ const body = CreateClaim.parse(req.body);
+ const claim = await app.prisma.claim.create({
+ data: {
+ userId: req.user.sub,
+ title: body.title,
+ hospital: body.hospital,
+ visitDate: body.visitDate ? new Date(body.visitDate) : null,
+ status: 'SUBMITTED',
+ events: {
+ create: { status: 'SUBMITTED', note: 'μ²κ΅¬ μ μ' },
+ },
+ },
+ include: { attachments: true, events: true },
+ });
+ reply.code(201);
+ return claim;
+ });
+
+ app.post('/:id/attachments', async (req, reply) => {
+ const { id } = req.params as { id: string };
+ const existing = await app.prisma.claim.findFirst({ where: { id, userId: req.user.sub } });
+ if (!existing) return reply.code(404).send({ message: 'Not found' });
+
+ const file = await req.file();
+ if (!file) return reply.code(400).send({ message: 'File required' });
+
+ const type = String((file.fields as any)?.type?.value ?? 'OTHER').toUpperCase();
+ const ext = path.extname(file.filename) || '';
+ const key = `${crypto.randomUUID()}${ext}`;
+ const abs = path.join(UPLOAD_DIR, key);
+ const buffer = await file.toBuffer();
+ await fs.writeFile(abs, buffer);
+
+ const attachment = await app.prisma.claimAttachment.create({
+ data: {
+ claimId: id,
+ type: (['RECEIPT', 'DIAGNOSIS', 'DETAIL', 'OTHER'].includes(type) ? type : 'OTHER') as any,
+ objectKey: key,
+ mimeType: file.mimetype,
+ size: buffer.length,
+ },
+ });
+ reply.code(201);
+ return attachment;
+ });
+
+ app.patch('/:id/status', async (req, reply) => {
+ const { id } = req.params as { id: string };
+ const { status, note, amount } = (req.body ?? {}) as { status?: string; note?: string; amount?: number };
+ if (!status) return reply.code(400).send({ message: 'status required' });
+
+ const existing = await app.prisma.claim.findFirst({ where: { id, userId: req.user.sub } });
+ if (!existing) return reply.code(404).send({ message: 'Not found' });
+
+ const updated = await app.prisma.claim.update({
+ where: { id },
+ data: {
+ status: status as any,
+ ...(amount !== undefined && { amount }),
+ events: { create: { status: status as any, note } },
+ },
+ include: { events: { orderBy: { createdAt: 'desc' } } },
+ });
+ return updated;
+ });
+}
diff --git a/server/src/routes/consults.ts b/server/src/routes/consults.ts
new file mode 100644
index 0000000..8e449b2
--- /dev/null
+++ b/server/src/routes/consults.ts
@@ -0,0 +1,35 @@
+import type { FastifyInstance } from 'fastify';
+import { z } from 'zod';
+
+const CreateConsult = z.object({
+ method: z.enum(['KAKAO', 'PHONE', 'VISIT']),
+ phone: z.string().optional(),
+ preferredAt: z.string().optional(),
+ memo: z.string().optional(),
+});
+
+export async function consultRoutes(app: FastifyInstance) {
+ app.addHook('onRequest', app.authenticate);
+
+ app.get('/', async (req) => {
+ return app.prisma.consult.findMany({
+ where: { userId: req.user.sub },
+ orderBy: { createdAt: 'desc' },
+ });
+ });
+
+ app.post('/', async (req, reply) => {
+ const body = CreateConsult.parse(req.body);
+ const c = await app.prisma.consult.create({
+ data: {
+ userId: req.user.sub,
+ method: body.method,
+ phone: body.phone,
+ preferredAt: body.preferredAt ? new Date(body.preferredAt) : null,
+ memo: body.memo,
+ },
+ });
+ reply.code(201);
+ return c;
+ });
+}
diff --git a/server/src/routes/diagnosis.ts b/server/src/routes/diagnosis.ts
new file mode 100644
index 0000000..9801d61
--- /dev/null
+++ b/server/src/routes/diagnosis.ts
@@ -0,0 +1,61 @@
+import type { FastifyInstance } from 'fastify';
+import { z } from 'zod';
+import { computeScore } from './score';
+
+const DiagnosisBody = z.object({
+ answers: z.record(z.string(), z.string()),
+});
+
+export async function diagnosisRoutes(app: FastifyInstance) {
+ app.addHook('onRequest', app.authenticate);
+
+ app.get('/', async (req) => {
+ return app.prisma.diagnosis.findMany({
+ where: { userId: req.user.sub },
+ orderBy: { createdAt: 'desc' },
+ take: 10,
+ });
+ });
+
+ app.post('/', async (req, reply) => {
+ const body = DiagnosisBody.parse(req.body);
+ const score = await computeScore(app, req.user.sub);
+ const recommendations = recommend(body.answers, score.breakdown);
+
+ const saved = await app.prisma.diagnosis.create({
+ data: {
+ userId: req.user.sub,
+ answers: body.answers,
+ score: score.total,
+ breakdown: { categories: score.breakdown, recommendations },
+ },
+ });
+ reply.code(201);
+ return saved;
+ });
+}
+
+function recommend(answers: Record, breakdown: Array<{ label: string; status: string }>) {
+ const recs: Array<{ name: string; reason: string; priority: 'νμ' | 'κΆμ₯' | 'μ ν' }> = [];
+
+ const missing = breakdown.filter((b) => b.status === 'none' || b.status === 'bad');
+ missing.forEach((m) => {
+ recs.push({
+ name: m.label,
+ reason: `νμ¬ ${m.status === 'none' ? 'λ―Έκ°μ
' : '보μ₯ λΆμ‘±'}`,
+ priority: m.label === 'μ€μ보ν' ? 'νμ' : 'κΆμ₯',
+ });
+ });
+
+ if (answers.family === 'μμ') {
+ recs.push({ name: 'μ보ν μ§λ¨λΉ κ°ν', reason: 'κ°μ‘±λ ₯ μμ', priority: 'νμ' });
+ }
+ if (answers.smoke === 'ν‘μ°') {
+ recs.push({ name: 'λνκ΄/μ¬μ₯ νΉμ½', reason: 'ν‘μ°μ κ³ μνκ΅°', priority: 'κΆμ₯' });
+ }
+ if (answers.hospital === '3ν μ΄μ') {
+ recs.push({ name: 'μ
μμΌλΉ νΉμ½', reason: 'μ¦μ μ
μ μ΄λ ₯', priority: 'κΆμ₯' });
+ }
+
+ return recs.slice(0, 6);
+}
diff --git a/server/src/routes/family.ts b/server/src/routes/family.ts
new file mode 100644
index 0000000..1644ecc
--- /dev/null
+++ b/server/src/routes/family.ts
@@ -0,0 +1,56 @@
+import type { FastifyInstance } from 'fastify';
+import { z } from 'zod';
+
+const CreateFamily = z.object({
+ relation: z.enum(['SELF', 'SPOUSE', 'CHILD', 'PARENT', 'SIBLING']),
+ name: z.string().min(1),
+ age: z.number().int().min(0).max(120),
+ gender: z.enum(['MALE', 'FEMALE']),
+});
+
+const UpdateFamily = CreateFamily.partial();
+
+export async function familyRoutes(app: FastifyInstance) {
+ app.addHook('onRequest', app.authenticate);
+
+ app.get('/', async (req) => {
+ const members = await app.prisma.familyMember.findMany({
+ where: { userId: req.user.sub },
+ include: { policies: true },
+ orderBy: { relation: 'asc' },
+ });
+ return members.map(sanitize);
+ });
+
+ app.post('/', async (req, reply) => {
+ const body = CreateFamily.parse(req.body);
+ const created = await app.prisma.familyMember.create({
+ data: { ...body, userId: req.user.sub },
+ });
+ reply.code(201);
+ return created;
+ });
+
+ app.patch('/:id', async (req, reply) => {
+ const { id } = req.params as { id: string };
+ const body = UpdateFamily.parse(req.body);
+ const existing = await app.prisma.familyMember.findFirst({ where: { id, userId: req.user.sub } });
+ if (!existing) return reply.code(404).send({ message: 'Not found' });
+ return app.prisma.familyMember.update({ where: { id }, data: body });
+ });
+
+ app.delete('/:id', async (req, reply) => {
+ const { id } = req.params as { id: string };
+ const existing = await app.prisma.familyMember.findFirst({ where: { id, userId: req.user.sub } });
+ if (!existing) return reply.code(404).send({ message: 'Not found' });
+ await app.prisma.familyMember.delete({ where: { id } });
+ return { ok: true };
+ });
+}
+
+function sanitize(m: any) {
+ return {
+ ...m,
+ policies: m.policies.map((p: any) => ({ ...p, coverage: Number(p.coverage) })),
+ };
+}
diff --git a/server/src/routes/index.ts b/server/src/routes/index.ts
new file mode 100644
index 0000000..32f23a7
--- /dev/null
+++ b/server/src/routes/index.ts
@@ -0,0 +1,22 @@
+import type { FastifyInstance } from 'fastify';
+import { authRoutes } from './auth';
+import { userRoutes } from './users';
+import { familyRoutes } from './family';
+import { policyRoutes } from './policies';
+import { claimRoutes } from './claims';
+import { scoreRoutes } from './score';
+import { notificationRoutes } from './notifications';
+import { diagnosisRoutes } from './diagnosis';
+import { consultRoutes } from './consults';
+
+export async function registerRoutes(app: FastifyInstance) {
+ await app.register(authRoutes, { prefix: '/auth' });
+ await app.register(userRoutes, { prefix: '/users' });
+ await app.register(familyRoutes, { prefix: '/family' });
+ await app.register(policyRoutes, { prefix: '/policies' });
+ await app.register(claimRoutes, { prefix: '/claims' });
+ await app.register(scoreRoutes, { prefix: '/score' });
+ await app.register(notificationRoutes, { prefix: '/notifications' });
+ await app.register(diagnosisRoutes, { prefix: '/diagnosis' });
+ await app.register(consultRoutes, { prefix: '/consults' });
+}
diff --git a/server/src/routes/notifications.ts b/server/src/routes/notifications.ts
new file mode 100644
index 0000000..4ff8b06
--- /dev/null
+++ b/server/src/routes/notifications.ts
@@ -0,0 +1,58 @@
+import type { FastifyInstance } from 'fastify';
+import { z } from 'zod';
+
+const CreateNotification = z.object({
+ title: z.string(),
+ body: z.string(),
+ tone: z.enum(['INFO', 'WARN', 'DANGER']).optional(),
+ scheduled: z.string(),
+});
+
+export async function notificationRoutes(app: FastifyInstance) {
+ app.addHook('onRequest', app.authenticate);
+
+ app.get('/', async (req) => {
+ return app.prisma.notification.findMany({
+ where: { userId: req.user.sub },
+ orderBy: { scheduled: 'asc' },
+ });
+ });
+
+ app.post('/', async (req, reply) => {
+ const body = CreateNotification.parse(req.body);
+ const created = await app.prisma.notification.create({
+ data: {
+ userId: req.user.sub,
+ title: body.title,
+ body: body.body,
+ tone: body.tone ?? 'INFO',
+ scheduled: new Date(body.scheduled),
+ },
+ });
+ reply.code(201);
+ return created;
+ });
+
+ app.patch('/:id/read', async (req, reply) => {
+ const { id } = req.params as { id: string };
+ const existing = await app.prisma.notification.findFirst({ where: { id, userId: req.user.sub } });
+ if (!existing) return reply.code(404).send({ message: 'Not found' });
+ return app.prisma.notification.update({ where: { id }, data: { readAt: new Date() } });
+ });
+
+ app.get('/upcoming', async (req) => {
+ const policies = await app.prisma.policy.findMany({
+ where: { userId: req.user.sub, renewalDate: { not: null } },
+ });
+ const now = new Date();
+ return policies
+ .filter((p) => p.renewalDate && p.renewalDate > now)
+ .map((p) => ({
+ policyId: p.id,
+ title: `${p.name} κ°±μ `,
+ scheduled: p.renewalDate,
+ days: Math.ceil(((p.renewalDate as Date).getTime() - now.getTime()) / (1000 * 60 * 60 * 24)),
+ }))
+ .sort((a, b) => (a.days ?? 0) - (b.days ?? 0));
+ });
+}
diff --git a/server/src/routes/policies.ts b/server/src/routes/policies.ts
new file mode 100644
index 0000000..a78af0e
--- /dev/null
+++ b/server/src/routes/policies.ts
@@ -0,0 +1,112 @@
+import type { FastifyInstance } from 'fastify';
+import { z } from 'zod';
+
+const PolicyType = z.enum([
+ 'SILSON',
+ 'CANCER',
+ 'LIFE',
+ 'ACCIDENT',
+ 'CHILD',
+ 'NURSING',
+ 'FEMALE',
+ 'DENTAL',
+ 'DRIVER',
+ 'CAR',
+]);
+
+const CreatePolicy = z.object({
+ name: z.string().min(1),
+ insurer: z.string().min(1),
+ type: PolicyType,
+ monthlyPremium: z.number().int().min(0),
+ coverage: z.number().int().min(0),
+ joinDate: z.string().datetime().or(z.string()),
+ maturityDate: z.string().datetime().or(z.string()).optional(),
+ renewalDate: z.string().datetime().or(z.string()).optional(),
+ silsonGen: z.number().int().min(1).max(5).optional(),
+ isGroup: z.boolean().optional(),
+ familyMemberId: z.string().optional(),
+});
+
+const UpdatePolicy = CreatePolicy.partial();
+
+export async function policyRoutes(app: FastifyInstance) {
+ app.addHook('onRequest', app.authenticate);
+
+ app.get('/', async (req) => {
+ const { familyMemberId } = (req.query ?? {}) as { familyMemberId?: string };
+ const policies = await app.prisma.policy.findMany({
+ where: { userId: req.user.sub, ...(familyMemberId ? { familyMemberId } : {}) },
+ orderBy: { createdAt: 'desc' },
+ });
+ return policies.map(sanitize);
+ });
+
+ app.post('/', async (req, reply) => {
+ const body = CreatePolicy.parse(req.body);
+ const created = await app.prisma.policy.create({
+ data: {
+ userId: req.user.sub,
+ name: body.name,
+ insurer: body.insurer,
+ type: body.type,
+ monthlyPremium: body.monthlyPremium,
+ coverage: BigInt(body.coverage),
+ joinDate: new Date(body.joinDate),
+ maturityDate: body.maturityDate ? new Date(body.maturityDate) : null,
+ renewalDate: body.renewalDate ? new Date(body.renewalDate) : null,
+ silsonGen: body.silsonGen,
+ isGroup: body.isGroup ?? false,
+ familyMemberId: body.familyMemberId,
+ },
+ });
+
+ await recomputeProfilePremium(app, req.user.sub);
+
+ reply.code(201);
+ return sanitize(created);
+ });
+
+ app.patch('/:id', async (req, reply) => {
+ const { id } = req.params as { id: string };
+ const body = UpdatePolicy.parse(req.body);
+ const existing = await app.prisma.policy.findFirst({ where: { id, userId: req.user.sub } });
+ if (!existing) return reply.code(404).send({ message: 'Not found' });
+
+ const updated = await app.prisma.policy.update({
+ where: { id },
+ data: {
+ ...body,
+ ...(body.coverage !== undefined && { coverage: BigInt(body.coverage) }),
+ ...(body.joinDate !== undefined && { joinDate: new Date(body.joinDate) }),
+ ...(body.maturityDate !== undefined && { maturityDate: body.maturityDate ? new Date(body.maturityDate) : null }),
+ ...(body.renewalDate !== undefined && { renewalDate: body.renewalDate ? new Date(body.renewalDate) : null }),
+ },
+ });
+
+ await recomputeProfilePremium(app, req.user.sub);
+
+ return sanitize(updated);
+ });
+
+ app.delete('/:id', async (req, reply) => {
+ const { id } = req.params as { id: string };
+ const existing = await app.prisma.policy.findFirst({ where: { id, userId: req.user.sub } });
+ if (!existing) return reply.code(404).send({ message: 'Not found' });
+ await app.prisma.policy.delete({ where: { id } });
+
+ await recomputeProfilePremium(app, req.user.sub);
+
+ return { ok: true };
+ });
+}
+
+function sanitize(p: any) {
+ return { ...p, coverage: Number(p.coverage) };
+}
+
+async function recomputeProfilePremium(app: FastifyInstance, userId: string) {
+ const own = await app.prisma.policy.findMany({ where: { userId, familyMemberId: null } });
+ const total = own.reduce((a, p) => a + p.monthlyPremium, 0);
+ await app.prisma.profile.update({ where: { userId }, data: { monthlyPremium: total } });
+}
diff --git a/server/src/routes/score.ts b/server/src/routes/score.ts
new file mode 100644
index 0000000..34b72b1
--- /dev/null
+++ b/server/src/routes/score.ts
@@ -0,0 +1,79 @@
+import type { FastifyInstance } from 'fastify';
+
+export async function scoreRoutes(app: FastifyInstance) {
+ app.addHook('onRequest', app.authenticate);
+
+ app.get('/', async (req) => {
+ return computeScore(app, req.user.sub);
+ });
+
+ app.post('/recompute', async (req) => {
+ const result = await computeScore(app, req.user.sub);
+ await app.prisma.profile.update({
+ where: { userId: req.user.sub },
+ data: { score: result.total },
+ });
+ return result;
+ });
+}
+
+type Category = 'μ€μ보ν' | 'μ보ν' | 'μν΄λ³΄ν' | 'μ’
μ 보ν' | 'κ°λ³λ³΄ν' | 'μΉμ보ν';
+type Status = 'good' | 'warn' | 'bad' | 'none';
+
+const categoryMap: Record = {
+ SILSON: 'μ€μ보ν',
+ CANCER: 'μ보ν',
+ ACCIDENT: 'μν΄λ³΄ν',
+ LIFE: 'μ’
μ 보ν',
+ NURSING: 'κ°λ³λ³΄ν',
+ DENTAL: 'μΉμ보ν',
+};
+
+const weights: Record = {
+ μ€μ보ν: 25,
+ μ보ν: 20,
+ μν΄λ³΄ν: 15,
+ μ’
μ 보ν: 15,
+ κ°λ³λ³΄ν: 15,
+ μΉμ보ν: 10,
+};
+
+const minCoverageByCategory: Record = {
+ μ€μ보ν: 10_000_000,
+ μ보ν: 30_000_000,
+ μν΄λ³΄ν: 10_000_000,
+ μ’
μ 보ν: 30_000_000,
+ κ°λ³λ³΄ν: 20_000_000,
+ μΉμ보ν: 3_000_000,
+};
+
+export async function computeScore(app: FastifyInstance, userId: string) {
+ const profile = await app.prisma.profile.findUnique({ where: { userId } });
+ const policies = await app.prisma.policy.findMany({ where: { userId, familyMemberId: null } });
+
+ const categories = Object.keys(weights) as Category[];
+ const breakdown = categories.map((cat) => {
+ const mine = policies.filter((p) => categoryMap[p.type] === cat);
+ if (mine.length === 0) {
+ return { label: cat, value: 0, status: 'none' as Status };
+ }
+ const maxCoverage = Math.max(...mine.map((p) => Number(p.coverage)));
+ const minReq = minCoverageByCategory[cat];
+ const ratio = Math.min(1.5, maxCoverage / minReq);
+ const value = Math.round(Math.min(100, ratio * 85 + mine.length * 5));
+ const status: Status = value >= 80 ? 'good' : value >= 60 ? 'warn' : 'bad';
+ return { label: cat, value, status };
+ });
+
+ const total = Math.round(
+ breakdown.reduce((a, b) => a + (b.value * weights[b.label as Category]) / 100, 0)
+ );
+
+ // λμ΄ κΈ°λ° κ°λ³λ³΄ν νμμ± κ°μ€μΉ
+ let adjustedTotal = total;
+ if (profile && profile.age >= 50 && breakdown.find((b) => b.label === 'κ°λ³λ³΄ν')?.status === 'none') {
+ adjustedTotal = Math.max(0, total - 10);
+ }
+
+ return { total: Math.min(100, Math.max(0, adjustedTotal)), breakdown };
+}
diff --git a/server/src/routes/users.ts b/server/src/routes/users.ts
new file mode 100644
index 0000000..441e1c6
--- /dev/null
+++ b/server/src/routes/users.ts
@@ -0,0 +1,36 @@
+import type { FastifyInstance } from 'fastify';
+import { z } from 'zod';
+
+const UpdateProfile = z.object({
+ age: z.number().int().min(0).max(120).optional(),
+ gender: z.enum(['MALE', 'FEMALE']).optional(),
+ job: z.string().optional(),
+ phone: z.string().optional(),
+ name: z.string().optional(),
+});
+
+export async function userRoutes(app: FastifyInstance) {
+ app.patch('/me', { onRequest: [app.authenticate] }, async (req) => {
+ const body = UpdateProfile.parse(req.body);
+ const userId = req.user.sub;
+
+ const { name, phone, ...profileFields } = body;
+
+ await app.prisma.$transaction(async (tx) => {
+ if (name !== undefined || phone !== undefined) {
+ await tx.user.update({
+ where: { id: userId },
+ data: { ...(name !== undefined && { name }), ...(phone !== undefined && { phone }) },
+ });
+ }
+ if (Object.keys(profileFields).length > 0) {
+ await tx.profile.update({
+ where: { userId },
+ data: profileFields,
+ });
+ }
+ });
+
+ return { ok: true };
+ });
+}
diff --git a/server/tsconfig.json b/server/tsconfig.json
new file mode 100644
index 0000000..835bdfa
--- /dev/null
+++ b/server/tsconfig.json
@@ -0,0 +1,22 @@
+{
+ "compilerOptions": {
+ "target": "ES2022",
+ "module": "commonjs",
+ "lib": ["ES2022"],
+ "outDir": "dist",
+ "rootDir": "src",
+ "strict": true,
+ "esModuleInterop": true,
+ "skipLibCheck": true,
+ "forceConsistentCasingInFileNames": true,
+ "resolveJsonModule": true,
+ "declaration": false,
+ "sourceMap": true,
+ "baseUrl": ".",
+ "paths": {
+ "@/*": ["src/*"]
+ }
+ },
+ "include": ["src/**/*"],
+ "exclude": ["node_modules", "dist"]
+}
diff --git a/src/api/client.ts b/src/api/client.ts
new file mode 100644
index 0000000..9803f2d
--- /dev/null
+++ b/src/api/client.ts
@@ -0,0 +1,92 @@
+import AsyncStorage from '@react-native-async-storage/async-storage';
+import { Platform } from 'react-native';
+
+const defaultBase =
+ Platform.OS === 'web'
+ ? (typeof window !== 'undefined' && (window as any).__API_BASE__) || 'http://localhost:4000'
+ : 'http://10.0.2.2:4000';
+
+export const API_BASE = (process.env.EXPO_PUBLIC_API_BASE as string) || defaultBase;
+
+const TOKEN_KEY = 'insurance.token';
+
+let memoryToken: string | null = null;
+
+export async function loadToken() {
+ if (memoryToken) return memoryToken;
+ try {
+ memoryToken = await AsyncStorage.getItem(TOKEN_KEY);
+ } catch {
+ memoryToken = null;
+ }
+ return memoryToken;
+}
+export async function saveToken(t: string) {
+ memoryToken = t;
+ try {
+ await AsyncStorage.setItem(TOKEN_KEY, t);
+ } catch {}
+}
+export async function clearToken() {
+ memoryToken = null;
+ try {
+ await AsyncStorage.removeItem(TOKEN_KEY);
+ } catch {}
+}
+
+export class ApiError extends Error {
+ constructor(public status: number, public payload: any, message: string) {
+ super(message);
+ }
+}
+
+type Options = {
+ method?: 'GET' | 'POST' | 'PATCH' | 'DELETE';
+ body?: any;
+ query?: Record;
+ multipart?: boolean;
+ skipAuth?: boolean;
+};
+
+export async function api(path: string, opts: Options = {}): Promise {
+ const { method = 'GET', body, query, multipart, skipAuth } = opts;
+
+ let url = `${API_BASE}${path}`;
+ if (query) {
+ const qs = new URLSearchParams();
+ Object.entries(query).forEach(([k, v]) => v !== undefined && qs.append(k, String(v)));
+ const q = qs.toString();
+ if (q) url += `?${q}`;
+ }
+
+ const headers: Record = {};
+ if (!multipart) headers['Content-Type'] = 'application/json';
+
+ if (!skipAuth) {
+ const tok = await loadToken();
+ if (tok) headers['Authorization'] = `Bearer ${tok}`;
+ }
+
+ const res = await fetch(url, {
+ method,
+ headers,
+ body: multipart ? body : body !== undefined ? JSON.stringify(body) : undefined,
+ });
+
+ const text = await res.text();
+ const payload = text ? safeParse(text) : null;
+
+ if (!res.ok) {
+ const msg = payload?.message ?? `HTTP ${res.status}`;
+ throw new ApiError(res.status, payload, msg);
+ }
+ return payload as T;
+}
+
+function safeParse(t: string) {
+ try {
+ return JSON.parse(t);
+ } catch {
+ return t;
+ }
+}
diff --git a/src/api/endpoints.ts b/src/api/endpoints.ts
new file mode 100644
index 0000000..de94fc3
--- /dev/null
+++ b/src/api/endpoints.ts
@@ -0,0 +1,128 @@
+import { api } from './client';
+
+export type User = {
+ id: string;
+ email: string | null;
+ name: string;
+ phone: string | null;
+ provider: 'EMAIL' | 'KAKAO' | 'APPLE' | 'NAVER' | 'GOOGLE';
+ profileImage: string | null;
+ profile: {
+ age: number;
+ gender: 'MALE' | 'FEMALE';
+ job: string;
+ monthlyPremium: number;
+ score: number;
+ } | null;
+};
+
+export type AuthResponse = { token: string; user: User };
+
+export const authApi = {
+ register: (body: {
+ email: string;
+ password: string;
+ name: string;
+ age: number;
+ gender: 'MALE' | 'FEMALE';
+ job?: string;
+ phone?: string;
+ }) => api('/auth/register', { method: 'POST', body, skipAuth: true }),
+
+ login: (email: string, password: string) =>
+ api('/auth/login', { method: 'POST', body: { email, password }, skipAuth: true }),
+
+ kakao: (accessToken: string) =>
+ api('/auth/kakao', { method: 'POST', body: { accessToken }, skipAuth: true }),
+
+ me: () => api('/auth/me'),
+};
+
+export type Policy = {
+ id: string;
+ name: string;
+ insurer: string;
+ type: string;
+ monthlyPremium: number;
+ coverage: number;
+ joinDate: string;
+ maturityDate: string | null;
+ renewalDate: string | null;
+ silsonGen: number | null;
+ isGroup: boolean;
+ familyMemberId: string | null;
+};
+
+export const policyApi = {
+ list: () => api('/policies'),
+ create: (body: Partial) => api('/policies', { method: 'POST', body }),
+ update: (id: string, body: Partial) => api(`/policies/${id}`, { method: 'PATCH', body }),
+ remove: (id: string) => api(`/policies/${id}`, { method: 'DELETE' }),
+};
+
+export type FamilyMember = {
+ id: string;
+ relation: 'SELF' | 'SPOUSE' | 'CHILD' | 'PARENT' | 'SIBLING';
+ name: string;
+ age: number;
+ gender: 'MALE' | 'FEMALE';
+ policies: Policy[];
+};
+
+export const familyApi = {
+ list: () => api('/family'),
+ create: (body: Omit) => api('/family', { method: 'POST', body }),
+ update: (id: string, body: Partial>) =>
+ api(`/family/${id}`, { method: 'PATCH', body }),
+ remove: (id: string) => api(`/family/${id}`, { method: 'DELETE' }),
+};
+
+export type Claim = {
+ id: string;
+ title: string;
+ hospital: string | null;
+ visitDate: string | null;
+ status: 'SUBMITTED' | 'REVIEWING' | 'ADDITIONAL_DOCS' | 'APPROVED' | 'PAID' | 'REJECTED';
+ amount: number | null;
+ attachments: Array<{ id: string; type: string; objectKey: string }>;
+ events: Array<{ id: string; status: string; note: string | null; createdAt: string }>;
+ createdAt: string;
+};
+
+export const claimApi = {
+ list: () => api('/claims'),
+ create: (body: { title: string; hospital?: string; visitDate?: string }) =>
+ api('/claims', { method: 'POST', body }),
+ uploadAttachment: async (claimId: string, fileUri: string, type: 'RECEIPT' | 'DIAGNOSIS' | 'DETAIL') => {
+ const form = new FormData();
+ form.append('type', type);
+ // @ts-expect-error RN FormData file shape
+ form.append('file', { uri: fileUri, name: fileUri.split('/').pop(), type: 'image/jpeg' });
+ return api(`/claims/${claimId}/attachments`, { method: 'POST', body: form as any, multipart: true });
+ },
+};
+
+export const scoreApi = {
+ get: () => api<{ total: number; breakdown: Array<{ label: string; value: number; status: string }> }>('/score'),
+ recompute: () =>
+ api<{ total: number; breakdown: Array<{ label: string; value: number; status: string }> }>('/score/recompute', {
+ method: 'POST',
+ }),
+};
+
+export const notificationApi = {
+ list: () => api('/notifications'),
+ upcoming: () => api('/notifications/upcoming'),
+ markRead: (id: string) => api(`/notifications/${id}/read`, { method: 'PATCH' }),
+};
+
+export const diagnosisApi = {
+ list: () => api('/diagnosis'),
+ submit: (answers: Record) => api('/diagnosis', { method: 'POST', body: { answers } }),
+};
+
+export const consultApi = {
+ list: () => api('/consults'),
+ create: (body: { method: 'KAKAO' | 'PHONE' | 'VISIT'; phone?: string; preferredAt?: string; memo?: string }) =>
+ api('/consults', { method: 'POST', body }),
+};
diff --git a/src/components/BarChartSimple.tsx b/src/components/BarChartSimple.tsx
new file mode 100644
index 0000000..ba28a15
--- /dev/null
+++ b/src/components/BarChartSimple.tsx
@@ -0,0 +1,46 @@
+import React from 'react';
+import { View, Text, StyleSheet } from 'react-native';
+import { colors } from '@/theme/colors';
+import { typography } from '@/theme/typography';
+
+type Props = {
+ data: Array<{ label: string; value: number }>;
+ unit?: string;
+ barColor?: string;
+ height?: number;
+};
+
+export default function BarChartSimple({ data, unit = '', barColor = colors.primary, height = 180 }: Props) {
+ const max = Math.max(...data.map((d) => d.value), 1);
+ return (
+
+
+ {data.map((d, i) => {
+ const pct = (d.value / max) * 100;
+ return (
+
+
+ {d.value}
+ {unit}
+
+
+
+
+ {d.label}
+
+ );
+ })}
+
+
+ );
+}
+
+const styles = StyleSheet.create({
+ wrap: { width: '100%' },
+ bars: { flexDirection: 'row', alignItems: 'flex-end', justifyContent: 'space-between' },
+ barCol: { flex: 1, alignItems: 'center', justifyContent: 'flex-end', paddingHorizontal: 4 },
+ barTrack: { width: '70%', height: '70%', justifyContent: 'flex-end', marginVertical: 4 },
+ bar: { width: '100%', borderTopLeftRadius: 6, borderTopRightRadius: 6, minHeight: 4 },
+ value: { ...typography.small, color: colors.textSecondary, fontWeight: '600' },
+ label: { ...typography.small, color: colors.text, fontWeight: '600' },
+});
diff --git a/src/components/ErrorBoundary.tsx b/src/components/ErrorBoundary.tsx
new file mode 100644
index 0000000..e919c57
--- /dev/null
+++ b/src/components/ErrorBoundary.tsx
@@ -0,0 +1,27 @@
+import React from 'react';
+import { View, Text, ScrollView } from 'react-native';
+
+type Props = { children: React.ReactNode };
+type State = { error: Error | null };
+
+export default class ErrorBoundary extends React.Component {
+ state: State = { error: null };
+ static getDerivedStateFromError(error: Error) {
+ return { error };
+ }
+ componentDidCatch(error: Error, info: React.ErrorInfo) {
+ console.error('[ErrorBoundary]', error, info);
+ }
+ render() {
+ if (this.state.error) {
+ return (
+
+ β οΈ λ λλ§ μ€λ₯
+ {this.state.error.message}
+ {this.state.error.stack}
+
+ );
+ }
+ return this.props.children as any;
+ }
+}
diff --git a/src/components/ScoreGauge.tsx b/src/components/ScoreGauge.tsx
index 7bade39..89b7bb7 100644
--- a/src/components/ScoreGauge.tsx
+++ b/src/components/ScoreGauge.tsx
@@ -1,39 +1,49 @@
import React from 'react';
import { View, Text, StyleSheet } from 'react-native';
-import Svg, { Circle } from 'react-native-svg';
import { colors } from '@/theme/colors';
import { typography } from '@/theme/typography';
type Props = {
- value: number; // 0-100
+ value: number;
size?: number;
label?: string;
};
export default function ScoreGauge({ value, size = 180, label = 'λ΄ λ³΄ν μ μ' }: Props) {
- const stroke = 14;
- const radius = (size - stroke) / 2;
- const circ = 2 * Math.PI * radius;
const pct = Math.max(0, Math.min(100, value));
- const dash = circ * (pct / 100);
const color = pct >= 80 ? colors.success : pct >= 60 ? colors.accent : colors.danger;
+ const stroke = 14;
return (
-
+
+ 25 ? color : colors.surfaceAlt,
+ borderBottomColor: pct > 50 ? color : colors.surfaceAlt,
+ borderLeftColor: pct > 75 ? color : colors.surfaceAlt,
+ transform: [{ rotate: `${pct * 3.6 - 90}deg` }],
+ },
+ ]}
+ />
{label}
{pct}
@@ -44,11 +54,9 @@ export default function ScoreGauge({ value, size = 180, label = 'λ΄ λ³΄ν μ
}
const styles = StyleSheet.create({
- center: {
- position: 'absolute',
- alignItems: 'center',
- justifyContent: 'center',
- },
+ track: { position: 'absolute' },
+ fill: { position: 'absolute' },
+ center: { position: 'absolute', alignItems: 'center', justifyContent: 'center' },
label: { ...typography.caption, color: colors.textSecondary },
value: { fontSize: 48, fontWeight: '800', marginTop: 2 },
unit: { ...typography.small, color: colors.textTertiary },
diff --git a/src/navigation/RootNavigator.tsx b/src/navigation/RootNavigator.tsx
index f8efbcc..b512042 100644
--- a/src/navigation/RootNavigator.tsx
+++ b/src/navigation/RootNavigator.tsx
@@ -1,6 +1,9 @@
-import React from 'react';
+import React, { useEffect } from 'react';
+import { View, ActivityIndicator } from 'react-native';
import { createNativeStackNavigator } from '@react-navigation/native-stack';
import BottomTabs from './BottomTabs';
+import LoginScreen from '@/screens/auth/LoginScreen';
+import RegisterScreen from '@/screens/auth/RegisterScreen';
import DiagnosisScreen from '@/screens/DiagnosisScreen';
import AnalysisScreen from '@/screens/AnalysisScreen';
import ScoreScreen from '@/screens/ScoreScreen';
@@ -15,8 +18,12 @@ import AIJudgeScreen from '@/screens/AIJudgeScreen';
import PremiumDietScreen from '@/screens/PremiumDietScreen';
import SilsonGenScreen from '@/screens/SilsonGenScreen';
import NotificationScreen from '@/screens/NotificationScreen';
+import { useAuthStore } from '@/store/useAuthStore';
+import { colors } from '@/theme/colors';
export type RootStackParamList = {
+ Login: undefined;
+ Register: undefined;
Tabs: undefined;
Diagnosis: undefined;
Analysis: undefined;
@@ -37,23 +44,46 @@ export type RootStackParamList = {
const Stack = createNativeStackNavigator();
export default function RootNavigator() {
+ const { user, loading, hydrate } = useAuthStore();
+
+ useEffect(() => {
+ hydrate();
+ }, [hydrate]);
+
+ if (loading) {
+ return (
+
+
+
+ );
+ }
+
return (
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+ {user ? (
+ <>
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ >
+ ) : (
+ <>
+
+
+ >
+ )}
);
}
diff --git a/src/screens/AnalysisScreen.tsx b/src/screens/AnalysisScreen.tsx
index f270172..53e3f81 100644
--- a/src/screens/AnalysisScreen.tsx
+++ b/src/screens/AnalysisScreen.tsx
@@ -1,16 +1,14 @@
import React, { useState } from 'react';
-import { View, Text, StyleSheet, TouchableOpacity, Dimensions } from 'react-native';
-import { BarChart } from 'react-native-chart-kit';
+import { View, Text, StyleSheet, TouchableOpacity } from 'react-native';
import ScreenContainer from '@/components/ScreenContainer';
import Header from '@/components/Header';
import Card from '@/components/Card';
import Section from '@/components/Section';
import Badge from '@/components/Badge';
+import BarChartSimple from '@/components/BarChartSimple';
import { colors } from '@/theme/colors';
import { radius, spacing, typography } from '@/theme/typography';
-const screenWidth = Dimensions.get('window').width;
-
const data = {
'20λ': { prem: 18, must: ['μ€μ', 'μν΄'], rec: ['μ (κ°μ‘±λ ₯)'], avgCoverage: '2,000λ§μ' },
'30λ': { prem: 28, must: ['μ€μ', 'μ', 'μ’
μ '], rec: ['μ¬μ±νΉν', 'μΉμ'], avgCoverage: '5,000λ§μ' },
@@ -79,26 +77,11 @@ export default function AnalysisScreen() {
- data[a].prem) }],
- }}
- width={screenWidth - spacing.lg * 2 - spacing.md * 2}
- height={220}
- yAxisLabel=""
- yAxisSuffix="λ§"
- fromZero
- chartConfig={{
- backgroundGradientFrom: '#FFF',
- backgroundGradientTo: '#FFF',
- decimalPlaces: 0,
- color: (o = 1) => `rgba(59, 130, 246, ${o})`,
- labelColor: (o = 1) => `rgba(107, 114, 128, ${o})`,
- propsForBackgroundLines: { stroke: colors.border, strokeDasharray: '3,3' },
- barPercentage: 0.6,
- }}
- style={{ borderRadius: radius.md, marginLeft: -10 }}
+ ({ label: a, value: data[a].prem }))}
+ unit="λ§"
+ barColor={colors.primary}
+ height={200}
/>
diff --git a/src/screens/ClaimHubScreen.tsx b/src/screens/ClaimHubScreen.tsx
index 9039031..cbd8ee7 100644
--- a/src/screens/ClaimHubScreen.tsx
+++ b/src/screens/ClaimHubScreen.tsx
@@ -1,5 +1,5 @@
-import React from 'react';
-import { View, Text, StyleSheet } from 'react-native';
+import React, { useEffect, useState } from 'react';
+import { View, Text, StyleSheet, RefreshControl, ScrollView } from 'react-native';
import { useNavigation } from '@react-navigation/native';
import type { NativeStackNavigationProp } from '@react-navigation/native-stack';
import { Ionicons } from '@expo/vector-icons';
@@ -9,86 +9,124 @@ import Card from '@/components/Card';
import Section from '@/components/Section';
import Badge from '@/components/Badge';
import Button from '@/components/Button';
-import { useAppStore } from '@/store/useAppStore';
+import { useDataStore } from '@/store/useDataStore';
import { colors } from '@/theme/colors';
import { spacing, typography } from '@/theme/typography';
import type { RootStackParamList } from '@/navigation/RootNavigator';
type Nav = NativeStackNavigationProp;
+const statusLabel: Record = {
+ SUBMITTED: 'μ μ',
+ REVIEWING: 'μ¬μ¬',
+ ADDITIONAL_DOCS: 'μλ₯보μ',
+ APPROVED: 'μΉμΈ',
+ PAID: 'μ§κΈμλ£',
+ REJECTED: 'κ±°μ ',
+};
+
export default function ClaimHubScreen() {
const nav = useNavigation
- {profile.name}λ
+ {user?.name ?? 'μ¬μ©μ'}λ
- {profile.age}μΈ Β· {profile.gender} Β· {profile.job}
+ {user?.profile?.age ?? '-'}μΈ Β· {user?.profile?.gender === 'FEMALE' ? 'μ¬' : 'λ¨'} Β· {user?.profile?.job ?? '-'}
-
-
+
+
+ {user?.provider === 'KAKAO' && }
@@ -68,6 +71,9 @@ export default function MyPageScreen() {
))}
+
+
+
보νμΌμ΄ v1.0.0
diff --git a/src/screens/ScoreScreen.tsx b/src/screens/ScoreScreen.tsx
index a7b300f..88f4714 100644
--- a/src/screens/ScoreScreen.tsx
+++ b/src/screens/ScoreScreen.tsx
@@ -1,5 +1,5 @@
-import React from 'react';
-import { View, Text, StyleSheet } from 'react-native';
+import React, { useEffect, useState } from 'react';
+import { View, Text, StyleSheet, RefreshControl, ScrollView } from 'react-native';
import { Ionicons } from '@expo/vector-icons';
import { useNavigation } from '@react-navigation/native';
import type { NativeStackNavigationProp } from '@react-navigation/native-stack';
@@ -10,7 +10,8 @@ import Section from '@/components/Section';
import Button from '@/components/Button';
import ScoreGauge from '@/components/ScoreGauge';
import ProgressBar from '@/components/ProgressBar';
-import { useAppStore } from '@/store/useAppStore';
+import { useDataStore } from '@/store/useDataStore';
+import { scoreApi } from '@/api/endpoints';
import { colors } from '@/theme/colors';
import { spacing, typography } from '@/theme/typography';
import type { RootStackParamList } from '@/navigation/RootNavigator';
@@ -26,26 +27,51 @@ const statusMap = {
export default function ScoreScreen() {
const nav = useNavigation();
- const score = useAppStore((s) => s.score);
+ const { score, fetchScore } = useDataStore();
+ const [refreshing, setRefreshing] = useState(false);
+
+ useEffect(() => {
+ fetchScore();
+ }, [fetchScore]);
+
+ const onRefresh = async () => {
+ setRefreshing(true);
+ try {
+ await scoreApi.recompute();
+ await fetchScore();
+ } finally {
+ setRefreshing(false);
+ }
+ };
+
+ const total = score?.total ?? 0;
+ const breakdown = score?.breakdown ?? [];
return (
-
+
-
+ }
+ >
-
+
- π‘ μμ 15% μμ€μ΄μμ. λͺ κ°μ§ 보μνλ©΄ 90μ μ΄μ κ°λ₯!
+ {total >= 80
+ ? 'πͺ μνΈν μμ€μ
λλ€. λΆμ‘±ν νλͺ©λ§ μ±μ°λ©΄ 90μ +'
+ : total >= 60
+ ? 'π‘ κΈ°λ³Έμ μμ§λ§ μ·¨μ½ νλͺ©μ΄ μμ΄μ'
+ : 'π΄ νμ¬ λ³΄μ₯μ΄ ν¬κ² λΆμ‘±ν©λλ€. μλ΄ κΆμ₯'}
- {score.breakdown.map((b) => {
- const s = statusMap[b.status];
+ {breakdown.map((b) => {
+ const s = statusMap[b.status as keyof typeof statusMap];
return (
@@ -63,28 +89,29 @@ export default function ScoreScreen() {
- {[
- { title: 'κ°λ³λ³΄ν κ°μ
', desc: '60λ μ΄ν νμ. +15μ ', route: 'Consult' },
- { title: 'μ’
μ 보ν 보μ₯ κ°ν', desc: 'μλ
μμ° μμ λλΉ. +10μ ', route: 'Consult' },
- { title: 'μΉμ보ν μΆκ°', desc: 'μ€λ
κΈ° μΉκ³Ό λΉμ© λλΉ. +5μ ', route: 'Consult' },
- ].map((a) => (
- nav.navigate(a.route as any)}>
-
-
- {a.title}
- {a.desc}
+ {breakdown
+ .filter((b) => b.status !== 'good')
+ .slice(0, 3)
+ .map((b) => (
+ nav.navigate('Consult')}>
+
+
+ {b.label} {b.status === 'none' ? 'κ°μ
' : '보μ₯ κ°ν'}
+
+ {b.status === 'none' ? 'λ―Έκ°μ
μν. νμ μ κ²' : `νμ¬ ${b.value}μ β μλ΄ ν΅ν΄ 보μ`}
+
+
+
-
-
-
- ))}
+
+ ))}
-
+
nav.navigate('Consult')} />
nav.navigate('PremiumDiet')} />
-
+
);
}
diff --git a/src/screens/auth/LoginScreen.tsx b/src/screens/auth/LoginScreen.tsx
new file mode 100644
index 0000000..b20a828
--- /dev/null
+++ b/src/screens/auth/LoginScreen.tsx
@@ -0,0 +1,141 @@
+import React, { useState } from 'react';
+import { View, Text, StyleSheet, TextInput, TouchableOpacity, Alert, Platform } from 'react-native';
+import { LinearGradient } from 'expo-linear-gradient';
+import { Ionicons } from '@expo/vector-icons';
+import { useNavigation } from '@react-navigation/native';
+import ScreenContainer from '@/components/ScreenContainer';
+import Button from '@/components/Button';
+import { colors } from '@/theme/colors';
+import { radius, spacing, typography } from '@/theme/typography';
+import { useAuthStore } from '@/store/useAuthStore';
+
+export default function LoginScreen() {
+ const nav = useNavigation();
+ const login = useAuthStore((s) => s.login);
+ const kakaoLogin = useAuthStore((s) => s.kakaoLogin);
+ const loading = useAuthStore((s) => s.loading);
+
+ const [email, setEmail] = useState('');
+ const [password, setPassword] = useState('');
+ const [submitting, setSubmitting] = useState(false);
+
+ const onSubmit = async () => {
+ if (!email || !password) {
+ Alert.alert('μ΄λ©μΌκ³Ό λΉλ°λ²νΈλ₯Ό μ
λ ₯νμΈμ');
+ return;
+ }
+ setSubmitting(true);
+ try {
+ await login(email, password);
+ } catch (e: any) {
+ Alert.alert('λ‘κ·ΈμΈ μ€ν¨', e?.message ?? 'λ€μ μλν΄ μ£ΌμΈμ');
+ } finally {
+ setSubmitting(false);
+ }
+ };
+
+ const onKakao = async () => {
+ if (Platform.OS === 'web') {
+ const input = window.prompt('[κ°λ°μ©] μΉ΄μΉ΄μ€ Access Tokenμ μ
λ ₯νμΈμ\nμ€νκ²½μμλ Kakao JS SDKλ‘ μλ νλλ©λλ€.');
+ if (!input) return;
+ try {
+ await kakaoLogin(input);
+ } catch (e: any) {
+ Alert.alert('μΉ΄μΉ΄μ€ λ‘κ·ΈμΈ μ€ν¨', e?.message ?? '');
+ }
+ return;
+ }
+ Alert.alert(
+ 'μΉ΄μΉ΄μ€ λ‘κ·ΈμΈ',
+ 'Native μΉ΄μΉ΄μ€ SDK (@react-native-seoul/kakao-login) μ°λμ΄ νμν©λλ€. λΉλ ν ν
μ€νΈ κ°λ₯ν©λλ€.'
+ );
+ };
+
+ return (
+
+
+
+
+ 보νμΌμ΄
+ λ΄ λ³΄ν, κ°μ‘± 보ν νλμ
+
+
+
+
+ μ΄λ©μΌ
+
+
+
+ λΉλ°λ²νΈ
+
+
+
+
+
+
+
+
+
+ λλ
+
+
+
+ }
+ onPress={onKakao}
+ />
+
+ nav.navigate('Register')}>
+
+ κ³μ μ΄ μμΌμ κ°μ? νμκ°μ
+
+
+
+
+
+ );
+}
+
+const styles = StyleSheet.create({
+ hero: {
+ padding: 28,
+ borderRadius: radius.xl,
+ alignItems: 'center',
+ },
+ heroTitle: { color: '#FFF', fontSize: 26, fontWeight: '800', marginTop: 8 },
+ heroSub: { color: 'rgba(255,255,255,0.85)', fontSize: 14, marginTop: 6 },
+ label: { ...typography.caption, color: colors.textSecondary, fontWeight: '600', marginBottom: 6 },
+ input: {
+ borderWidth: 1,
+ borderColor: colors.border,
+ borderRadius: radius.md,
+ padding: 14,
+ fontSize: 15,
+ color: colors.text,
+ backgroundColor: colors.surface,
+ },
+ divider: { flexDirection: 'row', alignItems: 'center', marginVertical: 4 },
+ line: { flex: 1, height: 1, backgroundColor: colors.border },
+ divText: { ...typography.small, color: colors.textSecondary, marginHorizontal: 10 },
+ signupRow: { alignItems: 'center', marginTop: 10, paddingVertical: 10 },
+ signupText: { ...typography.caption, color: colors.textSecondary },
+ signupLink: { color: colors.primary, fontWeight: '700' },
+});
diff --git a/src/screens/auth/RegisterScreen.tsx b/src/screens/auth/RegisterScreen.tsx
new file mode 100644
index 0000000..b193e91
--- /dev/null
+++ b/src/screens/auth/RegisterScreen.tsx
@@ -0,0 +1,188 @@
+import React, { useState } from 'react';
+import { View, Text, StyleSheet, TextInput, TouchableOpacity, Alert, ScrollView } from 'react-native';
+import { useNavigation } from '@react-navigation/native';
+import ScreenContainer from '@/components/ScreenContainer';
+import Header from '@/components/Header';
+import Button from '@/components/Button';
+import { colors } from '@/theme/colors';
+import { radius, spacing, typography } from '@/theme/typography';
+import { useAuthStore } from '@/store/useAuthStore';
+
+export default function RegisterScreen() {
+ const nav = useNavigation();
+ const register = useAuthStore((s) => s.register);
+
+ const [email, setEmail] = useState('');
+ const [password, setPassword] = useState('');
+ const [password2, setPassword2] = useState('');
+ const [name, setName] = useState('');
+ const [phone, setPhone] = useState('');
+ const [age, setAge] = useState('');
+ const [gender, setGender] = useState<'MALE' | 'FEMALE'>('MALE');
+ const [job, setJob] = useState('μ¬λ¬΄μ§');
+ const [submitting, setSubmitting] = useState(false);
+
+ const onSubmit = async () => {
+ if (!email || !password || !name || !age) {
+ Alert.alert('νμ νλͺ©μ λͺ¨λ μ
λ ₯νμΈμ');
+ return;
+ }
+ if (password.length < 8) {
+ Alert.alert('λΉλ°λ²νΈλ 8μ μ΄μμ΄μ΄μΌ ν©λλ€');
+ return;
+ }
+ if (password !== password2) {
+ Alert.alert('λΉλ°λ²νΈκ° μΌμΉνμ§ μμ΅λλ€');
+ return;
+ }
+ const ageNum = parseInt(age, 10);
+ if (isNaN(ageNum) || ageNum < 0 || ageNum > 120) {
+ Alert.alert('λμ΄λ₯Ό μ¬λ°λ₯΄κ² μ
λ ₯νμΈμ');
+ return;
+ }
+
+ setSubmitting(true);
+ try {
+ await register({ email, password, name, phone, age: ageNum, gender, job });
+ } catch (e: any) {
+ Alert.alert('νμκ°μ
μ€ν¨', e?.message ?? 'λ€μ μλν΄ μ£ΌμΈμ');
+ } finally {
+ setSubmitting(false);
+ }
+ };
+
+ return (
+
+
+
+ κΈ°λ³Έ μ 보
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ νλ‘ν
+
+
+
+
+
+
+ {([
+ { v: 'MALE' as const, l: 'λ¨μ±' },
+ { v: 'FEMALE' as const, l: 'μ¬μ±' },
+ ]).map((o) => (
+ setGender(o.v)}
+ >
+ {o.l}
+
+ ))}
+
+
+
+
+ {['μ¬λ¬΄μ§', 'μμ°μ§', 'μ λ¬Έμ§', 'νμ', 'μ£ΌλΆ', 'κΈ°ν'].map((j) => (
+ setJob(j)}>
+ {j}
+
+ ))}
+
+
+
+
+
+
+
+ );
+}
+
+function Field({ label, children }: { label: string; children: React.ReactNode }) {
+ return (
+
+ {label}
+ {children}
+
+ );
+}
+
+const styles = StyleSheet.create({
+ label: { ...typography.caption, color: colors.textSecondary, fontWeight: '600', marginBottom: 6 },
+ input: {
+ borderWidth: 1,
+ borderColor: colors.border,
+ borderRadius: radius.md,
+ padding: 14,
+ fontSize: 15,
+ color: colors.text,
+ backgroundColor: colors.surface,
+ },
+ pill: {
+ paddingHorizontal: 16,
+ paddingVertical: 10,
+ borderRadius: radius.pill,
+ backgroundColor: colors.surfaceAlt,
+ borderWidth: 1,
+ borderColor: colors.border,
+ },
+ pillActive: { backgroundColor: colors.primary, borderColor: colors.primary },
+ pillText: { color: colors.text, fontWeight: '500' },
+ pillTextActive: { color: '#FFF', fontWeight: '700' },
+});
diff --git a/src/store/useAuthStore.ts b/src/store/useAuthStore.ts
new file mode 100644
index 0000000..03df56b
--- /dev/null
+++ b/src/store/useAuthStore.ts
@@ -0,0 +1,76 @@
+import { create } from 'zustand';
+import { authApi, type User } from '@/api/endpoints';
+import { clearToken, loadToken, saveToken } from '@/api/client';
+
+type State = {
+ user: User | null;
+ loading: boolean;
+ error: string | null;
+ hydrate: () => Promise;
+ login: (email: string, password: string) => Promise;
+ register: (body: Parameters[0]) => Promise;
+ kakaoLogin: (accessToken: string) => Promise;
+ logout: () => Promise;
+};
+
+export const useAuthStore = create((set) => ({
+ user: null,
+ loading: true,
+ error: null,
+
+ hydrate: async () => {
+ const tok = await loadToken();
+ if (!tok) {
+ set({ loading: false });
+ return;
+ }
+ try {
+ const me = await authApi.me();
+ set({ user: me, loading: false });
+ } catch {
+ await clearToken();
+ set({ user: null, loading: false });
+ }
+ },
+
+ login: async (email, password) => {
+ set({ loading: true, error: null });
+ try {
+ const res = await authApi.login(email, password);
+ await saveToken(res.token);
+ set({ user: res.user, loading: false });
+ } catch (e: any) {
+ set({ loading: false, error: e?.message ?? 'λ‘κ·ΈμΈ μ€ν¨' });
+ throw e;
+ }
+ },
+
+ register: async (body) => {
+ set({ loading: true, error: null });
+ try {
+ const res = await authApi.register(body);
+ await saveToken(res.token);
+ set({ user: res.user, loading: false });
+ } catch (e: any) {
+ set({ loading: false, error: e?.message ?? 'νμκ°μ
μ€ν¨' });
+ throw e;
+ }
+ },
+
+ kakaoLogin: async (accessToken) => {
+ set({ loading: true, error: null });
+ try {
+ const res = await authApi.kakao(accessToken);
+ await saveToken(res.token);
+ set({ user: res.user, loading: false });
+ } catch (e: any) {
+ set({ loading: false, error: e?.message ?? 'μΉ΄μΉ΄μ€ λ‘κ·ΈμΈ μ€ν¨' });
+ throw e;
+ }
+ },
+
+ logout: async () => {
+ await clearToken();
+ set({ user: null });
+ },
+}));
diff --git a/src/store/useDataStore.ts b/src/store/useDataStore.ts
new file mode 100644
index 0000000..ace35ca
--- /dev/null
+++ b/src/store/useDataStore.ts
@@ -0,0 +1,88 @@
+import { create } from 'zustand';
+import { familyApi, type FamilyMember, policyApi, type Policy, claimApi, type Claim, scoreApi, notificationApi } from '@/api/endpoints';
+
+type ScoreState = { total: number; breakdown: Array<{ label: string; value: number; status: string }> };
+
+type State = {
+ family: FamilyMember[];
+ policies: Policy[];
+ claims: Claim[];
+ score: ScoreState | null;
+ notifications: any[];
+ upcoming: any[];
+
+ loading: { [k: string]: boolean };
+
+ fetchAll: () => Promise;
+ fetchFamily: () => Promise;
+ fetchPolicies: () => Promise;
+ fetchClaims: () => Promise;
+ fetchScore: () => Promise;
+ fetchNotifications: () => Promise;
+};
+
+export const useDataStore = create((set, get) => ({
+ family: [],
+ policies: [],
+ claims: [],
+ score: null,
+ notifications: [],
+ upcoming: [],
+ loading: {},
+
+ fetchAll: async () => {
+ await Promise.all([
+ get().fetchFamily(),
+ get().fetchPolicies(),
+ get().fetchClaims(),
+ get().fetchScore(),
+ get().fetchNotifications(),
+ ]);
+ },
+
+ fetchFamily: async () => {
+ set((s) => ({ loading: { ...s.loading, family: true } }));
+ try {
+ const family = await familyApi.list();
+ set({ family });
+ } finally {
+ set((s) => ({ loading: { ...s.loading, family: false } }));
+ }
+ },
+ fetchPolicies: async () => {
+ set((s) => ({ loading: { ...s.loading, policies: true } }));
+ try {
+ const policies = await policyApi.list();
+ set({ policies });
+ } finally {
+ set((s) => ({ loading: { ...s.loading, policies: false } }));
+ }
+ },
+ fetchClaims: async () => {
+ set((s) => ({ loading: { ...s.loading, claims: true } }));
+ try {
+ const claims = await claimApi.list();
+ set({ claims });
+ } finally {
+ set((s) => ({ loading: { ...s.loading, claims: false } }));
+ }
+ },
+ fetchScore: async () => {
+ set((s) => ({ loading: { ...s.loading, score: true } }));
+ try {
+ const score = await scoreApi.get();
+ set({ score });
+ } finally {
+ set((s) => ({ loading: { ...s.loading, score: false } }));
+ }
+ },
+ fetchNotifications: async () => {
+ set((s) => ({ loading: { ...s.loading, noti: true } }));
+ try {
+ const [notifications, upcoming] = await Promise.all([notificationApi.list(), notificationApi.upcoming()]);
+ set({ notifications, upcoming });
+ } finally {
+ set((s) => ({ loading: { ...s.loading, noti: false } }));
+ }
+ },
+}));