diff --git a/summership-26-prs/Niranjan Praveen/README.md b/summership-26-prs/Niranjan Praveen/README.md
new file mode 100644
index 0000000..e087b2e
--- /dev/null
+++ b/summership-26-prs/Niranjan Praveen/README.md
@@ -0,0 +1,83 @@
+# Mastery Progression Engine — PyBe Submission
+
+**Contributor:** Niranjan Praveen
+
+## The Feature
+
+PyBe's three difficulty labels (Beginner / Explorer / Builder) were cosmetic — every scenario was open to every learner from the first click, and nothing in the system answered the question the mentors raised directly in the review sessions: *when is a learner ready to move up, and what should they do next?*
+
+This submission adds a **Mastery Progression Engine**: a deterministic, rule-based service that turns PyBe's existing session data into
+
+1. **Per-concept mastery scores** (0–100) for every curriculum concept a learner has touched,
+2. **Stage gating** — Explorer and Builder scenarios stay locked until the learner crosses an explicit mastery threshold in the previous stage, and
+3. **A next-scenario recommender** that surfaces the three unattempted scenarios with the highest growth potential for that specific learner.
+
+No new database, no external AI, no new dependencies. The engine reads the same `db.json` sessions the rest of PyBe already writes, keeping the V0 constraint of fully local, deterministic logic — and, like the rest of the learning engine, every function is a clean seam for a future LLM/TinyLLM replacement in V2.
+
+## The Philosophies Behind It
+
+The feature is a direct implementation of three learning theories:
+
+**Bloom — Learning for Mastery (1968).** Bloom's central claim is that most learners can master material if advancement is tied to demonstrated mastery rather than seat time. The engine encodes this as an explicit threshold (default **70/100**, configurable via `MASTERY_THRESHOLD`): a stage unlocks only when the learner has mastered at least half of the previous stage's concept pool across a minimum of 3 sessions. The threshold the mentors said was "currently undefined" now has a concrete, tunable definition.
+
+**Piaget — Cognitive Stages.** The three difficulties are reframed as cognitive stages rather than labels: *Concrete Foundations* (single-value, single-decision reasoning), *Relational Thinking* (coordinating repetition, comparison, grouping), and *Formal Abstraction* (designing reusable general procedures). Each stage card in the UI explains its Piagetian rationale, so the progression is legible to the learner, not just enforced on them.
+
+**Vygotsky — Zone of Proximal Development.** The recommender scores every unattempted, unlocked scenario by summing how far each of its concepts sits *below* the mastery threshold. The scenarios that stretch the learner's weakest concepts — while remaining inside unlocked stages — rank highest. That is ZPD operationalized: always slightly beyond current ability, never out of reach.
+
+## How Mastery Is Computed
+
+For each curriculum concept (the tags on scenarios, e.g. `loops`), across every session touching that concept:
+
+| Signal | Meaning | Max |
+|---|---|---|
+| Exposure | Repeated engagement (10 pts/session, capped at 4) | 40 |
+| Prompt quality | Average prompt maturity score × 0.4 | 40 |
+| Abstraction depth | Average reasoning patterns recognized per session | 15 |
+| Reflection | Bonus for reflective practice | 5 |
+| Misconceptions | −5 per flagged misconception | −20 |
+
+Mastery = clamp(sum, 0, 100). Status bands: **Emerging** (< 35), **Developing** (35–69), **Mastered** (≥ 70).
+
+## What Changed
+
+```
+server/src/services/masteryEngine.js NEW — mastery scoring, stage gating, ZPD recommender
+server/src/routes/mastery.js NEW — GET /api/mastery (optional ?learner= filter)
+server/src/index.js + mounts /api/mastery
+client/src/main.jsx + Mastery Journey panel, locked scenarios in browser,
+ recommendation cards with one-click "Start"
+client/src/styles.css + styles for stages, concept bars, recommendations
+```
+
+### API
+
+`GET /api/mastery` returns:
+
+```json
+{
+ "threshold": 70,
+ "minSessionsToAdvance": 3,
+ "totalSessions": 8,
+ "concepts": [{ "concept": "variables", "mastery": 91, "sessions": 3, "status": "Mastered" }],
+ "levels": [{ "level": "Explorer", "stage": "Relational Thinking", "coverage": 33, "unlocked": true }],
+ "recommendations": [{ "title": "Bus Stop Search", "reason": "Builds search (current mastery 0/100)" }]
+}
+```
+
+## Running It
+
+```bash
+npm run installAll # server + client deps
+npm install # root (concurrently)
+cp server/.env.example server/.env
+npm run seed
+npm run dev
+```
+
+- Frontend: http://localhost:5173 · API: http://localhost:5000/api
+
+A fresh learner sees only Beginner unlocked and three Beginner recommendations. Complete sessions with thoughtful reasoning and prompts, and the Mastery Journey panel updates live: concept bars fill, Explorer unlocks at the threshold, and recommendations shift to the newly opened stage targeting the weakest concepts.
+
+## Why This and Not Another Theme Pack
+
+Most existing submissions add themed scenario content on top of the same open-ended loop. This submission changes the loop itself: PyBe now has a defensible, theory-grounded answer to "what should this learner do next, and when are they ready to advance" — the exact gap named in the mentor sessions, and the foundation the V3 "Adaptive Learning Engine" phase assumes will exist.
diff --git a/summership-26-prs/Niranjan Praveen/client/index.html b/summership-26-prs/Niranjan Praveen/client/index.html
new file mode 100644
index 0000000..c880bd3
--- /dev/null
+++ b/summership-26-prs/Niranjan Praveen/client/index.html
@@ -0,0 +1,12 @@
+
+
+
+
+
+ PyBe
+
+
+
+
+
+
diff --git a/summership-26-prs/Niranjan Praveen/client/package-lock.json b/summership-26-prs/Niranjan Praveen/client/package-lock.json
new file mode 100644
index 0000000..b7f5809
--- /dev/null
+++ b/summership-26-prs/Niranjan Praveen/client/package-lock.json
@@ -0,0 +1,1592 @@
+{
+ "name": "pybe-client",
+ "version": "1.0.0",
+ "lockfileVersion": 3,
+ "requires": true,
+ "packages": {
+ "": {
+ "name": "pybe-client",
+ "version": "1.0.0",
+ "dependencies": {
+ "@vitejs/plugin-react": "^4.3.4",
+ "lucide-react": "^0.468.0",
+ "pybe-mern-app": "file:..",
+ "react": "^18.3.1",
+ "react-dom": "^18.3.1",
+ "vite": "^6.0.7"
+ }
+ },
+ "..": {
+ "name": "pybe-mern-app",
+ "version": "1.0.0",
+ "dependencies": {
+ "concurrently": "^9.1.2"
+ },
+ "devDependencies": {}
+ },
+ "node_modules/@babel/code-frame": {
+ "version": "7.29.7",
+ "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.29.7.tgz",
+ "integrity": "sha512-Aup7aUOfpbAUg2ROOJN6Iw5f9DMBlzu0mIkm/malLQFN/YQgO48wCj0Kxa3sEHJvPVFg7siR+qRInwXd2qhQKw==",
+ "dependencies": {
+ "@babel/helper-validator-identifier": "^7.29.7",
+ "js-tokens": "^4.0.0",
+ "picocolors": "^1.1.1"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/compat-data": {
+ "version": "7.29.7",
+ "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.29.7.tgz",
+ "integrity": "sha512-locTkQyKvwIEgBzVrn8693ebc97F2U8ZHjbXwDXJ5Fn2TCpNwTlKcaKLkdHop5c/icOFE7qt7Q9JC5hnKNa6Gg==",
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/core": {
+ "version": "7.29.7",
+ "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.29.7.tgz",
+ "integrity": "sha512-RgHBCvtjbOK2gXSNBNIkNoEc9qoVEtau3hj8gEqKQuL3HZAibKarWFEI3Lfm6EYKkLalOh8eSrj9b+ch9H/VBA==",
+ "dependencies": {
+ "@babel/code-frame": "^7.29.7",
+ "@babel/generator": "^7.29.7",
+ "@babel/helper-compilation-targets": "^7.29.7",
+ "@babel/helper-module-transforms": "^7.29.7",
+ "@babel/helpers": "^7.29.7",
+ "@babel/parser": "^7.29.7",
+ "@babel/template": "^7.29.7",
+ "@babel/traverse": "^7.29.7",
+ "@babel/types": "^7.29.7",
+ "@jridgewell/remapping": "^2.3.5",
+ "convert-source-map": "^2.0.0",
+ "debug": "^4.1.0",
+ "gensync": "^1.0.0-beta.2",
+ "json5": "^2.2.3",
+ "semver": "^6.3.1"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/babel"
+ }
+ },
+ "node_modules/@babel/generator": {
+ "version": "7.29.7",
+ "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.29.7.tgz",
+ "integrity": "sha512-DkXD5OJQaAQIdZ1bt3UZdEnHAn9Imd3IVBdX03UFe+ony9Ojw5pzr9YVKGDY1jt+Gcn/FnGkNf8r+Vj5NOJWtQ==",
+ "dependencies": {
+ "@babel/parser": "^7.29.7",
+ "@babel/types": "^7.29.7",
+ "@jridgewell/gen-mapping": "^0.3.12",
+ "@jridgewell/trace-mapping": "^0.3.28",
+ "jsesc": "^3.0.2"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helper-compilation-targets": {
+ "version": "7.29.7",
+ "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.29.7.tgz",
+ "integrity": "sha512-wem6WaBj4NaVYVdNhLPPVacES6ZJ+KBBfSkTMD3YZxbP3rm3Di85tJU5ljaUNhaOynt+Aj0xruhYuzQBt8n71g==",
+ "dependencies": {
+ "@babel/compat-data": "^7.29.7",
+ "@babel/helper-validator-option": "^7.29.7",
+ "browserslist": "^4.24.0",
+ "lru-cache": "^5.1.1",
+ "semver": "^6.3.1"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helper-globals": {
+ "version": "7.29.7",
+ "resolved": "https://registry.npmjs.org/@babel/helper-globals/-/helper-globals-7.29.7.tgz",
+ "integrity": "sha512-3nQVUAtvkKH9zahfWgw96Jc/uFOmjACE1kQz82E2lqWmHBgjzbNlsC22nuQTfahmWeQtTq5nQ/4Nnd2A1wj4zA==",
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helper-module-imports": {
+ "version": "7.29.7",
+ "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.29.7.tgz",
+ "integrity": "sha512-ejHwrQQYcm9xnTivShn2IDOlIzInN34AXskvq9QicvCtEzq1Vzclu/tKF8Jq1Cg8JG2GL6/EmjgsCT7lXepE3g==",
+ "dependencies": {
+ "@babel/traverse": "^7.29.7",
+ "@babel/types": "^7.29.7"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helper-module-transforms": {
+ "version": "7.29.7",
+ "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.29.7.tgz",
+ "integrity": "sha512-UPUVSyXbOh627KiCIGQSgwWzGeBKLkaJ9PJEdrngIwMSzxLR4jS4+f1f1jb7VzBbg8nFLaYotvVPFCTqdrmTAg==",
+ "dependencies": {
+ "@babel/helper-module-imports": "^7.29.7",
+ "@babel/helper-validator-identifier": "^7.29.7",
+ "@babel/traverse": "^7.29.7"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0"
+ }
+ },
+ "node_modules/@babel/helper-plugin-utils": {
+ "version": "7.29.7",
+ "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.29.7.tgz",
+ "integrity": "sha512-G7sHYigPY17oO5SYWnfD/0MTBwVR781S/JI643e/JhUYgVgWE/61SoW3NH9KWUKyKq5LVh3npif99Wkt6j86Jw==",
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helper-string-parser": {
+ "version": "7.29.7",
+ "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.29.7.tgz",
+ "integrity": "sha512-Pb5ijPrZ89GDH8223L4UP8i6QApWxs04RbPQJTeWDV0/keR2E36MeKnyr6LYmUUvqRRI+Iv87SuF1W6ErINzYw==",
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helper-validator-identifier": {
+ "version": "7.29.7",
+ "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.29.7.tgz",
+ "integrity": "sha512-qehxGkRj55h/ff8EMaJ+cYhyaKlHIxqYDn682wQD7RNp9UujOQsHog2uS0r2vzr4pW+sXf90NeeayjcNaX3fFg==",
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helper-validator-option": {
+ "version": "7.29.7",
+ "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.29.7.tgz",
+ "integrity": "sha512-N9ZErrD+yW5geCDtBqnOoxmR8+tNKiGuxKlDpuJxfsqpa2dFcexaziGAE/qoHLiDDreVNMupxGmSoNlyvsA3gw==",
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helpers": {
+ "version": "7.29.7",
+ "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.29.7.tgz",
+ "integrity": "sha512-1k2lAGRMfHTcwuNYcCNUmaUffmQv8KWMfh2iJUUeRlwlwH4FdNG7mfPI10NPfLHJFThE4Tyr4mv7kTNZOiPuBg==",
+ "dependencies": {
+ "@babel/template": "^7.29.7",
+ "@babel/types": "^7.29.7"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/parser": {
+ "version": "7.29.7",
+ "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.29.7.tgz",
+ "integrity": "sha512-hnORnjP/1P/zFEndoeX+n+t1RwWRJiJpM/jO7FW32Kn9r5+sJB2JWOdYo4L6k78j15eCwY3Gm/7364B1EMwtNg==",
+ "dependencies": {
+ "@babel/types": "^7.29.7"
+ },
+ "bin": {
+ "parser": "bin/babel-parser.js"
+ },
+ "engines": {
+ "node": ">=6.0.0"
+ }
+ },
+ "node_modules/@babel/plugin-transform-react-jsx-self": {
+ "version": "7.29.7",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-self/-/plugin-transform-react-jsx-self-7.29.7.tgz",
+ "integrity": "sha512-TL0hMc9xzy86VD31nUiwzd5otRAcyEPcsegCxolO0PvcXuH1v0kECe/UIznYFihpkvU5wg/jk4v0TTEFfm53fw==",
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.29.7"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-transform-react-jsx-source": {
+ "version": "7.29.7",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-source/-/plugin-transform-react-jsx-source-7.29.7.tgz",
+ "integrity": "sha512-06IyK09H3wi4cGbhDBwp5gUGo0IKtnYa8tyTiephirPCK6fbobVGiXMMI5zLQ4aKEYP3wZ3ArU44o+8KMrSG/Q==",
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.29.7"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/template": {
+ "version": "7.29.7",
+ "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.29.7.tgz",
+ "integrity": "sha512-puq+Gf35oI24FeN11LkoUQFqv9uwNeWpxXZi/Ji3rRIoKAzKnxRaZ+Gkj0vKS9ZCiTESfng1N9LyOyXvo+m+Gg==",
+ "dependencies": {
+ "@babel/code-frame": "^7.29.7",
+ "@babel/parser": "^7.29.7",
+ "@babel/types": "^7.29.7"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/traverse": {
+ "version": "7.29.7",
+ "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.29.7.tgz",
+ "integrity": "sha512-EhlfNQtZ+NK22w5BM61ciuiq1m58ed33Wr1Xan//ZRTy6hgjnwyCffRYwzsGXdASJSUJ1guZILsErh1eQcl+zw==",
+ "dependencies": {
+ "@babel/code-frame": "^7.29.7",
+ "@babel/generator": "^7.29.7",
+ "@babel/helper-globals": "^7.29.7",
+ "@babel/parser": "^7.29.7",
+ "@babel/template": "^7.29.7",
+ "@babel/types": "^7.29.7",
+ "debug": "^4.3.1"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/types": {
+ "version": "7.29.7",
+ "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.29.7.tgz",
+ "integrity": "sha512-4zBIxpPzowiZpusoFkyGVwakdRJUyuH5PxQ/PrqghfdFWWasvnCdPfQXHrenDai+gyLARulZjZowCOj6fjT4pA==",
+ "dependencies": {
+ "@babel/helper-string-parser": "^7.29.7",
+ "@babel/helper-validator-identifier": "^7.29.7"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@esbuild/aix-ppc64": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.12.tgz",
+ "integrity": "sha512-Hhmwd6CInZ3dwpuGTF8fJG6yoWmsToE+vYgD4nytZVxcu1ulHpUQRAB1UJ8+N1Am3Mz4+xOByoQoSZf4D+CpkA==",
+ "cpu": [
+ "ppc64"
+ ],
+ "optional": true,
+ "os": [
+ "aix"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/android-arm": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.12.tgz",
+ "integrity": "sha512-VJ+sKvNA/GE7Ccacc9Cha7bpS8nyzVv0jdVgwNDaR4gDMC/2TTRc33Ip8qrNYUcpkOHUT5OZ0bUcNNVZQ9RLlg==",
+ "cpu": [
+ "arm"
+ ],
+ "optional": true,
+ "os": [
+ "android"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/android-arm64": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.12.tgz",
+ "integrity": "sha512-6AAmLG7zwD1Z159jCKPvAxZd4y/VTO0VkprYy+3N2FtJ8+BQWFXU+OxARIwA46c5tdD9SsKGZ/1ocqBS/gAKHg==",
+ "cpu": [
+ "arm64"
+ ],
+ "optional": true,
+ "os": [
+ "android"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/android-x64": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.12.tgz",
+ "integrity": "sha512-5jbb+2hhDHx5phYR2By8GTWEzn6I9UqR11Kwf22iKbNpYrsmRB18aX/9ivc5cabcUiAT/wM+YIZ6SG9QO6a8kg==",
+ "cpu": [
+ "x64"
+ ],
+ "optional": true,
+ "os": [
+ "android"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/darwin-arm64": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.12.tgz",
+ "integrity": "sha512-N3zl+lxHCifgIlcMUP5016ESkeQjLj/959RxxNYIthIg+CQHInujFuXeWbWMgnTo4cp5XVHqFPmpyu9J65C1Yg==",
+ "cpu": [
+ "arm64"
+ ],
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/darwin-x64": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.12.tgz",
+ "integrity": "sha512-HQ9ka4Kx21qHXwtlTUVbKJOAnmG1ipXhdWTmNXiPzPfWKpXqASVcWdnf2bnL73wgjNrFXAa3yYvBSd9pzfEIpA==",
+ "cpu": [
+ "x64"
+ ],
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/freebsd-arm64": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.12.tgz",
+ "integrity": "sha512-gA0Bx759+7Jve03K1S0vkOu5Lg/85dou3EseOGUes8flVOGxbhDDh/iZaoek11Y8mtyKPGF3vP8XhnkDEAmzeg==",
+ "cpu": [
+ "arm64"
+ ],
+ "optional": true,
+ "os": [
+ "freebsd"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/freebsd-x64": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.12.tgz",
+ "integrity": "sha512-TGbO26Yw2xsHzxtbVFGEXBFH0FRAP7gtcPE7P5yP7wGy7cXK2oO7RyOhL5NLiqTlBh47XhmIUXuGciXEqYFfBQ==",
+ "cpu": [
+ "x64"
+ ],
+ "optional": true,
+ "os": [
+ "freebsd"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/linux-arm": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.12.tgz",
+ "integrity": "sha512-lPDGyC1JPDou8kGcywY0YILzWlhhnRjdof3UlcoqYmS9El818LLfJJc3PXXgZHrHCAKs/Z2SeZtDJr5MrkxtOw==",
+ "cpu": [
+ "arm"
+ ],
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/linux-arm64": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.12.tgz",
+ "integrity": "sha512-8bwX7a8FghIgrupcxb4aUmYDLp8pX06rGh5HqDT7bB+8Rdells6mHvrFHHW2JAOPZUbnjUpKTLg6ECyzvas2AQ==",
+ "cpu": [
+ "arm64"
+ ],
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/linux-ia32": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.12.tgz",
+ "integrity": "sha512-0y9KrdVnbMM2/vG8KfU0byhUN+EFCny9+8g202gYqSSVMonbsCfLjUO+rCci7pM0WBEtz+oK/PIwHkzxkyharA==",
+ "cpu": [
+ "ia32"
+ ],
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/linux-loong64": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.12.tgz",
+ "integrity": "sha512-h///Lr5a9rib/v1GGqXVGzjL4TMvVTv+s1DPoxQdz7l/AYv6LDSxdIwzxkrPW438oUXiDtwM10o9PmwS/6Z0Ng==",
+ "cpu": [
+ "loong64"
+ ],
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/linux-mips64el": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.12.tgz",
+ "integrity": "sha512-iyRrM1Pzy9GFMDLsXn1iHUm18nhKnNMWscjmp4+hpafcZjrr2WbT//d20xaGljXDBYHqRcl8HnxbX6uaA/eGVw==",
+ "cpu": [
+ "mips64el"
+ ],
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/linux-ppc64": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.12.tgz",
+ "integrity": "sha512-9meM/lRXxMi5PSUqEXRCtVjEZBGwB7P/D4yT8UG/mwIdze2aV4Vo6U5gD3+RsoHXKkHCfSxZKzmDssVlRj1QQA==",
+ "cpu": [
+ "ppc64"
+ ],
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/linux-riscv64": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.12.tgz",
+ "integrity": "sha512-Zr7KR4hgKUpWAwb1f3o5ygT04MzqVrGEGXGLnj15YQDJErYu/BGg+wmFlIDOdJp0PmB0lLvxFIOXZgFRrdjR0w==",
+ "cpu": [
+ "riscv64"
+ ],
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/linux-s390x": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.12.tgz",
+ "integrity": "sha512-MsKncOcgTNvdtiISc/jZs/Zf8d0cl/t3gYWX8J9ubBnVOwlk65UIEEvgBORTiljloIWnBzLs4qhzPkJcitIzIg==",
+ "cpu": [
+ "s390x"
+ ],
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/linux-x64": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.12.tgz",
+ "integrity": "sha512-uqZMTLr/zR/ed4jIGnwSLkaHmPjOjJvnm6TVVitAa08SLS9Z0VM8wIRx7gWbJB5/J54YuIMInDquWyYvQLZkgw==",
+ "cpu": [
+ "x64"
+ ],
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/netbsd-arm64": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.12.tgz",
+ "integrity": "sha512-xXwcTq4GhRM7J9A8Gv5boanHhRa/Q9KLVmcyXHCTaM4wKfIpWkdXiMog/KsnxzJ0A1+nD+zoecuzqPmCRyBGjg==",
+ "cpu": [
+ "arm64"
+ ],
+ "optional": true,
+ "os": [
+ "netbsd"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/netbsd-x64": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.12.tgz",
+ "integrity": "sha512-Ld5pTlzPy3YwGec4OuHh1aCVCRvOXdH8DgRjfDy/oumVovmuSzWfnSJg+VtakB9Cm0gxNO9BzWkj6mtO1FMXkQ==",
+ "cpu": [
+ "x64"
+ ],
+ "optional": true,
+ "os": [
+ "netbsd"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/openbsd-arm64": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.12.tgz",
+ "integrity": "sha512-fF96T6KsBo/pkQI950FARU9apGNTSlZGsv1jZBAlcLL1MLjLNIWPBkj5NlSz8aAzYKg+eNqknrUJ24QBybeR5A==",
+ "cpu": [
+ "arm64"
+ ],
+ "optional": true,
+ "os": [
+ "openbsd"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/openbsd-x64": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.12.tgz",
+ "integrity": "sha512-MZyXUkZHjQxUvzK7rN8DJ3SRmrVrke8ZyRusHlP+kuwqTcfWLyqMOE3sScPPyeIXN/mDJIfGXvcMqCgYKekoQw==",
+ "cpu": [
+ "x64"
+ ],
+ "optional": true,
+ "os": [
+ "openbsd"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/openharmony-arm64": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.25.12.tgz",
+ "integrity": "sha512-rm0YWsqUSRrjncSXGA7Zv78Nbnw4XL6/dzr20cyrQf7ZmRcsovpcRBdhD43Nuk3y7XIoW2OxMVvwuRvk9XdASg==",
+ "cpu": [
+ "arm64"
+ ],
+ "optional": true,
+ "os": [
+ "openharmony"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/sunos-x64": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.12.tgz",
+ "integrity": "sha512-3wGSCDyuTHQUzt0nV7bocDy72r2lI33QL3gkDNGkod22EsYl04sMf0qLb8luNKTOmgF/eDEDP5BFNwoBKH441w==",
+ "cpu": [
+ "x64"
+ ],
+ "optional": true,
+ "os": [
+ "sunos"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/win32-arm64": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.12.tgz",
+ "integrity": "sha512-rMmLrur64A7+DKlnSuwqUdRKyd3UE7oPJZmnljqEptesKM8wx9J8gx5u0+9Pq0fQQW8vqeKebwNXdfOyP+8Bsg==",
+ "cpu": [
+ "arm64"
+ ],
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/win32-ia32": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.12.tgz",
+ "integrity": "sha512-HkqnmmBoCbCwxUKKNPBixiWDGCpQGVsrQfJoVGYLPT41XWF8lHuE5N6WhVia2n4o5QK5M4tYr21827fNhi4byQ==",
+ "cpu": [
+ "ia32"
+ ],
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/win32-x64": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.12.tgz",
+ "integrity": "sha512-alJC0uCZpTFrSL0CCDjcgleBXPnCrEAhTBILpeAp7M/OFgoqtAetfBzX0xM00MUsVVPpVjlPuMbREqnZCXaTnA==",
+ "cpu": [
+ "x64"
+ ],
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@jridgewell/gen-mapping": {
+ "version": "0.3.13",
+ "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz",
+ "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==",
+ "dependencies": {
+ "@jridgewell/sourcemap-codec": "^1.5.0",
+ "@jridgewell/trace-mapping": "^0.3.24"
+ }
+ },
+ "node_modules/@jridgewell/remapping": {
+ "version": "2.3.5",
+ "resolved": "https://registry.npmjs.org/@jridgewell/remapping/-/remapping-2.3.5.tgz",
+ "integrity": "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==",
+ "dependencies": {
+ "@jridgewell/gen-mapping": "^0.3.5",
+ "@jridgewell/trace-mapping": "^0.3.24"
+ }
+ },
+ "node_modules/@jridgewell/resolve-uri": {
+ "version": "3.1.2",
+ "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz",
+ "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==",
+ "engines": {
+ "node": ">=6.0.0"
+ }
+ },
+ "node_modules/@jridgewell/sourcemap-codec": {
+ "version": "1.5.5",
+ "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz",
+ "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og=="
+ },
+ "node_modules/@jridgewell/trace-mapping": {
+ "version": "0.3.31",
+ "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz",
+ "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==",
+ "dependencies": {
+ "@jridgewell/resolve-uri": "^3.1.0",
+ "@jridgewell/sourcemap-codec": "^1.4.14"
+ }
+ },
+ "node_modules/@rolldown/pluginutils": {
+ "version": "1.0.0-beta.27",
+ "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.0-beta.27.tgz",
+ "integrity": "sha512-+d0F4MKMCbeVUJwG96uQ4SgAznZNSq93I3V+9NHA4OpvqG8mRCpGdKmK8l/dl02h2CCDHwW2FqilnTyDcAnqjA=="
+ },
+ "node_modules/@rollup/rollup-android-arm-eabi": {
+ "version": "4.62.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.62.0.tgz",
+ "integrity": "sha512-IPIQ55ythEHkfEd9jMEi32OQ7SxURsGA43JI22lj01OLZNt2NUbJX8YUHxkVWyQ6daHPNn0truF5nSj3DQp6YQ==",
+ "cpu": [
+ "arm"
+ ],
+ "optional": true,
+ "os": [
+ "android"
+ ]
+ },
+ "node_modules/@rollup/rollup-android-arm64": {
+ "version": "4.62.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.62.0.tgz",
+ "integrity": "sha512-M6s9cr10MibETyo8JsOkq+Lo1+lU6hcvb1MApnUql5qte/5hMEgzlN8/ReIKNfRV8rrqX50W1BX9zoUhC192RA==",
+ "cpu": [
+ "arm64"
+ ],
+ "optional": true,
+ "os": [
+ "android"
+ ]
+ },
+ "node_modules/@rollup/rollup-darwin-arm64": {
+ "version": "4.62.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.62.0.tgz",
+ "integrity": "sha512-BqCoMoIbn0keKys+dEAdBa70EtOwV1bEsQCUgU9FdiZmmMge/Zk7LlkYGqbrdHR+Frnt0E1FOanly+rlwvvQzw==",
+ "cpu": [
+ "arm64"
+ ],
+ "optional": true,
+ "os": [
+ "darwin"
+ ]
+ },
+ "node_modules/@rollup/rollup-darwin-x64": {
+ "version": "4.62.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.62.0.tgz",
+ "integrity": "sha512-SIMzST3VFNXDAbeIWDWiFCNM5qncUBDWaEV7NfE7oZbDt2mgfW4MvbKdbYiGOLoM32gbTv608UMd0XktEYSD7w==",
+ "cpu": [
+ "x64"
+ ],
+ "optional": true,
+ "os": [
+ "darwin"
+ ]
+ },
+ "node_modules/@rollup/rollup-freebsd-arm64": {
+ "version": "4.62.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.62.0.tgz",
+ "integrity": "sha512-ezjfSQMP7ArdUsbBwbQIfwAlhE84I2iVnzQNCFSveqV42q+BmKlzVpf7mxv5EchLcoWU4y6/heFzVg1F+hodUQ==",
+ "cpu": [
+ "arm64"
+ ],
+ "optional": true,
+ "os": [
+ "freebsd"
+ ]
+ },
+ "node_modules/@rollup/rollup-freebsd-x64": {
+ "version": "4.62.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.62.0.tgz",
+ "integrity": "sha512-9+qTWGW9AZRhnUgwtTwzNwcPlL87ngkeN0LA+q1bADvmY9aNvWaF2TFW8BZgnQPYxpDI7+rMVLivcd4V737TAQ==",
+ "cpu": [
+ "x64"
+ ],
+ "optional": true,
+ "os": [
+ "freebsd"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-arm-gnueabihf": {
+ "version": "4.62.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.62.0.tgz",
+ "integrity": "sha512-T1dMEQhXA/jkJ/jyMIw9IovK8bSUq7A8kLIlvZTb/6YIVsp2zLavr4F3oyllHWo7eIVJRyE5n3tUjQJEbE1IuQ==",
+ "cpu": [
+ "arm"
+ ],
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-arm-musleabihf": {
+ "version": "4.62.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.62.0.tgz",
+ "integrity": "sha512-2as0LgT7qQpyceQq6VUJYnumUMUrgGQCWIiDIN9DE0/tglsk6o66uCB4f3djRawAltvfCNLyZZrsqbPA6inCsA==",
+ "cpu": [
+ "arm"
+ ],
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-arm64-gnu": {
+ "version": "4.62.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.62.0.tgz",
+ "integrity": "sha512-bVURMg+6eNN9C/yc0aVjooZcwTTtYF4YW3xta5pP0//r3o1V8gXEHXWCndj47w/HhwsFroZrFhR+6uQP5T0n0g==",
+ "cpu": [
+ "arm64"
+ ],
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-arm64-musl": {
+ "version": "4.62.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.62.0.tgz",
+ "integrity": "sha512-Ful8pM/2yYI83PViWdFdpZhdI8HJ5qsXANe5atypbHDf+KIBBDsZsbyy8hbXnULVvW9NsTh5DHwbcBftyLTfiw==",
+ "cpu": [
+ "arm64"
+ ],
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-loong64-gnu": {
+ "version": "4.62.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.62.0.tgz",
+ "integrity": "sha512-9Gp/DgrkzfUBmNPVTyPTvay+4xEP7M/clXpj3efXBcm6uTIVIgDg4rqUpqKXvLEuFRVuEpSAOkhgNeecvaZ4Cg==",
+ "cpu": [
+ "loong64"
+ ],
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-loong64-musl": {
+ "version": "4.62.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-musl/-/rollup-linux-loong64-musl-4.62.0.tgz",
+ "integrity": "sha512-m9tsJz54LUXkSYM8+8PG81B9IKK5r+2T0clMq4QrS16xFosufU7firBDAZEsDheDs7wTlP7h3++S7lMsU955HA==",
+ "cpu": [
+ "loong64"
+ ],
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-ppc64-gnu": {
+ "version": "4.62.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.62.0.tgz",
+ "integrity": "sha512-3UvJ5PNVU16aJf6M3tFI24pWzAl2/ynfbyRN3ICyQajK1lSkrnVYNnLz3v04J32qKa0FczJc22zeToc0lr2A3w==",
+ "cpu": [
+ "ppc64"
+ ],
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-ppc64-musl": {
+ "version": "4.62.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-musl/-/rollup-linux-ppc64-musl-4.62.0.tgz",
+ "integrity": "sha512-vRWUAbYLGHBZS6Q8Msb2sfnf1fvJf+47t8l/TwOerM2qArzy+IeNMTHrYLHXh95h8MoatPHI5hhSZNs+mGXKPg==",
+ "cpu": [
+ "ppc64"
+ ],
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-riscv64-gnu": {
+ "version": "4.62.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.62.0.tgz",
+ "integrity": "sha512-c00T5SYENHAt86cfW47URaP3Us5vLC/4QO7GYud1G5VNRffCwwCuBspwqYrriuJB+5m0WFzClCn9wed0FBjKvg==",
+ "cpu": [
+ "riscv64"
+ ],
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-riscv64-musl": {
+ "version": "4.62.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.62.0.tgz",
+ "integrity": "sha512-krrCDilhXOwFkSkO3Wm9I/f9H0L92XHHwy2fwxjukxIbh0dem8gZqOW5Y8BsHrpJv5qwlRBV+Wl4ZFyRWhUpwg==",
+ "cpu": [
+ "riscv64"
+ ],
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-s390x-gnu": {
+ "version": "4.62.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.62.0.tgz",
+ "integrity": "sha512-7pfYFSTc4/rUC/FtAI0Qp6QthDBCIi6/AuP1xYqFk5vanI6KnL5dWKP60OM/05LOsbwTmIcvr6eXC4CJuJ75IA==",
+ "cpu": [
+ "s390x"
+ ],
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-x64-gnu": {
+ "version": "4.62.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.62.0.tgz",
+ "integrity": "sha512-7SDIalKeIpG0Ifogbbdn58HmSotYMlf23K3dCJEmiVd9Fg36Vmni82iPQec27N3wY4Bvbxftkxz6vSx9OcouTg==",
+ "cpu": [
+ "x64"
+ ],
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-x64-musl": {
+ "version": "4.62.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.62.0.tgz",
+ "integrity": "sha512-eRZevouTH2i1HeAVLqJuLnt256krQkGY0TN6WsTmsIhuzbh457HuWDMakKwmi0Cjadux983CoSr8Lim2QhUIFw==",
+ "cpu": [
+ "x64"
+ ],
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-openbsd-x64": {
+ "version": "4.62.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-openbsd-x64/-/rollup-openbsd-x64-4.62.0.tgz",
+ "integrity": "sha512-3oVS7FLGa4U1qcvao9ylGxrjXZyUQqR8UwxEcnUEyPX53O/C/mKDZegNXTdHCP+h3e6ta/f1EN38Yif1mmZHYg==",
+ "cpu": [
+ "x64"
+ ],
+ "optional": true,
+ "os": [
+ "openbsd"
+ ]
+ },
+ "node_modules/@rollup/rollup-openharmony-arm64": {
+ "version": "4.62.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.62.0.tgz",
+ "integrity": "sha512-yTB9TgfWj5wHe5QgktAgXTLLot1gvEjl1NiPPAUiCs4oPrIWFl5V4nC3GrkNdj9LaAU4s94nVrGbGOCqUpyWsg==",
+ "cpu": [
+ "arm64"
+ ],
+ "optional": true,
+ "os": [
+ "openharmony"
+ ]
+ },
+ "node_modules/@rollup/rollup-win32-arm64-msvc": {
+ "version": "4.62.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.62.0.tgz",
+ "integrity": "sha512-5LOhoaesY3doG1c+ac/2JtgREpKoJr5bUHH8tKY0V8di7+uSV6BwLs2PlR0/yzefGOkR+wE7ZolZphHCsyG5Rw==",
+ "cpu": [
+ "arm64"
+ ],
+ "optional": true,
+ "os": [
+ "win32"
+ ]
+ },
+ "node_modules/@rollup/rollup-win32-ia32-msvc": {
+ "version": "4.62.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.62.0.tgz",
+ "integrity": "sha512-yYkWHhmbhRTWTnWos5HC4GcPQfjlzzCNbM9e/+GXrLuaBXYA3qSDR9f0Vgufd5S8yX81U8jPKp7ZnAjZFMtRnw==",
+ "cpu": [
+ "ia32"
+ ],
+ "optional": true,
+ "os": [
+ "win32"
+ ]
+ },
+ "node_modules/@rollup/rollup-win32-x64-gnu": {
+ "version": "4.62.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.62.0.tgz",
+ "integrity": "sha512-SoTb6lPg25xZlA2ibwQ++ahCCnH+FP0qmEuafMJ4gznZKOlXioKEAeJLgCrqjM98ACziXM9V1amFjICVL4IFoA==",
+ "cpu": [
+ "x64"
+ ],
+ "optional": true,
+ "os": [
+ "win32"
+ ]
+ },
+ "node_modules/@rollup/rollup-win32-x64-msvc": {
+ "version": "4.62.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.62.0.tgz",
+ "integrity": "sha512-5L+T1fMX4RIEBoZzT0+sQ0PhTS36NULFmMXtl1TZo44TMAROIMHbZufSOjVWt/Y622BtxgxtaNOokbTDvfsrZA==",
+ "cpu": [
+ "x64"
+ ],
+ "optional": true,
+ "os": [
+ "win32"
+ ]
+ },
+ "node_modules/@types/babel__core": {
+ "version": "7.20.5",
+ "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz",
+ "integrity": "sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==",
+ "dependencies": {
+ "@babel/parser": "^7.20.7",
+ "@babel/types": "^7.20.7",
+ "@types/babel__generator": "*",
+ "@types/babel__template": "*",
+ "@types/babel__traverse": "*"
+ }
+ },
+ "node_modules/@types/babel__generator": {
+ "version": "7.27.0",
+ "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.27.0.tgz",
+ "integrity": "sha512-ufFd2Xi92OAVPYsy+P4n7/U7e68fex0+Ee8gSG9KX7eo084CWiQ4sdxktvdl0bOPupXtVJPY19zk6EwWqUQ8lg==",
+ "dependencies": {
+ "@babel/types": "^7.0.0"
+ }
+ },
+ "node_modules/@types/babel__template": {
+ "version": "7.4.4",
+ "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.4.tgz",
+ "integrity": "sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==",
+ "dependencies": {
+ "@babel/parser": "^7.1.0",
+ "@babel/types": "^7.0.0"
+ }
+ },
+ "node_modules/@types/babel__traverse": {
+ "version": "7.28.0",
+ "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.28.0.tgz",
+ "integrity": "sha512-8PvcXf70gTDZBgt9ptxJ8elBeBjcLOAcOtoO/mPJjtji1+CdGbHgm77om1GrsPxsiE+uXIpNSK64UYaIwQXd4Q==",
+ "dependencies": {
+ "@babel/types": "^7.28.2"
+ }
+ },
+ "node_modules/@types/estree": {
+ "version": "1.0.9",
+ "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.9.tgz",
+ "integrity": "sha512-GhdPgy1el4/ImP05X05Uw4cw2/M93BCUmnEvWZNStlCzEKME4Fkk+YpoA5OiHNQmoS7Cafb8Xa3Pya8m1Qrzeg=="
+ },
+ "node_modules/@vitejs/plugin-react": {
+ "version": "4.7.0",
+ "resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-4.7.0.tgz",
+ "integrity": "sha512-gUu9hwfWvvEDBBmgtAowQCojwZmJ5mcLn3aufeCsitijs3+f2NsrPtlAWIR6OPiqljl96GVCUbLe0HyqIpVaoA==",
+ "dependencies": {
+ "@babel/core": "^7.28.0",
+ "@babel/plugin-transform-react-jsx-self": "^7.27.1",
+ "@babel/plugin-transform-react-jsx-source": "^7.27.1",
+ "@rolldown/pluginutils": "1.0.0-beta.27",
+ "@types/babel__core": "^7.20.5",
+ "react-refresh": "^0.17.0"
+ },
+ "engines": {
+ "node": "^14.18.0 || >=16.0.0"
+ },
+ "peerDependencies": {
+ "vite": "^4.2.0 || ^5.0.0 || ^6.0.0 || ^7.0.0"
+ }
+ },
+ "node_modules/baseline-browser-mapping": {
+ "version": "2.10.38",
+ "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.10.38.tgz",
+ "integrity": "sha512-31/02mVB4yuQU6adKk5SlY6m+mxDwUq5KZkyYgnLrrKl7TEm1+3PyDtDBz2kOv/wxZz41GHsvV1A/u6RmiyBvw==",
+ "bin": {
+ "baseline-browser-mapping": "dist/cli.cjs"
+ },
+ "engines": {
+ "node": ">=6.0.0"
+ }
+ },
+ "node_modules/browserslist": {
+ "version": "4.28.2",
+ "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.28.2.tgz",
+ "integrity": "sha512-48xSriZYYg+8qXna9kwqjIVzuQxi+KYWp2+5nCYnYKPTr0LvD89Jqk2Or5ogxz0NUMfIjhh2lIUX/LyX9B4oIg==",
+ "funding": [
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/browserslist"
+ },
+ {
+ "type": "tidelift",
+ "url": "https://tidelift.com/funding/github/npm/browserslist"
+ },
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/ai"
+ }
+ ],
+ "dependencies": {
+ "baseline-browser-mapping": "^2.10.12",
+ "caniuse-lite": "^1.0.30001782",
+ "electron-to-chromium": "^1.5.328",
+ "node-releases": "^2.0.36",
+ "update-browserslist-db": "^1.2.3"
+ },
+ "bin": {
+ "browserslist": "cli.js"
+ },
+ "engines": {
+ "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7"
+ }
+ },
+ "node_modules/caniuse-lite": {
+ "version": "1.0.30001799",
+ "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001799.tgz",
+ "integrity": "sha512-hG1bReV+OUU+MOqK4t/ZWI0tZOyz3rqS9XuhOUz1cIcbwBKjOyJEJuw9ER5JuNyqxNk8u/JUVbGibBOL1yrjFw==",
+ "funding": [
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/browserslist"
+ },
+ {
+ "type": "tidelift",
+ "url": "https://tidelift.com/funding/github/npm/caniuse-lite"
+ },
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/ai"
+ }
+ ]
+ },
+ "node_modules/convert-source-map": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz",
+ "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg=="
+ },
+ "node_modules/debug": {
+ "version": "4.4.3",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz",
+ "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==",
+ "dependencies": {
+ "ms": "^2.1.3"
+ },
+ "engines": {
+ "node": ">=6.0"
+ },
+ "peerDependenciesMeta": {
+ "supports-color": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/electron-to-chromium": {
+ "version": "1.5.376",
+ "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.376.tgz",
+ "integrity": "sha512-cUVA7/RvbFTEuw/i3obUwDTRIXojaxkResf+ibByPFxjc6XK3VNtcQXV0NSbAlJ0FMjcJGgftVVB4Qo184EXvA=="
+ },
+ "node_modules/esbuild": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.12.tgz",
+ "integrity": "sha512-bbPBYYrtZbkt6Os6FiTLCTFxvq4tt3JKall1vRwshA3fdVztsLAatFaZobhkBC8/BrPetoa0oksYoKXoG4ryJg==",
+ "hasInstallScript": true,
+ "bin": {
+ "esbuild": "bin/esbuild"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "optionalDependencies": {
+ "@esbuild/aix-ppc64": "0.25.12",
+ "@esbuild/android-arm": "0.25.12",
+ "@esbuild/android-arm64": "0.25.12",
+ "@esbuild/android-x64": "0.25.12",
+ "@esbuild/darwin-arm64": "0.25.12",
+ "@esbuild/darwin-x64": "0.25.12",
+ "@esbuild/freebsd-arm64": "0.25.12",
+ "@esbuild/freebsd-x64": "0.25.12",
+ "@esbuild/linux-arm": "0.25.12",
+ "@esbuild/linux-arm64": "0.25.12",
+ "@esbuild/linux-ia32": "0.25.12",
+ "@esbuild/linux-loong64": "0.25.12",
+ "@esbuild/linux-mips64el": "0.25.12",
+ "@esbuild/linux-ppc64": "0.25.12",
+ "@esbuild/linux-riscv64": "0.25.12",
+ "@esbuild/linux-s390x": "0.25.12",
+ "@esbuild/linux-x64": "0.25.12",
+ "@esbuild/netbsd-arm64": "0.25.12",
+ "@esbuild/netbsd-x64": "0.25.12",
+ "@esbuild/openbsd-arm64": "0.25.12",
+ "@esbuild/openbsd-x64": "0.25.12",
+ "@esbuild/openharmony-arm64": "0.25.12",
+ "@esbuild/sunos-x64": "0.25.12",
+ "@esbuild/win32-arm64": "0.25.12",
+ "@esbuild/win32-ia32": "0.25.12",
+ "@esbuild/win32-x64": "0.25.12"
+ }
+ },
+ "node_modules/escalade": {
+ "version": "3.2.0",
+ "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz",
+ "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==",
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/fdir": {
+ "version": "6.5.0",
+ "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz",
+ "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==",
+ "engines": {
+ "node": ">=12.0.0"
+ },
+ "peerDependencies": {
+ "picomatch": "^3 || ^4"
+ },
+ "peerDependenciesMeta": {
+ "picomatch": {
+ "optional": true
+ }
+ }
+ },
+ "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,
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": "^8.16.0 || ^10.6.0 || >=11.0.0"
+ }
+ },
+ "node_modules/gensync": {
+ "version": "1.0.0-beta.2",
+ "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz",
+ "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==",
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/js-tokens": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
+ "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ=="
+ },
+ "node_modules/jsesc": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz",
+ "integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==",
+ "bin": {
+ "jsesc": "bin/jsesc"
+ },
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/json5": {
+ "version": "2.2.3",
+ "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz",
+ "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==",
+ "bin": {
+ "json5": "lib/cli.js"
+ },
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/loose-envify": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz",
+ "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==",
+ "dependencies": {
+ "js-tokens": "^3.0.0 || ^4.0.0"
+ },
+ "bin": {
+ "loose-envify": "cli.js"
+ }
+ },
+ "node_modules/lru-cache": {
+ "version": "5.1.1",
+ "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz",
+ "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==",
+ "dependencies": {
+ "yallist": "^3.0.2"
+ }
+ },
+ "node_modules/lucide-react": {
+ "version": "0.468.0",
+ "resolved": "https://registry.npmjs.org/lucide-react/-/lucide-react-0.468.0.tgz",
+ "integrity": "sha512-6koYRhnM2N0GGZIdXzSeiNwguv1gt/FAjZOiPl76roBi3xKEXa4WmfpxgQwTTL4KipXjefrnf3oV4IsYhi4JFA==",
+ "peerDependencies": {
+ "react": "^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0-rc"
+ }
+ },
+ "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=="
+ },
+ "node_modules/nanoid": {
+ "version": "3.3.13",
+ "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.13.tgz",
+ "integrity": "sha512-sPdqC6ByMVVGvF1ynvvMo0/o+oD1VX7DaHhijt1bFgjvBkHBib4t49GoNDhf2NDta4oeUNlaGbSt5K7qjZ955Q==",
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/ai"
+ }
+ ],
+ "bin": {
+ "nanoid": "bin/nanoid.cjs"
+ },
+ "engines": {
+ "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1"
+ }
+ },
+ "node_modules/node-releases": {
+ "version": "2.0.48",
+ "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.48.tgz",
+ "integrity": "sha512-1uz8041X6LoI6ZSdZacM9lVY28vuzDlSKitnpbSNK0RfKoIJkX29NBPVEFXhnuSuEOA9Ww0xnPJ+ILWbGAv8DA==",
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/picocolors": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz",
+ "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA=="
+ },
+ "node_modules/picomatch": {
+ "version": "4.0.4",
+ "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.4.tgz",
+ "integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==",
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/jonschlinkert"
+ }
+ },
+ "node_modules/postcss": {
+ "version": "8.5.15",
+ "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.15.tgz",
+ "integrity": "sha512-FfR8sjd4em2T6fb3I2MwAJU7HWVMr9zba+enmQeeWFfCbm+UOC/0X4DS8XtpUTMwWMGbjKYP7xjfNekzyGmB3A==",
+ "funding": [
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/postcss/"
+ },
+ {
+ "type": "tidelift",
+ "url": "https://tidelift.com/funding/github/npm/postcss"
+ },
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/ai"
+ }
+ ],
+ "dependencies": {
+ "nanoid": "^3.3.12",
+ "picocolors": "^1.1.1",
+ "source-map-js": "^1.2.1"
+ },
+ "engines": {
+ "node": "^10 || ^12 || >=14"
+ }
+ },
+ "node_modules/pybe-mern-app": {
+ "resolved": "..",
+ "link": true
+ },
+ "node_modules/react": {
+ "version": "18.3.1",
+ "resolved": "https://registry.npmjs.org/react/-/react-18.3.1.tgz",
+ "integrity": "sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==",
+ "dependencies": {
+ "loose-envify": "^1.1.0"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/react-dom": {
+ "version": "18.3.1",
+ "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.3.1.tgz",
+ "integrity": "sha512-5m4nQKp+rZRb09LNH59GM4BxTh9251/ylbKIbpe7TpGxfJ+9kv6BLkLBXIjjspbgbnIBNqlI23tRnTWT0snUIw==",
+ "dependencies": {
+ "loose-envify": "^1.1.0",
+ "scheduler": "^0.23.2"
+ },
+ "peerDependencies": {
+ "react": "^18.3.1"
+ }
+ },
+ "node_modules/react-refresh": {
+ "version": "0.17.0",
+ "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.17.0.tgz",
+ "integrity": "sha512-z6F7K9bV85EfseRCp2bzrpyQ0Gkw1uLoCel9XBVWPg/TjRj94SkJzUTGfOa4bs7iJvBWtQG0Wq7wnI0syw3EBQ==",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/rollup": {
+ "version": "4.62.0",
+ "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.62.0.tgz",
+ "integrity": "sha512-nc72Wgq62I7rtDV4izT5/aaS0zxy3kttkinf9586ApknY3jZO9NYsmtc24fUckA0X7Q2v+ML4a15pdUlV5V/jA==",
+ "dependencies": {
+ "@types/estree": "1.0.9"
+ },
+ "bin": {
+ "rollup": "dist/bin/rollup"
+ },
+ "engines": {
+ "node": ">=18.0.0",
+ "npm": ">=8.0.0"
+ },
+ "optionalDependencies": {
+ "@rollup/rollup-android-arm-eabi": "4.62.0",
+ "@rollup/rollup-android-arm64": "4.62.0",
+ "@rollup/rollup-darwin-arm64": "4.62.0",
+ "@rollup/rollup-darwin-x64": "4.62.0",
+ "@rollup/rollup-freebsd-arm64": "4.62.0",
+ "@rollup/rollup-freebsd-x64": "4.62.0",
+ "@rollup/rollup-linux-arm-gnueabihf": "4.62.0",
+ "@rollup/rollup-linux-arm-musleabihf": "4.62.0",
+ "@rollup/rollup-linux-arm64-gnu": "4.62.0",
+ "@rollup/rollup-linux-arm64-musl": "4.62.0",
+ "@rollup/rollup-linux-loong64-gnu": "4.62.0",
+ "@rollup/rollup-linux-loong64-musl": "4.62.0",
+ "@rollup/rollup-linux-ppc64-gnu": "4.62.0",
+ "@rollup/rollup-linux-ppc64-musl": "4.62.0",
+ "@rollup/rollup-linux-riscv64-gnu": "4.62.0",
+ "@rollup/rollup-linux-riscv64-musl": "4.62.0",
+ "@rollup/rollup-linux-s390x-gnu": "4.62.0",
+ "@rollup/rollup-linux-x64-gnu": "4.62.0",
+ "@rollup/rollup-linux-x64-musl": "4.62.0",
+ "@rollup/rollup-openbsd-x64": "4.62.0",
+ "@rollup/rollup-openharmony-arm64": "4.62.0",
+ "@rollup/rollup-win32-arm64-msvc": "4.62.0",
+ "@rollup/rollup-win32-ia32-msvc": "4.62.0",
+ "@rollup/rollup-win32-x64-gnu": "4.62.0",
+ "@rollup/rollup-win32-x64-msvc": "4.62.0",
+ "fsevents": "~2.3.2"
+ }
+ },
+ "node_modules/scheduler": {
+ "version": "0.23.2",
+ "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.2.tgz",
+ "integrity": "sha512-UOShsPwz7NrMUqhR6t0hWjFduvOzbtv7toDH1/hIrfRNIDBnnBWd0CwJTGvTpngVlmwGCdP9/Zl/tVrDqcuYzQ==",
+ "dependencies": {
+ "loose-envify": "^1.1.0"
+ }
+ },
+ "node_modules/semver": {
+ "version": "6.3.1",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz",
+ "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==",
+ "bin": {
+ "semver": "bin/semver.js"
+ }
+ },
+ "node_modules/source-map-js": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz",
+ "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/tinyglobby": {
+ "version": "0.2.17",
+ "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.17.tgz",
+ "integrity": "sha512-wXR/dYpcqKmfWpEdZjiKJOwCNFndD0DMnrW/cYjVGttEkBfVgcLFHoNrlj47mjOVic9yyNu65alsgF4NQyTa2g==",
+ "dependencies": {
+ "fdir": "^6.5.0",
+ "picomatch": "^4.0.4"
+ },
+ "engines": {
+ "node": ">=12.0.0"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/SuperchupuDev"
+ }
+ },
+ "node_modules/update-browserslist-db": {
+ "version": "1.2.3",
+ "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.2.3.tgz",
+ "integrity": "sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w==",
+ "funding": [
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/browserslist"
+ },
+ {
+ "type": "tidelift",
+ "url": "https://tidelift.com/funding/github/npm/browserslist"
+ },
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/ai"
+ }
+ ],
+ "dependencies": {
+ "escalade": "^3.2.0",
+ "picocolors": "^1.1.1"
+ },
+ "bin": {
+ "update-browserslist-db": "cli.js"
+ },
+ "peerDependencies": {
+ "browserslist": ">= 4.21.0"
+ }
+ },
+ "node_modules/vite": {
+ "version": "6.4.3",
+ "resolved": "https://registry.npmjs.org/vite/-/vite-6.4.3.tgz",
+ "integrity": "sha512-NTKlcQjlAK7MlQoyb6LgaqHc8sso/pVyUJYWMws3jg21uTJw/LddqIFPcPqP6PzpgbIcZyKI85sFE4HBrQDA8A==",
+ "dependencies": {
+ "esbuild": "^0.25.0",
+ "fdir": "^6.4.4",
+ "picomatch": "^4.0.2",
+ "postcss": "^8.5.3",
+ "rollup": "^4.34.9",
+ "tinyglobby": "^0.2.13"
+ },
+ "bin": {
+ "vite": "bin/vite.js"
+ },
+ "engines": {
+ "node": "^18.0.0 || ^20.0.0 || >=22.0.0"
+ },
+ "funding": {
+ "url": "https://github.com/vitejs/vite?sponsor=1"
+ },
+ "optionalDependencies": {
+ "fsevents": "~2.3.3"
+ },
+ "peerDependencies": {
+ "@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0",
+ "jiti": ">=1.21.0",
+ "less": "*",
+ "lightningcss": "^1.21.0",
+ "sass": "*",
+ "sass-embedded": "*",
+ "stylus": "*",
+ "sugarss": "*",
+ "terser": "^5.16.0",
+ "tsx": "^4.8.1",
+ "yaml": "^2.4.2"
+ },
+ "peerDependenciesMeta": {
+ "@types/node": {
+ "optional": true
+ },
+ "jiti": {
+ "optional": true
+ },
+ "less": {
+ "optional": true
+ },
+ "lightningcss": {
+ "optional": true
+ },
+ "sass": {
+ "optional": true
+ },
+ "sass-embedded": {
+ "optional": true
+ },
+ "stylus": {
+ "optional": true
+ },
+ "sugarss": {
+ "optional": true
+ },
+ "terser": {
+ "optional": true
+ },
+ "tsx": {
+ "optional": true
+ },
+ "yaml": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/yallist": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz",
+ "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g=="
+ }
+ }
+}
diff --git a/summership-26-prs/Niranjan Praveen/client/package.json b/summership-26-prs/Niranjan Praveen/client/package.json
new file mode 100644
index 0000000..d45cb82
--- /dev/null
+++ b/summership-26-prs/Niranjan Praveen/client/package.json
@@ -0,0 +1,18 @@
+{
+ "name": "pybe-client",
+ "version": "1.0.0",
+ "private": true,
+ "scripts": {
+ "dev": "vite",
+ "build": "vite build",
+ "preview": "vite preview"
+ },
+ "dependencies": {
+ "@vitejs/plugin-react": "^4.3.4",
+ "lucide-react": "^0.468.0",
+ "pybe-mern-app": "file:..",
+ "react": "^18.3.1",
+ "react-dom": "^18.3.1",
+ "vite": "^6.0.7"
+ }
+}
diff --git a/summership-26-prs/Niranjan Praveen/client/src/main.jsx b/summership-26-prs/Niranjan Praveen/client/src/main.jsx
new file mode 100644
index 0000000..40f194b
--- /dev/null
+++ b/summership-26-prs/Niranjan Praveen/client/src/main.jsx
@@ -0,0 +1,385 @@
+import React, { useEffect, useMemo, useState } from 'react';
+import { createRoot } from 'react-dom/client';
+import {
+ Brain,
+ ChartNoAxesCombined,
+ Code2,
+ Compass,
+ Lightbulb,
+ Lock,
+ MessageSquareText,
+ Play,
+ Route,
+ Search,
+ Send,
+ Sparkles,
+ Target,
+ Trophy
+} from 'lucide-react';
+import './styles.css';
+
+const API_URL = import.meta.env.VITE_API_URL || 'http://localhost:5000/api';
+
+async function api(path, options) {
+ const response = await fetch(`${API_URL}${path}`, {
+ headers: { 'Content-Type': 'application/json' },
+ ...options
+ });
+ if (!response.ok) throw new Error(await response.text());
+ return response.json();
+}
+
+function App() {
+ const [scenarios, setScenarios] = useState([]);
+ const [selected, setSelected] = useState(null);
+ const [sessions, setSessions] = useState([]);
+ const [analytics, setAnalytics] = useState(null);
+ const [roadmap, setRoadmap] = useState([]);
+ const [mastery, setMastery] = useState(null);
+ const [filters, setFilters] = useState({ q: '', difficulty: '', concept: '' });
+ const [form, setForm] = useState({ learnerName: 'Guest learner', reasoning: '', promptText: '', reflection: '' });
+ const [activeResult, setActiveResult] = useState(null);
+ const [loading, setLoading] = useState(true);
+ const [submitting, setSubmitting] = useState(false);
+
+ const concepts = useMemo(() => [...new Set(scenarios.flatMap((scenario) => scenario.concepts || []))].sort(), [scenarios]);
+
+ async function refresh() {
+ const params = new URLSearchParams(Object.entries(filters).filter(([, value]) => value));
+ const [scenarioData, sessionData, analyticsData, roadmapData, masteryData] = await Promise.all([
+ api(`/scenarios?${params}`),
+ api('/sessions'),
+ api('/analytics'),
+ api('/roadmap'),
+ api('/mastery')
+ ]);
+ setScenarios(scenarioData);
+ setSessions(sessionData);
+ setAnalytics(analyticsData);
+ setRoadmap(roadmapData);
+ setMastery(masteryData);
+ const unlocked = new Set((masteryData?.levels || []).filter((level) => level.unlocked).map((level) => level.level));
+ setSelected((current) => current || scenarioData.find((scenario) => unlocked.has(scenario.difficulty)) || scenarioData[0] || null);
+ setLoading(false);
+ }
+
+ useEffect(() => {
+ refresh().catch(console.error);
+ }, [filters.q, filters.difficulty, filters.concept]);
+
+ async function submitSession(event) {
+ event.preventDefault();
+ if (!selected || !form.reasoning.trim()) return;
+ setSubmitting(true);
+ try {
+ const result = await api('/sessions', {
+ method: 'POST',
+ body: JSON.stringify({ ...form, scenarioId: selected._id })
+ });
+ setActiveResult(result);
+ setForm({ ...form, reasoning: '', promptText: '', reflection: '' });
+ await refresh();
+ } finally {
+ setSubmitting(false);
+ }
+ }
+
+ if (loading) return Loading PyBe... ;
+
+ return (
+
+
+
+
+
+ PyBe
+ Scenario-first Python
+
+
+
+
+
+ setFilters({ ...filters, q: event.target.value })}
+ placeholder="Search scenarios"
+ />
+
+
+ setFilters({ ...filters, difficulty: event.target.value })}>
+ All levels
+ Beginner
+ Explorer
+ Builder
+
+
+ setFilters({ ...filters, concept: event.target.value })}>
+ All concepts
+ {concepts.map((concept) => {concept} )}
+
+
+
+ {scenarios.map((scenario) => {
+ const locked = mastery && !mastery.levels.find((level) => level.level === scenario.difficulty)?.unlocked;
+ return (
+ {
+ setSelected(scenario);
+ setActiveResult(null);
+ }}
+ >
+ {locked ? <> {scenario.difficulty}> : scenario.difficulty}
+ {scenario.title}
+ {scenario.concepts.join(' / ')}
+
+ );
+ })}
+
+
+
+
+
+
+
+
+
+
+
{selected?.title}
+
+ {selected?.context}
+
+ {selected?.objectives.map((item) => {item} )}
+
+
+
+
+
+
+
+
AI Mentor Output
+
+ {!activeResult ? : }
+
+
+
+
+
Mastery Journey
+ {
+ const scenario = scenarios.find((item) => item._id === scenarioId);
+ if (scenario) {
+ setSelected(scenario);
+ setActiveResult(null);
+ window.scrollTo({ top: 0, behavior: 'smooth' });
+ }
+ }}
+ />
+
+
+
+
+
+ );
+}
+
+function MasteryJourney({ mastery, onPick }) {
+ if (!mastery) return Complete a session to start your mastery journey.
;
+
+ return (
+
+
+ {mastery.levels.map((level) => (
+
+
+ {level.unlocked ? : }
+ {level.level}
+
+ {level.stage}
+ {level.piaget}
+
+
+
+ {level.unlocked
+ ? `${level.coverage}% concepts mastered · ${level.sessionsCompleted} session${level.sessionsCompleted === 1 ? '' : 's'}`
+ : `Locked — master ${Math.round(mastery.threshold)}+ in half of the previous stage's concepts (${mastery.minSessionsToAdvance}+ sessions)`}
+
+
+
+ ))}
+
+
+
+
Concept mastery
+ {mastery.concepts.length ? mastery.concepts.map((item) => (
+
+ {item.concept}
+
+ {item.mastery}
+ {item.status}
+
+ )) :
No concepts practiced yet — your first session begins the map.
}
+
+
+
+
Recommended next
+ {mastery.recommendations.length ? mastery.recommendations.map((item) => (
+
+
+ {item.title}
+ {item.difficulty} · {item.concepts.join(' / ')}
+ {item.reason}
+
+ onPick(item.scenarioId)}>Start
+
+ )) :
All unlocked scenarios attempted — advance a stage to keep growing.
}
+
+
+ );
+}
+
+function EmptyResult() {
+ return (
+
+
+
Submit reasoning to see abstraction mapping, Python code, prompt feedback, and misconception signals.
+
+ );
+}
+
+function Result({ result }) {
+ return (
+
+
{result.promptScore} Prompt maturity
+
+ {result.abstractionMap.map((item) => (
+
+ {item.pattern}
+ {item.pythonConcept}
+ {item.explanation}
+
+ ))}
+
+
+
Generated Python
+
{result.generatedCode}
+
{result.codeExplanation}
+
+
+ {result.promptFeedback.map((item) => {item} )}
+
+ {result.misconceptions.length > 0 && (
+
+
Misconception watch
+ {result.misconceptions.map((item) =>
{item}
)}
+
+ )}
+
+ );
+}
+
+function Analytics({ analytics }) {
+ const concepts = Object.entries(analytics?.conceptCounts || {});
+ return (
+
+ {concepts.length ? concepts.map(([name, count]) => (
+
+ {name}
+
+ {count}
+
+ )) :
No learning sessions yet.
}
+
+ );
+}
+
+function Roadmap({ roadmap }) {
+ return (
+
+ {roadmap.map((phase) => (
+
+ {phase.phase}
+
+
{phase.title}
+
{phase.summary}
+
{phase.items.join(' / ')}
+
+
+ ))}
+
+ );
+}
+
+function SessionList({ sessions }) {
+ return (
+
+ {sessions.length ? sessions.slice(0, 6).map((session) => (
+
+
+
+ {session.scenario?.title}
+ {session.masterySignals.join(' / ')}
+
+
+ )) :
No sessions yet.
}
+
+ );
+}
+
+createRoot(document.getElementById('root')).render( );
diff --git a/summership-26-prs/Niranjan Praveen/client/src/styles.css b/summership-26-prs/Niranjan Praveen/client/src/styles.css
new file mode 100644
index 0000000..ff9d5f9
--- /dev/null
+++ b/summership-26-prs/Niranjan Praveen/client/src/styles.css
@@ -0,0 +1,607 @@
+:root {
+ font-family: Inter, ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
+ color: #17201d;
+ background: #f4f1ea;
+}
+
+* {
+ box-sizing: border-box;
+}
+
+body {
+ margin: 0;
+}
+
+button,
+input,
+textarea,
+select {
+ font: inherit;
+}
+
+.app-shell {
+ min-height: 100vh;
+ display: grid;
+ grid-template-columns: 320px 1fr;
+}
+
+.sidebar {
+ background: #16231f;
+ color: #f8f4ec;
+ padding: 24px;
+ display: flex;
+ flex-direction: column;
+ gap: 16px;
+ position: sticky;
+ top: 0;
+ height: 100vh;
+}
+
+.brand {
+ display: flex;
+ align-items: center;
+ gap: 12px;
+ margin-bottom: 8px;
+}
+
+.brand strong {
+ display: block;
+ font-size: 1.5rem;
+}
+
+.brand span {
+ color: #b9c7bf;
+ font-size: .9rem;
+}
+
+.search {
+ display: flex;
+ align-items: center;
+ gap: 8px;
+ background: #24342f;
+ border: 1px solid #385149;
+ padding: 10px 12px;
+ border-radius: 8px;
+}
+
+.search input {
+ flex: 1;
+ min-width: 0;
+ background: transparent;
+ color: #fff;
+ border: 0;
+ outline: 0;
+}
+
+select {
+ border: 1px solid #d7d0c3;
+ border-radius: 8px;
+ padding: 10px 12px;
+ background: #fffaf2;
+ color: #18221f;
+}
+
+.sidebar select {
+ background: #24342f;
+ border-color: #385149;
+ color: #f8f4ec;
+}
+
+.scenario-list {
+ overflow: auto;
+ display: grid;
+ gap: 10px;
+ padding-right: 4px;
+}
+
+.scenario {
+ text-align: left;
+ border: 1px solid #385149;
+ background: #20312c;
+ color: #f8f4ec;
+ border-radius: 8px;
+ padding: 14px;
+ display: grid;
+ gap: 7px;
+ cursor: pointer;
+}
+
+.scenario.active {
+ background: #d8f07c;
+ color: #14201c;
+ border-color: #d8f07c;
+}
+
+.scenario.locked {
+ opacity: .45;
+ cursor: not-allowed;
+}
+
+.scenario.locked span {
+ display: inline-flex;
+ align-items: center;
+ gap: 5px;
+}
+
+.scenario span {
+ font-size: .72rem;
+ text-transform: uppercase;
+ letter-spacing: .08em;
+}
+
+.scenario small {
+ color: inherit;
+ opacity: .75;
+}
+
+.workspace {
+ padding: 28px;
+ display: grid;
+ gap: 24px;
+}
+
+.hero {
+ min-height: 230px;
+ color: #fff;
+ background: linear-gradient(135deg, rgba(18,34,30,.92), rgba(36,77,67,.78)), url('https://images.unsplash.com/photo-1516321318423-f06f85e504b3?auto=format&fit=crop&w=1600&q=80');
+ background-size: cover;
+ background-position: center;
+ border-radius: 8px;
+ padding: 32px;
+ display: flex;
+ align-items: flex-end;
+ justify-content: space-between;
+ gap: 24px;
+}
+
+.hero p {
+ margin: 0 0 10px;
+ color: #d8f07c;
+ text-transform: uppercase;
+ letter-spacing: .08em;
+ font-size: .78rem;
+}
+
+.hero h1 {
+ max-width: 760px;
+ margin: 0;
+ font-size: clamp(2rem, 5vw, 4.7rem);
+ line-height: .98;
+ letter-spacing: 0;
+}
+
+.hero-stats {
+ display: flex;
+ gap: 12px;
+}
+
+.hero-stats span {
+ min-width: 88px;
+ background: rgba(255,255,255,.14);
+ border: 1px solid rgba(255,255,255,.22);
+ border-radius: 8px;
+ padding: 14px;
+ font-size: 1.7rem;
+ font-weight: 800;
+}
+
+.hero-stats small {
+ display: block;
+ font-size: .75rem;
+ font-weight: 500;
+ color: #d9e2dc;
+}
+
+.main-grid {
+ display: grid;
+ grid-template-columns: minmax(0, 1.1fr) minmax(360px, .9fr);
+ gap: 20px;
+ align-items: start;
+}
+
+.panel {
+ background: #fffdf7;
+ border: 1px solid #ded7cb;
+ border-radius: 8px;
+ padding: 22px;
+ box-shadow: 0 16px 42px rgba(40, 34, 23, .08);
+}
+
+.section-title {
+ display: flex;
+ align-items: center;
+ gap: 10px;
+ margin-bottom: 16px;
+}
+
+h2,
+h3,
+p {
+ margin-top: 0;
+}
+
+h2 {
+ font-size: 1.2rem;
+ margin-bottom: 0;
+}
+
+.context {
+ color: #53615c;
+ line-height: 1.6;
+}
+
+.objective-row {
+ display: flex;
+ gap: 8px;
+ flex-wrap: wrap;
+ margin: 16px 0;
+}
+
+.objective-row span {
+ background: #e7f2cb;
+ border: 1px solid #c9dc94;
+ border-radius: 999px;
+ padding: 7px 10px;
+ font-size: .86rem;
+}
+
+.learning-form {
+ display: grid;
+ gap: 14px;
+}
+
+.learning-form label {
+ display: grid;
+ gap: 7px;
+ font-weight: 700;
+}
+
+textarea {
+ width: 100%;
+ min-height: 105px;
+ border: 1px solid #d4cdc0;
+ border-radius: 8px;
+ padding: 12px;
+ resize: vertical;
+ background: #fffaf2;
+ color: #17201d;
+}
+
+.primary {
+ border: 0;
+ border-radius: 8px;
+ background: #17231f;
+ color: #fff;
+ padding: 13px 16px;
+ display: inline-flex;
+ justify-content: center;
+ align-items: center;
+ gap: 8px;
+ cursor: pointer;
+ font-weight: 800;
+}
+
+.primary:disabled {
+ opacity: .65;
+ cursor: wait;
+}
+
+.empty {
+ min-height: 360px;
+ display: grid;
+ place-items: center;
+ text-align: center;
+ color: #68736f;
+ padding: 30px;
+}
+
+.result-stack {
+ display: grid;
+ gap: 14px;
+}
+
+.score {
+ background: #17231f;
+ color: #fff;
+ border-radius: 8px;
+ padding: 18px;
+ display: flex;
+ justify-content: space-between;
+ align-items: baseline;
+}
+
+.score span {
+ font-size: 3rem;
+ font-weight: 900;
+}
+
+.mapping {
+ border-left: 4px solid #7b9f27;
+ padding: 10px 0 10px 12px;
+}
+
+.mapping strong,
+.mapping span {
+ display: block;
+}
+
+.mapping span {
+ color: #66730a;
+ font-weight: 800;
+ margin: 4px 0;
+}
+
+.code-block {
+ background: #111916;
+ color: #e6f2eb;
+ border-radius: 8px;
+ overflow: hidden;
+}
+
+.code-block div {
+ padding: 12px 14px;
+ display: flex;
+ gap: 8px;
+ align-items: center;
+ background: #1f2d28;
+}
+
+pre {
+ margin: 0;
+ padding: 16px;
+ white-space: pre-wrap;
+ line-height: 1.55;
+}
+
+.code-block p {
+ padding: 0 16px 16px;
+ color: #cddbd5;
+}
+
+.feedback {
+ padding-left: 20px;
+ color: #43504b;
+}
+
+.note {
+ background: #fff2d9;
+ border: 1px solid #ebcd8e;
+ border-radius: 8px;
+ padding: 14px;
+}
+
+.dashboard {
+ display: grid;
+ grid-template-columns: 1fr 1.2fr 1fr;
+ gap: 20px;
+ align-items: start;
+}
+
+.mastery-grid {
+ display: grid;
+ grid-template-columns: 1.2fr 1fr 1fr;
+ gap: 22px;
+ align-items: start;
+}
+
+.mastery-levels {
+ display: grid;
+ gap: 12px;
+}
+
+.stage {
+ border: 1px solid #ded7cb;
+ border-radius: 8px;
+ padding: 14px;
+ background: #f8f4ec;
+}
+
+.stage.unlocked {
+ background: #eef6d8;
+ border-color: #c9dc94;
+}
+
+.stage header {
+ display: flex;
+ align-items: center;
+ gap: 8px;
+ margin-bottom: 4px;
+}
+
+.stage-name {
+ display: block;
+ color: #66730a;
+ font-weight: 800;
+ font-size: .85rem;
+ margin-bottom: 6px;
+}
+
+.stage p {
+ color: #53615c;
+ font-size: .86rem;
+ line-height: 1.45;
+ margin-bottom: 8px;
+}
+
+.stage-progress meter {
+ width: 100%;
+}
+
+.stage-progress small {
+ display: block;
+ color: #68736f;
+ margin-top: 4px;
+}
+
+.mastery-concepts h3,
+.mastery-recommend h3 {
+ display: flex;
+ align-items: center;
+ gap: 7px;
+ margin-bottom: 12px;
+}
+
+.concept-row {
+ display: grid;
+ grid-template-columns: 1fr 90px 30px 82px;
+ align-items: center;
+ gap: 8px;
+ margin-bottom: 8px;
+}
+
+.concept-row small {
+ color: #7b867f;
+ font-size: .76rem;
+}
+
+.mastery-recommend article {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ gap: 12px;
+ border-bottom: 1px solid #e7dfd2;
+ padding: 10px 0;
+}
+
+.mastery-recommend article span {
+ display: block;
+ color: #66716c;
+ font-size: .85rem;
+ margin: 3px 0;
+}
+
+.mastery-recommend article small {
+ color: #7b9f27;
+ font-weight: 700;
+}
+
+.mastery-recommend .primary {
+ padding: 9px 14px;
+}
+
+.analytics-list {
+ display: grid;
+ gap: 10px;
+}
+
+.analytics-list div {
+ display: grid;
+ grid-template-columns: 1fr 110px 24px;
+ align-items: center;
+ gap: 10px;
+}
+
+meter {
+ width: 100%;
+}
+
+.roadmap {
+ display: grid;
+ gap: 14px;
+}
+
+.roadmap article {
+ display: grid;
+ grid-template-columns: 58px 1fr;
+ gap: 12px;
+}
+
+.roadmap article > strong {
+ width: 46px;
+ height: 46px;
+ border-radius: 8px;
+ background: #17231f;
+ color: #d8f07c;
+ display: grid;
+ place-items: center;
+}
+
+.roadmap h3 {
+ margin-bottom: 5px;
+}
+
+.roadmap p {
+ color: #52605b;
+ margin-bottom: 5px;
+ line-height: 1.45;
+}
+
+.roadmap small {
+ color: #7b867f;
+}
+
+.sessions {
+ display: grid;
+ gap: 12px;
+}
+
+.sessions article {
+ display: flex;
+ gap: 10px;
+ align-items: flex-start;
+ border-bottom: 1px solid #e7dfd2;
+ padding-bottom: 12px;
+}
+
+.sessions span {
+ display: block;
+ color: #66716c;
+ font-size: .88rem;
+ margin-top: 4px;
+}
+
+.loading {
+ min-height: 100vh;
+ display: grid;
+ place-items: center;
+ background: #17231f;
+ color: #d8f07c;
+ font-size: 1.5rem;
+}
+
+@media (max-width: 1050px) {
+ .app-shell {
+ grid-template-columns: 1fr;
+ }
+
+ .sidebar {
+ position: static;
+ height: auto;
+ }
+
+ .main-grid,
+ .dashboard,
+ .mastery-grid {
+ grid-template-columns: 1fr;
+ }
+
+ .hero {
+ align-items: flex-start;
+ flex-direction: column;
+ }
+}
+
+@media (max-width: 640px) {
+ .workspace,
+ .sidebar {
+ padding: 16px;
+ }
+
+ .hero {
+ padding: 22px;
+ }
+
+ .hero-stats {
+ width: 100%;
+ flex-wrap: wrap;
+ }
+
+ .hero-stats span {
+ flex: 1;
+ min-width: 96px;
+ }
+
+ .analytics-list div {
+ grid-template-columns: 1fr;
+ }
+}
diff --git a/summership-26-prs/Niranjan Praveen/package-lock.json b/summership-26-prs/Niranjan Praveen/package-lock.json
new file mode 100644
index 0000000..c9d9594
--- /dev/null
+++ b/summership-26-prs/Niranjan Praveen/package-lock.json
@@ -0,0 +1,305 @@
+{
+ "name": "pybe-mern-app",
+ "version": "1.0.0",
+ "lockfileVersion": 3,
+ "requires": true,
+ "packages": {
+ "": {
+ "name": "pybe-mern-app",
+ "version": "1.0.0",
+ "dependencies": {
+ "concurrently": "^9.1.2"
+ },
+ "devDependencies": {}
+ },
+ "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/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/chalk": {
+ "version": "4.1.2",
+ "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
+ "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
+ "license": "MIT",
+ "dependencies": {
+ "ansi-styles": "^4.1.0",
+ "supports-color": "^7.1.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/chalk?sponsor=1"
+ }
+ },
+ "node_modules/chalk/node_modules/supports-color": {
+ "version": "7.2.0",
+ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
+ "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
+ "license": "MIT",
+ "dependencies": {
+ "has-flag": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/cliui": {
+ "version": "8.0.1",
+ "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz",
+ "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==",
+ "license": "ISC",
+ "dependencies": {
+ "string-width": "^4.2.0",
+ "strip-ansi": "^6.0.1",
+ "wrap-ansi": "^7.0.0"
+ },
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "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/concurrently": {
+ "version": "9.2.4",
+ "resolved": "https://registry.npmjs.org/concurrently/-/concurrently-9.2.4.tgz",
+ "integrity": "sha512-TZ0CEhyzvFjgtAvHTusDMgj7wNdihCh7LLLrzdUOXIhdlnL2JBBGA9eJxR24rtqgmdjh3OA3hrN1rCHj6HM8qA==",
+ "license": "MIT",
+ "dependencies": {
+ "chalk": "4.1.2",
+ "rxjs": "7.8.2",
+ "shell-quote": "1.9.0",
+ "supports-color": "8.1.1",
+ "tree-kill": "1.2.2",
+ "yargs": "17.7.2"
+ },
+ "bin": {
+ "conc": "dist/bin/concurrently.js",
+ "concurrently": "dist/bin/concurrently.js"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/open-cli-tools/concurrently?sponsor=1"
+ }
+ },
+ "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/escalade": {
+ "version": "3.2.0",
+ "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz",
+ "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/get-caller-file": {
+ "version": "2.0.5",
+ "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz",
+ "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==",
+ "license": "ISC",
+ "engines": {
+ "node": "6.* || 8.* || >= 10.*"
+ }
+ },
+ "node_modules/has-flag": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
+ "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "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/require-directory": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz",
+ "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/rxjs": {
+ "version": "7.8.2",
+ "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.2.tgz",
+ "integrity": "sha512-dhKf903U/PQZY6boNNtAGdWbG85WAbjT/1xYoZIC7FAY0yWapOBQVsVrDl58W86//e1VpMNBtRV4MaXfdMySFA==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "tslib": "^2.1.0"
+ }
+ },
+ "node_modules/shell-quote": {
+ "version": "1.9.0",
+ "resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.9.0.tgz",
+ "integrity": "sha512-Iov+JwFv/2HcTpcwNMKd8+IWNb8tboQJNQTkAY/LLVK7gGH9jy+LGkVqPxfekHl+yMmiqXszdGWXgkfml7hjqA==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "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/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/supports-color": {
+ "version": "8.1.1",
+ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz",
+ "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==",
+ "license": "MIT",
+ "dependencies": {
+ "has-flag": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/supports-color?sponsor=1"
+ }
+ },
+ "node_modules/tree-kill": {
+ "version": "1.2.2",
+ "resolved": "https://registry.npmjs.org/tree-kill/-/tree-kill-1.2.2.tgz",
+ "integrity": "sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==",
+ "license": "MIT",
+ "bin": {
+ "tree-kill": "cli.js"
+ }
+ },
+ "node_modules/tslib": {
+ "version": "2.8.1",
+ "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz",
+ "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==",
+ "license": "0BSD"
+ },
+ "node_modules/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/y18n": {
+ "version": "5.0.8",
+ "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz",
+ "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==",
+ "license": "ISC",
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/yargs": {
+ "version": "17.7.2",
+ "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz",
+ "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==",
+ "license": "MIT",
+ "dependencies": {
+ "cliui": "^8.0.1",
+ "escalade": "^3.1.1",
+ "get-caller-file": "^2.0.5",
+ "require-directory": "^2.1.1",
+ "string-width": "^4.2.3",
+ "y18n": "^5.0.5",
+ "yargs-parser": "^21.1.1"
+ },
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/yargs-parser": {
+ "version": "21.1.1",
+ "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz",
+ "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==",
+ "license": "ISC",
+ "engines": {
+ "node": ">=12"
+ }
+ }
+ }
+}
diff --git a/summership-26-prs/Niranjan Praveen/package.json b/summership-26-prs/Niranjan Praveen/package.json
new file mode 100644
index 0000000..4bb3dee
--- /dev/null
+++ b/summership-26-prs/Niranjan Praveen/package.json
@@ -0,0 +1,15 @@
+{
+ "name": "pybe-mern-app",
+ "version": "1.0.0",
+ "private": true,
+ "scripts": {
+ "installAll": "npm install --prefix server && npm install --prefix client",
+ "dev": "concurrently \"npm run dev --prefix server\" \"npm run dev --prefix client\"",
+ "seed": "npm run seed --prefix server",
+ "start": "npm run start --prefix server"
+ },
+ "dependencies": {
+ "concurrently": "^9.1.2"
+ },
+ "devDependencies": {}
+}
diff --git a/summership-26-prs/Niranjan Praveen/server/.env.example b/summership-26-prs/Niranjan Praveen/server/.env.example
new file mode 100644
index 0000000..47c803c
--- /dev/null
+++ b/summership-26-prs/Niranjan Praveen/server/.env.example
@@ -0,0 +1,2 @@
+PORT=5000
+CLIENT_ORIGIN=http://localhost:5173
diff --git a/summership-26-prs/Niranjan Praveen/server/package-lock.json b/summership-26-prs/Niranjan Praveen/server/package-lock.json
new file mode 100644
index 0000000..fc0f4d1
--- /dev/null
+++ b/summership-26-prs/Niranjan Praveen/server/package-lock.json
@@ -0,0 +1,1084 @@
+{
+ "name": "pybe-server",
+ "version": "1.0.0",
+ "lockfileVersion": 3,
+ "requires": true,
+ "packages": {
+ "": {
+ "name": "pybe-server",
+ "version": "1.0.0",
+ "dependencies": {
+ "cors": "^2.8.5",
+ "dotenv": "^16.4.7",
+ "express": "^4.21.2",
+ "morgan": "^1.10.0",
+ "pybe-mern-app": "file:.."
+ },
+ "devDependencies": {
+ "nodemon": "^3.1.9"
+ }
+ },
+ "..": {
+ "name": "pybe-mern-app",
+ "version": "1.0.0",
+ "dependencies": {
+ "concurrently": "^9.1.2"
+ },
+ "devDependencies": {}
+ },
+ "node_modules/accepts": {
+ "version": "1.3.8",
+ "license": "MIT",
+ "dependencies": {
+ "mime-types": "~2.1.34",
+ "negotiator": "0.6.3"
+ },
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/anymatch": {
+ "version": "3.1.3",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "normalize-path": "^3.0.0",
+ "picomatch": "^2.0.4"
+ },
+ "engines": {
+ "node": ">= 8"
+ }
+ },
+ "node_modules/array-flatten": {
+ "version": "1.1.1",
+ "license": "MIT"
+ },
+ "node_modules/balanced-match": {
+ "version": "4.0.4",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": "18 || 20 || >=22"
+ }
+ },
+ "node_modules/basic-auth": {
+ "version": "2.0.1",
+ "license": "MIT",
+ "dependencies": {
+ "safe-buffer": "5.1.2"
+ },
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
+ "node_modules/basic-auth/node_modules/safe-buffer": {
+ "version": "5.1.2",
+ "license": "MIT"
+ },
+ "node_modules/binary-extensions": {
+ "version": "2.3.0",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/body-parser": {
+ "version": "1.20.5",
+ "license": "MIT",
+ "dependencies": {
+ "bytes": "~3.1.2",
+ "content-type": "~1.0.5",
+ "debug": "2.6.9",
+ "depd": "2.0.0",
+ "destroy": "~1.2.0",
+ "http-errors": "~2.0.1",
+ "iconv-lite": "~0.4.24",
+ "on-finished": "~2.4.1",
+ "qs": "~6.15.1",
+ "raw-body": "~2.5.3",
+ "type-is": "~1.6.18",
+ "unpipe": "~1.0.0"
+ },
+ "engines": {
+ "node": ">= 0.8",
+ "npm": "1.2.8000 || >= 1.4.16"
+ }
+ },
+ "node_modules/brace-expansion": {
+ "version": "5.0.6",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "balanced-match": "^4.0.2"
+ },
+ "engines": {
+ "node": "18 || 20 || >=22"
+ }
+ },
+ "node_modules/braces": {
+ "version": "3.0.3",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "fill-range": "^7.1.1"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/bytes": {
+ "version": "3.1.2",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
+ "node_modules/call-bind-apply-helpers": {
+ "version": "1.0.2",
+ "license": "MIT",
+ "dependencies": {
+ "es-errors": "^1.3.0",
+ "function-bind": "^1.1.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/call-bound": {
+ "version": "1.0.4",
+ "license": "MIT",
+ "dependencies": {
+ "call-bind-apply-helpers": "^1.0.2",
+ "get-intrinsic": "^1.3.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/chokidar": {
+ "version": "3.6.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "anymatch": "~3.1.2",
+ "braces": "~3.0.2",
+ "glob-parent": "~5.1.2",
+ "is-binary-path": "~2.1.0",
+ "is-glob": "~4.0.1",
+ "normalize-path": "~3.0.0",
+ "readdirp": "~3.6.0"
+ },
+ "engines": {
+ "node": ">= 8.10.0"
+ },
+ "funding": {
+ "url": "https://paulmillr.com/funding/"
+ },
+ "optionalDependencies": {
+ "fsevents": "~2.3.2"
+ }
+ },
+ "node_modules/content-disposition": {
+ "version": "0.5.4",
+ "license": "MIT",
+ "dependencies": {
+ "safe-buffer": "5.2.1"
+ },
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/content-type": {
+ "version": "1.0.5",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/cookie": {
+ "version": "0.7.2",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/cookie-signature": {
+ "version": "1.0.7",
+ "license": "MIT"
+ },
+ "node_modules/cors": {
+ "version": "2.8.6",
+ "license": "MIT",
+ "dependencies": {
+ "object-assign": "^4",
+ "vary": "^1"
+ },
+ "engines": {
+ "node": ">= 0.10"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/express"
+ }
+ },
+ "node_modules/debug": {
+ "version": "2.6.9",
+ "license": "MIT",
+ "dependencies": {
+ "ms": "2.0.0"
+ }
+ },
+ "node_modules/depd": {
+ "version": "2.0.0",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
+ "node_modules/destroy": {
+ "version": "1.2.0",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.8",
+ "npm": "1.2.8000 || >= 1.4.16"
+ }
+ },
+ "node_modules/dotenv": {
+ "version": "16.6.1",
+ "license": "BSD-2-Clause",
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://dotenvx.com"
+ }
+ },
+ "node_modules/dunder-proto": {
+ "version": "1.0.1",
+ "license": "MIT",
+ "dependencies": {
+ "call-bind-apply-helpers": "^1.0.1",
+ "es-errors": "^1.3.0",
+ "gopd": "^1.2.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/ee-first": {
+ "version": "1.1.1",
+ "license": "MIT"
+ },
+ "node_modules/encodeurl": {
+ "version": "2.0.0",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
+ "node_modules/es-define-property": {
+ "version": "1.0.1",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/es-errors": {
+ "version": "1.3.0",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/es-object-atoms": {
+ "version": "1.1.2",
+ "license": "MIT",
+ "dependencies": {
+ "es-errors": "^1.3.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/escape-html": {
+ "version": "1.0.3",
+ "license": "MIT"
+ },
+ "node_modules/etag": {
+ "version": "1.8.1",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/express": {
+ "version": "4.22.2",
+ "license": "MIT",
+ "dependencies": {
+ "accepts": "~1.3.8",
+ "array-flatten": "1.1.1",
+ "body-parser": "~1.20.5",
+ "content-disposition": "~0.5.4",
+ "content-type": "~1.0.4",
+ "cookie": "~0.7.1",
+ "cookie-signature": "~1.0.6",
+ "debug": "2.6.9",
+ "depd": "2.0.0",
+ "encodeurl": "~2.0.0",
+ "escape-html": "~1.0.3",
+ "etag": "~1.8.1",
+ "finalhandler": "~1.3.1",
+ "fresh": "~0.5.2",
+ "http-errors": "~2.0.0",
+ "merge-descriptors": "1.0.3",
+ "methods": "~1.1.2",
+ "on-finished": "~2.4.1",
+ "parseurl": "~1.3.3",
+ "path-to-regexp": "~0.1.12",
+ "proxy-addr": "~2.0.7",
+ "qs": "~6.15.1",
+ "range-parser": "~1.2.1",
+ "safe-buffer": "5.2.1",
+ "send": "~0.19.0",
+ "serve-static": "~1.16.2",
+ "setprototypeof": "1.2.0",
+ "statuses": "~2.0.1",
+ "type-is": "~1.6.18",
+ "utils-merge": "1.0.1",
+ "vary": "~1.1.2"
+ },
+ "engines": {
+ "node": ">= 0.10.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/express"
+ }
+ },
+ "node_modules/fill-range": {
+ "version": "7.1.1",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "to-regex-range": "^5.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/finalhandler": {
+ "version": "1.3.2",
+ "license": "MIT",
+ "dependencies": {
+ "debug": "2.6.9",
+ "encodeurl": "~2.0.0",
+ "escape-html": "~1.0.3",
+ "on-finished": "~2.4.1",
+ "parseurl": "~1.3.3",
+ "statuses": "~2.0.2",
+ "unpipe": "~1.0.0"
+ },
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
+ "node_modules/forwarded": {
+ "version": "0.2.0",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/fresh": {
+ "version": "0.5.2",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/function-bind": {
+ "version": "1.1.2",
+ "license": "MIT",
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/get-intrinsic": {
+ "version": "1.3.0",
+ "license": "MIT",
+ "dependencies": {
+ "call-bind-apply-helpers": "^1.0.2",
+ "es-define-property": "^1.0.1",
+ "es-errors": "^1.3.0",
+ "es-object-atoms": "^1.1.1",
+ "function-bind": "^1.1.2",
+ "get-proto": "^1.0.1",
+ "gopd": "^1.2.0",
+ "has-symbols": "^1.1.0",
+ "hasown": "^2.0.2",
+ "math-intrinsics": "^1.1.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/get-proto": {
+ "version": "1.0.1",
+ "license": "MIT",
+ "dependencies": {
+ "dunder-proto": "^1.0.1",
+ "es-object-atoms": "^1.0.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/glob-parent": {
+ "version": "5.1.2",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "is-glob": "^4.0.1"
+ },
+ "engines": {
+ "node": ">= 6"
+ }
+ },
+ "node_modules/gopd": {
+ "version": "1.2.0",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/has-flag": {
+ "version": "3.0.0",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/has-symbols": {
+ "version": "1.1.0",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/hasown": {
+ "version": "2.0.4",
+ "license": "MIT",
+ "dependencies": {
+ "function-bind": "^1.1.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/http-errors": {
+ "version": "2.0.1",
+ "license": "MIT",
+ "dependencies": {
+ "depd": "~2.0.0",
+ "inherits": "~2.0.4",
+ "setprototypeof": "~1.2.0",
+ "statuses": "~2.0.2",
+ "toidentifier": "~1.0.1"
+ },
+ "engines": {
+ "node": ">= 0.8"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/express"
+ }
+ },
+ "node_modules/iconv-lite": {
+ "version": "0.4.24",
+ "license": "MIT",
+ "dependencies": {
+ "safer-buffer": ">= 2.1.2 < 3"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/ignore-by-default": {
+ "version": "1.0.1",
+ "dev": true,
+ "license": "ISC"
+ },
+ "node_modules/inherits": {
+ "version": "2.0.4",
+ "license": "ISC"
+ },
+ "node_modules/ipaddr.js": {
+ "version": "1.9.1",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.10"
+ }
+ },
+ "node_modules/is-binary-path": {
+ "version": "2.1.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "binary-extensions": "^2.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/is-extglob": {
+ "version": "2.1.1",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/is-glob": {
+ "version": "4.0.3",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "is-extglob": "^2.1.1"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/is-number": {
+ "version": "7.0.0",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.12.0"
+ }
+ },
+ "node_modules/math-intrinsics": {
+ "version": "1.1.0",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/media-typer": {
+ "version": "0.3.0",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/merge-descriptors": {
+ "version": "1.0.3",
+ "license": "MIT",
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/methods": {
+ "version": "1.1.2",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/mime": {
+ "version": "1.6.0",
+ "license": "MIT",
+ "bin": {
+ "mime": "cli.js"
+ },
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/mime-db": {
+ "version": "1.52.0",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/mime-types": {
+ "version": "2.1.35",
+ "license": "MIT",
+ "dependencies": {
+ "mime-db": "1.52.0"
+ },
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/minimatch": {
+ "version": "10.2.5",
+ "dev": true,
+ "license": "BlueOak-1.0.0",
+ "dependencies": {
+ "brace-expansion": "^5.0.5"
+ },
+ "engines": {
+ "node": "18 || 20 || >=22"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ }
+ },
+ "node_modules/morgan": {
+ "version": "1.11.0",
+ "license": "MIT",
+ "dependencies": {
+ "basic-auth": "~2.0.1",
+ "debug": "2.6.9",
+ "depd": "~2.0.0",
+ "on-finished": "~2.4.1",
+ "on-headers": "~1.1.0"
+ },
+ "engines": {
+ "node": ">= 0.8.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/express"
+ }
+ },
+ "node_modules/ms": {
+ "version": "2.0.0",
+ "license": "MIT"
+ },
+ "node_modules/negotiator": {
+ "version": "0.6.3",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/nodemon": {
+ "version": "3.1.14",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "chokidar": "^3.5.2",
+ "debug": "^4",
+ "ignore-by-default": "^1.0.1",
+ "minimatch": "^10.2.1",
+ "pstree.remy": "^1.1.8",
+ "semver": "^7.5.3",
+ "simple-update-notifier": "^2.0.0",
+ "supports-color": "^5.5.0",
+ "touch": "^3.1.0",
+ "undefsafe": "^2.0.5"
+ },
+ "bin": {
+ "nodemon": "bin/nodemon.js"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/nodemon"
+ }
+ },
+ "node_modules/nodemon/node_modules/debug": {
+ "version": "4.4.3",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ms": "^2.1.3"
+ },
+ "engines": {
+ "node": ">=6.0"
+ },
+ "peerDependenciesMeta": {
+ "supports-color": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/nodemon/node_modules/ms": {
+ "version": "2.1.3",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/normalize-path": {
+ "version": "3.0.0",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/object-assign": {
+ "version": "4.1.1",
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/object-inspect": {
+ "version": "1.13.4",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/on-finished": {
+ "version": "2.4.1",
+ "license": "MIT",
+ "dependencies": {
+ "ee-first": "1.1.1"
+ },
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
+ "node_modules/on-headers": {
+ "version": "1.1.0",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
+ "node_modules/parseurl": {
+ "version": "1.3.3",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
+ "node_modules/path-to-regexp": {
+ "version": "0.1.13",
+ "license": "MIT"
+ },
+ "node_modules/picomatch": {
+ "version": "2.3.2",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8.6"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/jonschlinkert"
+ }
+ },
+ "node_modules/proxy-addr": {
+ "version": "2.0.7",
+ "license": "MIT",
+ "dependencies": {
+ "forwarded": "0.2.0",
+ "ipaddr.js": "1.9.1"
+ },
+ "engines": {
+ "node": ">= 0.10"
+ }
+ },
+ "node_modules/pstree.remy": {
+ "version": "1.1.8",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/pybe-mern-app": {
+ "resolved": "..",
+ "link": true
+ },
+ "node_modules/qs": {
+ "version": "6.15.2",
+ "license": "BSD-3-Clause",
+ "dependencies": {
+ "side-channel": "^1.1.0"
+ },
+ "engines": {
+ "node": ">=0.6"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/range-parser": {
+ "version": "1.2.1",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/raw-body": {
+ "version": "2.5.3",
+ "license": "MIT",
+ "dependencies": {
+ "bytes": "~3.1.2",
+ "http-errors": "~2.0.1",
+ "iconv-lite": "~0.4.24",
+ "unpipe": "~1.0.0"
+ },
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
+ "node_modules/readdirp": {
+ "version": "3.6.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "picomatch": "^2.2.1"
+ },
+ "engines": {
+ "node": ">=8.10.0"
+ }
+ },
+ "node_modules/safe-buffer": {
+ "version": "5.2.1",
+ "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/safer-buffer": {
+ "version": "2.1.2",
+ "license": "MIT"
+ },
+ "node_modules/semver": {
+ "version": "7.8.4",
+ "dev": true,
+ "license": "ISC",
+ "bin": {
+ "semver": "bin/semver.js"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/send": {
+ "version": "0.19.2",
+ "license": "MIT",
+ "dependencies": {
+ "debug": "2.6.9",
+ "depd": "2.0.0",
+ "destroy": "1.2.0",
+ "encodeurl": "~2.0.0",
+ "escape-html": "~1.0.3",
+ "etag": "~1.8.1",
+ "fresh": "~0.5.2",
+ "http-errors": "~2.0.1",
+ "mime": "1.6.0",
+ "ms": "2.1.3",
+ "on-finished": "~2.4.1",
+ "range-parser": "~1.2.1",
+ "statuses": "~2.0.2"
+ },
+ "engines": {
+ "node": ">= 0.8.0"
+ }
+ },
+ "node_modules/send/node_modules/ms": {
+ "version": "2.1.3",
+ "license": "MIT"
+ },
+ "node_modules/serve-static": {
+ "version": "1.16.3",
+ "license": "MIT",
+ "dependencies": {
+ "encodeurl": "~2.0.0",
+ "escape-html": "~1.0.3",
+ "parseurl": "~1.3.3",
+ "send": "~0.19.1"
+ },
+ "engines": {
+ "node": ">= 0.8.0"
+ }
+ },
+ "node_modules/setprototypeof": {
+ "version": "1.2.0",
+ "license": "ISC"
+ },
+ "node_modules/side-channel": {
+ "version": "1.1.1",
+ "license": "MIT",
+ "dependencies": {
+ "es-errors": "^1.3.0",
+ "object-inspect": "^1.13.4",
+ "side-channel-list": "^1.0.1",
+ "side-channel-map": "^1.0.1",
+ "side-channel-weakmap": "^1.0.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/side-channel-list": {
+ "version": "1.0.1",
+ "license": "MIT",
+ "dependencies": {
+ "es-errors": "^1.3.0",
+ "object-inspect": "^1.13.4"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/side-channel-map": {
+ "version": "1.0.1",
+ "license": "MIT",
+ "dependencies": {
+ "call-bound": "^1.0.2",
+ "es-errors": "^1.3.0",
+ "get-intrinsic": "^1.2.5",
+ "object-inspect": "^1.13.3"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/side-channel-weakmap": {
+ "version": "1.0.2",
+ "license": "MIT",
+ "dependencies": {
+ "call-bound": "^1.0.2",
+ "es-errors": "^1.3.0",
+ "get-intrinsic": "^1.2.5",
+ "object-inspect": "^1.13.3",
+ "side-channel-map": "^1.0.1"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/simple-update-notifier": {
+ "version": "2.0.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "semver": "^7.5.3"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/statuses": {
+ "version": "2.0.2",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
+ "node_modules/supports-color": {
+ "version": "5.5.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "has-flag": "^3.0.0"
+ },
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/to-regex-range": {
+ "version": "5.0.1",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "is-number": "^7.0.0"
+ },
+ "engines": {
+ "node": ">=8.0"
+ }
+ },
+ "node_modules/toidentifier": {
+ "version": "1.0.1",
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.6"
+ }
+ },
+ "node_modules/touch": {
+ "version": "3.1.1",
+ "dev": true,
+ "license": "ISC",
+ "bin": {
+ "nodetouch": "bin/nodetouch.js"
+ }
+ },
+ "node_modules/type-is": {
+ "version": "1.6.18",
+ "license": "MIT",
+ "dependencies": {
+ "media-typer": "0.3.0",
+ "mime-types": "~2.1.24"
+ },
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/undefsafe": {
+ "version": "2.0.5",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/unpipe": {
+ "version": "1.0.0",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
+ "node_modules/utils-merge": {
+ "version": "1.0.1",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4.0"
+ }
+ },
+ "node_modules/vary": {
+ "version": "1.1.2",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.8"
+ }
+ }
+ }
+}
diff --git a/summership-26-prs/Niranjan Praveen/server/package.json b/summership-26-prs/Niranjan Praveen/server/package.json
new file mode 100644
index 0000000..67b9171
--- /dev/null
+++ b/summership-26-prs/Niranjan Praveen/server/package.json
@@ -0,0 +1,20 @@
+{
+ "name": "pybe-server",
+ "version": "1.0.0",
+ "private": true,
+ "scripts": {
+ "dev": "nodemon src/index.js",
+ "start": "node src/index.js",
+ "seed": "node src/seed.js"
+ },
+ "dependencies": {
+ "cors": "^2.8.5",
+ "dotenv": "^16.4.7",
+ "express": "^4.21.2",
+ "morgan": "^1.10.0",
+ "pybe-mern-app": "file:.."
+ },
+ "devDependencies": {
+ "nodemon": "^3.1.9"
+ }
+}
diff --git a/summership-26-prs/Niranjan Praveen/server/src/data/db.json b/summership-26-prs/Niranjan Praveen/server/src/data/db.json
new file mode 100644
index 0000000..f1dfeb3
--- /dev/null
+++ b/summership-26-prs/Niranjan Praveen/server/src/data/db.json
@@ -0,0 +1,600 @@
+{
+ "scenarios": [
+ {
+ "_id": "dbe9843e-5e52-44f5-a8e7-94c1d6880f05",
+ "title": "Bag Weight Label",
+ "difficulty": "Beginner",
+ "concepts": [
+ "variables"
+ ],
+ "context": "A student has one school bag with a weight written on a scale.",
+ "prompt": "What single piece of information would you store so the computer can remember the bag weight?",
+ "objectives": [
+ "Identify one value",
+ "Give the value a name",
+ "Connect naming to a variable"
+ ],
+ "sampleReasoning": "I only need to remember the bag weight, so I would store it with a clear name.",
+ "effectivenessScore": 96,
+ "createdAt": "2026-08-01T13:11:33.710Z",
+ "updatedAt": "2026-08-01T13:11:33.710Z"
+ },
+ {
+ "_id": "90c39ed4-d7e0-4baf-93db-973704a9e927",
+ "title": "Rainy Day Choice",
+ "difficulty": "Beginner",
+ "concepts": [
+ "conditionals"
+ ],
+ "context": "Before leaving home, a learner checks whether it is raining.",
+ "prompt": "What small decision rule would help the learner decide whether to carry an umbrella?",
+ "objectives": [
+ "Notice one condition",
+ "Choose one action",
+ "Map the rule to if/else"
+ ],
+ "sampleReasoning": "If it is raining, carry an umbrella. Otherwise, leave it at home.",
+ "effectivenessScore": 95,
+ "createdAt": "2026-08-01T13:11:33.711Z",
+ "updatedAt": "2026-08-01T13:11:33.711Z"
+ },
+ {
+ "_id": "f5ece411-d838-4ad5-b8a7-8e809ec05f5c",
+ "title": "Two Snack Prices",
+ "difficulty": "Beginner",
+ "concepts": [
+ "arithmetic",
+ "variables"
+ ],
+ "context": "A learner buys one samosa and one juice from the canteen.",
+ "prompt": "How would you figure out the total cost using just the two prices?",
+ "objectives": [
+ "Store two values",
+ "Add values",
+ "Name the result"
+ ],
+ "sampleReasoning": "I would keep the two prices separately and add them to get the total.",
+ "effectivenessScore": 94,
+ "createdAt": "2026-08-01T13:11:33.711Z",
+ "updatedAt": "2026-08-01T13:11:33.711Z"
+ },
+ {
+ "_id": "15abeb6a-b3e7-4d67-a602-5a12ba7db98f",
+ "title": "Greeting by Name",
+ "difficulty": "Beginner",
+ "concepts": [
+ "strings",
+ "variables"
+ ],
+ "context": "A classroom screen should greet one learner by name.",
+ "prompt": "What text should the computer remember, and how should it combine that text into a greeting?",
+ "objectives": [
+ "Store text",
+ "Combine text",
+ "Recognize strings"
+ ],
+ "sampleReasoning": "The computer should store the learner name and place it inside a greeting sentence.",
+ "effectivenessScore": 93,
+ "createdAt": "2026-08-01T13:11:33.711Z",
+ "updatedAt": "2026-08-01T13:11:33.711Z"
+ },
+ {
+ "_id": "44537274-78cc-44e9-8c4e-3e73b522ced0",
+ "title": "Pass Mark Check",
+ "difficulty": "Beginner",
+ "concepts": [
+ "comparisons",
+ "conditionals"
+ ],
+ "context": "A learner receives one test score and wants to know whether it is at least the pass mark.",
+ "prompt": "What comparison would decide if the learner passed?",
+ "objectives": [
+ "Compare two numbers",
+ "Create a yes/no result",
+ "Map comparison to condition"
+ ],
+ "sampleReasoning": "I would compare the score with the pass mark and decide pass if it is high enough.",
+ "effectivenessScore": 92,
+ "createdAt": "2026-08-01T13:11:33.711Z",
+ "updatedAt": "2026-08-01T13:11:33.711Z"
+ },
+ {
+ "_id": "4f975c4a-9e53-4101-a28a-93ffd9656b97",
+ "title": "Pocket Money Left",
+ "difficulty": "Beginner",
+ "concepts": [
+ "subtraction",
+ "variables"
+ ],
+ "context": "A learner starts with pocket money and spends a small amount on stationery.",
+ "prompt": "What two values matter, and how would you find the money left?",
+ "objectives": [
+ "Name starting amount",
+ "Name spent amount",
+ "Subtract to find remaining value"
+ ],
+ "sampleReasoning": "Start with the original money, subtract the spent amount, and store what remains.",
+ "effectivenessScore": 91,
+ "createdAt": "2026-08-01T13:11:33.711Z",
+ "updatedAt": "2026-08-01T13:11:33.711Z"
+ },
+ {
+ "_id": "c93502ef-5663-497a-a6b3-f696232aea9b",
+ "title": "Favorite Color List",
+ "difficulty": "Beginner",
+ "concepts": [
+ "lists"
+ ],
+ "context": "Three friends each say their favorite color.",
+ "prompt": "How would you keep all three colors together instead of making separate notes?",
+ "objectives": [
+ "Group related values",
+ "Recognize a collection",
+ "Map grouping to a list"
+ ],
+ "sampleReasoning": "Since all values are colors, I would keep them together in one list.",
+ "effectivenessScore": 90,
+ "createdAt": "2026-08-01T13:11:33.711Z",
+ "updatedAt": "2026-08-01T13:11:33.711Z"
+ },
+ {
+ "_id": "cac73a32-b400-46df-b32e-ec6163288b34",
+ "title": "First Item in a Bag",
+ "difficulty": "Beginner",
+ "concepts": [
+ "lists",
+ "indexing"
+ ],
+ "context": "A bag contains a pencil, eraser, and ruler in that order.",
+ "prompt": "How would you ask for only the first item?",
+ "objectives": [
+ "Notice order",
+ "Pick one position",
+ "Connect position to indexing"
+ ],
+ "sampleReasoning": "The first item is based on its position in the ordered group.",
+ "effectivenessScore": 89,
+ "createdAt": "2026-08-01T13:11:33.711Z",
+ "updatedAt": "2026-08-01T13:11:33.711Z"
+ },
+ {
+ "_id": "37604455-3233-4fbe-abfc-a8b81cdc4e58",
+ "title": "Attendance Count",
+ "difficulty": "Beginner",
+ "concepts": [
+ "counting",
+ "lists"
+ ],
+ "context": "A teacher has a short list of students present today.",
+ "prompt": "What question would help you find how many students are present?",
+ "objectives": [
+ "Recognize a group",
+ "Ask for its size",
+ "Connect size to length"
+ ],
+ "sampleReasoning": "I would count how many names are in the present-students list.",
+ "effectivenessScore": 88,
+ "createdAt": "2026-08-01T13:11:33.711Z",
+ "updatedAt": "2026-08-01T13:11:33.711Z"
+ },
+ {
+ "_id": "13dc5fcc-6a1d-4155-a7da-e452c714231d",
+ "title": "Temperature Message",
+ "difficulty": "Beginner",
+ "concepts": [
+ "conditionals",
+ "comparisons"
+ ],
+ "context": "A weather display has one temperature reading.",
+ "prompt": "What simple rule would decide whether to show Hot or Comfortable?",
+ "objectives": [
+ "Set a threshold",
+ "Compare one value",
+ "Choose one message"
+ ],
+ "sampleReasoning": "If the temperature is above the threshold, show Hot; otherwise show Comfortable.",
+ "effectivenessScore": 87,
+ "createdAt": "2026-08-01T13:11:33.711Z",
+ "updatedAt": "2026-08-01T13:11:33.711Z"
+ },
+ {
+ "_id": "ca79a569-c9d6-4779-a6ef-7f55be542704",
+ "title": "Water Bottle Reminder",
+ "difficulty": "Explorer",
+ "concepts": [
+ "loops"
+ ],
+ "context": "A learner wants a reminder for each break in the school day.",
+ "prompt": "How would you avoid writing the same reminder separately for every break?",
+ "objectives": [
+ "Recognize repeated action",
+ "Identify each break",
+ "Map repetition to a loop"
+ ],
+ "sampleReasoning": "For every break, show the same water reminder.",
+ "effectivenessScore": 96,
+ "createdAt": "2026-08-01T13:11:33.711Z",
+ "updatedAt": "2026-08-01T13:11:33.711Z"
+ },
+ {
+ "_id": "121f5ae0-3bf1-415b-9b28-68d4a6d32859",
+ "title": "Find the Longest Pencil",
+ "difficulty": "Explorer",
+ "concepts": [
+ "loops",
+ "comparisons"
+ ],
+ "context": "A learner has several pencil lengths and wants to know which one is longest.",
+ "prompt": "What small comparison would you repeat as you look through the lengths?",
+ "objectives": [
+ "Keep current best",
+ "Compare one item at a time",
+ "Update when larger"
+ ],
+ "sampleReasoning": "Start with one pencil as the longest, then compare each next pencil against it.",
+ "effectivenessScore": 95,
+ "createdAt": "2026-08-01T13:11:33.711Z",
+ "updatedAt": "2026-08-01T13:11:33.711Z"
+ },
+ {
+ "_id": "67020da4-6cec-4c81-8a59-f5fe08bc4c91",
+ "title": "Clean Chore Checklist",
+ "difficulty": "Explorer",
+ "concepts": [
+ "lists",
+ "loops"
+ ],
+ "context": "A family has a checklist of small chores for Saturday morning.",
+ "prompt": "How would you go through each chore and mark it as done?",
+ "objectives": [
+ "Store chores in a list",
+ "Process one chore at a time",
+ "Repeat a simple action"
+ ],
+ "sampleReasoning": "Put chores in a list and handle each chore one by one.",
+ "effectivenessScore": 94,
+ "createdAt": "2026-08-01T13:11:33.711Z",
+ "updatedAt": "2026-08-01T13:11:33.711Z"
+ },
+ {
+ "_id": "e7e17429-41c4-496b-9ebd-4f5099c83b7b",
+ "title": "Movie Age Filter",
+ "difficulty": "Explorer",
+ "concepts": [
+ "filtering",
+ "conditionals"
+ ],
+ "context": "A movie app has a list of films, each with a minimum age.",
+ "prompt": "What rule would decide which movies a 12-year-old can see?",
+ "objectives": [
+ "Check one item rule",
+ "Keep allowed items",
+ "Map rule to filtering"
+ ],
+ "sampleReasoning": "For each movie, keep it only if the learner age is at least the minimum age.",
+ "effectivenessScore": 93,
+ "createdAt": "2026-08-01T13:11:33.711Z",
+ "updatedAt": "2026-08-01T13:11:33.711Z"
+ },
+ {
+ "_id": "ed00990d-cd7a-420f-8c79-a261a829fbf6",
+ "title": "Classroom Supply Lookup",
+ "difficulty": "Explorer",
+ "concepts": [
+ "dictionaries"
+ ],
+ "context": "A class monitor tracks how many chalk pieces, markers, and notebooks are available.",
+ "prompt": "How would you store each supply name with its count?",
+ "objectives": [
+ "Pair names with values",
+ "Look up by name",
+ "Map pairs to a dictionary"
+ ],
+ "sampleReasoning": "Each supply has a count, so I would store supply names as keys with counts as values.",
+ "effectivenessScore": 92,
+ "createdAt": "2026-08-01T13:11:33.711Z",
+ "updatedAt": "2026-08-01T13:11:33.711Z"
+ },
+ {
+ "_id": "57e6c466-3c29-4cbd-88b1-7344f74a9bdd",
+ "title": "Bus Stop Search",
+ "difficulty": "Explorer",
+ "concepts": [
+ "search",
+ "loops"
+ ],
+ "context": "A route list contains several bus stops, and a learner wants to know if Library Stop is included.",
+ "prompt": "How would you check the stops one at a time until you find the target?",
+ "objectives": [
+ "Identify target",
+ "Scan a list",
+ "Stop when found"
+ ],
+ "sampleReasoning": "Look at each stop and compare it with the stop I want.",
+ "effectivenessScore": 91,
+ "createdAt": "2026-08-01T13:11:33.711Z",
+ "updatedAt": "2026-08-01T13:11:33.711Z"
+ },
+ {
+ "_id": "04daf101-0c03-42e4-ba0a-59ad6e3c3bbf",
+ "title": "Average Practice Score",
+ "difficulty": "Explorer",
+ "concepts": [
+ "averages",
+ "loops"
+ ],
+ "context": "A learner has five practice scores and wants one average score.",
+ "prompt": "What two small steps are needed before dividing?",
+ "objectives": [
+ "Add all scores",
+ "Count scores",
+ "Divide total by count"
+ ],
+ "sampleReasoning": "Find the total of all scores, count how many scores there are, then divide.",
+ "effectivenessScore": 90,
+ "createdAt": "2026-08-01T13:11:33.711Z",
+ "updatedAt": "2026-08-01T13:11:33.711Z"
+ },
+ {
+ "_id": "8aa41f65-0bb8-4f5f-be84-d612ed470755",
+ "title": "Separate Even Roll Numbers",
+ "difficulty": "Explorer",
+ "concepts": [
+ "modulo",
+ "filtering"
+ ],
+ "context": "A class list has roll numbers, and the teacher wants only even-numbered students for Team A.",
+ "prompt": "What small test tells you whether a roll number is even?",
+ "objectives": [
+ "Test divisibility by two",
+ "Keep matching numbers",
+ "Connect remainder to modulo"
+ ],
+ "sampleReasoning": "A roll number is even if dividing by two leaves no remainder.",
+ "effectivenessScore": 89,
+ "createdAt": "2026-08-01T13:11:33.711Z",
+ "updatedAt": "2026-08-01T13:11:33.711Z"
+ },
+ {
+ "_id": "463d69dd-f3d1-41d4-b7ad-c590ee34bf47",
+ "title": "Capitalize Name Tags",
+ "difficulty": "Explorer",
+ "concepts": [
+ "strings",
+ "loops"
+ ],
+ "context": "A club has names typed in lowercase and wants neat name tags.",
+ "prompt": "What same text-cleaning action should happen to every name?",
+ "objectives": [
+ "Recognize repeated string change",
+ "Apply to each name",
+ "Create cleaned names"
+ ],
+ "sampleReasoning": "For each name, convert it to title case before printing the tag.",
+ "effectivenessScore": 88,
+ "createdAt": "2026-08-01T13:11:33.711Z",
+ "updatedAt": "2026-08-01T13:11:33.711Z"
+ },
+ {
+ "_id": "c905eec8-ee3e-4a85-9ba1-71eaf41f885b",
+ "title": "Find Missing Homework",
+ "difficulty": "Explorer",
+ "concepts": [
+ "sets",
+ "comparison"
+ ],
+ "context": "A teacher has one list of all students and another list of students who submitted homework.",
+ "prompt": "How would you reason about who is missing from the submitted list?",
+ "objectives": [
+ "Compare two groups",
+ "Find difference",
+ "Map group difference to sets"
+ ],
+ "sampleReasoning": "Take everyone in the class and remove the students who submitted.",
+ "effectivenessScore": 87,
+ "createdAt": "2026-08-01T13:11:33.711Z",
+ "updatedAt": "2026-08-01T13:11:33.711Z"
+ },
+ {
+ "_id": "ef3a3347-9769-4065-bd8c-991f49952c6d",
+ "title": "Reusable Discount Rule",
+ "difficulty": "Builder",
+ "concepts": [
+ "functions",
+ "conditionals"
+ ],
+ "context": "A shop gives the same discount rule to many different bills.",
+ "prompt": "What inputs should a reusable discount helper receive?",
+ "objectives": [
+ "Identify reusable rule",
+ "Choose inputs",
+ "Return discounted price"
+ ],
+ "sampleReasoning": "The helper needs the bill amount and should return the final price after applying the rule.",
+ "effectivenessScore": 96,
+ "createdAt": "2026-08-01T13:11:33.711Z",
+ "updatedAt": "2026-08-01T13:11:33.711Z"
+ },
+ {
+ "_id": "ec85002c-1623-4c1f-b393-3e3bf8d8c557",
+ "title": "Mini Quiz Checker",
+ "difficulty": "Builder",
+ "concepts": [
+ "functions",
+ "comparisons"
+ ],
+ "context": "A quiz app needs to check one answer against one correct answer.",
+ "prompt": "How would you design a tiny reusable checker for one question?",
+ "objectives": [
+ "Accept learner answer",
+ "Accept correct answer",
+ "Return right or wrong"
+ ],
+ "sampleReasoning": "Compare the learner answer with the correct answer and return whether they match.",
+ "effectivenessScore": 95,
+ "createdAt": "2026-08-01T13:11:33.711Z",
+ "updatedAt": "2026-08-01T13:11:33.711Z"
+ },
+ {
+ "_id": "777ef161-94b6-4e47-9500-6a01c73e15c6",
+ "title": "Step Counter Function",
+ "difficulty": "Builder",
+ "concepts": [
+ "functions",
+ "lists"
+ ],
+ "context": "A fitness note has a list of step counts from different walks.",
+ "prompt": "How would you make a reusable helper that returns the total steps?",
+ "objectives": [
+ "Accept a list",
+ "Add all values",
+ "Return total"
+ ],
+ "sampleReasoning": "The function should take step counts, add them, and give back the total.",
+ "effectivenessScore": 94,
+ "createdAt": "2026-08-01T13:11:33.711Z",
+ "updatedAt": "2026-08-01T13:11:33.711Z"
+ },
+ {
+ "_id": "b2c3381d-9ba2-4b78-a17d-0523cb9372d9",
+ "title": "Safe Username Maker",
+ "difficulty": "Builder",
+ "concepts": [
+ "functions",
+ "strings"
+ ],
+ "context": "A classroom app needs to turn a full name into a simple username.",
+ "prompt": "What small text transformations should a username function perform?",
+ "objectives": [
+ "Accept a name",
+ "Normalize text",
+ "Return username"
+ ],
+ "sampleReasoning": "Make the name lowercase and remove spaces so it can be used as a username.",
+ "effectivenessScore": 93,
+ "createdAt": "2026-08-01T13:11:33.711Z",
+ "updatedAt": "2026-08-01T13:11:33.711Z"
+ },
+ {
+ "_id": "4a5c4d3a-50c4-4c9b-a281-a2ba712db8e0",
+ "title": "Retry Until Valid",
+ "difficulty": "Builder",
+ "concepts": [
+ "while loops",
+ "validation"
+ ],
+ "context": "A form should keep asking until the learner enters a positive number.",
+ "prompt": "What condition tells the program to keep asking?",
+ "objectives": [
+ "Define valid input",
+ "Repeat while invalid",
+ "Stop after valid value"
+ ],
+ "sampleReasoning": "Keep asking while the number is not positive, then stop once it is valid.",
+ "effectivenessScore": 92,
+ "createdAt": "2026-08-01T13:11:33.711Z",
+ "updatedAt": "2026-08-01T13:11:33.711Z"
+ },
+ {
+ "_id": "691357d0-9696-4544-bf5e-f8b01ddb68d6",
+ "title": "Simple Score Report",
+ "difficulty": "Builder",
+ "concepts": [
+ "dictionaries",
+ "functions"
+ ],
+ "context": "A tutor wants a small report showing a learner name and score together.",
+ "prompt": "How would you package two related pieces of information in one result?",
+ "objectives": [
+ "Create key-value structure",
+ "Return structured result",
+ "Connect structure to dictionary"
+ ],
+ "sampleReasoning": "Return a dictionary with the learner name and score as labeled values.",
+ "effectivenessScore": 91,
+ "createdAt": "2026-08-01T13:11:33.711Z",
+ "updatedAt": "2026-08-01T13:11:33.711Z"
+ },
+ {
+ "_id": "1fa7af37-730f-4459-ad3d-9617e1b2460e",
+ "title": "Task Status Updater",
+ "difficulty": "Builder",
+ "concepts": [
+ "dictionaries",
+ "mutation"
+ ],
+ "context": "A to-do app has tasks with statuses and needs to mark one task as done.",
+ "prompt": "What exact value changes when a task is completed?",
+ "objectives": [
+ "Find one task",
+ "Change one status",
+ "Understand updating data"
+ ],
+ "sampleReasoning": "Find the task by name and change its status from pending to done.",
+ "effectivenessScore": 90,
+ "createdAt": "2026-08-01T13:11:33.711Z",
+ "updatedAt": "2026-08-01T13:11:33.711Z"
+ },
+ {
+ "_id": "695f9446-519d-4d71-936a-f75bf5f8ee25",
+ "title": "Small Receipt Builder",
+ "difficulty": "Builder",
+ "concepts": [
+ "functions",
+ "formatting"
+ ],
+ "context": "A cafe wants a short text receipt for one item and its price.",
+ "prompt": "What pieces should a receipt function combine into a readable line?",
+ "objectives": [
+ "Accept item and price",
+ "Format text",
+ "Return one receipt line"
+ ],
+ "sampleReasoning": "Combine the item name and price into one clear sentence.",
+ "effectivenessScore": 89,
+ "createdAt": "2026-08-01T13:11:33.711Z",
+ "updatedAt": "2026-08-01T13:11:33.711Z"
+ },
+ {
+ "_id": "521060bf-fc84-4cd7-a6f7-4691eaebfce0",
+ "title": "Choose Next Scenario",
+ "difficulty": "Builder",
+ "concepts": [
+ "conditionals",
+ "adaptive logic"
+ ],
+ "context": "PyBe wants to choose an easier or harder next scenario based on one score.",
+ "prompt": "What simple score rule could decide the next difficulty?",
+ "objectives": [
+ "Set score thresholds",
+ "Branch by score",
+ "Return next level"
+ ],
+ "sampleReasoning": "If the score is high choose harder, if low choose easier, otherwise stay similar.",
+ "effectivenessScore": 88,
+ "createdAt": "2026-08-01T13:11:33.711Z",
+ "updatedAt": "2026-08-01T13:11:33.711Z"
+ },
+ {
+ "_id": "6795144c-bd8d-4469-8289-53aca81ca048",
+ "title": "Reflection Keyword Finder",
+ "difficulty": "Builder",
+ "concepts": [
+ "strings",
+ "search"
+ ],
+ "context": "PyBe reads a short reflection and checks whether the learner mentioned confusion.",
+ "prompt": "What tiny search would detect that the reflection may need extra support?",
+ "objectives": [
+ "Choose keywords",
+ "Search text",
+ "Return support signal"
+ ],
+ "sampleReasoning": "Look for words like confused or stuck and flag the reflection if they appear.",
+ "effectivenessScore": 87,
+ "createdAt": "2026-08-01T13:11:33.711Z",
+ "updatedAt": "2026-08-01T13:11:33.711Z"
+ }
+ ],
+ "sessions": []
+}
diff --git a/summership-26-prs/Niranjan Praveen/server/src/data/roadmap.js b/summership-26-prs/Niranjan Praveen/server/src/data/roadmap.js
new file mode 100644
index 0000000..ba8db7d
--- /dev/null
+++ b/summership-26-prs/Niranjan Praveen/server/src/data/roadmap.js
@@ -0,0 +1,26 @@
+module.exports = [
+ {
+ phase: 'V0',
+ title: 'Core Learning Experience',
+ summary: 'Scenario interface, abstraction mapping, conversational questioning, Python construct generation, and prompt evaluation.',
+ items: ['Scenario Interface', 'AI Abstraction Mapper', 'Conversational Questioning', 'Python Construct Generator', 'Prompt Evaluation Engine']
+ },
+ {
+ phase: 'V1',
+ title: 'Educational Data Engine',
+ summary: 'Structured educational data for scenarios, learner interactions, reflections, misconceptions, and prompts.',
+ items: ['Scenario Database', 'Learner Interaction Logging', 'Reflection Storage', 'Misconception Dataset', 'Prompt Dataset Builder']
+ },
+ {
+ phase: 'V2',
+ title: 'TinyLLM Specialization',
+ summary: 'RAG, retrieval, prompt grading, scenario generation, and misconception detection for a specialized learning companion.',
+ items: ['RAG Pipeline', 'Educational Retrieval Engine', 'Prompt Grading Model', 'Scenario Generation Model', 'Misconception Detection Model']
+ },
+ {
+ phase: 'V3',
+ title: 'Intelligent Learning Ecosystem',
+ summary: 'Personalized pathways, persistent mentor, dashboard, gamification, and collaborative learning.',
+ items: ['Learner Dashboard', 'Gamification System', 'Adaptive Learning Engine', 'Persistent AI Mentor', 'Community Ecosystem']
+ }
+];
diff --git a/summership-26-prs/Niranjan Praveen/server/src/data/store.js b/summership-26-prs/Niranjan Praveen/server/src/data/store.js
new file mode 100644
index 0000000..b6a4ce1
--- /dev/null
+++ b/summership-26-prs/Niranjan Praveen/server/src/data/store.js
@@ -0,0 +1,105 @@
+const fs = require('fs/promises');
+const path = require('path');
+const crypto = require('crypto');
+
+const dbPath = path.join(__dirname, 'db.json');
+
+async function ensureDb() {
+ try {
+ await fs.access(dbPath);
+ } catch {
+ await writeDb({ scenarios: [], sessions: [] });
+ }
+}
+
+async function readDb() {
+ await ensureDb();
+ const raw = await fs.readFile(dbPath, 'utf8');
+ return JSON.parse(raw);
+}
+
+async function writeDb(data) {
+ await fs.mkdir(path.dirname(dbPath), { recursive: true });
+ await fs.writeFile(dbPath, `${JSON.stringify(data, null, 2)}\n`, 'utf8');
+}
+
+function now() {
+ return new Date().toISOString();
+}
+
+function createRecord(input) {
+ const timestamp = now();
+ return {
+ _id: crypto.randomUUID(),
+ ...input,
+ createdAt: timestamp,
+ updatedAt: timestamp
+ };
+}
+
+async function listScenarios(filters = {}) {
+ const db = await readDb();
+ let scenarios = [...db.scenarios];
+ if (filters.difficulty) scenarios = scenarios.filter((item) => item.difficulty === filters.difficulty);
+ if (filters.concept) scenarios = scenarios.filter((item) => item.concepts.includes(filters.concept));
+ if (filters.q) {
+ const query = filters.q.toLowerCase();
+ scenarios = scenarios.filter((item) => (
+ item.title.toLowerCase().includes(query) ||
+ item.context.toLowerCase().includes(query) ||
+ item.concepts.some((concept) => concept.toLowerCase().includes(query))
+ ));
+ }
+ return scenarios.sort((a, b) => (b.effectivenessScore || 0) - (a.effectivenessScore || 0));
+}
+
+async function getScenario(id) {
+ const db = await readDb();
+ return db.scenarios.find((scenario) => scenario._id === id) || null;
+}
+
+async function addScenario(input) {
+ const db = await readDb();
+ const scenario = createRecord(input);
+ db.scenarios.push(scenario);
+ await writeDb(db);
+ return scenario;
+}
+
+async function listSessions() {
+ const db = await readDb();
+ return db.sessions
+ .map((session) => ({
+ ...session,
+ scenario: db.scenarios.find((scenario) => scenario._id === session.scenario) || null
+ }))
+ .sort((a, b) => new Date(b.createdAt) - new Date(a.createdAt));
+}
+
+async function addSession(input) {
+ const db = await readDb();
+ const session = createRecord(input);
+ db.sessions.push(session);
+ await writeDb(db);
+ return {
+ ...session,
+ scenario: db.scenarios.find((scenario) => scenario._id === session.scenario) || null
+ };
+}
+
+async function resetData(scenarios) {
+ await writeDb({
+ scenarios: scenarios.map((scenario) => createRecord(scenario)),
+ sessions: []
+ });
+}
+
+module.exports = {
+ addScenario,
+ addSession,
+ getScenario,
+ listScenarios,
+ listSessions,
+ readDb,
+ resetData
+};
diff --git a/summership-26-prs/Niranjan Praveen/server/src/index.js b/summership-26-prs/Niranjan Praveen/server/src/index.js
new file mode 100644
index 0000000..a756928
--- /dev/null
+++ b/summership-26-prs/Niranjan Praveen/server/src/index.js
@@ -0,0 +1,30 @@
+const express = require('express');
+const cors = require('cors');
+const morgan = require('morgan');
+const scenarioRoutes = require('./routes/scenarios');
+const sessionRoutes = require('./routes/sessions');
+const analyticsRoutes = require('./routes/analytics');
+const roadmapRoutes = require('./routes/roadmap');
+const masteryRoutes = require('./routes/mastery');
+require('dotenv').config();
+
+const app = express();
+const port = process.env.PORT || 5000;
+
+app.use(cors({ origin: process.env.CLIENT_ORIGIN || 'http://localhost:5173' }));
+app.use(express.json());
+app.use(morgan('dev'));
+
+app.get('/api/health', (_req, res) => res.json({ ok: true, product: 'PyBe' }));
+app.use('/api/scenarios', scenarioRoutes);
+app.use('/api/sessions', sessionRoutes);
+app.use('/api/analytics', analyticsRoutes);
+app.use('/api/roadmap', roadmapRoutes);
+app.use('/api/mastery', masteryRoutes);
+
+app.use((error, _req, res, _next) => {
+ console.error(error);
+ res.status(error.status || 500).json({ message: error.message || 'Server error' });
+});
+
+app.listen(port, () => console.log(`PyBe API running on http://localhost:${port}`));
diff --git a/summership-26-prs/Niranjan Praveen/server/src/routes/analytics.js b/summership-26-prs/Niranjan Praveen/server/src/routes/analytics.js
new file mode 100644
index 0000000..8d4c57f
--- /dev/null
+++ b/summership-26-prs/Niranjan Praveen/server/src/routes/analytics.js
@@ -0,0 +1,37 @@
+const express = require('express');
+const store = require('../data/store');
+
+const router = express.Router();
+
+router.get('/', async (_req, res, next) => {
+ try {
+ const [sessions, db] = await Promise.all([store.listSessions(), store.readDb()]);
+ const scenarioCount = db.scenarios.length;
+ const conceptCounts = {};
+ const misconceptionCounts = {};
+ let promptTotal = 0;
+
+ sessions.forEach((session) => {
+ promptTotal += session.promptScore || 0;
+ session.abstractionMap.forEach((map) => {
+ conceptCounts[map.pythonConcept] = (conceptCounts[map.pythonConcept] || 0) + 1;
+ });
+ session.misconceptions.forEach((item) => {
+ misconceptionCounts[item] = (misconceptionCounts[item] || 0) + 1;
+ });
+ });
+
+ res.json({
+ scenarioCount,
+ sessionCount: sessions.length,
+ averagePromptScore: sessions.length ? Math.round(promptTotal / sessions.length) : 0,
+ conceptCounts,
+ misconceptionCounts,
+ recentSessions: sessions.slice(0, 5)
+ });
+ } catch (error) {
+ next(error);
+ }
+});
+
+module.exports = router;
diff --git a/summership-26-prs/Niranjan Praveen/server/src/routes/mastery.js b/summership-26-prs/Niranjan Praveen/server/src/routes/mastery.js
new file mode 100644
index 0000000..12bb35f
--- /dev/null
+++ b/summership-26-prs/Niranjan Praveen/server/src/routes/mastery.js
@@ -0,0 +1,20 @@
+const express = require('express');
+const store = require('../data/store');
+const { buildMasteryProfile } = require('../services/masteryEngine');
+
+const router = express.Router();
+
+router.get('/', async (req, res, next) => {
+ try {
+ const [db, sessions] = await Promise.all([store.readDb(), store.listSessions()]);
+ const learner = (req.query.learner || '').trim();
+ const scoped = learner
+ ? sessions.filter((session) => session.learnerName === learner)
+ : sessions;
+ res.json(buildMasteryProfile(db.scenarios, scoped));
+ } catch (error) {
+ next(error);
+ }
+});
+
+module.exports = router;
diff --git a/summership-26-prs/Niranjan Praveen/server/src/routes/roadmap.js b/summership-26-prs/Niranjan Praveen/server/src/routes/roadmap.js
new file mode 100644
index 0000000..aa14b0e
--- /dev/null
+++ b/summership-26-prs/Niranjan Praveen/server/src/routes/roadmap.js
@@ -0,0 +1,8 @@
+const express = require('express');
+const roadmap = require('../data/roadmap');
+
+const router = express.Router();
+
+router.get('/', (_req, res) => res.json(roadmap));
+
+module.exports = router;
diff --git a/summership-26-prs/Niranjan Praveen/server/src/routes/scenarios.js b/summership-26-prs/Niranjan Praveen/server/src/routes/scenarios.js
new file mode 100644
index 0000000..ea17344
--- /dev/null
+++ b/summership-26-prs/Niranjan Praveen/server/src/routes/scenarios.js
@@ -0,0 +1,35 @@
+const express = require('express');
+const store = require('../data/store');
+
+const router = express.Router();
+
+router.get('/', async (req, res, next) => {
+ try {
+ const { q, concept, difficulty } = req.query;
+ const scenarios = await store.listScenarios({ q, concept, difficulty });
+ res.json(scenarios);
+ } catch (error) {
+ next(error);
+ }
+});
+
+router.post('/', async (req, res, next) => {
+ try {
+ const scenario = await store.addScenario(req.body);
+ res.status(201).json(scenario);
+ } catch (error) {
+ next(error);
+ }
+});
+
+router.get('/:id', async (req, res, next) => {
+ try {
+ const scenario = await store.getScenario(req.params.id);
+ if (!scenario) return res.status(404).json({ message: 'Scenario not found' });
+ res.json(scenario);
+ } catch (error) {
+ next(error);
+ }
+});
+
+module.exports = router;
diff --git a/summership-26-prs/Niranjan Praveen/server/src/routes/sessions.js b/summership-26-prs/Niranjan Praveen/server/src/routes/sessions.js
new file mode 100644
index 0000000..86d942a
--- /dev/null
+++ b/summership-26-prs/Niranjan Praveen/server/src/routes/sessions.js
@@ -0,0 +1,44 @@
+const express = require('express');
+const store = require('../data/store');
+const engine = require('../services/learningEngine');
+
+const router = express.Router();
+
+router.get('/', async (_req, res, next) => {
+ try {
+ const sessions = await store.listSessions();
+ res.json(sessions.slice(0, 30));
+ } catch (error) {
+ next(error);
+ }
+});
+
+router.post('/', async (req, res, next) => {
+ try {
+ const scenario = await store.getScenario(req.body.scenarioId);
+ if (!scenario) return res.status(404).json({ message: 'Scenario not found' });
+
+ const abstractionMap = engine.mapReasoning(req.body.reasoning);
+ const generatedCode = engine.generateCode(scenario, abstractionMap);
+ const prompt = engine.evaluatePrompt(req.body.promptText);
+ const session = await store.addSession({
+ learnerName: req.body.learnerName || 'Guest learner',
+ scenario: scenario._id,
+ reasoning: req.body.reasoning,
+ promptText: req.body.promptText || '',
+ abstractionMap,
+ generatedCode,
+ codeExplanation: engine.explainCode(abstractionMap),
+ promptScore: prompt.score,
+ promptFeedback: prompt.feedback,
+ reflection: req.body.reflection || '',
+ misconceptions: engine.detectMisconceptions(req.body.reasoning),
+ masterySignals: engine.masterySignals(abstractionMap, prompt.score)
+ });
+ res.status(201).json(session);
+ } catch (error) {
+ next(error);
+ }
+});
+
+module.exports = router;
diff --git a/summership-26-prs/Niranjan Praveen/server/src/seed.js b/summership-26-prs/Niranjan Praveen/server/src/seed.js
new file mode 100644
index 0000000..2f80d45
--- /dev/null
+++ b/summership-26-prs/Niranjan Praveen/server/src/seed.js
@@ -0,0 +1,315 @@
+const { resetData } = require('./data/store');
+require('dotenv').config();
+
+const scenarios = [
+ {
+ title: 'Bag Weight Label',
+ difficulty: 'Beginner',
+ concepts: ['variables'],
+ context: 'A student has one school bag with a weight written on a scale.',
+ prompt: 'What single piece of information would you store so the computer can remember the bag weight?',
+ objectives: ['Identify one value', 'Give the value a name', 'Connect naming to a variable'],
+ sampleReasoning: 'I only need to remember the bag weight, so I would store it with a clear name.',
+ effectivenessScore: 96
+ },
+ {
+ title: 'Rainy Day Choice',
+ difficulty: 'Beginner',
+ concepts: ['conditionals'],
+ context: 'Before leaving home, a learner checks whether it is raining.',
+ prompt: 'What small decision rule would help the learner decide whether to carry an umbrella?',
+ objectives: ['Notice one condition', 'Choose one action', 'Map the rule to if/else'],
+ sampleReasoning: 'If it is raining, carry an umbrella. Otherwise, leave it at home.',
+ effectivenessScore: 95
+ },
+ {
+ title: 'Two Snack Prices',
+ difficulty: 'Beginner',
+ concepts: ['arithmetic', 'variables'],
+ context: 'A learner buys one samosa and one juice from the canteen.',
+ prompt: 'How would you figure out the total cost using just the two prices?',
+ objectives: ['Store two values', 'Add values', 'Name the result'],
+ sampleReasoning: 'I would keep the two prices separately and add them to get the total.',
+ effectivenessScore: 94
+ },
+ {
+ title: 'Greeting by Name',
+ difficulty: 'Beginner',
+ concepts: ['strings', 'variables'],
+ context: 'A classroom screen should greet one learner by name.',
+ prompt: 'What text should the computer remember, and how should it combine that text into a greeting?',
+ objectives: ['Store text', 'Combine text', 'Recognize strings'],
+ sampleReasoning: 'The computer should store the learner name and place it inside a greeting sentence.',
+ effectivenessScore: 93
+ },
+ {
+ title: 'Pass Mark Check',
+ difficulty: 'Beginner',
+ concepts: ['comparisons', 'conditionals'],
+ context: 'A learner receives one test score and wants to know whether it is at least the pass mark.',
+ prompt: 'What comparison would decide if the learner passed?',
+ objectives: ['Compare two numbers', 'Create a yes/no result', 'Map comparison to condition'],
+ sampleReasoning: 'I would compare the score with the pass mark and decide pass if it is high enough.',
+ effectivenessScore: 92
+ },
+ {
+ title: 'Pocket Money Left',
+ difficulty: 'Beginner',
+ concepts: ['subtraction', 'variables'],
+ context: 'A learner starts with pocket money and spends a small amount on stationery.',
+ prompt: 'What two values matter, and how would you find the money left?',
+ objectives: ['Name starting amount', 'Name spent amount', 'Subtract to find remaining value'],
+ sampleReasoning: 'Start with the original money, subtract the spent amount, and store what remains.',
+ effectivenessScore: 91
+ },
+ {
+ title: 'Favorite Color List',
+ difficulty: 'Beginner',
+ concepts: ['lists'],
+ context: 'Three friends each say their favorite color.',
+ prompt: 'How would you keep all three colors together instead of making separate notes?',
+ objectives: ['Group related values', 'Recognize a collection', 'Map grouping to a list'],
+ sampleReasoning: 'Since all values are colors, I would keep them together in one list.',
+ effectivenessScore: 90
+ },
+ {
+ title: 'First Item in a Bag',
+ difficulty: 'Beginner',
+ concepts: ['lists', 'indexing'],
+ context: 'A bag contains a pencil, eraser, and ruler in that order.',
+ prompt: 'How would you ask for only the first item?',
+ objectives: ['Notice order', 'Pick one position', 'Connect position to indexing'],
+ sampleReasoning: 'The first item is based on its position in the ordered group.',
+ effectivenessScore: 89
+ },
+ {
+ title: 'Attendance Count',
+ difficulty: 'Beginner',
+ concepts: ['counting', 'lists'],
+ context: 'A teacher has a short list of students present today.',
+ prompt: 'What question would help you find how many students are present?',
+ objectives: ['Recognize a group', 'Ask for its size', 'Connect size to length'],
+ sampleReasoning: 'I would count how many names are in the present-students list.',
+ effectivenessScore: 88
+ },
+ {
+ title: 'Temperature Message',
+ difficulty: 'Beginner',
+ concepts: ['conditionals', 'comparisons'],
+ context: 'A weather display has one temperature reading.',
+ prompt: 'What simple rule would decide whether to show Hot or Comfortable?',
+ objectives: ['Set a threshold', 'Compare one value', 'Choose one message'],
+ sampleReasoning: 'If the temperature is above the threshold, show Hot; otherwise show Comfortable.',
+ effectivenessScore: 87
+ },
+ {
+ title: 'Water Bottle Reminder',
+ difficulty: 'Explorer',
+ concepts: ['loops'],
+ context: 'A learner wants a reminder for each break in the school day.',
+ prompt: 'How would you avoid writing the same reminder separately for every break?',
+ objectives: ['Recognize repeated action', 'Identify each break', 'Map repetition to a loop'],
+ sampleReasoning: 'For every break, show the same water reminder.',
+ effectivenessScore: 96
+ },
+ {
+ title: 'Find the Longest Pencil',
+ difficulty: 'Explorer',
+ concepts: ['loops', 'comparisons'],
+ context: 'A learner has several pencil lengths and wants to know which one is longest.',
+ prompt: 'What small comparison would you repeat as you look through the lengths?',
+ objectives: ['Keep current best', 'Compare one item at a time', 'Update when larger'],
+ sampleReasoning: 'Start with one pencil as the longest, then compare each next pencil against it.',
+ effectivenessScore: 95
+ },
+ {
+ title: 'Clean Chore Checklist',
+ difficulty: 'Explorer',
+ concepts: ['lists', 'loops'],
+ context: 'A family has a checklist of small chores for Saturday morning.',
+ prompt: 'How would you go through each chore and mark it as done?',
+ objectives: ['Store chores in a list', 'Process one chore at a time', 'Repeat a simple action'],
+ sampleReasoning: 'Put chores in a list and handle each chore one by one.',
+ effectivenessScore: 94
+ },
+ {
+ title: 'Movie Age Filter',
+ difficulty: 'Explorer',
+ concepts: ['filtering', 'conditionals'],
+ context: 'A movie app has a list of films, each with a minimum age.',
+ prompt: 'What rule would decide which movies a 12-year-old can see?',
+ objectives: ['Check one item rule', 'Keep allowed items', 'Map rule to filtering'],
+ sampleReasoning: 'For each movie, keep it only if the learner age is at least the minimum age.',
+ effectivenessScore: 93
+ },
+ {
+ title: 'Classroom Supply Lookup',
+ difficulty: 'Explorer',
+ concepts: ['dictionaries'],
+ context: 'A class monitor tracks how many chalk pieces, markers, and notebooks are available.',
+ prompt: 'How would you store each supply name with its count?',
+ objectives: ['Pair names with values', 'Look up by name', 'Map pairs to a dictionary'],
+ sampleReasoning: 'Each supply has a count, so I would store supply names as keys with counts as values.',
+ effectivenessScore: 92
+ },
+ {
+ title: 'Bus Stop Search',
+ difficulty: 'Explorer',
+ concepts: ['search', 'loops'],
+ context: 'A route list contains several bus stops, and a learner wants to know if Library Stop is included.',
+ prompt: 'How would you check the stops one at a time until you find the target?',
+ objectives: ['Identify target', 'Scan a list', 'Stop when found'],
+ sampleReasoning: 'Look at each stop and compare it with the stop I want.',
+ effectivenessScore: 91
+ },
+ {
+ title: 'Average Practice Score',
+ difficulty: 'Explorer',
+ concepts: ['averages', 'loops'],
+ context: 'A learner has five practice scores and wants one average score.',
+ prompt: 'What two small steps are needed before dividing?',
+ objectives: ['Add all scores', 'Count scores', 'Divide total by count'],
+ sampleReasoning: 'Find the total of all scores, count how many scores there are, then divide.',
+ effectivenessScore: 90
+ },
+ {
+ title: 'Separate Even Roll Numbers',
+ difficulty: 'Explorer',
+ concepts: ['modulo', 'filtering'],
+ context: 'A class list has roll numbers, and the teacher wants only even-numbered students for Team A.',
+ prompt: 'What small test tells you whether a roll number is even?',
+ objectives: ['Test divisibility by two', 'Keep matching numbers', 'Connect remainder to modulo'],
+ sampleReasoning: 'A roll number is even if dividing by two leaves no remainder.',
+ effectivenessScore: 89
+ },
+ {
+ title: 'Capitalize Name Tags',
+ difficulty: 'Explorer',
+ concepts: ['strings', 'loops'],
+ context: 'A club has names typed in lowercase and wants neat name tags.',
+ prompt: 'What same text-cleaning action should happen to every name?',
+ objectives: ['Recognize repeated string change', 'Apply to each name', 'Create cleaned names'],
+ sampleReasoning: 'For each name, convert it to title case before printing the tag.',
+ effectivenessScore: 88
+ },
+ {
+ title: 'Find Missing Homework',
+ difficulty: 'Explorer',
+ concepts: ['sets', 'comparison'],
+ context: 'A teacher has one list of all students and another list of students who submitted homework.',
+ prompt: 'How would you reason about who is missing from the submitted list?',
+ objectives: ['Compare two groups', 'Find difference', 'Map group difference to sets'],
+ sampleReasoning: 'Take everyone in the class and remove the students who submitted.',
+ effectivenessScore: 87
+ },
+ {
+ title: 'Reusable Discount Rule',
+ difficulty: 'Builder',
+ concepts: ['functions', 'conditionals'],
+ context: 'A shop gives the same discount rule to many different bills.',
+ prompt: 'What inputs should a reusable discount helper receive?',
+ objectives: ['Identify reusable rule', 'Choose inputs', 'Return discounted price'],
+ sampleReasoning: 'The helper needs the bill amount and should return the final price after applying the rule.',
+ effectivenessScore: 96
+ },
+ {
+ title: 'Mini Quiz Checker',
+ difficulty: 'Builder',
+ concepts: ['functions', 'comparisons'],
+ context: 'A quiz app needs to check one answer against one correct answer.',
+ prompt: 'How would you design a tiny reusable checker for one question?',
+ objectives: ['Accept learner answer', 'Accept correct answer', 'Return right or wrong'],
+ sampleReasoning: 'Compare the learner answer with the correct answer and return whether they match.',
+ effectivenessScore: 95
+ },
+ {
+ title: 'Step Counter Function',
+ difficulty: 'Builder',
+ concepts: ['functions', 'lists'],
+ context: 'A fitness note has a list of step counts from different walks.',
+ prompt: 'How would you make a reusable helper that returns the total steps?',
+ objectives: ['Accept a list', 'Add all values', 'Return total'],
+ sampleReasoning: 'The function should take step counts, add them, and give back the total.',
+ effectivenessScore: 94
+ },
+ {
+ title: 'Safe Username Maker',
+ difficulty: 'Builder',
+ concepts: ['functions', 'strings'],
+ context: 'A classroom app needs to turn a full name into a simple username.',
+ prompt: 'What small text transformations should a username function perform?',
+ objectives: ['Accept a name', 'Normalize text', 'Return username'],
+ sampleReasoning: 'Make the name lowercase and remove spaces so it can be used as a username.',
+ effectivenessScore: 93
+ },
+ {
+ title: 'Retry Until Valid',
+ difficulty: 'Builder',
+ concepts: ['while loops', 'validation'],
+ context: 'A form should keep asking until the learner enters a positive number.',
+ prompt: 'What condition tells the program to keep asking?',
+ objectives: ['Define valid input', 'Repeat while invalid', 'Stop after valid value'],
+ sampleReasoning: 'Keep asking while the number is not positive, then stop once it is valid.',
+ effectivenessScore: 92
+ },
+ {
+ title: 'Simple Score Report',
+ difficulty: 'Builder',
+ concepts: ['dictionaries', 'functions'],
+ context: 'A tutor wants a small report showing a learner name and score together.',
+ prompt: 'How would you package two related pieces of information in one result?',
+ objectives: ['Create key-value structure', 'Return structured result', 'Connect structure to dictionary'],
+ sampleReasoning: 'Return a dictionary with the learner name and score as labeled values.',
+ effectivenessScore: 91
+ },
+ {
+ title: 'Task Status Updater',
+ difficulty: 'Builder',
+ concepts: ['dictionaries', 'mutation'],
+ context: 'A to-do app has tasks with statuses and needs to mark one task as done.',
+ prompt: 'What exact value changes when a task is completed?',
+ objectives: ['Find one task', 'Change one status', 'Understand updating data'],
+ sampleReasoning: 'Find the task by name and change its status from pending to done.',
+ effectivenessScore: 90
+ },
+ {
+ title: 'Small Receipt Builder',
+ difficulty: 'Builder',
+ concepts: ['functions', 'formatting'],
+ context: 'A cafe wants a short text receipt for one item and its price.',
+ prompt: 'What pieces should a receipt function combine into a readable line?',
+ objectives: ['Accept item and price', 'Format text', 'Return one receipt line'],
+ sampleReasoning: 'Combine the item name and price into one clear sentence.',
+ effectivenessScore: 89
+ },
+ {
+ title: 'Choose Next Scenario',
+ difficulty: 'Builder',
+ concepts: ['conditionals', 'adaptive logic'],
+ context: 'PyBe wants to choose an easier or harder next scenario based on one score.',
+ prompt: 'What simple score rule could decide the next difficulty?',
+ objectives: ['Set score thresholds', 'Branch by score', 'Return next level'],
+ sampleReasoning: 'If the score is high choose harder, if low choose easier, otherwise stay similar.',
+ effectivenessScore: 88
+ },
+ {
+ title: 'Reflection Keyword Finder',
+ difficulty: 'Builder',
+ concepts: ['strings', 'search'],
+ context: 'PyBe reads a short reflection and checks whether the learner mentioned confusion.',
+ prompt: 'What tiny search would detect that the reflection may need extra support?',
+ objectives: ['Choose keywords', 'Search text', 'Return support signal'],
+ sampleReasoning: 'Look for words like confused or stuck and flag the reflection if they appear.',
+ effectivenessScore: 87
+ }
+];
+
+async function run() {
+ await resetData(scenarios);
+ console.log(`Seeded ${scenarios.length} PyBe scenarios`);
+}
+
+run().catch((error) => {
+ console.error(error);
+ process.exit(1);
+});
diff --git a/summership-26-prs/Niranjan Praveen/server/src/services/learningEngine.js b/summership-26-prs/Niranjan Praveen/server/src/services/learningEngine.js
new file mode 100644
index 0000000..9e6b82b
--- /dev/null
+++ b/summership-26-prs/Niranjan Praveen/server/src/services/learningEngine.js
@@ -0,0 +1,109 @@
+const conceptRules = [
+ {
+ keywords: ['repeat', 'again', 'each', 'every', 'loop'],
+ pattern: 'Repetition',
+ pythonConcept: 'for / while loops',
+ explanation: 'Your reasoning repeats an action, which maps naturally to loop constructs.'
+ },
+ {
+ keywords: ['if', 'when', 'unless', 'decide', 'choose', 'condition'],
+ pattern: 'Decision making',
+ pythonConcept: 'if / elif / else',
+ explanation: 'You are branching based on a condition, which is exactly what conditional statements express.'
+ },
+ {
+ keywords: ['list', 'items', 'collection', 'group', 'many'],
+ pattern: 'Collection handling',
+ pythonConcept: 'lists and dictionaries',
+ explanation: 'You grouped multiple values, so Python collections help store and process them.'
+ },
+ {
+ keywords: ['calculate', 'total', 'average', 'sum', 'score', 'cost'],
+ pattern: 'Computation',
+ pythonConcept: 'variables and arithmetic expressions',
+ explanation: 'You are transforming values into a result, so variables and expressions become useful.'
+ },
+ {
+ keywords: ['step', 'process', 'recipe', 'function', 'reuse'],
+ pattern: 'Reusable procedure',
+ pythonConcept: 'functions',
+ explanation: 'You described a repeatable process, which maps to a Python function.'
+ },
+ {
+ keywords: ['compare', 'match', 'filter', 'find', 'search'],
+ pattern: 'Selection and filtering',
+ pythonConcept: 'comparisons and list comprehensions',
+ explanation: 'You are narrowing options using rules, which Python can express with comparisons and filters.'
+ }
+];
+
+function mapReasoning(reasoning = '') {
+ const lower = reasoning.toLowerCase();
+ const matches = conceptRules.filter((rule) => rule.keywords.some((keyword) => lower.includes(keyword)));
+ return matches.length ? matches : [{
+ pattern: 'Sequential thinking',
+ pythonConcept: 'statements and variables',
+ explanation: 'You described a step-by-step solution. Python starts by representing those steps as statements.'
+ }];
+}
+
+function generateCode(scenario, maps) {
+ const concepts = maps.map((item) => item.pythonConcept).join(', ');
+ const hasLoop = concepts.includes('loop');
+ const hasCondition = concepts.includes('if');
+ const hasFunction = concepts.includes('function');
+
+ if (hasLoop && hasCondition) {
+ return 'items = [12, 7, 19, 4]\nthreshold = 10\n\nfor item in items:\n if item >= threshold:\n print(f"{item} needs attention")\n else:\n print(f"{item} is okay")';
+ }
+
+ if (hasFunction) {
+ return 'def solve_scenario(inputs):\n result = []\n for value in inputs:\n result.append(value * 2)\n return result\n\nprint(solve_scenario([1, 2, 3]))';
+ }
+
+ if (hasLoop) {
+ return 'steps = ["notice the situation", "look for a pattern", "apply the rule"]\n\nfor step in steps:\n print(step)';
+ }
+
+ if (hasCondition) {
+ return 'temperature = 32\n\nif temperature > 30:\n print("Take action now")\nelse:\n print("Keep observing")';
+ }
+
+ return `scenario = "${scenario.title.replace(/"/g, '\\"')}"\nreasoning = "Break the situation into clear steps"\nprint(scenario)\nprint(reasoning)`;
+}
+
+function explainCode(maps) {
+ return `The code starts from your natural reasoning and turns it into Python structure: ${maps.map((map) => `${map.pattern} becomes ${map.pythonConcept}`).join('; ')}.`;
+}
+
+function evaluatePrompt(promptText = '') {
+ const feedback = [];
+ let score = 35;
+ if (promptText.length > 40) score += 15;
+ else feedback.push('Add more context about the situation and expected output.');
+ if (/step|explain|why|reason/i.test(promptText)) score += 20;
+ else feedback.push('Ask the AI to explain its reasoning, not just produce code.');
+ if (/example|input|output|data/i.test(promptText)) score += 15;
+ else feedback.push('Include an example input or output to make the prompt testable.');
+ if (/python|loop|if|list|function/i.test(promptText)) score += 15;
+ else feedback.push('Name the Python concept you think may apply.');
+ return {
+ score: Math.min(score, 100),
+ feedback: feedback.length ? feedback : ['Strong prompt: it includes context, reasoning, examples, and a Python direction.']
+ };
+}
+
+function detectMisconceptions(reasoning = '') {
+ const misses = [];
+ if (/always|never/i.test(reasoning)) misses.push('Watch for absolute rules. Programming logic often needs explicit edge cases.');
+ if (reasoning.length < 60) misses.push('Reasoning is brief. Try naming the inputs, decision rule, and expected result.');
+ return misses;
+}
+
+function masterySignals(maps, promptScore) {
+ const signals = maps.map((map) => `Recognized ${map.pattern.toLowerCase()}`);
+ if (promptScore >= 70) signals.push('Prompt maturity is developing');
+ return signals;
+}
+
+module.exports = { mapReasoning, generateCode, explainCode, evaluatePrompt, detectMisconceptions, masterySignals };
diff --git a/summership-26-prs/Niranjan Praveen/server/src/services/masteryEngine.js b/summership-26-prs/Niranjan Praveen/server/src/services/masteryEngine.js
new file mode 100644
index 0000000..37b3175
--- /dev/null
+++ b/summership-26-prs/Niranjan Praveen/server/src/services/masteryEngine.js
@@ -0,0 +1,165 @@
+// Mastery Progression Engine
+//
+// Pedagogical grounding:
+// - Bloom's "Learning for Mastery" (1968): learners advance only after crossing an
+// explicit mastery threshold, instead of moving on with gaps.
+// - Piaget's stage theory: the three PyBe difficulties are treated as cognitive
+// stages that build on each other rather than arbitrary labels.
+// - Vygotsky's Zone of Proximal Development: the recommender surfaces scenarios
+// that stretch the learner's weakest concepts while staying within reach.
+//
+// Everything below is deterministic and local, consistent with the PyBe V0
+// constraint of rule-based logic with no external AI calls.
+
+const MASTERY_THRESHOLD = Number(process.env.MASTERY_THRESHOLD) || 70;
+const MIN_SESSIONS_TO_ADVANCE = 3;
+const COVERAGE_TO_ADVANCE = 0.5;
+
+const LEVELS = [
+ {
+ level: 'Beginner',
+ stage: 'Concrete Foundations',
+ piaget: 'Concrete operations: reasoning anchored to tangible, single-step situations.'
+ },
+ {
+ level: 'Explorer',
+ stage: 'Relational Thinking',
+ piaget: 'Transitional stage: coordinating repetition, comparison, and grouping.'
+ },
+ {
+ level: 'Builder',
+ stage: 'Formal Abstraction',
+ piaget: 'Formal operations: designing reusable, general procedures from rules.'
+ }
+];
+
+function clamp(value, min, max) {
+ return Math.max(min, Math.min(max, value));
+}
+
+// Mastery per curriculum concept (the tags on scenarios, e.g. "loops").
+// Signals per concept, drawn from every session touching a scenario with that tag:
+// exposure – repeated engagement (max 40)
+// prompt quality – avg prompt maturity score (max 40)
+// abstraction depth – avg reasoning patterns recognized per session (max 15)
+// reflection – bonus for reflective practice (max 5)
+// misconceptions – penalty for flagged reasoning gaps (up to -20)
+function computeConceptMastery(sessions) {
+ const perConcept = {};
+
+ sessions.forEach((session) => {
+ const concepts = session.scenario?.concepts || [];
+ concepts.forEach((concept) => {
+ if (!perConcept[concept]) {
+ perConcept[concept] = { count: 0, promptTotal: 0, mapTotal: 0, reflections: 0, misconceptions: 0 };
+ }
+ const entry = perConcept[concept];
+ entry.count += 1;
+ entry.promptTotal += session.promptScore || 0;
+ entry.mapTotal += (session.abstractionMap || []).length;
+ if ((session.reflection || '').trim().length > 0) entry.reflections += 1;
+ entry.misconceptions += (session.misconceptions || []).length;
+ });
+ });
+
+ return Object.entries(perConcept).map(([concept, entry]) => {
+ const exposure = Math.min(entry.count, 4) * 10;
+ const promptQuality = (entry.promptTotal / entry.count) * 0.4;
+ const abstractionDepth = Math.min(entry.mapTotal / entry.count, 3) * 5;
+ const reflectionBonus = entry.reflections > 0 ? 5 : 0;
+ const misconceptionPenalty = Math.min(entry.misconceptions * 5, 20);
+ const mastery = clamp(Math.round(exposure + promptQuality + abstractionDepth + reflectionBonus - misconceptionPenalty), 0, 100);
+ return {
+ concept,
+ mastery,
+ sessions: entry.count,
+ status: mastery >= MASTERY_THRESHOLD ? 'Mastered' : mastery >= MASTERY_THRESHOLD / 2 ? 'Developing' : 'Emerging'
+ };
+ }).sort((a, b) => b.mastery - a.mastery);
+}
+
+// Level progression: a level's concept pool is every tag used by its scenarios.
+// The next stage unlocks once the learner has (a) completed enough sessions at the
+// current stage and (b) mastered at least half of its concept pool — Bloom's
+// threshold applied at the stage boundary.
+function computeLevelProgress(scenarios, sessions, conceptMastery) {
+ const masteryByConcept = Object.fromEntries(conceptMastery.map((item) => [item.concept, item.mastery]));
+
+ const levels = LEVELS.map((meta) => {
+ const levelScenarios = scenarios.filter((scenario) => scenario.difficulty === meta.level);
+ const conceptPool = [...new Set(levelScenarios.flatMap((scenario) => scenario.concepts || []))];
+ const mastered = conceptPool.filter((concept) => (masteryByConcept[concept] || 0) >= MASTERY_THRESHOLD);
+ const sessionsCompleted = sessions.filter((session) => session.scenario?.difficulty === meta.level).length;
+ return {
+ ...meta,
+ conceptPool,
+ masteredConcepts: mastered,
+ coverage: conceptPool.length ? Math.round((mastered.length / conceptPool.length) * 100) : 0,
+ sessionsCompleted,
+ unlocked: false
+ };
+ });
+
+ levels.forEach((level, index) => {
+ if (index === 0) {
+ level.unlocked = true;
+ return;
+ }
+ const previous = levels[index - 1];
+ level.unlocked = previous.unlocked &&
+ previous.sessionsCompleted >= MIN_SESSIONS_TO_ADVANCE &&
+ previous.coverage >= COVERAGE_TO_ADVANCE * 100;
+ });
+
+ return levels;
+}
+
+// ZPD recommender: among unattempted scenarios in unlocked levels, prefer the ones
+// whose concepts have the largest gap below the mastery threshold — the scenarios
+// that stretch the learner most while remaining reachable.
+function recommendScenarios(scenarios, sessions, conceptMastery, levels) {
+ const masteryByConcept = Object.fromEntries(conceptMastery.map((item) => [item.concept, item.mastery]));
+ const unlockedLevels = new Set(levels.filter((level) => level.unlocked).map((level) => level.level));
+ const attempted = new Set(sessions.map((session) => session.scenario?._id).filter(Boolean));
+
+ return scenarios
+ .filter((scenario) => unlockedLevels.has(scenario.difficulty) && !attempted.has(scenario._id))
+ .map((scenario) => {
+ const gaps = (scenario.concepts || []).map((concept) => ({
+ concept,
+ gap: Math.max(0, MASTERY_THRESHOLD - (masteryByConcept[concept] || 0))
+ }));
+ const growthPotential = gaps.reduce((total, item) => total + item.gap, 0);
+ const weakest = gaps.sort((a, b) => b.gap - a.gap)[0];
+ return {
+ scenarioId: scenario._id,
+ title: scenario.title,
+ difficulty: scenario.difficulty,
+ concepts: scenario.concepts,
+ growthPotential,
+ reason: weakest && weakest.gap > 0
+ ? `Builds ${weakest.concept} (current mastery ${masteryByConcept[weakest.concept] || 0}/100)`
+ : 'Reinforces concepts you already handle well'
+ };
+ })
+ .sort((a, b) => b.growthPotential - a.growthPotential ||
+ (scenarios.find((s) => s._id === b.scenarioId)?.effectivenessScore || 0) -
+ (scenarios.find((s) => s._id === a.scenarioId)?.effectivenessScore || 0))
+ .slice(0, 3);
+}
+
+function buildMasteryProfile(scenarios, sessions) {
+ const conceptMastery = computeConceptMastery(sessions);
+ const levels = computeLevelProgress(scenarios, sessions, conceptMastery);
+ const recommendations = recommendScenarios(scenarios, sessions, conceptMastery, levels);
+ return {
+ threshold: MASTERY_THRESHOLD,
+ minSessionsToAdvance: MIN_SESSIONS_TO_ADVANCE,
+ totalSessions: sessions.length,
+ concepts: conceptMastery,
+ levels,
+ recommendations
+ };
+}
+
+module.exports = { buildMasteryProfile, computeConceptMastery, computeLevelProgress, recommendScenarios };