binaychandra commited on
Commit
1a282d7
·
0 Parent(s):

added all files

Browse files
.dockerignore ADDED
@@ -0,0 +1,38 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Node dependencies & build outputs
2
+ frontend/node_modules
3
+ frontend/dist
4
+
5
+ # Python virtual environments
6
+ backend/.venv
7
+ venv
8
+ ENV
9
+
10
+ # Caches / bytecode
11
+ **/__pycache__
12
+ *.py[cod]
13
+ .pytest_cache
14
+ .mypy_cache
15
+ .pytype
16
+ .pyright
17
+
18
+ # Git & VCS
19
+ .git
20
+ .gitignore
21
+
22
+ # Logs
23
+ *.log
24
+ npm-debug.log*
25
+ yarn-debug.log*
26
+ pnpm-debug.log*
27
+
28
+ # Editors / OS
29
+ .idea
30
+ .vscode
31
+ *.code-workspace
32
+ Thumbs.db
33
+ .DS_Store
34
+
35
+ # Misc
36
+ coverage*
37
+ dist
38
+ build
.github/workflows/ci.yml ADDED
@@ -0,0 +1,27 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ name: Build and Push Docker Image to ACR
2
+
3
+ on:
4
+ push:
5
+ branches:
6
+ - main
7
+
8
+ jobs:
9
+ build:
10
+ runs-on: ubuntu-latest
11
+
12
+ steps:
13
+ - name: Checkout code
14
+ uses: actions/checkout@v4
15
+
16
+ - name: Log in to Azure Container Registry
17
+ run: |
18
+ echo "${{ secrets.ACR_PASSWORD }}" | docker login ${{ secrets.ACR_LOGIN_SERVER }} \
19
+ --username ${{ secrets.ACR_USERNAME }} --password-stdin
20
+
21
+ - name: Build Docker image
22
+ run: |
23
+ docker build -t ${{ secrets.ACR_LOGIN_SERVER }}/reactfast:latest .
24
+
25
+ - name: Push Docker image
26
+ run: |
27
+ docker push ${{ secrets.ACR_LOGIN_SERVER }}/reactfast:latest
.gitignore ADDED
@@ -0,0 +1,53 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # --- OS / Editor ---
2
+ .DS_Store
3
+ Thumbs.db
4
+ desktop.ini
5
+ .idea/
6
+ .vscode/
7
+ *.code-workspace
8
+
9
+ # --- Root caches ---
10
+ .pytest_cache/
11
+ .mypy_cache/
12
+ .pytype/
13
+ .pyright/
14
+ .coverage
15
+ coverage.xml
16
+ htmlcov/
17
+ *.log
18
+ npm-debug.log*
19
+ yarn-debug.log*
20
+ yarn-error.log*
21
+ pnpm-debug.log*
22
+ *.tsbuildinfo
23
+
24
+ # --- Python (backend) ---
25
+ backend/.venv/
26
+ venv/
27
+ ENV/
28
+ env/
29
+ **/__pycache__/
30
+ **/*.py[cod]
31
+ *.pyd
32
+ *.pyo
33
+ *.so
34
+ *.egg-info/
35
+ .eggs/
36
+
37
+ # --- Node / Vite (frontend) ---
38
+ frontend/node_modules/
39
+ frontend/dist/
40
+ frontend/.env
41
+ frontend/.env.local
42
+ frontend/.env.*.local
43
+ # Optional: local tooling caches
44
+ frontend/.cache/
45
+
46
+ # --- Misc build outputs (root) ---
47
+ /dist/
48
+ /build/
49
+
50
+ # --- OS sync artifacts ---
51
+ ~$*
52
+ *.tmp
53
+ *.temp
Dockerfile ADDED
@@ -0,0 +1,34 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # -------- Stage 1: Build frontend (Vite + React) --------
2
+ FROM node:20-alpine AS frontend-builder
3
+ WORKDIR /frontend
4
+
5
+ # Install deps first (better layer caching)
6
+ COPY frontend/package*.json ./
7
+ COPY frontend/tsconfig.json frontend/vite.config.* frontend/index.html ./
8
+ RUN npm install
9
+
10
+ # Copy source and build
11
+ COPY frontend/src ./src
12
+ RUN npm run build
13
+
14
+ # -------- Stage 2: Backend runtime (FastAPI + Uvicorn) --------
15
+ FROM python:3.12-slim AS runtime
16
+ ENV PYTHONDONTWRITEBYTECODE=1 \
17
+ PYTHONUNBUFFERED=1
18
+
19
+ WORKDIR /app
20
+
21
+ # Install backend dependencies
22
+ COPY backend/requirements.txt ./backend/requirements.txt
23
+ RUN pip install --no-cache-dir -r backend/requirements.txt
24
+
25
+ # Copy backend code
26
+ COPY backend ./backend
27
+
28
+ # Copy built frontend into expected path (/app/frontend/dist)
29
+ COPY --from=frontend-builder /frontend/dist ./frontend/dist
30
+
31
+ EXPOSE 8000
32
+
33
+ # Default command
34
+ CMD ["python", "-m", "uvicorn", "backend.app:app", "--host", "0.0.0.0", "--port", "8000"]
README.md ADDED
@@ -0,0 +1,166 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # ReactFast
2
+
3
+ Minimal full-stack template: **FastAPI** backend + **Vite/React (TypeScript)** frontend. The backend serves the built frontend (single-page app) and exposes a simple JSON API. Includes a multi‑stage Docker build and GitHub Actions workflow (Commit-4) to push the image to Azure Container Registry (ACR).
4
+
5
+ ---
6
+
7
+ ## Features
8
+ - FastAPI backend (`/api/transform`, `/api/health`) with static file serving.
9
+ - Vite + React + TypeScript frontend built to `frontend/dist`.
10
+ - Frontend served at `/` (adjust base in `vite.config.ts`).
11
+ - Simple round‑trip demo: user enters text, backend returns transformed string.
12
+ - Multi-stage Dockerfile: builds frontend, copies build into Python runtime image.
13
+ - GitHub Actions CI: builds & pushes image to ACR (tags: commit SHA + `latest`).
14
+
15
+ ---
16
+
17
+ ## Tech Stack
18
+ | Layer | Technology | Notes |
19
+ |------------|------------|-------|
20
+ | Backend | FastAPI / Uvicorn | ASGI app serving API + static assets |
21
+ | Frontend | React 18 + Vite | Fast dev server & optimized build |
22
+ | Language | Python 3.12 & TypeScript | Type safety on both sides |
23
+ | Packaging | Docker multi-stage | Small final image (Python slim) |
24
+ | CI / CD | GitHub Actions | Image build & ACR push |
25
+ | Registry | Azure Container Registry | Deployment artifact storage |
26
+
27
+ ---
28
+
29
+ ## Repository Layout
30
+ ```
31
+ backend/
32
+ app.py # FastAPI app + API endpoints + static mounting
33
+ requirements.txt # Backend dependencies
34
+ frontend/
35
+ src/ # React source (App.tsx, main.tsx, style.css)
36
+ index.html # Vite entry HTML
37
+ vite.config.ts # Vite config (base path, build outDir)
38
+ package.json # Frontend scripts/deps
39
+ Dockerfile # Multi-stage build (frontend build → backend runtime)
40
+ .dockerignore # Prunes build context
41
+ builderflow.md # Incremental commit summaries (Commit-1..4)
42
+ README.md # This file
43
+ ```
44
+
45
+ ---
46
+
47
+ ## Backend Overview
48
+ - Mounts frontend build via `StaticFiles` after defining API routes.
49
+ - Example endpoints:
50
+ - `POST /api/transform` → `{ result: "You said: ..." }`
51
+ - `GET /api/health` → `{ status: "ok" }`
52
+ - Ensure API routes are declared **before** mounting static root to avoid 405 errors (StaticFiles intercepting non-GET methods).
53
+
54
+ ### Running backend (local dev)
55
+ ```powershell
56
+ cd backend
57
+ python -m venv .venv
58
+ .\.venv\Scripts\pip install -r requirements.txt
59
+ .\.venv\Scripts\python -m uvicorn app:app --host 127.0.0.1 --port 8000 --reload
60
+ ```
61
+ Open: http://127.0.0.1:8000/
62
+
63
+ ---
64
+
65
+ ## Frontend Overview
66
+ - Vite handles dev (`npm run dev`) and production builds (`npm run build`).
67
+ - Output bundle placed in `frontend/dist` and served by FastAPI.
68
+ - If you change route mount (e.g., from `/` to `/app`), update `base` in `vite.config.ts`.
69
+
70
+ ### Running frontend (standalone dev mode)
71
+ ```powershell
72
+ cd frontend
73
+ npm install
74
+ npm run dev
75
+ ```
76
+ Dev server: http://127.0.0.1:5173/ (API calls to `/api/...` will need proxy config or full backend URL if not served together).
77
+
78
+ ### Production build
79
+ ```powershell
80
+ cd frontend
81
+ npm run build
82
+ ```
83
+ Rebuild whenever you change frontend assets before packaging backend or Docker image.
84
+
85
+ ---
86
+
87
+ ## End‑to‑End Flow
88
+ 1. User enters text in the form.
89
+ 2. Frontend sends `POST /api/transform` with `{ text }`.
90
+ 3. Backend returns a transformed string.
91
+ 4. UI displays the response below the form.
92
+
93
+ ---
94
+
95
+ ## Docker
96
+ Multi-stage build: Node → Python.
97
+
98
+ ### Build locally
99
+ ```powershell
100
+ docker build -t reactfast .
101
+ docker run --rm -p 8000:8000 reactfast
102
+ ```
103
+ Visit: http://localhost:8000/
104
+
105
+ ### Environment customization
106
+ - Adjust exposed port by changing `-p hostPort:8000`.
107
+ - Add env vars by appending `-e KEY=value` to `docker run`.
108
+ - For dev hot-reload, prefer running backend & frontend separately outside container.
109
+
110
+ ---
111
+
112
+ ## GitHub Actions (Commit-4)
113
+ Workflow builds and pushes image to ACR on push to `main`.
114
+
115
+ ### Required GitHub Secrets
116
+ - `AZURE_CREDENTIALS` – Service Principal JSON (`--sdk-auth`) with AcrPush role.
117
+ - `ACR_LOGIN_SERVER` – e.g. `myregistry.azurecr.io`.
118
+
119
+ ### Resulting Tags
120
+ - `<loginServer>/reactfast:<git-sha>` (immutable)
121
+ - `<loginServer>/reactfast:latest`
122
+
123
+ ### Typical Service Principal Creation
124
+ ```powershell
125
+ $ACR_ID = az acr show -n <ACR_NAME> --query id -o tsv
126
+ az ad sp create-for-rbac --name reactfast-sp --role AcrPush --scopes $ACR_ID --sdk-auth
127
+ ```
128
+ Paste JSON output into `AZURE_CREDENTIALS` secret.
129
+
130
+ ---
131
+
132
+ ## Troubleshooting
133
+ | Issue | Cause | Fix |
134
+ |-------|-------|-----|
135
+ | 404 assets | Wrong mount/base mismatch | Align `vite.config.ts` base with `app.mount()` path and rebuild |
136
+ | 405 on POST /api/transform | StaticFiles mounted before API routes | Declare API routes first, mount static last |
137
+ | Image lacks new frontend changes | Forgot to rebuild frontend in Docker | Dockerfile handles build; ensure source changes committed |
138
+ | ACR push fails | Missing/incorrect secrets | Verify `AZURE_CREDENTIALS`, `ACR_LOGIN_SERVER` |
139
+
140
+ ---
141
+
142
+ ## Extending
143
+ - Add tests (pytest + React Testing Library).
144
+ - Introduce type checking (mypy/pyright) in CI.
145
+ - Add security scanning (Trivy / GitHub Dependabot alerts).
146
+ - Implement version tagging (semantic-release or manual release workflow).
147
+ - Deploy automatically to Azure Web App / Container Apps after push.
148
+
149
+ ---
150
+
151
+ ## Quick Start (All-in-One)
152
+ ```powershell
153
+ # Backend & Frontend build
154
+ cd frontend
155
+ npm install
156
+ npm run build
157
+ cd ../backend
158
+ python -m venv .venv
159
+ .\.venv\Scripts\pip install -r requirements.txt
160
+ .\.venv\Scripts\python -m uvicorn app:app --host 127.0.0.1 --port 8000 --reload
161
+ # Open http://127.0.0.1:8000/
162
+ ```
163
+
164
+ ---
165
+
166
+ For commit-by-commit evolution see `builderflow.md`.
backend/app.py ADDED
@@ -0,0 +1,60 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # mypy: disable - error - code = "no-untyped-def,misc"
2
+ import pathlib
3
+ from fastapi import FastAPI, Response
4
+ from fastapi.staticfiles import StaticFiles
5
+ from pydantic import BaseModel
6
+
7
+ # Define the FastAPI app
8
+ app = FastAPI()
9
+
10
+ # --- Simple API endpoint ---
11
+ class TextIn(BaseModel):
12
+ text: str
13
+
14
+
15
+ @app.post("/api/transform")
16
+ def transform_text(payload: TextIn):
17
+ # Minimal transformation: uppercase with a prefix
18
+ modified = f"Hello {payload.text.capitalize()}! How are you!"
19
+ return {"result": modified}
20
+
21
+
22
+ def create_frontend_router(build_dir="frontend/dist"):
23
+ """Creates a router to serve the React frontend.
24
+
25
+ Args:
26
+ build_dir: Path to the React build directory relative to this file.
27
+
28
+ Returns:
29
+ A Starlette application serving the frontend.
30
+ """
31
+ # Resolve build path from repo root (two levels up from this file: backend/ -> reactfast/)
32
+ build_path = pathlib.Path(__file__).resolve().parent.parent / build_dir
33
+
34
+ if not build_path.is_dir() or not (build_path / "index.html").is_file():
35
+ print(
36
+ f"WARN: Frontend build directory not found or incomplete at {build_path}. Serving frontend will likely fail."
37
+ )
38
+ # Return a dummy router if build isn't ready
39
+ from starlette.routing import Route
40
+
41
+ async def dummy_frontend(request):
42
+ return Response(
43
+ "Frontend not built. Run 'npm run build' in the frontend directory.",
44
+ media_type="text/plain",
45
+ status_code=503,
46
+ )
47
+
48
+ return Route("/{path:path}", endpoint=dummy_frontend)
49
+
50
+ return StaticFiles(directory=build_path, html=True)
51
+
52
+
53
+ # Mount the frontend under /app to avoid conflicts and align with Vite base
54
+ app.mount(
55
+ "/",
56
+ create_frontend_router(),
57
+ name="frontend",
58
+ )
59
+
60
+
backend/requirements.txt ADDED
@@ -0,0 +1,2 @@
 
 
 
1
+ fastapi
2
+ uvicorn
builderflow.md ADDED
@@ -0,0 +1,180 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Commit-1
2
+
3
+ # ReactFast Project Overview
4
+
5
+ A minimal full-stack setup with a FastAPI backend serving a Vite + React frontend. The frontend builds into `frontend/dist`, and FastAPI mounts it under the `/app` route.
6
+
7
+ ## What this project includes
8
+ - Backend: FastAPI app (`backend/app.py`) mounting static files from the React build.
9
+ - Frontend: Vite + React (TypeScript) app configured with base `/app/` so assets resolve when hosted under that path.
10
+ - Local dev: Build frontend once, then run the FastAPI server. Visit `http://127.0.0.1:<port>/app/`.
11
+
12
+ ## Dependencies
13
+ - Python (backend)
14
+ - fastapi: Web framework serving API and static files
15
+ - uvicorn: ASGI server to run the FastAPI app
16
+ - Node (frontend)
17
+ - react, react-dom: UI library and DOM renderer
18
+ - vite: Build tool and dev server
19
+ - @vitejs/plugin-react: React plugin for Vite (Fast Refresh, JSX, etc.)
20
+ - typescript, @types/react, @types/react-dom: TypeScript and React typings
21
+
22
+ ---
23
+
24
+ ## Folder tree and file descriptions
25
+
26
+ ### backend/
27
+ ```
28
+ backend/
29
+ ├─ app.py # FastAPI app mounting the React build at /app
30
+ ├─ requirements.txt # Python dependencies for backend (fastapi, uvicorn)
31
+ ├─ __pycache__/ # Python bytecode cache (auto-generated)
32
+ └─ .venv/ # Local Python virtual environment (developer local)
33
+ ```
34
+
35
+ ### frontend/
36
+ ```
37
+ frontend/
38
+ ├─ index.html # Vite HTML entry; loads /src/main.tsx
39
+ ├─ package.json # Frontend scripts and dependencies
40
+ ├─ package-lock.json # Exact dependency versions (npm lockfile)
41
+ ├─ tsconfig.json # TypeScript compiler options for the app
42
+ ├─ vite.config.ts # Vite config; base set to /app and outDir=dist
43
+ ├─ src/ # Application source code
44
+ │ ├─ App.tsx # Main UI component rendered by the app
45
+ │ ├─ main.tsx # React entry; creates root and renders <App />
46
+ │ └─ style.css # Minimal global styles
47
+ ├─ dist/ # Production build output (generated by `npm run build`)
48
+ │ ├─ index.html # Built HTML referencing hashed asset files under /app
49
+ │ └─ assets/ # Hashed JS/CSS bundles and sourcemaps
50
+ │ ├─ index-*.js # Production JS bundle (hashed filename)
51
+ │ ├─ index-*.js.map # Sourcemap for debugging (if enabled)
52
+ │ └─ index-*.css # Production CSS bundle (hashed filename)
53
+ └─ node_modules/ # Installed frontend dependencies (generated by npm)
54
+ ```
55
+
56
+ ---
57
+
58
+ ## How it works
59
+ 1. Build the frontend (Vite) which outputs to `frontend/dist` with asset URLs prefixed by `/app/`.
60
+ 2. Start the FastAPI server; it mounts `frontend/dist` as static files at the `/app` route.
61
+ 3. Navigate to `http://127.0.0.1:<port>/app/` to view the app (index.html + assets).
62
+
63
+ ## Common commands (optional)
64
+ - Build frontend: `npm run build` in `frontend/`
65
+ - Run backend: `uvicorn app:app --host 127.0.0.1 --port 8000` in `backend/` (after installing requirements)
66
+
67
+ ## Notes
68
+ - If you change the frontend base path or output folder, update either Vite’s `base`/`build.outDir` or the backend static mount path accordingly.
69
+ - `dist/` is generated—do not edit files there manually; edit files under `src/` instead and rebuild.
70
+
71
+ ---
72
+
73
+ # Commit-2
74
+
75
+ High-level summary of enabling frontend ↔ backend communication.
76
+
77
+ - Backend
78
+ - Added a simple POST API at `/api/transform` that accepts `{ text: string }` and returns `{ result: string }` with a minimal transformation.
79
+ - Kept the React static site mounted at `/app` so built assets resolve correctly (aligned with Vite `base: '/app/'`).
80
+
81
+ - Frontend
82
+ - Updated the main UI (`src/App.tsx`) to include:
83
+ - A label, a textbox for user input, and a submit button.
84
+ - On submit, a `fetch('/api/transform', { method: 'POST', body: JSON.stringify({ text }) })` call.
85
+ - Displays the returned `result` string below the form.
86
+ - Light, elegant styling in `src/style.css` to keep the layout centered and readable without overengineering.
87
+
88
+ - Result
89
+ - Users can type a message, submit, and see a transformed response from the FastAPI backend—served together under the same origin, avoiding CORS configuration.
90
+
91
+ ---
92
+
93
+ # Commit-3
94
+
95
+ High-level summary of adding containerization (Docker) support.
96
+
97
+ - Purpose
98
+ - Provide a reproducible build artifact bundling backend (FastAPI) and pre-built frontend (Vite) into one image.
99
+ - Simplify deployment: single `docker run` serves both API and static UI.
100
+
101
+ - Dockerfile Structure (multi-stage)
102
+ - Stage 1 (node:20-alpine): installs frontend deps and runs `npm run build` to produce `dist/`.
103
+ - Stage 2 (python:3.12-slim): installs backend Python deps, copies backend code and built `frontend/dist`.
104
+ - Starts with: `uvicorn backend.app:app --host 0.0.0.0 --port 8000`.
105
+
106
+ - Key Paths Inside Image
107
+ - `/app/backend` – FastAPI code
108
+ - `/app/frontend/dist` – Built static assets served by FastAPI at `/app` route
109
+
110
+ - Added Files
111
+ - `Dockerfile` – Multi-stage build definition
112
+ - `.dockerignore` – Excludes node_modules, virtual envs, caches, VCS metadata, logs, etc., reducing context size and image bloat
113
+
114
+ - Build & Run (local)
115
+ 1. Build image:
116
+ - `docker build -t reactfast .`
117
+ 2. Run container:
118
+ - `docker run --rm -p 8000:8000 reactfast`
119
+ 3. Access UI:
120
+ - `http://localhost:8000/app/`
121
+
122
+ - Customization Notes
123
+ - To enable auto-reload in development, run locally without Docker or create a dev Dockerfile variant mounting source.
124
+ - For production scaling, consider adding a process manager (e.g., `gunicorn` with `uvicorn.workers.UvicornWorker`) and HEALTHCHECK.
125
+ - Pin dependency versions more strictly if reproducibility across time is critical.
126
+
127
+ - Outcome
128
+ - Project can be built and deployed as a single immutable image; frontend and backend remain in sync at build time.
129
+
130
+ - Pushing the app to Azure Container Registry. Use below commands
131
+ - `docker login` to login to Azure Container Registry
132
+ - `docker tag <app_name>:latest <registry-name>.azurecr.io/<app_name>:latest`
133
+ - `docker push <registry-name>.azurecr.io/<app_name>:latest`
134
+
135
+
136
+ # Commit-4
137
+
138
+ High-level summary of adding CI automation (GitHub Actions) to build and push the Docker image to Azure Container Registry (ACR).
139
+
140
+ - Purpose
141
+ - Automate image builds on each push to `main` (and manual dispatch) ensuring the registry always has an up‑to‑date image.
142
+ - Provide traceable image tags (`<commit-sha>` and `latest`) for rollback and promotion.
143
+
144
+ - Secrets / Inputs
145
+ - `AZURE_CREDENTIALS`: JSON from `az ad sp create-for-rbac --role AcrPush --scopes <ACR_ID> --sdk-auth`.
146
+ - `ACR_LOGIN_SERVER`: e.g. `minimum.azurecr.io`.
147
+ - (Optional) `ACR_NAME` if deriving login server dynamically.
148
+
149
+ - Workflow Steps (simplified)
150
+ 1. Checkout repository source.
151
+ 2. Azure login using service principal (`azure/login`).
152
+ 3. Authenticate to ACR (either via `az acr login` or `docker/login-action`).
153
+ 4. Build Docker image with existing multi-stage `Dockerfile`.
154
+ 5. Tag image twice: `:<git-sha>` and `:latest`.
155
+ 6. Push both tags to ACR.
156
+ 7. Summarize pushed tags for visibility.
157
+
158
+ - Tagging Strategy
159
+ - Immutable: `registry/app:{{ github.sha }}` for precise traceability.
160
+ - Mutable convenience: `registry/app:latest` for default deployments / quick tests.
161
+
162
+ - Minimal Example (conceptual)
163
+ - Trigger: `on: push: branches: [ main ]` + `workflow_dispatch`.
164
+ - Uses official actions: `actions/checkout`, `azure/login`, `docker/build-push-action`.
165
+
166
+ - Benefits
167
+ - Eliminates manual local build/push steps.
168
+ - Reduces risk of “works on my machine” discrepancies.
169
+ - Provides consistent, auditable artifact generation tied to commit history.
170
+
171
+ - Follow-on Opportunities
172
+ - Add deploy job (e.g., to Azure Web App / Container Apps / AKS) after successful push.
173
+ - Introduce image security scanning (Trivy / Microsoft Defender).
174
+ - Add build cache (GitHub Actions cache or ACR build tasks) for faster builds.
175
+ - Add semantic version tagging (e.g., `v1.2.3`) if release process formalizes.
176
+
177
+ - Outcome
178
+ - CI pipeline ensures every code change can rapidly produce a runnable, versioned container image in ACR, ready for deployment workflows.
179
+
180
+
frontend/index.html ADDED
@@ -0,0 +1,12 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!doctype html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8" />
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
6
+ <title>ReactFast</title>
7
+ </head>
8
+ <body>
9
+ <div id="root"></div>
10
+ <script type="module" src="/src/main.tsx"></script>
11
+ </body>
12
+ </html>
frontend/package-lock.json ADDED
@@ -0,0 +1,1658 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "name": "reactfast-frontend",
3
+ "version": "0.0.1",
4
+ "lockfileVersion": 3,
5
+ "requires": true,
6
+ "packages": {
7
+ "": {
8
+ "name": "reactfast-frontend",
9
+ "version": "0.0.1",
10
+ "dependencies": {
11
+ "react": "^18.3.1",
12
+ "react-dom": "^18.3.1"
13
+ },
14
+ "devDependencies": {
15
+ "@types/react": "^18.3.3",
16
+ "@types/react-dom": "^18.3.0",
17
+ "@vitejs/plugin-react": "^4.3.1",
18
+ "typescript": "^5.5.4",
19
+ "vite": "^5.4.0"
20
+ }
21
+ },
22
+ "node_modules/@ampproject/remapping": {
23
+ "version": "2.3.0",
24
+ "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.3.0.tgz",
25
+ "integrity": "sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==",
26
+ "dev": true,
27
+ "license": "Apache-2.0",
28
+ "dependencies": {
29
+ "@jridgewell/gen-mapping": "^0.3.5",
30
+ "@jridgewell/trace-mapping": "^0.3.24"
31
+ },
32
+ "engines": {
33
+ "node": ">=6.0.0"
34
+ }
35
+ },
36
+ "node_modules/@babel/code-frame": {
37
+ "version": "7.27.1",
38
+ "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.27.1.tgz",
39
+ "integrity": "sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg==",
40
+ "dev": true,
41
+ "license": "MIT",
42
+ "dependencies": {
43
+ "@babel/helper-validator-identifier": "^7.27.1",
44
+ "js-tokens": "^4.0.0",
45
+ "picocolors": "^1.1.1"
46
+ },
47
+ "engines": {
48
+ "node": ">=6.9.0"
49
+ }
50
+ },
51
+ "node_modules/@babel/compat-data": {
52
+ "version": "7.28.0",
53
+ "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.28.0.tgz",
54
+ "integrity": "sha512-60X7qkglvrap8mn1lh2ebxXdZYtUcpd7gsmy9kLaBJ4i/WdY8PqTSdxyA8qraikqKQK5C1KRBKXqznrVapyNaw==",
55
+ "dev": true,
56
+ "license": "MIT",
57
+ "engines": {
58
+ "node": ">=6.9.0"
59
+ }
60
+ },
61
+ "node_modules/@babel/core": {
62
+ "version": "7.28.3",
63
+ "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.28.3.tgz",
64
+ "integrity": "sha512-yDBHV9kQNcr2/sUr9jghVyz9C3Y5G2zUM2H2lo+9mKv4sFgbA8s8Z9t8D1jiTkGoO/NoIfKMyKWr4s6CN23ZwQ==",
65
+ "dev": true,
66
+ "license": "MIT",
67
+ "dependencies": {
68
+ "@ampproject/remapping": "^2.2.0",
69
+ "@babel/code-frame": "^7.27.1",
70
+ "@babel/generator": "^7.28.3",
71
+ "@babel/helper-compilation-targets": "^7.27.2",
72
+ "@babel/helper-module-transforms": "^7.28.3",
73
+ "@babel/helpers": "^7.28.3",
74
+ "@babel/parser": "^7.28.3",
75
+ "@babel/template": "^7.27.2",
76
+ "@babel/traverse": "^7.28.3",
77
+ "@babel/types": "^7.28.2",
78
+ "convert-source-map": "^2.0.0",
79
+ "debug": "^4.1.0",
80
+ "gensync": "^1.0.0-beta.2",
81
+ "json5": "^2.2.3",
82
+ "semver": "^6.3.1"
83
+ },
84
+ "engines": {
85
+ "node": ">=6.9.0"
86
+ },
87
+ "funding": {
88
+ "type": "opencollective",
89
+ "url": "https://opencollective.com/babel"
90
+ }
91
+ },
92
+ "node_modules/@babel/generator": {
93
+ "version": "7.28.3",
94
+ "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.28.3.tgz",
95
+ "integrity": "sha512-3lSpxGgvnmZznmBkCRnVREPUFJv2wrv9iAoFDvADJc0ypmdOxdUtcLeBgBJ6zE0PMeTKnxeQzyk0xTBq4Ep7zw==",
96
+ "dev": true,
97
+ "license": "MIT",
98
+ "dependencies": {
99
+ "@babel/parser": "^7.28.3",
100
+ "@babel/types": "^7.28.2",
101
+ "@jridgewell/gen-mapping": "^0.3.12",
102
+ "@jridgewell/trace-mapping": "^0.3.28",
103
+ "jsesc": "^3.0.2"
104
+ },
105
+ "engines": {
106
+ "node": ">=6.9.0"
107
+ }
108
+ },
109
+ "node_modules/@babel/helper-compilation-targets": {
110
+ "version": "7.27.2",
111
+ "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.27.2.tgz",
112
+ "integrity": "sha512-2+1thGUUWWjLTYTHZWK1n8Yga0ijBz1XAhUXcKy81rd5g6yh7hGqMp45v7cadSbEHc9G3OTv45SyneRN3ps4DQ==",
113
+ "dev": true,
114
+ "license": "MIT",
115
+ "dependencies": {
116
+ "@babel/compat-data": "^7.27.2",
117
+ "@babel/helper-validator-option": "^7.27.1",
118
+ "browserslist": "^4.24.0",
119
+ "lru-cache": "^5.1.1",
120
+ "semver": "^6.3.1"
121
+ },
122
+ "engines": {
123
+ "node": ">=6.9.0"
124
+ }
125
+ },
126
+ "node_modules/@babel/helper-globals": {
127
+ "version": "7.28.0",
128
+ "resolved": "https://registry.npmjs.org/@babel/helper-globals/-/helper-globals-7.28.0.tgz",
129
+ "integrity": "sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw==",
130
+ "dev": true,
131
+ "license": "MIT",
132
+ "engines": {
133
+ "node": ">=6.9.0"
134
+ }
135
+ },
136
+ "node_modules/@babel/helper-module-imports": {
137
+ "version": "7.27.1",
138
+ "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.27.1.tgz",
139
+ "integrity": "sha512-0gSFWUPNXNopqtIPQvlD5WgXYI5GY2kP2cCvoT8kczjbfcfuIljTbcWrulD1CIPIX2gt1wghbDy08yE1p+/r3w==",
140
+ "dev": true,
141
+ "license": "MIT",
142
+ "dependencies": {
143
+ "@babel/traverse": "^7.27.1",
144
+ "@babel/types": "^7.27.1"
145
+ },
146
+ "engines": {
147
+ "node": ">=6.9.0"
148
+ }
149
+ },
150
+ "node_modules/@babel/helper-module-transforms": {
151
+ "version": "7.28.3",
152
+ "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.28.3.tgz",
153
+ "integrity": "sha512-gytXUbs8k2sXS9PnQptz5o0QnpLL51SwASIORY6XaBKF88nsOT0Zw9szLqlSGQDP/4TljBAD5y98p2U1fqkdsw==",
154
+ "dev": true,
155
+ "license": "MIT",
156
+ "dependencies": {
157
+ "@babel/helper-module-imports": "^7.27.1",
158
+ "@babel/helper-validator-identifier": "^7.27.1",
159
+ "@babel/traverse": "^7.28.3"
160
+ },
161
+ "engines": {
162
+ "node": ">=6.9.0"
163
+ },
164
+ "peerDependencies": {
165
+ "@babel/core": "^7.0.0"
166
+ }
167
+ },
168
+ "node_modules/@babel/helper-plugin-utils": {
169
+ "version": "7.27.1",
170
+ "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.27.1.tgz",
171
+ "integrity": "sha512-1gn1Up5YXka3YYAHGKpbideQ5Yjf1tDa9qYcgysz+cNCXukyLl6DjPXhD3VRwSb8c0J9tA4b2+rHEZtc6R0tlw==",
172
+ "dev": true,
173
+ "license": "MIT",
174
+ "engines": {
175
+ "node": ">=6.9.0"
176
+ }
177
+ },
178
+ "node_modules/@babel/helper-string-parser": {
179
+ "version": "7.27.1",
180
+ "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz",
181
+ "integrity": "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==",
182
+ "dev": true,
183
+ "license": "MIT",
184
+ "engines": {
185
+ "node": ">=6.9.0"
186
+ }
187
+ },
188
+ "node_modules/@babel/helper-validator-identifier": {
189
+ "version": "7.27.1",
190
+ "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.27.1.tgz",
191
+ "integrity": "sha512-D2hP9eA+Sqx1kBZgzxZh0y1trbuU+JoDkiEwqhQ36nodYqJwyEIhPSdMNd7lOm/4io72luTPWH20Yda0xOuUow==",
192
+ "dev": true,
193
+ "license": "MIT",
194
+ "engines": {
195
+ "node": ">=6.9.0"
196
+ }
197
+ },
198
+ "node_modules/@babel/helper-validator-option": {
199
+ "version": "7.27.1",
200
+ "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.27.1.tgz",
201
+ "integrity": "sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==",
202
+ "dev": true,
203
+ "license": "MIT",
204
+ "engines": {
205
+ "node": ">=6.9.0"
206
+ }
207
+ },
208
+ "node_modules/@babel/helpers": {
209
+ "version": "7.28.3",
210
+ "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.28.3.tgz",
211
+ "integrity": "sha512-PTNtvUQihsAsDHMOP5pfobP8C6CM4JWXmP8DrEIt46c3r2bf87Ua1zoqevsMo9g+tWDwgWrFP5EIxuBx5RudAw==",
212
+ "dev": true,
213
+ "license": "MIT",
214
+ "dependencies": {
215
+ "@babel/template": "^7.27.2",
216
+ "@babel/types": "^7.28.2"
217
+ },
218
+ "engines": {
219
+ "node": ">=6.9.0"
220
+ }
221
+ },
222
+ "node_modules/@babel/parser": {
223
+ "version": "7.28.3",
224
+ "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.28.3.tgz",
225
+ "integrity": "sha512-7+Ey1mAgYqFAx2h0RuoxcQT5+MlG3GTV0TQrgr7/ZliKsm/MNDxVVutlWaziMq7wJNAz8MTqz55XLpWvva6StA==",
226
+ "dev": true,
227
+ "license": "MIT",
228
+ "dependencies": {
229
+ "@babel/types": "^7.28.2"
230
+ },
231
+ "bin": {
232
+ "parser": "bin/babel-parser.js"
233
+ },
234
+ "engines": {
235
+ "node": ">=6.0.0"
236
+ }
237
+ },
238
+ "node_modules/@babel/plugin-transform-react-jsx-self": {
239
+ "version": "7.27.1",
240
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-self/-/plugin-transform-react-jsx-self-7.27.1.tgz",
241
+ "integrity": "sha512-6UzkCs+ejGdZ5mFFC/OCUrv028ab2fp1znZmCZjAOBKiBK2jXD1O+BPSfX8X2qjJ75fZBMSnQn3Rq2mrBJK2mw==",
242
+ "dev": true,
243
+ "license": "MIT",
244
+ "dependencies": {
245
+ "@babel/helper-plugin-utils": "^7.27.1"
246
+ },
247
+ "engines": {
248
+ "node": ">=6.9.0"
249
+ },
250
+ "peerDependencies": {
251
+ "@babel/core": "^7.0.0-0"
252
+ }
253
+ },
254
+ "node_modules/@babel/plugin-transform-react-jsx-source": {
255
+ "version": "7.27.1",
256
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-source/-/plugin-transform-react-jsx-source-7.27.1.tgz",
257
+ "integrity": "sha512-zbwoTsBruTeKB9hSq73ha66iFeJHuaFkUbwvqElnygoNbj/jHRsSeokowZFN3CZ64IvEqcmmkVe89OPXc7ldAw==",
258
+ "dev": true,
259
+ "license": "MIT",
260
+ "dependencies": {
261
+ "@babel/helper-plugin-utils": "^7.27.1"
262
+ },
263
+ "engines": {
264
+ "node": ">=6.9.0"
265
+ },
266
+ "peerDependencies": {
267
+ "@babel/core": "^7.0.0-0"
268
+ }
269
+ },
270
+ "node_modules/@babel/template": {
271
+ "version": "7.27.2",
272
+ "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.27.2.tgz",
273
+ "integrity": "sha512-LPDZ85aEJyYSd18/DkjNh4/y1ntkE5KwUHWTiqgRxruuZL2F1yuHligVHLvcHY2vMHXttKFpJn6LwfI7cw7ODw==",
274
+ "dev": true,
275
+ "license": "MIT",
276
+ "dependencies": {
277
+ "@babel/code-frame": "^7.27.1",
278
+ "@babel/parser": "^7.27.2",
279
+ "@babel/types": "^7.27.1"
280
+ },
281
+ "engines": {
282
+ "node": ">=6.9.0"
283
+ }
284
+ },
285
+ "node_modules/@babel/traverse": {
286
+ "version": "7.28.3",
287
+ "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.28.3.tgz",
288
+ "integrity": "sha512-7w4kZYHneL3A6NP2nxzHvT3HCZ7puDZZjFMqDpBPECub79sTtSO5CGXDkKrTQq8ksAwfD/XI2MRFX23njdDaIQ==",
289
+ "dev": true,
290
+ "license": "MIT",
291
+ "dependencies": {
292
+ "@babel/code-frame": "^7.27.1",
293
+ "@babel/generator": "^7.28.3",
294
+ "@babel/helper-globals": "^7.28.0",
295
+ "@babel/parser": "^7.28.3",
296
+ "@babel/template": "^7.27.2",
297
+ "@babel/types": "^7.28.2",
298
+ "debug": "^4.3.1"
299
+ },
300
+ "engines": {
301
+ "node": ">=6.9.0"
302
+ }
303
+ },
304
+ "node_modules/@babel/types": {
305
+ "version": "7.28.2",
306
+ "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.28.2.tgz",
307
+ "integrity": "sha512-ruv7Ae4J5dUYULmeXw1gmb7rYRz57OWCPM57pHojnLq/3Z1CK2lNSLTCVjxVk1F/TZHwOZZrOWi0ur95BbLxNQ==",
308
+ "dev": true,
309
+ "license": "MIT",
310
+ "dependencies": {
311
+ "@babel/helper-string-parser": "^7.27.1",
312
+ "@babel/helper-validator-identifier": "^7.27.1"
313
+ },
314
+ "engines": {
315
+ "node": ">=6.9.0"
316
+ }
317
+ },
318
+ "node_modules/@esbuild/aix-ppc64": {
319
+ "version": "0.21.5",
320
+ "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.21.5.tgz",
321
+ "integrity": "sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ==",
322
+ "cpu": [
323
+ "ppc64"
324
+ ],
325
+ "dev": true,
326
+ "license": "MIT",
327
+ "optional": true,
328
+ "os": [
329
+ "aix"
330
+ ],
331
+ "engines": {
332
+ "node": ">=12"
333
+ }
334
+ },
335
+ "node_modules/@esbuild/android-arm": {
336
+ "version": "0.21.5",
337
+ "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.21.5.tgz",
338
+ "integrity": "sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg==",
339
+ "cpu": [
340
+ "arm"
341
+ ],
342
+ "dev": true,
343
+ "license": "MIT",
344
+ "optional": true,
345
+ "os": [
346
+ "android"
347
+ ],
348
+ "engines": {
349
+ "node": ">=12"
350
+ }
351
+ },
352
+ "node_modules/@esbuild/android-arm64": {
353
+ "version": "0.21.5",
354
+ "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.21.5.tgz",
355
+ "integrity": "sha512-c0uX9VAUBQ7dTDCjq+wdyGLowMdtR/GoC2U5IYk/7D1H1JYC0qseD7+11iMP2mRLN9RcCMRcjC4YMclCzGwS/A==",
356
+ "cpu": [
357
+ "arm64"
358
+ ],
359
+ "dev": true,
360
+ "license": "MIT",
361
+ "optional": true,
362
+ "os": [
363
+ "android"
364
+ ],
365
+ "engines": {
366
+ "node": ">=12"
367
+ }
368
+ },
369
+ "node_modules/@esbuild/android-x64": {
370
+ "version": "0.21.5",
371
+ "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.21.5.tgz",
372
+ "integrity": "sha512-D7aPRUUNHRBwHxzxRvp856rjUHRFW1SdQATKXH2hqA0kAZb1hKmi02OpYRacl0TxIGz/ZmXWlbZgjwWYaCakTA==",
373
+ "cpu": [
374
+ "x64"
375
+ ],
376
+ "dev": true,
377
+ "license": "MIT",
378
+ "optional": true,
379
+ "os": [
380
+ "android"
381
+ ],
382
+ "engines": {
383
+ "node": ">=12"
384
+ }
385
+ },
386
+ "node_modules/@esbuild/darwin-arm64": {
387
+ "version": "0.21.5",
388
+ "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.21.5.tgz",
389
+ "integrity": "sha512-DwqXqZyuk5AiWWf3UfLiRDJ5EDd49zg6O9wclZ7kUMv2WRFr4HKjXp/5t8JZ11QbQfUS6/cRCKGwYhtNAY88kQ==",
390
+ "cpu": [
391
+ "arm64"
392
+ ],
393
+ "dev": true,
394
+ "license": "MIT",
395
+ "optional": true,
396
+ "os": [
397
+ "darwin"
398
+ ],
399
+ "engines": {
400
+ "node": ">=12"
401
+ }
402
+ },
403
+ "node_modules/@esbuild/darwin-x64": {
404
+ "version": "0.21.5",
405
+ "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.21.5.tgz",
406
+ "integrity": "sha512-se/JjF8NlmKVG4kNIuyWMV/22ZaerB+qaSi5MdrXtd6R08kvs2qCN4C09miupktDitvh8jRFflwGFBQcxZRjbw==",
407
+ "cpu": [
408
+ "x64"
409
+ ],
410
+ "dev": true,
411
+ "license": "MIT",
412
+ "optional": true,
413
+ "os": [
414
+ "darwin"
415
+ ],
416
+ "engines": {
417
+ "node": ">=12"
418
+ }
419
+ },
420
+ "node_modules/@esbuild/freebsd-arm64": {
421
+ "version": "0.21.5",
422
+ "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.21.5.tgz",
423
+ "integrity": "sha512-5JcRxxRDUJLX8JXp/wcBCy3pENnCgBR9bN6JsY4OmhfUtIHe3ZW0mawA7+RDAcMLrMIZaf03NlQiX9DGyB8h4g==",
424
+ "cpu": [
425
+ "arm64"
426
+ ],
427
+ "dev": true,
428
+ "license": "MIT",
429
+ "optional": true,
430
+ "os": [
431
+ "freebsd"
432
+ ],
433
+ "engines": {
434
+ "node": ">=12"
435
+ }
436
+ },
437
+ "node_modules/@esbuild/freebsd-x64": {
438
+ "version": "0.21.5",
439
+ "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.21.5.tgz",
440
+ "integrity": "sha512-J95kNBj1zkbMXtHVH29bBriQygMXqoVQOQYA+ISs0/2l3T9/kj42ow2mpqerRBxDJnmkUDCaQT/dfNXWX/ZZCQ==",
441
+ "cpu": [
442
+ "x64"
443
+ ],
444
+ "dev": true,
445
+ "license": "MIT",
446
+ "optional": true,
447
+ "os": [
448
+ "freebsd"
449
+ ],
450
+ "engines": {
451
+ "node": ">=12"
452
+ }
453
+ },
454
+ "node_modules/@esbuild/linux-arm": {
455
+ "version": "0.21.5",
456
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.21.5.tgz",
457
+ "integrity": "sha512-bPb5AHZtbeNGjCKVZ9UGqGwo8EUu4cLq68E95A53KlxAPRmUyYv2D6F0uUI65XisGOL1hBP5mTronbgo+0bFcA==",
458
+ "cpu": [
459
+ "arm"
460
+ ],
461
+ "dev": true,
462
+ "license": "MIT",
463
+ "optional": true,
464
+ "os": [
465
+ "linux"
466
+ ],
467
+ "engines": {
468
+ "node": ">=12"
469
+ }
470
+ },
471
+ "node_modules/@esbuild/linux-arm64": {
472
+ "version": "0.21.5",
473
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.21.5.tgz",
474
+ "integrity": "sha512-ibKvmyYzKsBeX8d8I7MH/TMfWDXBF3db4qM6sy+7re0YXya+K1cem3on9XgdT2EQGMu4hQyZhan7TeQ8XkGp4Q==",
475
+ "cpu": [
476
+ "arm64"
477
+ ],
478
+ "dev": true,
479
+ "license": "MIT",
480
+ "optional": true,
481
+ "os": [
482
+ "linux"
483
+ ],
484
+ "engines": {
485
+ "node": ">=12"
486
+ }
487
+ },
488
+ "node_modules/@esbuild/linux-ia32": {
489
+ "version": "0.21.5",
490
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.21.5.tgz",
491
+ "integrity": "sha512-YvjXDqLRqPDl2dvRODYmmhz4rPeVKYvppfGYKSNGdyZkA01046pLWyRKKI3ax8fbJoK5QbxblURkwK/MWY18Tg==",
492
+ "cpu": [
493
+ "ia32"
494
+ ],
495
+ "dev": true,
496
+ "license": "MIT",
497
+ "optional": true,
498
+ "os": [
499
+ "linux"
500
+ ],
501
+ "engines": {
502
+ "node": ">=12"
503
+ }
504
+ },
505
+ "node_modules/@esbuild/linux-loong64": {
506
+ "version": "0.21.5",
507
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.21.5.tgz",
508
+ "integrity": "sha512-uHf1BmMG8qEvzdrzAqg2SIG/02+4/DHB6a9Kbya0XDvwDEKCoC8ZRWI5JJvNdUjtciBGFQ5PuBlpEOXQj+JQSg==",
509
+ "cpu": [
510
+ "loong64"
511
+ ],
512
+ "dev": true,
513
+ "license": "MIT",
514
+ "optional": true,
515
+ "os": [
516
+ "linux"
517
+ ],
518
+ "engines": {
519
+ "node": ">=12"
520
+ }
521
+ },
522
+ "node_modules/@esbuild/linux-mips64el": {
523
+ "version": "0.21.5",
524
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.21.5.tgz",
525
+ "integrity": "sha512-IajOmO+KJK23bj52dFSNCMsz1QP1DqM6cwLUv3W1QwyxkyIWecfafnI555fvSGqEKwjMXVLokcV5ygHW5b3Jbg==",
526
+ "cpu": [
527
+ "mips64el"
528
+ ],
529
+ "dev": true,
530
+ "license": "MIT",
531
+ "optional": true,
532
+ "os": [
533
+ "linux"
534
+ ],
535
+ "engines": {
536
+ "node": ">=12"
537
+ }
538
+ },
539
+ "node_modules/@esbuild/linux-ppc64": {
540
+ "version": "0.21.5",
541
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.21.5.tgz",
542
+ "integrity": "sha512-1hHV/Z4OEfMwpLO8rp7CvlhBDnjsC3CttJXIhBi+5Aj5r+MBvy4egg7wCbe//hSsT+RvDAG7s81tAvpL2XAE4w==",
543
+ "cpu": [
544
+ "ppc64"
545
+ ],
546
+ "dev": true,
547
+ "license": "MIT",
548
+ "optional": true,
549
+ "os": [
550
+ "linux"
551
+ ],
552
+ "engines": {
553
+ "node": ">=12"
554
+ }
555
+ },
556
+ "node_modules/@esbuild/linux-riscv64": {
557
+ "version": "0.21.5",
558
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.21.5.tgz",
559
+ "integrity": "sha512-2HdXDMd9GMgTGrPWnJzP2ALSokE/0O5HhTUvWIbD3YdjME8JwvSCnNGBnTThKGEB91OZhzrJ4qIIxk/SBmyDDA==",
560
+ "cpu": [
561
+ "riscv64"
562
+ ],
563
+ "dev": true,
564
+ "license": "MIT",
565
+ "optional": true,
566
+ "os": [
567
+ "linux"
568
+ ],
569
+ "engines": {
570
+ "node": ">=12"
571
+ }
572
+ },
573
+ "node_modules/@esbuild/linux-s390x": {
574
+ "version": "0.21.5",
575
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.21.5.tgz",
576
+ "integrity": "sha512-zus5sxzqBJD3eXxwvjN1yQkRepANgxE9lgOW2qLnmr8ikMTphkjgXu1HR01K4FJg8h1kEEDAqDcZQtbrRnB41A==",
577
+ "cpu": [
578
+ "s390x"
579
+ ],
580
+ "dev": true,
581
+ "license": "MIT",
582
+ "optional": true,
583
+ "os": [
584
+ "linux"
585
+ ],
586
+ "engines": {
587
+ "node": ">=12"
588
+ }
589
+ },
590
+ "node_modules/@esbuild/linux-x64": {
591
+ "version": "0.21.5",
592
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.21.5.tgz",
593
+ "integrity": "sha512-1rYdTpyv03iycF1+BhzrzQJCdOuAOtaqHTWJZCWvijKD2N5Xu0TtVC8/+1faWqcP9iBCWOmjmhoH94dH82BxPQ==",
594
+ "cpu": [
595
+ "x64"
596
+ ],
597
+ "dev": true,
598
+ "license": "MIT",
599
+ "optional": true,
600
+ "os": [
601
+ "linux"
602
+ ],
603
+ "engines": {
604
+ "node": ">=12"
605
+ }
606
+ },
607
+ "node_modules/@esbuild/netbsd-x64": {
608
+ "version": "0.21.5",
609
+ "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.21.5.tgz",
610
+ "integrity": "sha512-Woi2MXzXjMULccIwMnLciyZH4nCIMpWQAs049KEeMvOcNADVxo0UBIQPfSmxB3CWKedngg7sWZdLvLczpe0tLg==",
611
+ "cpu": [
612
+ "x64"
613
+ ],
614
+ "dev": true,
615
+ "license": "MIT",
616
+ "optional": true,
617
+ "os": [
618
+ "netbsd"
619
+ ],
620
+ "engines": {
621
+ "node": ">=12"
622
+ }
623
+ },
624
+ "node_modules/@esbuild/openbsd-x64": {
625
+ "version": "0.21.5",
626
+ "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.21.5.tgz",
627
+ "integrity": "sha512-HLNNw99xsvx12lFBUwoT8EVCsSvRNDVxNpjZ7bPn947b8gJPzeHWyNVhFsaerc0n3TsbOINvRP2byTZ5LKezow==",
628
+ "cpu": [
629
+ "x64"
630
+ ],
631
+ "dev": true,
632
+ "license": "MIT",
633
+ "optional": true,
634
+ "os": [
635
+ "openbsd"
636
+ ],
637
+ "engines": {
638
+ "node": ">=12"
639
+ }
640
+ },
641
+ "node_modules/@esbuild/sunos-x64": {
642
+ "version": "0.21.5",
643
+ "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.21.5.tgz",
644
+ "integrity": "sha512-6+gjmFpfy0BHU5Tpptkuh8+uw3mnrvgs+dSPQXQOv3ekbordwnzTVEb4qnIvQcYXq6gzkyTnoZ9dZG+D4garKg==",
645
+ "cpu": [
646
+ "x64"
647
+ ],
648
+ "dev": true,
649
+ "license": "MIT",
650
+ "optional": true,
651
+ "os": [
652
+ "sunos"
653
+ ],
654
+ "engines": {
655
+ "node": ">=12"
656
+ }
657
+ },
658
+ "node_modules/@esbuild/win32-arm64": {
659
+ "version": "0.21.5",
660
+ "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.21.5.tgz",
661
+ "integrity": "sha512-Z0gOTd75VvXqyq7nsl93zwahcTROgqvuAcYDUr+vOv8uHhNSKROyU961kgtCD1e95IqPKSQKH7tBTslnS3tA8A==",
662
+ "cpu": [
663
+ "arm64"
664
+ ],
665
+ "dev": true,
666
+ "license": "MIT",
667
+ "optional": true,
668
+ "os": [
669
+ "win32"
670
+ ],
671
+ "engines": {
672
+ "node": ">=12"
673
+ }
674
+ },
675
+ "node_modules/@esbuild/win32-ia32": {
676
+ "version": "0.21.5",
677
+ "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.21.5.tgz",
678
+ "integrity": "sha512-SWXFF1CL2RVNMaVs+BBClwtfZSvDgtL//G/smwAc5oVK/UPu2Gu9tIaRgFmYFFKrmg3SyAjSrElf0TiJ1v8fYA==",
679
+ "cpu": [
680
+ "ia32"
681
+ ],
682
+ "dev": true,
683
+ "license": "MIT",
684
+ "optional": true,
685
+ "os": [
686
+ "win32"
687
+ ],
688
+ "engines": {
689
+ "node": ">=12"
690
+ }
691
+ },
692
+ "node_modules/@esbuild/win32-x64": {
693
+ "version": "0.21.5",
694
+ "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.21.5.tgz",
695
+ "integrity": "sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw==",
696
+ "cpu": [
697
+ "x64"
698
+ ],
699
+ "dev": true,
700
+ "license": "MIT",
701
+ "optional": true,
702
+ "os": [
703
+ "win32"
704
+ ],
705
+ "engines": {
706
+ "node": ">=12"
707
+ }
708
+ },
709
+ "node_modules/@jridgewell/gen-mapping": {
710
+ "version": "0.3.13",
711
+ "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz",
712
+ "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==",
713
+ "dev": true,
714
+ "license": "MIT",
715
+ "dependencies": {
716
+ "@jridgewell/sourcemap-codec": "^1.5.0",
717
+ "@jridgewell/trace-mapping": "^0.3.24"
718
+ }
719
+ },
720
+ "node_modules/@jridgewell/resolve-uri": {
721
+ "version": "3.1.2",
722
+ "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz",
723
+ "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==",
724
+ "dev": true,
725
+ "license": "MIT",
726
+ "engines": {
727
+ "node": ">=6.0.0"
728
+ }
729
+ },
730
+ "node_modules/@jridgewell/sourcemap-codec": {
731
+ "version": "1.5.5",
732
+ "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz",
733
+ "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==",
734
+ "dev": true,
735
+ "license": "MIT"
736
+ },
737
+ "node_modules/@jridgewell/trace-mapping": {
738
+ "version": "0.3.30",
739
+ "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.30.tgz",
740
+ "integrity": "sha512-GQ7Nw5G2lTu/BtHTKfXhKHok2WGetd4XYcVKGx00SjAk8GMwgJM3zr6zORiPGuOE+/vkc90KtTosSSvaCjKb2Q==",
741
+ "dev": true,
742
+ "license": "MIT",
743
+ "dependencies": {
744
+ "@jridgewell/resolve-uri": "^3.1.0",
745
+ "@jridgewell/sourcemap-codec": "^1.4.14"
746
+ }
747
+ },
748
+ "node_modules/@rolldown/pluginutils": {
749
+ "version": "1.0.0-beta.27",
750
+ "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.0-beta.27.tgz",
751
+ "integrity": "sha512-+d0F4MKMCbeVUJwG96uQ4SgAznZNSq93I3V+9NHA4OpvqG8mRCpGdKmK8l/dl02h2CCDHwW2FqilnTyDcAnqjA==",
752
+ "dev": true,
753
+ "license": "MIT"
754
+ },
755
+ "node_modules/@rollup/rollup-android-arm-eabi": {
756
+ "version": "4.50.0",
757
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.50.0.tgz",
758
+ "integrity": "sha512-lVgpeQyy4fWN5QYebtW4buT/4kn4p4IJ+kDNB4uYNT5b8c8DLJDg6titg20NIg7E8RWwdWZORW6vUFfrLyG3KQ==",
759
+ "cpu": [
760
+ "arm"
761
+ ],
762
+ "dev": true,
763
+ "license": "MIT",
764
+ "optional": true,
765
+ "os": [
766
+ "android"
767
+ ]
768
+ },
769
+ "node_modules/@rollup/rollup-android-arm64": {
770
+ "version": "4.50.0",
771
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.50.0.tgz",
772
+ "integrity": "sha512-2O73dR4Dc9bp+wSYhviP6sDziurB5/HCym7xILKifWdE9UsOe2FtNcM+I4xZjKrfLJnq5UR8k9riB87gauiQtw==",
773
+ "cpu": [
774
+ "arm64"
775
+ ],
776
+ "dev": true,
777
+ "license": "MIT",
778
+ "optional": true,
779
+ "os": [
780
+ "android"
781
+ ]
782
+ },
783
+ "node_modules/@rollup/rollup-darwin-arm64": {
784
+ "version": "4.50.0",
785
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.50.0.tgz",
786
+ "integrity": "sha512-vwSXQN8T4sKf1RHr1F0s98Pf8UPz7pS6P3LG9NSmuw0TVh7EmaE+5Ny7hJOZ0M2yuTctEsHHRTMi2wuHkdS6Hg==",
787
+ "cpu": [
788
+ "arm64"
789
+ ],
790
+ "dev": true,
791
+ "license": "MIT",
792
+ "optional": true,
793
+ "os": [
794
+ "darwin"
795
+ ]
796
+ },
797
+ "node_modules/@rollup/rollup-darwin-x64": {
798
+ "version": "4.50.0",
799
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.50.0.tgz",
800
+ "integrity": "sha512-cQp/WG8HE7BCGyFVuzUg0FNmupxC+EPZEwWu2FCGGw5WDT1o2/YlENbm5e9SMvfDFR6FRhVCBePLqj0o8MN7Vw==",
801
+ "cpu": [
802
+ "x64"
803
+ ],
804
+ "dev": true,
805
+ "license": "MIT",
806
+ "optional": true,
807
+ "os": [
808
+ "darwin"
809
+ ]
810
+ },
811
+ "node_modules/@rollup/rollup-freebsd-arm64": {
812
+ "version": "4.50.0",
813
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.50.0.tgz",
814
+ "integrity": "sha512-UR1uTJFU/p801DvvBbtDD7z9mQL8J80xB0bR7DqW7UGQHRm/OaKzp4is7sQSdbt2pjjSS72eAtRh43hNduTnnQ==",
815
+ "cpu": [
816
+ "arm64"
817
+ ],
818
+ "dev": true,
819
+ "license": "MIT",
820
+ "optional": true,
821
+ "os": [
822
+ "freebsd"
823
+ ]
824
+ },
825
+ "node_modules/@rollup/rollup-freebsd-x64": {
826
+ "version": "4.50.0",
827
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.50.0.tgz",
828
+ "integrity": "sha512-G/DKyS6PK0dD0+VEzH/6n/hWDNPDZSMBmqsElWnCRGrYOb2jC0VSupp7UAHHQ4+QILwkxSMaYIbQ72dktp8pKA==",
829
+ "cpu": [
830
+ "x64"
831
+ ],
832
+ "dev": true,
833
+ "license": "MIT",
834
+ "optional": true,
835
+ "os": [
836
+ "freebsd"
837
+ ]
838
+ },
839
+ "node_modules/@rollup/rollup-linux-arm-gnueabihf": {
840
+ "version": "4.50.0",
841
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.50.0.tgz",
842
+ "integrity": "sha512-u72Mzc6jyJwKjJbZZcIYmd9bumJu7KNmHYdue43vT1rXPm2rITwmPWF0mmPzLm9/vJWxIRbao/jrQmxTO0Sm9w==",
843
+ "cpu": [
844
+ "arm"
845
+ ],
846
+ "dev": true,
847
+ "license": "MIT",
848
+ "optional": true,
849
+ "os": [
850
+ "linux"
851
+ ]
852
+ },
853
+ "node_modules/@rollup/rollup-linux-arm-musleabihf": {
854
+ "version": "4.50.0",
855
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.50.0.tgz",
856
+ "integrity": "sha512-S4UefYdV0tnynDJV1mdkNawp0E5Qm2MtSs330IyHgaccOFrwqsvgigUD29uT+B/70PDY1eQ3t40+xf6wIvXJyg==",
857
+ "cpu": [
858
+ "arm"
859
+ ],
860
+ "dev": true,
861
+ "license": "MIT",
862
+ "optional": true,
863
+ "os": [
864
+ "linux"
865
+ ]
866
+ },
867
+ "node_modules/@rollup/rollup-linux-arm64-gnu": {
868
+ "version": "4.50.0",
869
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.50.0.tgz",
870
+ "integrity": "sha512-1EhkSvUQXJsIhk4msxP5nNAUWoB4MFDHhtc4gAYvnqoHlaL9V3F37pNHabndawsfy/Tp7BPiy/aSa6XBYbaD1g==",
871
+ "cpu": [
872
+ "arm64"
873
+ ],
874
+ "dev": true,
875
+ "license": "MIT",
876
+ "optional": true,
877
+ "os": [
878
+ "linux"
879
+ ]
880
+ },
881
+ "node_modules/@rollup/rollup-linux-arm64-musl": {
882
+ "version": "4.50.0",
883
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.50.0.tgz",
884
+ "integrity": "sha512-EtBDIZuDtVg75xIPIK1l5vCXNNCIRM0OBPUG+tbApDuJAy9mKago6QxX+tfMzbCI6tXEhMuZuN1+CU8iDW+0UQ==",
885
+ "cpu": [
886
+ "arm64"
887
+ ],
888
+ "dev": true,
889
+ "license": "MIT",
890
+ "optional": true,
891
+ "os": [
892
+ "linux"
893
+ ]
894
+ },
895
+ "node_modules/@rollup/rollup-linux-loongarch64-gnu": {
896
+ "version": "4.50.0",
897
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loongarch64-gnu/-/rollup-linux-loongarch64-gnu-4.50.0.tgz",
898
+ "integrity": "sha512-BGYSwJdMP0hT5CCmljuSNx7+k+0upweM2M4YGfFBjnFSZMHOLYR0gEEj/dxyYJ6Zc6AiSeaBY8dWOa11GF/ppQ==",
899
+ "cpu": [
900
+ "loong64"
901
+ ],
902
+ "dev": true,
903
+ "license": "MIT",
904
+ "optional": true,
905
+ "os": [
906
+ "linux"
907
+ ]
908
+ },
909
+ "node_modules/@rollup/rollup-linux-ppc64-gnu": {
910
+ "version": "4.50.0",
911
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.50.0.tgz",
912
+ "integrity": "sha512-I1gSMzkVe1KzAxKAroCJL30hA4DqSi+wGc5gviD0y3IL/VkvcnAqwBf4RHXHyvH66YVHxpKO8ojrgc4SrWAnLg==",
913
+ "cpu": [
914
+ "ppc64"
915
+ ],
916
+ "dev": true,
917
+ "license": "MIT",
918
+ "optional": true,
919
+ "os": [
920
+ "linux"
921
+ ]
922
+ },
923
+ "node_modules/@rollup/rollup-linux-riscv64-gnu": {
924
+ "version": "4.50.0",
925
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.50.0.tgz",
926
+ "integrity": "sha512-bSbWlY3jZo7molh4tc5dKfeSxkqnf48UsLqYbUhnkdnfgZjgufLS/NTA8PcP/dnvct5CCdNkABJ56CbclMRYCA==",
927
+ "cpu": [
928
+ "riscv64"
929
+ ],
930
+ "dev": true,
931
+ "license": "MIT",
932
+ "optional": true,
933
+ "os": [
934
+ "linux"
935
+ ]
936
+ },
937
+ "node_modules/@rollup/rollup-linux-riscv64-musl": {
938
+ "version": "4.50.0",
939
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.50.0.tgz",
940
+ "integrity": "sha512-LSXSGumSURzEQLT2e4sFqFOv3LWZsEF8FK7AAv9zHZNDdMnUPYH3t8ZlaeYYZyTXnsob3htwTKeWtBIkPV27iQ==",
941
+ "cpu": [
942
+ "riscv64"
943
+ ],
944
+ "dev": true,
945
+ "license": "MIT",
946
+ "optional": true,
947
+ "os": [
948
+ "linux"
949
+ ]
950
+ },
951
+ "node_modules/@rollup/rollup-linux-s390x-gnu": {
952
+ "version": "4.50.0",
953
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.50.0.tgz",
954
+ "integrity": "sha512-CxRKyakfDrsLXiCyucVfVWVoaPA4oFSpPpDwlMcDFQvrv3XY6KEzMtMZrA+e/goC8xxp2WSOxHQubP8fPmmjOQ==",
955
+ "cpu": [
956
+ "s390x"
957
+ ],
958
+ "dev": true,
959
+ "license": "MIT",
960
+ "optional": true,
961
+ "os": [
962
+ "linux"
963
+ ]
964
+ },
965
+ "node_modules/@rollup/rollup-linux-x64-gnu": {
966
+ "version": "4.50.0",
967
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.50.0.tgz",
968
+ "integrity": "sha512-8PrJJA7/VU8ToHVEPu14FzuSAqVKyo5gg/J8xUerMbyNkWkO9j2ExBho/68RnJsMGNJq4zH114iAttgm7BZVkA==",
969
+ "cpu": [
970
+ "x64"
971
+ ],
972
+ "dev": true,
973
+ "license": "MIT",
974
+ "optional": true,
975
+ "os": [
976
+ "linux"
977
+ ]
978
+ },
979
+ "node_modules/@rollup/rollup-linux-x64-musl": {
980
+ "version": "4.50.0",
981
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.50.0.tgz",
982
+ "integrity": "sha512-SkE6YQp+CzpyOrbw7Oc4MgXFvTw2UIBElvAvLCo230pyxOLmYwRPwZ/L5lBe/VW/qT1ZgND9wJfOsdy0XptRvw==",
983
+ "cpu": [
984
+ "x64"
985
+ ],
986
+ "dev": true,
987
+ "license": "MIT",
988
+ "optional": true,
989
+ "os": [
990
+ "linux"
991
+ ]
992
+ },
993
+ "node_modules/@rollup/rollup-openharmony-arm64": {
994
+ "version": "4.50.0",
995
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.50.0.tgz",
996
+ "integrity": "sha512-PZkNLPfvXeIOgJWA804zjSFH7fARBBCpCXxgkGDRjjAhRLOR8o0IGS01ykh5GYfod4c2yiiREuDM8iZ+pVsT+Q==",
997
+ "cpu": [
998
+ "arm64"
999
+ ],
1000
+ "dev": true,
1001
+ "license": "MIT",
1002
+ "optional": true,
1003
+ "os": [
1004
+ "openharmony"
1005
+ ]
1006
+ },
1007
+ "node_modules/@rollup/rollup-win32-arm64-msvc": {
1008
+ "version": "4.50.0",
1009
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.50.0.tgz",
1010
+ "integrity": "sha512-q7cIIdFvWQoaCbLDUyUc8YfR3Jh2xx3unO8Dn6/TTogKjfwrax9SyfmGGK6cQhKtjePI7jRfd7iRYcxYs93esg==",
1011
+ "cpu": [
1012
+ "arm64"
1013
+ ],
1014
+ "dev": true,
1015
+ "license": "MIT",
1016
+ "optional": true,
1017
+ "os": [
1018
+ "win32"
1019
+ ]
1020
+ },
1021
+ "node_modules/@rollup/rollup-win32-ia32-msvc": {
1022
+ "version": "4.50.0",
1023
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.50.0.tgz",
1024
+ "integrity": "sha512-XzNOVg/YnDOmFdDKcxxK410PrcbcqZkBmz+0FicpW5jtjKQxcW1BZJEQOF0NJa6JO7CZhett8GEtRN/wYLYJuw==",
1025
+ "cpu": [
1026
+ "ia32"
1027
+ ],
1028
+ "dev": true,
1029
+ "license": "MIT",
1030
+ "optional": true,
1031
+ "os": [
1032
+ "win32"
1033
+ ]
1034
+ },
1035
+ "node_modules/@rollup/rollup-win32-x64-msvc": {
1036
+ "version": "4.50.0",
1037
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.50.0.tgz",
1038
+ "integrity": "sha512-xMmiWRR8sp72Zqwjgtf3QbZfF1wdh8X2ABu3EaozvZcyHJeU0r+XAnXdKgs4cCAp6ORoYoCygipYP1mjmbjrsg==",
1039
+ "cpu": [
1040
+ "x64"
1041
+ ],
1042
+ "dev": true,
1043
+ "license": "MIT",
1044
+ "optional": true,
1045
+ "os": [
1046
+ "win32"
1047
+ ]
1048
+ },
1049
+ "node_modules/@types/babel__core": {
1050
+ "version": "7.20.5",
1051
+ "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz",
1052
+ "integrity": "sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==",
1053
+ "dev": true,
1054
+ "license": "MIT",
1055
+ "dependencies": {
1056
+ "@babel/parser": "^7.20.7",
1057
+ "@babel/types": "^7.20.7",
1058
+ "@types/babel__generator": "*",
1059
+ "@types/babel__template": "*",
1060
+ "@types/babel__traverse": "*"
1061
+ }
1062
+ },
1063
+ "node_modules/@types/babel__generator": {
1064
+ "version": "7.27.0",
1065
+ "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.27.0.tgz",
1066
+ "integrity": "sha512-ufFd2Xi92OAVPYsy+P4n7/U7e68fex0+Ee8gSG9KX7eo084CWiQ4sdxktvdl0bOPupXtVJPY19zk6EwWqUQ8lg==",
1067
+ "dev": true,
1068
+ "license": "MIT",
1069
+ "dependencies": {
1070
+ "@babel/types": "^7.0.0"
1071
+ }
1072
+ },
1073
+ "node_modules/@types/babel__template": {
1074
+ "version": "7.4.4",
1075
+ "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.4.tgz",
1076
+ "integrity": "sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==",
1077
+ "dev": true,
1078
+ "license": "MIT",
1079
+ "dependencies": {
1080
+ "@babel/parser": "^7.1.0",
1081
+ "@babel/types": "^7.0.0"
1082
+ }
1083
+ },
1084
+ "node_modules/@types/babel__traverse": {
1085
+ "version": "7.28.0",
1086
+ "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.28.0.tgz",
1087
+ "integrity": "sha512-8PvcXf70gTDZBgt9ptxJ8elBeBjcLOAcOtoO/mPJjtji1+CdGbHgm77om1GrsPxsiE+uXIpNSK64UYaIwQXd4Q==",
1088
+ "dev": true,
1089
+ "license": "MIT",
1090
+ "dependencies": {
1091
+ "@babel/types": "^7.28.2"
1092
+ }
1093
+ },
1094
+ "node_modules/@types/estree": {
1095
+ "version": "1.0.8",
1096
+ "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz",
1097
+ "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==",
1098
+ "dev": true,
1099
+ "license": "MIT"
1100
+ },
1101
+ "node_modules/@types/prop-types": {
1102
+ "version": "15.7.15",
1103
+ "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.15.tgz",
1104
+ "integrity": "sha512-F6bEyamV9jKGAFBEmlQnesRPGOQqS2+Uwi0Em15xenOxHaf2hv6L8YCVn3rPdPJOiJfPiCnLIRyvwVaqMY3MIw==",
1105
+ "dev": true,
1106
+ "license": "MIT"
1107
+ },
1108
+ "node_modules/@types/react": {
1109
+ "version": "18.3.24",
1110
+ "resolved": "https://registry.npmjs.org/@types/react/-/react-18.3.24.tgz",
1111
+ "integrity": "sha512-0dLEBsA1kI3OezMBF8nSsb7Nk19ZnsyE1LLhB8r27KbgU5H4pvuqZLdtE+aUkJVoXgTVuA+iLIwmZ0TuK4tx6A==",
1112
+ "dev": true,
1113
+ "license": "MIT",
1114
+ "dependencies": {
1115
+ "@types/prop-types": "*",
1116
+ "csstype": "^3.0.2"
1117
+ }
1118
+ },
1119
+ "node_modules/@types/react-dom": {
1120
+ "version": "18.3.7",
1121
+ "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-18.3.7.tgz",
1122
+ "integrity": "sha512-MEe3UeoENYVFXzoXEWsvcpg6ZvlrFNlOQ7EOsvhI3CfAXwzPfO8Qwuxd40nepsYKqyyVQnTdEfv68q91yLcKrQ==",
1123
+ "dev": true,
1124
+ "license": "MIT",
1125
+ "peerDependencies": {
1126
+ "@types/react": "^18.0.0"
1127
+ }
1128
+ },
1129
+ "node_modules/@vitejs/plugin-react": {
1130
+ "version": "4.7.0",
1131
+ "resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-4.7.0.tgz",
1132
+ "integrity": "sha512-gUu9hwfWvvEDBBmgtAowQCojwZmJ5mcLn3aufeCsitijs3+f2NsrPtlAWIR6OPiqljl96GVCUbLe0HyqIpVaoA==",
1133
+ "dev": true,
1134
+ "license": "MIT",
1135
+ "dependencies": {
1136
+ "@babel/core": "^7.28.0",
1137
+ "@babel/plugin-transform-react-jsx-self": "^7.27.1",
1138
+ "@babel/plugin-transform-react-jsx-source": "^7.27.1",
1139
+ "@rolldown/pluginutils": "1.0.0-beta.27",
1140
+ "@types/babel__core": "^7.20.5",
1141
+ "react-refresh": "^0.17.0"
1142
+ },
1143
+ "engines": {
1144
+ "node": "^14.18.0 || >=16.0.0"
1145
+ },
1146
+ "peerDependencies": {
1147
+ "vite": "^4.2.0 || ^5.0.0 || ^6.0.0 || ^7.0.0"
1148
+ }
1149
+ },
1150
+ "node_modules/browserslist": {
1151
+ "version": "4.25.4",
1152
+ "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.25.4.tgz",
1153
+ "integrity": "sha512-4jYpcjabC606xJ3kw2QwGEZKX0Aw7sgQdZCvIK9dhVSPh76BKo+C+btT1RRofH7B+8iNpEbgGNVWiLki5q93yg==",
1154
+ "dev": true,
1155
+ "funding": [
1156
+ {
1157
+ "type": "opencollective",
1158
+ "url": "https://opencollective.com/browserslist"
1159
+ },
1160
+ {
1161
+ "type": "tidelift",
1162
+ "url": "https://tidelift.com/funding/github/npm/browserslist"
1163
+ },
1164
+ {
1165
+ "type": "github",
1166
+ "url": "https://github.com/sponsors/ai"
1167
+ }
1168
+ ],
1169
+ "license": "MIT",
1170
+ "dependencies": {
1171
+ "caniuse-lite": "^1.0.30001737",
1172
+ "electron-to-chromium": "^1.5.211",
1173
+ "node-releases": "^2.0.19",
1174
+ "update-browserslist-db": "^1.1.3"
1175
+ },
1176
+ "bin": {
1177
+ "browserslist": "cli.js"
1178
+ },
1179
+ "engines": {
1180
+ "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7"
1181
+ }
1182
+ },
1183
+ "node_modules/caniuse-lite": {
1184
+ "version": "1.0.30001739",
1185
+ "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001739.tgz",
1186
+ "integrity": "sha512-y+j60d6ulelrNSwpPyrHdl+9mJnQzHBr08xm48Qno0nSk4h3Qojh+ziv2qE6rXf4k3tadF4o1J/1tAbVm1NtnA==",
1187
+ "dev": true,
1188
+ "funding": [
1189
+ {
1190
+ "type": "opencollective",
1191
+ "url": "https://opencollective.com/browserslist"
1192
+ },
1193
+ {
1194
+ "type": "tidelift",
1195
+ "url": "https://tidelift.com/funding/github/npm/caniuse-lite"
1196
+ },
1197
+ {
1198
+ "type": "github",
1199
+ "url": "https://github.com/sponsors/ai"
1200
+ }
1201
+ ],
1202
+ "license": "CC-BY-4.0"
1203
+ },
1204
+ "node_modules/convert-source-map": {
1205
+ "version": "2.0.0",
1206
+ "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz",
1207
+ "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==",
1208
+ "dev": true,
1209
+ "license": "MIT"
1210
+ },
1211
+ "node_modules/csstype": {
1212
+ "version": "3.1.3",
1213
+ "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz",
1214
+ "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==",
1215
+ "dev": true,
1216
+ "license": "MIT"
1217
+ },
1218
+ "node_modules/debug": {
1219
+ "version": "4.4.1",
1220
+ "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.1.tgz",
1221
+ "integrity": "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==",
1222
+ "dev": true,
1223
+ "license": "MIT",
1224
+ "dependencies": {
1225
+ "ms": "^2.1.3"
1226
+ },
1227
+ "engines": {
1228
+ "node": ">=6.0"
1229
+ },
1230
+ "peerDependenciesMeta": {
1231
+ "supports-color": {
1232
+ "optional": true
1233
+ }
1234
+ }
1235
+ },
1236
+ "node_modules/electron-to-chromium": {
1237
+ "version": "1.5.213",
1238
+ "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.213.tgz",
1239
+ "integrity": "sha512-xr9eRzSLNa4neDO0xVFrkXu3vyIzG4Ay08dApecw42Z1NbmCt+keEpXdvlYGVe0wtvY5dhW0Ay0lY0IOfsCg0Q==",
1240
+ "dev": true,
1241
+ "license": "ISC"
1242
+ },
1243
+ "node_modules/esbuild": {
1244
+ "version": "0.21.5",
1245
+ "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.21.5.tgz",
1246
+ "integrity": "sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw==",
1247
+ "dev": true,
1248
+ "hasInstallScript": true,
1249
+ "license": "MIT",
1250
+ "bin": {
1251
+ "esbuild": "bin/esbuild"
1252
+ },
1253
+ "engines": {
1254
+ "node": ">=12"
1255
+ },
1256
+ "optionalDependencies": {
1257
+ "@esbuild/aix-ppc64": "0.21.5",
1258
+ "@esbuild/android-arm": "0.21.5",
1259
+ "@esbuild/android-arm64": "0.21.5",
1260
+ "@esbuild/android-x64": "0.21.5",
1261
+ "@esbuild/darwin-arm64": "0.21.5",
1262
+ "@esbuild/darwin-x64": "0.21.5",
1263
+ "@esbuild/freebsd-arm64": "0.21.5",
1264
+ "@esbuild/freebsd-x64": "0.21.5",
1265
+ "@esbuild/linux-arm": "0.21.5",
1266
+ "@esbuild/linux-arm64": "0.21.5",
1267
+ "@esbuild/linux-ia32": "0.21.5",
1268
+ "@esbuild/linux-loong64": "0.21.5",
1269
+ "@esbuild/linux-mips64el": "0.21.5",
1270
+ "@esbuild/linux-ppc64": "0.21.5",
1271
+ "@esbuild/linux-riscv64": "0.21.5",
1272
+ "@esbuild/linux-s390x": "0.21.5",
1273
+ "@esbuild/linux-x64": "0.21.5",
1274
+ "@esbuild/netbsd-x64": "0.21.5",
1275
+ "@esbuild/openbsd-x64": "0.21.5",
1276
+ "@esbuild/sunos-x64": "0.21.5",
1277
+ "@esbuild/win32-arm64": "0.21.5",
1278
+ "@esbuild/win32-ia32": "0.21.5",
1279
+ "@esbuild/win32-x64": "0.21.5"
1280
+ }
1281
+ },
1282
+ "node_modules/escalade": {
1283
+ "version": "3.2.0",
1284
+ "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz",
1285
+ "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==",
1286
+ "dev": true,
1287
+ "license": "MIT",
1288
+ "engines": {
1289
+ "node": ">=6"
1290
+ }
1291
+ },
1292
+ "node_modules/fsevents": {
1293
+ "version": "2.3.3",
1294
+ "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz",
1295
+ "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==",
1296
+ "dev": true,
1297
+ "hasInstallScript": true,
1298
+ "license": "MIT",
1299
+ "optional": true,
1300
+ "os": [
1301
+ "darwin"
1302
+ ],
1303
+ "engines": {
1304
+ "node": "^8.16.0 || ^10.6.0 || >=11.0.0"
1305
+ }
1306
+ },
1307
+ "node_modules/gensync": {
1308
+ "version": "1.0.0-beta.2",
1309
+ "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz",
1310
+ "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==",
1311
+ "dev": true,
1312
+ "license": "MIT",
1313
+ "engines": {
1314
+ "node": ">=6.9.0"
1315
+ }
1316
+ },
1317
+ "node_modules/js-tokens": {
1318
+ "version": "4.0.0",
1319
+ "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
1320
+ "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==",
1321
+ "license": "MIT"
1322
+ },
1323
+ "node_modules/jsesc": {
1324
+ "version": "3.1.0",
1325
+ "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz",
1326
+ "integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==",
1327
+ "dev": true,
1328
+ "license": "MIT",
1329
+ "bin": {
1330
+ "jsesc": "bin/jsesc"
1331
+ },
1332
+ "engines": {
1333
+ "node": ">=6"
1334
+ }
1335
+ },
1336
+ "node_modules/json5": {
1337
+ "version": "2.2.3",
1338
+ "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz",
1339
+ "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==",
1340
+ "dev": true,
1341
+ "license": "MIT",
1342
+ "bin": {
1343
+ "json5": "lib/cli.js"
1344
+ },
1345
+ "engines": {
1346
+ "node": ">=6"
1347
+ }
1348
+ },
1349
+ "node_modules/loose-envify": {
1350
+ "version": "1.4.0",
1351
+ "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz",
1352
+ "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==",
1353
+ "license": "MIT",
1354
+ "dependencies": {
1355
+ "js-tokens": "^3.0.0 || ^4.0.0"
1356
+ },
1357
+ "bin": {
1358
+ "loose-envify": "cli.js"
1359
+ }
1360
+ },
1361
+ "node_modules/lru-cache": {
1362
+ "version": "5.1.1",
1363
+ "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz",
1364
+ "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==",
1365
+ "dev": true,
1366
+ "license": "ISC",
1367
+ "dependencies": {
1368
+ "yallist": "^3.0.2"
1369
+ }
1370
+ },
1371
+ "node_modules/ms": {
1372
+ "version": "2.1.3",
1373
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
1374
+ "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
1375
+ "dev": true,
1376
+ "license": "MIT"
1377
+ },
1378
+ "node_modules/nanoid": {
1379
+ "version": "3.3.11",
1380
+ "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz",
1381
+ "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==",
1382
+ "dev": true,
1383
+ "funding": [
1384
+ {
1385
+ "type": "github",
1386
+ "url": "https://github.com/sponsors/ai"
1387
+ }
1388
+ ],
1389
+ "license": "MIT",
1390
+ "bin": {
1391
+ "nanoid": "bin/nanoid.cjs"
1392
+ },
1393
+ "engines": {
1394
+ "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1"
1395
+ }
1396
+ },
1397
+ "node_modules/node-releases": {
1398
+ "version": "2.0.19",
1399
+ "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.19.tgz",
1400
+ "integrity": "sha512-xxOWJsBKtzAq7DY0J+DTzuz58K8e7sJbdgwkbMWQe8UYB6ekmsQ45q0M/tJDsGaZmbC+l7n57UV8Hl5tHxO9uw==",
1401
+ "dev": true,
1402
+ "license": "MIT"
1403
+ },
1404
+ "node_modules/picocolors": {
1405
+ "version": "1.1.1",
1406
+ "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz",
1407
+ "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==",
1408
+ "dev": true,
1409
+ "license": "ISC"
1410
+ },
1411
+ "node_modules/postcss": {
1412
+ "version": "8.5.6",
1413
+ "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.6.tgz",
1414
+ "integrity": "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==",
1415
+ "dev": true,
1416
+ "funding": [
1417
+ {
1418
+ "type": "opencollective",
1419
+ "url": "https://opencollective.com/postcss/"
1420
+ },
1421
+ {
1422
+ "type": "tidelift",
1423
+ "url": "https://tidelift.com/funding/github/npm/postcss"
1424
+ },
1425
+ {
1426
+ "type": "github",
1427
+ "url": "https://github.com/sponsors/ai"
1428
+ }
1429
+ ],
1430
+ "license": "MIT",
1431
+ "dependencies": {
1432
+ "nanoid": "^3.3.11",
1433
+ "picocolors": "^1.1.1",
1434
+ "source-map-js": "^1.2.1"
1435
+ },
1436
+ "engines": {
1437
+ "node": "^10 || ^12 || >=14"
1438
+ }
1439
+ },
1440
+ "node_modules/react": {
1441
+ "version": "18.3.1",
1442
+ "resolved": "https://registry.npmjs.org/react/-/react-18.3.1.tgz",
1443
+ "integrity": "sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==",
1444
+ "license": "MIT",
1445
+ "dependencies": {
1446
+ "loose-envify": "^1.1.0"
1447
+ },
1448
+ "engines": {
1449
+ "node": ">=0.10.0"
1450
+ }
1451
+ },
1452
+ "node_modules/react-dom": {
1453
+ "version": "18.3.1",
1454
+ "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.3.1.tgz",
1455
+ "integrity": "sha512-5m4nQKp+rZRb09LNH59GM4BxTh9251/ylbKIbpe7TpGxfJ+9kv6BLkLBXIjjspbgbnIBNqlI23tRnTWT0snUIw==",
1456
+ "license": "MIT",
1457
+ "dependencies": {
1458
+ "loose-envify": "^1.1.0",
1459
+ "scheduler": "^0.23.2"
1460
+ },
1461
+ "peerDependencies": {
1462
+ "react": "^18.3.1"
1463
+ }
1464
+ },
1465
+ "node_modules/react-refresh": {
1466
+ "version": "0.17.0",
1467
+ "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.17.0.tgz",
1468
+ "integrity": "sha512-z6F7K9bV85EfseRCp2bzrpyQ0Gkw1uLoCel9XBVWPg/TjRj94SkJzUTGfOa4bs7iJvBWtQG0Wq7wnI0syw3EBQ==",
1469
+ "dev": true,
1470
+ "license": "MIT",
1471
+ "engines": {
1472
+ "node": ">=0.10.0"
1473
+ }
1474
+ },
1475
+ "node_modules/rollup": {
1476
+ "version": "4.50.0",
1477
+ "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.50.0.tgz",
1478
+ "integrity": "sha512-/Zl4D8zPifNmyGzJS+3kVoyXeDeT/GrsJM94sACNg9RtUE0hrHa1bNPtRSrfHTMH5HjRzce6K7rlTh3Khiw+pw==",
1479
+ "dev": true,
1480
+ "license": "MIT",
1481
+ "dependencies": {
1482
+ "@types/estree": "1.0.8"
1483
+ },
1484
+ "bin": {
1485
+ "rollup": "dist/bin/rollup"
1486
+ },
1487
+ "engines": {
1488
+ "node": ">=18.0.0",
1489
+ "npm": ">=8.0.0"
1490
+ },
1491
+ "optionalDependencies": {
1492
+ "@rollup/rollup-android-arm-eabi": "4.50.0",
1493
+ "@rollup/rollup-android-arm64": "4.50.0",
1494
+ "@rollup/rollup-darwin-arm64": "4.50.0",
1495
+ "@rollup/rollup-darwin-x64": "4.50.0",
1496
+ "@rollup/rollup-freebsd-arm64": "4.50.0",
1497
+ "@rollup/rollup-freebsd-x64": "4.50.0",
1498
+ "@rollup/rollup-linux-arm-gnueabihf": "4.50.0",
1499
+ "@rollup/rollup-linux-arm-musleabihf": "4.50.0",
1500
+ "@rollup/rollup-linux-arm64-gnu": "4.50.0",
1501
+ "@rollup/rollup-linux-arm64-musl": "4.50.0",
1502
+ "@rollup/rollup-linux-loongarch64-gnu": "4.50.0",
1503
+ "@rollup/rollup-linux-ppc64-gnu": "4.50.0",
1504
+ "@rollup/rollup-linux-riscv64-gnu": "4.50.0",
1505
+ "@rollup/rollup-linux-riscv64-musl": "4.50.0",
1506
+ "@rollup/rollup-linux-s390x-gnu": "4.50.0",
1507
+ "@rollup/rollup-linux-x64-gnu": "4.50.0",
1508
+ "@rollup/rollup-linux-x64-musl": "4.50.0",
1509
+ "@rollup/rollup-openharmony-arm64": "4.50.0",
1510
+ "@rollup/rollup-win32-arm64-msvc": "4.50.0",
1511
+ "@rollup/rollup-win32-ia32-msvc": "4.50.0",
1512
+ "@rollup/rollup-win32-x64-msvc": "4.50.0",
1513
+ "fsevents": "~2.3.2"
1514
+ }
1515
+ },
1516
+ "node_modules/scheduler": {
1517
+ "version": "0.23.2",
1518
+ "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.2.tgz",
1519
+ "integrity": "sha512-UOShsPwz7NrMUqhR6t0hWjFduvOzbtv7toDH1/hIrfRNIDBnnBWd0CwJTGvTpngVlmwGCdP9/Zl/tVrDqcuYzQ==",
1520
+ "license": "MIT",
1521
+ "dependencies": {
1522
+ "loose-envify": "^1.1.0"
1523
+ }
1524
+ },
1525
+ "node_modules/semver": {
1526
+ "version": "6.3.1",
1527
+ "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz",
1528
+ "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==",
1529
+ "dev": true,
1530
+ "license": "ISC",
1531
+ "bin": {
1532
+ "semver": "bin/semver.js"
1533
+ }
1534
+ },
1535
+ "node_modules/source-map-js": {
1536
+ "version": "1.2.1",
1537
+ "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz",
1538
+ "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==",
1539
+ "dev": true,
1540
+ "license": "BSD-3-Clause",
1541
+ "engines": {
1542
+ "node": ">=0.10.0"
1543
+ }
1544
+ },
1545
+ "node_modules/typescript": {
1546
+ "version": "5.9.2",
1547
+ "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.2.tgz",
1548
+ "integrity": "sha512-CWBzXQrc/qOkhidw1OzBTQuYRbfyxDXJMVJ1XNwUHGROVmuaeiEm3OslpZ1RV96d7SKKjZKrSJu3+t/xlw3R9A==",
1549
+ "dev": true,
1550
+ "license": "Apache-2.0",
1551
+ "bin": {
1552
+ "tsc": "bin/tsc",
1553
+ "tsserver": "bin/tsserver"
1554
+ },
1555
+ "engines": {
1556
+ "node": ">=14.17"
1557
+ }
1558
+ },
1559
+ "node_modules/update-browserslist-db": {
1560
+ "version": "1.1.3",
1561
+ "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.3.tgz",
1562
+ "integrity": "sha512-UxhIZQ+QInVdunkDAaiazvvT/+fXL5Osr0JZlJulepYu6Jd7qJtDZjlur0emRlT71EN3ScPoE7gvsuIKKNavKw==",
1563
+ "dev": true,
1564
+ "funding": [
1565
+ {
1566
+ "type": "opencollective",
1567
+ "url": "https://opencollective.com/browserslist"
1568
+ },
1569
+ {
1570
+ "type": "tidelift",
1571
+ "url": "https://tidelift.com/funding/github/npm/browserslist"
1572
+ },
1573
+ {
1574
+ "type": "github",
1575
+ "url": "https://github.com/sponsors/ai"
1576
+ }
1577
+ ],
1578
+ "license": "MIT",
1579
+ "dependencies": {
1580
+ "escalade": "^3.2.0",
1581
+ "picocolors": "^1.1.1"
1582
+ },
1583
+ "bin": {
1584
+ "update-browserslist-db": "cli.js"
1585
+ },
1586
+ "peerDependencies": {
1587
+ "browserslist": ">= 4.21.0"
1588
+ }
1589
+ },
1590
+ "node_modules/vite": {
1591
+ "version": "5.4.19",
1592
+ "resolved": "https://registry.npmjs.org/vite/-/vite-5.4.19.tgz",
1593
+ "integrity": "sha512-qO3aKv3HoQC8QKiNSTuUM1l9o/XX3+c+VTgLHbJWHZGeTPVAg2XwazI9UWzoxjIJCGCV2zU60uqMzjeLZuULqA==",
1594
+ "dev": true,
1595
+ "license": "MIT",
1596
+ "dependencies": {
1597
+ "esbuild": "^0.21.3",
1598
+ "postcss": "^8.4.43",
1599
+ "rollup": "^4.20.0"
1600
+ },
1601
+ "bin": {
1602
+ "vite": "bin/vite.js"
1603
+ },
1604
+ "engines": {
1605
+ "node": "^18.0.0 || >=20.0.0"
1606
+ },
1607
+ "funding": {
1608
+ "url": "https://github.com/vitejs/vite?sponsor=1"
1609
+ },
1610
+ "optionalDependencies": {
1611
+ "fsevents": "~2.3.3"
1612
+ },
1613
+ "peerDependencies": {
1614
+ "@types/node": "^18.0.0 || >=20.0.0",
1615
+ "less": "*",
1616
+ "lightningcss": "^1.21.0",
1617
+ "sass": "*",
1618
+ "sass-embedded": "*",
1619
+ "stylus": "*",
1620
+ "sugarss": "*",
1621
+ "terser": "^5.4.0"
1622
+ },
1623
+ "peerDependenciesMeta": {
1624
+ "@types/node": {
1625
+ "optional": true
1626
+ },
1627
+ "less": {
1628
+ "optional": true
1629
+ },
1630
+ "lightningcss": {
1631
+ "optional": true
1632
+ },
1633
+ "sass": {
1634
+ "optional": true
1635
+ },
1636
+ "sass-embedded": {
1637
+ "optional": true
1638
+ },
1639
+ "stylus": {
1640
+ "optional": true
1641
+ },
1642
+ "sugarss": {
1643
+ "optional": true
1644
+ },
1645
+ "terser": {
1646
+ "optional": true
1647
+ }
1648
+ }
1649
+ },
1650
+ "node_modules/yallist": {
1651
+ "version": "3.1.1",
1652
+ "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz",
1653
+ "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==",
1654
+ "dev": true,
1655
+ "license": "ISC"
1656
+ }
1657
+ }
1658
+ }
frontend/package.json ADDED
@@ -0,0 +1,22 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "name": "reactfast-frontend",
3
+ "private": true,
4
+ "version": "0.0.1",
5
+ "type": "module",
6
+ "scripts": {
7
+ "dev": "vite",
8
+ "build": "vite build",
9
+ "preview": "vite preview --port 5173"
10
+ },
11
+ "dependencies": {
12
+ "react": "^18.3.1",
13
+ "react-dom": "^18.3.1"
14
+ },
15
+ "devDependencies": {
16
+ "@vitejs/plugin-react": "^4.3.1",
17
+ "vite": "^5.4.0",
18
+ "typescript": "^5.5.4",
19
+ "@types/react": "^18.3.3",
20
+ "@types/react-dom": "^18.3.0"
21
+ }
22
+ }
frontend/src/App.tsx ADDED
@@ -0,0 +1,60 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import { useState, type FormEvent } from 'react';
2
+
3
+ export default function App() {
4
+ const [text, setText] = useState('');
5
+ const [loading, setLoading] = useState(false);
6
+ const [result, setResult] = useState<string | null>(null);
7
+ const [error, setError] = useState<string | null>(null);
8
+
9
+ async function onSubmit(e: FormEvent<HTMLFormElement>) {
10
+ e.preventDefault();
11
+ setLoading(true);
12
+ setError(null);
13
+ setResult(null);
14
+ try {
15
+ const res = await fetch('/api/transform', {
16
+ method: 'POST',
17
+ headers: { 'Content-Type': 'application/json' },
18
+ body: JSON.stringify({ text }),
19
+ });
20
+ if (!res.ok) throw new Error(`Request failed: ${res.status}`);
21
+ const data = await res.json();
22
+ setResult(data.result ?? 'No result');
23
+ } catch (err: any) {
24
+ setError(err.message || 'Something went wrong');
25
+ } finally {
26
+ setLoading(false);
27
+ }
28
+ }
29
+
30
+ return (
31
+ <div className="container">
32
+ <div className="form-wrapper">
33
+ <label htmlFor="text-input" className="label">
34
+ Send a message to the backend
35
+ </label>
36
+ <form onSubmit={onSubmit} className="form-row">
37
+ <input
38
+ id="text-input"
39
+ type="text"
40
+ value={text}
41
+ onChange={(e) => setText(e.target.value)}
42
+ placeholder="Type here..."
43
+ className="input"
44
+ />
45
+ <button type="submit" className="button" disabled={loading || !text}>
46
+ {loading ? 'Sending...' : 'Send'}
47
+ </button>
48
+ </form>
49
+ {error && <div className="error-text">{error}</div>}
50
+ {result && (
51
+ <div className="result-wrapper">
52
+ <p className="result-text">
53
+ <span className="result-label">Response:</span> {result}
54
+ </p>
55
+ </div>
56
+ )}
57
+ </div>
58
+ </div>
59
+ );
60
+ }
frontend/src/main.tsx ADDED
@@ -0,0 +1,13 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import React from 'react'
2
+ import { createRoot } from 'react-dom/client'
3
+ import App from './App'
4
+ import './style.css'
5
+
6
+ const el = document.getElementById('root')
7
+ if (el) {
8
+ createRoot(el).render(
9
+ <React.StrictMode>
10
+ <App />
11
+ </React.StrictMode>
12
+ )
13
+ }
frontend/src/style.css ADDED
@@ -0,0 +1,77 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ :root {
2
+ color-scheme: light dark;
3
+ }
4
+ body {
5
+ margin: 0;
6
+ font-family: system-ui, -apple-system, Segoe UI, Roboto, Arial, sans-serif;
7
+ background-color: #f0f2f5;
8
+ }
9
+ #root {
10
+ padding: 24px;
11
+ }
12
+ .container {
13
+ display: flex;
14
+ justify-content: center;
15
+ align-items: center;
16
+ min-height: 90vh;
17
+ }
18
+ .form-wrapper {
19
+ width: 100%;
20
+ max-width: 500px;
21
+ padding: 2rem;
22
+ background-color: #ffffff;
23
+ border-radius: 8px;
24
+ box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
25
+ }
26
+ .label {
27
+ display: block;
28
+ margin-bottom: 1rem;
29
+ font-size: 1.2rem;
30
+ font-weight: 600;
31
+ color: #333;
32
+ }
33
+ .form-row {
34
+ display: flex;
35
+ gap: 0.5rem;
36
+ }
37
+ .input {
38
+ flex-grow: 1;
39
+ padding: 0.75rem;
40
+ border: 1px solid #ccc;
41
+ border-radius: 4px;
42
+ font-size: 1rem;
43
+ }
44
+ .button {
45
+ padding: 0.75rem 1.5rem;
46
+ border: none;
47
+ border-radius: 4px;
48
+ background-color: #007bff;
49
+ color: white;
50
+ font-size: 1rem;
51
+ cursor: pointer;
52
+ transition: background-color 0.2s;
53
+ }
54
+ .button:hover:not(:disabled) {
55
+ background-color: #0056b3;
56
+ }
57
+ .button:disabled {
58
+ background-color: #ccc;
59
+ cursor: not-allowed;
60
+ }
61
+ .error-text {
62
+ margin-top: 1rem;
63
+ color: #d9534f;
64
+ }
65
+ .result-wrapper {
66
+ margin-top: 1.5rem;
67
+ padding: 1rem;
68
+ background-color: #e9f7ef;
69
+ border-left: 4px solid #28a745;
70
+ }
71
+ .result-text {
72
+ margin: 0;
73
+ color: #155724;
74
+ }
75
+ .result-label {
76
+ font-weight: bold;
77
+ }
frontend/tsconfig.json ADDED
@@ -0,0 +1,17 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "compilerOptions": {
3
+ "target": "ES2020",
4
+ "useDefineForClassFields": true,
5
+ "lib": ["ES2020", "DOM", "DOM.Iterable"],
6
+ "module": "ESNext",
7
+ "skipLibCheck": true,
8
+ "moduleResolution": "Bundler",
9
+ "resolveJsonModule": true,
10
+ "isolatedModules": true,
11
+ "noEmit": true,
12
+ "jsx": "react-jsx",
13
+ "strict": true,
14
+ "forceConsistentCasingInFileNames": true
15
+ },
16
+ "include": ["src"]
17
+ }
frontend/vite.config.ts ADDED
@@ -0,0 +1,14 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import { defineConfig } from 'vite'
2
+ import react from '@vitejs/plugin-react'
3
+
4
+ export default defineConfig({
5
+ base: '/',
6
+ plugins: [react()],
7
+ build: {
8
+ outDir: 'dist',
9
+ sourcemap: true
10
+ },
11
+ server: {
12
+ port: 5173
13
+ }
14
+ })