diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index 2676745a..88ab7dc8 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -32,8 +32,11 @@ jobs: filters: | backend: - 'backend/**' + # The rebuilt app in web/ is what ships. frontend/ is still in the + # tree for one release as a fallback but is no longer deployed, so + # a change to it must not trigger a build. frontend: - - 'frontend/**' + - 'web/**' nginx: - 'infra/nginx/**' meilisearch: @@ -59,6 +62,74 @@ jobs: - 'infra/postgres/**' - 'infra/backup/**' + # Validate the exact source contract before publishing an image or static + # artifact. The dummy env supplies import-time Config values only; FastAPI + # lifespan is not started by the exporter. + - name: Install PostgreSQL build tools + if: steps.changes.outputs.backend == 'true' || steps.changes.outputs.frontend == 'true' + run: sudo apt-get update && sudo apt-get install -y libpq-dev + + - name: Set up Python for backend checks + if: steps.changes.outputs.backend == 'true' || steps.changes.outputs.frontend == 'true' + uses: actions/setup-python@v5 + with: + python-version: "3.12" + + - name: Set up uv + if: steps.changes.outputs.backend == 'true' || steps.changes.outputs.frontend == 'true' + uses: astral-sh/setup-uv@v6 + with: + enable-cache: true + cache-dependency-glob: backend/uv.lock + + - name: Install backend dependencies + if: steps.changes.outputs.backend == 'true' || steps.changes.outputs.frontend == 'true' + working-directory: backend + run: uv sync --frozen + + - name: Test backend + if: steps.changes.outputs.backend == 'true' + working-directory: backend + run: uv run --env-file ../infra/.env.example pytest + + - name: Export OpenAPI contract + if: steps.changes.outputs.backend == 'true' || steps.changes.outputs.frontend == 'true' + working-directory: backend + run: uv run python scripts/export_openapi.py --output "${{ runner.temp }}/openapi.json" + + - name: Enable pnpm + if: steps.changes.outputs.backend == 'true' || steps.changes.outputs.frontend == 'true' + run: corepack enable + + - name: Set up Node.js for checks + if: steps.changes.outputs.backend == 'true' || steps.changes.outputs.frontend == 'true' + uses: actions/setup-node@v4 + with: + node-version: '22' + cache: pnpm + cache-dependency-path: web/pnpm-lock.yaml + + - name: Install web dependencies + if: steps.changes.outputs.backend == 'true' || steps.changes.outputs.frontend == 'true' + working-directory: web + run: pnpm install --frozen-lockfile + + - name: Check generated API types + if: steps.changes.outputs.backend == 'true' || steps.changes.outputs.frontend == 'true' + working-directory: web + env: + OPENAPI_URL: ${{ runner.temp }}/openapi.json + run: pnpm api:check + + - name: Validate frontend + if: steps.changes.outputs.frontend == 'true' + working-directory: web + run: | + pnpm format:check + pnpm lint + pnpm test + pnpm build + - name: Log in to Docker Hub uses: docker/login-action@v3 with: @@ -120,22 +191,9 @@ jobs: cache-to: type=gha,mode=max,scope=nuspace-backup # --- Frontend static export (Vite SPA) — built on the runner, not on the VM --- - - name: Set up Node.js for frontend build - if: steps.changes.outputs.frontend == 'true' - uses: actions/setup-node@v4 - with: - node-version: '20' - cache: npm - cache-dependency-path: frontend/package-lock.json - - - name: Build frontend static output - if: steps.changes.outputs.frontend == 'true' - working-directory: frontend - run: npm ci && npm run build - - name: Pack frontend out/ for Ansible if: steps.changes.outputs.frontend == 'true' - run: tar czf "${{ runner.temp }}/frontend-static.tar.gz" -C frontend/out . + run: tar czf "${{ runner.temp }}/frontend-static.tar.gz" -C web/out . - name: Frontend tarball path for Ansible id: fe_artifact diff --git a/.github/workflows/quality.yml b/.github/workflows/quality.yml new file mode 100644 index 00000000..17d6a0d0 --- /dev/null +++ b/.github/workflows/quality.yml @@ -0,0 +1,107 @@ +name: Quality + +on: + pull_request: + workflow_dispatch: + +jobs: + backend: + runs-on: ubuntu-latest + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Install PostgreSQL build tools + run: sudo apt-get update && sudo apt-get install -y libpq-dev + + - name: Set up Python + uses: actions/setup-python@v5 + with: + python-version: "3.12" + + - name: Set up uv + uses: astral-sh/setup-uv@v6 + with: + enable-cache: true + cache-dependency-glob: backend/uv.lock + + - name: Install backend dependencies + working-directory: backend + run: uv sync --frozen + + - name: Run backend tests + working-directory: backend + run: uv run --env-file ../infra/.env.example pytest + + - name: Export OpenAPI + working-directory: backend + run: uv run python scripts/export_openapi.py --output "${{ runner.temp }}/openapi.json" + + - name: Upload OpenAPI contract + uses: actions/upload-artifact@v4 + with: + name: openapi-contract + path: ${{ runner.temp }}/openapi.json + + web: + runs-on: ubuntu-latest + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Enable pnpm + run: corepack enable + + - name: Set up Node.js + uses: actions/setup-node@v4 + with: + node-version: "22" + cache: pnpm + cache-dependency-path: web/pnpm-lock.yaml + + - name: Install web dependencies + working-directory: web + run: pnpm install --frozen-lockfile + + - name: Check formatting + working-directory: web + run: pnpm format:check + + - name: Lint web + working-directory: web + run: pnpm lint + + - name: Test web + working-directory: web + run: pnpm test + + - name: Build web + working-directory: web + run: pnpm build + + contract: + needs: [backend, web] + runs-on: ubuntu-latest + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Download OpenAPI contract + uses: actions/download-artifact@v4 + with: + name: openapi-contract + path: openapi + + - name: Enable pnpm + run: corepack enable + + - name: Set up Node.js + uses: actions/setup-node@v4 + with: + node-version: "22" + + - name: Check generated API types + working-directory: web + env: + OPENAPI_URL: ${{ github.workspace }}/openapi/openapi.json + run: pnpm api:check diff --git a/AGENTS.md b/AGENTS.md index ee051abd..e81992ae 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -4,7 +4,9 @@ ``` backend/ Python 3.12 / FastAPI app (package name: nuros) -frontend/ React 19 / Vite 7 / TypeScript SPA +web/ React 19 / Vite 8 / TypeScript SPA — the app that ships +frontend/ The previous SPA. Kept one release as a fallback; not built, + not routed to, not deployed. Work in web/. infra/ Docker Compose, Nginx, monitoring, service configs terraform/ GCP infrastructure-as-code ansible/ Deployment playbooks (runs from CI, not locally) @@ -39,15 +41,27 @@ uv run pytest # tests Pre-commit hooks run `ruff --fix` then `black` automatically. -### Frontend (`frontend/`) +### Frontend (`web/`) + +pnpm, not npm. The lockfile is `web/pnpm-lock.yaml` and CI installs with +`--frozen-lockfile`, so a dependency added with npm will fail the build. ```sh -npm ci && npm run build # production build → out/ -npm run dev # dev server on :5173 -npm run test:url-validation # run a single test file via tsx +pnpm install # deps +pnpm build # typecheck + production build → out/ +pnpm dev # dev server on :5173 +pnpm typecheck # tsc -b --noEmit +pnpm lint # oxlint, including type-aware rules +pnpm format # prettier, sorts Tailwind classes +pnpm test # node --test over src/**/*.test.ts +pnpm api:check # fail if src/api/schema.d.ts is stale ``` -TypeScript strict mode. `@` alias maps to `src/`. +`out/`, not Vite's default `dist/` — the deployment path expects it (see +`web/vite.config.ts`). + +TypeScript strict mode. `@` alias maps to `src/`. Read `web/README.md` before +changing anything there; several of its conventions are load-bearing. ## Backend Architecture diff --git a/ansible/roles/frontend/tasks/main.yml b/ansible/roles/frontend/tasks/main.yml index 1d3f71b1..476600ea 100644 --- a/ansible/roles/frontend/tasks/main.yml +++ b/ansible/roles/frontend/tasks/main.yml @@ -1,10 +1,14 @@ --- # Static files are built in GitHub Actions and shipped as a tarball on the controller. # `frontend_tarball_path` is set by CI when frontend paths changed; empty skips deploy. +# +# The path is web/out, not frontend/out: the app that ships is the rebuild in +# web/. The frontend/ tree is still in the repository for one release, but +# nothing unpacks into it and nginx no longer serves it. - name: Ensure frontend out directory exists with proper permissions ansible.builtin.file: - path: "/home/{{ ansible_user }}/nuspace/frontend/out" + path: "/home/{{ ansible_user }}/nuspace/web/out" state: directory owner: "{{ ansible_user }}" group: "{{ ansible_user }}" @@ -14,7 +18,7 @@ - name: Clear previous static export ansible.builtin.shell: | set -e - rm -rf /home/{{ ansible_user }}/nuspace/frontend/out/* + rm -rf /home/{{ ansible_user }}/nuspace/web/out/* args: executable: /bin/bash when: frontend_tarball_path | default('') | length > 0 @@ -22,7 +26,7 @@ - name: Unpack frontend build from CI runner to VM ansible.builtin.unarchive: src: "{{ frontend_tarball_path }}" - dest: "/home/{{ ansible_user }}/nuspace/frontend/out" + dest: "/home/{{ ansible_user }}/nuspace/web/out" remote_src: false owner: "{{ ansible_user }}" group: "{{ ansible_user }}" @@ -30,7 +34,7 @@ - name: Verify out files were created ansible.builtin.stat: - path: "/home/{{ ansible_user }}/nuspace/frontend/out/index.html" + path: "/home/{{ ansible_user }}/nuspace/web/out/index.html" register: dist_check when: frontend_tarball_path | default('') | length > 0 @@ -43,7 +47,7 @@ - name: Show frontend deploy success message ansible.builtin.debug: - msg: "Frontend static files from CI are deployed under frontend/out." + msg: "Frontend static files from CI are deployed under web/out." when: frontend_tarball_path | default('') | length > 0 - name: Recreate nginx service to pick up new out files diff --git a/backend/common/utils/meilisearch.py b/backend/common/utils/meilisearch.py index 0e41b489..0a1e6fe9 100644 --- a/backend/common/utils/meilisearch.py +++ b/backend/common/utils/meilisearch.py @@ -2,6 +2,7 @@ from typing import Type from backend.core.database.manager import AsyncDatabaseManager +from fastapi import HTTPException from httpx import AsyncClient from sqlalchemy import select from sqlalchemy.orm import DeclarativeBase @@ -81,6 +82,24 @@ async def get( payload["filter"] = filters response = await client.post(f"/indexes/{storage_name}/search", json=payload) + + # An error response has no "hits", and every caller reads result["hits"] + # directly -- so without this the failure surfaces as an opaque + # `KeyError: 'hits'` and a 500 with no indication of what went wrong. + # + # This is not a rare edge: index setup deletes and recreates every index on + # startup, so there is a window after each deploy where searching an index + # that does not exist yet 500s. + if response.status_code >= 400: + body = response.json() + raise HTTPException( + status_code=503, + detail=( + f"Search index '{storage_name}' is unavailable " + f"({body.get('code', response.status_code)})." + ), + ) + return response.json() diff --git a/backend/core/configs/config.py b/backend/core/configs/config.py index 83087e80..cc140a8d 100644 --- a/backend/core/configs/config.py +++ b/backend/core/configs/config.py @@ -44,6 +44,15 @@ class Config(BaseSettings): GCP_SIGNING_SERVICE_ACCOUNT_KEY_JSON: str | None = None ORIGINS: List[str] = ["*"] MOCK_KEYCLOAK: bool # always set True in local dev + # Your own registrar username, for testing the registrar sync locally. + # + # Mock users have no real NU email, so the username cannot be derived from + # the session the way it is in production. This used to be a colleague's + # username written into the source, which meant every developer's local sync + # attempt was a login attempt against that person's real registrar account -- + # and enough wrong passwords locks them out. Set your own here, or leave it + # unset and the sync will simply fail to authenticate. + REGISTRAR_DEBUG_USERNAME: str | None = None USE_GCS_EMULATOR: bool # keep True for local dev; For staging/prod .env will have it False GCS_EMULATOR_HOST: str INTEGRATION_SECRET: str diff --git a/backend/fixtures/dev/seed_campus.sql b/backend/fixtures/dev/seed_campus.sql new file mode 100644 index 00000000..7038164d --- /dev/null +++ b/backend/fixtures/dev/seed_campus.sql @@ -0,0 +1,435 @@ +-- Realistic Campus Current and SGotinish data for local dev. +-- +-- Without this, a fresh database has one event, one community and two users, so +-- most screens render an empty state and the ones that do not are exercised +-- against a single row. Every filter, every grouping and every permission +-- branch then looks like it works because there is nothing for it to get wrong. +-- +-- What this covers, and why each case is here: +-- +-- * events — past, in progress, today and weeks out, so the time +-- filters and the "Happening now" badge have something to +-- separate; open and registration policies; a featured tag. +-- * communities — every type and several categories, one verified, one +-- headed by a non-admin so `can_edit` differs by viewer. +-- * opportunities — expired and open, year-round (`deadline IS NULL`), one +-- restricted by major, and eligibilities covering the +-- three level shapes: specific years, a whole level, PhD. +-- * SG membership — Heads, Executives and Members across regular and special +-- departments, so the roster grouping is non-trivial and +-- the capo permission rules can actually be violated. +-- * tickets — one per status, one anonymous with a known key, and one +-- with a delegated access list. +-- +-- Announcements need no seeding of their own: `/announcements/bundle` is built +-- from upcoming events and recruiting communities, both of which are below. +-- +-- Run against the local stack: +-- docker compose exec -T postgres psql -U postgres -d postgres \ +-- < backend/fixtures/dev/seed_campus.sql +-- +-- Then restart the API — Meilisearch indexes from the database at startup, so +-- anything inserted afterwards is invisible to search until it does: +-- docker compose restart fastapi +-- +-- Signing in as each of these people (MOCK_KEYCLOAK=true): +-- http://localhost/api/login?mock_user=alice admin +-- http://localhost/api/login?mock_user=bob ordinary student +-- http://localhost/api/login?mock_user=charlie Head (boss) +-- http://localhost/api/login?mock_user=dana Executive (capo) +-- http://localhost/api/login?mock_user=erik Member (soldier) +-- +-- Re-runnable. Rows are matched on the natural key a person would recognise — +-- a community's name, an event's name, a ticket's title — because none of these +-- tables has a unique constraint to hang `ON CONFLICT` on. A bare +-- `ON CONFLICT DO NOTHING` looks like it deduplicates and does nothing at all +-- without a constraint to conflict against; the second run of an earlier draft +-- of this file produced a clean duplicate of every event, ticket, community and +-- opportunity. Nothing here deletes anything you created by hand. + +BEGIN; + +-- --------------------------------------------------------------------------- +-- People +-- --------------------------------------------------------------------------- +-- alice and bob already exist from the mock provider; charlie is defined there +-- too but only appears once he has signed in. Inserting all five means the +-- roster is populated before anyone logs in, and the FK targets for the +-- communities and tickets below exist. + +INSERT INTO users (sub, email, role, scope, name, surname, picture, created_at, updated_at) VALUES + ('mock-sub-alice', 'alice@example.com', 'admin', 'allowed', 'Alice', 'Anderson', 'https://i.pravatar.cc/150?img=3', now(), now()), + ('mock-sub-bob', 'bob@example.com', 'default', 'allowed', 'Bob', 'Brown', 'https://i.pravatar.cc/150?img=4', now(), now()), + ('mock-sub-charlie', 'charlie@example.com', 'boss', 'allowed', 'Charlie', 'Clark', 'https://i.pravatar.cc/150?img=5', now(), now()), + ('mock-sub-dana', 'dana@example.com', 'capo', 'allowed', 'Dana', 'Dosanova', 'https://i.pravatar.cc/150?img=6', now(), now()), + ('mock-sub-erik', 'erik@example.com', 'soldier', 'allowed', 'Erik', 'Ermekov', 'https://i.pravatar.cc/150?img=7', now(), now()), + ('mock-sub-fatima', 'fatima@example.com', 'soldier', 'allowed', 'Fatima', 'Fazylova', 'https://i.pravatar.cc/150?img=8', now(), now()), + ('mock-sub-gulnar', 'gulnar@example.com', 'capo', 'allowed', 'Gulnar', 'Galieva', 'https://i.pravatar.cc/150?img=9', now(), now()), + ('mock-sub-hassan', 'hassan@example.com', 'default', 'allowed', 'Hassan', 'Hakim', 'https://i.pravatar.cc/150?img=10', now(), now()) +ON CONFLICT (sub) DO UPDATE SET + role = EXCLUDED.role, + name = EXCLUDED.name, + surname = EXCLUDED.surname, + updated_at = now(); + +-- --------------------------------------------------------------------------- +-- Departments +-- --------------------------------------------------------------------------- +-- Id 9 is the root Student Government department. The backend hardcodes it — +-- `DelegationPolicy.check_department_deletable` refuses to delete id 9 by +-- number — so a local database has to reproduce that id for the protected-row +-- behaviour to be testable at all. Explicit ids here, not a sequence. + +INSERT INTO departments (id, name, is_special) VALUES + (9, 'Student Government', false), + (10, 'Ministry of Academics', false), + (11, 'Ministry of Sport', false), + (12, 'Ministry of Media', false), + (13, 'Ethics Commission', true), + (14, 'Election Committee', true) +ON CONFLICT (id) DO UPDATE SET + name = EXCLUDED.name, + is_special = EXCLUDED.is_special; + +-- Keep the sequence ahead of the explicit ids, or the first department created +-- through the UI collides with one of them. +SELECT setval( + pg_get_serial_sequence('departments', 'id'), + GREATEST((SELECT MAX(id) FROM departments), 1) +); + +UPDATE users SET department_id = 9, sg_assigned_at = now() - interval '200 days' WHERE sub = 'mock-sub-charlie'; +UPDATE users SET department_id = 10, sg_assigned_at = now() - interval '150 days', sg_assigned_by_sub = 'mock-sub-charlie' WHERE sub = 'mock-sub-dana'; +UPDATE users SET department_id = 10, sg_assigned_at = now() - interval '100 days', sg_assigned_by_sub = 'mock-sub-dana' WHERE sub = 'mock-sub-erik'; +-- Fatima sits in a different department from Dana on purpose: a capo must not +-- be able to remove or delegate to her, and that is only visible with someone +-- outside their own department in the roster. +UPDATE users SET department_id = 11, sg_assigned_at = now() - interval '90 days', sg_assigned_by_sub = 'mock-sub-charlie' WHERE sub = 'mock-sub-fatima'; +UPDATE users SET department_id = 13, sg_assigned_at = now() - interval '80 days', sg_assigned_by_sub = 'mock-sub-charlie' WHERE sub = 'mock-sub-gulnar'; + +-- --------------------------------------------------------------------------- +-- Communities +-- --------------------------------------------------------------------------- + +INSERT INTO communities (name, type, category, email, verified, description, established, head, telegram_url, instagram_url, created_at, updated_at) +SELECT * FROM (VALUES + ('NU Fencing Club', 'club', 'sports', 'fencing@nu.edu.kz', true, + E'Foil, épée and sabre, three evenings a week in the Sports Complex.\n\nBeginners welcome — we lend kit for the first term. **No experience needed.**', + '2019-09-01', 'mock-sub-bob', 'https://t.me/nufencing', 'https://instagram.com/nufencing', now(), now()), + + ('Debate Society', 'club', 'academic', 'debate@nu.edu.kz', true, + 'British Parliamentary format, weekly rounds, and the training squad for regional tournaments.', + '2015-02-15', 'mock-sub-charlie', 'https://t.me/nudebate', NULL, now(), now()), + + ('Robotics and Mechatronics Association', 'organization', 'professional', 'rma@nu.edu.kz', false, + 'Builds the university competition robots. Recruiting mechanical, electronics and software people every September.', + '2021-09-10', 'mock-sub-dana', 'https://t.me/nurobotics', 'https://instagram.com/nurobotics', now(), now()), + + ('Student Theatre', 'club', 'art', NULL, false, + 'Two productions a year, in English and Kazakh. No audition required to join the crew.', + '2017-10-05', 'mock-sub-erik', NULL, 'https://instagram.com/nutheatre', now(), now()), + + ('Office of Student Affairs', 'university', 'social', 'osa@nu.edu.kz', true, + 'Official university office. Runs orientation, student support and the campus events calendar.', + '2010-01-01', 'mock-sub-alice', NULL, NULL, now(), now()), + + ('Kazakh Culture Club', 'club', 'cultural', 'kcc@nu.edu.kz', false, + 'Language evenings, Nauryz, and dombra lessons for anyone who wants them.', + '2018-03-21', 'mock-sub-fatima', 'https://t.me/nukcc', NULL, now(), now()), + + ('Hiking and Outdoors', 'club', 'recreational', NULL, false, + 'Weekend trips to Burabay and the Ile-Alatau. Transport shared, no membership fee.', + '2022-04-12', 'mock-sub-gulnar', 'https://t.me/nuhiking', NULL, now(), now()) +) AS seed(name, type, category, email, verified, description, established, head, telegram_url, instagram_url, created_at, updated_at) +WHERE NOT EXISTS (SELECT 1 FROM communities c WHERE c.name = seed.name); + +-- --------------------------------------------------------------------------- +-- Events +-- --------------------------------------------------------------------------- +-- Times are relative to now() so the time filters keep working next month — +-- fixed dates in a seed script quietly become "all past" and the upcoming list +-- reads as empty. + +INSERT INTO events (creator_sub, policy, registration_link, name, place, start_datetime, end_datetime, description, type, status, tag, created_at, updated_at) +SELECT * FROM (VALUES + ('mock-sub-charlie', 'open', NULL, + 'Open Mic Night', 'Block 8, Atrium', + now() + interval '2 hours', now() + interval '5 hours', + E'Bring an instrument, a poem or nothing at all.\n\n- Sign-up sheet at the door\n- Free tea', + 'art', 'approved', 'regular', now(), now()), + + ('mock-sub-bob', 'registration', 'https://forms.gle/example-fencing', + 'Fencing Taster Session', 'Sports Complex, Hall 2', + now() + interval '3 days', now() + interval '3 days 2 hours', + 'Kit provided. Wear trainers and something you can move in. Places are limited to 24.', + 'sports', 'approved', 'regular', now(), now()), + + ('mock-sub-alice', 'open', NULL, + 'Career Fair 2026', 'Block 7, Main Hall', + now() + interval '11 days', now() + interval '11 days 8 hours', + E'Forty employers across engineering, data and life sciences.\n\n## What to bring\n\n1. Printed CV\n2. Student ID', + 'recruitment', 'approved', 'featured', now(), now()), + + ('mock-sub-dana', 'registration', 'https://forms.gle/example-hackathon', + 'NU Hackathon: 36 Hours', 'SCAI, Floor 3', + now() + interval '25 days', now() + interval '26 days 12 hours', + 'Teams of up to four. Hardware track and a software track, with mentors from the Robotics Association.', + 'academic', 'approved', 'promotional', now(), now()), + + ('mock-sub-erik', 'open', NULL, + 'Winter Charity Bazaar', 'Block 1, Foyer', + now() + interval '40 days', now() + interval '40 days 6 hours', + 'All proceeds to the Astana children''s hospital. Stalls are free for student societies.', + 'social', 'approved', 'charity', now(), now()), + + -- Already finished: the detail page should show "Finished" and hide the + -- registration button even though a link is set. + ('mock-sub-charlie', 'registration', 'https://forms.gle/example-past', + 'Autumn Debate Open', 'Block 6, Room 6.201', + now() - interval '14 days', now() - interval '14 days' + interval '9 hours', + 'Eight rounds, British Parliamentary. Results are on the Debate Society channel.', + 'academic', 'approved', 'regular', now(), now()), + + ('mock-sub-gulnar', 'open', NULL, + 'Sunrise Hike: Burabay', 'Meet at Block 22 car park', + now() + interval '6 days', now() + interval '6 days 14 hours', + 'Leaving at 05:00. Bring water, layers and boots — this is not a stroll.', + 'recreational', 'approved', 'regular', now(), now()), + + -- Not approved: must not appear in the student-facing list at all. + ('mock-sub-hassan', 'open', NULL, + 'Unapproved Test Event', 'Nowhere', + now() + interval '9 days', now() + interval '9 days 1 hour', + 'Exists so the status filter has something it is supposed to be hiding.', + 'social', 'pending', 'regular', now(), now()) +) AS seed(creator_sub, policy, registration_link, name, place, start_datetime, end_datetime, description, type, status, tag, created_at, updated_at) +WHERE NOT EXISTS (SELECT 1 FROM events e WHERE e.name = seed.name); + +-- --------------------------------------------------------------------------- +-- Opportunities +-- --------------------------------------------------------------------------- +-- The enum labels here are the Python member *names* (RESEARCH, GRM), not the +-- values the API speaks (research, GrM) — SQLAlchemy stores names by default, +-- and using the API spelling fails with an invalid-enum error. + +INSERT INTO opportunities (name, description, deadline, host, type, link, location, funding, created_at, updated_at) +SELECT * FROM (VALUES + ('Nazarbayev University Research Assistantship', + E'Paid research assistant positions across all schools for the spring semester.\n\nSupervisors post projects in February; applications go through your school office.', + CURRENT_DATE + 21, 'NU Office of the Provost', 'RESEARCH', + 'https://nu.edu.kz/research', 'Astana', 'Stipend, 120,000 KZT/month', now(), now()), + + ('DAAD Summer School in Germany', + 'Four weeks at a German university, language course included. Covers travel, accommodation and a monthly allowance.', + CURRENT_DATE + 45, 'DAAD', 'SUMMER_SCHOOL', + 'https://daad.de', 'Germany', 'Fully funded', now(), now()), + + ('Google Summer of Code', + 'Work on an open-source project with a mentoring organisation over the summer. Open to any student who can write code.', + CURRENT_DATE + 60, 'Google', 'INTERNSHIP', + 'https://summerofcode.withgoogle.com', 'Remote', 'Stipend by country tier', now(), now()), + + -- Year-round: `deadline IS NULL` is the shape the "year-round" toggle + -- produces, and the card must not render a closing date for it. + ('NU Alumni Mentorship Programme', + 'Matched with an alumnus in your field for a semester. Applications are read as they arrive.', + NULL, 'NU Alumni Association', 'FORUM', + 'https://nu.edu.kz/alumni', 'Astana / Remote', NULL, now(), now()), + + -- Already closed: `hide_expired=true` is the default on the list, so this one + -- proves the filter is doing something. + ('Bolashak Scholarship — 2025 intake', + 'Graduate study abroad, fully funded, with a return-service commitment.', + CURRENT_DATE - 30, 'Centre for International Programmes', 'SCHOLARSHIP', + 'https://bolashak.gov.kz', 'Abroad', 'Fully funded', now(), now()), + + ('Astana Hub Startup Grant', + 'Seed funding for student teams with a working prototype. Two rounds a year.', + CURRENT_DATE + 90, 'Astana Hub', 'GRANT', + 'https://astanahub.com', 'Astana', 'Up to 20,000 USD', now(), now()) +) AS seed(name, description, deadline, host, type, link, location, funding, created_at, updated_at) +WHERE NOT EXISTS (SELECT 1 FROM opportunities o WHERE o.name = seed.name); + +-- Eligibility, covering the three shapes the form can produce. +INSERT INTO opportunity_eligibility (opportunity_id, education_level, year) +SELECT o.id, level, year FROM opportunities o +CROSS JOIN LATERAL (VALUES ('UG'::education_level, 3), ('UG', 4), ('GRM', NULL)) + AS e(level, year) +WHERE o.name = 'Nazarbayev University Research Assistantship' +ON CONFLICT DO NOTHING; + +-- A whole level with no years: one row with year NULL, which is what "the +-- entire level is eligible" means. Zero rows would mean nobody is. +INSERT INTO opportunity_eligibility (opportunity_id, education_level, year) +SELECT id, 'UG', NULL FROM opportunities WHERE name = 'DAAD Summer School in Germany' +ON CONFLICT DO NOTHING; + +INSERT INTO opportunity_eligibility (opportunity_id, education_level, year) +SELECT o.id, level, year FROM opportunities o +CROSS JOIN LATERAL (VALUES ('UG'::education_level, 2), ('UG', 3), ('UG', 4), ('GRM', 1)) + AS e(level, year) +WHERE o.name = 'Google Summer of Code' +ON CONFLICT DO NOTHING; + +-- PhD always carries year NULL — there is no year to pick. +INSERT INTO opportunity_eligibility (opportunity_id, education_level, year) +SELECT o.id, level, NULL FROM opportunities o +CROSS JOIN LATERAL (VALUES ('GRM'::education_level), ('PHD')) AS e(level) +WHERE o.name = 'NU Alumni Mentorship Programme' +ON CONFLICT DO NOTHING; + +INSERT INTO opportunity_eligibility (opportunity_id, education_level, year) +SELECT id, 'PHD', NULL FROM opportunities WHERE name = 'Bolashak Scholarship — 2025 intake' +ON CONFLICT DO NOTHING; + +INSERT INTO opportunity_eligibility (opportunity_id, education_level, year) +SELECT id, 'UG', NULL FROM opportunities WHERE name = 'Astana Hub Startup Grant' +ON CONFLICT DO NOTHING; + +-- Majors: one restricted opportunity, one left open to everybody. As with the +-- types above, these are Python member names — `COMPUTER_SCIENCE`, not the +-- "Computer Science" the API sends. +INSERT INTO opportunity_majors (opportunity_id, major) +SELECT o.id, major FROM opportunities o +CROSS JOIN LATERAL (VALUES + ('COMPUTER_SCIENCE'::opportunity_major), + ('DATA_SCIENCE'), + ('ELECTRICAL_AND_COMPUTER_ENGINEERING'), + ('ROBOTICS_AND_MECHATRONICS_ENGINEERING') +) AS m(major) +WHERE o.name = 'Google Summer of Code' +ON CONFLICT DO NOTHING; + +INSERT INTO opportunity_majors (opportunity_id, major) +SELECT o.id, major FROM opportunities o +CROSS JOIN LATERAL (VALUES ('PHYSICS'::opportunity_major), ('CHEMISTRY'), ('BIOLOGICAL_SCIENCES')) + AS m(major) +WHERE o.name = 'Nazarbayev University Research Assistantship' +ON CONFLICT DO NOTHING; + +-- --------------------------------------------------------------------------- +-- SGotinish +-- --------------------------------------------------------------------------- + +INSERT INTO tickets (author_sub, category, title, body, status, is_anonymous, owner_hash, created_at, updated_at) +SELECT * FROM (VALUES + ('mock-sub-bob', 'academic', + 'Library closes too early during finals', + 'The 24-hour reading room stops being 24-hour in week 14, which is exactly when it is needed. Could the hours be extended through the exam period?', + 'open', false, NULL, now() - interval '3 days', now() - interval '3 days'), + + ('mock-sub-hassan', 'technical', + 'Wi-fi drops in Block 22 every evening', + 'Between roughly 21:00 and midnight the connection in the west wing becomes unusable. It has been like this since September.', + 'in_progress', false, NULL, now() - interval '9 days', now() - interval '2 days'), + + ('mock-sub-bob', 'suggestion', + 'Add a bike rack outside Block 7', + 'There is nowhere to lock a bike near the main entrance, so people chain them to the railings.', + 'resolved', false, NULL, now() - interval '30 days', now() - interval '20 days'), + + ('mock-sub-hassan', 'administrative', + 'Transcript request took five weeks', + 'Requested on 12 March, received on 16 April, with no way to check progress in between.', + 'closed', false, NULL, now() - interval '60 days', now() - interval '35 days'), + + -- Anonymous. `author_sub` is NULL by design: the owner hash is the only thing + -- that identifies the author, and the key that produces it is + -- demo-anonymous-warpkey-for-local-dev + -- so this ticket is reachable at + -- http://localhost:6767/t#key=demo-anonymous-warpkey-for-local-dev + -- Do not put a real key in a fixture; this one exists to be public. + (NULL, 'complaint', + 'Grading in a core course feels inconsistent', + 'Two students submitted near-identical work and received grades a full letter apart. Raising it with the instructor did not help.', + 'in_progress', true, 'b7e8ba0e5b9ebf78ea3c78426a9b419a2e42e00698f4ee8b364cf27bd87dd07e', + now() - interval '5 days', now() - interval '1 day') +) AS seed(author_sub, category, title, body, status, is_anonymous, owner_hash, created_at, updated_at) +WHERE NOT EXISTS (SELECT 1 FROM tickets t WHERE t.title = seed.title); + +INSERT INTO conversations (ticket_id, sg_member_sub, status, created_at) +SELECT t.id, 'mock-sub-dana', 'active', t.created_at + interval '4 hours' +FROM tickets t +WHERE t.title = 'Wi-fi drops in Block 22 every evening' + AND NOT EXISTS (SELECT 1 FROM conversations c WHERE c.ticket_id = t.id); + +INSERT INTO conversations (ticket_id, sg_member_sub, status, created_at) +SELECT t.id, 'mock-sub-charlie', 'active', t.created_at + interval '2 hours' +FROM tickets t +WHERE t.title = 'Grading in a core course feels inconsistent' + AND NOT EXISTS (SELECT 1 FROM conversations c WHERE c.ticket_id = t.id); + +INSERT INTO messages (conversation_id, sender_sub, body, is_from_sg_member, sent_at) +SELECT c.id, 'mock-sub-dana', + 'Thanks for reporting this — we have passed it to IT and asked for the access point logs for the west wing.', + true, c.created_at + interval '20 minutes' +FROM conversations c JOIN tickets t ON t.id = c.ticket_id +WHERE t.title = 'Wi-fi drops in Block 22 every evening' + AND NOT EXISTS ( + SELECT 1 FROM messages m WHERE m.conversation_id = c.id AND m.body = 'Thanks for reporting this — we have passed it to IT and asked for the access point logs for the west wing.' + ); + +INSERT INTO messages (conversation_id, sender_sub, body, is_from_sg_member, sent_at) +SELECT c.id, 'mock-sub-hassan', + 'It happened again last night, around 22:30. Two other people on my floor see the same thing.', + false, c.created_at + interval '1 day' +FROM conversations c JOIN tickets t ON t.id = c.ticket_id +WHERE t.title = 'Wi-fi drops in Block 22 every evening' + AND NOT EXISTS ( + SELECT 1 FROM messages m WHERE m.conversation_id = c.id AND m.body = 'It happened again last night, around 22:30. Two other people on my floor see the same thing.' + ); + +-- The anonymous author's own message carries no sender_sub. That is the whole +-- point of the anonymous flow, and it is why the conversation view has to cope +-- with a message that has no sender. +INSERT INTO messages (conversation_id, sender_sub, body, is_from_sg_member, sent_at) +SELECT c.id, NULL, + 'I would rather not name the course here. Is there a way to raise it without the instructor knowing who asked?', + false, c.created_at + interval '10 minutes' +FROM conversations c JOIN tickets t ON t.id = c.ticket_id +WHERE t.title = 'Grading in a core course feels inconsistent' + AND NOT EXISTS ( + SELECT 1 FROM messages m WHERE m.conversation_id = c.id AND m.body = 'I would rather not name the course here. Is there a way to raise it without the instructor knowing who asked?' + ); + +INSERT INTO messages (conversation_id, sender_sub, body, is_from_sg_member, sent_at) +SELECT c.id, 'mock-sub-charlie', + 'Yes. We can raise it through the Ethics Commission without attributing it to you. Nothing you have written here is linked to your account.', + true, c.created_at + interval '3 hours' +FROM conversations c JOIN tickets t ON t.id = c.ticket_id +WHERE t.title = 'Grading in a core course feels inconsistent' + AND NOT EXISTS ( + SELECT 1 FROM messages m WHERE m.conversation_id = c.id AND m.body = 'Yes. We can raise it through the Ethics Commission without attributing it to you. Nothing you have written here is linked to your account.' + ); + +-- Every Head gets DELEGATE on every ticket, which is what +-- `TicketService.create_ticket` does for real tickets: it grants access to all +-- bosses and notifies them. Inserting rows directly skips that, and without +-- these the SG inbox is empty for a Head — the list is filtered to tickets you +-- authored or have explicit access to, so a boss with no access rows sees +-- nothing and it looks like the inbox is broken. +INSERT INTO ticket_access (ticket_id, user_sub, permission, granted_by_sub, granted_at) +SELECT t.id, u.sub, 'DELEGATE', NULL, t.created_at +FROM tickets t CROSS JOIN users u +WHERE u.role = 'boss' +ON CONFLICT DO NOTHING; + +-- A delegated access list, so the ticket page has something to show and the +-- "who can see this" question has a non-empty answer. Enum labels again are +-- Python member names: VIEW / ASSIGN / DELEGATE. +INSERT INTO ticket_access (ticket_id, user_sub, permission, granted_by_sub, granted_at) +SELECT t.id, 'mock-sub-dana', 'ASSIGN', 'mock-sub-charlie', now() - interval '4 days' +FROM tickets t WHERE t.title = 'Grading in a core course feels inconsistent' +ON CONFLICT DO NOTHING; + +INSERT INTO ticket_access (ticket_id, user_sub, permission, granted_by_sub, granted_at) +SELECT t.id, 'mock-sub-erik', 'VIEW', 'mock-sub-dana', now() - interval '3 days' +FROM tickets t WHERE t.title = 'Grading in a core course feels inconsistent' +ON CONFLICT DO NOTHING; + +INSERT INTO ticket_access (ticket_id, user_sub, permission, granted_by_sub, granted_at) +SELECT t.id, 'mock-sub-dana', 'DELEGATE', 'mock-sub-charlie', now() - interval '8 days' +FROM tickets t WHERE t.title = 'Wi-fi drops in Block 22 every evening' +ON CONFLICT DO NOTHING; + +COMMIT; diff --git a/backend/fixtures/dev/seed_registered_courses.sql b/backend/fixtures/dev/seed_registered_courses.sql new file mode 100644 index 00000000..2cc72aa4 --- /dev/null +++ b/backend/fixtures/dev/seed_registered_courses.sql @@ -0,0 +1,85 @@ +-- Synthetic registered courses for local dev. +-- +-- Real ones are pulled from the registrar per student, which needs credentials +-- no local environment has — so without this every Courses screen renders its +-- empty state and the live GPA calculator can't be exercised at all. +-- +-- Shaped like week 8 of a term, deliberately covering the cases the GPA +-- arithmetic distinguishes: a course doing well with most of the term still +-- ahead, one where banked and so-far diverge hard, one with nothing graded yet +-- (must be skipped, not counted as zero), one fully graded, and one with no +-- items at all. +-- +-- Run against the local stack: +-- docker compose exec -T postgres psql -U postgres -d postgres \ +-- < backend/fixtures/dev/seed_registered_courses.sql +-- +-- Assumes the mock user `mock-sub-alice` exists (MOCK_KEYCLOAK=true, then +-- hit /api/login?mock_user=1). +BEGIN; + +INSERT INTO courses (registrar_id, course_code, level, school, title, credits, term, created_at, updated_at) VALUES + (100151, 'CSCI 151', 'UG', 'SCAI', 'Programming for Scientists and Engineers', 4, 'Fall 2026', now(), now()), + (100231, 'CSCI 231', 'UG', 'SCAI', 'Computer Systems and Organization', 3, 'Fall 2026', now(), now()), + (100241, 'MATH 241', 'UG', 'SSH', 'Linear Algebra with Applications', 3, 'Fall 2026', now(), now()), + (100161, 'PHYS 161', 'UG', 'SSH', 'Physics I', 4, 'Fall 2026', now(), now()), + (100101, 'WCS 101', 'UG', 'SSH', 'Writing and Communication I', 3, 'Fall 2026', now(), now()); + +INSERT INTO student_courses (student_sub, course_id, created_at, updated_at) +SELECT 'mock-sub-alice', id, now(), now() FROM courses; + +-- CSCI 151: doing well, most of the term still ahead. +INSERT INTO course_items (student_course_id, item_name, total_weight_pct, max_score, obtained_score, created_at, updated_at) +SELECT sc.id, v.name, v.w, v.max, v.got, now(), now() +FROM student_courses sc +JOIN courses c ON c.id = sc.course_id AND c.course_code = 'CSCI 151' +CROSS JOIN (VALUES + ('Lab 1', 5.0, 100.0, 98.0), + ('Lab 2', 5.0, 100.0, 92.0), + ('Lab 3', 5.0, 100.0, 88.0), + ('Homework 1', 10.0, 50.0, 47.5), + ('Homework 2', 10.0, 50.0, 41.0), + ('Midterm', 25.0, 100.0, 84.0), + ('Project', 15.0, 100.0, NULL), + ('Final', 25.0, 100.0, NULL) +) AS v(name, w, max, got); + +-- CSCI 231: a rough midterm — the case where "banked" and "so far" diverge. +INSERT INTO course_items (student_course_id, item_name, total_weight_pct, max_score, obtained_score, created_at, updated_at) +SELECT sc.id, v.name, v.w, v.max, v.got, now(), now() +FROM student_courses sc +JOIN courses c ON c.id = sc.course_id AND c.course_code = 'CSCI 231' +CROSS JOIN (VALUES + ('Quiz 1', 10.0, 20.0, 13.0), + ('Quiz 2', 10.0, 20.0, 16.0), + ('Midterm', 30.0, 100.0, 61.0), + ('Lab work', 20.0, 100.0, NULL), + ('Final', 30.0, 100.0, NULL) +) AS v(name, w, max, got); + +-- MATH 241: nothing graded yet — must be skipped by the GPA, not read as 0. +INSERT INTO course_items (student_course_id, item_name, total_weight_pct, max_score, obtained_score, created_at, updated_at) +SELECT sc.id, v.name, v.w, v.max, v.got, now(), now() +FROM student_courses sc +JOIN courses c ON c.id = sc.course_id AND c.course_code = 'MATH 241' +CROSS JOIN (VALUES + ('Midterm 1', 25.0, 100.0, NULL::numeric), + ('Midterm 2', 25.0, 100.0, NULL), + ('Final', 50.0, 100.0, NULL) +) AS v(name, w, max, got); + +-- PHYS 161: borderline, and weights sum to 100 with everything graded. +INSERT INTO course_items (student_course_id, item_name, total_weight_pct, max_score, obtained_score, created_at, updated_at) +SELECT sc.id, v.name, v.w, v.max, v.got, now(), now() +FROM student_courses sc +JOIN courses c ON c.id = sc.course_id AND c.course_code = 'PHYS 161' +CROSS JOIN (VALUES + ('Problem set 1', 20.0, 40.0, 31.0), + ('Problem set 2', 20.0, 40.0, 29.0), + ('Midterm', 30.0, 100.0, 74.0), + ('Final', 30.0, 100.0, 71.0) +) AS v(name, w, max, got); + +-- WCS 101: no items at all — the empty-course case. + +COMMIT; diff --git a/backend/lifespan.py b/backend/lifespan.py index 0f5b574a..ed87cc27 100644 --- a/backend/lifespan.py +++ b/backend/lifespan.py @@ -3,13 +3,15 @@ from fastapi import FastAPI from google.auth.credentials import Credentials +# Register Rabbit subscribers before the broker starts. +import backend.modules.notification.tasks # noqa: F401 +import backend.modules.notion.tasks # noqa: F401 from backend.bootstrap.db import cleanup_db, setup_db from backend.bootstrap.gcp import setup_gcp from backend.bootstrap.meilisearch import cleanup_meilisearch, setup_meilisearch from backend.bootstrap.rbq import cleanup_rbq, setup_rbq from backend.bootstrap.redis import cleanup_redis, setup_redis from backend.core.configs.config import Config -from backend.modules.routers import routers from backend.modules.auth.app_token import AppTokenManager from backend.modules.auth.keycloak_manager import KeyCloakManager from backend.modules.bot.startup import cleanup_bot, setup_bot @@ -27,10 +29,6 @@ MEILISEARCH_INDEXES as OPPORTUNITIES_MEILI_INDEXES, ) -# Register Rabbit subscribers before the broker starts. -import backend.modules.notification.tasks # noqa: F401 -import backend.modules.notion.tasks # noqa: F401 - @asynccontextmanager async def lifespan(app: FastAPI): @@ -56,8 +54,6 @@ async def lifespan(app: FastAPI): await setup_bot(app) - for router in routers: - app.include_router(router) yield finally: diff --git a/backend/main.py b/backend/main.py index b1646584..edc213de 100644 --- a/backend/main.py +++ b/backend/main.py @@ -7,6 +7,7 @@ # Import both the instrumentor and the metrics_app from backend.middlewares.prometheus_metrics import instrument_app, metrics_app +from backend.modules.routers import routers app = FastAPI( debug=True if config.IS_DEBUG else False, @@ -18,9 +19,15 @@ title="nuspace API", description=" Nuspace.kz is a SuperApp for NU students that streamlines communication and " "replaces disorganized Telegram chats with a more reliable solution. " - "[Project Github](https://github.com/ulanpy/nuspace). " + "[Project Github](https://github.com/ulanpy/nuspace). ", ) +# Routes describe the application and must exist before lifespan starts. +# Keeping them out of startup lets tooling export OpenAPI without connecting to +# Postgres, Redis, Meilisearch, RabbitMQ, GCS, or Telegram. +for router in routers: + app.include_router(router) + app.mount("/metrics", metrics_app) app.add_middleware( diff --git a/backend/modules/auth/api.py b/backend/modules/auth/api.py index dfe3316d..33111ee2 100644 --- a/backend/modules/auth/api.py +++ b/backend/modules/auth/api.py @@ -1,3 +1,4 @@ +import logging from typing import Annotated from aiogram import Bot @@ -7,7 +8,6 @@ from jose import JWTError from redis.asyncio import Redis -from backend.modules.auth.dependencies import get_creds_or_401 from backend.common.request_url import request_app_base_url from backend.core.configs.config import config from backend.modules.auth import dependencies as deps @@ -16,10 +16,12 @@ set_kc_auth_cookies, unset_kc_auth_cookies, ) +from backend.modules.auth.dependencies import get_creds_or_401 from backend.modules.auth.schemas import CurrentUserResponse, Sub from backend.modules.auth.service import AuthService router = APIRouter(tags=["Auth Routes"]) +logger = logging.getLogger(__name__) @router.get("/login") @@ -41,12 +43,11 @@ async def login( status_code=303, ) + response = await auth_service.get_authorize_redirect(request, state, app_base_url, reauth) if reauth: refresh_token = request.cookies.get(config.COOKIE_REFRESH_NAME) - response = RedirectResponse(url="/", status_code=303) await auth_service.prepare_reauth(request, response, refresh_token) - - return await auth_service.get_authorize_redirect(request, state, app_base_url, reauth) + return response @router.get("/auth/callback") @@ -66,10 +67,31 @@ async def auth_callback( async def bind_tg( request: Request, sub_param: Sub, + user: Annotated[tuple[dict, dict], Depends(get_creds_or_401)], auth_service: AuthService = Depends(deps.get_auth_service), ): + """Issue a Telegram deeplink binding the caller's own account to a chat.""" + # The sub is taken from the session, never from the request body, and the + # endpoint requires a session at all. Both used to be untrue: it had no auth + # dependency and trusted the body, while returning `correct_number` — the + # answer to the emoji confirmation — in the same response. Subs are public + # (every event and community response carries `creator.sub`), so anyone + # could request a link for any account and bind their own Telegram to it, + # quietly redirecting that user's notifications, SGotinish ticket updates + # included. + # + # The body is still accepted so existing clients keep working, but a sub + # that is not the caller's is refused rather than ignored: a client sending + # someone else's is either stale or hostile, and both are worth surfacing. + session_sub = user[0].get("sub") + if sub_param.sub != session_sub: + raise HTTPException( + status_code=status.HTTP_403_FORBIDDEN, + detail="Cannot bind Telegram for another user", + ) + bot: Bot = request.app.state.bot - payload = auth_service.build_telegram_bind_payload(sub_param.sub) + payload = auth_service.build_telegram_bind_payload(session_sub) link = await create_start_link(bot, payload["start_payload"], encode=True) return { "link": link, @@ -125,26 +147,23 @@ async def get_current_user( return await auth_service.get_current_user(kc_principal, app_principal) -@router.get("/logout") +# 204 with no body: the only thing this endpoint produces is Set-Cookie headers. +# It used to return the integer 200 as a JSON body, which said nothing and +# invited callers to read a result that was never meaningful. +@router.get("/logout", status_code=status.HTTP_204_NO_CONTENT, response_class=Response) async def logout( response: Response, auth_service: AuthService = Depends(deps.get_auth_service), refresh_token: Annotated[str | None, Cookie(alias=config.COOKIE_REFRESH_NAME)] = None, ): - if not refresh_token: - raise HTTPException( - status_code=status.HTTP_401_UNAUTHORIZED, - detail="No refresh token found in cookies", - ) - - try: - await auth_service.logout(refresh_token) - except Exception as exc: - raise HTTPException( - status_code=status.HTTP_403_FORBIDDEN, - detail=f"Error revoking token: {exc}", - ) from exc + if refresh_token: + try: + await auth_service.logout(refresh_token) + except Exception: + # Remote revocation is desirable, but an unavailable identity + # provider must not prevent the browser from ending its local + # session. The short-lived access token will expire independently. + logger.warning("Failed to revoke refresh token during logout", exc_info=True) unset_kc_auth_cookies(response) response.delete_cookie(key=config.COOKIE_APP_NAME) - return status.HTTP_200_OK diff --git a/backend/modules/auth/mock.py b/backend/modules/auth/mock.py index 537a8adf..4c0ac001 100644 --- a/backend/modules/auth/mock.py +++ b/backend/modules/auth/mock.py @@ -20,6 +20,33 @@ "picture": "https://i.pravatar.cc/150?img=5", "sub": "mock-sub-charlie", }, + # Added so every Student Government role can be signed into rather than + # role-swapped in SQL between passes. backend/fixtures/dev/seed_campus.sql + # gives these three their roles and departments: Dana is an Executive and + # Erik a Member of the same department, which is the pair the capo rules + # are about, and Gulnar sits in a special department so "not your + # department" has something to refuse. + { + "email": "dana@example.com", + "given_name": "Dana", + "family_name": "Dosanova", + "picture": "https://i.pravatar.cc/150?img=6", + "sub": "mock-sub-dana", + }, + { + "email": "erik@example.com", + "given_name": "Erik", + "family_name": "Ermekov", + "picture": "https://i.pravatar.cc/150?img=7", + "sub": "mock-sub-erik", + }, + { + "email": "hassan@example.com", + "given_name": "Hassan", + "family_name": "Hakim", + "picture": "https://i.pravatar.cc/150?img=10", + "sub": "mock-sub-hassan", + }, ] diff --git a/backend/modules/auth/tests/test_auth_api.py b/backend/modules/auth/tests/test_auth_api.py new file mode 100644 index 00000000..23e49128 --- /dev/null +++ b/backend/modules/auth/tests/test_auth_api.py @@ -0,0 +1,88 @@ +from unittest.mock import AsyncMock + +import pytest +from backend.core.configs.config import config +from backend.modules.auth.api import login, logout +from fastapi import Response +from fastapi.responses import RedirectResponse +from starlette.requests import Request + + +def request_with_cookies(cookies: dict[str, str] | None = None) -> Request: + cookie_header = "; ".join(f"{name}={value}" for name, value in (cookies or {}).items()) + headers = [(b"host", b"localhost")] + if cookie_header: + headers.append((b"cookie", cookie_header.encode())) + return Request( + { + "type": "http", + "scheme": "http", + "method": "GET", + "path": "/api/login", + "query_string": b"", + "headers": headers, + "server": ("localhost", 80), + "client": ("127.0.0.1", 1234), + } + ) + + +@pytest.mark.asyncio +async def test_reauthentication_clears_cookies_on_authorize_response(monkeypatch): + from backend.modules.auth import api + + monkeypatch.setattr(api.config, "MOCK_KEYCLOAK", False) + auth_service = AsyncMock() + auth_service.ensure_login_state.return_value = "state" + authorize_response = RedirectResponse("https://identity.example/login") + auth_service.get_authorize_redirect.return_value = authorize_response + redis = AsyncMock() + request = request_with_cookies({config.COOKIE_REFRESH_NAME: "refresh-token"}) + + result = await login( + request=request, + auth_service=auth_service, + redis=redis, + reauth=True, + ) + + assert result is authorize_response + auth_service.prepare_reauth.assert_awaited_once_with( + request, authorize_response, "refresh-token" + ) + + +@pytest.mark.asyncio +async def test_logout_without_refresh_token_still_clears_local_cookies(): + response = Response() + auth_service = AsyncMock() + + result = await logout( + response=response, + auth_service=auth_service, + refresh_token=None, + ) + + assert result is None + auth_service.logout.assert_not_awaited() + deleted_cookies = response.headers.getlist("set-cookie") + assert any(cookie.startswith(f"{config.COOKIE_ACCESS_NAME}=") for cookie in deleted_cookies) + assert any(cookie.startswith(f"{config.COOKIE_REFRESH_NAME}=") for cookie in deleted_cookies) + assert any(cookie.startswith(f"{config.COOKIE_APP_NAME}=") for cookie in deleted_cookies) + + +@pytest.mark.asyncio +async def test_logout_clears_local_cookies_when_remote_revocation_fails(): + response = Response() + auth_service = AsyncMock() + auth_service.logout.side_effect = RuntimeError("identity provider unavailable") + + result = await logout( + response=response, + auth_service=auth_service, + refresh_token="refresh-token", + ) + + assert result is None + auth_service.logout.assert_awaited_once_with("refresh-token") + assert len(response.headers.getlist("set-cookie")) == 3 diff --git a/backend/modules/campuscurrent/communities/og.py b/backend/modules/campuscurrent/communities/og.py index 8f39519b..15addb9f 100644 --- a/backend/modules/campuscurrent/communities/og.py +++ b/backend/modules/campuscurrent/communities/og.py @@ -112,3 +112,25 @@ async def get_community_og_by_query( html = _build_community_html(community_response, request) return HTMLResponse(content=html, status_code=status.HTTP_200_OK) + + +@router.get("/og/communities/{id}", response_class=HTMLResponse, include_in_schema=False) +async def get_community_og_by_path( + request: Request, + id: int, + user=Depends(get_creds_or_guest), + infra: Infra = Depends(get_infra), + community_service: CommunityService = Depends(get_community_service), +) -> HTMLResponse: + """Path-param variant for /communities/{id} URLs. + + Nginx rewrites crawler requests to /api/og$request_uri, so the shape of this + route has to mirror the frontend's URLs. The query-param route above is kept + so links already shared as /communities?id=123 keep their previews. + """ + community_response = await community_service.get_community_response( + infra=infra, community_id=id, user=user + ) + + html = _build_community_html(community_response, request) + return HTMLResponse(content=html, status_code=status.HTTP_200_OK) diff --git a/backend/modules/campuscurrent/events/og.py b/backend/modules/campuscurrent/events/og.py index ea3db7d2..71621c34 100644 --- a/backend/modules/campuscurrent/events/og.py +++ b/backend/modules/campuscurrent/events/og.py @@ -32,13 +32,13 @@ def _truncate(text: str, max_len: int) -> str: def _select_og_image(event_response) -> str | None: + # Events no longer carry a community (see "detached communities from + # events"), so there is no community banner to fall back to. Reading it + # here raised AttributeError and turned every preview for an event without + # its own media into a 500. for media in event_response.media: if getattr(media, "url", ""): return media.url - if event_response.community: - for media in event_response.community.media: - if getattr(media, "url", ""): - return media.url return None @@ -104,3 +104,25 @@ async def get_event_og_by_query( html = _build_event_html(event_response, request) return HTMLResponse(content=html, status_code=status.HTTP_200_OK) + + +@router.get("/og/events/{id}", response_class=HTMLResponse, include_in_schema=False) +async def get_event_og_by_path( + request: Request, + id: int, + user=Depends(get_creds_or_guest), + infra: Infra = Depends(get_infra), + event_service: EventService = Depends(get_event_service), +) -> HTMLResponse: + """Path-param variant for /events/{id} URLs. + + Nginx rewrites crawler requests to /api/og$request_uri, so the shape of this + route has to mirror the frontend's URLs. The query-param route above is kept + so links already shared as /events?id=123 keep their previews. + """ + event_response = await event_service.get_event_by_id( + infra=infra, event_id=id, user=user + ) + + html = _build_event_html(event_response, request) + return HTMLResponse(content=html, status_code=status.HTTP_200_OK) diff --git a/backend/modules/courses/courses/api.py b/backend/modules/courses/courses/api.py index 18ab8a12..e82922d7 100644 --- a/backend/modules/courses/courses/api.py +++ b/backend/modules/courses/courses/api.py @@ -1,23 +1,28 @@ """ -This module is as a tribute to the creator +This module is as a tribute to the creator of crashed.nu — @superhooman. GitHub: https://github.com/superhooman/crashed.nu """ - - from typing import Annotated, List import httpx from backend.common.dependencies import get_infra -from backend.modules.auth.dependencies import get_creds_or_401 from backend.common.schemas import Infra from backend.core.configs.config import config +from backend.modules.auth.dependencies import get_creds_or_401 from backend.modules.courses.courses import schemas from backend.modules.courses.courses.dependencies import get_student_course_service from backend.modules.courses.courses.errors import CourseLookupError, SemesterResolutionError from backend.modules.courses.courses.policy import StudentCoursePolicy from backend.modules.courses.courses.service import StudentCourseService +from backend.modules.courses.registrar.clients.registrar_client import ( + RegistrarLoginUnconfirmed, +) +from backend.modules.courses.registrar.username import ( + RegistrarDebugUsernameMissing, + resolve_registrar_username, +) from fastapi import APIRouter, Cookie, Depends, HTTPException, Query, status from fastapi.responses import Response @@ -52,8 +57,10 @@ async def sync_courses_from_registrar( """ try: student_sub = user[0].get("sub") - student_username = ( - user[0].get("email").split("@")[0] if not config.IS_DEBUG else "ulan.sharipov" + student_username = resolve_registrar_username( + user[0].get("email"), + is_debug=config.IS_DEBUG, + debug_username=config.REGISTRAR_DEBUG_USERNAME, ) sync_result = await service.sync_courses_from_registrar( student_sub=student_sub, @@ -62,17 +69,30 @@ async def sync_courses_from_registrar( ) return sync_result + except RegistrarDebugUsernameMissing as e: + raise HTTPException(status_code=status.HTTP_503_SERVICE_UNAVAILABLE, detail=str(e)) + except CourseLookupError as e: raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail=str(e)) except SemesterResolutionError as e: raise HTTPException(status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail=str(e)) + except RegistrarLoginUnconfirmed as e: + # Not a credentials problem, so not a 401 -- telling the student to + # check a password that works wastes their time and hides a real fault. + raise HTTPException(status_code=status.HTTP_502_BAD_GATEWAY, detail=str(e)) + except ValueError as e: # Registrar login failures bubble up as ValueError raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED, detail=str(e)) - + except httpx.RequestError as e: + # DNS, TLS or connectivity failure reaching the registrar. + raise HTTPException( + status_code=status.HTTP_502_BAD_GATEWAY, + detail=f"Could not reach the registrar: {e.__class__.__name__}", + ) @router.post("/registered_courses/sync/pdf", response_model=schemas.RegistrarSyncResponse) @@ -240,9 +260,7 @@ async def update_course_item( - Updated course item with all details """ - return await service.update_course_item( - item_id=item_id, item_update=item_update, user=user - ) + return await service.update_course_item(item_id=item_id, item_update=item_update, user=user) @router.delete("/course_items/{item_id}", status_code=status.HTTP_204_NO_CONTENT) @@ -295,6 +313,4 @@ async def get_courses( **Returns:** - Paginated list of courses with total pages information """ - return await service.get_courses( - infra=infra, page=page, size=size, term=term, keyword=keyword - ) + return await service.get_courses(infra=infra, page=page, size=size, term=term, keyword=keyword) diff --git a/backend/modules/courses/courses/tests/test_course_code_normalization.py b/backend/modules/courses/courses/tests/test_course_code_normalization.py index 5f7fcc19..d48532c3 100644 --- a/backend/modules/courses/courses/tests/test_course_code_normalization.py +++ b/backend/modules/courses/courses/tests/test_course_code_normalization.py @@ -1,20 +1,14 @@ +import pytest from backend.modules.courses.courses.service import StudentCourseService from backend.modules.courses.registrar.schemas import CourseSearchResponse, CourseSummary -import pytest def test_normalize_course_code_spaces_cross_list_wcs_wll() -> None: - assert ( - StudentCourseService._normalize_course_code("WCS260/WLL235") - == "WCS 260/WLL 235" - ) + assert StudentCourseService._normalize_course_code("WCS260/WLL235") == "WCS 260/WLL 235" def test_normalize_course_code_spaces_cross_list_wll_ant() -> None: - assert ( - StudentCourseService._normalize_course_code(" WLL171 / ANT175 ") - == "WLL 171/ANT 175" - ) + assert StudentCourseService._normalize_course_code(" WLL171 / ANT175 ") == "WLL 171/ANT 175" @pytest.mark.asyncio @@ -62,11 +56,10 @@ async def find_course_by_registrar_id(self, registrar_id: int): async def create_course(self, data): return {"created": True, "registrar_id": data.registrar_id} - service = StudentCourseService(repository=FakeRepo(), registrar_service=FakeRegistrar()) + service = StudentCourseService(repository=FakeRepo(), registrar=FakeRegistrar()) result = await service._get_or_create_course( course_code="WLL 235/WCS 260", term_value="822", ) assert result == {"created": True, "registrar_id": 9999} - diff --git a/backend/modules/courses/courses/tests/test_registrar_sync_api.py b/backend/modules/courses/courses/tests/test_registrar_sync_api.py new file mode 100644 index 00000000..752dd029 --- /dev/null +++ b/backend/modules/courses/courses/tests/test_registrar_sync_api.py @@ -0,0 +1,25 @@ +from unittest.mock import AsyncMock + +import pytest +from backend.modules.courses.courses.api import sync_courses_from_registrar +from backend.modules.courses.courses.schemas import RegistrarSyncRequest +from fastapi import HTTPException + + +@pytest.mark.asyncio +async def test_debug_sync_without_username_returns_503_before_service_call(monkeypatch): + from backend.modules.courses.courses import api + + monkeypatch.setattr(api.config, "IS_DEBUG", True) + monkeypatch.setattr(api.config, "REGISTRAR_DEBUG_USERNAME", " ") + service = AsyncMock() + + with pytest.raises(HTTPException) as raised: + await sync_courses_from_registrar( + data=RegistrarSyncRequest(password="real-password"), + user=({"sub": "mock-user"}, {}), + service=service, + ) + + assert raised.value.status_code == 503 + service.sync_courses_from_registrar.assert_not_awaited() diff --git a/backend/modules/courses/degree_audit/api.py b/backend/modules/courses/degree_audit/api.py index a730284f..3ddcd45a 100644 --- a/backend/modules/courses/degree_audit/api.py +++ b/backend/modules/courses/degree_audit/api.py @@ -14,7 +14,12 @@ DegreeRequirement, ) from backend.modules.courses.degree_audit.service import DegreeAuditService -from fastapi import APIRouter, Depends, status +from backend.modules.courses.registrar.clients.registrar_client import ( + InvalidRegistrarCredentials, + RegistrarLoginUnconfirmed, +) +from fastapi import APIRouter, Depends, HTTPException, status +import httpx from sqlalchemy.ext.asyncio import AsyncSession router = APIRouter(prefix="/degree-audit", tags=["Degree Audit"]) @@ -44,16 +49,38 @@ async def audit_from_registrar( db_session: AsyncSession = Depends(get_db_session), service: DegreeAuditService = Depends(get_degree_audit_service), ) -> AuditResponse: - return await service.audit_with_registrar( - year=payload.year, - majors=payload.majors, - minors=payload.minors, - username=payload.username if not config.IS_DEBUG else "bauyrzhan.kizatov", - password=payload.password, - student_sub=_creds[1]["sub"], - session=db_session, - tc_mappings=payload.tc_mappings, - ) + # Locally, override with your own registrar username via + # REGISTRAR_DEBUG_USERNAME rather than sending someone else's. + username = payload.username + if config.IS_DEBUG and config.REGISTRAR_DEBUG_USERNAME: + username = config.REGISTRAR_DEBUG_USERNAME + + try: + return await service.audit_with_registrar( + year=payload.year, + majors=payload.majors, + minors=payload.minors, + username=username, + password=payload.password, + student_sub=_creds[1]["sub"], + session=db_session, + tc_mappings=payload.tc_mappings, + ) + + # Without these the registrar's own failures escape as unhandled exceptions + # and become a 500, which tells the caller nothing and (with debug on) sends + # a full traceback back in the response body. + except InvalidRegistrarCredentials as exc: + raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED, detail=str(exc)) + + except RegistrarLoginUnconfirmed as exc: + raise HTTPException(status_code=status.HTTP_502_BAD_GATEWAY, detail=str(exc)) + + except httpx.RequestError as exc: + raise HTTPException( + status_code=status.HTTP_502_BAD_GATEWAY, + detail=f"Could not reach the registrar: {exc.__class__.__name__}", + ) @router.post( diff --git a/backend/modules/courses/degree_audit/service.py b/backend/modules/courses/degree_audit/service.py index 2b6723f2..680db3b6 100644 --- a/backend/modules/courses/degree_audit/service.py +++ b/backend/modules/courses/degree_audit/service.py @@ -339,21 +339,32 @@ async def get_cached_result( stmt = select(DegreeAuditResult).where(DegreeAuditResult.student_sub == student_sub) if year: stmt = stmt.where(DegreeAuditResult.admission_year == year) - if major: - stmt = stmt.where(DegreeAuditResult.major == major) stmt = stmt.order_by(DegreeAuditResult.updated_at.desc()) result = await session.execute(stmt) - row: DegreeAuditResult | None = result.scalars().first() + rows: list[DegreeAuditResult] = list(result.scalars().all()) + + def programmes(row: DegreeAuditResult) -> tuple[list[str], list[str]]: + # `major` holds a JSON blob -- {"majors": [...], "minors": [...]} -- + # not a programme name. Older rows may still hold a bare name, hence + # the fallback. + try: + parsed = json.loads(row.major) + return parsed.get("majors", []), parsed.get("minors", []) + except Exception: + return [row.major], [] + + # Filtering by major has to look inside that blob. It used to compare the + # column to the name directly, which could never match, so passing + # `?major=` always returned null -- the caller could not tell "no audit + # for this programme" from "the filter is broken". + if major: + rows = [row for row in rows if major in programmes(row)[0]] + + row = rows[0] if rows else None if not row: return None - - try: - parsed = json.loads(row.major) - majors = parsed.get("majors", []) - minors = parsed.get("minors", []) - except Exception: - majors = [row.major] - minors = [] + + majors, minors = programmes(row) from backend.modules.courses.degree_audit.schemas import AuditProgramResult diff --git a/backend/modules/courses/registrar/clients/registrar_client.py b/backend/modules/courses/registrar/clients/registrar_client.py index 03dd4c79..0b9a5303 100644 --- a/backend/modules/courses/registrar/clients/registrar_client.py +++ b/backend/modules/courses/registrar/clients/registrar_client.py @@ -1,31 +1,72 @@ import json -from typing import Any -import time import re +import ssl +import time +from pathlib import Path +from typing import Any import httpx -from httpx import Cookies - HOST = "https://registrar.nu.edu.kz" LOGIN_PATH = "/index.php" SCHEDULE_HTML_PATH = "/my-registrar/personal-schedule" SCHEDULE_JSON_PATH = "/my-registrar/personal-schedule/json" +# registrar.nu.edu.kz serves a valid, publicly-trusted Sectigo certificate for +# *.nu.edu.kz, but sends only the leaf -- it omits the intermediate CA. Clients +# therefore cannot build a chain to the Sectigo root and verification fails with +# "unable to get local issuer certificate". +# +# Supplying the missing intermediate ourselves is the actual fix. The +# alternative that was in place -- disabling verification entirely -- also +# disables every guarantee TLS provides on this connection, and this is the +# connection that carries a student's registrar password. Anyone able to +# intercept traffic between the backend and the registrar could present any +# certificate at all and harvest those credentials in plaintext. +_INTERMEDIATE_CA = Path(__file__).with_name("sectigo_intermediate.pem") + + +# Session cookies a successful login may set. AUTHSSL is what the registrar +# used; Drupal's own are SESS*/SSESS*. Accepting any of them means a rename on +# their side degrades to "could not confirm" rather than "wrong password". +_SESSION_COOKIE_NAMES = {"AUTHSSL", "AUTHSESS"} + + +class InvalidRegistrarCredentials(ValueError): + """The registrar explicitly rejected the username or password.""" + + +class RegistrarLoginUnconfirmed(RuntimeError): + """Login neither clearly succeeded nor was clearly rejected.""" + + +def _build_ssl_context() -> ssl.SSLContext: + # Default trust store for the roots, plus the one intermediate the registrar + # omits. Loading it as an additional anchor is only a path-building aid -- + # its own root is already trusted, so this grants no new trust. + context = ssl.create_default_context() + context.load_verify_locations(cafile=str(_INTERMEDIATE_CA)) + return context + class RegistrarClient: """ HTTP client for interacting with NU registrar system. - + Provides async context manager interface for authenticated requests to fetch student schedule data. Handles login authentication and session management automatically. - + Args: - verify_ssl: Whether to verify SSL certificates (default: False for registrar) + verify_ssl: Whether to verify the registrar's TLS certificate. Defaults + to True and should stay that way; the missing intermediate that made + verification fail is bundled, so there is no longer a reason to + disable it. Kept only as an escape hatch if the registrar's + certificate chain changes again. timeout: Request timeout in seconds (default: 30.0) """ - def __init__(self, *, verify_ssl: bool = False, timeout: float = 30.0) -> None: + + def __init__(self, *, verify_ssl: bool = True, timeout: float = 30.0) -> None: self.verify_ssl = verify_ssl self.timeout = timeout self._client: httpx.AsyncClient | None = None @@ -44,7 +85,7 @@ async def _ensure_client(self) -> httpx.AsyncClient: self._client = httpx.AsyncClient( base_url=HOST, timeout=self.timeout, - verify=self.verify_ssl, + verify=_build_ssl_context() if self.verify_ssl else False, ) self._client.cookies.set("has_js", "1", domain="registrar.nu.edu.kz") return self._client @@ -68,9 +109,29 @@ async def login(self, username: str, password: str) -> None: if response.status_code >= 400: response.raise_for_status() - cookies: Cookies = client.cookies - if not any(cookie for cookie in cookies.jar if cookie.name == "AUTHSSL"): - raise ValueError("Invalid registrar credentials") + # Drupal answers a rejected login with 200 and an error message, so the + # status code says nothing. Read the outcome from three signals rather + # than one: previously this checked only for an AUTHSSL cookie and + # reported "invalid credentials" whenever it was absent, which means a + # rename of that cookie would tell every student their password was + # wrong while it was in fact fine. + body = response.text + rejected = "unrecognized username or password" in body.lower() + has_session = any( + cookie.name in _SESSION_COOKIE_NAMES or cookie.name.startswith("SSESS") + for cookie in client.cookies.jar + ) + looks_logged_in = has_session or "user/logout" in body + + if rejected: + raise InvalidRegistrarCredentials("The registrar rejected the username or password.") + if not looks_logged_in: + # Neither signal present: the login page changed, or the registrar + # answered with something unexpected. Saying "wrong password" here + # would send the student to reset a password that works. + raise RegistrarLoginUnconfirmed( + "Could not confirm the registrar login; the sign-in page may have changed." + ) async def _get_schedule_access(self) -> dict[str, Any]: """ @@ -143,7 +204,9 @@ async def fetch_schedule(self, username: str, password: str) -> dict[str, Any]: return primary - async def fetch_unofficial_transcript_raw(self, username: str, password: str) -> tuple[str, Any]: + async def fetch_unofficial_transcript_raw( + self, username: str, password: str + ) -> tuple[str, Any]: """ Fetch unofficial transcript payload directly from registrar. Returns tuple of (content_type, payload) where payload is JSON (dict) @@ -186,7 +249,11 @@ async def fetch_unofficial_transcript_pdf(self, username: str, password: str) -> pass if text: - m = re.search(r'(?:href|src)="([^"]*transcriptRenderDocumentPDF[^"]+)"', text, re.IGNORECASE) + m = re.search( + r'(?:href|src)="([^"]*transcriptRenderDocumentPDF[^"]+)"', + text, + re.IGNORECASE, + ) if m: url = m.group(1) # Support both absolute and relative links. diff --git a/backend/modules/courses/registrar/clients/sectigo_intermediate.pem b/backend/modules/courses/registrar/clients/sectigo_intermediate.pem new file mode 100644 index 00000000..af4a9d66 --- /dev/null +++ b/backend/modules/courses/registrar/clients/sectigo_intermediate.pem @@ -0,0 +1,36 @@ +-----BEGIN CERTIFICATE----- +MIIGTDCCBDSgAwIBAgIQOXpmzCdWNi4NqofKbqvjsTANBgkqhkiG9w0BAQwFADBf +MQswCQYDVQQGEwJHQjEYMBYGA1UEChMPU2VjdGlnbyBMaW1pdGVkMTYwNAYDVQQD +Ey1TZWN0aWdvIFB1YmxpYyBTZXJ2ZXIgQXV0aGVudGljYXRpb24gUm9vdCBSNDYw +HhcNMjEwMzIyMDAwMDAwWhcNMzYwMzIxMjM1OTU5WjBgMQswCQYDVQQGEwJHQjEY +MBYGA1UEChMPU2VjdGlnbyBMaW1pdGVkMTcwNQYDVQQDEy5TZWN0aWdvIFB1Ymxp +YyBTZXJ2ZXIgQXV0aGVudGljYXRpb24gQ0EgRFYgUjM2MIIBojANBgkqhkiG9w0B +AQEFAAOCAY8AMIIBigKCAYEAljZf2HIz7+SPUPQCQObZYcrxLTHYdf1ZtMRe7Yeq +RPSwygz16qJ9cAWtWNTcuICc++p8Dct7zNGxCpqmEtqifO7NvuB5dEVexXn9RFFH +12Hm+NtPRQgXIFjx6MSJcNWuVO3XGE57L1mHlcQYj+g4hny90aFh2SCZCDEVkAja +EMMfYPKuCjHuuF+bzHFb/9gV8P9+ekcHENF2nR1efGWSKwnfG5RawlkaQDpRtZTm +M64TIsv/r7cyFO4nSjs1jLdXYdz5q3a4L0NoabZfbdxVb+CUEHfB0bpulZQtH1Rv +38e/lIdP7OTTIlZh6OYL6NhxP8So0/sht/4J9mqIGxRFc0/pC8suja+wcIUna0HB +pXKfXTKpzgis+zmXDL06ASJf5E4A2/m+Hp6b84sfPAwQ766rI65mh50S0Di9E3Pn +2WcaJc+PILsBmYpgtmgWTR9eV9otfKRUBfzHUHcVgarub/XluEpRlTtZudU5xbFN +xx/DgMrXLUAPaI60fZ6wA+PTAgMBAAGjggGBMIIBfTAfBgNVHSMEGDAWgBRWc1hk +lfmSGrASKgRieaFAFYghSTAdBgNVHQ4EFgQUaMASFhgOr872h6YyV6NGUV3LBycw +DgYDVR0PAQH/BAQDAgGGMBIGA1UdEwEB/wQIMAYBAf8CAQAwHQYDVR0lBBYwFAYI +KwYBBQUHAwEGCCsGAQUFBwMCMBsGA1UdIAQUMBIwBgYEVR0gADAIBgZngQwBAgEw +VAYDVR0fBE0wSzBJoEegRYZDaHR0cDovL2NybC5zZWN0aWdvLmNvbS9TZWN0aWdv +UHVibGljU2VydmVyQXV0aGVudGljYXRpb25Sb290UjQ2LmNybDCBhAYIKwYBBQUH +AQEEeDB2ME8GCCsGAQUFBzAChkNodHRwOi8vY3J0LnNlY3RpZ28uY29tL1NlY3Rp +Z29QdWJsaWNTZXJ2ZXJBdXRoZW50aWNhdGlvblJvb3RSNDYucDdjMCMGCCsGAQUF +BzABhhdodHRwOi8vb2NzcC5zZWN0aWdvLmNvbTANBgkqhkiG9w0BAQwFAAOCAgEA +YtOC9Fy+TqECFw40IospI92kLGgoSZGPOSQXMBqmsGWZUQ7rux7cj1du6d9rD6C8 +ze1B2eQjkrGkIL/OF1s7vSmgYVafsRoZd/IHUrkoQvX8FZwUsmPu7amgBfaY3g+d +q1x0jNGKb6I6Bzdl6LgMD9qxp+3i7GQOnd9J8LFSietY6Z4jUBzVoOoz8iAU84OF +h2HhAuiPw1ai0VnY38RTI+8kepGWVfGxfBWzwH9uIjeooIeaosVFvE8cmYUB4TSH +5dUyD0jHct2+8ceKEtIoFU/FfHq/mDaVnvcDCZXtIgitdMFQdMZaVehmObyhRdDD +4NQCs0gaI9AAgFj4L9QtkARzhQLNyRf87Kln+YU0lgCGr9HLg3rGO8q+Y4ppLsOd +unQZ6ZxPNGIfOApbPVf5hCe58EZwiWdHIMn9lPP6+F404y8NNugbQixBber+x536 +WrZhFZLjEkhp7fFXf9r32rNPfb74X/U90Bdy4lzp3+X1ukh1BuMxA/EEhDoTOS3l +7ABvc7BYSQubQ2490OcdkIzUh3ZwDrakMVrbaTxUM2p24N6dB+ns2zptWCva6jzW +r8IWKIMxzxLPv5Kt3ePKcUdvkBU/smqujSczTzzSjIoR5QqQA6lN1ZRSnuHIWCvh +JEltkYnTAH41QJ6SAWO66GrrUESwN/cgZzL4JLEqz1Y= +-----END CERTIFICATE----- diff --git a/backend/modules/courses/registrar/tests/test_registrar_client.py b/backend/modules/courses/registrar/tests/test_registrar_client.py index d8b5406e..1587404b 100644 --- a/backend/modules/courses/registrar/tests/test_registrar_client.py +++ b/backend/modules/courses/registrar/tests/test_registrar_client.py @@ -1,16 +1,84 @@ import importlib.util from pathlib import Path +from types import SimpleNamespace import pytest -CLIENT_PATH = ( - Path(__file__).resolve().parents[1] / "clients" / "registrar_client.py" -) +CLIENT_PATH = Path(__file__).resolve().parents[1] / "clients" / "registrar_client.py" spec = importlib.util.spec_from_file_location("registrar_client", CLIENT_PATH) registrar_client = importlib.util.module_from_spec(spec) assert spec and spec.loader spec.loader.exec_module(registrar_client) RegistrarClient = registrar_client.RegistrarClient +InvalidRegistrarCredentials = registrar_client.InvalidRegistrarCredentials +RegistrarLoginUnconfirmed = registrar_client.RegistrarLoginUnconfirmed + + +class LoginResponse: + status_code = 200 + + def __init__(self, text: str): + self.text = text + + def raise_for_status(self) -> None: + raise AssertionError("raise_for_status should not be called") + + +class LoginHttpClient: + def __init__(self, text: str, cookie_names: tuple[str, ...] = ()): + self.response = LoginResponse(text) + self.cookies = SimpleNamespace(jar=[SimpleNamespace(name=name) for name in cookie_names]) + + async def post(self, *args, **kwargs): + return self.response + + +def install_login_client(monkeypatch, registrar, fake_http_client): + async def fake_ensure_client(): + return fake_http_client + + monkeypatch.setattr(registrar, "_ensure_client", fake_ensure_client) + + +@pytest.mark.asyncio +async def test_login_recognizes_explicitly_rejected_credentials(monkeypatch): + client = RegistrarClient() + install_login_client( + monkeypatch, + client, + LoginHttpClient("Unrecognized username or password."), + ) + + with pytest.raises(InvalidRegistrarCredentials): + await client.login("student", "wrong") + + +@pytest.mark.asyncio +@pytest.mark.parametrize( + ("body", "cookies"), + [ + ("Welcome", ("AUTHSSL",)), + ('Log out', ()), + ], +) +async def test_login_accepts_session_or_logout_success_signals(monkeypatch, body, cookies): + client = RegistrarClient() + install_login_client(monkeypatch, client, LoginHttpClient(body, cookies)) + + await client.login("student", "correct") + + +@pytest.mark.asyncio +async def test_login_reports_an_unconfirmed_page_change(monkeypatch): + client = RegistrarClient() + install_login_client( + monkeypatch, + client, + LoginHttpClient("Unexpected login response"), + ) + + with pytest.raises(RegistrarLoginUnconfirmed): + await client.login("student", "correct") @pytest.mark.asyncio diff --git a/backend/modules/courses/registrar/tests/test_registrar_service.py b/backend/modules/courses/registrar/tests/test_registrar_service.py index eb065bae..350b1e71 100644 --- a/backend/modules/courses/registrar/tests/test_registrar_service.py +++ b/backend/modules/courses/registrar/tests/test_registrar_service.py @@ -1,5 +1,4 @@ import pytest - from backend.modules.courses.registrar import service as registrar_service from backend.modules.courses.registrar.service import RegistrarService @@ -12,7 +11,7 @@ async def fake_get(*args, **kwargs): return { "hits": [ { - "abbr": "MATH 263", + "course_code": "MATH 263", "prerequisite": "Wrong prereq", "corequisite": "", "antirequisite": "", @@ -22,7 +21,7 @@ async def fake_get(*args, **kwargs): "priority_4": "", }, { - "abbr": "MATH 162", + "course_code": "MATH 162", "prerequisite": "Correct prereq", "corequisite": "", "antirequisite": "", @@ -50,7 +49,7 @@ async def fake_get(*args, **kwargs): return { "hits": [ { - "abbr": "MATH 263", + "course_code": "MATH 263", "prerequisite": "Wrong prereq", } ] @@ -61,4 +60,3 @@ async def fake_get(*args, **kwargs): result = await service.fetch_course_priorities(["MATH 162"]) assert result == {} - diff --git a/backend/modules/courses/registrar/tests/test_registrar_username.py b/backend/modules/courses/registrar/tests/test_registrar_username.py new file mode 100644 index 00000000..e444da03 --- /dev/null +++ b/backend/modules/courses/registrar/tests/test_registrar_username.py @@ -0,0 +1,47 @@ +import pytest +from backend.modules.courses.registrar.username import ( + RegistrarDebugUsernameMissing, + resolve_registrar_username, +) + + +def test_debug_requires_an_explicit_nonblank_username(): + for configured in (None, "", " "): + with pytest.raises(RegistrarDebugUsernameMissing): + resolve_registrar_username( + None, + is_debug=True, + debug_username=configured, + ) + + +def test_debug_uses_only_the_configured_username(): + assert ( + resolve_registrar_username( + "mock-user@example.com", + is_debug=True, + debug_username=" developer ", + ) + == "developer" + ) + + +def test_production_derives_the_email_local_part(): + assert ( + resolve_registrar_username( + "student@nu.edu.kz", + is_debug=False, + debug_username="ignored", + ) + == "student" + ) + + +@pytest.mark.parametrize("email", [None, "", "not-an-email", "@nu.edu.kz"]) +def test_production_rejects_an_invalid_email(email): + with pytest.raises(ValueError): + resolve_registrar_username( + email, + is_debug=False, + debug_username=None, + ) diff --git a/backend/modules/courses/registrar/username.py b/backend/modules/courses/registrar/username.py new file mode 100644 index 00000000..911c8859 --- /dev/null +++ b/backend/modules/courses/registrar/username.py @@ -0,0 +1,31 @@ +class RegistrarDebugUsernameMissing(RuntimeError): + """Debug registrar access was attempted without an explicit safe username.""" + + +def resolve_registrar_username( + email: object, + *, + is_debug: bool, + debug_username: str | None, +) -> str: + """ + Resolve credentials without ever falling back to a mock identity in debug. + + Production keeps using the local part of the authenticated NU email. Debug + mode must opt in with a nonblank username before any registrar call occurs. + """ + if is_debug: + username = (debug_username or "").strip() + if not username: + raise RegistrarDebugUsernameMissing( + "Registrar sync is unavailable until REGISTRAR_DEBUG_USERNAME is configured." + ) + return username + + if not isinstance(email, str) or "@" not in email: + raise ValueError("The signed-in account does not have a valid email address.") + + username = email.split("@", 1)[0].strip() + if not username: + raise ValueError("The signed-in account does not have a valid email address.") + return username diff --git a/backend/modules/courses/statistics/service.py b/backend/modules/courses/statistics/service.py index 147e327f..fef827d6 100644 --- a/backend/modules/courses/statistics/service.py +++ b/backend/modules/courses/statistics/service.py @@ -23,6 +23,14 @@ async def list_grade_reports( meili_filters = [f"term = {term}"] if term else None meili_result = None + # The term filter has to be applied in SQL as well as in Meilisearch. + # Meilisearch is only consulted when there is a keyword, so without this the + # filter silently did nothing while browsing a term -- `?term=FA2025` and + # even `?term=NONSENSE` both returned every row, which reads as "this term + # has 1308 courses" rather than as a broken filter. + if term: + conditions.append(GradeReport.term == term) + if keyword: meili_result = await meilisearch.get( client=meilisearch_client, diff --git a/backend/modules/sgotinish/conversations/dependencies.py b/backend/modules/sgotinish/conversations/dependencies.py index e860ca0a..ac147b5a 100644 --- a/backend/modules/sgotinish/conversations/dependencies.py +++ b/backend/modules/sgotinish/conversations/dependencies.py @@ -1,10 +1,19 @@ +from backend.common.dependencies import get_db_session from backend.modules.sgotinish.conversations.service import ConversationService from backend.modules.sgotinish.tickets.dependencies import get_ticket_service from backend.modules.sgotinish.tickets.service import TicketService from fastapi import Depends +from sqlalchemy.ext.asyncio import AsyncSession def get_conversation_service( + db_session: AsyncSession = Depends(get_db_session), ticket_service: TicketService = Depends(get_ticket_service), ) -> ConversationService: - return ticket_service.conversation_service + # Built the same way the message service is: TicketService satisfies the + # TicketAccessChecker protocol the conversation service needs. + # + # This previously returned `ticket_service.conversation_service`, an + # attribute TicketService does not define, so every conversation endpoint + # raised AttributeError and answered 500. + return ConversationService(db_session, ticket_service) diff --git a/backend/modules/sgotinish/messages/api.py b/backend/modules/sgotinish/messages/api.py index a0b3e92f..74f26c88 100644 --- a/backend/modules/sgotinish/messages/api.py +++ b/backend/modules/sgotinish/messages/api.py @@ -7,6 +7,7 @@ from backend.modules.sgotinish.messages import dependencies as deps from backend.modules.sgotinish.messages import schemas from backend.modules.sgotinish.messages.service import MessageService +from backend.modules.sgotinish.owner_hash import get_owner_hash from fastapi import APIRouter, Depends, Query router = APIRouter(tags=["SGotinish Messages Routes"]) @@ -22,7 +23,7 @@ async def create_message( message_data: schemas.MessageCreateDTO, user_tuple: Annotated[tuple[dict, dict], Depends(get_creds_or_guest)], service: MessageService = Depends(deps.get_message_service), - owner_hash: str | None = Query(default=None), + owner_hash: str | None = Depends(get_owner_hash), ) -> schemas.MessageResponseDTO: """ Creates a new message in a conversation. @@ -52,7 +53,7 @@ async def get_messages( service: MessageService = Depends(deps.get_message_service), size: int = Query(20, ge=1, le=100), page: int = 1, - owner_hash: str | None = Query(default=None), + owner_hash: str | None = Depends(get_owner_hash), ) -> schemas.ListMessageDTO: """ Retrieves a paginated list of messages with flexible filtering. @@ -83,7 +84,7 @@ async def get_message( message_id: int, user_tuple: Annotated[tuple[dict, dict], Depends(get_creds_or_guest)], service: MessageService = Depends(deps.get_message_service), - owner_hash: str | None = Query(default=None), + owner_hash: str | None = Depends(get_owner_hash), ) -> schemas.MessageResponseDTO: """ Retrieves a single message by its unique ID. @@ -115,7 +116,7 @@ async def mark_message_as_read( message_id: int, user_tuple: Annotated[tuple[dict, dict], Depends(get_creds_or_guest)], service: MessageService = Depends(deps.get_message_service), - owner_hash: str | None = Query(default=None), + owner_hash: str | None = Depends(get_owner_hash), ) -> schemas.MessageResponseDTO: """ Marks a message as read by the current user. diff --git a/backend/modules/sgotinish/owner_hash.py b/backend/modules/sgotinish/owner_hash.py new file mode 100644 index 00000000..74354aa8 --- /dev/null +++ b/backend/modules/sgotinish/owner_hash.py @@ -0,0 +1,31 @@ +"""Where an anonymous ticket's owner hash is read from. + +`owner_hash` is the *only* credential guarding an anonymous ticket: whoever +holds it can read and post messages on that ticket. It was accepted solely as a +query parameter, which writes it into every access log that records a request +line -- nginx's default format does, verbatim -- along with any proxy, CDN or +backup that touches those logs. + +That undoes what the anonymity design is for. Ticket rows deliberately store no +`author_sub`, and then the key to the ticket was being copied into plaintext +logs on every poll for new messages. + +Reading it from a header instead keeps it out of request lines and out of +`Referer`. The query parameter still works so the existing frontend and any +anonymous link already in circulation keep functioning; the header takes +precedence, and new clients should send only the header. +""" + +from typing import Annotated + +from fastapi import Header, Query + +OWNER_HASH_HEADER = "X-Owner-Hash" + + +async def get_owner_hash( + x_owner_hash: Annotated[str | None, Header(alias=OWNER_HASH_HEADER)] = None, + owner_hash: Annotated[str | None, Query()] = None, +) -> str | None: + """The anonymous owner hash, preferring the header over the query string.""" + return x_owner_hash or owner_hash diff --git a/backend/scripts/__init__.py b/backend/scripts/__init__.py new file mode 100644 index 00000000..663a0e6c --- /dev/null +++ b/backend/scripts/__init__.py @@ -0,0 +1 @@ +"""Developer and CI entry points.""" diff --git a/backend/scripts/export_openapi.py b/backend/scripts/export_openapi.py new file mode 100644 index 00000000..00f6ff23 --- /dev/null +++ b/backend/scripts/export_openapi.py @@ -0,0 +1,55 @@ +import argparse +import json +import sys +from pathlib import Path + +from dotenv import load_dotenv + +REPOSITORY_ROOT = Path(__file__).resolve().parents[2] +DEFAULT_ENV_FILE = REPOSITORY_ROOT / "infra" / ".env.example" + + +def parse_args() -> argparse.Namespace: + parser = argparse.ArgumentParser( + description="Export the FastAPI contract without starting external services." + ) + parser.add_argument( + "--env-file", + type=Path, + default=DEFAULT_ENV_FILE, + help="Dummy configuration fixture loaded before importing the app.", + ) + parser.add_argument( + "--output", + type=Path, + help="Write JSON here instead of stdout.", + ) + return parser.parse_args() + + +def main() -> None: + args = parse_args() + load_dotenv(args.env_file, override=False) + sys.path.insert(0, str(REPOSITORY_ROOT)) + + # Config is instantiated while importing the app, so the fixture must be + # loaded first. Importing does not run FastAPI lifespan. + from backend.main import app + + schema = app.openapi() + if not schema.get("paths"): + raise RuntimeError("OpenAPI export has no paths; routers were not registered.") + + # FastAPI's insertion order follows router registration and is stable. + # Preserve it so codegen diffs reflect contract changes instead of sorting + # thousands of generated declarations alphabetically. + document = json.dumps(schema, ensure_ascii=False, indent=2) + "\n" + if args.output: + args.output.parent.mkdir(parents=True, exist_ok=True) + args.output.write_text(document, encoding="utf-8") + return + sys.stdout.write(document) + + +if __name__ == "__main__": + main() diff --git a/docs/web-parity-uat.md b/docs/web-parity-uat.md new file mode 100644 index 00000000..1a1f05f7 --- /dev/null +++ b/docs/web-parity-uat.md @@ -0,0 +1,176 @@ +# `web/` parity release checklist + +This is the required human acceptance gate for the first production release of +`web/`. Automated checks protect the implementation; they do not prove that a +real user can complete every legacy workflow. Every required checkbox below +must be checked against the release candidate before merge. + +Do not delete `frontend/` as part of this release. It remains the rollback +fallback for one production release. Capacitor is intentionally outside the +`web/` parity scope. + +## Release candidate + +- Commit SHA: +- Environment and URL: +- Tester: +- Date: +- Browser and version: +- Desktop viewport: +- Mobile device or viewport: +- CI run: +- Failed checks/issues (write `none`, or link each issue): + +## Automated gate + +- [ ] `pnpm format:check` +- [ ] `pnpm lint` +- [ ] `pnpm test` +- [ ] `pnpm typecheck` +- [ ] `pnpm build` +- [ ] `pnpm api:check` +- [ ] Backend tests and OpenAPI export passed in CI +- [ ] No required UAT item below is blocked, skipped, or failing + +## Discovery and profile + +Use `alice` for administration, `bob` for opportunity authoring, and `hassan` +for an ordinary student. + +- [ ] Events can be searched and filtered by type and time period; clearing + filters restores the unfiltered list and a copied filtered URL reloads + with the same controls. +- [ ] Communities can be searched and filtered by type and category; clearing + and URL reload behave as above. +- [ ] Opportunities can be searched and filtered by type, education level, + year, major, and active-only; eligibility labels match the selected + levels/years. +- [ ] `bob` can create and edit an opportunity, and adding it to Google + Calendar either succeeds or shows the expected reauthentication path. +- [ ] An event detail can be shared, copied, and opened in Google Calendar with + the correct title, time, location, and description. +- [ ] Event and community create/edit flows work with no image, one valid + image, invalid/oversized selection, all uploads failing, and a partially + failing upload batch; retry never creates a duplicate entity. +- [ ] A Telegram-linked user does not see the connection prompt; an unlinked + user can follow it; dismissing it persists for that browser. +- [ ] In Telegram, the community form's native main button submits exactly + once and is removed when the form/dialog unmounts. +- [ ] A profile with no managed communities offers the create-community action. + +## Courses + +Use a student with registered courses and at least two published grade reports. + +- [ ] Registrar-password sync and PDF sync both refresh registered courses and + show actionable failures. +- [ ] The weekly timetable orders classes, does not duplicate merged meetings, + and Google/ICS exports contain the visible schedule. +- [ ] Excluding a course updates current, projected, and ceiling GPA for the + session; re-including it restores the figures; reload resets exclusions. +- [ ] Sharing an assignment template exposes names and weights only—never + obtained/max scores—and updates the student's previous template instead + of duplicating it. +- [ ] Importing a classmate's template requires confirmation and produces the + expected assignment rows without overwriting silently. +- [ ] Between two and eight grade reports can be compared on desktop and + mobile; removal works and a ninth report is not added. + +## Planner + +- [ ] Create, rename, duplicate, switch, and delete plans; switching or + reloading a copied plan URL preserves the selected plan and term. +- [ ] Add courses, load their sections, and independently select lecture/lab + section types; genuine clashes are visible. +- [ ] Auto-build produces a schedule, and its Undo action restores every + course's exact prior section selection, including courses with none. +- [ ] Reset names the destructive effect, can be cancelled without changes, + and removes the current plan's courses only after confirmation. +- [ ] Catalog, course cards, and selected-section details show a syllabus link + when the shipped CSV contains that course, and omit it otherwise. +- [ ] Term-query and plan-list failures show retryable errors; a successful + empty plan list does not leave permanent skeletons. + +## Degree audit + +- [ ] Registrar-password and transcript-PDF audits both run against selected + year/major/minor combinations and show cached results on return. +- [ ] Each programme's “View requirements” dialog shows the published + requirement fields and retries a failed request. +- [ ] Unmatched transfer courses open the mapping step; blank rows remain + unmatched, invalid credits are rejected, and valid NU mappings rerun the + same audit. +- [ ] A rerun that still has unmatched courses can be mapped again. +- [ ] Password/PDF audit input is cleared after success without unmatched + courses, skip, dialog close, mode switch, request failure, and route + unmount; it never appears in browser storage or logs. + +## SGotinish + +Use `hassan` as a student, `charlie` as Head, `dana` as Executive, and `erik` +as Member. + +- [ ] Student tickets filter by category and status and retain those filters in + a copied URL. +- [ ] Normal and anonymous ticket creation both work; an anonymous WarpKey is + kept in the URL fragment and is not stored by the browser application. +- [ ] Head, Executive, and Member cabinets expose only their permitted actions; + category/status filtering works without changing authorization. +- [ ] Assignment, delegation, withdrawal, conversation, and completion flows + work for their allowed roles, including confirmation and error states. +- [ ] Telegram-linked/unlinked prompt behavior matches the discovery pages. + +## Sign-off and rollback + +- Product sign-off: +- Engineering sign-off: +- Known non-blocking differences: + - `web/` remains the only shipped application. `frontend/` is retained + unchanged for the soak-release rollback described below. + - The landing page does not repeat the unverified “3100+ students” claim or + the legacy joke footer. It keeps the public Nuspace header, legal links, + and student-project attribution. + - The desktop sidebar returns as a collapsible navigation rail, while mobile + keeps the sticky header and sheet. The unused legacy bottom navigation is + intentionally not restored. + - Discovery pages retain compact, URL-filtered lists and the current + permission-aware authoring flows; legacy infinite-list and state-only tab + implementations are not carried over. + - Course comparison supports up to eight reports, not the legacy limit of + four. Planner scheduling retains overlap lanes, demand calculations, + section-type grouping, exact undo state, and retryable failures. + - Degree Audit keeps the current credential lifecycle, transfer mapping, and + cached-result behavior. About keeps team and help content directly visible + instead of putting static information in legacy modals. + - Disabled elections, Dorm Eats, animation wrappers, raw palette styling, + injected style tags, and obsolete API hooks remain outside parity scope. + +### Rollback procedure + +Rolling back to `frontend/` is a revert-and-rebuild, not a traffic switch, and +it costs a full deploy cycle. Two things make it slower than it sounds: + +- The VM keeps no previous build. `ansible/roles/frontend/tasks/main.yml` wipes + `web/out/*` before unpacking, so there is no known-good artifact sitting on + the host to re-serve. +- Nothing routes between the two apps. Prod nginx bind-mounts `../web/out` + directly, so serving `frontend/` again requires changing that mount, not a + config flag. + +If a required item fails after deployment, stop the rollout and open a revert PR +touching these three files together — reverting any subset leaves the deploy +inconsistent: + +1. `infra/prod.docker-compose.yml` — point the nginx `/var/www/my-app/out` + mount back at `../frontend/out`. +2. `ansible/roles/frontend/tasks/main.yml` — unpack into `frontend/out` instead + of `web/out`. +3. `.github/workflows/deploy.yml` — build `frontend/` and pack its export as + the Ansible tarball. + +Then deploy from that branch; CI rebuilds the legacy export from source. Keep +`frontend/`, its Compose service, and the fallback comments intact for this +entire soak release — the revert above assumes that code is still present. + +Record the failure, the revert PR, and the rollback deployment in the release +issue before resuming. diff --git a/infra/.env.example b/infra/.env.example index 7b8d9e7c..e03ffb47 100644 --- a/infra/.env.example +++ b/infra/.env.example @@ -15,9 +15,9 @@ REDIS_HOST=redis REDIS_PORT=6379 -# Meeilisearch connection -MEILISEARCH_URL = "http://meilisearch:7700" -MEILISEARCH_MASTER_KEY = "secret" +# Meilisearch connection +MEILISEARCH_URL=http://meilisearch:7700 +MEILISEARCH_MASTER_KEY=secret # Keycloak Client information @@ -41,11 +41,21 @@ SESSION_MIDDLEWARE_KEY=secret TG_WEBHOOK_SECRET_TOKEN=secret APP_JWT_SECRET_256=secret APP_TOKEN_EXPIRY_MINUTES=5 -GCS_EMULATOR_HOST="http://gcs:4443" # this is a container adress -DEV_APP_URL=http://localhost # browser/OAuth origin in debug (not used for webhooks) -MOCK_KEYCLOAK=True # true unless you are have necessary keys for services -USE_GCS_EMULATOR=True # true unless you are have necessary keys for services -IS_DEBUG=True # local: uvicorn --reload; prod compose sets IS_DEBUG=false (no reload, single process) +# Browser/OAuth origin in debug (not used for webhooks) +DEV_APP_URL=http://localhost +# Keep these true unless you have the necessary local service credentials. +MOCK_KEYCLOAK=True + +# Your own registrar username (the part of your NU email before the @), used +# only when IS_DEBUG. Mock users have no NU email, so registrar sync and degree +# audit cannot derive it from the session locally. +# Leave blank to make those calls fail authentication instead of reaching a real +# account -- never point it at someone else's username. +REGISTRAR_DEBUG_USERNAME= +GCS_EMULATOR_HOST=http://gcs:4443 +USE_GCS_EMULATOR=True +# Local: uvicorn --reload; prod compose sets false (no reload, single process). +IS_DEBUG=True INTEGRATION_SECRET=secret # Gemini (/post event extraction) @@ -85,4 +95,4 @@ VM_SERVICE_ACCOUNT_EMAIL=admin@admin #Qualtrics credentials QUALTRICS_API_TOKEN=secret QUALTRICS_DATA_CENTER_ID=secret -QUALTRICS_SURVEY_ID=secret \ No newline at end of file +QUALTRICS_SURVEY_ID=secret diff --git a/infra/docker-compose.yml b/infra/docker-compose.yml index 6427b503..fbc75dd0 100644 --- a/infra/docker-compose.yml +++ b/infra/docker-compose.yml @@ -41,7 +41,9 @@ services: - nuros restart: always ports: - - "80:80" # Expose Nginx on port 80 of localhost + # :80 now serves web/. The old frontend/ service is still defined below + # for one release, but nothing routes to it — see infra/nginx/nginx.dev.conf. + - "80:80" volumes: - ./nginx/nginx.dev.conf:/etc/nginx/nginx.conf - ../frontend/dist:/var/www/my-app/dist # Make sure this folder exists in production builds @@ -157,6 +159,23 @@ services: depends_on: - cloudflared + # The app. Served at http://localhost since the cutover; `frontend` below is + # the previous one, kept for one release as a fallback and not routed to. + web: + build: + context: .. + dockerfile: web/Dockerfile.dev + restart: always + volumes: + - ../web:/app # Mount source for HMR + - /app/node_modules # Anonymous volume keeps the image's deps + environment: + # The bind mount makes pnpm's deps-status check want to purge + # node_modules, which it refuses to do without a TTY. + - CI=true + networks: + - nuros + loki: container_name: loki image: grafana/loki:latest diff --git a/infra/nginx/nginx.dev.conf b/infra/nginx/nginx.dev.conf index 45560bb8..a27c4765 100644 --- a/infra/nginx/nginx.dev.conf +++ b/infra/nginx/nginx.dev.conf @@ -27,9 +27,13 @@ http { "~*discordbot" 1; } - # Route bots to OG endpoint while preserving the request path + # Route bots to the OG endpoint while preserving the request path, and + # everyone else to the app. `web` is the rebuilt frontend: at the cutover + # it took over :80, and the old `frontend` service is still defined in the + # compose file but no longer routed to. Point this back at frontend:5173 to + # fall back to it without redeploying anything. map $is_bot $proxy_upstream { - 0 http://frontend:5173; + 0 http://web:5173; 1 http://fastapi:8000; } @@ -102,4 +106,5 @@ http { } + } diff --git a/infra/prod.docker-compose.yml b/infra/prod.docker-compose.yml index 30f9ce59..16382de3 100644 --- a/infra/prod.docker-compose.yml +++ b/infra/prod.docker-compose.yml @@ -113,7 +113,7 @@ services: - "443:443" volumes: - ./nginx/nginx.conf:/etc/nginx/nginx.conf - - ../frontend/out:/var/www/my-app/out + - ../web/out:/var/www/my-app/out - ../ssl/cloudflare.crt:/etc/ssl/cloudflare/origin.crt - ../ssl/cloudflare.key:/etc/ssl/cloudflare/origin.key - ./nginx/vpn-index.html:/etc/nginx/vpn-index.html:ro diff --git a/web/.gitignore b/web/.gitignore new file mode 100644 index 00000000..4bd514db --- /dev/null +++ b/web/.gitignore @@ -0,0 +1,30 @@ +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* +pnpm-debug.log* +lerna-debug.log* + +node_modules +dist +dist-ssr +*.local + +# Editor directories and files +.vscode/* +!.vscode/extensions.json +.idea +.DS_Store +*.suo +*.ntvs* +*.njsproj +*.sln +*.sw? + +# Generated from the backend OpenAPI schema +openapi.json + +# pnpm store, created when the dev container installs into the bind mount +.pnpm-store/ diff --git a/web/.oxlintrc.json b/web/.oxlintrc.json new file mode 100644 index 00000000..f348808d --- /dev/null +++ b/web/.oxlintrc.json @@ -0,0 +1,34 @@ +{ + "$schema": "./node_modules/oxlint/configuration_schema.json", + "ignorePatterns": ["dist", "src/api/schema.d.ts", "src/routeTree.gen.ts"], + "plugins": ["react", "typescript", "jsx-a11y", "import"], + "categories": { + "correctness": "error", + "suspicious": "warn" + }, + "rules": { + "react/react-in-jsx-scope": "off", + "import/no-unassigned-import": "off" + }, + "overrides": [ + { + "files": ["src/components/ui/**"], + "rules": { + "jsx-a11y/label-has-associated-control": "off" + } + }, + { + "files": ["src/**/*.test.ts"], + "rules": { + "typescript/no-floating-promises": "off" + } + }, + { + "files": ["src/components/markdown.tsx"], + "rules": { + "jsx-a11y/anchor-has-content": "off", + "jsx-a11y/heading-has-content": "off" + } + } + ] +} diff --git a/web/.prettierignore b/web/.prettierignore new file mode 100644 index 00000000..81207d82 --- /dev/null +++ b/web/.prettierignore @@ -0,0 +1,10 @@ +node_modules/ +coverage/ +.pnpm-store/ +pnpm-lock.yaml +package-lock.json +pnpm-lock.yaml +yarn.lock +src/routeTree.gen.ts +src/api/schema.d.ts +out/ diff --git a/web/.prettierrc b/web/.prettierrc new file mode 100644 index 00000000..9000bfaa --- /dev/null +++ b/web/.prettierrc @@ -0,0 +1,11 @@ +{ + "endOfLine": "lf", + "semi": false, + "singleQuote": false, + "tabWidth": 2, + "trailingComma": "es5", + "printWidth": 80, + "plugins": ["prettier-plugin-tailwindcss"], + "tailwindStylesheet": "src/index.css", + "tailwindFunctions": ["cn", "cva"] +} diff --git a/web/Dockerfile.dev b/web/Dockerfile.dev new file mode 100644 index 00000000..b0c18080 --- /dev/null +++ b/web/Dockerfile.dev @@ -0,0 +1,26 @@ +# Dev container for the new frontend (web/). Mirrors frontend/Dockerfile_next +# but uses pnpm via corepack instead of npm. +FROM node:24-alpine + +RUN apk add --no-cache bash curl git +RUN corepack enable + +# Keep the content-addressable store outside /app. The source directory is +# bind-mounted from the host, so a store under it would write ~300MB of package +# files straight into the working tree. +ENV npm_config_store_dir=/pnpm-store + +WORKDIR /app + +# Cache the dependency layer separately from source. +COPY web/package.json web/pnpm-lock.yaml web/pnpm-workspace.yaml ./ +RUN pnpm install --frozen-lockfile + +COPY web . + +# Reach the backend by its compose service name rather than through nginx. +ENV VITE_API_PROXY_TARGET=http://fastapi:8000 + +EXPOSE 5173 + +CMD ["pnpm", "dev"] diff --git a/web/README.md b/web/README.md new file mode 100644 index 00000000..3f25cd63 --- /dev/null +++ b/web/README.md @@ -0,0 +1,181 @@ +# web + +The Nuspace frontend. Serves `http://localhost` in development and everything +CI deploys to production. + +The previous app in `frontend/` is still in the repository for one release as a +fallback and is no longer routed to or built. Deleting it is a follow-up. + +**Stack:** Vite 8 · React 19 · TypeScript 7 · TanStack Router (file-based) · +TanStack Query · Tailwind v4 · shadcn/ui on Base UI. + +## Running it + +The app needs the backend, so bring up the stack rather than running `pnpm dev` +standalone: + +```sh +cd ../infra +cp .env.example .env # fill TELEGRAM_BOT_TOKEN +docker compose up --build +``` + +| | | +| ----------------------- | -------- | +| `http://localhost` | this app | +| `http://localhost:8080` | pgAdmin | + +To compare against the old app, point `$proxy_upstream` in +`infra/nginx/nginx.dev.conf` back at `frontend:5173` and reload nginx. + +To run outside Docker, `pnpm dev` serves on 5173 and proxies `/api` to +`http://localhost` (override with `VITE_API_PROXY_TARGET`). + +### HMR can go stale — check before you trust what you see + +This repo runs under **Docker Desktop on Linux**, which shares the host +filesystem into a VM. That layer desynchronizes: new files appear, but files +edited **in place** can keep serving old content to the container for hours. +Vite never fires, because from inside the container nothing changed. + +The failure is quiet and it will waste your time — the page looks fine, it is +just not your code. A route returning 200 proves nothing either, since the dev +server serves the same `index.html` for every path. + +Confirm the container actually sees an edit: + +```sh +docker compose exec web grep -c somethingYouJustTyped /app/src/path/to/file.tsx +``` + +`docker compose restart web` resyncs the mount. The permanent fix is to run the +stack on the native Docker daemon (`docker context use default`) instead of the +Docker Desktop VM, which uses real bind mounts and has no such layer. + +### Signing in locally + +`MOCK_KEYCLOAK=true` is the default, so no real credentials are needed: + +``` +http://localhost/api/login?mock_user=alice +``` + +`mock_user` accepts a number, an email, a sub or a first name. The people +`backend/fixtures/dev/seed_campus.sql` sets up cover every role worth testing: + +| `mock_user` | role | +| ----------- | ------------------------------------------- | +| `alice` | admin | +| `bob` | student, and on the opportunities allowlist | +| `charlie` | Head (`boss`) | +| `dana` | Executive (`capo`) | +| `erik` | Member (`soldier`) | +| `hassan` | student, no special access | + +## Commands + +```sh +pnpm dev # dev server +pnpm build # typecheck + production build +pnpm typecheck # tsc -b --noEmit over the project references +pnpm lint # oxlint, including type-aware rules +pnpm format # prettier, sorts Tailwind classes +pnpm test # node --test over src/**/*.test.ts +pnpm api:generate # regenerate src/api/schema.d.ts from the backend +pnpm api:check # fail if the committed schema is stale +``` + +## API types are generated, not written + +`src/api/schema.d.ts` is generated from the backend's OpenAPI document and +committed. Nothing about a request or response is typed by hand, so a backend +change that breaks the contract fails `tsc` instead of surfacing at runtime. + +Regenerate from an offline backend export: + +```sh +cd ../backend +uv run python scripts/export_openapi.py --output /tmp/nuspace-openapi.json +cd ../web +OPENAPI_URL=/tmp/nuspace-openapi.json pnpm api:generate +``` + +`pnpm api:check` runs the same generation and fails on any diff, so drift is +caught in CI. The exporter loads `infra/.env.example` as a dummy configuration +fixture and constructs the schema without starting Postgres, Redis, +Meilisearch, RabbitMQ, GCS, or the bot. + +The generator runs in an isolated environment pinned to TypeScript 6. +`openapi-typescript` emits through the compiler's `ts.factory` AST API, which +TypeScript 7 does not expose. Its output is plain text, so the version that +produced it does not matter. + +**The one exception is `/me`.** The backend declares it `user: Dict[str, Any]`, +so OpenAPI reports an opaque index signature and codegen cannot describe it. +`src/features/auth/schema.ts` parses it with zod, reusing the generated +`UserRole` union so a backend role change still breaks the build. That file is +the only place a payload shape is written by hand — everywhere else, use the +generated types. + +## Media uploads + +Bytes never pass through our API. `useMediaUpload()` (in `src/features/media/`) +runs the three-step presigned flow: ask the backend to sign one PUT per file, +send the bytes straight at the returned URL, and let the bucket tell the backend +what landed. The `x-goog-meta-*` headers are part of the signature and are +replayed exactly as issued — re-deriving `Content-Type` from the `File` is +enough for GCS to answer 403. + +Two things about step three are easy to get wrong: + +- **Locally the Media row already exists before the bytes do.** With + `USE_GCS_EMULATOR=true` there is no Pub/Sub, so the backend creates the row + while signing, and the upload URL points at its own `/bucket/local-upload` + proxy. In production the row appears only after GCS notifies + `POST /bucket/gcs-hook`, which happens _after_ the PUT resolves. A refetch + immediately on success can legitimately come back without the new image, and + only production will show you that. +- **Formats are not interchangeable, and the backend filters on them.** Events + return `carousel` only; communities return `profile` and `banner`. Asking an + entity for a format it never carries renders nothing, with no error — the + generated types cannot catch it, because the value is valid either way. Go + through `selectMedia()` in `src/features/media/select.ts`, where that mapping + is written down. + +## Conventions + +- **One QueryClient**, exported from `src/app/query-client.ts` and passed to + both `QueryClientProvider` and the router context. Never construct another. +- **Auth is a route concern.** `routes/_app.tsx` guards everything beneath it in + `beforeLoad`. Pages assume a signed-in user via `useCurrentUser()`. +- **Permissions go through `usePermissions()`**, not inline role checks. +- **Datetimes cross the boundary in `lib/datetime.ts`.** The backend reads a + naive datetime as Almaty local time; that conversion lives in one file. +- **Query keys come from `src/api/query-keys.ts`**, so invalidation can target a + whole subtree. +- **Loading, error and empty states** go through `components/query-boundary.tsx`. +- **Use the design tokens** (`bg-muted`, `text-foreground`, `border-border`), + not raw palette classes like `bg-gray-100`. The tokens already handle dark + mode; palette classes need a hand-written `dark:` variant for every use and + drift out of sync. +- **Never build a Tailwind class by interpolation** (`` `grid-cols-${n}` ``). + The scanner cannot see it and the class is silently dropped from the + stylesheet. + +## Tests + +`node --test`, no test framework. Node 22+ strips TypeScript natively, so a +`.test.ts` file next to the module it covers runs with no build step and no +dependency. `tsconfig.test.json` gives those files Node's types; the app project +excludes them so app code cannot reach for `node:` builtins by accident. + +There is one suite, on `features/communities/url-validation.ts`, ported from the +old app along with the module. It is the interesting kind of test: `new URL` +accepts `https://wtf://t.me/x` and reports `wtf:` as the host, so a naive +protocol check passes something that goes nowhere near Telegram. + +## Linting + +Oxlint, not ESLint. typescript-eslint refuses to load under TypeScript 7 and +has no supporting release yet, while oxlint parses with its own Rust parser and +its type-aware mode tracks TS 7 via `oxlint-tsgolint`. diff --git a/web/components.json b/web/components.json new file mode 100644 index 00000000..15addee8 --- /dev/null +++ b/web/components.json @@ -0,0 +1,25 @@ +{ + "$schema": "https://ui.shadcn.com/schema.json", + "style": "base-nova", + "rsc": false, + "tsx": true, + "tailwind": { + "config": "", + "css": "src/index.css", + "baseColor": "neutral", + "cssVariables": true, + "prefix": "" + }, + "iconLibrary": "lucide", + "rtl": false, + "aliases": { + "components": "@/components", + "utils": "@/lib/utils", + "ui": "@/components/ui", + "lib": "@/lib", + "hooks": "@/hooks" + }, + "menuColor": "default", + "menuAccent": "subtle", + "registries": {} +} diff --git a/web/index.html b/web/index.html new file mode 100644 index 00000000..b3435a9c --- /dev/null +++ b/web/index.html @@ -0,0 +1,17 @@ + + + + + + + + Nuspace + + +
+ + + diff --git a/web/package.json b/web/package.json new file mode 100644 index 00000000..e07d9e23 --- /dev/null +++ b/web/package.json @@ -0,0 +1,58 @@ +{ + "name": "web", + "private": true, + "version": "0.0.1", + "type": "module", + "packageManager": "pnpm@11.15.0", + "scripts": { + "dev": "vite", + "build": "tsc -b && vite build", + "lint": "oxlint --type-aware", + "format": "prettier --write \"**/*.{ts,tsx,css,json,md}\"", + "format:check": "prettier --check \"**/*.{ts,tsx,css,json,md}\"", + "typecheck": "tsc -b --noEmit", + "test": "node --test \"src/**/*.test.ts\"", + "preview": "vite preview", + "api:generate": "node scripts/generate-api-types.mjs", + "api:check": "node scripts/generate-api-types.mjs --check", + "lint:fix": "oxlint --type-aware --fix" + }, + "dependencies": { + "@base-ui/react": "^1.6.0", + "@fontsource-variable/onest": "^5.3.0", + "@hookform/resolvers": "^5.4.0", + "@tailwindcss/vite": "^4", + "@tanstack/react-query": "^5.101.4", + "@tanstack/react-router": "^1.170.18", + "class-variance-authority": "^0.7.1", + "clsx": "^2.1.1", + "lucide-react": "^1.26.0", + "openapi-fetch": "^0.17.0", + "react": "^19.2.6", + "react-dom": "^19.2.6", + "react-hook-form": "^7.82.0", + "react-markdown": "^10.1.0", + "remark-gfm": "^4.0.1", + "shadcn": "^4.14.1", + "sonner": "^2.0.7", + "tailwind-merge": "^3.6.0", + "tailwindcss": "^4", + "tw-animate-css": "^1.4.0", + "zod": "^4.4.3" + }, + "devDependencies": { + "@tanstack/react-query-devtools": "^5.101.4", + "@tanstack/react-router-devtools": "^1.167.0", + "@tanstack/router-plugin": "^1.168.23", + "@types/node": "^24", + "@types/react": "^19", + "@types/react-dom": "^19", + "@vitejs/plugin-react": "^6", + "oxlint": "^1.75.0", + "oxlint-tsgolint": "^7.0.2001", + "prettier": "^3.8.3", + "prettier-plugin-tailwindcss": "^0.8.0", + "typescript": "~7.0.2", + "vite": "^8" + } +} diff --git a/web/pnpm-lock.yaml b/web/pnpm-lock.yaml new file mode 100644 index 00000000..b9000e0a --- /dev/null +++ b/web/pnpm-lock.yaml @@ -0,0 +1,5374 @@ +lockfileVersion: '9.0' + +settings: + autoInstallPeers: true + excludeLinksFromLockfile: false + +importers: + + .: + dependencies: + '@base-ui/react': + specifier: ^1.6.0 + version: 1.6.0(@types/react@19.2.17)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) + '@fontsource-variable/onest': + specifier: ^5.3.0 + version: 5.3.0 + '@hookform/resolvers': + specifier: ^5.4.0 + version: 5.4.0(react-hook-form@7.82.0(react@19.2.8)) + '@tailwindcss/vite': + specifier: ^4 + version: 4.3.3(vite@8.1.5(@types/node@24.13.3)(jiti@2.7.0)) + '@tanstack/react-query': + specifier: ^5.101.4 + version: 5.101.4(react@19.2.8) + '@tanstack/react-router': + specifier: ^1.170.18 + version: 1.170.18(react-dom@19.2.8(react@19.2.8))(react@19.2.8) + class-variance-authority: + specifier: ^0.7.1 + version: 0.7.1 + clsx: + specifier: ^2.1.1 + version: 2.1.1 + lucide-react: + specifier: ^1.26.0 + version: 1.26.0(react@19.2.8) + openapi-fetch: + specifier: ^0.17.0 + version: 0.17.0 + react: + specifier: ^19.2.6 + version: 19.2.8 + react-dom: + specifier: ^19.2.6 + version: 19.2.8(react@19.2.8) + react-hook-form: + specifier: ^7.82.0 + version: 7.82.0(react@19.2.8) + react-markdown: + specifier: ^10.1.0 + version: 10.1.0(@types/react@19.2.17)(react@19.2.8)(supports-color@10.2.2) + remark-gfm: + specifier: ^4.0.1 + version: 4.0.1(supports-color@10.2.2) + shadcn: + specifier: ^4.14.1 + version: 4.14.1(supports-color@10.2.2)(typescript@7.0.2) + sonner: + specifier: ^2.0.7 + version: 2.0.7(react-dom@19.2.8(react@19.2.8))(react@19.2.8) + tailwind-merge: + specifier: ^3.6.0 + version: 3.6.0 + tailwindcss: + specifier: ^4 + version: 4.3.3 + tw-animate-css: + specifier: ^1.4.0 + version: 1.4.0 + zod: + specifier: ^4.4.3 + version: 4.4.3 + devDependencies: + '@tanstack/react-query-devtools': + specifier: ^5.101.4 + version: 5.101.4(@tanstack/react-query@5.101.4(react@19.2.8))(react@19.2.8) + '@tanstack/react-router-devtools': + specifier: ^1.167.0 + version: 1.167.0(@tanstack/react-router@1.170.18(react-dom@19.2.8(react@19.2.8))(react@19.2.8))(@tanstack/router-core@1.171.15)(csstype@3.2.3)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) + '@tanstack/router-plugin': + specifier: ^1.168.23 + version: 1.168.23(@tanstack/react-router@1.170.18(react-dom@19.2.8(react@19.2.8))(react@19.2.8))(rolldown@1.1.5)(supports-color@10.2.2)(vite@8.1.5(@types/node@24.13.3)(jiti@2.7.0)) + '@types/node': + specifier: ^24 + version: 24.13.3 + '@types/react': + specifier: ^19 + version: 19.2.17 + '@types/react-dom': + specifier: ^19 + version: 19.2.3(@types/react@19.2.17) + '@vitejs/plugin-react': + specifier: ^6 + version: 6.0.4(vite@8.1.5(@types/node@24.13.3)(jiti@2.7.0)) + oxlint: + specifier: ^1.75.0 + version: 1.75.0(oxlint-tsgolint@7.0.2001) + oxlint-tsgolint: + specifier: ^7.0.2001 + version: 7.0.2001 + prettier: + specifier: ^3.8.3 + version: 3.9.6 + prettier-plugin-tailwindcss: + specifier: ^0.8.0 + version: 0.8.1(prettier@3.9.6) + typescript: + specifier: ~7.0.2 + version: 7.0.2 + vite: + specifier: ^8 + version: 8.1.5(@types/node@24.13.3)(jiti@2.7.0) + +packages: + + '@babel/code-frame@7.29.7': + resolution: {integrity: sha512-Aup7aUOfpbAUg2ROOJN6Iw5f9DMBlzu0mIkm/malLQFN/YQgO48wCj0Kxa3sEHJvPVFg7siR+qRInwXd2qhQKw==} + engines: {node: '>=6.9.0'} + + '@babel/compat-data@7.29.7': + resolution: {integrity: sha512-locTkQyKvwIEgBzVrn8693ebc97F2U8ZHjbXwDXJ5Fn2TCpNwTlKcaKLkdHop5c/icOFE7qt7Q9JC5hnKNa6Gg==} + engines: {node: '>=6.9.0'} + + '@babel/core@7.29.7': + resolution: {integrity: sha512-RgHBCvtjbOK2gXSNBNIkNoEc9qoVEtau3hj8gEqKQuL3HZAibKarWFEI3Lfm6EYKkLalOh8eSrj9b+ch9H/VBA==} + engines: {node: '>=6.9.0'} + + '@babel/generator@7.29.7': + resolution: {integrity: sha512-DkXD5OJQaAQIdZ1bt3UZdEnHAn9Imd3IVBdX03UFe+ony9Ojw5pzr9YVKGDY1jt+Gcn/FnGkNf8r+Vj5NOJWtQ==} + engines: {node: '>=6.9.0'} + + '@babel/helper-annotate-as-pure@7.29.7': + resolution: {integrity: sha512-OoK6239jHPuSQOoS0kfTVKn0b/rVTk0seKq4Gd2UMLtmOVLjDC0ki3e+c90Trqv2gMfvJFqkiljrr568+qddiw==} + engines: {node: '>=6.9.0'} + + '@babel/helper-compilation-targets@7.29.7': + resolution: {integrity: sha512-wem6WaBj4NaVYVdNhLPPVacES6ZJ+KBBfSkTMD3YZxbP3rm3Di85tJU5ljaUNhaOynt+Aj0xruhYuzQBt8n71g==} + engines: {node: '>=6.9.0'} + + '@babel/helper-create-class-features-plugin@7.29.7': + resolution: {integrity: sha512-IY3ZD9Tmooqr3TUhc3DUWxiuo8xx1DWLhd5M7hQ+ZWJamqM2BbalrBJb2MisSLoYorOj75U03qULCxQTY9r3hg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + + '@babel/helper-globals@7.29.7': + resolution: {integrity: sha512-3nQVUAtvkKH9zahfWgw96Jc/uFOmjACE1kQz82E2lqWmHBgjzbNlsC22nuQTfahmWeQtTq5nQ/4Nnd2A1wj4zA==} + engines: {node: '>=6.9.0'} + + '@babel/helper-member-expression-to-functions@7.29.7': + resolution: {integrity: sha512-j+7JYmk1JYDtACIGj0QJqqWZjoUpMoEikQGADMaHgCMCSDqd2+P32rfcibUNrGOMWrlzK1WJBdxrB3JJQZwWtg==} + engines: {node: '>=6.9.0'} + + '@babel/helper-module-imports@7.29.7': + resolution: {integrity: sha512-ejHwrQQYcm9xnTivShn2IDOlIzInN34AXskvq9QicvCtEzq1Vzclu/tKF8Jq1Cg8JG2GL6/EmjgsCT7lXepE3g==} + engines: {node: '>=6.9.0'} + + '@babel/helper-module-transforms@7.29.7': + resolution: {integrity: sha512-UPUVSyXbOh627KiCIGQSgwWzGeBKLkaJ9PJEdrngIwMSzxLR4jS4+f1f1jb7VzBbg8nFLaYotvVPFCTqdrmTAg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + + '@babel/helper-optimise-call-expression@7.29.7': + resolution: {integrity: sha512-+kmGVjcT9RGYzoDwdwEqEvGgKe3BYq+O1iGzjFubaNgZHwYHP6lsF2Yghf4kEuv9BV7tYDZ913aBW9am6YKong==} + engines: {node: '>=6.9.0'} + + '@babel/helper-plugin-utils@7.29.7': + resolution: {integrity: sha512-G7sHYigPY17oO5SYWnfD/0MTBwVR781S/JI643e/JhUYgVgWE/61SoW3NH9KWUKyKq5LVh3npif99Wkt6j86Jw==} + engines: {node: '>=6.9.0'} + + '@babel/helper-replace-supers@7.29.7': + resolution: {integrity: sha512-atfGXWSeCiF4DnKZIfmJfQRkSw9b9gNNXR1kqKjbhG4pGYCOnkp8OcTB8E3NXjBu8NpheSnOeNKz8KT7UNFTmQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + + '@babel/helper-skip-transparent-expression-wrappers@7.29.7': + resolution: {integrity: sha512-brcMGQaVzIeUb+6/bs1Av0f8YuNNjKY2JyvfRCsFuFsdKccEQ5Ges2y74D74NZ1Rz8lKJ9ksJkfqwQFJ/iNEyQ==} + engines: {node: '>=6.9.0'} + + '@babel/helper-string-parser@7.29.7': + resolution: {integrity: sha512-Pb5ijPrZ89GDH8223L4UP8i6QApWxs04RbPQJTeWDV0/keR2E36MeKnyr6LYmUUvqRRI+Iv87SuF1W6ErINzYw==} + engines: {node: '>=6.9.0'} + + '@babel/helper-validator-identifier@7.29.7': + resolution: {integrity: sha512-qehxGkRj55h/ff8EMaJ+cYhyaKlHIxqYDn682wQD7RNp9UujOQsHog2uS0r2vzr4pW+sXf90NeeayjcNaX3fFg==} + engines: {node: '>=6.9.0'} + + '@babel/helper-validator-option@7.29.7': + resolution: {integrity: sha512-N9ZErrD+yW5geCDtBqnOoxmR8+tNKiGuxKlDpuJxfsqpa2dFcexaziGAE/qoHLiDDreVNMupxGmSoNlyvsA3gw==} + engines: {node: '>=6.9.0'} + + '@babel/helpers@7.29.7': + resolution: {integrity: sha512-1k2lAGRMfHTcwuNYcCNUmaUffmQv8KWMfh2iJUUeRlwlwH4FdNG7mfPI10NPfLHJFThE4Tyr4mv7kTNZOiPuBg==} + engines: {node: '>=6.9.0'} + + '@babel/parser@7.29.7': + resolution: {integrity: sha512-hnORnjP/1P/zFEndoeX+n+t1RwWRJiJpM/jO7FW32Kn9r5+sJB2JWOdYo4L6k78j15eCwY3Gm/7364B1EMwtNg==} + engines: {node: '>=6.0.0'} + hasBin: true + + '@babel/plugin-syntax-jsx@7.29.7': + resolution: {integrity: sha512-TSu8+mHCoEaaCDEZ0I3+6mvTBYR4PCxQwf2z9/r5Tbztv6NaLR3B9thGTTxX2WGuGHJqRiAbKPeGTJ5XWXVg6A==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-syntax-typescript@7.29.7': + resolution: {integrity: sha512-ngr+82Sh0xMz25TPCZi+nC2iTzjfCdWS2ONXTp/PtSCHCgaCNBpdMqgvJ2ccdLlClVZ7sisIgB914j/JFe+RZA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-modules-commonjs@7.29.7': + resolution: {integrity: sha512-j0vCldybPC5b5dwCQOJ21uKtHzt7hxLygJTg9eF1ScfaikEDNfzn94XoW5Fi+seBR0nCyL23xaBFFkq7dTM8XQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-typescript@7.29.7': + resolution: {integrity: sha512-jK52h8LaLc7JarhQV2ofeFMts4H7vnOXnqZNA6fYglBTZewRBE51KWt3BUltW1P+KoPsYkHoJeXePuz4zo2LMw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/preset-typescript@7.29.7': + resolution: {integrity: sha512-/Foi8vKY2EVbed/1eZx0gJEEwHAIxogrySI7rULcRIvhZzbvoE/b5qG5Ghc0WKAFKOHA9SD1x7RsFlOYdutIiQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/runtime@7.29.7': + resolution: {integrity: sha512-Nq8OhGWiZIZGV6hLHoyAKLLcJihP/xFeBMGJoUrxTX2psI8dCifzLhZISFb+VWS3wFMRDmCGw5R+dOySCqPLhw==} + engines: {node: '>=6.9.0'} + + '@babel/template@7.29.7': + resolution: {integrity: sha512-puq+Gf35oI24FeN11LkoUQFqv9uwNeWpxXZi/Ji3rRIoKAzKnxRaZ+Gkj0vKS9ZCiTESfng1N9LyOyXvo+m+Gg==} + engines: {node: '>=6.9.0'} + + '@babel/traverse@7.29.7': + resolution: {integrity: sha512-EhlfNQtZ+NK22w5BM61ciuiq1m58ed33Wr1Xan//ZRTy6hgjnwyCffRYwzsGXdASJSUJ1guZILsErh1eQcl+zw==} + engines: {node: '>=6.9.0'} + + '@babel/types@7.29.7': + resolution: {integrity: sha512-4zBIxpPzowiZpusoFkyGVwakdRJUyuH5PxQ/PrqghfdFWWasvnCdPfQXHrenDai+gyLARulZjZowCOj6fjT4pA==} + engines: {node: '>=6.9.0'} + + '@base-ui/react@1.6.0': + resolution: {integrity: sha512-/jzjTWJYXhRFO45Bev9lc3cHbmjzCMpUqbMZ2AgKy/z25mY9B6shGSNcXcjQar9n5doM0KYW1W8fcFv2jZBuMw==} + engines: {node: '>=14.0.0'} + peerDependencies: + '@date-fns/tz': ^1.2.0 + '@types/react': ^17 || ^18 || ^19 + date-fns: ^4.0.0 + react: ^17 || ^18 || ^19 + react-dom: ^17 || ^18 || ^19 + peerDependenciesMeta: + '@date-fns/tz': + optional: true + '@types/react': + optional: true + date-fns: + optional: true + + '@base-ui/utils@0.3.1': + resolution: {integrity: sha512-gFFiltORVmW/N6IILTGxizP3PBpVpysqML1ALY5Vk0mH+7faVkCknOU31goYHN5Aoek2dkjxva1XOD2Ce9WuIg==} + peerDependencies: + '@types/react': ^17 || ^18 || ^19 + react: ^17 || ^18 || ^19 + react-dom: ^17 || ^18 || ^19 + peerDependenciesMeta: + '@types/react': + optional: true + + '@dotenvx/dotenvx@1.75.1': + resolution: {integrity: sha512-/BITOC9dmS/edY2zQwZNicQ059O6RKabtQfyEafV0nGtfYRNHYy1DIPiYVcov40+tob9hfmBnbR963dS+EQ1DQ==} + hasBin: true + + '@dotenvx/primitives@0.8.0': + resolution: {integrity: sha512-VYJy0uhFm9zTJ1TxBaW/pA8bjbOM/OttaNMwZ1RHG4JKyRG7DhSdiqD1ipQoAyoD22olUtxbP78W9xY3Wd11bg==} + + '@emnapi/core@1.11.1': + resolution: {integrity: sha512-RSvbQmHzdKzNsLYa/wHrbc3KN4sYLKAdPZxqiM2HATqv/SBk2/ENSHpvXGaLOMcsAyz0poEGqkmmKYG3OWiJEQ==} + + '@emnapi/runtime@1.11.1': + resolution: {integrity: sha512-vgj7R3y3Wgx24IQaGPA/R6YFXLHVMOZ0uVEyIQPaWs+rd1AzfEMXlAC22FYwO1XkKR6NPsq7mUandH8oIRdZFw==} + + '@emnapi/wasi-threads@1.2.2': + resolution: {integrity: sha512-c95qOXkHdydNKhscBTebqEC1CVAZpyqOfVfBzQ1qgzyl3gfeldUjIggDbIZgDKsHLgnsM+igH7TJ/eAasaVuMA==} + + '@floating-ui/core@1.8.0': + resolution: {integrity: sha512-0CIZ5itps/8x7BG8dEIhs53BvCUH2PCoogtakwRTut+Arm58sJooJ0AuZhLw2HJYIR5cMLNPBSS728sPho2khQ==} + + '@floating-ui/dom@1.8.0': + resolution: {integrity: sha512-yXSrzeHZBTZadLOlfyhCkJHNeLJnHRnRInwdZ40L7ZiaAtrBwoYlsDrX3v5zB1Utk7CLfzcOVnVVWoXEky7Ceg==} + + '@floating-ui/react-dom@2.1.9': + resolution: {integrity: sha512-JDjEFGCpImxDCA7JJKviA0M9+RtmJdj0m/NVU5IMgBK+AmZouAQQ7/+2GLH0GXXY0YMw9oXPB8hKdbPYg5QLYg==} + peerDependencies: + react: '>=16.8.0' + react-dom: '>=16.8.0' + + '@floating-ui/utils@0.2.12': + resolution: {integrity: sha512-HpCo8tmWzLVad5s2d19EhAz5zqrrQ6s69qd6moPMQvkOuSwDT1YgRfWSVuc4ennqrgv3OHppiOGMQ7oC13yIww==} + + '@fontsource-variable/onest@5.3.0': + resolution: {integrity: sha512-K4Es0F3M+iFnVY5mfunimhPSk1Uvl3eyovQVWjtf8oS00a7WSrSg2BD5Bh/pAcAZUe4ocV4M08dg5tvCULBJ7Q==} + + '@hono/node-server@1.19.15': + resolution: {integrity: sha512-Za2ai6TLdKjUvnur+eenO6nuYYipVAEhyCAdaV8IRvmU9kK8crOZUSYvIXn72E4f8fJqyAbpcJuTsYYmZp9Deg==} + engines: {node: '>=18.14.1'} + peerDependencies: + hono: ^4 + + '@hookform/resolvers@5.4.0': + resolution: {integrity: sha512-EIsqr/t/qbinPIhGjMdtvutIN1Kk4uwbROE9/UQ93CAVGR7GkA7Y92+fX80OzXi/OB67jVFYwKGO1WzkxmkFZw==} + peerDependencies: + react-hook-form: ^7.55.0 + + '@jridgewell/gen-mapping@0.3.13': + resolution: {integrity: sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==} + + '@jridgewell/remapping@2.3.5': + resolution: {integrity: sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==} + + '@jridgewell/resolve-uri@3.1.2': + resolution: {integrity: sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==} + engines: {node: '>=6.0.0'} + + '@jridgewell/sourcemap-codec@1.5.5': + resolution: {integrity: sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==} + + '@jridgewell/trace-mapping@0.3.31': + resolution: {integrity: sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==} + + '@modelcontextprotocol/sdk@1.29.0': + resolution: {integrity: sha512-zo37mZA9hJWpULgkRpowewez1y6ML5GsXJPY8FI0tBBCd77HEvza4jDqRKOXgHNn867PVGCyTdzqpz0izu5ZjQ==} + engines: {node: '>=18'} + peerDependencies: + '@cfworker/json-schema': ^4.1.1 + zod: ^3.25 || ^4.0 + peerDependenciesMeta: + '@cfworker/json-schema': + optional: true + + '@napi-rs/wasm-runtime@1.1.6': + resolution: {integrity: sha512-ZLv/JdUfkvOy9eCnnBaGfiO+XimbjebAeO+MRQqD/B+FR1tnRN0tpKSJHRbE8sFfS6aqsXZ67TQjfwfsxULVbg==} + peerDependencies: + '@emnapi/core': ^1.7.1 + '@emnapi/runtime': ^1.7.1 + + '@nodelib/fs.scandir@2.1.5': + resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==} + engines: {node: '>= 8'} + + '@nodelib/fs.stat@2.0.5': + resolution: {integrity: sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==} + engines: {node: '>= 8'} + + '@nodelib/fs.walk@1.2.8': + resolution: {integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==} + engines: {node: '>= 8'} + + '@oxc-project/types@0.139.0': + resolution: {integrity: sha512-r9gHphtCs+1M7J0pw6Sn/hh/Wpa/iQrOOkrNAlVLF/gHq+/CJmHIWKKUUhdWjcD6CIa8idarspCsASiXCXvFUw==} + + '@oxlint-tsgolint/darwin-arm64@7.0.2001': + resolution: {integrity: sha512-CUJEdbSZ54+Xy9OXqOhWLTKZKV0BBiV7C2i/ygyVmXtkUNXx5YCzN8DpSSshTAKktoL7S+tnQ/ftFG/i7X896w==} + cpu: [arm64] + os: [darwin] + + '@oxlint-tsgolint/darwin-x64@7.0.2001': + resolution: {integrity: sha512-pXfBb5BqONCcgrXQNUZWXgiYmRSWJzd97S8i41VVOh6ut0tyo+cJ5FKFpczDHxiVNfj/3e7c9B4MtztNdpIVCw==} + cpu: [x64] + os: [darwin] + + '@oxlint-tsgolint/linux-arm64@7.0.2001': + resolution: {integrity: sha512-roP7zujb/QDPzDwEKsFFpzNHHy91/Y7oX9vQXk78ekyZtcQj1QXDIMH33gjDdHBfRl4K9pZ36xhRgrP4Zr+R8A==} + cpu: [arm64] + os: [linux] + + '@oxlint-tsgolint/linux-x64@7.0.2001': + resolution: {integrity: sha512-UDezNqdECVmngu2TPnjaS1YoAmcTaBoI5lV9vk3VahBxoi+I5r9k3iJTT7qZoYWOXTD/7T7bNcwRgrocR6BscQ==} + cpu: [x64] + os: [linux] + + '@oxlint-tsgolint/win32-arm64@7.0.2001': + resolution: {integrity: sha512-uJZhqB6pdXLuN+AD1F5082byyQti/NPmJA77GtcFlmT2HzRelqbNls3SaIqxpjdFgvSBF9g0yOKGBkGFg7kX8Q==} + cpu: [arm64] + os: [win32] + + '@oxlint-tsgolint/win32-x64@7.0.2001': + resolution: {integrity: sha512-FkDRm8hx9OwzGQqyWG1tO5QrTLRApff9DzSgpz9QZau37BR8d1VYKOxMLGf6shPZntJFoTwIIJYT68VndYDCog==} + cpu: [x64] + os: [win32] + + '@oxlint/binding-android-arm-eabi@1.75.0': + resolution: {integrity: sha512-lutovtFzJqlRaqpZrCqSSGaHZzl9nIxxpjLzhSRLunN6dCLylj0uzlCyQGaQDIys7rrv8kVXiFO+R4Zpn0bX7g==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm] + os: [android] + + '@oxlint/binding-android-arm64@1.75.0': + resolution: {integrity: sha512-hXI0hDgHkw4w5nfru72aG7y+2iQJmC4waH/KV6H/hbgA6yAP5jYNx0P9yug15Hs0tWl/+mda3Jjn/2gmDT48tw==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [android] + + '@oxlint/binding-darwin-arm64@1.75.0': + resolution: {integrity: sha512-D91BWbK/dMYfCcrghspPIuKs2D9LF4Z/OabVSQjw1AO6PWxArD7teDA48bm0ySFqWDaPVqmQRl5GMWNglTXyrQ==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [darwin] + + '@oxlint/binding-darwin-x64@1.75.0': + resolution: {integrity: sha512-02mpwzf12BonZ6PT0TuQoomvEh2kVl2WGBIKWezCyToIS+rYkQZ6GXnARBAl9A4Ovm2V+Xe7M4KretyqmmcnJQ==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [darwin] + + '@oxlint/binding-freebsd-x64@1.75.0': + resolution: {integrity: sha512-qZJgLnDaBsiL5YESx2t/TZ8eXkL9fEkKoXEdzegROhlz9A0lgyGnZ0dAzJrh7LJAHQl2K9RdRueN2s/9N7+odg==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [freebsd] + + '@oxlint/binding-linux-arm-gnueabihf@1.75.0': + resolution: {integrity: sha512-7XlaWA5BJD3XpCfrEqjEe6Zseeb14S7QGa304XfwKignRaKQ+eIj775BQ7nIslggWickl4IsPUFqJ+/gAyNHVg==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm] + os: [linux] + + '@oxlint/binding-linux-arm-musleabihf@1.75.0': + resolution: {integrity: sha512-av6Tpv8yrcMMMOadOqENBhlsLRcGFXXwoQ0hzHhsmS9FJ4Wioy8we427GbcMe2XTxmL2e60T67H1Dyr3up+tAA==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm] + os: [linux] + + '@oxlint/binding-linux-arm64-gnu@1.75.0': + resolution: {integrity: sha512-WcUhd8fHT5plrA14lANevl+hOl815mVI5t2hU21oFWrZKFXIVV/Sr4rWQV0NzSvzBupbMLNc5ErEA6Ehxh5jMg==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [linux] + libc: [glibc] + + '@oxlint/binding-linux-arm64-musl@1.75.0': + resolution: {integrity: sha512-UWzp5wRHFe/ESO3+eEaxXsTkYTGLYjnTsi/I5neEacXSItQ6WNleapfOAeA4x2b8nyhJ4uQxqvtv9pHv8kWJtQ==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [linux] + libc: [musl] + + '@oxlint/binding-linux-ppc64-gnu@1.75.0': + resolution: {integrity: sha512-XEVRwGMLKCUKrvhLAz4F6AIh8MJrQVdSZtAmPpRZt9tGPsUnamPOcl3dS/ZQzJnar/Ymgc//+xho0L60Emzuxg==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [ppc64] + os: [linux] + libc: [glibc] + + '@oxlint/binding-linux-riscv64-gnu@1.75.0': + resolution: {integrity: sha512-mAG4DUXqfLC8cTjMD2kt3jDmVzFREYtDyeLNdLdsCcBc4Zbl2EMuiFektGBilQwkNjYnMvCqJs55U+Hyb+b+jw==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [riscv64] + os: [linux] + libc: [glibc] + + '@oxlint/binding-linux-riscv64-musl@1.75.0': + resolution: {integrity: sha512-95hrAvriAlI+pekSomTFIn0+bawMDlDwTNVmdjsFusTHyL2JWh7TWvRNG/Lkim72uN8OiCcO9wcaC6omLP5E3w==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [riscv64] + os: [linux] + libc: [musl] + + '@oxlint/binding-linux-s390x-gnu@1.75.0': + resolution: {integrity: sha512-4b6f2+FrtruAESrCqIKcrarzfrSx+wk2QNcp+RT91/Prc+pMQMAfyZ1rG1c3tFQNl8Bc616tx40uNXyxNBRPbQ==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [s390x] + os: [linux] + libc: [glibc] + + '@oxlint/binding-linux-x64-gnu@1.75.0': + resolution: {integrity: sha512-nshAhrUvXFUWOvqQ2soIw7HFNWvpvEV4o0cYSqPtzLiPF5gKyYTDOOTJ6Rn8g8K/iGvPIrbDA4v8+5MvnjJrrg==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [linux] + libc: [glibc] + + '@oxlint/binding-linux-x64-musl@1.75.0': + resolution: {integrity: sha512-e4jNxLKnxLC6sYBQRxrI2pgIIxnmMtF8U/VwNYcjTT/CLS+spH624cYVnj07bTKwaEWT37/e025isOs6j/0xqA==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [linux] + libc: [musl] + + '@oxlint/binding-openharmony-arm64@1.75.0': + resolution: {integrity: sha512-hZ2lH+1qLf/DiEP9UWuQTK2JWj/BgvMB4jhIV4SmNU1wfEiYYX4TynQyAZXx0j9X4qRYizAL042SKaV+8ynh4w==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [openharmony] + + '@oxlint/binding-win32-arm64-msvc@1.75.0': + resolution: {integrity: sha512-Ilj6PNzGDS3bCU0MSJH7Msh0NhH+T/mRp2shwg+q+GHeVlPwP5LEboW96aW+3kVKFk6zYZy1Xi5pZkqZh6X8KQ==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [win32] + + '@oxlint/binding-win32-ia32-msvc@1.75.0': + resolution: {integrity: sha512-QVit2nOEOiPhkmsrksPSkoGCdnZRNkspt8fwoYyP09te1VEbnSj4LAxua4rc8FKTmWkySVe05j8iz9GXYfF1AQ==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [ia32] + os: [win32] + + '@oxlint/binding-win32-x64-msvc@1.75.0': + resolution: {integrity: sha512-DSxnNkBUAYARPwJtR12Ig3deWr8w0H997xP6jy33i+e0SyYJw8FKuz4+cZtpmPEhQmvlPJE3X/2vNxDmLkd/rA==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [win32] + + '@rolldown/binding-android-arm64@1.1.5': + resolution: {integrity: sha512-lZg8fqIv2v7FF237bwMgzGZEJvGL79/s5knJ/i6FmsGF4XXlzccZ4jb+TrFIxtSSxFtIpdsgrPZeMk1I9AFcyQ==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [android] + + '@rolldown/binding-darwin-arm64@1.1.5': + resolution: {integrity: sha512-51Bnx9pNiMRKSUNtBfySkNJ9vMU9Hh3I1ozDd6gyPPYzaXCfnptUcEZxXGYFn+ul2dtcMUiqGR1Yai2K10uoTw==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [darwin] + + '@rolldown/binding-darwin-x64@1.1.5': + resolution: {integrity: sha512-Tm+gbfC0aHu1tBA/JvKQh32S0K6YgCHkiAF4/W6xX0K0RmNuc94VeK419dJoE65R5aRxmo+noZQSWrAMF6yb6g==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [darwin] + + '@rolldown/binding-freebsd-x64@1.1.5': + resolution: {integrity: sha512-JMzDKCCXq93YccG5gz3hvOs1oXRKAf0XYpfOS88e+wZrC8Iugj6j68867vrYZkvpDDpKn/KoKORThmchMpF6TA==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [freebsd] + + '@rolldown/binding-linux-arm-gnueabihf@1.1.5': + resolution: {integrity: sha512-uML21j2K5TfPGutKxub+M+nLjZIrWjXQ5Grx4lCe/nimTj9B4L63zHpjXLl4y0L3mcm2htEQIb06oCG/szerNw==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm] + os: [linux] + + '@rolldown/binding-linux-arm64-gnu@1.1.5': + resolution: {integrity: sha512-navSiuTMogvnQoZoM/v+l3ZWo50/NTwSHSzheABx/RCnmUPaKwq9qSo4Br2OYRs21+Fz8uFqITZM3H4opOB0/Q==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [linux] + libc: [glibc] + + '@rolldown/binding-linux-arm64-musl@1.1.5': + resolution: {integrity: sha512-lAryqH7IteztmCXQXk0etKj4wBQ7Gx5S6LjKhsgp9zb8I5bsuvU/2llH1hDQcjsFeqIsovMVN339/8pUDDBXxA==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [linux] + libc: [musl] + + '@rolldown/binding-linux-ppc64-gnu@1.1.5': + resolution: {integrity: sha512-fsK/sNBnxzBlL4O1JNrZakVQxPspqpED5dLtNsZS9oOKmtSpdNIzxH2kkol5HYTWJN47sE20ztMJPxfZ89qGOg==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [ppc64] + os: [linux] + libc: [glibc] + + '@rolldown/binding-linux-s390x-gnu@1.1.5': + resolution: {integrity: sha512-gLYb4BIadlfTOYT5gO503n8zQjXflgzpD0FcyKh0Mzx3rqCZKnHoJWV9xe1KXUJ5lx2JfcSHr/mhzS0PC/McAA==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [s390x] + os: [linux] + libc: [glibc] + + '@rolldown/binding-linux-x64-gnu@1.1.5': + resolution: {integrity: sha512-FjcpEKUyJygHgs1o50VYNvkt5+7Le/VEdYt0AkRpkL33MnyQfwr8l5mXwMmfmTbyMPr5vJLC+8/Gd9gXnwU1QQ==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [linux] + libc: [glibc] + + '@rolldown/binding-linux-x64-musl@1.1.5': + resolution: {integrity: sha512-Me+PfPI2TMeOQk0gYWfLQZtTktrmzbr8cDboqX83XKc7UrgAi55gF+2dUkWdxd19n55Essp2yeca+O9N5rBxHg==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [linux] + libc: [musl] + + '@rolldown/binding-openharmony-arm64@1.1.5': + resolution: {integrity: sha512-yc5WrLzXks6zCQfn9Oxr8pORKyl/pF+QjHmW/Qx3qu0oyrrNC+y2JLTU1E2rcWYAmzlnqngWXHQjy51VzW70Vw==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [openharmony] + + '@rolldown/binding-wasm32-wasi@1.1.5': + resolution: {integrity: sha512-VbQGPX2b4r48TAMIM2cjgluIM1HYutm4pcTEJsle7iEP7sB1dFqtPLBVbdLAZCxy1txCcPxf4QFf4v8uvltPqA==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [wasm32] + + '@rolldown/binding-win32-arm64-msvc@1.1.5': + resolution: {integrity: sha512-gHv82k63z4qpV5+Q1y/12KrK0ltWBukVDI8nZcbT7Tt/ZlOIVwppazneq0F93oDxTo3IgAMEDIoQh3E2n6mVsw==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [win32] + + '@rolldown/binding-win32-x64-msvc@1.1.5': + resolution: {integrity: sha512-tTZuDBPw85tEN5PQi1pnEBzDy0Z49HtScLAbD5t6hyeU92A95pRWaSMw1GZZi/RwgSgUIl0xrSlXIT/9QzvYSA==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [win32] + + '@rolldown/pluginutils@1.0.1': + resolution: {integrity: sha512-2j9bGt5Jh8hj+vPtgzPtl72j0yRxHAyumoo6TNfAjsLB04UtpSvPbPcDcBMxz7n+9CYB0c1GxQFxYRg2jimqGw==} + + '@sec-ant/readable-stream@0.4.1': + resolution: {integrity: sha512-831qok9r2t8AlxLko40y2ebgSDhenenCatLVeW/uBtnHPyhHOvG0C7TvfgecV+wHzIm5KUICgzmVpWS+IMEAeg==} + + '@sindresorhus/merge-streams@4.0.0': + resolution: {integrity: sha512-tlqY9xq5ukxTUZBmoOp+m61cqwQD5pHJtFY3Mn8CA8ps6yghLH/Hw8UPdqg4OLmFW3IFlcXnQNmo/dh8HzXYIQ==} + engines: {node: '>=18'} + + '@standard-schema/utils@0.3.0': + resolution: {integrity: sha512-e7Mew686owMaPJVNNLs55PUvgz371nKgwsc4vxE49zsODpJEnxgxRo2y/OKrqueavXgZNMDVj3DdHFlaSAeU8g==} + + '@tailwindcss/node@4.3.3': + resolution: {integrity: sha512-/T8IKEsf9VTU6tLjgC7+sv2mOPtQxzE2jMw7u4Tt40Tx+QSZxpzh95/H6cMKoja9XuW7iMdLJYBB0o9G1CaAgg==} + + '@tailwindcss/oxide-android-arm64@4.3.3': + resolution: {integrity: sha512-Y85A2gmPSkl5Ve5qR86GL4HT509cFqQh1aes9p3sSkyTPwt0Pppf3GkwGe4JPACcRYjgJIEhQgM6dBClnr0NYw==} + engines: {node: '>= 20'} + cpu: [arm64] + os: [android] + + '@tailwindcss/oxide-darwin-arm64@4.3.3': + resolution: {integrity: sha512-BiaWatpBcERQFDlOjRDpIVXuFK5PJez5SA4JMg6VYZdBYU+qKfV/vqjcIs+IYmtitf1xYQZTwXvU/8y4lfZUGw==} + engines: {node: '>= 20'} + cpu: [arm64] + os: [darwin] + + '@tailwindcss/oxide-darwin-x64@4.3.3': + resolution: {integrity: sha512-fAeUqfV5ndhxRwai8cXGzdLvul9utWOmeTkv69unv4ZXixjn61Z+p9lCWdwOwA3TYboG3BwdVuN/RDjhBRl0mw==} + engines: {node: '>= 20'} + cpu: [x64] + os: [darwin] + + '@tailwindcss/oxide-freebsd-x64@4.3.3': + resolution: {integrity: sha512-iyf5bV6+wnAlflVeEy7R25dupxTNECZN5QMI0qNT6eT+EgaGdZcKhGkr5SdoaWiLJ3spLqIY9VCeSGrwmtg4kw==} + engines: {node: '>= 20'} + cpu: [x64] + os: [freebsd] + + '@tailwindcss/oxide-linux-arm-gnueabihf@4.3.3': + resolution: {integrity: sha512-aAYUprJAJQWWbRrPvtjdroZ56Md+JM8pMiopS6xGEwDfLhqj+2ver2p4nU4Mb3CRqcMmNBjo8KkUgcxhkzVQGQ==} + engines: {node: '>= 20'} + cpu: [arm] + os: [linux] + + '@tailwindcss/oxide-linux-arm64-gnu@4.3.3': + resolution: {integrity: sha512-nDxldcEENOxZRzC2uu9jrutZdAAQtb+8WWDCSnWL1zvBk1+FN+x6MtDViPB5AJMfttVCUhehGWus3XBPgatM/w==} + engines: {node: '>= 20'} + cpu: [arm64] + os: [linux] + libc: [glibc] + + '@tailwindcss/oxide-linux-arm64-musl@4.3.3': + resolution: {integrity: sha512-Md44bD6veX/PC5iyF8cDVnw4HBIANZepRZZ7a8DQOvkfo5WUBwcp6iAuCUz23u+4SUkhJlD3eL7hNdW8ezd/kA==} + engines: {node: '>= 20'} + cpu: [arm64] + os: [linux] + libc: [musl] + + '@tailwindcss/oxide-linux-x64-gnu@4.3.3': + resolution: {integrity: sha512-tx7us1muwOKAKWao2v/GaafFeQboE6aj88vC6ziN2NCGcRm8gWUhwjzg+YdVB1e4boAtdtma4L43onunI6NS4w==} + engines: {node: '>= 20'} + cpu: [x64] + os: [linux] + libc: [glibc] + + '@tailwindcss/oxide-linux-x64-musl@4.3.3': + resolution: {integrity: sha512-SJxX60smvHgasZoBy11dX6YRjXJFovwWBoedhbQPOBzgFWBHGB+TVPWB9BxzR7TTxU8FQZAI2AyiNCMzFm8Img==} + engines: {node: '>= 20'} + cpu: [x64] + os: [linux] + libc: [musl] + + '@tailwindcss/oxide-wasm32-wasi@4.3.3': + resolution: {integrity: sha512-jx1+rPhY/5Ympkktd656HBWEBLxP7dH06losBLjjf5vgCODXvi9KhtftWcMIwTFIDqBr7cRnQkdLnAG+IOlGvQ==} + engines: {node: '>=14.0.0'} + cpu: [wasm32] + bundledDependencies: + - '@napi-rs/wasm-runtime' + - '@emnapi/core' + - '@emnapi/runtime' + - '@tybys/wasm-util' + - '@emnapi/wasi-threads' + - tslib + + '@tailwindcss/oxide-win32-arm64-msvc@4.3.3': + resolution: {integrity: sha512-3rc292Ca2ceK6Ulcc/bAVnTs/3nDtoPhyEKlgPv+yQJQi/JS/AMJlqzxvlDacL1nekbrcf6bTqp/jV4qgnPxNQ==} + engines: {node: '>= 20'} + cpu: [arm64] + os: [win32] + + '@tailwindcss/oxide-win32-x64-msvc@4.3.3': + resolution: {integrity: sha512-yJ0pwIVc/nYeGoV02WtsN8KYyLQv7kyI2wDnkezyJlGGjkd4QLwDGAwl47YpPJeuI0M0ObaXGSPjvWDPeTPggw==} + engines: {node: '>= 20'} + cpu: [x64] + os: [win32] + + '@tailwindcss/oxide@4.3.3': + resolution: {integrity: sha512-krXjAikiaFSPaK/FkAQT5UTx3VormQaiZ5hBFlJZ9UFQGB/rwg1MZIhHAG9smMQRTdyJxP6Qt5MwMtdyU5FWrA==} + engines: {node: '>= 20'} + + '@tailwindcss/vite@4.3.3': + resolution: {integrity: sha512-yYU8cogLeSh/ms2jh8Fj7jaba/EWa7Ja6GoUqYZaraEuCI5YS6ms6ObZgjjedm+jm6XZjdNRWBpPP6Z86oOxcw==} + peerDependencies: + vite: ^5.2.0 || ^6 || ^7 || ^8 + + '@tanstack/history@1.162.0': + resolution: {integrity: sha512-79pf/RkhteYZTRgcR4F9kbk84P2N8rugQJswxfIqovlbRiT3yI7eBE+5QorIrZaOKktsgzRlXh1l/du/xpl4iA==} + engines: {node: '>=20.19'} + + '@tanstack/query-core@5.101.4': + resolution: {integrity: sha512-gNwcvOJcRbLWPOLG/2OBm+zM+Yv+MKsXKEOWC57USuZDEsI71hEErQsiEGx5wX9rzWWkfwM0fVSPoiIFSsxfiw==} + + '@tanstack/query-devtools@5.101.4': + resolution: {integrity: sha512-z5IPHnDX3aUWeTWlRKLyooBQekaCAw4xRpZqPQ390RiWTDBcTynjpPT221BArw0u2+pnQMdGvPQI9YNNubBcmA==} + + '@tanstack/react-query-devtools@5.101.4': + resolution: {integrity: sha512-VeK2gtmfj7kvRBjtxS7TKxt/6qKhn8VzabY4UiYMr7NV9CddjSRYRgeYyld+NpjAkgMV9dd+2Qdr8ah5I03NeA==} + peerDependencies: + '@tanstack/react-query': ^5.101.4 + react: ^18 || ^19 + + '@tanstack/react-query@5.101.4': + resolution: {integrity: sha512-yRg2pfOCxIs4ZJW3XYYHU/WgtD04FHSnfHlpRT7h7pR77hwkdRG4wxbKe4aq6P0RvXUTBSQpQeadS1SUYUe+KA==} + peerDependencies: + react: ^18 || ^19 + + '@tanstack/react-router-devtools@1.167.0': + resolution: {integrity: sha512-nGw095EG7IHx0h5NtlEmzf6vcCTaFNPWdTSuDKazajhN0ct/v/TkekJ9J6KYUCeV1a8/2ZmToc58M+0rrOyn7w==} + engines: {node: '>=20.19'} + peerDependencies: + '@tanstack/react-router': ^1.170.0 + '@tanstack/router-core': ^1.170.0 + react: '>=18.0.0 || >=19.0.0' + react-dom: '>=18.0.0 || >=19.0.0' + peerDependenciesMeta: + '@tanstack/router-core': + optional: true + + '@tanstack/react-router@1.170.18': + resolution: {integrity: sha512-wpbGYZEp/fmz1q4bn7BD8VZ+/VZ7GBqSJv5V969pU+chP8y7dquWDmKTFMohvUegb9lg12m1uPVvD6kB2wORvQ==} + engines: {node: '>=20.19'} + peerDependencies: + react: '>=18.0.0 || >=19.0.0' + react-dom: '>=18.0.0 || >=19.0.0' + + '@tanstack/react-store@0.9.3': + resolution: {integrity: sha512-y2iHd/N9OkoQbFJLUX1T9vbc2O9tjH0pQRgTcx1/Nz4IlwLvkgpuglXUx+mXt0g5ZDFrEeDnONPqkbfxXJKwRg==} + peerDependencies: + react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 + react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 + + '@tanstack/router-core@1.171.15': + resolution: {integrity: sha512-IILCDcLaItMZQ2jEmCABHY1Nhjjn5XUvwpQp3e4Nmu+vfg0BgYFuu/QASz2SwE2ZNbVMrvt8X/wxa+Gg5aErxA==} + engines: {node: '>=20.19'} + + '@tanstack/router-devtools-core@1.168.0': + resolution: {integrity: sha512-wQoQhlBK7nlZgqzaqdYXKWNTpdHdsaREdaPhFZVH0/Ador+F+eM3/NF2i3f2LPeS0GgKraZUQXe1Q/1+KHyEYg==} + engines: {node: '>=20.19'} + peerDependencies: + '@tanstack/router-core': ^1.170.0 + csstype: ^3.0.10 + peerDependenciesMeta: + csstype: + optional: true + + '@tanstack/router-generator@1.167.21': + resolution: {integrity: sha512-m3oXZyienj8owialdyoZ0txHQrnEx/Ra+D9kWtar5fC2cWZr5Pvxl86VY2mX5RRLC5QLKLeRGT1x4HV95wHVDQ==} + engines: {node: '>=20.19'} + + '@tanstack/router-plugin@1.168.23': + resolution: {integrity: sha512-0+PIcvnaAimFwjoEIeV3h7LKjzC8zNnp7pH2UamdKwQ9QlY99WU9V0Xl0zbM0i9hrUa/mKgWPDAzELmPUu5fMA==} + engines: {node: '>=20.19'} + peerDependencies: + '@rsbuild/core': '>=1.0.2 || ^2.0.0' + '@tanstack/react-router': ^1.170.18 + vite: '>=5.0.0 || >=6.0.0 || >=7.0.0 || >=8.0.0' + vite-plugin-solid: ^2.11.10 || ^3.0.0-0 + webpack: '>=5.92.0' + peerDependenciesMeta: + '@rsbuild/core': + optional: true + '@tanstack/react-router': + optional: true + vite: + optional: true + vite-plugin-solid: + optional: true + webpack: + optional: true + + '@tanstack/router-utils@1.162.2': + resolution: {integrity: sha512-hTWqJtqIFFdvuCl8WXNyrodp2L9zo2G37xKRrcVmVRWpAB2h+U1LuRAfS4tsFTiWOIoE/B+WDVFB8JpoEdw6jQ==} + engines: {node: '>=20.19'} + + '@tanstack/store@0.9.3': + resolution: {integrity: sha512-8reSzl/qGWGGVKhBoxXPMWzATSbZLZFWhwBAFO9NAyp0TxzfBP0mIrGb8CP8KrQTmvzXlR/vFPPUrHTLBGyFyw==} + + '@tanstack/virtual-file-routes@1.162.0': + resolution: {integrity: sha512-uhOeFyxLcU41HzvrxsGpiWdcMbScY1EDgbZ5K7DVRMYInbLYWAC0EA/kx9wXAoSM8q82bUG2hRl8+EAjE6XAbA==} + engines: {node: '>=20.19'} + + '@ts-morph/common@0.27.0': + resolution: {integrity: sha512-Wf29UqxWDpc+i61k3oIOzcUfQt79PIT9y/MWfAGlrkjg6lBC1hwDECLXPVJAhWjiGbfBCxZd65F/LIZF3+jeJQ==} + + '@tybys/wasm-util@0.10.3': + resolution: {integrity: sha512-F3fo1MYrRJYL3zER0OUOmkutjr1Vp23m7OsSgp7nq4SP6OqX6C/56XFIPAl5bt3zaBRjmW7SGz3u/6LwFpYcOg==} + + '@types/debug@4.1.13': + resolution: {integrity: sha512-KSVgmQmzMwPlmtljOomayoR89W4FynCAi3E8PPs7vmDVPe84hT+vGPKkJfThkmXs0x0jAaa9U8uW8bbfyS2fWw==} + + '@types/estree-jsx@1.0.5': + resolution: {integrity: sha512-52CcUVNFyfb1A2ALocQw/Dd1BQFNmSdkuC3BkZ6iqhdMfQz7JWOFRuJFloOzjk+6WijU56m9oKXFAXc7o3Towg==} + + '@types/estree@1.0.9': + resolution: {integrity: sha512-GhdPgy1el4/ImP05X05Uw4cw2/M93BCUmnEvWZNStlCzEKME4Fkk+YpoA5OiHNQmoS7Cafb8Xa3Pya8m1Qrzeg==} + + '@types/hast@3.0.5': + resolution: {integrity: sha512-rp/ezSWaD1m44dPKICGhiskI13nVr7qTloFwDa/IYkhhf5nzwP+zIQcIJh3WIFSBOy/H1PzB40jPjMDksN4F+g==} + + '@types/mdast@4.0.4': + resolution: {integrity: sha512-kGaNbPh1k7AFzgpud/gMdvIm5xuECykRR+JnWKQno9TAXVa6WIVCGTPvYGekIDL4uwCZQSYbUxNBSb1aUo79oA==} + + '@types/ms@2.1.0': + resolution: {integrity: sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA==} + + '@types/node@24.13.3': + resolution: {integrity: sha512-Dh8vAsV36ig5wa9OX4pXvMc9D3Veibfw2wix0CUwYODLD8nkj9UsLjASr49nPg+2eKzxhBV+v7L8pXvT4e639Q==} + + '@types/react-dom@19.2.3': + resolution: {integrity: sha512-jp2L/eY6fn+KgVVQAOqYItbF0VY/YApe5Mz2F0aykSO8gx31bYCZyvSeYxCHKvzHG5eZjc+zyaS5BrBWya2+kQ==} + peerDependencies: + '@types/react': ^19.2.0 + + '@types/react@19.2.17': + resolution: {integrity: sha512-MXfmqaVPEVgkBT/aY0aGCkRWWtByiYQXo3xdQ8r5RzuFrPiRn8Gar2tQdXSUQ2GKV3bkXckek89V8wQBY2Q/Aw==} + + '@types/unist@2.0.11': + resolution: {integrity: sha512-CmBKiL6NNo/OqgmMn95Fk9Whlp2mtvIv+KNpQKN2F4SjvrEesubTRWGYSg+BnWZOnlCaSTU1sMpsBOzgbYhnsA==} + + '@types/unist@3.0.3': + resolution: {integrity: sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==} + + '@types/validate-npm-package-name@4.0.2': + resolution: {integrity: sha512-lrpDziQipxCEeK5kWxvljWYhUvOiB2A9izZd9B2AFarYAkqZshb4lPbRs7zKEic6eGtH8V/2qJW+dPp9OtF6bw==} + + '@typescript/typescript-aix-ppc64@7.0.2': + resolution: {integrity: sha512-MTKKkWB7p/0E9xi1d1tHtZ5PiLkGEMIq88pK2CubZjOsLtYTLqhgIgi6zepFa+9GHZ6h05NMCkQxGKiPXMxXtQ==} + engines: {node: '>=16.20.0'} + cpu: [ppc64] + os: [aix] + + '@typescript/typescript-darwin-arm64@7.0.2': + resolution: {integrity: sha512-gowzar9MwS/aRWp6f3a4KUqzRjAZjOsmGNCM6LcTgXum+dBfgsBVMN+AgvOCCbguXyick6LJhpBszxMebJ8syA==} + engines: {node: '>=16.20.0'} + cpu: [arm64] + os: [darwin] + + '@typescript/typescript-darwin-x64@7.0.2': + resolution: {integrity: sha512-SZ9xZInqApNlNGc9s0W1VSsktYSOe9cFqNOIqmN1Gs8SmkjKZYFt017G4VwPxASInODuAdbTW7sXiFUf893RgA==} + engines: {node: '>=16.20.0'} + cpu: [x64] + os: [darwin] + + '@typescript/typescript-freebsd-arm64@7.0.2': + resolution: {integrity: sha512-W5NH4y/J0plIIS5b2xvTEkU7JFxyqdMAOgf+Ilhl0vHQXKO5dZoxd+C/jEtq56c4F3wk71RB4BMRQ2XdI+bwYQ==} + engines: {node: '>=16.20.0'} + cpu: [arm64] + os: [freebsd] + + '@typescript/typescript-freebsd-x64@7.0.2': + resolution: {integrity: sha512-UMGDx5sTpzNw3WiPebH7l90IWfJggEd+egHt/q6p7/Cm3zqoV7VxkGXt+3DxPIw8CcmvAB0j3sVVfbhX+M4Tpw==} + engines: {node: '>=16.20.0'} + cpu: [x64] + os: [freebsd] + + '@typescript/typescript-linux-arm64@7.0.2': + resolution: {integrity: sha512-Qh4eU4/y3yDjnfjjyPYihMj5/ODIlmt+Bzu17OI+fiSRDW57QmU5SiN63exPRNJPKUzcc1INa1NXdrJ+MqHjUQ==} + engines: {node: '>=16.20.0'} + cpu: [arm64] + os: [linux] + + '@typescript/typescript-linux-arm@7.0.2': + resolution: {integrity: sha512-gffT3xPz9sR7j/YJExkyPntrI0P2EP9XbOyWzth2/Gs0RstK+90RBcO0ncXoXy/beYll1SXw846Nf2zdnEz0QQ==} + engines: {node: '>=16.20.0'} + cpu: [arm] + os: [linux] + + '@typescript/typescript-linux-loong64@7.0.2': + resolution: {integrity: sha512-uEHck9i8hoAzXPiYRib1O7miOnz23SxIeVl6F4LXox+qov1K35jHcEW6VHKvZI+pyvl7fZEP4MCU5LYvIq1GuQ==} + engines: {node: '>=16.20.0'} + cpu: [loong64] + os: [linux] + + '@typescript/typescript-linux-mips64el@7.0.2': + resolution: {integrity: sha512-R4KvAMnE43W5Qeqb0Ly56O3mWMWIAgsMyz36DCaycd5nbg/9kzm0liw3JocfRqyJY0KPmzFjbswozXyW0DnIYA==} + engines: {node: '>=16.20.0'} + cpu: [mips64el] + os: [linux] + + '@typescript/typescript-linux-ppc64@7.0.2': + resolution: {integrity: sha512-DORx5b3sd/4S7eayxm4FQv+A7CrkUIGRaHiwI8oiHTAI1fAPWhF4J0vAlkC8biAlHSVVwxMQ3tjZ2/DVbnQiiA==} + engines: {node: '>=16.20.0'} + cpu: [ppc64] + os: [linux] + + '@typescript/typescript-linux-riscv64@7.0.2': + resolution: {integrity: sha512-wf0jqEDOjrPRnKwYRyyJDRo11KMbvMFrU+q4zqKyChODBzvlkbhNQfKvLxQCcwTpdDaXSHZTVuh0JoCrKCUMHQ==} + engines: {node: '>=16.20.0'} + cpu: [riscv64] + os: [linux] + + '@typescript/typescript-linux-s390x@7.0.2': + resolution: {integrity: sha512-IkwJc3L7yhytWd/ewjyxNDfOmswCm9GWMJT/ue/dU4aZNbwZeYAetq42VyLmsmSjvoX7z74X6ZaYCtzAr0EuGw==} + engines: {node: '>=16.20.0'} + cpu: [s390x] + os: [linux] + + '@typescript/typescript-linux-x64@7.0.2': + resolution: {integrity: sha512-EYdf2cNg7rgCWJnxCdJ+F3V39O8ihb37eHAu1LK8oAFizgTQbPOK7zHHXbPt8rX24COqODXeI3sIf0fCXG7H/A==} + engines: {node: '>=16.20.0'} + cpu: [x64] + os: [linux] + + '@typescript/typescript-netbsd-arm64@7.0.2': + resolution: {integrity: sha512-+polYF4MF04aPpO5FTkHran9yUQDSXqy5GiSDKpsll5jy3l3+g9QLhpf39T+ePtefhXLOGrLl0QIjkQP6VnelA==} + engines: {node: '>=16.20.0'} + cpu: [arm64] + os: [netbsd] + + '@typescript/typescript-netbsd-x64@7.0.2': + resolution: {integrity: sha512-8YIT0EHM/3dq10ZOVF/A7pc/YSMtbcecct4rWtexrnSCHOPcpC2KTLXfTCR6vDpnSiY12heNb1GiN/wu+T/FyA==} + engines: {node: '>=16.20.0'} + cpu: [x64] + os: [netbsd] + + '@typescript/typescript-openbsd-arm64@7.0.2': + resolution: {integrity: sha512-APT8+ClYnuYm1u9+kgGXoMj2VzWzcymwh2gNSQVySHfkRDGOTVkoWLjCmOQSaO+PoqQ57B0flRp9SA+7GnnkzQ==} + engines: {node: '>=16.20.0'} + cpu: [arm64] + os: [openbsd] + + '@typescript/typescript-openbsd-x64@7.0.2': + resolution: {integrity: sha512-yX7s+Q0Dln0Dt9tEzZsAjXXR/+ytBM7AlglaqyeMPxQszJ1JhlJdZ6jLA+IzldHtflX81em7lDao1xXu+aRRkg==} + engines: {node: '>=16.20.0'} + cpu: [x64] + os: [openbsd] + + '@typescript/typescript-sunos-x64@7.0.2': + resolution: {integrity: sha512-dLJDGaLZ1D4HPQn62u1n8mBDkJREwMsAkCdkwd4Ieqw+x3TUyTsqY0YiBCtE6H6OzzgGk3iuZ3vFWRS+E8/d1g==} + engines: {node: '>=16.20.0'} + cpu: [x64] + os: [sunos] + + '@typescript/typescript-win32-arm64@7.0.2': + resolution: {integrity: sha512-Gyl1Vy6OsWesLzmq+EP0Fb7b4Nid5232AvcA2SFcdYreldpNtYFFofPjnt62y9hQy7VTaZp65ICJjuAQRaVcIQ==} + engines: {node: '>=16.20.0'} + cpu: [arm64] + os: [win32] + + '@typescript/typescript-win32-x64@7.0.2': + resolution: {integrity: sha512-0BQ3HkAHHlKLSp1qRvf3SUhGpGsDuhB/jgFw75guyqbxJqEaS0Cw/VFO8i2nHglJUzQCRtMMR/IBAKE3ETMC4g==} + engines: {node: '>=16.20.0'} + cpu: [x64] + os: [win32] + + '@ungap/structured-clone@1.3.3': + resolution: {integrity: sha512-60YRaenCQcVjYEKOcG824+DRGGIQ3VKErcBoAEDJZz5bKIs2ZG+X/H9Nk+Q6EVkwJk5QNApxbrc5QtBSwtrXAg==} + + '@vitejs/plugin-react@6.0.4': + resolution: {integrity: sha512-XcCQz0TBpBgljhj0gMuuDj49i6Ytqh5q1osT/Gp5uAVJUCTWxyskk/l1jwYYiu2xcNHHipdMz40EGfM1VdamVg==} + engines: {node: ^20.19.0 || >=22.12.0} + peerDependencies: + '@rolldown/plugin-babel': ^0.1.7 || ^0.2.0 + babel-plugin-react-compiler: ^1.0.0 + vite: ^8.0.0 + peerDependenciesMeta: + '@rolldown/plugin-babel': + optional: true + babel-plugin-react-compiler: + optional: true + + accepts@2.0.0: + resolution: {integrity: sha512-5cvg6CtKwfgdmVqY1WIiXKc3Q1bkRqGLi+2W/6ao+6Y7gu/RCwRuAhGEzh5B4KlszSuTLgZYuqFqo5bImjNKng==} + engines: {node: '>= 0.6'} + + ajv-formats@2.1.1: + resolution: {integrity: sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==} + peerDependencies: + ajv: ^8.0.0 + peerDependenciesMeta: + ajv: + optional: true + + ajv-formats@3.0.1: + resolution: {integrity: sha512-8iUql50EUR+uUcdRQ3HDqa6EVyo3docL8g5WJ3FNcWmu62IbkGUue/pEyLBW8VGKKucTPgqeks4fIU1DA4yowQ==} + peerDependencies: + ajv: ^8.0.0 + peerDependenciesMeta: + ajv: + optional: true + + ajv@8.20.0: + resolution: {integrity: sha512-Thbli+OlOj+iMPYFBVBfJ3OmCAnaSyNn4M1vz9T6Gka5Jt9ba/HIR56joy65tY6kx/FCF5VXNB819Y7/GUrBGA==} + + ansi-colors@4.1.3: + resolution: {integrity: sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw==} + engines: {node: '>=6'} + + ansi-regex@5.0.1: + resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==} + engines: {node: '>=8'} + + ansi-regex@6.2.2: + resolution: {integrity: sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==} + engines: {node: '>=12'} + + ansis@4.3.1: + resolution: {integrity: sha512-BJ8/l4R5LRE7hW9WdSuGYrLSHi2ynxeFpDFbH0K/CgNeY/tyhk+vO6TYxXC5r5CpUhNVX310xzPsN/H9lCdfOA==} + engines: {node: '>=14'} + + argparse@2.0.1: + resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==} + + ast-types@0.16.1: + resolution: {integrity: sha512-6t10qk83GOG8p0vKmaCr8eiilZwO171AvbROMtvvNiwrTly62t+7XkA8RdIIVbpMhCASAsxgAzdRSwh6nw/5Dg==} + engines: {node: '>=4'} + + atomically@1.7.0: + resolution: {integrity: sha512-Xcz9l0z7y9yQ9rdDaxlmaI4uJHf/T8g9hOEzJcsEqX2SjCj4J20uK7+ldkDHMbpJDK76wF7xEIgxc/vSlsfw5w==} + engines: {node: '>=10.12.0'} + + babel-dead-code-elimination@1.0.12: + resolution: {integrity: sha512-GERT7L2TiYcYDtYk1IpD+ASAYXjKbLTDPhBtYj7X1NuRMDTMtAx9kyBenub1Ev41lo91OHCKdmP+egTDmfQ7Ig==} + + bail@2.0.2: + resolution: {integrity: sha512-0xO6mYd7JB2YesxDKplafRpsiOzPt9V02ddPCLbY1xYGPOX24NTyN50qnUxgCPcSoYMhKpAuBTjQoRZCAkUDRw==} + + balanced-match@4.0.4: + resolution: {integrity: sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==} + engines: {node: 18 || 20 || >=22} + + baseline-browser-mapping@2.11.1: + resolution: {integrity: sha512-HYXq73DDpCtNzOmrFsm9eSwCvWCql0RzqjpDzXN9EadiLJ4DNat0nsZ/Bzmy+Ud12mb4/zKDY0cQ805ZzN+i0A==} + engines: {node: '>=6.0.0'} + hasBin: true + + body-parser@2.3.0: + resolution: {integrity: sha512-2cGmJupaNgg+QUwVLAucDuWuoMZ6EX9iHDRswZ5lsNYEmwPaRknMPCLZz07yTzVq/83p4o/wzbDZbBrTvGGTIw==} + engines: {node: '>=18'} + + brace-expansion@5.0.8: + resolution: {integrity: sha512-JZyDyq3D4AUifKTPOB7DELf6XsB3WdPuNxCtob1vFXPsSXhdAiHBWJ/tJ8HAc9aH84BK+5JFZLNkJKx3G9kzQg==} + engines: {node: 20 || >=22} + + braces@3.0.3: + resolution: {integrity: sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==} + engines: {node: '>=8'} + + browserslist@4.28.7: + resolution: {integrity: sha512-JxV13hNrFxqjOc8alRbq9dK1MM79NEXYpma2B2J4wAtpWS5zIEIKqWPGCl7N4o7Uc7B7itylh7SuDujATRyyTw==} + engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} + hasBin: true + + bundle-name@4.1.0: + resolution: {integrity: sha512-tjwM5exMg6BGRI+kNmTntNsvdZS1X8BFYS6tnJ2hdH0kVxM6/eVZ2xy+FqStSWvYmtfFMDLIxurorHwDKfDz5Q==} + engines: {node: '>=18'} + + bytes@3.1.2: + resolution: {integrity: sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==} + engines: {node: '>= 0.8'} + + call-bind-apply-helpers@1.0.2: + resolution: {integrity: sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==} + engines: {node: '>= 0.4'} + + call-bound@1.0.4: + resolution: {integrity: sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==} + engines: {node: '>= 0.4'} + + callsites@3.1.0: + resolution: {integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==} + engines: {node: '>=6'} + + caniuse-lite@1.0.30001806: + resolution: {integrity: sha512-72Cuvd95zbSYPKq6Fhg8eDJRlzgWDf7/mtoZv6Qe/DYNCEBdNxoA3+rZAU2ZhGCpZlns3EssFavaZomckT5Uuw==} + + ccount@2.0.1: + resolution: {integrity: sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg==} + + chalk@5.6.2: + resolution: {integrity: sha512-7NzBL0rN6fMUW+f7A6Io4h40qQlG+xGmtMxfbnH/K7TAtt8JQWVQK+6g0UXKMeVJoyV5EkkNsErQ8pVD3bLHbA==} + engines: {node: ^12.17.0 || ^14.13 || >=16.0.0} + + character-entities-html4@2.1.0: + resolution: {integrity: sha512-1v7fgQRj6hnSwFpq1Eu0ynr/CDEw0rXo2B61qXrLNdHZmPKgb7fqS1a2JwF0rISo9q77jDI8VMEHoApn8qDoZA==} + + character-entities-legacy@3.0.0: + resolution: {integrity: sha512-RpPp0asT/6ufRm//AJVwpViZbGM/MkjQFxJccQRHmISF/22NBtsHqAWmL+/pmkPWoIUJdWyeVleTl1wydHATVQ==} + + character-entities@2.0.2: + resolution: {integrity: sha512-shx7oQ0Awen/BRIdkjkvz54PnEEI/EjwXDSIZp86/KKdbafHh1Df/RYGBhn4hbe2+uKC9FnT5UCEdyPz3ai9hQ==} + + character-reference-invalid@2.0.1: + resolution: {integrity: sha512-iBZ4F4wRbyORVsu0jPV7gXkOsGYjGHPmAyv+HiHG8gi5PtC9KI2j1+v8/tlibRvjoWX027ypmG/n0HtO5t7unw==} + + chokidar@5.0.0: + resolution: {integrity: sha512-TQMmc3w+5AxjpL8iIiwebF73dRDF4fBIieAqGn9RGCWaEVwQ6Fb2cGe31Yns0RRIzii5goJ1Y7xbMwo1TxMplw==} + engines: {node: '>= 20.19.0'} + + class-variance-authority@0.7.1: + resolution: {integrity: sha512-Ka+9Trutv7G8M6WT6SeiRWz792K5qEqIGEGzXKhAE6xOWAY6pPH8U+9IY3oCMv6kqTmLsv7Xh/2w2RigkePMsg==} + + cli-cursor@5.0.0: + resolution: {integrity: sha512-aCj4O5wKyszjMmDT4tZj93kxyydN/K5zPWSCe6/0AV/AA1pqe5ZBIw0a2ZfPQV7lL5/yb5HsUreJ6UFAF1tEQw==} + engines: {node: '>=18'} + + cli-spinners@2.9.2: + resolution: {integrity: sha512-ywqV+5MmyL4E7ybXgKys4DugZbX0FC6LnwrhjuykIjnK9k8OQacQ7axGKnjDXWNhns0xot3bZI5h55H8yo9cJg==} + engines: {node: '>=6'} + + clsx@2.1.1: + resolution: {integrity: sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==} + engines: {node: '>=6'} + + code-block-writer@13.0.3: + resolution: {integrity: sha512-Oofo0pq3IKnsFtuHqSF7TqBfr71aeyZDVJ0HpmqB7FBM2qEigL0iPONSCZSO9pE9dZTAxANe5XHG9Uy0YMv8cg==} + + comma-separated-tokens@2.0.3: + resolution: {integrity: sha512-Fu4hJdvzeylCfQPp9SGWidpzrMs7tTrlu6Vb8XGaRGck8QSNZJJp538Wrb60Lax4fPwR64ViY468OIUTbRlGZg==} + + commander@11.1.0: + resolution: {integrity: sha512-yPVavfyCcRhmorC7rWlkHn15b4wDVgVmBA7kV4QVBsF7kv/9TKJAbAXVTxvTnwP8HHKjRCJDClKbciiYS7p0DQ==} + engines: {node: '>=16'} + + commander@14.0.3: + resolution: {integrity: sha512-H+y0Jo/T1RZ9qPP4Eh1pkcQcLRglraJaSLoyOtHxu6AapkjWVCy2Sit1QQ4x3Dng8qDlSsZEet7g5Pq06MvTgw==} + engines: {node: '>=20'} + + conf@10.2.0: + resolution: {integrity: sha512-8fLl9F04EJqjSqH+QjITQfJF8BrOVaYr1jewVgSRAEWePfxT0sku4w2hrGQ60BC/TNLGQ2pgxNlTbWQmMPFvXg==} + engines: {node: '>=12'} + + content-disposition@1.1.0: + resolution: {integrity: sha512-5jRCH9Z/+DRP7rkvY83B+yGIGX96OYdJmzngqnw2SBSxqCFPd0w2km3s5iawpGX8krnwSGmF0FW5Nhr0Hfai3g==} + engines: {node: '>=18'} + + content-type@1.0.5: + resolution: {integrity: sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==} + engines: {node: '>= 0.6'} + + content-type@2.0.0: + resolution: {integrity: sha512-j/O/d7GcZCyNl7/hwZAb606rzqkyvaDctLmckbxLzHvFBzTJHuGEdodATcP3yIRoDrLHkIATJuvzbFlp/ki2cQ==} + engines: {node: '>=18'} + + convert-source-map@2.0.0: + resolution: {integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==} + + cookie-es@3.1.1: + resolution: {integrity: sha512-UaXxwISYJPTr9hwQxMFYZ7kNhSXboMXP+Z3TRX6f1/NyaGPfuNUZOWP1pUEb75B2HjfklIYLVRfWiFZJyC6Npg==} + + cookie-signature@1.2.2: + resolution: {integrity: sha512-D76uU73ulSXrD1UXF4KE2TMxVVwhsnCgfAyTg9k8P6KGZjlXKrOLe4dJQKI3Bxi5wjesZoFXJWElNWBjPZMbhg==} + engines: {node: '>=6.6.0'} + + cookie@0.7.2: + resolution: {integrity: sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==} + engines: {node: '>= 0.6'} + + cors@2.8.6: + resolution: {integrity: sha512-tJtZBBHA6vjIAaF6EnIaq6laBBP9aq/Y3ouVJjEfoHbRBcHBAHYcMh/w8LDrk2PvIMMq8gmopa5D4V8RmbrxGw==} + engines: {node: '>= 0.10'} + + cosmiconfig@9.0.2: + resolution: {integrity: sha512-gtTZxTDau1wL7Y7zifc2dd8jHSK/k6BTx/2Xp/BpdlAdnlYWFVt7qhJqgwi7637yRwRQ3qL4ZidbB4I8tA5VOg==} + engines: {node: '>=14'} + peerDependencies: + typescript: '>=4.9.5' + peerDependenciesMeta: + typescript: + optional: true + + cross-spawn@7.0.6: + resolution: {integrity: sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==} + engines: {node: '>= 8'} + + cssesc@3.0.0: + resolution: {integrity: sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==} + engines: {node: '>=4'} + hasBin: true + + csstype@3.2.3: + resolution: {integrity: sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==} + + debounce-fn@4.0.0: + resolution: {integrity: sha512-8pYCQiL9Xdcg0UPSD3d+0KMlOjp+KGU5EPwYddgzQ7DATsg4fuUDjQtsYLmWjnk2obnNHgV3vE2Y4jejSOJVBQ==} + engines: {node: '>=10'} + + debug@4.4.3: + resolution: {integrity: sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==} + engines: {node: '>=6.0'} + peerDependencies: + supports-color: '*' + peerDependenciesMeta: + supports-color: + optional: true + + decode-named-character-reference@1.3.0: + resolution: {integrity: sha512-GtpQYB283KrPp6nRw50q3U9/VfOutZOe103qlN7BPP6Ad27xYnOIWv4lPzo8HCAL+mMZofJ9KEy30fq6MfaK6Q==} + + dedent@1.7.2: + resolution: {integrity: sha512-WzMx3mW98SN+zn3hgemf4OzdmyNhhhKz5Ay0pUfQiMQ3e1g+xmTJWp/pKdwKVXhdSkAEGIIzqeuWrL3mV/AXbA==} + peerDependencies: + babel-plugin-macros: ^3.1.0 + peerDependenciesMeta: + babel-plugin-macros: + optional: true + + deepmerge@4.3.1: + resolution: {integrity: sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==} + engines: {node: '>=0.10.0'} + + default-browser-id@5.0.1: + resolution: {integrity: sha512-x1VCxdX4t+8wVfd1so/9w+vQ4vx7lKd2Qp5tDRutErwmR85OgmfX7RlLRMWafRMY7hbEiXIbudNrjOAPa/hL8Q==} + engines: {node: '>=18'} + + default-browser@5.5.0: + resolution: {integrity: sha512-H9LMLr5zwIbSxrmvikGuI/5KGhZ8E2zH3stkMgM5LpOWDutGM2JZaj460Udnf1a+946zc7YBgrqEWwbk7zHvGw==} + engines: {node: '>=18'} + + define-lazy-prop@2.0.0: + resolution: {integrity: sha512-Ds09qNh8yw3khSjiJjiUInaGX9xlqZDY7JVryGxdxV7NPeuqQfplOpQ66yJFZut3jLa5zOwkXw1g9EI2uKh4Og==} + engines: {node: '>=8'} + + define-lazy-prop@3.0.0: + resolution: {integrity: sha512-N+MeXYoqr3pOgn8xfyRPREN7gHakLYjhsHhWGT3fWAiL4IkAt0iDw14QiiEm2bE30c5XX5q0FtAA3CK5f9/BUg==} + engines: {node: '>=12'} + + depd@2.0.0: + resolution: {integrity: sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==} + engines: {node: '>= 0.8'} + + dequal@2.0.3: + resolution: {integrity: sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==} + engines: {node: '>=6'} + + detect-libc@2.1.2: + resolution: {integrity: sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==} + engines: {node: '>=8'} + + devlop@1.1.0: + resolution: {integrity: sha512-RWmIqhcFf1lRYBvNmr7qTNuyCt/7/ns2jbpp1+PalgE/rDQcBT0fioSMUpJ93irlUhC5hrg4cYqe6U+0ImW0rA==} + + diff@8.0.4: + resolution: {integrity: sha512-DPi0FmjiSU5EvQV0++GFDOJ9ASQUVFh5kD+OzOnYdi7n3Wpm9hWWGfB/O2blfHcMVTL5WkQXSnRiK9makhrcnw==} + engines: {node: '>=0.3.1'} + + dot-prop@6.0.1: + resolution: {integrity: sha512-tE7ztYzXHIeyvc7N+hR3oi7FIbf/NIjVP9hmAt3yMXzrQ072/fpjGLx2GxNxGxUl5V73MEqYzioOMoVhGMJ5cA==} + engines: {node: '>=10'} + + dotenv@17.4.2: + resolution: {integrity: sha512-nI4U3TottKAcAD9LLud4Cb7b2QztQMUEfHbvhTH09bqXTxnSie8WnjPALV/WMCrJZ6UV/qHJ6L03OqO3LcdYZw==} + engines: {node: '>=12'} + + dunder-proto@1.0.1: + resolution: {integrity: sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==} + engines: {node: '>= 0.4'} + + ee-first@1.1.1: + resolution: {integrity: sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==} + + electron-to-chromium@1.5.396: + resolution: {integrity: sha512-yHiw2Y3C3H9U6TMbOfoWK/BPreiOPXRfTWPBwQBoZG6/8TB6eOPnsy5oaRYuatR7Fw2SJ4kKforgufeo7fq0EQ==} + + emoji-regex@10.6.0: + resolution: {integrity: sha512-toUI84YS5YmxW219erniWD0CIVOo46xGKColeNQRgOzDorgBi1v4D71/OFzgD9GO2UGKIv1C3Sp8DAn0+j5w7A==} + + encodeurl@2.0.0: + resolution: {integrity: sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==} + engines: {node: '>= 0.8'} + + enhanced-resolve@5.24.3: + resolution: {integrity: sha512-PwKooW9JUzh5chmYfHM3IQl5OkK2u2Nm011MgeZrss3JmFraUx/fqrf78kk8GUMYoibx/14MdwTl/1WKkG7TpQ==} + engines: {node: '>=10.13.0'} + + enquirer@2.4.1: + resolution: {integrity: sha512-rRqJg/6gd538VHvR3PSrdRBb/1Vy2YfzHqzvbhGIQpDRKIa4FgV/54b5Q1xYSxOOwKvjXweS26E0Q+nAMwp2pQ==} + engines: {node: '>=8.6'} + + env-paths@2.2.1: + resolution: {integrity: sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==} + engines: {node: '>=6'} + + error-ex@1.3.4: + resolution: {integrity: sha512-sqQamAnR14VgCr1A618A3sGrygcpK+HEbenA/HiEAkkUwcZIIB/tgWqHFxWgOyDh4nB4JCRimh79dR5Ywc9MDQ==} + + es-define-property@1.0.1: + resolution: {integrity: sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==} + engines: {node: '>= 0.4'} + + es-errors@1.3.0: + resolution: {integrity: sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==} + engines: {node: '>= 0.4'} + + es-object-atoms@1.1.2: + resolution: {integrity: sha512-HWcBoN6NileqtSydK2FqHbS/LoDd2pqrnQHLyJzBj4kOp/ky2MWMN694xOfkK8/SnUsW2DH7EfyVlydKCsm1Zw==} + engines: {node: '>= 0.4'} + + escalade@3.2.0: + resolution: {integrity: sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==} + engines: {node: '>=6'} + + escape-html@1.0.3: + resolution: {integrity: sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==} + + escape-string-regexp@5.0.0: + resolution: {integrity: sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw==} + engines: {node: '>=12'} + + esprima@4.0.1: + resolution: {integrity: sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==} + engines: {node: '>=4'} + hasBin: true + + estree-util-is-identifier-name@3.0.0: + resolution: {integrity: sha512-hFtqIDZTIUZ9BXLb8y4pYGyk6+wekIivNVTcmvk8NoOh+VeRn5y6cEHzbURrWbfp1fIqdVipilzj+lfaadNZmg==} + + etag@1.8.1: + resolution: {integrity: sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==} + engines: {node: '>= 0.6'} + + eventsource-parser@3.1.0: + resolution: {integrity: sha512-kJezFj9YFAMLeORyi7aCLxLbD5/qWMQnoMVlVPyHIll7lgRJCc3JVln9Vgl9nwQi0YkMnhdGTMNn7CkRRAptMg==} + engines: {node: '>=18.0.0'} + + eventsource@3.0.7: + resolution: {integrity: sha512-CRT1WTyuQoD771GW56XEZFQ/ZoSfWid1alKGDYMmkt2yl8UXrVR4pspqWNEcqKvVIzg6PAltWjxcSSPrboA4iA==} + engines: {node: '>=18.0.0'} + + execa@5.1.1: + resolution: {integrity: sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==} + engines: {node: '>=10'} + + execa@9.6.1: + resolution: {integrity: sha512-9Be3ZoN4LmYR90tUoVu2te2BsbzHfhJyfEiAVfz7N5/zv+jduIfLrV2xdQXOHbaD6KgpGdO9PRPM1Y4Q9QkPkA==} + engines: {node: ^18.19.0 || >=20.5.0} + + express-rate-limit@8.6.0: + resolution: {integrity: sha512-XKJXDsASUOo0LLtFwW5hCcQGH0N4WQc/Rn8/Pvoia+TJFOkkFPvrtW9lZOeeNcxQJspvOIERMwiRLsVFlhHEkA==} + engines: {node: '>= 16'} + peerDependencies: + express: '>= 4.11' + + express@5.2.1: + resolution: {integrity: sha512-hIS4idWWai69NezIdRt2xFVofaF4j+6INOpJlVOLDO8zXGpUVEVzIYk12UUi2JzjEzWL3IOAxcTubgz9Po0yXw==} + engines: {node: '>= 18'} + + extend@3.0.2: + resolution: {integrity: sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==} + + fast-deep-equal@3.1.3: + resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==} + + fast-glob@3.3.3: + resolution: {integrity: sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==} + engines: {node: '>=8.6.0'} + + fast-uri@3.1.4: + resolution: {integrity: sha512-8JnbkQ4juDyvYs4mgFGQqg4yCYtFDtUtmp2QIQq11ZZe5CFQ5wcqm1rqDgAh/QdMySuBnPzMUiJUNZG5N/AiQw==} + + fastq@1.20.1: + resolution: {integrity: sha512-GGToxJ/w1x32s/D2EKND7kTil4n8OVk/9mycTc4VDza13lOvpUZTGX3mFSCtV9ksdGBVzvsyAVLM6mHFThxXxw==} + + fdir@6.5.0: + resolution: {integrity: sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==} + engines: {node: '>=12.0.0'} + peerDependencies: + picomatch: ^3 || ^4 + peerDependenciesMeta: + picomatch: + optional: true + + figures@6.1.0: + resolution: {integrity: sha512-d+l3qxjSesT4V7v2fh+QnmFnUWv9lSpjarhShNTgBOfA0ttejbQUAlHLitbjkoRiDulW0OPoQPYIGhIC8ohejg==} + engines: {node: '>=18'} + + fill-range@7.1.1: + resolution: {integrity: sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==} + engines: {node: '>=8'} + + finalhandler@2.1.1: + resolution: {integrity: sha512-S8KoZgRZN+a5rNwqTxlZZePjT/4cnm0ROV70LedRHZ0p8u9fRID0hJUZQpkKLzro8LfmC8sx23bY6tVNxv8pQA==} + engines: {node: '>= 18.0.0'} + + find-up@3.0.0: + resolution: {integrity: sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==} + engines: {node: '>=6'} + + forwarded@0.2.0: + resolution: {integrity: sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==} + engines: {node: '>= 0.6'} + + fresh@2.0.0: + resolution: {integrity: sha512-Rx/WycZ60HOaqLKAi6cHRKKI7zxWbJ31MhntmtwMoaTeF7XFH9hhBp8vITaMidfljRQ6eYWCKkaTK+ykVJHP2A==} + engines: {node: '>= 0.8'} + + fs-extra@11.4.0: + resolution: {integrity: sha512-EQsFzMUJkCKGr1ePqlYADkIUmHW1s3ZXr5Yqy6wbGrfUCphpl2maM/kyOIRA2HpP3AaFQTZXD4ldjek+nccddA==} + engines: {node: '>=14.14'} + + fsevents@2.3.3: + resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==} + engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} + os: [darwin] + + function-bind@1.1.2: + resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==} + + fuzzysort@3.1.0: + resolution: {integrity: sha512-sR9BNCjBg6LNgwvxlBd0sBABvQitkLzoVY9MYYROQVX/FvfJ4Mai9LsGhDgd8qYdds0bY77VzYd5iuB+v5rwQQ==} + + gensync@1.0.0-beta.2: + resolution: {integrity: sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==} + engines: {node: '>=6.9.0'} + + get-east-asian-width@1.6.0: + resolution: {integrity: sha512-QRbvDIbx6YklUe6RxeTeleMR0yv3cYH6PsPZHcnVn7xv7zO1BHN8r0XETu8n6Ye3Q+ahtSarc3WgtNWmehIBfA==} + engines: {node: '>=18'} + + get-intrinsic@1.3.0: + resolution: {integrity: sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==} + engines: {node: '>= 0.4'} + + get-own-enumerable-keys@1.0.0: + resolution: {integrity: sha512-PKsK2FSrQCyxcGHsGrLDcK0lx+0Ke+6e8KFFozA9/fIQLhQzPaRvJFdcz7+Axg3jUH/Mq+NI4xa5u/UT2tQskA==} + engines: {node: '>=14.16'} + + get-proto@1.0.1: + resolution: {integrity: sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==} + engines: {node: '>= 0.4'} + + get-stream@6.0.1: + resolution: {integrity: sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==} + engines: {node: '>=10'} + + get-stream@9.0.1: + resolution: {integrity: sha512-kVCxPF3vQM/N0B1PmoqVUqgHP+EeVjmZSQn+1oCRPxd2P21P2F19lIgbR3HBosbB1PUhOAoctJnfEn2GbN2eZA==} + engines: {node: '>=18'} + + glob-parent@5.1.2: + resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==} + engines: {node: '>= 6'} + + goober@2.1.19: + resolution: {integrity: sha512-U7veizMqxyKlM58+Z5j2ngJBH/r9siDmxpvNxSw0PylF6WQvrASJEZrxh1hidRBJc2jqoBVSyOban5u8m+6Rxg==} + peerDependencies: + csstype: ^3.0.10 + + gopd@1.2.0: + resolution: {integrity: sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==} + engines: {node: '>= 0.4'} + + graceful-fs@4.2.11: + resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==} + + has-symbols@1.1.0: + resolution: {integrity: sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==} + engines: {node: '>= 0.4'} + + hasown@2.0.4: + resolution: {integrity: sha512-T2UbfbBEF32wiepXIsMlTW9+dDYC6wMh/t/vYA4tuOMKqWz/n3vr1NFSxQiyP+zk2mXsoMA/i/7qV6LKut1t1A==} + engines: {node: '>= 0.4'} + + hast-util-to-jsx-runtime@2.3.6: + resolution: {integrity: sha512-zl6s8LwNyo1P9uw+XJGvZtdFF1GdAkOg8ujOw+4Pyb76874fLps4ueHXDhXWdk6YHQ6OgUtinliG7RsYvCbbBg==} + + hast-util-whitespace@3.0.0: + resolution: {integrity: sha512-88JUN06ipLwsnv+dVn+OIYOvAuvBMy/Qoi6O7mQHxdPXpjy+Cd6xRkWwux7DKO+4sYILtLBRIKgsdpS2gQc7qw==} + + hono@4.12.32: + resolution: {integrity: sha512-XcuyW9qE2kJn07PkecMOBd5Vq/hMy7mmGw+idz1yblbg9N17ijJODrvPkn7/dwL3Kulj8LcRJ69DLOWf91dRUg==} + engines: {node: '>=16.9.0'} + + html-url-attributes@3.0.1: + resolution: {integrity: sha512-ol6UPyBWqsrO6EJySPz2O7ZSr856WDrEzM5zMqp+FJJLGMW35cLYmmZnl0vztAZxRUoNZJFTCohfjuIJ8I4QBQ==} + + http-errors@2.0.1: + resolution: {integrity: sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ==} + engines: {node: '>= 0.8'} + + human-signals@2.1.0: + resolution: {integrity: sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==} + engines: {node: '>=10.17.0'} + + human-signals@8.0.1: + resolution: {integrity: sha512-eKCa6bwnJhvxj14kZk5NCPc6Hb6BdsU9DZcOnmQKSnO1VKrfV0zCvtttPZUsBvjmNDn8rpcJfpwSYnHBjc95MQ==} + engines: {node: '>=18.18.0'} + + iconv-lite@0.7.3: + resolution: {integrity: sha512-IKXpvIzjnC9XTAUbVBcMfGS0EPaIXtW6v+zr+RRp+hqULEpo0owZax6wyRwPOJbWbzjYspQwusTsfVr0ifh4uQ==} + engines: {node: '>=0.10.0'} + + ignore@5.3.2: + resolution: {integrity: sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==} + engines: {node: '>= 4'} + + import-fresh@3.3.1: + resolution: {integrity: sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==} + engines: {node: '>=6'} + + inherits@2.0.4: + resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==} + + inline-style-parser@0.2.7: + resolution: {integrity: sha512-Nb2ctOyNR8DqQoR0OwRG95uNWIC0C1lCgf5Naz5H6Ji72KZ8OcFZLz2P5sNgwlyoJ8Yif11oMuYs5pBQa86csA==} + + ip-address@10.2.0: + resolution: {integrity: sha512-/+S6j4E9AHvW9SWMSEY9Xfy66O5PWvVEJ08O0y5JGyEKQpojb0K0GKpz/v5HJ/G0vi3D2sjGK78119oXZeE0qA==} + engines: {node: '>= 12'} + + ipaddr.js@1.9.1: + resolution: {integrity: sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==} + engines: {node: '>= 0.10'} + + is-alphabetical@2.0.1: + resolution: {integrity: sha512-FWyyY60MeTNyeSRpkM2Iry0G9hpr7/9kD40mD/cGQEuilcZYS4okz8SN2Q6rLCJ8gbCt6fN+rC+6tMGS99LaxQ==} + + is-alphanumerical@2.0.1: + resolution: {integrity: sha512-hmbYhX/9MUMF5uh7tOXyK/n0ZvWpad5caBA17GsC6vyuCqaWliRG5K1qS9inmUhEMaOBIW7/whAnSwveW/LtZw==} + + is-arrayish@0.2.1: + resolution: {integrity: sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==} + + is-decimal@2.0.1: + resolution: {integrity: sha512-AAB9hiomQs5DXWcRB1rqsxGUstbRroFOPPVAomNk/3XHR5JyEZChOyTWe2oayKnsSsr/kcGqF+z6yuH6HHpN0A==} + + is-docker@2.2.1: + resolution: {integrity: sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==} + engines: {node: '>=8'} + hasBin: true + + is-docker@3.0.0: + resolution: {integrity: sha512-eljcgEDlEns/7AXFosB5K/2nCM4P7FQPkGc/DWLy5rmFEWvZayGrik1d9/QIY5nJ4f9YsVvBkA6kJpHn9rISdQ==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + hasBin: true + + is-extglob@2.1.1: + resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==} + engines: {node: '>=0.10.0'} + + is-glob@4.0.3: + resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==} + engines: {node: '>=0.10.0'} + + is-hexadecimal@2.0.1: + resolution: {integrity: sha512-DgZQp241c8oO6cA1SbTEWiXeoxV42vlcJxgH+B3hi1AiqqKruZR3ZGF8In3fj4+/y/7rHvlOZLZtgJ/4ttYGZg==} + + is-in-ssh@1.0.0: + resolution: {integrity: sha512-jYa6Q9rH90kR1vKB6NM7qqd1mge3Fx4Dhw5TVlK1MUBqhEOuCagrEHMevNuCcbECmXZ0ThXkRm+Ymr51HwEPAw==} + engines: {node: '>=20'} + + is-inside-container@1.0.0: + resolution: {integrity: sha512-KIYLCCJghfHZxqjYBE7rEy0OBuTd5xCHS7tHVgvCLkx7StIoaxwNW3hCALgEUjFfeRk+MG/Qxmp/vtETEF3tRA==} + engines: {node: '>=14.16'} + hasBin: true + + is-interactive@2.0.0: + resolution: {integrity: sha512-qP1vozQRI+BMOPcjFzrjXuQvdak2pHNUMZoeG2eRbiSqyvbEf/wQtEOTOX1guk6E3t36RkaqiSt8A/6YElNxLQ==} + engines: {node: '>=12'} + + is-number@7.0.0: + resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==} + engines: {node: '>=0.12.0'} + + is-obj@2.0.0: + resolution: {integrity: sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w==} + engines: {node: '>=8'} + + is-obj@3.0.0: + resolution: {integrity: sha512-IlsXEHOjtKhpN8r/tRFj2nDyTmHvcfNeu/nrRIcXE17ROeatXchkojffa1SpdqW4cr/Fj6QkEf/Gn4zf6KKvEQ==} + engines: {node: '>=12'} + + is-plain-obj@4.1.0: + resolution: {integrity: sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg==} + engines: {node: '>=12'} + + is-promise@4.0.0: + resolution: {integrity: sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ==} + + is-regexp@3.1.0: + resolution: {integrity: sha512-rbku49cWloU5bSMI+zaRaXdQHXnthP6DZ/vLnfdSKyL4zUzuWnomtOEiZZOd+ioQ+avFo/qau3KPTc7Fjy1uPA==} + engines: {node: '>=12'} + + is-stream@2.0.1: + resolution: {integrity: sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==} + engines: {node: '>=8'} + + is-stream@4.0.1: + resolution: {integrity: sha512-Dnz92NInDqYckGEUJv689RbRiTSEHCQ7wOVeALbkOz999YpqT46yMRIGtSNl2iCL1waAZSx40+h59NV/EwzV/A==} + engines: {node: '>=18'} + + is-unicode-supported@1.3.0: + resolution: {integrity: sha512-43r2mRvz+8JRIKnWJ+3j8JtjRKZ6GmjzfaE/qiBJnikNnYv/6bagRJ1kUhNk8R5EX/GkobD+r+sfxCPJsiKBLQ==} + engines: {node: '>=12'} + + is-unicode-supported@2.1.0: + resolution: {integrity: sha512-mE00Gnza5EEB3Ds0HfMyllZzbBrmLOX3vfWoj9A9PEnTfratQ/BcaJOuMhnkhjXvb2+FkY3VuHqtAGpTPmglFQ==} + engines: {node: '>=18'} + + is-wsl@2.2.0: + resolution: {integrity: sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==} + engines: {node: '>=8'} + + is-wsl@3.1.1: + resolution: {integrity: sha512-e6rvdUCiQCAuumZslxRJWR/Doq4VpPR82kqclvcS0efgt430SlGIk05vdCN58+VrzgtIcfNODjozVielycD4Sw==} + engines: {node: '>=16'} + + isbot@5.2.1: + resolution: {integrity: sha512-dJ+LpKyClQZ7NG+j3OensC/mAZkGpukE9YUrgPYvAZj2doVL0edfDgywTUh5CXa0o+nW9a1V9e5+CJTX8+SxRw==} + engines: {node: '>=18'} + + isexe@2.0.0: + resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} + + isexe@3.1.5: + resolution: {integrity: sha512-6B3tLtFqtQS4ekarvLVMZ+X+VlvQekbe4taUkf/rhVO3d/h0M2rfARm/pXLcPEsjjMsFgrFgSrhQIxcSVrBz8w==} + engines: {node: '>=18'} + + jiti@2.7.0: + resolution: {integrity: sha512-AC/7JofJvZGrrneWNaEnJeOLUx+JlGt7tNa0wZiRPT4MY1wmfKjt2+6O2p2uz2+skll8OZZmJMNqeke7kKbNgQ==} + hasBin: true + + jose@6.2.4: + resolution: {integrity: sha512-N8acGzVsQy6M/fjFcxtysNc4Q379TcM5dM/qKkNtsHFji88yANnXTr7BLeP75iPnFwBfQzM/jg2BZ9+HZrHCZA==} + + js-tokens@4.0.0: + resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==} + + js-yaml@4.3.0: + resolution: {integrity: sha512-1td788aAnnZ5qs7V2QIRl1owjtYpbKt749Y3xauqQgwIIGF/xXWz1wMTEBx5O3LK3lXLVuqXPdPxj2BoFHaW9Q==} + hasBin: true + + jsesc@3.1.0: + resolution: {integrity: sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==} + engines: {node: '>=6'} + hasBin: true + + json-parse-even-better-errors@2.3.1: + resolution: {integrity: sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==} + + json-schema-traverse@1.0.0: + resolution: {integrity: sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==} + + json-schema-typed@7.0.3: + resolution: {integrity: sha512-7DE8mpG+/fVw+dTpjbxnx47TaMnDfOI1jwft9g1VybltZCduyRQPJPvc+zzKY9WPHxhPWczyFuYa6I8Mw4iU5A==} + + json-schema-typed@8.0.2: + resolution: {integrity: sha512-fQhoXdcvc3V28x7C7BMs4P5+kNlgUURe2jmUT1T//oBRMDrqy1QPelJimwZGo7Hg9VPV3EQV5Bnq4hbFy2vetA==} + + json5@2.2.3: + resolution: {integrity: sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==} + engines: {node: '>=6'} + hasBin: true + + jsonfile@6.2.1: + resolution: {integrity: sha512-zwOTdL3rFQ/lRdBnntKVOX6k5cKJwEc1HdilT71BWEu7J41gXIB2MRp+vxduPSwZJPWBxEzv4yH1wYLJGUHX4Q==} + + kleur@3.0.3: + resolution: {integrity: sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==} + engines: {node: '>=6'} + + kleur@4.1.5: + resolution: {integrity: sha512-o+NO+8WrRiQEE4/7nwRJhN1HWpVmJm511pBHUxPLtp0BUISzlBplORYSmTclCnJvQq2tKu/sgl3xVpkc7ZWuQQ==} + engines: {node: '>=6'} + + lightningcss-android-arm64@1.32.0: + resolution: {integrity: sha512-YK7/ClTt4kAK0vo6w3X+Pnm0D2cf2vPHbhOXdoNti1Ga0al1P4TBZhwjATvjNwLEBCnKvjJc2jQgHXH0NEwlAg==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [android] + + lightningcss-android-arm64@1.33.0: + resolution: {integrity: sha512-gEpRTalKdosp4Bb8qWtc2iOgE5SeIHlpS1up9bFq2wAyYhl1UdTObYiHe98zEM9SQvSoqQZ1IQD0JNpg3Ml5pg==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [android] + + lightningcss-darwin-arm64@1.32.0: + resolution: {integrity: sha512-RzeG9Ju5bag2Bv1/lwlVJvBE3q6TtXskdZLLCyfg5pt+HLz9BqlICO7LZM7VHNTTn/5PRhHFBSjk5lc4cmscPQ==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [darwin] + + lightningcss-darwin-arm64@1.33.0: + resolution: {integrity: sha512-Sciaz8eenNTKn9b3t7+xr0ipTp9YxKQY4npwQ3mrRuL0BAVHBLyZxofhaKBAVtzmtRZ/zTyo0/to4B1uWG/Djg==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [darwin] + + lightningcss-darwin-x64@1.32.0: + resolution: {integrity: sha512-U+QsBp2m/s2wqpUYT/6wnlagdZbtZdndSmut/NJqlCcMLTWp5muCrID+K5UJ6jqD2BFshejCYXniPDbNh73V8w==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [darwin] + + lightningcss-darwin-x64@1.33.0: + resolution: {integrity: sha512-Z5UPAxzrjlWNNyGy6i65cJzzvgJ5D3T6wMvs+gWpY9d7qRhANrxqAp6LhxIgZhWEw18RfJTGcRxjuLIBr+m8XQ==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [darwin] + + lightningcss-freebsd-x64@1.32.0: + resolution: {integrity: sha512-JCTigedEksZk3tHTTthnMdVfGf61Fky8Ji2E4YjUTEQX14xiy/lTzXnu1vwiZe3bYe0q+SpsSH/CTeDXK6WHig==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [freebsd] + + lightningcss-freebsd-x64@1.33.0: + resolution: {integrity: sha512-QQM/Ti/hQajJwCY+RiWuCZ9sdtI/XQk7nDK5vC8kkdwixezOlDgvDx7+RT+QjK6FcFT4MpsuoBnHIo/O3StRRg==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [freebsd] + + lightningcss-linux-arm-gnueabihf@1.32.0: + resolution: {integrity: sha512-x6rnnpRa2GL0zQOkt6rts3YDPzduLpWvwAF6EMhXFVZXD4tPrBkEFqzGowzCsIWsPjqSK+tyNEODUBXeeVHSkw==} + engines: {node: '>= 12.0.0'} + cpu: [arm] + os: [linux] + + lightningcss-linux-arm-gnueabihf@1.33.0: + resolution: {integrity: sha512-N7FVBe6iS24MlM6R/4RBTxGhQheZGs7tiQ9U32UtF75NzP5Q7xWPRqLBCKxlRQRk3rY1jCIPLzx7WzOhuUIRLQ==} + engines: {node: '>= 12.0.0'} + cpu: [arm] + os: [linux] + + lightningcss-linux-arm64-gnu@1.32.0: + resolution: {integrity: sha512-0nnMyoyOLRJXfbMOilaSRcLH3Jw5z9HDNGfT/gwCPgaDjnx0i8w7vBzFLFR1f6CMLKF8gVbebmkUN3fa/kQJpQ==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [linux] + libc: [glibc] + + lightningcss-linux-arm64-gnu@1.33.0: + resolution: {integrity: sha512-j2v/itmy4HlNxlc6voKXYgBqNi0Ng2LShg4z7GufpEgs05P+2suBVyi9I6YHq5uoVFx9ETin3eCEhLVyXGQnKg==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [linux] + libc: [glibc] + + lightningcss-linux-arm64-musl@1.32.0: + resolution: {integrity: sha512-UpQkoenr4UJEzgVIYpI80lDFvRmPVg6oqboNHfoH4CQIfNA+HOrZ7Mo7KZP02dC6LjghPQJeBsvXhJod/wnIBg==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [linux] + libc: [musl] + + lightningcss-linux-arm64-musl@1.33.0: + resolution: {integrity: sha512-yiO5ROMuYQgXbC60yjZU5CYSFZGKXL0HFATXt9mHJn1+zW55oCtMI9NfcVhYLMFDL7gV7oBPon/EmMMGg2OvtQ==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [linux] + libc: [musl] + + lightningcss-linux-x64-gnu@1.32.0: + resolution: {integrity: sha512-V7Qr52IhZmdKPVr+Vtw8o+WLsQJYCTd8loIfpDaMRWGUZfBOYEJeyJIkqGIDMZPwPx24pUMfwSxxI8phr/MbOA==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [linux] + libc: [glibc] + + lightningcss-linux-x64-gnu@1.33.0: + resolution: {integrity: sha512-ar+Ju7LmcN0Jo4FpL4hpFybwNG9/3A/Br5KW2n2jyODg3MEZXaDYADdemoNS+BDNfMgKvylJLj4S5tyRActuAg==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [linux] + libc: [glibc] + + lightningcss-linux-x64-musl@1.32.0: + resolution: {integrity: sha512-bYcLp+Vb0awsiXg/80uCRezCYHNg1/l3mt0gzHnWV9XP1W5sKa5/TCdGWaR/zBM2PeF/HbsQv/j2URNOiVuxWg==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [linux] + libc: [musl] + + lightningcss-linux-x64-musl@1.33.0: + resolution: {integrity: sha512-RYiYbkokw0trfKqqzfF55lginwEPrD3OJDfTuJzFs1MK6iFnDenaz1fqLLtX4ITG3OktJQXOeTaw1awrBAlZPw==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [linux] + libc: [musl] + + lightningcss-win32-arm64-msvc@1.32.0: + resolution: {integrity: sha512-8SbC8BR40pS6baCM8sbtYDSwEVQd4JlFTOlaD3gWGHfThTcABnNDBda6eTZeqbofalIJhFx0qKzgHJmcPTnGdw==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [win32] + + lightningcss-win32-arm64-msvc@1.33.0: + resolution: {integrity: sha512-1K+MPfLSFVpphzpdbfkhlWk6wBrTObBzS2T6db10PNOZgR9GoVsAWzwNyuhUYYbTp23j+4RrncfujZ4uAzXvwA==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [win32] + + lightningcss-win32-x64-msvc@1.32.0: + resolution: {integrity: sha512-Amq9B/SoZYdDi1kFrojnoqPLxYhQ4Wo5XiL8EVJrVsB8ARoC1PWW6VGtT0WKCemjy8aC+louJnjS7U18x3b06Q==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [win32] + + lightningcss-win32-x64-msvc@1.33.0: + resolution: {integrity: sha512-OlEICDx/Xl0FqSp4bry8zFnCvGpig3Gl4gCquvYwHuqJKEC1+n9NgDniFvqHGmMv1ZkqDJrDqKKSykTDX+ehuA==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [win32] + + lightningcss@1.32.0: + resolution: {integrity: sha512-NXYBzinNrblfraPGyrbPoD19C1h9lfI/1mzgWYvXUTe414Gz/X1FD2XBZSZM7rRTrMA8JL3OtAaGifrIKhQ5yQ==} + engines: {node: '>= 12.0.0'} + + lightningcss@1.33.0: + resolution: {integrity: sha512-WkUDrojuJs0xkgGf2udWxa3yGBRxPtxUkB79i6aCZLRgc7PM8fZe9TosfPDcvEpQZbuFASnHYmRLBLUbmLOIIA==} + engines: {node: '>= 12.0.0'} + + lines-and-columns@1.2.4: + resolution: {integrity: sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==} + + locate-path@3.0.0: + resolution: {integrity: sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==} + engines: {node: '>=6'} + + log-symbols@6.0.0: + resolution: {integrity: sha512-i24m8rpwhmPIS4zscNzK6MSEhk0DUWa/8iYQWxhffV8jkI4Phvs3F+quL5xvS0gdQR0FyTCMMH33Y78dDTzzIw==} + engines: {node: '>=18'} + + longest-streak@3.1.0: + resolution: {integrity: sha512-9Ri+o0JYgehTaVBBDoMqIl8GXtbWg711O3srftcHhZ0dqnETqLaoIK0x17fUw9rFSlK/0NlsKe0Ahhyl5pXE2g==} + + lru-cache@5.1.1: + resolution: {integrity: sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==} + + lucide-react@1.26.0: + resolution: {integrity: sha512-raglYVR2+VkMfJL158krjVmE+rV5ST2lzA/KQm1FRSjMHT4MnWaegHxoVEpmc2So3nOEhp9oGejJwAPX8MoAjg==} + peerDependencies: + react: ^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0 + + magic-string@0.30.21: + resolution: {integrity: sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==} + + markdown-table@3.0.4: + resolution: {integrity: sha512-wiYz4+JrLyb/DqW2hkFJxP7Vd7JuTDm77fvbM8VfEQdmSMqcImWeeRbHwZjBjIFki/VaMK2BhFi7oUUZeM5bqw==} + + math-intrinsics@1.1.0: + resolution: {integrity: sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==} + engines: {node: '>= 0.4'} + + mdast-util-find-and-replace@3.0.2: + resolution: {integrity: sha512-Tmd1Vg/m3Xz43afeNxDIhWRtFZgM2VLyaf4vSTYwudTyeuTneoL3qtWMA5jeLyz/O1vDJmmV4QuScFCA2tBPwg==} + + mdast-util-from-markdown@2.0.3: + resolution: {integrity: sha512-W4mAWTvSlKvf8L6J+VN9yLSqQ9AOAAvHuoDAmPkz4dHf553m5gVj2ejadHJhoJmcmxEnOv6Pa8XJhpxE93kb8Q==} + + mdast-util-gfm-autolink-literal@2.0.1: + resolution: {integrity: sha512-5HVP2MKaP6L+G6YaxPNjuL0BPrq9orG3TsrZ9YXbA3vDw/ACI4MEsnoDpn6ZNm7GnZgtAcONJyPhOP8tNJQavQ==} + + mdast-util-gfm-footnote@2.1.0: + resolution: {integrity: sha512-sqpDWlsHn7Ac9GNZQMeUzPQSMzR6Wv0WKRNvQRg0KqHh02fpTz69Qc1QSseNX29bhz1ROIyNyxExfawVKTm1GQ==} + + mdast-util-gfm-strikethrough@2.0.0: + resolution: {integrity: sha512-mKKb915TF+OC5ptj5bJ7WFRPdYtuHv0yTRxK2tJvi+BDqbkiG7h7u/9SI89nRAYcmap2xHQL9D+QG/6wSrTtXg==} + + mdast-util-gfm-table@2.0.0: + resolution: {integrity: sha512-78UEvebzz/rJIxLvE7ZtDd/vIQ0RHv+3Mh5DR96p7cS7HsBhYIICDBCu8csTNWNO6tBWfqXPWekRuj2FNOGOZg==} + + mdast-util-gfm-task-list-item@2.0.0: + resolution: {integrity: sha512-IrtvNvjxC1o06taBAVJznEnkiHxLFTzgonUdy8hzFVeDun0uTjxxrRGVaNFqkU1wJR3RBPEfsxmU6jDWPofrTQ==} + + mdast-util-gfm@3.1.0: + resolution: {integrity: sha512-0ulfdQOM3ysHhCJ1p06l0b0VKlhU0wuQs3thxZQagjcjPrlFRqY215uZGHHJan9GEAXd9MbfPjFJz+qMkVR6zQ==} + + mdast-util-mdx-expression@2.0.1: + resolution: {integrity: sha512-J6f+9hUp+ldTZqKRSg7Vw5V6MqjATc+3E4gf3CFNcuZNWD8XdyI6zQ8GqH7f8169MM6P7hMBRDVGnn7oHB9kXQ==} + + mdast-util-mdx-jsx@3.2.0: + resolution: {integrity: sha512-lj/z8v0r6ZtsN/cGNNtemmmfoLAFZnjMbNyLzBafjzikOM+glrjNHPlf6lQDOTccj9n5b0PPihEBbhneMyGs1Q==} + + mdast-util-mdxjs-esm@2.0.1: + resolution: {integrity: sha512-EcmOpxsZ96CvlP03NghtH1EsLtr0n9Tm4lPUJUBccV9RwUOneqSycg19n5HGzCf+10LozMRSObtVr3ee1WoHtg==} + + mdast-util-phrasing@4.1.0: + resolution: {integrity: sha512-TqICwyvJJpBwvGAMZjj4J2n0X8QWp21b9l0o7eXyVJ25YNWYbJDVIyD1bZXE6WtV6RmKJVYmQAKWa0zWOABz2w==} + + mdast-util-to-hast@13.2.1: + resolution: {integrity: sha512-cctsq2wp5vTsLIcaymblUriiTcZd0CwWtCbLvrOzYCDZoWyMNV8sZ7krj09FSnsiJi3WVsHLM4k6Dq/yaPyCXA==} + + mdast-util-to-markdown@2.1.2: + resolution: {integrity: sha512-xj68wMTvGXVOKonmog6LwyJKrYXZPvlwabaryTjLh9LuvovB/KAH+kvi8Gjj+7rJjsFi23nkUxRQv1KqSroMqA==} + + mdast-util-to-string@4.0.0: + resolution: {integrity: sha512-0H44vDimn51F0YwvxSJSm0eCDOJTRlmN0R1yBh4HLj9wiV1Dn0QoXGbvFAWj2hSItVTlCmBF1hqKlIyUBVFLPg==} + + media-typer@1.1.1: + resolution: {integrity: sha512-yz3xRaG20c6/BOzvYoDaGtPmGscs7YivItZEEqe6GbwNfHuxu9YNmvnEkMzKldAGY4/80pRcQRZSEnhquk9XuQ==} + engines: {node: '>= 0.8'} + + merge-descriptors@2.0.0: + resolution: {integrity: sha512-Snk314V5ayFLhp3fkUREub6WtjBfPdCPY1Ln8/8munuLuiYhsABgBVWsozAG+MWMbVEvcdcpbi9R7ww22l9Q3g==} + engines: {node: '>=18'} + + merge-stream@2.0.0: + resolution: {integrity: sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==} + + merge2@1.4.1: + resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==} + engines: {node: '>= 8'} + + micromark-core-commonmark@2.0.3: + resolution: {integrity: sha512-RDBrHEMSxVFLg6xvnXmb1Ayr2WzLAWjeSATAoxwKYJV94TeNavgoIdA0a9ytzDSVzBy2YKFK+emCPOEibLeCrg==} + + micromark-extension-gfm-autolink-literal@2.1.0: + resolution: {integrity: sha512-oOg7knzhicgQ3t4QCjCWgTmfNhvQbDDnJeVu9v81r7NltNCVmhPy1fJRX27pISafdjL+SVc4d3l48Gb6pbRypw==} + + micromark-extension-gfm-footnote@2.1.0: + resolution: {integrity: sha512-/yPhxI1ntnDNsiHtzLKYnE3vf9JZ6cAisqVDauhp4CEHxlb4uoOTxOCJ+9s51bIB8U1N1FJ1RXOKTIlD5B/gqw==} + + micromark-extension-gfm-strikethrough@2.1.0: + resolution: {integrity: sha512-ADVjpOOkjz1hhkZLlBiYA9cR2Anf8F4HqZUO6e5eDcPQd0Txw5fxLzzxnEkSkfnD0wziSGiv7sYhk/ktvbf1uw==} + + micromark-extension-gfm-table@2.1.1: + resolution: {integrity: sha512-t2OU/dXXioARrC6yWfJ4hqB7rct14e8f7m0cbI5hUmDyyIlwv5vEtooptH8INkbLzOatzKuVbQmAYcbWoyz6Dg==} + + micromark-extension-gfm-tagfilter@2.0.0: + resolution: {integrity: sha512-xHlTOmuCSotIA8TW1mDIM6X2O1SiX5P9IuDtqGonFhEK0qgRI4yeC6vMxEV2dgyr2TiD+2PQ10o+cOhdVAcwfg==} + + micromark-extension-gfm-task-list-item@2.1.0: + resolution: {integrity: sha512-qIBZhqxqI6fjLDYFTBIa4eivDMnP+OZqsNwmQ3xNLE4Cxwc+zfQEfbs6tzAo2Hjq+bh6q5F+Z8/cksrLFYWQQw==} + + micromark-extension-gfm@3.0.0: + resolution: {integrity: sha512-vsKArQsicm7t0z2GugkCKtZehqUm31oeGBV/KVSorWSy8ZlNAv7ytjFhvaryUiCUJYqs+NoE6AFhpQvBTM6Q4w==} + + micromark-factory-destination@2.0.1: + resolution: {integrity: sha512-Xe6rDdJlkmbFRExpTOmRj9N3MaWmbAgdpSrBQvCFqhezUn4AHqJHbaEnfbVYYiexVSs//tqOdY/DxhjdCiJnIA==} + + micromark-factory-label@2.0.1: + resolution: {integrity: sha512-VFMekyQExqIW7xIChcXn4ok29YE3rnuyveW3wZQWWqF4Nv9Wk5rgJ99KzPvHjkmPXF93FXIbBp6YdW3t71/7Vg==} + + micromark-factory-space@2.0.1: + resolution: {integrity: sha512-zRkxjtBxxLd2Sc0d+fbnEunsTj46SWXgXciZmHq0kDYGnck/ZSGj9/wULTV95uoeYiK5hRXP2mJ98Uo4cq/LQg==} + + micromark-factory-title@2.0.1: + resolution: {integrity: sha512-5bZ+3CjhAd9eChYTHsjy6TGxpOFSKgKKJPJxr293jTbfry2KDoWkhBb6TcPVB4NmzaPhMs1Frm9AZH7OD4Cjzw==} + + micromark-factory-whitespace@2.0.1: + resolution: {integrity: sha512-Ob0nuZ3PKt/n0hORHyvoD9uZhr+Za8sFoP+OnMcnWK5lngSzALgQYKMr9RJVOWLqQYuyn6ulqGWSXdwf6F80lQ==} + + micromark-util-character@2.1.1: + resolution: {integrity: sha512-wv8tdUTJ3thSFFFJKtpYKOYiGP2+v96Hvk4Tu8KpCAsTMs6yi+nVmGh1syvSCsaxz45J6Jbw+9DD6g97+NV67Q==} + + micromark-util-chunked@2.0.1: + resolution: {integrity: sha512-QUNFEOPELfmvv+4xiNg2sRYeS/P84pTW0TCgP5zc9FpXetHY0ab7SxKyAQCNCc1eK0459uoLI1y5oO5Vc1dbhA==} + + micromark-util-classify-character@2.0.1: + resolution: {integrity: sha512-K0kHzM6afW/MbeWYWLjoHQv1sgg2Q9EccHEDzSkxiP/EaagNzCm7T/WMKZ3rjMbvIpvBiZgwR3dKMygtA4mG1Q==} + + micromark-util-combine-extensions@2.0.1: + resolution: {integrity: sha512-OnAnH8Ujmy59JcyZw8JSbK9cGpdVY44NKgSM7E9Eh7DiLS2E9RNQf0dONaGDzEG9yjEl5hcqeIsj4hfRkLH/Bg==} + + micromark-util-decode-numeric-character-reference@2.0.2: + resolution: {integrity: sha512-ccUbYk6CwVdkmCQMyr64dXz42EfHGkPQlBj5p7YVGzq8I7CtjXZJrubAYezf7Rp+bjPseiROqe7G6foFd+lEuw==} + + micromark-util-decode-string@2.0.1: + resolution: {integrity: sha512-nDV/77Fj6eH1ynwscYTOsbK7rR//Uj0bZXBwJZRfaLEJ1iGBR6kIfNmlNqaqJf649EP0F3NWNdeJi03elllNUQ==} + + micromark-util-encode@2.0.1: + resolution: {integrity: sha512-c3cVx2y4KqUnwopcO9b/SCdo2O67LwJJ/UyqGfbigahfegL9myoEFoDYZgkT7f36T0bLrM9hZTAaAyH+PCAXjw==} + + micromark-util-html-tag-name@2.0.1: + resolution: {integrity: sha512-2cNEiYDhCWKI+Gs9T0Tiysk136SnR13hhO8yW6BGNyhOC4qYFnwF1nKfD3HFAIXA5c45RrIG1ub11GiXeYd1xA==} + + micromark-util-normalize-identifier@2.0.1: + resolution: {integrity: sha512-sxPqmo70LyARJs0w2UclACPUUEqltCkJ6PhKdMIDuJ3gSf/Q+/GIe3WKl0Ijb/GyH9lOpUkRAO2wp0GVkLvS9Q==} + + micromark-util-resolve-all@2.0.1: + resolution: {integrity: sha512-VdQyxFWFT2/FGJgwQnJYbe1jjQoNTS4RjglmSjTUlpUMa95Htx9NHeYW4rGDJzbjvCsl9eLjMQwGeElsqmzcHg==} + + micromark-util-sanitize-uri@2.0.1: + resolution: {integrity: sha512-9N9IomZ/YuGGZZmQec1MbgxtlgougxTodVwDzzEouPKo3qFWvymFHWcnDi2vzV1ff6kas9ucW+o3yzJK9YB1AQ==} + + micromark-util-subtokenize@2.1.0: + resolution: {integrity: sha512-XQLu552iSctvnEcgXw6+Sx75GflAPNED1qx7eBJ+wydBb2KCbRZe+NwvIEEMM83uml1+2WSXpBAcp9IUCgCYWA==} + + micromark-util-symbol@2.0.1: + resolution: {integrity: sha512-vs5t8Apaud9N28kgCrRUdEed4UJ+wWNvicHLPxCa9ENlYuAY31M0ETy5y1vA33YoNPDFTghEbnh6efaE8h4x0Q==} + + micromark-util-types@2.0.2: + resolution: {integrity: sha512-Yw0ECSpJoViF1qTU4DC6NwtC4aWGt1EkzaQB8KPPyCRR8z9TWeV0HbEFGTO+ZY1wB22zmxnJqhPyTpOVCpeHTA==} + + micromark@4.0.2: + resolution: {integrity: sha512-zpe98Q6kvavpCr1NPVSCMebCKfD7CA2NqZ+rykeNhONIJBpc1tFKt9hucLGwha3jNTNI8lHpctWJWoimVF4PfA==} + + micromatch@4.0.8: + resolution: {integrity: sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==} + engines: {node: '>=8.6'} + + mime-db@1.54.0: + resolution: {integrity: sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==} + engines: {node: '>= 0.6'} + + mime-types@3.0.2: + resolution: {integrity: sha512-Lbgzdk0h4juoQ9fCKXW4by0UJqj+nOOrI9MJ1sSj4nI8aI2eo1qmvQEie4VD1glsS250n15LsWsYtCugiStS5A==} + engines: {node: '>=18'} + + mimic-fn@2.1.0: + resolution: {integrity: sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==} + engines: {node: '>=6'} + + mimic-fn@3.1.0: + resolution: {integrity: sha512-Ysbi9uYW9hFyfrThdDEQuykN4Ey6BuwPD2kpI5ES/nFTDn/98yxYNLZJcgUAKPT/mcrLLKaGzJR9YVxJrIdASQ==} + engines: {node: '>=8'} + + mimic-function@5.0.1: + resolution: {integrity: sha512-VP79XUPxV2CigYP3jWwAUFSku2aKqBH7uTAapFWCBqutsbmDo96KY5o8uh6U+/YSIn5OxJnXp73beVkpqMIGhA==} + engines: {node: '>=18'} + + minimatch@10.2.5: + resolution: {integrity: sha512-MULkVLfKGYDFYejP07QOurDLLQpcjk7Fw+7jXS2R2czRQzR56yHRveU5NDJEOviH+hETZKSkIk5c+T23GjFUMg==} + engines: {node: 18 || 20 || >=22} + + minimist@1.2.8: + resolution: {integrity: sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==} + + ms@2.1.3: + resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} + + nanoid@3.3.16: + resolution: {integrity: sha512-bzlKTyNJ7+LdGIIwy8ijFpIqEQIvafahV7eYykJ8Cvh42EdJeODoJ6gUJXpQJvej1BddH8OqTXZNE/KfbWAu8Q==} + engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} + hasBin: true + + negotiator@1.0.0: + resolution: {integrity: sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg==} + engines: {node: '>= 0.6'} + + node-releases@2.0.51: + resolution: {integrity: sha512-wRNIrw4DmVLKQlbgOMdkMx27Wrpzes2hh5Jtbi2bjPd+4wJstWIqP5A+lscnqbm0xxmT5Bpg8Lec5ItEBwx6BQ==} + engines: {node: '>=18'} + + npm-run-path@4.0.1: + resolution: {integrity: sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==} + engines: {node: '>=8'} + + npm-run-path@6.0.0: + resolution: {integrity: sha512-9qny7Z9DsQU8Ou39ERsPU4OZQlSTP47ShQzuKZ6PRXpYLtIFgl/DEBYEXKlvcEa+9tHVcK8CF81Y2V72qaZhWA==} + engines: {node: '>=18'} + + object-assign@4.1.1: + resolution: {integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==} + engines: {node: '>=0.10.0'} + + object-inspect@1.13.4: + resolution: {integrity: sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==} + engines: {node: '>= 0.4'} + + object-treeify@1.1.33: + resolution: {integrity: sha512-EFVjAYfzWqWsBMRHPMAXLCDIJnpMhdWAqR7xG6M6a2cs6PMFpl/+Z20w9zDW4vkxOFfddegBKq9Rehd0bxWE7A==} + engines: {node: '>= 10'} + + on-finished@2.4.1: + resolution: {integrity: sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==} + engines: {node: '>= 0.8'} + + once@1.4.0: + resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==} + + onetime@5.1.2: + resolution: {integrity: sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==} + engines: {node: '>=6'} + + onetime@7.0.0: + resolution: {integrity: sha512-VXJjc87FScF88uafS3JllDgvAm+c/Slfz06lorj2uAY34rlUu0Nt+v8wreiImcrgAjjIHp1rXpTDlLOGw29WwQ==} + engines: {node: '>=18'} + + open@11.0.0: + resolution: {integrity: sha512-smsWv2LzFjP03xmvFoJ331ss6h+jixfA4UUV/Bsiyuu4YJPfN+FIQGOIiv4w9/+MoHkfkJ22UIaQWRVFRfH6Vw==} + engines: {node: '>=20'} + + open@8.4.2: + resolution: {integrity: sha512-7x81NCL719oNbsq/3mh+hVrAWmFuEYUqrq/Iw3kUzH8ReypT9QQ0BLoJS7/G9k6N81XjW4qHWtjWwe/9eLy1EQ==} + engines: {node: '>=12'} + + openapi-fetch@0.17.0: + resolution: {integrity: sha512-PsbZR1wAPcG91eEthKhN+Zn92FMHxv+/faECIwjXdxfTODGSGegYv0sc1Olz+HYPvKOuoXfp+0pA2XVt2cI0Ig==} + + openapi-typescript-helpers@0.1.0: + resolution: {integrity: sha512-OKTGPthhivLw/fHz6c3OPtg72vi86qaMlqbJuVJ23qOvQ+53uw1n7HdmkJFibloF7QEjDrDkzJiOJuockM/ljw==} + + ora@8.2.0: + resolution: {integrity: sha512-weP+BZ8MVNnlCm8c0Qdc1WSWq4Qn7I+9CJGm7Qali6g44e/PUzbjNqJX5NJ9ljlNMosfJvg1fKEGILklK9cwnw==} + engines: {node: '>=18'} + + oxlint-tsgolint@7.0.2001: + resolution: {integrity: sha512-KjK/XLcXr1DSyonKhsuFqJRiuKqcyG9j3LJ8nkOsrLzGvodBPqzHOKauy10asLMDI0sUpvb+1sxlzff3udZvfg==} + hasBin: true + + oxlint@1.75.0: + resolution: {integrity: sha512-m9WzjRcRYA/uqIZDa9tclrieoPJ/ln1QYTKdFx6NUOs8uY5DiHlIwRQoCrHT6OM6O3ww3l2skY5gO7G7ZphE7g==} + engines: {node: ^20.19.0 || >=22.12.0} + hasBin: true + peerDependencies: + oxlint-tsgolint: '>=7.0.2001' + vite-plus: '*' + peerDependenciesMeta: + oxlint-tsgolint: + optional: true + vite-plus: + optional: true + + p-limit@2.3.0: + resolution: {integrity: sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==} + engines: {node: '>=6'} + + p-locate@3.0.0: + resolution: {integrity: sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==} + engines: {node: '>=6'} + + p-try@2.2.0: + resolution: {integrity: sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==} + engines: {node: '>=6'} + + parent-module@1.0.1: + resolution: {integrity: sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==} + engines: {node: '>=6'} + + parse-entities@4.0.2: + resolution: {integrity: sha512-GG2AQYWoLgL877gQIKeRPGO1xF9+eG1ujIb5soS5gPvLQ1y2o8FL90w2QWNdf9I361Mpp7726c+lj3U0qK1uGw==} + + parse-json@5.2.0: + resolution: {integrity: sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==} + engines: {node: '>=8'} + + parse-ms@4.0.0: + resolution: {integrity: sha512-TXfryirbmq34y8QBwgqCVLi+8oA3oWx2eAnSn62ITyEhEYaWRlVZ2DvMM9eZbMs/RfxPu/PK/aBLyGj4IrqMHw==} + engines: {node: '>=18'} + + parseurl@1.3.3: + resolution: {integrity: sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==} + engines: {node: '>= 0.8'} + + path-browserify@1.0.1: + resolution: {integrity: sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g==} + + path-exists@3.0.0: + resolution: {integrity: sha512-bpC7GYwiDYQ4wYLe+FA8lhRjhQCMcQGuSgGGqDkg/QerRWw9CmGRT0iSOVRSZJ29NMLZgIzqaljJ63oaL4NIJQ==} + engines: {node: '>=4'} + + path-key@3.1.1: + resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==} + engines: {node: '>=8'} + + path-key@4.0.0: + resolution: {integrity: sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ==} + engines: {node: '>=12'} + + path-to-regexp@8.4.2: + resolution: {integrity: sha512-qRcuIdP69NPm4qbACK+aDogI5CBDMi1jKe0ry5rSQJz8JVLsC7jV8XpiJjGRLLol3N+R5ihGYcrPLTno6pAdBA==} + + pathe@2.0.3: + resolution: {integrity: sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==} + + picocolors@1.1.1: + resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==} + + picomatch@2.3.2: + resolution: {integrity: sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==} + engines: {node: '>=8.6'} + + picomatch@4.0.5: + resolution: {integrity: sha512-RvwwcruNjI1ncT5xRakeyS9Lf8lcItv34KD+aif+VH9kduAyfYBipGh12274xtenIPZ119/R9BdTBa8gAwSh0A==} + engines: {node: '>=12'} + + pkce-challenge@5.0.1: + resolution: {integrity: sha512-wQ0b/W4Fr01qtpHlqSqspcj3EhBvimsdh0KlHhH8HRZnMsEa0ea2fTULOXOS9ccQr3om+GcGRk4e+isrZWV8qQ==} + engines: {node: '>=16.20.0'} + + pkg-up@3.1.0: + resolution: {integrity: sha512-nDywThFk1i4BQK4twPQ6TA4RT8bDY96yeuCVBWL3ePARCiEKDRSrNGbFIgUJpLp+XeIR65v8ra7WuJOFUBtkMA==} + engines: {node: '>=8'} + + postcss-selector-parser@7.1.4: + resolution: {integrity: sha512-HeP7D2wyhkR+XaK6v4W8oRF62Dsz4flyuczALJp61GckGm42u1saSSJ/0auvcBqxs3jMRFEcPK34At/0JBKdOg==} + engines: {node: '>=4'} + + postcss@8.5.23: + resolution: {integrity: sha512-g50586zr4bZmwFiTlflMu8E0bDTb5I5gertgwAKmsdUlTQIhZtunzUlD1WSzwcVWPoAVpsrA6vlfCD7oXvRwgg==} + engines: {node: ^10 || ^12 || >=14} + + powershell-utils@0.1.0: + resolution: {integrity: sha512-dM0jVuXJPsDN6DvRpea484tCUaMiXWjuCn++HGTqUWzGDjv5tZkEZldAJ/UMlqRYGFrD/etByo4/xOuC/snX2A==} + engines: {node: '>=20'} + + prettier-plugin-tailwindcss@0.8.1: + resolution: {integrity: sha512-iaFMYqDsE4ffdDkn5qup0j5f2aCEBFZrdrZnvu9QKTlWx/iGPeQ4HHu7b7fCPMxeo9nwQBiOAh2nSypdFYWJkw==} + engines: {node: '>=20.19'} + peerDependencies: + '@ianvs/prettier-plugin-sort-imports': '*' + '@prettier/plugin-hermes': '*' + '@prettier/plugin-oxc': '*' + '@prettier/plugin-pug': '*' + '@shopify/prettier-plugin-liquid': '*' + '@trivago/prettier-plugin-sort-imports': '*' + '@zackad/prettier-plugin-twig': '*' + prettier: ^3.0 + prettier-plugin-astro: '*' + prettier-plugin-css-order: '*' + prettier-plugin-jsdoc: '*' + prettier-plugin-marko: '*' + prettier-plugin-multiline-arrays: '*' + prettier-plugin-organize-attributes: '*' + prettier-plugin-organize-imports: '*' + prettier-plugin-sort-imports: '*' + prettier-plugin-svelte: '*' + peerDependenciesMeta: + '@ianvs/prettier-plugin-sort-imports': + optional: true + '@prettier/plugin-hermes': + optional: true + '@prettier/plugin-oxc': + optional: true + '@prettier/plugin-pug': + optional: true + '@shopify/prettier-plugin-liquid': + optional: true + '@trivago/prettier-plugin-sort-imports': + optional: true + '@zackad/prettier-plugin-twig': + optional: true + prettier-plugin-astro: + optional: true + prettier-plugin-css-order: + optional: true + prettier-plugin-jsdoc: + optional: true + prettier-plugin-marko: + optional: true + prettier-plugin-multiline-arrays: + optional: true + prettier-plugin-organize-attributes: + optional: true + prettier-plugin-organize-imports: + optional: true + prettier-plugin-sort-imports: + optional: true + prettier-plugin-svelte: + optional: true + + prettier@3.9.6: + resolution: {integrity: sha512-OpN0zzVdiaiAhxpuuj5efpIS4sY9j7bY6uR5mnj5yPzGkdkjNKSJeUThPb60Jw29QuAZgA4o+/iB49kFiaBX6g==} + engines: {node: '>=14'} + hasBin: true + + pretty-ms@9.3.0: + resolution: {integrity: sha512-gjVS5hOP+M3wMm5nmNOucbIrqudzs9v/57bWRHQWLYklXqoXKrVfYW2W9+glfGsqtPgpiz5WwyEEB+ksXIx3gQ==} + engines: {node: '>=18'} + + prompts@2.4.2: + resolution: {integrity: sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q==} + engines: {node: '>= 6'} + + property-information@7.2.0: + resolution: {integrity: sha512-IAtzIB6sUiWaJYrX9smp3V46pBGbBeLFRGdh25kg1334VcBlD8HzhPeNIWQH9zhGmo2itIe25EHt9dQP7G5hmg==} + + proxy-addr@2.0.7: + resolution: {integrity: sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==} + engines: {node: '>= 0.10'} + + qs@6.15.3: + resolution: {integrity: sha512-O9gl3zCl5h5blw1KGUzQKhA5oUXSl8rwUIM5o0S3nCXMliSvy5Dzx7/DJcI+SwgICv+IneSZwhBh1oSyEHA71A==} + engines: {node: '>=0.6'} + + queue-microtask@1.2.3: + resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==} + + range-parser@1.3.0: + resolution: {integrity: sha512-hek2mFQpPuI4E1BBKrSto+BU3e3x4xuarsbiwr3+lf7p44juvFMV0XFWQAP3xUyqXA4RrXLIoaSUGbSt056ZMw==} + engines: {node: '>= 0.6'} + + raw-body@3.0.2: + resolution: {integrity: sha512-K5zQjDllxWkf7Z5xJdV0/B0WTNqx6vxG70zJE4N0kBs4LovmEYWJzQGxC9bS9RAKu3bgM40lrd5zoLJ12MQ5BA==} + engines: {node: '>= 0.10'} + + react-dom@19.2.8: + resolution: {integrity: sha512-rVprimfGBG3DR+Tq0IQG2DT5PxKth1WIGDmj5yPmlzr4YBe7uyE+Du4oVqTDXZSHGGGXRtTJEGSSePyQCMBglQ==} + peerDependencies: + react: ^19.2.8 + + react-hook-form@7.82.0: + resolution: {integrity: sha512-Zw/uFZ2dO+02GHlBn7JFGn8kZJ7LdM33B/0BXOovzFay+CMhf94JMw5BVu+F1tVkUKjNvBuaE3fz5BJhga10Tg==} + engines: {node: '>=18.0.0'} + peerDependencies: + react: ^16.8.0 || ^17 || ^18 || ^19 + + react-markdown@10.1.0: + resolution: {integrity: sha512-qKxVopLT/TyA6BX3Ue5NwabOsAzm0Q7kAPwq6L+wWDwisYs7R8vZ0nRXqq6rkueboxpkjvLGU9fWifiX/ZZFxQ==} + peerDependencies: + '@types/react': '>=18' + react: '>=18' + + react@19.2.8: + resolution: {integrity: sha512-PWaYA1L/q9u2u7xYQi+Y3L3Yfnie7XyLeaJICV1MGD6LprsBxcAqGjYyr0eY3p+QdsA+x/Irkt4Qif8D63+Sbw==} + engines: {node: '>=0.10.0'} + + readdirp@5.0.0: + resolution: {integrity: sha512-9u/XQ1pvrQtYyMpZe7DXKv2p5CNvyVwzUB6uhLAnQwHMSgKMBR62lc7AHljaeteeHXn11XTAaLLUVZYVZyuRBQ==} + engines: {node: '>= 20.19.0'} + + recast@0.23.12: + resolution: {integrity: sha512-dEWRjcINDu/F4l2dYx57ugBtD7HV9KXESyxhzw/MqWLeglJrsjJKqACPyUPg+6AF8mIgm+Zi0dZ3ACoIg+QtpA==} + engines: {node: '>= 4'} + + remark-gfm@4.0.1: + resolution: {integrity: sha512-1quofZ2RQ9EWdeN34S79+KExV1764+wCUGop5CPL1WGdD0ocPpu91lzPGbwWMECpEpd42kJGQwzRfyov9j4yNg==} + + remark-parse@11.0.0: + resolution: {integrity: sha512-FCxlKLNGknS5ba/1lmpYijMUzX2esxW5xQqjWxw2eHFfS2MSdaHVINFmhjo+qN1WhZhNimq0dZATN9pH0IDrpA==} + + remark-rehype@11.1.2: + resolution: {integrity: sha512-Dh7l57ianaEoIpzbp0PC9UKAdCSVklD8E5Rpw7ETfbTl3FqcOOgq5q2LVDhgGCkaBv7p24JXikPdvhhmHvKMsw==} + + remark-stringify@11.0.0: + resolution: {integrity: sha512-1OSmLd3awB/t8qdoEOMazZkNsfVTeY4fTsgzcQFdXNq8ToTN4ZGwrMnlda4K6smTFKD+GRV6O48i6Z4iKgPPpw==} + + require-from-string@2.0.2: + resolution: {integrity: sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==} + engines: {node: '>=0.10.0'} + + reselect@5.2.0: + resolution: {integrity: sha512-AgZ3UOZm3YndfrJ4OYjgrT7bmCm/1iqkjvEfH/oYjzh6PD2qw4QuT3jjnXIrpdt4MTpMXclMT3lXbmRY+XRakw==} + + resolve-from@4.0.0: + resolution: {integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==} + engines: {node: '>=4'} + + restore-cursor@5.1.0: + resolution: {integrity: sha512-oMA2dcrw6u0YfxJQXm342bFKX/E4sG9rbTzO9ptUcR/e8A33cHuvStiYOwH7fszkZlZ1z/ta9AAoPk2F4qIOHA==} + engines: {node: '>=18'} + + reusify@1.1.0: + resolution: {integrity: sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==} + engines: {iojs: '>=1.0.0', node: '>=0.10.0'} + + rolldown@1.1.5: + resolution: {integrity: sha512-t9z29cJjXf/vxQ8dyhCSpt6H6aSwHTk8cT5I3iy6SMXuFpk5mB6PL6XfC8PCwrPTx93udwKUm9HRteAlTGBLiA==} + engines: {node: ^20.19.0 || >=22.12.0} + hasBin: true + + router@2.2.0: + resolution: {integrity: sha512-nLTrUKm2UyiL7rlhapu/Zl45FwNgkZGaCpZbIHajDYgwlJCOzLSk+cIPAnsEqV955GjILJnKbdQC1nVPz+gAYQ==} + engines: {node: '>= 18'} + + run-applescript@7.1.0: + resolution: {integrity: sha512-DPe5pVFaAsinSaV6QjQ6gdiedWDcRCbUuiQfQa2wmWV7+xC9bGulGI8+TdRmoFkAPaBXk8CrAbnlY2ISniJ47Q==} + engines: {node: '>=18'} + + run-parallel@1.2.0: + resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==} + + safer-buffer@2.1.2: + resolution: {integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==} + + scheduler@0.27.0: + resolution: {integrity: sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q==} + + semver@6.3.1: + resolution: {integrity: sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==} + hasBin: true + + semver@7.8.5: + resolution: {integrity: sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA==} + engines: {node: '>=10'} + hasBin: true + + send@1.2.1: + resolution: {integrity: sha512-1gnZf7DFcoIcajTjTwjwuDjzuz4PPcY2StKPlsGAQ1+YH20IRVrBaXSWmdjowTJ6u8Rc01PoYOGHXfP1mYcZNQ==} + engines: {node: '>= 18'} + + seroval-plugins@1.5.6: + resolution: {integrity: sha512-HXuLAX2pu/UByPpaeo/TaMfvMIi+1QqIoPJYCcAtU8QkVNwgR6MPlGuCQTErV1JwraaMbYaWVIBX7mppzGLATQ==} + engines: {node: '>=10'} + peerDependencies: + seroval: ^1.0 + + seroval@1.5.6: + resolution: {integrity: sha512-rVQVWjjSvlINzaQPZH5JFqsqEsIWdTxY3iJZCnTL/5gQbXIRooVZKI60tVCkOVfzcRPejboxO2t0P89dg5mQaA==} + engines: {node: '>=10'} + + serve-static@2.2.1: + resolution: {integrity: sha512-xRXBn0pPqQTVQiC8wyQrKs2MOlX24zQ0POGaj0kultvoOCstBQM5yvOhAVSUwOMjQtTvsPWoNCHfPGwaaQJhTw==} + engines: {node: '>= 18'} + + setprototypeof@1.2.0: + resolution: {integrity: sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==} + + shadcn@4.14.1: + resolution: {integrity: sha512-Xb4r150oGNexMBxUnWPG1emcaUlFbhzMOJubdA+k0NTkmhgBBycFZjTz9Aw2yQbpDR6QnIZbk234N20s/s7s9A==} + engines: {node: '>=20.18.1'} + hasBin: true + + shebang-command@2.0.0: + resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==} + engines: {node: '>=8'} + + shebang-regex@3.0.0: + resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==} + engines: {node: '>=8'} + + side-channel-list@1.0.1: + resolution: {integrity: sha512-mjn/0bi/oUURjc5Xl7IaWi/OJJJumuoJFQJfDDyO46+hBWsfaVM65TBHq2eoZBhzl9EchxOijpkbRC8SVBQU0w==} + engines: {node: '>= 0.4'} + + side-channel-map@1.0.1: + resolution: {integrity: sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==} + engines: {node: '>= 0.4'} + + side-channel-weakmap@1.0.2: + resolution: {integrity: sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==} + engines: {node: '>= 0.4'} + + side-channel@1.1.1: + resolution: {integrity: sha512-6x6dK6zJdpTzF4sQeNYxwtvBzf6Eg4GtlesS94HOvTudUeyK2WXAaIfmDgsyslYrRBeFIlsi54AYsFGUuhmvrQ==} + engines: {node: '>= 0.4'} + + signal-exit@3.0.7: + resolution: {integrity: sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==} + + signal-exit@4.1.0: + resolution: {integrity: sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==} + engines: {node: '>=14'} + + sisteransi@1.0.5: + resolution: {integrity: sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==} + + sonner@2.0.7: + resolution: {integrity: sha512-W6ZN4p58k8aDKA4XPcx2hpIQXBRAgyiWVkYhT7CvK6D3iAu7xjvVyhQHg2/iaKJZ1XVJ4r7XuwGL+WGEK37i9w==} + peerDependencies: + react: ^18.0.0 || ^19.0.0 || ^19.0.0-rc + react-dom: ^18.0.0 || ^19.0.0 || ^19.0.0-rc + + source-map-js@1.2.1: + resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==} + engines: {node: '>=0.10.0'} + + source-map@0.6.1: + resolution: {integrity: sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==} + engines: {node: '>=0.10.0'} + + space-separated-tokens@2.0.2: + resolution: {integrity: sha512-PEGlAwrG8yXGXRjW32fGbg66JAlOAwbObuqVoJpv/mRgoWDQfgH1wDPvtzWyUSNAXBGSk8h755YDbbcEy3SH2Q==} + + statuses@2.0.2: + resolution: {integrity: sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==} + engines: {node: '>= 0.8'} + + stdin-discarder@0.2.2: + resolution: {integrity: sha512-UhDfHmA92YAlNnCfhmq0VeNL5bDbiZGg7sZ2IvPsXubGkiNa9EC+tUTsjBRsYUAz87btI6/1wf4XoVvQ3uRnmQ==} + engines: {node: '>=18'} + + string-width@7.2.0: + resolution: {integrity: sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==} + engines: {node: '>=18'} + + stringify-entities@4.0.4: + resolution: {integrity: sha512-IwfBptatlO+QCJUo19AqvrPNqlVMpW9YEL2LIVY+Rpv2qsjCGxaDLNRgeGsQWJhfItebuJhsGSLjaBbNSQ+ieg==} + + stringify-object@5.0.0: + resolution: {integrity: sha512-zaJYxz2FtcMb4f+g60KsRNFOpVMUyuJgA51Zi5Z1DOTC3S59+OQiVOzE9GZt0x72uBGWKsQIuBKeF9iusmKFsg==} + engines: {node: '>=14.16'} + + strip-ansi@6.0.1: + resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==} + engines: {node: '>=8'} + + strip-ansi@7.2.0: + resolution: {integrity: sha512-yDPMNjp4WyfYBkHnjIRLfca1i6KMyGCtsVgoKe/z1+6vukgaENdgGBZt+ZmKPc4gavvEZ5OgHfHdrazhgNyG7w==} + engines: {node: '>=12'} + + strip-bom@3.0.0: + resolution: {integrity: sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==} + engines: {node: '>=4'} + + strip-final-newline@2.0.0: + resolution: {integrity: sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==} + engines: {node: '>=6'} + + strip-final-newline@4.0.0: + resolution: {integrity: sha512-aulFJcD6YK8V1G7iRB5tigAP4TsHBZZrOV8pjV++zdUwmeV8uzbY7yn6h9MswN62adStNZFuCIx4haBnRuMDaw==} + engines: {node: '>=18'} + + style-to-js@1.1.21: + resolution: {integrity: sha512-RjQetxJrrUJLQPHbLku6U/ocGtzyjbJMP9lCNK7Ag0CNh690nSH8woqWH9u16nMjYBAok+i7JO1NP2pOy8IsPQ==} + + style-to-object@1.0.14: + resolution: {integrity: sha512-LIN7rULI0jBscWQYaSswptyderlarFkjQ+t79nzty8tcIAceVomEVlLzH5VP4Cmsv6MtKhs7qaAiwlcp+Mgaxw==} + + supports-color@10.2.2: + resolution: {integrity: sha512-SS+jx45GF1QjgEXQx4NJZV9ImqmO2NPz5FNsIHrsDjh2YsHnawpan7SNQ1o8NuhrbHZy9AZhIoCUiCeaW/C80g==} + engines: {node: '>=18'} + + systeminformation@5.33.1: + resolution: {integrity: sha512-DEN6ICHk3Tk0Uf/hrAHh7xlt7iL5CJFBtPZinA0H62DrGG/KPKqq/Nzj6lCXPS4Ay/sf/14zNnk9LpqKzBIc+w==} + engines: {node: '>=10.0.0'} + os: [darwin, linux, win32, freebsd, openbsd, netbsd, sunos, android] + hasBin: true + + tailwind-merge@3.6.0: + resolution: {integrity: sha512-uxL7qAVQriqRQPAyK3pj66VqskWqoZ37PW94jwOTwNfq/z9oyu1V+eqrZqtR2+fCiXdYOZe/Modt8GtvqNzu+w==} + + tailwindcss@4.3.3: + resolution: {integrity: sha512-gOhV3P7ufE62QDGg1zVaTgCR+EtPv92k2nIhVcVKcLmxT1sUBsQGhnZj175j+MqRt4zLF7ic+sCYjfhxMxj7YQ==} + + tapable@2.3.3: + resolution: {integrity: sha512-uxc/zpqFg6x7C8vOE7lh6Lbda8eEL9zmVm/PLeTPBRhh1xCgdWaQ+J1CUieGpIfm2HdtsUpRv+HshiasBMcc6A==} + engines: {node: '>=6'} + + tiny-invariant@1.3.3: + resolution: {integrity: sha512-+FbBPE1o9QAYvviau/qC5SE3caw21q3xkvWKBtja5vgqOWIHHJ3ioaq1VPfn/Szqctz2bU/oYeKd9/z5BL+PVg==} + + tinyglobby@0.2.17: + resolution: {integrity: sha512-wXR/dYpcqKmfWpEdZjiKJOwCNFndD0DMnrW/cYjVGttEkBfVgcLFHoNrlj47mjOVic9yyNu65alsgF4NQyTa2g==} + engines: {node: '>=12.0.0'} + + to-regex-range@5.0.1: + resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==} + engines: {node: '>=8.0'} + + toidentifier@1.0.1: + resolution: {integrity: sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==} + engines: {node: '>=0.6'} + + trim-lines@3.0.1: + resolution: {integrity: sha512-kRj8B+YHZCc9kQYdWfJB2/oUl9rA99qbowYYBtr4ui4mZyAQ2JpvVBd/6U2YloATfqBhBTSMhTpgBHtU0Mf3Rg==} + + trough@2.2.0: + resolution: {integrity: sha512-tmMpK00BjZiUyVyvrBK7knerNgmgvcV/KLVyuma/SC+TQN167GrMRciANTz09+k3zW8L8t60jWO1GpfkZdjTaw==} + + ts-morph@26.0.0: + resolution: {integrity: sha512-ztMO++owQnz8c/gIENcM9XfCEzgoGphTv+nKpYNM1bgsdOVC/jRZuEBf6N+mLLDNg68Kl+GgUZfOySaRiG1/Ug==} + + tsconfig-paths@4.2.0: + resolution: {integrity: sha512-NoZ4roiN7LnbKn9QqE1amc9DJfzvZXxF4xDavcOWt1BPkdx+m+0gJuPM+S0vCe7zTJMYUP0R8pO2XMr+Y8oLIg==} + engines: {node: '>=6'} + + tslib@2.8.1: + resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==} + + tw-animate-css@1.4.0: + resolution: {integrity: sha512-7bziOlRqH0hJx80h/3mbicLW7o8qLsH5+RaLR2t+OHM3D0JlWGODQKQ4cxbK7WlvmUxpcj6Kgu6EKqjrGFe3QQ==} + + type-is@2.1.0: + resolution: {integrity: sha512-faYHw0anBbc/kWF3zFTEnxSFOAGUX9GFbOBthvDdLsIlEoWOFOtS0zgCiQYwIskL9iGXZL3kAXD8OoZ4GmMATA==} + engines: {node: '>= 18'} + + typescript@7.0.2: + resolution: {integrity: sha512-8FYau96o3NKOhbjKi/qNvG/W5jhzxkbdm5sj9AbZ/5T5sWqn3hJgLfGx27sRKZWTvyzCP8dLRBTf5tBTSRVUNA==} + engines: {node: '>=16.20.0'} + hasBin: true + + undici-types@7.18.2: + resolution: {integrity: sha512-AsuCzffGHJybSaRrmr5eHr81mwJU3kjw6M+uprWvCXiNeN9SOGwQ3Jn8jb8m3Z6izVgknn1R0FTCEAP2QrLY/w==} + + undici@7.29.0: + resolution: {integrity: sha512-IDxfleLmmbSskfWSUATiN1nfn2rDuvnMOqb5CWR92iIfojA0Ud+ulOAAEQ57LPr9rWmsreUyf5lwyao+7GNNVw==} + engines: {node: '>=20.18.1'} + + unicorn-magic@0.3.0: + resolution: {integrity: sha512-+QBBXBCvifc56fsbuxZQ6Sic3wqqc3WWaqxs58gvJrcOuN83HGTCwz3oS5phzU9LthRNE9VrJCFCLUgHeeFnfA==} + engines: {node: '>=18'} + + unified@11.0.5: + resolution: {integrity: sha512-xKvGhPWw3k84Qjh8bI3ZeJjqnyadK+GEFtazSfZv/rKeTkTjOJho6mFqh2SM96iIcZokxiOpg78GazTSg8+KHA==} + + unist-util-is@6.0.1: + resolution: {integrity: sha512-LsiILbtBETkDz8I9p1dQ0uyRUWuaQzd/cuEeS1hoRSyW5E5XGmTzlwY1OrNzzakGowI9Dr/I8HVaw4hTtnxy8g==} + + unist-util-position@5.0.0: + resolution: {integrity: sha512-fucsC7HjXvkB5R3kTCO7kUjRdrS0BJt3M/FPxmHMBOm8JQi2BsHAHFsy27E0EolP8rp0NzXsJ+jNPyDWvOJZPA==} + + unist-util-stringify-position@4.0.0: + resolution: {integrity: sha512-0ASV06AAoKCDkS2+xw5RXJywruurpbC4JZSm7nr7MOt1ojAzvyyaO+UxZf18j8FCF6kmzCZKcAgN/yu2gm2XgQ==} + + unist-util-visit-parents@6.0.2: + resolution: {integrity: sha512-goh1s1TBrqSqukSc8wrjwWhL0hiJxgA8m4kFxGlQ+8FYQ3C/m11FcTs4YYem7V664AhHVvgoQLk890Ssdsr2IQ==} + + unist-util-visit@5.1.0: + resolution: {integrity: sha512-m+vIdyeCOpdr/QeQCu2EzxX/ohgS8KbnPDgFni4dQsfSCtpz8UqDyY5GjRru8PDKuYn7Fq19j1CQ+nJSsGKOzg==} + + universalify@2.0.1: + resolution: {integrity: sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==} + engines: {node: '>= 10.0.0'} + + unpipe@1.0.0: + resolution: {integrity: sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==} + engines: {node: '>= 0.8'} + + unplugin@3.3.0: + resolution: {integrity: sha512-qa66K+crbfyE6JK10GjvbJeRrOsuC/JpbnHctfyp/i4oBTxWOzJfRZyDiOk1PtErMFRu8JhsU/wPvOdBNWe5Rg==} + engines: {node: ^20.19.0 || >=22.12.0} + peerDependencies: + '@farmfe/core': '*' + '@rspack/core': '*' + bun-types-no-globals: '*' + esbuild: '*' + rolldown: '*' + rollup: '*' + unloader: '*' + vite: '*' + webpack: '*' + peerDependenciesMeta: + '@farmfe/core': + optional: true + '@rspack/core': + optional: true + bun-types-no-globals: + optional: true + esbuild: + optional: true + rolldown: + optional: true + rollup: + optional: true + unloader: + optional: true + vite: + optional: true + webpack: + optional: true + + update-browserslist-db@1.2.3: + resolution: {integrity: sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w==} + hasBin: true + peerDependencies: + browserslist: '>= 4.21.0' + + use-sync-external-store@1.6.0: + resolution: {integrity: sha512-Pp6GSwGP/NrPIrxVFAIkOQeyw8lFenOHijQWkUTrDvrF4ALqylP2C/KCkeS9dpUM3KvYRQhna5vt7IL95+ZQ9w==} + peerDependencies: + react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 + + util-deprecate@1.0.2: + resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==} + + validate-npm-package-name@7.0.2: + resolution: {integrity: sha512-hVDIBwsRruT73PbK7uP5ebUt+ezEtCmzZz3F59BSr2F6OVFnJ/6h8liuvdLrQ88Xmnk6/+xGGuq+pG9WwTuy3A==} + engines: {node: ^20.17.0 || >=22.9.0} + + vary@1.1.2: + resolution: {integrity: sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==} + engines: {node: '>= 0.8'} + + vfile-message@4.0.3: + resolution: {integrity: sha512-QTHzsGd1EhbZs4AsQ20JX1rC3cOlt/IWJruk893DfLRr57lcnOeMaWG4K0JrRta4mIJZKth2Au3mM3u03/JWKw==} + + vfile@6.0.3: + resolution: {integrity: sha512-KzIbH/9tXat2u30jf+smMwFCsno4wHVdNmzFyL+T/L3UGqqk6JKfVqOFOZEpZSHADH1k40ab6NUIXZq422ov3Q==} + + vite@8.1.5: + resolution: {integrity: sha512-7ULLwsCdYx/nRyrpiEwvqb5TFHrMVZyBt+rg/OAXT7rgj/z+DtTDyKFeLAdDkubDVDKD8jOsndmy7m55XcfUsw==} + engines: {node: ^20.19.0 || >=22.12.0} + hasBin: true + peerDependencies: + '@types/node': ^20.19.0 || >=22.12.0 + '@vitejs/devtools': ^0.3.0 + esbuild: ^0.27.0 || ^0.28.0 + jiti: '>=1.21.0' + less: ^4.0.0 + sass: ^1.70.0 + sass-embedded: ^1.70.0 + stylus: '>=0.54.8' + sugarss: ^5.0.0 + terser: ^5.16.0 + tsx: ^4.8.1 + yaml: ^2.4.2 + peerDependenciesMeta: + '@types/node': + optional: true + '@vitejs/devtools': + optional: true + esbuild: + optional: true + jiti: + optional: true + less: + optional: true + sass: + optional: true + sass-embedded: + optional: true + stylus: + optional: true + sugarss: + optional: true + terser: + optional: true + tsx: + optional: true + yaml: + optional: true + + webpack-virtual-modules@0.6.2: + resolution: {integrity: sha512-66/V2i5hQanC51vBQKPH4aI8NMAcBW59FVBs+rC7eGHupMyfn34q7rZIE+ETlJ+XTevqfUhVVBgSUNSW2flEUQ==} + + which@2.0.2: + resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==} + engines: {node: '>= 8'} + hasBin: true + + which@4.0.0: + resolution: {integrity: sha512-GlaYyEb07DPxYCKhKzplCWBJtvxZcZMrL+4UkrTSJHHPyZU4mYYTv3qaOe77H7EODLSSopAUFAc6W8U4yqvscg==} + engines: {node: ^16.13.0 || >=18.0.0} + hasBin: true + + wrappy@1.0.2: + resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==} + + wsl-utils@0.3.1: + resolution: {integrity: sha512-g/eziiSUNBSsdDJtCLB8bdYEUMj4jR7AGeUo96p/3dTafgjHhpF4RiCFPiRILwjQoDXx5MqkBr4fwWtR3Ky4Wg==} + engines: {node: '>=20'} + + yallist@3.1.1: + resolution: {integrity: sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==} + + yocto-spinner@1.2.2: + resolution: {integrity: sha512-DODGl1wJjA/s5pnJFKau9lIYHT81lnhob1i3e1TjxZRxEhWRKl74nTbWE6H5KlkViQQTo/Z29YFdxzTZAMY3ng==} + engines: {node: '>=18.19'} + + yoctocolors@2.1.2: + resolution: {integrity: sha512-CzhO+pFNo8ajLM2d2IW/R93ipy99LWjtwblvC1RsoSUMZgyLbYFr221TnSNT7GjGdYui6P459mw9JH/g/zW2ug==} + engines: {node: '>=18'} + + zod-to-json-schema@3.25.2: + resolution: {integrity: sha512-O/PgfnpT1xKSDeQYSCfRI5Gy3hPf91mKVDuYLUHZJMiDFptvP41MSnWofm8dnCm0256ZNfZIM7DSzuSMAFnjHA==} + peerDependencies: + zod: ^3.25.28 || ^4 + + zod@3.25.76: + resolution: {integrity: sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==} + + zod@4.4.3: + resolution: {integrity: sha512-ytENFjIJFl2UwYglde2jchW2Hwm4GJFLDiSXWdTrJQBIN9Fcyp7n4DhxJEiWNAJMV1/BqWfW/kkg71UDcHJyTQ==} + + zwitch@2.0.4: + resolution: {integrity: sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A==} + +snapshots: + + '@babel/code-frame@7.29.7': + dependencies: + '@babel/helper-validator-identifier': 7.29.7 + js-tokens: 4.0.0 + picocolors: 1.1.1 + + '@babel/compat-data@7.29.7': {} + + '@babel/core@7.29.7(supports-color@10.2.2)': + 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/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2) + '@babel/helpers': 7.29.7 + '@babel/parser': 7.29.7 + '@babel/template': 7.29.7 + '@babel/traverse': 7.29.7(supports-color@10.2.2) + '@babel/types': 7.29.7 + '@jridgewell/remapping': 2.3.5 + convert-source-map: 2.0.0 + debug: 4.4.3(supports-color@10.2.2) + gensync: 1.0.0-beta.2 + json5: 2.2.3 + semver: 6.3.1 + transitivePeerDependencies: + - supports-color + + '@babel/generator@7.29.7': + dependencies: + '@babel/parser': 7.29.7 + '@babel/types': 7.29.7 + '@jridgewell/gen-mapping': 0.3.13 + '@jridgewell/trace-mapping': 0.3.31 + jsesc: 3.1.0 + + '@babel/helper-annotate-as-pure@7.29.7': + dependencies: + '@babel/types': 7.29.7 + + '@babel/helper-compilation-targets@7.29.7': + dependencies: + '@babel/compat-data': 7.29.7 + '@babel/helper-validator-option': 7.29.7 + browserslist: 4.28.7 + lru-cache: 5.1.1 + semver: 6.3.1 + + '@babel/helper-create-class-features-plugin@7.29.7(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2)': + dependencies: + '@babel/core': 7.29.7(supports-color@10.2.2) + '@babel/helper-annotate-as-pure': 7.29.7 + '@babel/helper-member-expression-to-functions': 7.29.7(supports-color@10.2.2) + '@babel/helper-optimise-call-expression': 7.29.7 + '@babel/helper-replace-supers': 7.29.7(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2) + '@babel/helper-skip-transparent-expression-wrappers': 7.29.7(supports-color@10.2.2) + '@babel/traverse': 7.29.7(supports-color@10.2.2) + semver: 6.3.1 + transitivePeerDependencies: + - supports-color + + '@babel/helper-globals@7.29.7': {} + + '@babel/helper-member-expression-to-functions@7.29.7(supports-color@10.2.2)': + dependencies: + '@babel/traverse': 7.29.7(supports-color@10.2.2) + '@babel/types': 7.29.7 + transitivePeerDependencies: + - supports-color + + '@babel/helper-module-imports@7.29.7(supports-color@10.2.2)': + dependencies: + '@babel/traverse': 7.29.7(supports-color@10.2.2) + '@babel/types': 7.29.7 + transitivePeerDependencies: + - supports-color + + '@babel/helper-module-transforms@7.29.7(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2)': + dependencies: + '@babel/core': 7.29.7(supports-color@10.2.2) + '@babel/helper-module-imports': 7.29.7(supports-color@10.2.2) + '@babel/helper-validator-identifier': 7.29.7 + '@babel/traverse': 7.29.7(supports-color@10.2.2) + transitivePeerDependencies: + - supports-color + + '@babel/helper-optimise-call-expression@7.29.7': + dependencies: + '@babel/types': 7.29.7 + + '@babel/helper-plugin-utils@7.29.7': {} + + '@babel/helper-replace-supers@7.29.7(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2)': + dependencies: + '@babel/core': 7.29.7(supports-color@10.2.2) + '@babel/helper-member-expression-to-functions': 7.29.7(supports-color@10.2.2) + '@babel/helper-optimise-call-expression': 7.29.7 + '@babel/traverse': 7.29.7(supports-color@10.2.2) + transitivePeerDependencies: + - supports-color + + '@babel/helper-skip-transparent-expression-wrappers@7.29.7(supports-color@10.2.2)': + dependencies: + '@babel/traverse': 7.29.7(supports-color@10.2.2) + '@babel/types': 7.29.7 + transitivePeerDependencies: + - supports-color + + '@babel/helper-string-parser@7.29.7': {} + + '@babel/helper-validator-identifier@7.29.7': {} + + '@babel/helper-validator-option@7.29.7': {} + + '@babel/helpers@7.29.7': + dependencies: + '@babel/template': 7.29.7 + '@babel/types': 7.29.7 + + '@babel/parser@7.29.7': + dependencies: + '@babel/types': 7.29.7 + + '@babel/plugin-syntax-jsx@7.29.7(@babel/core@7.29.7(supports-color@10.2.2))': + dependencies: + '@babel/core': 7.29.7(supports-color@10.2.2) + '@babel/helper-plugin-utils': 7.29.7 + + '@babel/plugin-syntax-typescript@7.29.7(@babel/core@7.29.7(supports-color@10.2.2))': + dependencies: + '@babel/core': 7.29.7(supports-color@10.2.2) + '@babel/helper-plugin-utils': 7.29.7 + + '@babel/plugin-transform-modules-commonjs@7.29.7(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2)': + dependencies: + '@babel/core': 7.29.7(supports-color@10.2.2) + '@babel/helper-module-transforms': 7.29.7(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2) + '@babel/helper-plugin-utils': 7.29.7 + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-typescript@7.29.7(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2)': + dependencies: + '@babel/core': 7.29.7(supports-color@10.2.2) + '@babel/helper-annotate-as-pure': 7.29.7 + '@babel/helper-create-class-features-plugin': 7.29.7(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2) + '@babel/helper-plugin-utils': 7.29.7 + '@babel/helper-skip-transparent-expression-wrappers': 7.29.7(supports-color@10.2.2) + '@babel/plugin-syntax-typescript': 7.29.7(@babel/core@7.29.7(supports-color@10.2.2)) + transitivePeerDependencies: + - supports-color + + '@babel/preset-typescript@7.29.7(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2)': + dependencies: + '@babel/core': 7.29.7(supports-color@10.2.2) + '@babel/helper-plugin-utils': 7.29.7 + '@babel/helper-validator-option': 7.29.7 + '@babel/plugin-syntax-jsx': 7.29.7(@babel/core@7.29.7(supports-color@10.2.2)) + '@babel/plugin-transform-modules-commonjs': 7.29.7(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2) + '@babel/plugin-transform-typescript': 7.29.7(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2) + transitivePeerDependencies: + - supports-color + + '@babel/runtime@7.29.7': {} + + '@babel/template@7.29.7': + dependencies: + '@babel/code-frame': 7.29.7 + '@babel/parser': 7.29.7 + '@babel/types': 7.29.7 + + '@babel/traverse@7.29.7(supports-color@10.2.2)': + 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.4.3(supports-color@10.2.2) + transitivePeerDependencies: + - supports-color + + '@babel/types@7.29.7': + dependencies: + '@babel/helper-string-parser': 7.29.7 + '@babel/helper-validator-identifier': 7.29.7 + + '@base-ui/react@1.6.0(@types/react@19.2.17)(react-dom@19.2.8(react@19.2.8))(react@19.2.8)': + dependencies: + '@babel/runtime': 7.29.7 + '@base-ui/utils': 0.3.1(@types/react@19.2.17)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) + '@floating-ui/react-dom': 2.1.9(react-dom@19.2.8(react@19.2.8))(react@19.2.8) + '@floating-ui/utils': 0.2.12 + react: 19.2.8 + react-dom: 19.2.8(react@19.2.8) + use-sync-external-store: 1.6.0(react@19.2.8) + optionalDependencies: + '@types/react': 19.2.17 + + '@base-ui/utils@0.3.1(@types/react@19.2.17)(react-dom@19.2.8(react@19.2.8))(react@19.2.8)': + dependencies: + '@babel/runtime': 7.29.7 + '@floating-ui/utils': 0.2.12 + react: 19.2.8 + react-dom: 19.2.8(react@19.2.8) + reselect: 5.2.0 + use-sync-external-store: 1.6.0(react@19.2.8) + optionalDependencies: + '@types/react': 19.2.17 + + '@dotenvx/dotenvx@1.75.1': + dependencies: + '@dotenvx/primitives': 0.8.0 + commander: 11.1.0 + conf: 10.2.0 + dotenv: 17.4.2 + enquirer: 2.4.1 + env-paths: 2.2.1 + execa: 5.1.1 + fdir: 6.5.0(picomatch@4.0.5) + ignore: 5.3.2 + object-treeify: 1.1.33 + open: 8.4.2 + picomatch: 4.0.5 + systeminformation: 5.33.1 + undici: 7.29.0 + which: 4.0.0 + yocto-spinner: 1.2.2 + + '@dotenvx/primitives@0.8.0': {} + + '@emnapi/core@1.11.1': + dependencies: + '@emnapi/wasi-threads': 1.2.2 + tslib: 2.8.1 + optional: true + + '@emnapi/runtime@1.11.1': + dependencies: + tslib: 2.8.1 + optional: true + + '@emnapi/wasi-threads@1.2.2': + dependencies: + tslib: 2.8.1 + optional: true + + '@floating-ui/core@1.8.0': + dependencies: + '@floating-ui/utils': 0.2.12 + + '@floating-ui/dom@1.8.0': + dependencies: + '@floating-ui/core': 1.8.0 + '@floating-ui/utils': 0.2.12 + + '@floating-ui/react-dom@2.1.9(react-dom@19.2.8(react@19.2.8))(react@19.2.8)': + dependencies: + '@floating-ui/dom': 1.8.0 + react: 19.2.8 + react-dom: 19.2.8(react@19.2.8) + + '@floating-ui/utils@0.2.12': {} + + '@fontsource-variable/onest@5.3.0': {} + + '@hono/node-server@1.19.15(hono@4.12.32)': + dependencies: + hono: 4.12.32 + + '@hookform/resolvers@5.4.0(react-hook-form@7.82.0(react@19.2.8))': + dependencies: + '@standard-schema/utils': 0.3.0 + react-hook-form: 7.82.0(react@19.2.8) + + '@jridgewell/gen-mapping@0.3.13': + dependencies: + '@jridgewell/sourcemap-codec': 1.5.5 + '@jridgewell/trace-mapping': 0.3.31 + + '@jridgewell/remapping@2.3.5': + dependencies: + '@jridgewell/gen-mapping': 0.3.13 + '@jridgewell/trace-mapping': 0.3.31 + + '@jridgewell/resolve-uri@3.1.2': {} + + '@jridgewell/sourcemap-codec@1.5.5': {} + + '@jridgewell/trace-mapping@0.3.31': + dependencies: + '@jridgewell/resolve-uri': 3.1.2 + '@jridgewell/sourcemap-codec': 1.5.5 + + '@modelcontextprotocol/sdk@1.29.0(supports-color@10.2.2)(zod@3.25.76)': + dependencies: + '@hono/node-server': 1.19.15(hono@4.12.32) + ajv: 8.20.0 + ajv-formats: 3.0.1(ajv@8.20.0) + content-type: 1.0.5 + cors: 2.8.6 + cross-spawn: 7.0.6 + eventsource: 3.0.7 + eventsource-parser: 3.1.0 + express: 5.2.1(supports-color@10.2.2) + express-rate-limit: 8.6.0(express@5.2.1(supports-color@10.2.2))(supports-color@10.2.2) + hono: 4.12.32 + jose: 6.2.4 + json-schema-typed: 8.0.2 + pkce-challenge: 5.0.1 + raw-body: 3.0.2 + zod: 3.25.76 + zod-to-json-schema: 3.25.2(zod@3.25.76) + transitivePeerDependencies: + - supports-color + + '@napi-rs/wasm-runtime@1.1.6(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.1)': + dependencies: + '@emnapi/core': 1.11.1 + '@emnapi/runtime': 1.11.1 + '@tybys/wasm-util': 0.10.3 + optional: true + + '@nodelib/fs.scandir@2.1.5': + dependencies: + '@nodelib/fs.stat': 2.0.5 + run-parallel: 1.2.0 + + '@nodelib/fs.stat@2.0.5': {} + + '@nodelib/fs.walk@1.2.8': + dependencies: + '@nodelib/fs.scandir': 2.1.5 + fastq: 1.20.1 + + '@oxc-project/types@0.139.0': {} + + '@oxlint-tsgolint/darwin-arm64@7.0.2001': + optional: true + + '@oxlint-tsgolint/darwin-x64@7.0.2001': + optional: true + + '@oxlint-tsgolint/linux-arm64@7.0.2001': + optional: true + + '@oxlint-tsgolint/linux-x64@7.0.2001': + optional: true + + '@oxlint-tsgolint/win32-arm64@7.0.2001': + optional: true + + '@oxlint-tsgolint/win32-x64@7.0.2001': + optional: true + + '@oxlint/binding-android-arm-eabi@1.75.0': + optional: true + + '@oxlint/binding-android-arm64@1.75.0': + optional: true + + '@oxlint/binding-darwin-arm64@1.75.0': + optional: true + + '@oxlint/binding-darwin-x64@1.75.0': + optional: true + + '@oxlint/binding-freebsd-x64@1.75.0': + optional: true + + '@oxlint/binding-linux-arm-gnueabihf@1.75.0': + optional: true + + '@oxlint/binding-linux-arm-musleabihf@1.75.0': + optional: true + + '@oxlint/binding-linux-arm64-gnu@1.75.0': + optional: true + + '@oxlint/binding-linux-arm64-musl@1.75.0': + optional: true + + '@oxlint/binding-linux-ppc64-gnu@1.75.0': + optional: true + + '@oxlint/binding-linux-riscv64-gnu@1.75.0': + optional: true + + '@oxlint/binding-linux-riscv64-musl@1.75.0': + optional: true + + '@oxlint/binding-linux-s390x-gnu@1.75.0': + optional: true + + '@oxlint/binding-linux-x64-gnu@1.75.0': + optional: true + + '@oxlint/binding-linux-x64-musl@1.75.0': + optional: true + + '@oxlint/binding-openharmony-arm64@1.75.0': + optional: true + + '@oxlint/binding-win32-arm64-msvc@1.75.0': + optional: true + + '@oxlint/binding-win32-ia32-msvc@1.75.0': + optional: true + + '@oxlint/binding-win32-x64-msvc@1.75.0': + optional: true + + '@rolldown/binding-android-arm64@1.1.5': + optional: true + + '@rolldown/binding-darwin-arm64@1.1.5': + optional: true + + '@rolldown/binding-darwin-x64@1.1.5': + optional: true + + '@rolldown/binding-freebsd-x64@1.1.5': + optional: true + + '@rolldown/binding-linux-arm-gnueabihf@1.1.5': + optional: true + + '@rolldown/binding-linux-arm64-gnu@1.1.5': + optional: true + + '@rolldown/binding-linux-arm64-musl@1.1.5': + optional: true + + '@rolldown/binding-linux-ppc64-gnu@1.1.5': + optional: true + + '@rolldown/binding-linux-s390x-gnu@1.1.5': + optional: true + + '@rolldown/binding-linux-x64-gnu@1.1.5': + optional: true + + '@rolldown/binding-linux-x64-musl@1.1.5': + optional: true + + '@rolldown/binding-openharmony-arm64@1.1.5': + optional: true + + '@rolldown/binding-wasm32-wasi@1.1.5': + dependencies: + '@emnapi/core': 1.11.1 + '@emnapi/runtime': 1.11.1 + '@napi-rs/wasm-runtime': 1.1.6(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.1) + optional: true + + '@rolldown/binding-win32-arm64-msvc@1.1.5': + optional: true + + '@rolldown/binding-win32-x64-msvc@1.1.5': + optional: true + + '@rolldown/pluginutils@1.0.1': {} + + '@sec-ant/readable-stream@0.4.1': {} + + '@sindresorhus/merge-streams@4.0.0': {} + + '@standard-schema/utils@0.3.0': {} + + '@tailwindcss/node@4.3.3': + dependencies: + '@jridgewell/remapping': 2.3.5 + enhanced-resolve: 5.24.3 + jiti: 2.7.0 + lightningcss: 1.32.0 + magic-string: 0.30.21 + source-map-js: 1.2.1 + tailwindcss: 4.3.3 + + '@tailwindcss/oxide-android-arm64@4.3.3': + optional: true + + '@tailwindcss/oxide-darwin-arm64@4.3.3': + optional: true + + '@tailwindcss/oxide-darwin-x64@4.3.3': + optional: true + + '@tailwindcss/oxide-freebsd-x64@4.3.3': + optional: true + + '@tailwindcss/oxide-linux-arm-gnueabihf@4.3.3': + optional: true + + '@tailwindcss/oxide-linux-arm64-gnu@4.3.3': + optional: true + + '@tailwindcss/oxide-linux-arm64-musl@4.3.3': + optional: true + + '@tailwindcss/oxide-linux-x64-gnu@4.3.3': + optional: true + + '@tailwindcss/oxide-linux-x64-musl@4.3.3': + optional: true + + '@tailwindcss/oxide-wasm32-wasi@4.3.3': + optional: true + + '@tailwindcss/oxide-win32-arm64-msvc@4.3.3': + optional: true + + '@tailwindcss/oxide-win32-x64-msvc@4.3.3': + optional: true + + '@tailwindcss/oxide@4.3.3': + optionalDependencies: + '@tailwindcss/oxide-android-arm64': 4.3.3 + '@tailwindcss/oxide-darwin-arm64': 4.3.3 + '@tailwindcss/oxide-darwin-x64': 4.3.3 + '@tailwindcss/oxide-freebsd-x64': 4.3.3 + '@tailwindcss/oxide-linux-arm-gnueabihf': 4.3.3 + '@tailwindcss/oxide-linux-arm64-gnu': 4.3.3 + '@tailwindcss/oxide-linux-arm64-musl': 4.3.3 + '@tailwindcss/oxide-linux-x64-gnu': 4.3.3 + '@tailwindcss/oxide-linux-x64-musl': 4.3.3 + '@tailwindcss/oxide-wasm32-wasi': 4.3.3 + '@tailwindcss/oxide-win32-arm64-msvc': 4.3.3 + '@tailwindcss/oxide-win32-x64-msvc': 4.3.3 + + '@tailwindcss/vite@4.3.3(vite@8.1.5(@types/node@24.13.3)(jiti@2.7.0))': + dependencies: + '@tailwindcss/node': 4.3.3 + '@tailwindcss/oxide': 4.3.3 + tailwindcss: 4.3.3 + vite: 8.1.5(@types/node@24.13.3)(jiti@2.7.0) + + '@tanstack/history@1.162.0': {} + + '@tanstack/query-core@5.101.4': {} + + '@tanstack/query-devtools@5.101.4': {} + + '@tanstack/react-query-devtools@5.101.4(@tanstack/react-query@5.101.4(react@19.2.8))(react@19.2.8)': + dependencies: + '@tanstack/query-devtools': 5.101.4 + '@tanstack/react-query': 5.101.4(react@19.2.8) + react: 19.2.8 + + '@tanstack/react-query@5.101.4(react@19.2.8)': + dependencies: + '@tanstack/query-core': 5.101.4 + react: 19.2.8 + + '@tanstack/react-router-devtools@1.167.0(@tanstack/react-router@1.170.18(react-dom@19.2.8(react@19.2.8))(react@19.2.8))(@tanstack/router-core@1.171.15)(csstype@3.2.3)(react-dom@19.2.8(react@19.2.8))(react@19.2.8)': + dependencies: + '@tanstack/react-router': 1.170.18(react-dom@19.2.8(react@19.2.8))(react@19.2.8) + '@tanstack/router-devtools-core': 1.168.0(@tanstack/router-core@1.171.15)(csstype@3.2.3) + react: 19.2.8 + react-dom: 19.2.8(react@19.2.8) + optionalDependencies: + '@tanstack/router-core': 1.171.15 + transitivePeerDependencies: + - csstype + + '@tanstack/react-router@1.170.18(react-dom@19.2.8(react@19.2.8))(react@19.2.8)': + dependencies: + '@tanstack/history': 1.162.0 + '@tanstack/react-store': 0.9.3(react-dom@19.2.8(react@19.2.8))(react@19.2.8) + '@tanstack/router-core': 1.171.15 + isbot: 5.2.1 + react: 19.2.8 + react-dom: 19.2.8(react@19.2.8) + + '@tanstack/react-store@0.9.3(react-dom@19.2.8(react@19.2.8))(react@19.2.8)': + dependencies: + '@tanstack/store': 0.9.3 + react: 19.2.8 + react-dom: 19.2.8(react@19.2.8) + use-sync-external-store: 1.6.0(react@19.2.8) + + '@tanstack/router-core@1.171.15': + dependencies: + '@tanstack/history': 1.162.0 + cookie-es: 3.1.1 + seroval: 1.5.6 + seroval-plugins: 1.5.6(seroval@1.5.6) + + '@tanstack/router-devtools-core@1.168.0(@tanstack/router-core@1.171.15)(csstype@3.2.3)': + dependencies: + '@tanstack/router-core': 1.171.15 + clsx: 2.1.1 + goober: 2.1.19(csstype@3.2.3) + optionalDependencies: + csstype: 3.2.3 + + '@tanstack/router-generator@1.167.21(supports-color@10.2.2)': + dependencies: + '@babel/types': 7.29.7 + '@tanstack/router-core': 1.171.15 + '@tanstack/router-utils': 1.162.2(supports-color@10.2.2) + '@tanstack/virtual-file-routes': 1.162.0 + jiti: 2.7.0 + magic-string: 0.30.21 + prettier: 3.9.6 + zod: 4.4.3 + transitivePeerDependencies: + - supports-color + + '@tanstack/router-plugin@1.168.23(@tanstack/react-router@1.170.18(react-dom@19.2.8(react@19.2.8))(react@19.2.8))(rolldown@1.1.5)(supports-color@10.2.2)(vite@8.1.5(@types/node@24.13.3)(jiti@2.7.0))': + dependencies: + '@babel/core': 7.29.7(supports-color@10.2.2) + '@babel/template': 7.29.7 + '@babel/types': 7.29.7 + '@tanstack/router-core': 1.171.15 + '@tanstack/router-generator': 1.167.21(supports-color@10.2.2) + '@tanstack/router-utils': 1.162.2(supports-color@10.2.2) + chokidar: 5.0.0 + unplugin: 3.3.0(rolldown@1.1.5)(vite@8.1.5(@types/node@24.13.3)(jiti@2.7.0)) + zod: 4.4.3 + optionalDependencies: + '@tanstack/react-router': 1.170.18(react-dom@19.2.8(react@19.2.8))(react@19.2.8) + vite: 8.1.5(@types/node@24.13.3)(jiti@2.7.0) + transitivePeerDependencies: + - '@farmfe/core' + - '@rspack/core' + - bun-types-no-globals + - esbuild + - rolldown + - rollup + - supports-color + - unloader + + '@tanstack/router-utils@1.162.2(supports-color@10.2.2)': + dependencies: + '@babel/generator': 7.29.7 + '@babel/parser': 7.29.7 + '@babel/types': 7.29.7 + ansis: 4.3.1 + babel-dead-code-elimination: 1.0.12(supports-color@10.2.2) + diff: 8.0.4 + pathe: 2.0.3 + tinyglobby: 0.2.17 + transitivePeerDependencies: + - supports-color + + '@tanstack/store@0.9.3': {} + + '@tanstack/virtual-file-routes@1.162.0': {} + + '@ts-morph/common@0.27.0': + dependencies: + fast-glob: 3.3.3 + minimatch: 10.2.5 + path-browserify: 1.0.1 + + '@tybys/wasm-util@0.10.3': + dependencies: + tslib: 2.8.1 + optional: true + + '@types/debug@4.1.13': + dependencies: + '@types/ms': 2.1.0 + + '@types/estree-jsx@1.0.5': + dependencies: + '@types/estree': 1.0.9 + + '@types/estree@1.0.9': {} + + '@types/hast@3.0.5': + dependencies: + '@types/unist': 3.0.3 + + '@types/mdast@4.0.4': + dependencies: + '@types/unist': 3.0.3 + + '@types/ms@2.1.0': {} + + '@types/node@24.13.3': + dependencies: + undici-types: 7.18.2 + + '@types/react-dom@19.2.3(@types/react@19.2.17)': + dependencies: + '@types/react': 19.2.17 + + '@types/react@19.2.17': + dependencies: + csstype: 3.2.3 + + '@types/unist@2.0.11': {} + + '@types/unist@3.0.3': {} + + '@types/validate-npm-package-name@4.0.2': {} + + '@typescript/typescript-aix-ppc64@7.0.2': + optional: true + + '@typescript/typescript-darwin-arm64@7.0.2': + optional: true + + '@typescript/typescript-darwin-x64@7.0.2': + optional: true + + '@typescript/typescript-freebsd-arm64@7.0.2': + optional: true + + '@typescript/typescript-freebsd-x64@7.0.2': + optional: true + + '@typescript/typescript-linux-arm64@7.0.2': + optional: true + + '@typescript/typescript-linux-arm@7.0.2': + optional: true + + '@typescript/typescript-linux-loong64@7.0.2': + optional: true + + '@typescript/typescript-linux-mips64el@7.0.2': + optional: true + + '@typescript/typescript-linux-ppc64@7.0.2': + optional: true + + '@typescript/typescript-linux-riscv64@7.0.2': + optional: true + + '@typescript/typescript-linux-s390x@7.0.2': + optional: true + + '@typescript/typescript-linux-x64@7.0.2': + optional: true + + '@typescript/typescript-netbsd-arm64@7.0.2': + optional: true + + '@typescript/typescript-netbsd-x64@7.0.2': + optional: true + + '@typescript/typescript-openbsd-arm64@7.0.2': + optional: true + + '@typescript/typescript-openbsd-x64@7.0.2': + optional: true + + '@typescript/typescript-sunos-x64@7.0.2': + optional: true + + '@typescript/typescript-win32-arm64@7.0.2': + optional: true + + '@typescript/typescript-win32-x64@7.0.2': + optional: true + + '@ungap/structured-clone@1.3.3': {} + + '@vitejs/plugin-react@6.0.4(vite@8.1.5(@types/node@24.13.3)(jiti@2.7.0))': + dependencies: + '@rolldown/pluginutils': 1.0.1 + vite: 8.1.5(@types/node@24.13.3)(jiti@2.7.0) + + accepts@2.0.0: + dependencies: + mime-types: 3.0.2 + negotiator: 1.0.0 + + ajv-formats@2.1.1(ajv@8.20.0): + optionalDependencies: + ajv: 8.20.0 + + ajv-formats@3.0.1(ajv@8.20.0): + optionalDependencies: + ajv: 8.20.0 + + ajv@8.20.0: + dependencies: + fast-deep-equal: 3.1.3 + fast-uri: 3.1.4 + json-schema-traverse: 1.0.0 + require-from-string: 2.0.2 + + ansi-colors@4.1.3: {} + + ansi-regex@5.0.1: {} + + ansi-regex@6.2.2: {} + + ansis@4.3.1: {} + + argparse@2.0.1: {} + + ast-types@0.16.1: + dependencies: + tslib: 2.8.1 + + atomically@1.7.0: {} + + babel-dead-code-elimination@1.0.12(supports-color@10.2.2): + dependencies: + '@babel/core': 7.29.7(supports-color@10.2.2) + '@babel/parser': 7.29.7 + '@babel/traverse': 7.29.7(supports-color@10.2.2) + '@babel/types': 7.29.7 + transitivePeerDependencies: + - supports-color + + bail@2.0.2: {} + + balanced-match@4.0.4: {} + + baseline-browser-mapping@2.11.1: {} + + body-parser@2.3.0(supports-color@10.2.2): + dependencies: + bytes: 3.1.2 + content-type: 2.0.0 + debug: 4.4.3(supports-color@10.2.2) + http-errors: 2.0.1 + iconv-lite: 0.7.3 + on-finished: 2.4.1 + qs: 6.15.3 + raw-body: 3.0.2 + type-is: 2.1.0 + transitivePeerDependencies: + - supports-color + + brace-expansion@5.0.8: + dependencies: + balanced-match: 4.0.4 + + braces@3.0.3: + dependencies: + fill-range: 7.1.1 + + browserslist@4.28.7: + dependencies: + baseline-browser-mapping: 2.11.1 + caniuse-lite: 1.0.30001806 + electron-to-chromium: 1.5.396 + node-releases: 2.0.51 + update-browserslist-db: 1.2.3(browserslist@4.28.7) + + bundle-name@4.1.0: + dependencies: + run-applescript: 7.1.0 + + bytes@3.1.2: {} + + call-bind-apply-helpers@1.0.2: + dependencies: + es-errors: 1.3.0 + function-bind: 1.1.2 + + call-bound@1.0.4: + dependencies: + call-bind-apply-helpers: 1.0.2 + get-intrinsic: 1.3.0 + + callsites@3.1.0: {} + + caniuse-lite@1.0.30001806: {} + + ccount@2.0.1: {} + + chalk@5.6.2: {} + + character-entities-html4@2.1.0: {} + + character-entities-legacy@3.0.0: {} + + character-entities@2.0.2: {} + + character-reference-invalid@2.0.1: {} + + chokidar@5.0.0: + dependencies: + readdirp: 5.0.0 + + class-variance-authority@0.7.1: + dependencies: + clsx: 2.1.1 + + cli-cursor@5.0.0: + dependencies: + restore-cursor: 5.1.0 + + cli-spinners@2.9.2: {} + + clsx@2.1.1: {} + + code-block-writer@13.0.3: {} + + comma-separated-tokens@2.0.3: {} + + commander@11.1.0: {} + + commander@14.0.3: {} + + conf@10.2.0: + dependencies: + ajv: 8.20.0 + ajv-formats: 2.1.1(ajv@8.20.0) + atomically: 1.7.0 + debounce-fn: 4.0.0 + dot-prop: 6.0.1 + env-paths: 2.2.1 + json-schema-typed: 7.0.3 + onetime: 5.1.2 + pkg-up: 3.1.0 + semver: 7.8.5 + + content-disposition@1.1.0: {} + + content-type@1.0.5: {} + + content-type@2.0.0: {} + + convert-source-map@2.0.0: {} + + cookie-es@3.1.1: {} + + cookie-signature@1.2.2: {} + + cookie@0.7.2: {} + + cors@2.8.6: + dependencies: + object-assign: 4.1.1 + vary: 1.1.2 + + cosmiconfig@9.0.2(typescript@7.0.2): + dependencies: + env-paths: 2.2.1 + import-fresh: 3.3.1 + js-yaml: 4.3.0 + parse-json: 5.2.0 + optionalDependencies: + typescript: 7.0.2 + + cross-spawn@7.0.6: + dependencies: + path-key: 3.1.1 + shebang-command: 2.0.0 + which: 2.0.2 + + cssesc@3.0.0: {} + + csstype@3.2.3: {} + + debounce-fn@4.0.0: + dependencies: + mimic-fn: 3.1.0 + + debug@4.4.3(supports-color@10.2.2): + dependencies: + ms: 2.1.3 + optionalDependencies: + supports-color: 10.2.2 + + decode-named-character-reference@1.3.0: + dependencies: + character-entities: 2.0.2 + + dedent@1.7.2: {} + + deepmerge@4.3.1: {} + + default-browser-id@5.0.1: {} + + default-browser@5.5.0: + dependencies: + bundle-name: 4.1.0 + default-browser-id: 5.0.1 + + define-lazy-prop@2.0.0: {} + + define-lazy-prop@3.0.0: {} + + depd@2.0.0: {} + + dequal@2.0.3: {} + + detect-libc@2.1.2: {} + + devlop@1.1.0: + dependencies: + dequal: 2.0.3 + + diff@8.0.4: {} + + dot-prop@6.0.1: + dependencies: + is-obj: 2.0.0 + + dotenv@17.4.2: {} + + dunder-proto@1.0.1: + dependencies: + call-bind-apply-helpers: 1.0.2 + es-errors: 1.3.0 + gopd: 1.2.0 + + ee-first@1.1.1: {} + + electron-to-chromium@1.5.396: {} + + emoji-regex@10.6.0: {} + + encodeurl@2.0.0: {} + + enhanced-resolve@5.24.3: + dependencies: + graceful-fs: 4.2.11 + tapable: 2.3.3 + + enquirer@2.4.1: + dependencies: + ansi-colors: 4.1.3 + strip-ansi: 6.0.1 + + env-paths@2.2.1: {} + + error-ex@1.3.4: + dependencies: + is-arrayish: 0.2.1 + + es-define-property@1.0.1: {} + + es-errors@1.3.0: {} + + es-object-atoms@1.1.2: + dependencies: + es-errors: 1.3.0 + + escalade@3.2.0: {} + + escape-html@1.0.3: {} + + escape-string-regexp@5.0.0: {} + + esprima@4.0.1: {} + + estree-util-is-identifier-name@3.0.0: {} + + etag@1.8.1: {} + + eventsource-parser@3.1.0: {} + + eventsource@3.0.7: + dependencies: + eventsource-parser: 3.1.0 + + execa@5.1.1: + dependencies: + cross-spawn: 7.0.6 + get-stream: 6.0.1 + human-signals: 2.1.0 + is-stream: 2.0.1 + merge-stream: 2.0.0 + npm-run-path: 4.0.1 + onetime: 5.1.2 + signal-exit: 3.0.7 + strip-final-newline: 2.0.0 + + execa@9.6.1: + dependencies: + '@sindresorhus/merge-streams': 4.0.0 + cross-spawn: 7.0.6 + figures: 6.1.0 + get-stream: 9.0.1 + human-signals: 8.0.1 + is-plain-obj: 4.1.0 + is-stream: 4.0.1 + npm-run-path: 6.0.0 + pretty-ms: 9.3.0 + signal-exit: 4.1.0 + strip-final-newline: 4.0.0 + yoctocolors: 2.1.2 + + express-rate-limit@8.6.0(express@5.2.1(supports-color@10.2.2))(supports-color@10.2.2): + dependencies: + debug: 4.4.3(supports-color@10.2.2) + express: 5.2.1(supports-color@10.2.2) + ip-address: 10.2.0 + transitivePeerDependencies: + - supports-color + + express@5.2.1(supports-color@10.2.2): + dependencies: + accepts: 2.0.0 + body-parser: 2.3.0(supports-color@10.2.2) + content-disposition: 1.1.0 + content-type: 1.0.5 + cookie: 0.7.2 + cookie-signature: 1.2.2 + debug: 4.4.3(supports-color@10.2.2) + depd: 2.0.0 + encodeurl: 2.0.0 + escape-html: 1.0.3 + etag: 1.8.1 + finalhandler: 2.1.1(supports-color@10.2.2) + fresh: 2.0.0 + http-errors: 2.0.1 + merge-descriptors: 2.0.0 + mime-types: 3.0.2 + on-finished: 2.4.1 + once: 1.4.0 + parseurl: 1.3.3 + proxy-addr: 2.0.7 + qs: 6.15.3 + range-parser: 1.3.0 + router: 2.2.0(supports-color@10.2.2) + send: 1.2.1(supports-color@10.2.2) + serve-static: 2.2.1(supports-color@10.2.2) + statuses: 2.0.2 + type-is: 2.1.0 + vary: 1.1.2 + transitivePeerDependencies: + - supports-color + + extend@3.0.2: {} + + fast-deep-equal@3.1.3: {} + + fast-glob@3.3.3: + dependencies: + '@nodelib/fs.stat': 2.0.5 + '@nodelib/fs.walk': 1.2.8 + glob-parent: 5.1.2 + merge2: 1.4.1 + micromatch: 4.0.8 + + fast-uri@3.1.4: {} + + fastq@1.20.1: + dependencies: + reusify: 1.1.0 + + fdir@6.5.0(picomatch@4.0.5): + optionalDependencies: + picomatch: 4.0.5 + + figures@6.1.0: + dependencies: + is-unicode-supported: 2.1.0 + + fill-range@7.1.1: + dependencies: + to-regex-range: 5.0.1 + + finalhandler@2.1.1(supports-color@10.2.2): + dependencies: + debug: 4.4.3(supports-color@10.2.2) + encodeurl: 2.0.0 + escape-html: 1.0.3 + on-finished: 2.4.1 + parseurl: 1.3.3 + statuses: 2.0.2 + transitivePeerDependencies: + - supports-color + + find-up@3.0.0: + dependencies: + locate-path: 3.0.0 + + forwarded@0.2.0: {} + + fresh@2.0.0: {} + + fs-extra@11.4.0: + dependencies: + graceful-fs: 4.2.11 + jsonfile: 6.2.1 + universalify: 2.0.1 + + fsevents@2.3.3: + optional: true + + function-bind@1.1.2: {} + + fuzzysort@3.1.0: {} + + gensync@1.0.0-beta.2: {} + + get-east-asian-width@1.6.0: {} + + get-intrinsic@1.3.0: + dependencies: + call-bind-apply-helpers: 1.0.2 + es-define-property: 1.0.1 + es-errors: 1.3.0 + es-object-atoms: 1.1.2 + function-bind: 1.1.2 + get-proto: 1.0.1 + gopd: 1.2.0 + has-symbols: 1.1.0 + hasown: 2.0.4 + math-intrinsics: 1.1.0 + + get-own-enumerable-keys@1.0.0: {} + + get-proto@1.0.1: + dependencies: + dunder-proto: 1.0.1 + es-object-atoms: 1.1.2 + + get-stream@6.0.1: {} + + get-stream@9.0.1: + dependencies: + '@sec-ant/readable-stream': 0.4.1 + is-stream: 4.0.1 + + glob-parent@5.1.2: + dependencies: + is-glob: 4.0.3 + + goober@2.1.19(csstype@3.2.3): + dependencies: + csstype: 3.2.3 + + gopd@1.2.0: {} + + graceful-fs@4.2.11: {} + + has-symbols@1.1.0: {} + + hasown@2.0.4: + dependencies: + function-bind: 1.1.2 + + hast-util-to-jsx-runtime@2.3.6(supports-color@10.2.2): + dependencies: + '@types/estree': 1.0.9 + '@types/hast': 3.0.5 + '@types/unist': 3.0.3 + comma-separated-tokens: 2.0.3 + devlop: 1.1.0 + estree-util-is-identifier-name: 3.0.0 + hast-util-whitespace: 3.0.0 + mdast-util-mdx-expression: 2.0.1(supports-color@10.2.2) + mdast-util-mdx-jsx: 3.2.0(supports-color@10.2.2) + mdast-util-mdxjs-esm: 2.0.1(supports-color@10.2.2) + property-information: 7.2.0 + space-separated-tokens: 2.0.2 + style-to-js: 1.1.21 + unist-util-position: 5.0.0 + vfile-message: 4.0.3 + transitivePeerDependencies: + - supports-color + + hast-util-whitespace@3.0.0: + dependencies: + '@types/hast': 3.0.5 + + hono@4.12.32: {} + + html-url-attributes@3.0.1: {} + + http-errors@2.0.1: + dependencies: + depd: 2.0.0 + inherits: 2.0.4 + setprototypeof: 1.2.0 + statuses: 2.0.2 + toidentifier: 1.0.1 + + human-signals@2.1.0: {} + + human-signals@8.0.1: {} + + iconv-lite@0.7.3: + dependencies: + safer-buffer: 2.1.2 + + ignore@5.3.2: {} + + import-fresh@3.3.1: + dependencies: + parent-module: 1.0.1 + resolve-from: 4.0.0 + + inherits@2.0.4: {} + + inline-style-parser@0.2.7: {} + + ip-address@10.2.0: {} + + ipaddr.js@1.9.1: {} + + is-alphabetical@2.0.1: {} + + is-alphanumerical@2.0.1: + dependencies: + is-alphabetical: 2.0.1 + is-decimal: 2.0.1 + + is-arrayish@0.2.1: {} + + is-decimal@2.0.1: {} + + is-docker@2.2.1: {} + + is-docker@3.0.0: {} + + is-extglob@2.1.1: {} + + is-glob@4.0.3: + dependencies: + is-extglob: 2.1.1 + + is-hexadecimal@2.0.1: {} + + is-in-ssh@1.0.0: {} + + is-inside-container@1.0.0: + dependencies: + is-docker: 3.0.0 + + is-interactive@2.0.0: {} + + is-number@7.0.0: {} + + is-obj@2.0.0: {} + + is-obj@3.0.0: {} + + is-plain-obj@4.1.0: {} + + is-promise@4.0.0: {} + + is-regexp@3.1.0: {} + + is-stream@2.0.1: {} + + is-stream@4.0.1: {} + + is-unicode-supported@1.3.0: {} + + is-unicode-supported@2.1.0: {} + + is-wsl@2.2.0: + dependencies: + is-docker: 2.2.1 + + is-wsl@3.1.1: + dependencies: + is-inside-container: 1.0.0 + + isbot@5.2.1: {} + + isexe@2.0.0: {} + + isexe@3.1.5: {} + + jiti@2.7.0: {} + + jose@6.2.4: {} + + js-tokens@4.0.0: {} + + js-yaml@4.3.0: + dependencies: + argparse: 2.0.1 + + jsesc@3.1.0: {} + + json-parse-even-better-errors@2.3.1: {} + + json-schema-traverse@1.0.0: {} + + json-schema-typed@7.0.3: {} + + json-schema-typed@8.0.2: {} + + json5@2.2.3: {} + + jsonfile@6.2.1: + dependencies: + universalify: 2.0.1 + optionalDependencies: + graceful-fs: 4.2.11 + + kleur@3.0.3: {} + + kleur@4.1.5: {} + + lightningcss-android-arm64@1.32.0: + optional: true + + lightningcss-android-arm64@1.33.0: + optional: true + + lightningcss-darwin-arm64@1.32.0: + optional: true + + lightningcss-darwin-arm64@1.33.0: + optional: true + + lightningcss-darwin-x64@1.32.0: + optional: true + + lightningcss-darwin-x64@1.33.0: + optional: true + + lightningcss-freebsd-x64@1.32.0: + optional: true + + lightningcss-freebsd-x64@1.33.0: + optional: true + + lightningcss-linux-arm-gnueabihf@1.32.0: + optional: true + + lightningcss-linux-arm-gnueabihf@1.33.0: + optional: true + + lightningcss-linux-arm64-gnu@1.32.0: + optional: true + + lightningcss-linux-arm64-gnu@1.33.0: + optional: true + + lightningcss-linux-arm64-musl@1.32.0: + optional: true + + lightningcss-linux-arm64-musl@1.33.0: + optional: true + + lightningcss-linux-x64-gnu@1.32.0: + optional: true + + lightningcss-linux-x64-gnu@1.33.0: + optional: true + + lightningcss-linux-x64-musl@1.32.0: + optional: true + + lightningcss-linux-x64-musl@1.33.0: + optional: true + + lightningcss-win32-arm64-msvc@1.32.0: + optional: true + + lightningcss-win32-arm64-msvc@1.33.0: + optional: true + + lightningcss-win32-x64-msvc@1.32.0: + optional: true + + lightningcss-win32-x64-msvc@1.33.0: + optional: true + + lightningcss@1.32.0: + dependencies: + detect-libc: 2.1.2 + optionalDependencies: + lightningcss-android-arm64: 1.32.0 + lightningcss-darwin-arm64: 1.32.0 + lightningcss-darwin-x64: 1.32.0 + lightningcss-freebsd-x64: 1.32.0 + lightningcss-linux-arm-gnueabihf: 1.32.0 + lightningcss-linux-arm64-gnu: 1.32.0 + lightningcss-linux-arm64-musl: 1.32.0 + lightningcss-linux-x64-gnu: 1.32.0 + lightningcss-linux-x64-musl: 1.32.0 + lightningcss-win32-arm64-msvc: 1.32.0 + lightningcss-win32-x64-msvc: 1.32.0 + + lightningcss@1.33.0: + dependencies: + detect-libc: 2.1.2 + optionalDependencies: + lightningcss-android-arm64: 1.33.0 + lightningcss-darwin-arm64: 1.33.0 + lightningcss-darwin-x64: 1.33.0 + lightningcss-freebsd-x64: 1.33.0 + lightningcss-linux-arm-gnueabihf: 1.33.0 + lightningcss-linux-arm64-gnu: 1.33.0 + lightningcss-linux-arm64-musl: 1.33.0 + lightningcss-linux-x64-gnu: 1.33.0 + lightningcss-linux-x64-musl: 1.33.0 + lightningcss-win32-arm64-msvc: 1.33.0 + lightningcss-win32-x64-msvc: 1.33.0 + + lines-and-columns@1.2.4: {} + + locate-path@3.0.0: + dependencies: + p-locate: 3.0.0 + path-exists: 3.0.0 + + log-symbols@6.0.0: + dependencies: + chalk: 5.6.2 + is-unicode-supported: 1.3.0 + + longest-streak@3.1.0: {} + + lru-cache@5.1.1: + dependencies: + yallist: 3.1.1 + + lucide-react@1.26.0(react@19.2.8): + dependencies: + react: 19.2.8 + + magic-string@0.30.21: + dependencies: + '@jridgewell/sourcemap-codec': 1.5.5 + + markdown-table@3.0.4: {} + + math-intrinsics@1.1.0: {} + + mdast-util-find-and-replace@3.0.2: + dependencies: + '@types/mdast': 4.0.4 + escape-string-regexp: 5.0.0 + unist-util-is: 6.0.1 + unist-util-visit-parents: 6.0.2 + + mdast-util-from-markdown@2.0.3(supports-color@10.2.2): + dependencies: + '@types/mdast': 4.0.4 + '@types/unist': 3.0.3 + decode-named-character-reference: 1.3.0 + devlop: 1.1.0 + mdast-util-to-string: 4.0.0 + micromark: 4.0.2(supports-color@10.2.2) + micromark-util-decode-numeric-character-reference: 2.0.2 + micromark-util-decode-string: 2.0.1 + micromark-util-normalize-identifier: 2.0.1 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + unist-util-stringify-position: 4.0.0 + transitivePeerDependencies: + - supports-color + + mdast-util-gfm-autolink-literal@2.0.1: + dependencies: + '@types/mdast': 4.0.4 + ccount: 2.0.1 + devlop: 1.1.0 + mdast-util-find-and-replace: 3.0.2 + micromark-util-character: 2.1.1 + + mdast-util-gfm-footnote@2.1.0(supports-color@10.2.2): + dependencies: + '@types/mdast': 4.0.4 + devlop: 1.1.0 + mdast-util-from-markdown: 2.0.3(supports-color@10.2.2) + mdast-util-to-markdown: 2.1.2 + micromark-util-normalize-identifier: 2.0.1 + transitivePeerDependencies: + - supports-color + + mdast-util-gfm-strikethrough@2.0.0(supports-color@10.2.2): + dependencies: + '@types/mdast': 4.0.4 + mdast-util-from-markdown: 2.0.3(supports-color@10.2.2) + mdast-util-to-markdown: 2.1.2 + transitivePeerDependencies: + - supports-color + + mdast-util-gfm-table@2.0.0(supports-color@10.2.2): + dependencies: + '@types/mdast': 4.0.4 + devlop: 1.1.0 + markdown-table: 3.0.4 + mdast-util-from-markdown: 2.0.3(supports-color@10.2.2) + mdast-util-to-markdown: 2.1.2 + transitivePeerDependencies: + - supports-color + + mdast-util-gfm-task-list-item@2.0.0(supports-color@10.2.2): + dependencies: + '@types/mdast': 4.0.4 + devlop: 1.1.0 + mdast-util-from-markdown: 2.0.3(supports-color@10.2.2) + mdast-util-to-markdown: 2.1.2 + transitivePeerDependencies: + - supports-color + + mdast-util-gfm@3.1.0(supports-color@10.2.2): + dependencies: + mdast-util-from-markdown: 2.0.3(supports-color@10.2.2) + mdast-util-gfm-autolink-literal: 2.0.1 + mdast-util-gfm-footnote: 2.1.0(supports-color@10.2.2) + mdast-util-gfm-strikethrough: 2.0.0(supports-color@10.2.2) + mdast-util-gfm-table: 2.0.0(supports-color@10.2.2) + mdast-util-gfm-task-list-item: 2.0.0(supports-color@10.2.2) + mdast-util-to-markdown: 2.1.2 + transitivePeerDependencies: + - supports-color + + mdast-util-mdx-expression@2.0.1(supports-color@10.2.2): + dependencies: + '@types/estree-jsx': 1.0.5 + '@types/hast': 3.0.5 + '@types/mdast': 4.0.4 + devlop: 1.1.0 + mdast-util-from-markdown: 2.0.3(supports-color@10.2.2) + mdast-util-to-markdown: 2.1.2 + transitivePeerDependencies: + - supports-color + + mdast-util-mdx-jsx@3.2.0(supports-color@10.2.2): + dependencies: + '@types/estree-jsx': 1.0.5 + '@types/hast': 3.0.5 + '@types/mdast': 4.0.4 + '@types/unist': 3.0.3 + ccount: 2.0.1 + devlop: 1.1.0 + mdast-util-from-markdown: 2.0.3(supports-color@10.2.2) + mdast-util-to-markdown: 2.1.2 + parse-entities: 4.0.2 + stringify-entities: 4.0.4 + unist-util-stringify-position: 4.0.0 + vfile-message: 4.0.3 + transitivePeerDependencies: + - supports-color + + mdast-util-mdxjs-esm@2.0.1(supports-color@10.2.2): + dependencies: + '@types/estree-jsx': 1.0.5 + '@types/hast': 3.0.5 + '@types/mdast': 4.0.4 + devlop: 1.1.0 + mdast-util-from-markdown: 2.0.3(supports-color@10.2.2) + mdast-util-to-markdown: 2.1.2 + transitivePeerDependencies: + - supports-color + + mdast-util-phrasing@4.1.0: + dependencies: + '@types/mdast': 4.0.4 + unist-util-is: 6.0.1 + + mdast-util-to-hast@13.2.1: + dependencies: + '@types/hast': 3.0.5 + '@types/mdast': 4.0.4 + '@ungap/structured-clone': 1.3.3 + devlop: 1.1.0 + micromark-util-sanitize-uri: 2.0.1 + trim-lines: 3.0.1 + unist-util-position: 5.0.0 + unist-util-visit: 5.1.0 + vfile: 6.0.3 + + mdast-util-to-markdown@2.1.2: + dependencies: + '@types/mdast': 4.0.4 + '@types/unist': 3.0.3 + longest-streak: 3.1.0 + mdast-util-phrasing: 4.1.0 + mdast-util-to-string: 4.0.0 + micromark-util-classify-character: 2.0.1 + micromark-util-decode-string: 2.0.1 + unist-util-visit: 5.1.0 + zwitch: 2.0.4 + + mdast-util-to-string@4.0.0: + dependencies: + '@types/mdast': 4.0.4 + + media-typer@1.1.1: {} + + merge-descriptors@2.0.0: {} + + merge-stream@2.0.0: {} + + merge2@1.4.1: {} + + micromark-core-commonmark@2.0.3: + dependencies: + decode-named-character-reference: 1.3.0 + devlop: 1.1.0 + micromark-factory-destination: 2.0.1 + micromark-factory-label: 2.0.1 + micromark-factory-space: 2.0.1 + micromark-factory-title: 2.0.1 + micromark-factory-whitespace: 2.0.1 + micromark-util-character: 2.1.1 + micromark-util-chunked: 2.0.1 + micromark-util-classify-character: 2.0.1 + micromark-util-html-tag-name: 2.0.1 + micromark-util-normalize-identifier: 2.0.1 + micromark-util-resolve-all: 2.0.1 + micromark-util-subtokenize: 2.1.0 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + + micromark-extension-gfm-autolink-literal@2.1.0: + dependencies: + micromark-util-character: 2.1.1 + micromark-util-sanitize-uri: 2.0.1 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + + micromark-extension-gfm-footnote@2.1.0: + dependencies: + devlop: 1.1.0 + micromark-core-commonmark: 2.0.3 + micromark-factory-space: 2.0.1 + micromark-util-character: 2.1.1 + micromark-util-normalize-identifier: 2.0.1 + micromark-util-sanitize-uri: 2.0.1 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + + micromark-extension-gfm-strikethrough@2.1.0: + dependencies: + devlop: 1.1.0 + micromark-util-chunked: 2.0.1 + micromark-util-classify-character: 2.0.1 + micromark-util-resolve-all: 2.0.1 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + + micromark-extension-gfm-table@2.1.1: + dependencies: + devlop: 1.1.0 + micromark-factory-space: 2.0.1 + micromark-util-character: 2.1.1 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + + micromark-extension-gfm-tagfilter@2.0.0: + dependencies: + micromark-util-types: 2.0.2 + + micromark-extension-gfm-task-list-item@2.1.0: + dependencies: + devlop: 1.1.0 + micromark-factory-space: 2.0.1 + micromark-util-character: 2.1.1 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + + micromark-extension-gfm@3.0.0: + dependencies: + micromark-extension-gfm-autolink-literal: 2.1.0 + micromark-extension-gfm-footnote: 2.1.0 + micromark-extension-gfm-strikethrough: 2.1.0 + micromark-extension-gfm-table: 2.1.1 + micromark-extension-gfm-tagfilter: 2.0.0 + micromark-extension-gfm-task-list-item: 2.1.0 + micromark-util-combine-extensions: 2.0.1 + micromark-util-types: 2.0.2 + + micromark-factory-destination@2.0.1: + dependencies: + micromark-util-character: 2.1.1 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + + micromark-factory-label@2.0.1: + dependencies: + devlop: 1.1.0 + micromark-util-character: 2.1.1 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + + micromark-factory-space@2.0.1: + dependencies: + micromark-util-character: 2.1.1 + micromark-util-types: 2.0.2 + + micromark-factory-title@2.0.1: + dependencies: + micromark-factory-space: 2.0.1 + micromark-util-character: 2.1.1 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + + micromark-factory-whitespace@2.0.1: + dependencies: + micromark-factory-space: 2.0.1 + micromark-util-character: 2.1.1 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + + micromark-util-character@2.1.1: + dependencies: + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + + micromark-util-chunked@2.0.1: + dependencies: + micromark-util-symbol: 2.0.1 + + micromark-util-classify-character@2.0.1: + dependencies: + micromark-util-character: 2.1.1 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + + micromark-util-combine-extensions@2.0.1: + dependencies: + micromark-util-chunked: 2.0.1 + micromark-util-types: 2.0.2 + + micromark-util-decode-numeric-character-reference@2.0.2: + dependencies: + micromark-util-symbol: 2.0.1 + + micromark-util-decode-string@2.0.1: + dependencies: + decode-named-character-reference: 1.3.0 + micromark-util-character: 2.1.1 + micromark-util-decode-numeric-character-reference: 2.0.2 + micromark-util-symbol: 2.0.1 + + micromark-util-encode@2.0.1: {} + + micromark-util-html-tag-name@2.0.1: {} + + micromark-util-normalize-identifier@2.0.1: + dependencies: + micromark-util-symbol: 2.0.1 + + micromark-util-resolve-all@2.0.1: + dependencies: + micromark-util-types: 2.0.2 + + micromark-util-sanitize-uri@2.0.1: + dependencies: + micromark-util-character: 2.1.1 + micromark-util-encode: 2.0.1 + micromark-util-symbol: 2.0.1 + + micromark-util-subtokenize@2.1.0: + dependencies: + devlop: 1.1.0 + micromark-util-chunked: 2.0.1 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + + micromark-util-symbol@2.0.1: {} + + micromark-util-types@2.0.2: {} + + micromark@4.0.2(supports-color@10.2.2): + dependencies: + '@types/debug': 4.1.13 + debug: 4.4.3(supports-color@10.2.2) + decode-named-character-reference: 1.3.0 + devlop: 1.1.0 + micromark-core-commonmark: 2.0.3 + micromark-factory-space: 2.0.1 + micromark-util-character: 2.1.1 + micromark-util-chunked: 2.0.1 + micromark-util-combine-extensions: 2.0.1 + micromark-util-decode-numeric-character-reference: 2.0.2 + micromark-util-encode: 2.0.1 + micromark-util-normalize-identifier: 2.0.1 + micromark-util-resolve-all: 2.0.1 + micromark-util-sanitize-uri: 2.0.1 + micromark-util-subtokenize: 2.1.0 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + transitivePeerDependencies: + - supports-color + + micromatch@4.0.8: + dependencies: + braces: 3.0.3 + picomatch: 2.3.2 + + mime-db@1.54.0: {} + + mime-types@3.0.2: + dependencies: + mime-db: 1.54.0 + + mimic-fn@2.1.0: {} + + mimic-fn@3.1.0: {} + + mimic-function@5.0.1: {} + + minimatch@10.2.5: + dependencies: + brace-expansion: 5.0.8 + + minimist@1.2.8: {} + + ms@2.1.3: {} + + nanoid@3.3.16: {} + + negotiator@1.0.0: {} + + node-releases@2.0.51: {} + + npm-run-path@4.0.1: + dependencies: + path-key: 3.1.1 + + npm-run-path@6.0.0: + dependencies: + path-key: 4.0.0 + unicorn-magic: 0.3.0 + + object-assign@4.1.1: {} + + object-inspect@1.13.4: {} + + object-treeify@1.1.33: {} + + on-finished@2.4.1: + dependencies: + ee-first: 1.1.1 + + once@1.4.0: + dependencies: + wrappy: 1.0.2 + + onetime@5.1.2: + dependencies: + mimic-fn: 2.1.0 + + onetime@7.0.0: + dependencies: + mimic-function: 5.0.1 + + open@11.0.0: + dependencies: + default-browser: 5.5.0 + define-lazy-prop: 3.0.0 + is-in-ssh: 1.0.0 + is-inside-container: 1.0.0 + powershell-utils: 0.1.0 + wsl-utils: 0.3.1 + + open@8.4.2: + dependencies: + define-lazy-prop: 2.0.0 + is-docker: 2.2.1 + is-wsl: 2.2.0 + + openapi-fetch@0.17.0: + dependencies: + openapi-typescript-helpers: 0.1.0 + + openapi-typescript-helpers@0.1.0: {} + + ora@8.2.0: + dependencies: + chalk: 5.6.2 + cli-cursor: 5.0.0 + cli-spinners: 2.9.2 + is-interactive: 2.0.0 + is-unicode-supported: 2.1.0 + log-symbols: 6.0.0 + stdin-discarder: 0.2.2 + string-width: 7.2.0 + strip-ansi: 7.2.0 + + oxlint-tsgolint@7.0.2001: + optionalDependencies: + '@oxlint-tsgolint/darwin-arm64': 7.0.2001 + '@oxlint-tsgolint/darwin-x64': 7.0.2001 + '@oxlint-tsgolint/linux-arm64': 7.0.2001 + '@oxlint-tsgolint/linux-x64': 7.0.2001 + '@oxlint-tsgolint/win32-arm64': 7.0.2001 + '@oxlint-tsgolint/win32-x64': 7.0.2001 + + oxlint@1.75.0(oxlint-tsgolint@7.0.2001): + optionalDependencies: + '@oxlint/binding-android-arm-eabi': 1.75.0 + '@oxlint/binding-android-arm64': 1.75.0 + '@oxlint/binding-darwin-arm64': 1.75.0 + '@oxlint/binding-darwin-x64': 1.75.0 + '@oxlint/binding-freebsd-x64': 1.75.0 + '@oxlint/binding-linux-arm-gnueabihf': 1.75.0 + '@oxlint/binding-linux-arm-musleabihf': 1.75.0 + '@oxlint/binding-linux-arm64-gnu': 1.75.0 + '@oxlint/binding-linux-arm64-musl': 1.75.0 + '@oxlint/binding-linux-ppc64-gnu': 1.75.0 + '@oxlint/binding-linux-riscv64-gnu': 1.75.0 + '@oxlint/binding-linux-riscv64-musl': 1.75.0 + '@oxlint/binding-linux-s390x-gnu': 1.75.0 + '@oxlint/binding-linux-x64-gnu': 1.75.0 + '@oxlint/binding-linux-x64-musl': 1.75.0 + '@oxlint/binding-openharmony-arm64': 1.75.0 + '@oxlint/binding-win32-arm64-msvc': 1.75.0 + '@oxlint/binding-win32-ia32-msvc': 1.75.0 + '@oxlint/binding-win32-x64-msvc': 1.75.0 + oxlint-tsgolint: 7.0.2001 + + p-limit@2.3.0: + dependencies: + p-try: 2.2.0 + + p-locate@3.0.0: + dependencies: + p-limit: 2.3.0 + + p-try@2.2.0: {} + + parent-module@1.0.1: + dependencies: + callsites: 3.1.0 + + parse-entities@4.0.2: + dependencies: + '@types/unist': 2.0.11 + character-entities-legacy: 3.0.0 + character-reference-invalid: 2.0.1 + decode-named-character-reference: 1.3.0 + is-alphanumerical: 2.0.1 + is-decimal: 2.0.1 + is-hexadecimal: 2.0.1 + + parse-json@5.2.0: + dependencies: + '@babel/code-frame': 7.29.7 + error-ex: 1.3.4 + json-parse-even-better-errors: 2.3.1 + lines-and-columns: 1.2.4 + + parse-ms@4.0.0: {} + + parseurl@1.3.3: {} + + path-browserify@1.0.1: {} + + path-exists@3.0.0: {} + + path-key@3.1.1: {} + + path-key@4.0.0: {} + + path-to-regexp@8.4.2: {} + + pathe@2.0.3: {} + + picocolors@1.1.1: {} + + picomatch@2.3.2: {} + + picomatch@4.0.5: {} + + pkce-challenge@5.0.1: {} + + pkg-up@3.1.0: + dependencies: + find-up: 3.0.0 + + postcss-selector-parser@7.1.4: + dependencies: + cssesc: 3.0.0 + util-deprecate: 1.0.2 + + postcss@8.5.23: + dependencies: + nanoid: 3.3.16 + picocolors: 1.1.1 + source-map-js: 1.2.1 + + powershell-utils@0.1.0: {} + + prettier-plugin-tailwindcss@0.8.1(prettier@3.9.6): + dependencies: + prettier: 3.9.6 + + prettier@3.9.6: {} + + pretty-ms@9.3.0: + dependencies: + parse-ms: 4.0.0 + + prompts@2.4.2: + dependencies: + kleur: 3.0.3 + sisteransi: 1.0.5 + + property-information@7.2.0: {} + + proxy-addr@2.0.7: + dependencies: + forwarded: 0.2.0 + ipaddr.js: 1.9.1 + + qs@6.15.3: + dependencies: + es-define-property: 1.0.1 + side-channel: 1.1.1 + + queue-microtask@1.2.3: {} + + range-parser@1.3.0: {} + + raw-body@3.0.2: + dependencies: + bytes: 3.1.2 + http-errors: 2.0.1 + iconv-lite: 0.7.3 + unpipe: 1.0.0 + + react-dom@19.2.8(react@19.2.8): + dependencies: + react: 19.2.8 + scheduler: 0.27.0 + + react-hook-form@7.82.0(react@19.2.8): + dependencies: + react: 19.2.8 + + react-markdown@10.1.0(@types/react@19.2.17)(react@19.2.8)(supports-color@10.2.2): + dependencies: + '@types/hast': 3.0.5 + '@types/mdast': 4.0.4 + '@types/react': 19.2.17 + devlop: 1.1.0 + hast-util-to-jsx-runtime: 2.3.6(supports-color@10.2.2) + html-url-attributes: 3.0.1 + mdast-util-to-hast: 13.2.1 + react: 19.2.8 + remark-parse: 11.0.0(supports-color@10.2.2) + remark-rehype: 11.1.2 + unified: 11.0.5 + unist-util-visit: 5.1.0 + vfile: 6.0.3 + transitivePeerDependencies: + - supports-color + + react@19.2.8: {} + + readdirp@5.0.0: {} + + recast@0.23.12: + dependencies: + ast-types: 0.16.1 + esprima: 4.0.1 + source-map: 0.6.1 + tiny-invariant: 1.3.3 + tslib: 2.8.1 + + remark-gfm@4.0.1(supports-color@10.2.2): + dependencies: + '@types/mdast': 4.0.4 + mdast-util-gfm: 3.1.0(supports-color@10.2.2) + micromark-extension-gfm: 3.0.0 + remark-parse: 11.0.0(supports-color@10.2.2) + remark-stringify: 11.0.0 + unified: 11.0.5 + transitivePeerDependencies: + - supports-color + + remark-parse@11.0.0(supports-color@10.2.2): + dependencies: + '@types/mdast': 4.0.4 + mdast-util-from-markdown: 2.0.3(supports-color@10.2.2) + micromark-util-types: 2.0.2 + unified: 11.0.5 + transitivePeerDependencies: + - supports-color + + remark-rehype@11.1.2: + dependencies: + '@types/hast': 3.0.5 + '@types/mdast': 4.0.4 + mdast-util-to-hast: 13.2.1 + unified: 11.0.5 + vfile: 6.0.3 + + remark-stringify@11.0.0: + dependencies: + '@types/mdast': 4.0.4 + mdast-util-to-markdown: 2.1.2 + unified: 11.0.5 + + require-from-string@2.0.2: {} + + reselect@5.2.0: {} + + resolve-from@4.0.0: {} + + restore-cursor@5.1.0: + dependencies: + onetime: 7.0.0 + signal-exit: 4.1.0 + + reusify@1.1.0: {} + + rolldown@1.1.5: + dependencies: + '@oxc-project/types': 0.139.0 + '@rolldown/pluginutils': 1.0.1 + optionalDependencies: + '@rolldown/binding-android-arm64': 1.1.5 + '@rolldown/binding-darwin-arm64': 1.1.5 + '@rolldown/binding-darwin-x64': 1.1.5 + '@rolldown/binding-freebsd-x64': 1.1.5 + '@rolldown/binding-linux-arm-gnueabihf': 1.1.5 + '@rolldown/binding-linux-arm64-gnu': 1.1.5 + '@rolldown/binding-linux-arm64-musl': 1.1.5 + '@rolldown/binding-linux-ppc64-gnu': 1.1.5 + '@rolldown/binding-linux-s390x-gnu': 1.1.5 + '@rolldown/binding-linux-x64-gnu': 1.1.5 + '@rolldown/binding-linux-x64-musl': 1.1.5 + '@rolldown/binding-openharmony-arm64': 1.1.5 + '@rolldown/binding-wasm32-wasi': 1.1.5 + '@rolldown/binding-win32-arm64-msvc': 1.1.5 + '@rolldown/binding-win32-x64-msvc': 1.1.5 + + router@2.2.0(supports-color@10.2.2): + dependencies: + debug: 4.4.3(supports-color@10.2.2) + depd: 2.0.0 + is-promise: 4.0.0 + parseurl: 1.3.3 + path-to-regexp: 8.4.2 + transitivePeerDependencies: + - supports-color + + run-applescript@7.1.0: {} + + run-parallel@1.2.0: + dependencies: + queue-microtask: 1.2.3 + + safer-buffer@2.1.2: {} + + scheduler@0.27.0: {} + + semver@6.3.1: {} + + semver@7.8.5: {} + + send@1.2.1(supports-color@10.2.2): + dependencies: + debug: 4.4.3(supports-color@10.2.2) + encodeurl: 2.0.0 + escape-html: 1.0.3 + etag: 1.8.1 + fresh: 2.0.0 + http-errors: 2.0.1 + mime-types: 3.0.2 + ms: 2.1.3 + on-finished: 2.4.1 + range-parser: 1.3.0 + statuses: 2.0.2 + transitivePeerDependencies: + - supports-color + + seroval-plugins@1.5.6(seroval@1.5.6): + dependencies: + seroval: 1.5.6 + + seroval@1.5.6: {} + + serve-static@2.2.1(supports-color@10.2.2): + dependencies: + encodeurl: 2.0.0 + escape-html: 1.0.3 + parseurl: 1.3.3 + send: 1.2.1(supports-color@10.2.2) + transitivePeerDependencies: + - supports-color + + setprototypeof@1.2.0: {} + + shadcn@4.14.1(supports-color@10.2.2)(typescript@7.0.2): + dependencies: + '@babel/core': 7.29.7(supports-color@10.2.2) + '@babel/parser': 7.29.7 + '@babel/plugin-transform-typescript': 7.29.7(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2) + '@babel/preset-typescript': 7.29.7(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2) + '@dotenvx/dotenvx': 1.75.1 + '@modelcontextprotocol/sdk': 1.29.0(supports-color@10.2.2)(zod@3.25.76) + '@types/validate-npm-package-name': 4.0.2 + browserslist: 4.28.7 + commander: 14.0.3 + cosmiconfig: 9.0.2(typescript@7.0.2) + dedent: 1.7.2 + deepmerge: 4.3.1 + diff: 8.0.4 + execa: 9.6.1 + fast-glob: 3.3.3 + fs-extra: 11.4.0 + fuzzysort: 3.1.0 + kleur: 4.1.5 + open: 11.0.0 + ora: 8.2.0 + postcss: 8.5.23 + postcss-selector-parser: 7.1.4 + prompts: 2.4.2 + recast: 0.23.12 + stringify-object: 5.0.0 + tailwind-merge: 3.6.0 + ts-morph: 26.0.0 + tsconfig-paths: 4.2.0 + undici: 7.29.0 + validate-npm-package-name: 7.0.2 + zod: 3.25.76 + zod-to-json-schema: 3.25.2(zod@3.25.76) + transitivePeerDependencies: + - '@cfworker/json-schema' + - babel-plugin-macros + - supports-color + - typescript + + shebang-command@2.0.0: + dependencies: + shebang-regex: 3.0.0 + + shebang-regex@3.0.0: {} + + side-channel-list@1.0.1: + dependencies: + es-errors: 1.3.0 + object-inspect: 1.13.4 + + side-channel-map@1.0.1: + dependencies: + call-bound: 1.0.4 + es-errors: 1.3.0 + get-intrinsic: 1.3.0 + object-inspect: 1.13.4 + + side-channel-weakmap@1.0.2: + dependencies: + call-bound: 1.0.4 + es-errors: 1.3.0 + get-intrinsic: 1.3.0 + object-inspect: 1.13.4 + side-channel-map: 1.0.1 + + side-channel@1.1.1: + 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 + + signal-exit@3.0.7: {} + + signal-exit@4.1.0: {} + + sisteransi@1.0.5: {} + + sonner@2.0.7(react-dom@19.2.8(react@19.2.8))(react@19.2.8): + dependencies: + react: 19.2.8 + react-dom: 19.2.8(react@19.2.8) + + source-map-js@1.2.1: {} + + source-map@0.6.1: {} + + space-separated-tokens@2.0.2: {} + + statuses@2.0.2: {} + + stdin-discarder@0.2.2: {} + + string-width@7.2.0: + dependencies: + emoji-regex: 10.6.0 + get-east-asian-width: 1.6.0 + strip-ansi: 7.2.0 + + stringify-entities@4.0.4: + dependencies: + character-entities-html4: 2.1.0 + character-entities-legacy: 3.0.0 + + stringify-object@5.0.0: + dependencies: + get-own-enumerable-keys: 1.0.0 + is-obj: 3.0.0 + is-regexp: 3.1.0 + + strip-ansi@6.0.1: + dependencies: + ansi-regex: 5.0.1 + + strip-ansi@7.2.0: + dependencies: + ansi-regex: 6.2.2 + + strip-bom@3.0.0: {} + + strip-final-newline@2.0.0: {} + + strip-final-newline@4.0.0: {} + + style-to-js@1.1.21: + dependencies: + style-to-object: 1.0.14 + + style-to-object@1.0.14: + dependencies: + inline-style-parser: 0.2.7 + + supports-color@10.2.2: + optional: true + + systeminformation@5.33.1: {} + + tailwind-merge@3.6.0: {} + + tailwindcss@4.3.3: {} + + tapable@2.3.3: {} + + tiny-invariant@1.3.3: {} + + tinyglobby@0.2.17: + dependencies: + fdir: 6.5.0(picomatch@4.0.5) + picomatch: 4.0.5 + + to-regex-range@5.0.1: + dependencies: + is-number: 7.0.0 + + toidentifier@1.0.1: {} + + trim-lines@3.0.1: {} + + trough@2.2.0: {} + + ts-morph@26.0.0: + dependencies: + '@ts-morph/common': 0.27.0 + code-block-writer: 13.0.3 + + tsconfig-paths@4.2.0: + dependencies: + json5: 2.2.3 + minimist: 1.2.8 + strip-bom: 3.0.0 + + tslib@2.8.1: {} + + tw-animate-css@1.4.0: {} + + type-is@2.1.0: + dependencies: + content-type: 2.0.0 + media-typer: 1.1.1 + mime-types: 3.0.2 + + typescript@7.0.2: + optionalDependencies: + '@typescript/typescript-aix-ppc64': 7.0.2 + '@typescript/typescript-darwin-arm64': 7.0.2 + '@typescript/typescript-darwin-x64': 7.0.2 + '@typescript/typescript-freebsd-arm64': 7.0.2 + '@typescript/typescript-freebsd-x64': 7.0.2 + '@typescript/typescript-linux-arm': 7.0.2 + '@typescript/typescript-linux-arm64': 7.0.2 + '@typescript/typescript-linux-loong64': 7.0.2 + '@typescript/typescript-linux-mips64el': 7.0.2 + '@typescript/typescript-linux-ppc64': 7.0.2 + '@typescript/typescript-linux-riscv64': 7.0.2 + '@typescript/typescript-linux-s390x': 7.0.2 + '@typescript/typescript-linux-x64': 7.0.2 + '@typescript/typescript-netbsd-arm64': 7.0.2 + '@typescript/typescript-netbsd-x64': 7.0.2 + '@typescript/typescript-openbsd-arm64': 7.0.2 + '@typescript/typescript-openbsd-x64': 7.0.2 + '@typescript/typescript-sunos-x64': 7.0.2 + '@typescript/typescript-win32-arm64': 7.0.2 + '@typescript/typescript-win32-x64': 7.0.2 + + undici-types@7.18.2: {} + + undici@7.29.0: {} + + unicorn-magic@0.3.0: {} + + unified@11.0.5: + dependencies: + '@types/unist': 3.0.3 + bail: 2.0.2 + devlop: 1.1.0 + extend: 3.0.2 + is-plain-obj: 4.1.0 + trough: 2.2.0 + vfile: 6.0.3 + + unist-util-is@6.0.1: + dependencies: + '@types/unist': 3.0.3 + + unist-util-position@5.0.0: + dependencies: + '@types/unist': 3.0.3 + + unist-util-stringify-position@4.0.0: + dependencies: + '@types/unist': 3.0.3 + + unist-util-visit-parents@6.0.2: + dependencies: + '@types/unist': 3.0.3 + unist-util-is: 6.0.1 + + unist-util-visit@5.1.0: + dependencies: + '@types/unist': 3.0.3 + unist-util-is: 6.0.1 + unist-util-visit-parents: 6.0.2 + + universalify@2.0.1: {} + + unpipe@1.0.0: {} + + unplugin@3.3.0(rolldown@1.1.5)(vite@8.1.5(@types/node@24.13.3)(jiti@2.7.0)): + dependencies: + '@jridgewell/remapping': 2.3.5 + picomatch: 4.0.5 + webpack-virtual-modules: 0.6.2 + optionalDependencies: + rolldown: 1.1.5 + vite: 8.1.5(@types/node@24.13.3)(jiti@2.7.0) + + update-browserslist-db@1.2.3(browserslist@4.28.7): + dependencies: + browserslist: 4.28.7 + escalade: 3.2.0 + picocolors: 1.1.1 + + use-sync-external-store@1.6.0(react@19.2.8): + dependencies: + react: 19.2.8 + + util-deprecate@1.0.2: {} + + validate-npm-package-name@7.0.2: {} + + vary@1.1.2: {} + + vfile-message@4.0.3: + dependencies: + '@types/unist': 3.0.3 + unist-util-stringify-position: 4.0.0 + + vfile@6.0.3: + dependencies: + '@types/unist': 3.0.3 + vfile-message: 4.0.3 + + vite@8.1.5(@types/node@24.13.3)(jiti@2.7.0): + dependencies: + lightningcss: 1.33.0 + picomatch: 4.0.5 + postcss: 8.5.23 + rolldown: 1.1.5 + tinyglobby: 0.2.17 + optionalDependencies: + '@types/node': 24.13.3 + fsevents: 2.3.3 + jiti: 2.7.0 + + webpack-virtual-modules@0.6.2: {} + + which@2.0.2: + dependencies: + isexe: 2.0.0 + + which@4.0.0: + dependencies: + isexe: 3.1.5 + + wrappy@1.0.2: {} + + wsl-utils@0.3.1: + dependencies: + is-wsl: 3.1.1 + powershell-utils: 0.1.0 + + yallist@3.1.1: {} + + yocto-spinner@1.2.2: + dependencies: + yoctocolors: 2.1.2 + + yoctocolors@2.1.2: {} + + zod-to-json-schema@3.25.2(zod@3.25.76): + dependencies: + zod: 3.25.76 + + zod@3.25.76: {} + + zod@4.4.3: {} + + zwitch@2.0.4: {} diff --git a/web/pnpm-workspace.yaml b/web/pnpm-workspace.yaml new file mode 100644 index 00000000..6afd1d41 --- /dev/null +++ b/web/pnpm-workspace.yaml @@ -0,0 +1,5 @@ +packages: [] + +allowBuilds: + esbuild: true + msw: false diff --git a/web/public/data/course_links.csv b/web/public/data/course_links.csv new file mode 100644 index 00000000..610a76b1 --- /dev/null +++ b/web/public/data/course_links.csv @@ -0,0 +1,2023 @@ +course_code,course_name,link +ACCT 201,Introduction to Accounting,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/351?entityType=Course&reportId=422 +ANT 101,Being Human: An Introduction to Four Field Anthropology,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2880?entityType=Course&reportId=422 +ANT 110,Introduction to Sociocultural Anthropology,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2403?entityType=Course&reportId=422 +ANT 140,World Prehistory,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/3021?entityType=Course&reportId=422 +ANT 160,Introduction to Biological Anthropology,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/3050?entityType=Course&reportId=422 +ANT 175,Introduction to Linguistic Anthropology,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/3181?entityType=Course&reportId=422 +ANT 204,Capitalism in Crisis,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2404?entityType=Course&reportId=422 +ANT 214,Qualitative Methods,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/3022?entityType=Course&reportId=422 +ANT 215,What is Islam? Anthropological Perspectives,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/3095?entityType=Course&reportId=422 +ANT 231,Frauds and Fallacies in Archaeology,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2879?entityType=Course&reportId=422 +ANT 232,"Life, Death and Economy: Archaeology of Central Asia",https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/3016?entityType=Course&reportId=422 +ANT 233,"Cavemen, Mammoths, and Sabertooths: Stone Age Archaeology in Eurasia",https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2346?entityType=Course&reportId=422 +ANT 240,Laboratory Methods in Archaeology,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/3020?entityType=Course&reportId=422 +ANT 262,Monkey Business: Primate Society and Behavior,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/3051?entityType=Course&reportId=422 +ANT 270,Anthropology of Warfare,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/3195?entityType=Course&reportId=422 +ANT 275,Language and Society,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/3178?entityType=Course&reportId=422 +ANT 285,Food and Society,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2881?entityType=Course&reportId=422 +ANT 286,Nomads: Around the World and Through Time,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/3019?entityType=Course&reportId=422 +ANT 306,Anthropology of Performance,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/3136?entityType=Course&reportId=422 +ANT 312,The Anthropology of Conflict,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/3192?entityType=Course&reportId=422 +ANT 313,Islam and Politics in Eurasia,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/3193?entityType=Course&reportId=422 +ANT 314,Politics of Identity in Eurasia,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/3194?entityType=Course&reportId=422 +ANT 316,Bazaars and Museums: Material and Cultural Heritage in Central Asia and Beyond,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/3137?entityType=Course&reportId=422 +ANT 331,Archaeology of Power and Inequality,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/3015?entityType=Course&reportId=422 +ANT 333,Anthropology of Space,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/3053?entityType=Course&reportId=422 +ANT 340,Method and Theory in Archaeology,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/3017?entityType=Course&reportId=422 +ANT 345,Archaeology Field School,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/3018?entityType=Course&reportId=422 +ANT 361,"Human Evolution: Bones, Stones and Genomes",https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/3052?entityType=Course&reportId=422 +ANT 375,Inner Asian Frontiers of China: A Cultural Perspective,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/3179?entityType=Course&reportId=422 +ANT 380,Linguistic and Cultural Diversity in Contemporary China: From Beijing to Xinjiang,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/3180?entityType=Course&reportId=422 +ANT 385,Postcolonial Theory and its Applications in Eurasia,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2405?entityType=Course&reportId=422 +ANT 386,Social Challenges of Climate Change,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2942?entityType=Course&reportId=422 +ANT 399,Special Topics in Anthropology,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/3094?entityType=Course&reportId=422 +ANT 400,Research Assistance in Anthropology,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/3054?entityType=Course&reportId=422 +ANT 401,Research Assistance in Anthropology,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/3055?entityType=Course&reportId=422 +ANT 402,Research Assistance in Anthropology,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/3056?entityType=Course&reportId=422 +ANT 403,Research Assistance in Anthropology,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/3057?entityType=Course&reportId=422 +ANT 404,Research Assistance in Anthropology,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/3058?entityType=Course&reportId=422 +ANT 412,Approaches to Global Development,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/3023?entityType=Course&reportId=422 +ANT 420,Materiality and Eurasian Society,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/3014?entityType=Course&reportId=422 +ANT 421,Sport and Construction of Identity,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/3197?entityType=Course&reportId=422 +ANT 435,The Archaeology of Ritual,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2877?entityType=Course&reportId=422 +ANT 440,Skeletal Materials for Archaeologists,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/3049?entityType=Course&reportId=422 +ANT 475,Digital Ethnographies,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/3024?entityType=Course&reportId=422 +ANT 481,Anthropology of Entrepreneurship,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/3025?entityType=Course&reportId=422 +ANT 482,"Climate Change, Future Energy, and Sustainability",https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/3026?entityType=Course&reportId=422 +ANT 483,Applied Public Heritage,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2878?entityType=Course&reportId=422 +ANT 498,Capstone Seminar I,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2581?entityType=Course&reportId=422 +ANT 499,Capstone Seminar II,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2401?entityType=Course&reportId=422 +ANT 506,Anthropology of Performance,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/3138?entityType=Course&reportId=422 +ANT 520,Materiality and Eurasian Society,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/3013?entityType=Course&reportId=422 +ANT 521,Sport and Construction of Identity,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/3196?entityType=Course&reportId=422 +ANT 535,The Archaeology of Ritual,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2876?entityType=Course&reportId=422 +ANT 585,Postcolonial Theory and Its Application in Eurasia,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2402?entityType=Course&reportId=422 +BBA 201,Management and Organizations,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/356?entityType=Course&reportId=422 +BBA 202,Business Communication,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/352?entityType=Course&reportId=422 +BBA 203,Responsible Leadership (Ethics),https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/353?entityType=Course&reportId=422 +BBA 208,Data Analytics for Business,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/1358?entityType=Course&reportId=422 +BBA 209,"Environmental, Social and Governance Factors (ESG) for Business",https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/359?entityType=Course&reportId=422 +BBA 210,Fundamentals of Marketing,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/1329?entityType=Course&reportId=422 +BBA 230,Entrepreneurship,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/1330?entityType=Course&reportId=422 +BBA 240,Strategy,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/357?entityType=Course&reportId=422 +BBA 260,Operations Management,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/361?entityType=Course&reportId=422 +BBA 310,BBA Elective I,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/362?entityType=Course&reportId=422 +BBA 320,BBA Elective IV,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/363?entityType=Course&reportId=422 +BBA 321,BBA Elective II,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/364?entityType=Course&reportId=422 +BBA 361,BBA Elective III,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/365?entityType=Course&reportId=422 +BBA 399,Internship,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/366?entityType=Course&reportId=422 +BBA 499,Capstone,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/355?entityType=Course&reportId=422 +BIOL 101,Introductory Biology,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2500?entityType=Course&reportId=422 +BIOL 105,General Biology,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/3003?entityType=Course&reportId=422 +BIOL 110,Modern Biology I,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/3004?entityType=Course&reportId=422 +BIOL 110L,Modern Biology I Laboratory,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2501?entityType=Course&reportId=422 +BIOL 120,Modern Biology II,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/3281?entityType=Course&reportId=422 +BIOL 120L,Modern Biology II Laboratory,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2368?entityType=Course&reportId=422 +BIOL 230,Human Anatomy and Physiology I,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/3177?entityType=Course&reportId=422 +BIOL 301,Molecular Cell Biology,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2806?entityType=Course&reportId=422 +BIOL 301L,Molecular Cell Biology Laboratory,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/3040?entityType=Course&reportId=422 +BIOL 305,Introduction to Microbiology,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2566?entityType=Course&reportId=422 +BIOL 305L,Introduction to Microbiology Laboratory,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2567?entityType=Course&reportId=422 +BIOL 310,Immunology,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/3151?entityType=Course&reportId=422 +BIOL 320,Developmental Biology,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/3280?entityType=Course&reportId=422 +BIOL 321,Bioethics,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/3008?entityType=Course&reportId=422 +BIOL 331,Human Anatomy and Physiology II,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/3175?entityType=Course&reportId=422 +BIOL 331L,Human Anatomy and Physiology II Laboratory,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/3176?entityType=Course&reportId=422 +BIOL 333,Environmental Biology,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2971?entityType=Course&reportId=422 +BIOL 340,Introduction to Bioinformatics with Lab,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/3229?entityType=Course&reportId=422 +BIOL 341,Biochemistry I,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/3155?entityType=Course&reportId=422 +BIOL 341L,Biochemistry I Laboratory,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/3156?entityType=Course&reportId=422 +BIOL 355,Critical Research Reasoning,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/3009?entityType=Course&reportId=422 +BIOL 363,Structural Bioinformatics,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2702?entityType=Course&reportId=422 +BIOL 370,Genetics,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/3277?entityType=Course&reportId=422 +BIOL 370L,Genetics Laboratory,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/3168?entityType=Course&reportId=422 +BIOL 380,Biology of Behavior,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/3171?entityType=Course&reportId=422 +BIOL 385,Cell Signaling: Principles and Mechanisms,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/3276?entityType=Course&reportId=422 +BIOL 392,Directed Study in Biology,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/3172?entityType=Course&reportId=422 +BIOL 399,Biology Internship,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2681?entityType=Course&reportId=422 +BIOL 425,Biomedical Research Methods,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2656?entityType=Course&reportId=422 +BIOL 430,Histology with Laboratory,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/3278?entityType=Course&reportId=422 +BIOL 440,Neuroscience,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/3002?entityType=Course&reportId=422 +BIOL 444,Stem Cell Biology and Applications,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2759?entityType=Course&reportId=422 +BIOL 445,Medical Microbiology,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/3275?entityType=Course&reportId=422 +BIOL 450,Food Microbiology,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2568?entityType=Course&reportId=422 +BIOL 450L,Food Microbiology Lab,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2569?entityType=Course&reportId=422 +BIOL 452,Biology of Cancer,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2682?entityType=Course&reportId=422 +BIOL 456,Biology Research Design,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2683?entityType=Course&reportId=422 +BIOL 460,Human Parasitology,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/3279?entityType=Course&reportId=422 +BIOL 468,Integrated Cell Biology,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2760?entityType=Course&reportId=422 +BIOL 470,Advanced Cell Biology,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2803?entityType=Course&reportId=422 +BIOL 471,Light and Electron Microscopy Concepts and Techniques,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2804?entityType=Course&reportId=422 +BIOL 471L,Light and Electron Microscopy Concepts and Techniques-Lab,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/3042?entityType=Course&reportId=422 +BIOL 480,Molecular Immunology,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/3154?entityType=Course&reportId=422 +BIOL 481,Neuroimmunology,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2685?entityType=Course&reportId=422 +BIOL 490,Honors Thesis Research,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/3000?entityType=Course&reportId=422 +BIOL 491,Honors Thesis,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/3001?entityType=Course&reportId=422 +BIOL 492,Research Experience in Biology,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2684?entityType=Course&reportId=422 +BIOL 502,Research Methods and Bioethics,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/3169?entityType=Course&reportId=422 +BIOL 520,Statistical Methods in Life Sciences,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/3170?entityType=Course&reportId=422 +BIOL 550,Structural and Molecular Biology in Health and Disease,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2975?entityType=Course&reportId=422 +BIOL 560,Molecular Biology of Prokaryotic Cells,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2570?entityType=Course&reportId=422 +BIOL 570,Advanced Biotechnology,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2805?entityType=Course&reportId=422 +BIOL 580,Applied Bioinformatics,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/3231?entityType=Course&reportId=422 +BIOL 590,Research Design and Project Planning,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2700?entityType=Course&reportId=422 +BIOL 591,Cellular Biophysics,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2976?entityType=Course&reportId=422 +BIOL 592,Graduate Research Seminar,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2977?entityType=Course&reportId=422 +BIOL 599,Master’s Thesis Research,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2701?entityType=Course&reportId=422 +BIOL 605,Radiation Biology,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/3010?entityType=Course&reportId=422 +BIOL 606,Advanced Environmental Biology,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2974?entityType=Course&reportId=422 +BIOL 623,Advanced Immunology,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/3152?entityType=Course&reportId=422 +BIOL 630,Advanced Neuroscience,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/3174?entityType=Course&reportId=422 +BIOL 631,Synthetic Biology,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2703?entityType=Course&reportId=422 +BIOL 632,Drug Discovery and Design,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2704?entityType=Course&reportId=422 +BIOL 634,Advanced Cancer Biology,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2655?entityType=Course&reportId=422 +BIOL 637,Fundamentals of Advanced Microscopy,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2802?entityType=Course&reportId=422 +BIOL 637L,Fundamentals of Advanced Microscopy,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/3041?entityType=Course&reportId=422 +BIOL 670,Gene Therapy,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2756?entityType=Course&reportId=422 +BIOL 675,Regenerative Medicine,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2757?entityType=Course&reportId=422 +BIOL 691,Master’s Thesis Research Proposal,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/3256?entityType=Course&reportId=422 +BIOL 692,Master's Thesis,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/3011?entityType=Course&reportId=422 +BIOL 800,Doctoral Thesis,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2705?entityType=Course&reportId=422 +BIOL 800,0 Thesis Research,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/3153?entityType=Course&reportId=422 +BIOL 805,Radiation Biology,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/3012?entityType=Course&reportId=422 +BIOL 820,Statistical Methods in Life Sciences,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/3167?entityType=Course&reportId=422 +BIOL 830,Advanced Neuroscience,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/3173?entityType=Course&reportId=422 +BIOL 831,Synthetic Biology,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2706?entityType=Course&reportId=422 +BIOL 832,Drug Discovery and Design,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2707?entityType=Course&reportId=422 +BIOL 834,Advanced Cancer Biology,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2654?entityType=Course&reportId=422 +BIOL 837,Advanced Optical and Electron Microscopy,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2800?entityType=Course&reportId=422 +BIOL 850,Structural and Molecular Biology in Health and Disease,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2972?entityType=Course&reportId=422 +BIOL 860,Molecular Biology of Prokaryotic Cells,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2571?entityType=Course&reportId=422 +BIOL 870,Advanced Biotechnology,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2801?entityType=Course&reportId=422 +BIOL 875,Gene Therapy,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2758?entityType=Course&reportId=422 +BIOL 880,Applied Bioinformatics,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/3230?entityType=Course&reportId=422 +BIOL 891,Cellular Biophysics,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2973?entityType=Course&reportId=422 +BUS 101,Core Course in Business,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/368?entityType=Course&reportId=422 +BUS 420,Business Law,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/724?entityType=Course&reportId=422 +CEE 200,Structural Mechanics I,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/946?entityType=Course&reportId=422 +CEE 201,Environmental Chemistry,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/1172?entityType=Course&reportId=422 +CEE 202,Environmental Engineering,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/1304?entityType=Course&reportId=422 +CEE 203,Structural Analysis,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/1047?entityType=Course&reportId=422 +CEE 204,Civil Engineering CAD and Surveying,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/953?entityType=Course&reportId=422 +CEE 300,Structural Design - Concrete,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/1242?entityType=Course&reportId=422 +CEE 301,Structural Design - Steel,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/1028?entityType=Course&reportId=422 +CEE 302,Geotechnical Engineering,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/1278?entityType=Course&reportId=422 +CEE 303,Geotechnical Design,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/1279?entityType=Course&reportId=422 +CEE 304,Fluid Mechanics,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/1060?entityType=Course&reportId=422 +CEE 305,Hydraulics and Hydrology,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/1061?entityType=Course&reportId=422 +CEE 306,Civil Engineering Materials,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/995?entityType=Course&reportId=422 +CEE 350,Water and Wastewater Treatment Processes,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/1295?entityType=Course&reportId=422 +CEE 351,Application of Geomatics in Civil Engineering,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/998?entityType=Course&reportId=422 +CEE 352,Engineering Geology,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/1171?entityType=Course&reportId=422 +CEE 400,Transportation Engineering,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/996?entityType=Course&reportId=422 +CEE 401,Construction Technology and Management,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/861?entityType=Course&reportId=422 +CEE 450,Behavior and Design of Structural System,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/1026?entityType=Course&reportId=422 +CEE 454,Foundation Engineering,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/3429?entityType=Course&reportId=422 +CEE 455,Solid & Hazardous Waste Management,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/1170?entityType=Course&reportId=422 +CEE 457,Air Quality Management,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/1062?entityType=Course&reportId=422 +CEE 458,Modern Information Technology in Construction,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/903?entityType=Course&reportId=422 +CEE 460,Water Supply and Distribution Management,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/1063?entityType=Course&reportId=422 +CEE 462,Pavement Design and Performance,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/997?entityType=Course&reportId=422 +CEE 463,Individual Research Project in Civil & Environmental Engineering 1,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/1087?entityType=Course&reportId=422 +CEE 464,Individual Research Project in Civil and Environmental Engineering II,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/1088?entityType=Course&reportId=422 +CEE 465,Structure and Properties of Concrete Materials,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/1243?entityType=Course&reportId=422 +CEE 466,Introduction to Finite Element Methods,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/3446?entityType=Course&reportId=422 +CEE 467,Building Information Modelling for Geotechnical Engineering,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/904?entityType=Course&reportId=422 +CEE 468,Structural Design – Concrete II,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/1244?entityType=Course&reportId=422 +CHEM 090,Chemistry for Non-Science Majors,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/3122?entityType=Course&reportId=422 +CHEM 091,Survey of Physical Science,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/3157?entityType=Course&reportId=422 +CHEM 092,Survey of Environmental Sciences for Non-Science Majors,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/3121?entityType=Course&reportId=422 +CHEM 101,General Chemistry I,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2892?entityType=Course&reportId=422 +CHEM 101L,General Chemistry I Lab,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/3080?entityType=Course&reportId=422 +CHEM 102,General Chemistry II,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2921?entityType=Course&reportId=422 +CHEM 189C,Independent Study,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2922?entityType=Course&reportId=422 +CHEM 211,Organic Chemistry I,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2632?entityType=Course&reportId=422 +CHEM 211L,Organic Chemistry I Lab,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2633?entityType=Course&reportId=422 +CHEM 212,Organic Chemistry II,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2792?entityType=Course&reportId=422 +CHEM 212L,Organic Chemistry II Lab,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2793?entityType=Course&reportId=422 +CHEM 220,Quantitative Chemical Analysis,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/3075?entityType=Course&reportId=422 +CHEM 220L,Quantitative Chemical Analysis Lab,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2408?entityType=Course&reportId=422 +CHEM 250,Descriptive Inorganic Chemistry,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/3158?entityType=Course&reportId=422 +CHEM 320,Instrumental Analysis,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/3076?entityType=Course&reportId=422 +CHEM 320L,Instrumental Analysis Lab,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/3077?entityType=Course&reportId=422 +CHEM 331,Physical Chemistry I,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2764?entityType=Course&reportId=422 +CHEM 331L,Physical Chemistry Lab,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2765?entityType=Course&reportId=422 +CHEM 332,Physical Chemistry II,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2766?entityType=Course&reportId=422 +CHEM 332L,Physical Chemistry II Lab,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2767?entityType=Course&reportId=422 +CHEM 341,Biochemistry I,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2672?entityType=Course&reportId=422 +CHEM 341L,Biochemistry I Lab,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2673?entityType=Course&reportId=422 +CHEM 350,Advanced Inorganic Chemistry,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2425?entityType=Course&reportId=422 +CHEM 350L,Advanced Inorganic Chemistry Laboratory,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2370?entityType=Course&reportId=422 +CHEM 380,Research Methods,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2426?entityType=Course&reportId=422 +CHEM 400,Chemistry Seminar,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/3162?entityType=Course&reportId=422 +CHEM 410,Structural Spectroscopy,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/3109?entityType=Course&reportId=422 +CHEM 411,Advanced Organic Chemistry I,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2631?entityType=Course&reportId=422 +CHEM 431,Computational Chemistry,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2919?entityType=Course&reportId=422 +CHEM 432,Introduction to Cheminformatics and Computer Aided Drug Design,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2920?entityType=Course&reportId=422 +CHEM 433,Surfactants and Colloids,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/3247?entityType=Course&reportId=422 +CHEM 442,Biochemistry II with Lab-Metabolic Biochemistry,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2354?entityType=Course&reportId=422 +CHEM 451,Applied Homogenous Catalysis,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2427?entityType=Course&reportId=422 +CHEM 471,Environmental Chemistry,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2918?entityType=Course&reportId=422 +CHEM 488,Directed Research I,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/3159?entityType=Course&reportId=422 +CHEM 489,Directed Research II,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/3160?entityType=Course&reportId=422 +CHEM 490,Nanochemistry,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/3161?entityType=Course&reportId=422 +CHEM 510,Principles of Physical Chemistry,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2763?entityType=Course&reportId=422 +CHEM 511,Theoretical Chemistry,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2913?entityType=Course&reportId=422 +CHEM 515,Applied Colloid and Surfactant Science,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/3238?entityType=Course&reportId=422 +CHEM 518,Applied Polymer Science,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/3239?entityType=Course&reportId=422 +CHEM 520,Applied Analytical Chemistry,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/3079?entityType=Course&reportId=422 +CHEM 522,Analytical Environmental Chemistry,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2912?entityType=Course&reportId=422 +CHEM 530,Inorganic Structures and Reaction Mechanisms,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2434?entityType=Course&reportId=422 +CHEM 531,Nanochemistry and Functional Nanomaterials,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/3164?entityType=Course&reportId=422 +CHEM 532,Organometallic Chemistry,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2431?entityType=Course&reportId=422 +CHEM 540,Organic Reactions and Mechanisms,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2628?entityType=Course&reportId=422 +CHEM 541,Medicinal Chemistry and Drug Design,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2629?entityType=Course&reportId=422 +CHEM 543,Structural Identification of Organic Compounds,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/3108?entityType=Course&reportId=422 +CHEM 550,Selected Topics in Chemistry,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2432?entityType=Course&reportId=422 +CHEM 560,Directed Research in Chemistry,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2433?entityType=Course&reportId=422 +CHEM 591,Scientific Methods in Chemistry,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/3240?entityType=Course&reportId=422 +CHEM 592,Chemistry Seminar,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/3241?entityType=Course&reportId=422 +CHEM 691,Thesis Proposal,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/3242?entityType=Course&reportId=422 +CHEM 692,MSc Thesis in Chemistry,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/3243?entityType=Course&reportId=422 +CHEM 700,Hot Topics in Chemistry,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/3245?entityType=Course&reportId=422 +CHEM 710,Mechanistic Principles of Organic Reactions,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2630?entityType=Course&reportId=422 +CHEM 711,Polymer Chemistry,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/3244?entityType=Course&reportId=422 +CHEM 720,Spectrochemical Methods of Analysis,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/3078?entityType=Course&reportId=422 +CHEM 722,Advanced Environmental Chemistry,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2914?entityType=Course&reportId=422 +CHEM 730,Chemical Thermodynamics and Kinetics,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2768?entityType=Course&reportId=422 +CHEM 731,Design of Functional Materials,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2915?entityType=Course&reportId=422 +CHEM 733,Colloid and Surfactant Science,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/3246?entityType=Course&reportId=422 +CHEM 740,Advanced Medicinal Chemistry,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2355?entityType=Course&reportId=422 +CHEM 750,Modern Inorganic Chemistry,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2430?entityType=Course&reportId=422 +CHEM 752,Materials Chemistry,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/3163?entityType=Course&reportId=422 +CHEM 753,Organometallic Catalysis,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2428?entityType=Course&reportId=422 +CHEM 780,Research Methods and Ethics,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2429?entityType=Course&reportId=422 +CHEM 798,0 Thesis Research,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2916?entityType=Course&reportId=422 +CHEM 800,Doctoral Thesis,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2917?entityType=Course&reportId=422 +CHME 200,Basic Principles and Calculations in Chemical Engineering,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/993?entityType=Course&reportId=422 +CHME 201,Chemical Engineering Thermodynamics,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/1209?entityType=Course&reportId=422 +CHME 202,Fluid Mechanics,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/1301?entityType=Course&reportId=422 +CHME 203,Organic and Polymer Chemistry,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/884?entityType=Course&reportId=422 +CHME 222,Inorganic and Analytical Chemistry,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/905?entityType=Course&reportId=422 +CHME 300,Heat and Mass Transfer,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/1232?entityType=Course&reportId=422 +CHME 301,Applied Mathematics for Process Design,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/982?entityType=Course&reportId=422 +CHME 302,Instrumental Methods of Analysis for Engineers,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/1008?entityType=Course&reportId=422 +CHME 303,Separation Processes,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/1263?entityType=Course&reportId=422 +CHME 304,Chemical Reaction Engineering,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/1264?entityType=Course&reportId=422 +CHME 305,Chemical Engineering Laboratory I,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/883?entityType=Course&reportId=422 +CHME 351,Environment and Development,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/1266?entityType=Course&reportId=422 +CHME 352,Research Practice,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/1324?entityType=Course&reportId=422 +CHME 353,Electrochemical Engineering,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/886?entityType=Course&reportId=422 +CHME 400,Process Design and Simulation,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/1025?entityType=Course&reportId=422 +CHME 401,Chemical Engineering Laboratory II,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/885?entityType=Course&reportId=422 +CHME 402,Materials Chemistry,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/889?entityType=Course&reportId=422 +CHME 403,Chemical Process Control and Safety,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/1023?entityType=Course&reportId=422 +CHME 421,Tissue Engineering,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/994?entityType=Course&reportId=422 +CHME 453,Multiphase Systems,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/1233?entityType=Course&reportId=422 +CHME 454,Transport Phenomena and Operations,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/1234?entityType=Course&reportId=422 +CHME 459,Biomechanics,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/992?entityType=Course&reportId=422 +CHME 461,Powder Technology,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/981?entityType=Course&reportId=422 +CHME 464,Directed Study,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/1024?entityType=Course&reportId=422 +CHME 485,Introduction to Biochemical Engineering,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/1265?entityType=Course&reportId=422 +CHN 101,Beginning Mandarin I,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2896?entityType=Course&reportId=422 +CHN 102,Beginning Mandarin II,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2897?entityType=Course&reportId=422 +CHN 201,Intermediate Mandarin I,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2898?entityType=Course&reportId=422 +CHN 202,Intermediate Mandarin II,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2899?entityType=Course&reportId=422 +CHN 301,Upper Intermediate Chinese I,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2900?entityType=Course&reportId=422 +CSCI 101,Introduction to Computational Sciences,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/1167?entityType=Course&reportId=422 +CSCI 111,Web Programming and Problem Solving,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/1129?entityType=Course&reportId=422 +CSCI 115,Programming Fundamentals,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/1252?entityType=Course&reportId=422 +CSCI 151,Programming for Scientists and Engineers,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/938?entityType=Course&reportId=422 +CSCI 152,Performance and Data Structures,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/970?entityType=Course&reportId=422 +CSCI 231,Computer Systems and Organization,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/1076?entityType=Course&reportId=422 +CSCI 235,Programming Languages,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/968?entityType=Course&reportId=422 +CSCI 245,System Analysis and Design,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/942?entityType=Course&reportId=422 +CSCI 262,Software Project Management,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/943?entityType=Course&reportId=422 +CSCI 270,Algorithms,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/1168?entityType=Course&reportId=422 +CSCI 272,Formal Languages,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/1084?entityType=Course&reportId=422 +CSCI 281,Human-Computer Interaction,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/939?entityType=Course&reportId=422 +CSCI 299,Internship I,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/969?entityType=Course&reportId=422 +CSCI 307,Research Methods,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/1176?entityType=Course&reportId=422 +CSCI 325,Introduction to Parallel Systems and GPU Programming,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/1100?entityType=Course&reportId=422 +CSCI 330,Mobile Computing,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/1127?entityType=Course&reportId=422 +CSCI 332,Operating Systems,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/1041?entityType=Course&reportId=422 +CSCI 333,Computer Networks,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/1251?entityType=Course&reportId=422 +CSCI 336,Ubiquity and Sensing,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/1226?entityType=Course&reportId=422 +CSCI 341,Database Systems,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/1314?entityType=Course&reportId=422 +CSCI 344,Data Mining and Decision Support,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/1099?entityType=Course&reportId=422 +CSCI 355,Compiler Construction,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/1085?entityType=Course&reportId=422 +CSCI 361,Software Engineering,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/1136?entityType=Course&reportId=422 +CSCI 363,Software Testing and Quality Assurance,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/870?entityType=Course&reportId=422 +CSCI 371,Algorithmic Problem Solving,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/1166?entityType=Course&reportId=422 +CSCI 390,Artificial Intelligence,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/1186?entityType=Course&reportId=422 +CSCI 393,Introduction to Natural Language Processing,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/1212?entityType=Course&reportId=422 +CSCI 399,Internship II,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/971?entityType=Course&reportId=422 +CSCI 399F,Internship II-F,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/972?entityType=Course&reportId=422 +CSCI 408,Senior Project I,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/928?entityType=Course&reportId=422 +CSCI 409,Senior Project II,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/929?entityType=Course&reportId=422 +CSCI 434,Information Security,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/1177?entityType=Course&reportId=422 +CSCI 437,Internet of Things: Technologies and Applications,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/1040?entityType=Course&reportId=422 +CSCI 445,Bioinformatics,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/1255?entityType=Course&reportId=422 +CSCI 447,Machine Learning: Theory and Practice,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/1256?entityType=Course&reportId=422 +CSCI 455,Scripting Languages,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/1124?entityType=Course&reportId=422 +CSCI 462,Open Source Software,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/940?entityType=Course&reportId=422 +CSCI 471,Complexity and Computability,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/1165?entityType=Course&reportId=422 +CSCI 490,Brain Computer Interface,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/1259?entityType=Course&reportId=422 +CSCI 494,Deep Learning,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/1137?entityType=Course&reportId=422 +CSCI 496,Generative Artificial Intelligence,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/926?entityType=Course&reportId=422 +CSCI 501,Software Principles and Practice,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/974?entityType=Course&reportId=422 +CSCI 502,Hardware / Software Co-Design,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/914?entityType=Course&reportId=422 +CSCI 511,CS Track Core Theory,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/1083?entityType=Course&reportId=422 +CSCI 512,Information Theory,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/1039?entityType=Course&reportId=422 +CSCI 515,Modeling and Simulation for Computer Science,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/927?entityType=Course&reportId=422 +CSCI 531,Distributed Systems,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/1125?entityType=Course&reportId=422 +CSCI 535,Wireless Communication and Networks,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/1227?entityType=Course&reportId=422 +CSCI 537,Software Defined Networking,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/1126?entityType=Course&reportId=422 +CSCI 545,Big Data Analytics,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/1258?entityType=Course&reportId=422 +CSCI 547,Algorithmic Trading,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/871?entityType=Course&reportId=422 +CSCI 563,Software Testing and Quality Assurance,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/975?entityType=Course&reportId=422 +CSCI 572,Cognitive Modelling for Human-Computer Interaction,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/941?entityType=Course&reportId=422 +CSCI 575,Formal Methods and Applications,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/973?entityType=Course&reportId=422 +CSCI 581,Acquisition and Analysis of Biomedical Data,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/1257?entityType=Course&reportId=422 +CSCI 585,Computer Vision,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/1102?entityType=Course&reportId=422 +CSCI 591,Advanced Artificial Intelligence,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/1077?entityType=Course&reportId=422 +CSCI 592,Intelligent Systems,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/1078?entityType=Course&reportId=422 +CSCI 594,Deep Learning,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/872?entityType=Course&reportId=422 +CSCI 595,Generative Artificial Intelligence,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/1188?entityType=Course&reportId=422 +CSCI 693,Thesis Proposal,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/1122?entityType=Course&reportId=422 +CSCI 694,Thesis,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/1123?entityType=Course&reportId=422 +CSCI 700,Thesis Research,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/1260?entityType=Course&reportId=422 +CSCI 700,0 Thesis Research,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/1261?entityType=Course&reportId=422 +CSCI 701,Advanced Software Principles and Practice,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/967?entityType=Course&reportId=422 +CSCI 702,Advanced Hardware / Software Co-Design,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/913?entityType=Course&reportId=422 +CSCI 711,Advanced Theory of Computation,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/1086?entityType=Course&reportId=422 +CSCI 722,Current Research Literature,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/1075?entityType=Course&reportId=422 +CSCI 725,Advanced Quantum Computing,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/867?entityType=Course&reportId=422 +CSCI 735,Advanced Wireless Communication and Networks,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/1228?entityType=Course&reportId=422 +CSCI 737,Advanced Software Defined Networking,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/1128?entityType=Course&reportId=422 +CSCI 745,Advanced Big Data Analytics,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/1101?entityType=Course&reportId=422 +CSCI 747,Advanced Algorithmic Trading,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/868?entityType=Course&reportId=422 +CSCI 772,Advanced Cognitive Modelling for Human-Computer Interaction,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/937?entityType=Course&reportId=422 +CSCI 775,Advanced Formal Methods and Applications,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/966?entityType=Course&reportId=422 +CSCI 781,Advanced Acquisition and Analysis of Biomedical Data,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/1262?entityType=Course&reportId=422 +CSCI 785,Advanced Computer Vision,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/930?entityType=Course&reportId=422 +CSCI 791,Advanced Artificial Reasoning and Problem Solving,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/1073?entityType=Course&reportId=422 +CSCI 792,Advanced Intelligent Systems,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/1074?entityType=Course&reportId=422 +CSCI 794,Advanced Deep Learning,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/869?entityType=Course&reportId=422 +CSCI 795,Advanced Generative Artificial Intelligence,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/1187?entityType=Course&reportId=422 +DCEE 700,Thesis Research,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/1089?entityType=Course&reportId=422 +DCEE 700,0 Thesis Research,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/1090?entityType=Course&reportId=422 +DCEE 701,Current Research Literature,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/1091?entityType=Course&reportId=422 +DCEE 750,Finite Element Methods,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/1029?entityType=Course&reportId=422 +DCEE 751,Structural Dynamics and Earthquake Engineering,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/3435?entityType=Course&reportId=422 +DCEE 752,Advanced Concrete Technology,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/999?entityType=Course&reportId=422 +DCEE 753,Structural Evaluation and Rehabilitation,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/1245?entityType=Course&reportId=422 +DCEE 754,Theory of Physio-chemical Treatment Processes,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/1296?entityType=Course&reportId=422 +DCEE 755,Contaminated Site Management and Soil Treatment Technologies,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/1169?entityType=Course&reportId=422 +DCEE 756,Environmental Modeling Development,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/1059?entityType=Course&reportId=422 +DCEE 757,Advanced Environmental Chemistry,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/1298?entityType=Course&reportId=422 +DCEE 758,Computational Geomechanics,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/1275?entityType=Course&reportId=422 +DCEE 759,Geotechnical Earthquake Engineering,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/1276?entityType=Course&reportId=422 +DCEE 760,Advanced Foundation Engineering,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/901?entityType=Course&reportId=422 +DCEE 761,Advanced Soil Mechanics,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/1274?entityType=Course&reportId=422 +DCEE 762,Advanced Project Management,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/862?entityType=Course&reportId=422 +DCEE 763,Building Information Modeling in Construction,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/1092?entityType=Course&reportId=422 +DCEE 765,Concrete Repair and Maintenance,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/1246?entityType=Course&reportId=422 +DCEE 766,"Advanced Pavement Design, Analysis, and Rehabilitation",https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/1000?entityType=Course&reportId=422 +DCEE 767,Advanced Concrete Design,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/1031?entityType=Course&reportId=422 +DCEE 768,Advanced Wastewater Treatment,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/1297?entityType=Course&reportId=422 +DCEE 769,Sustainable Development,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/1057?entityType=Course&reportId=422 +DCEE 770,"Green Building - Concept, Design, Construction, and Operation",https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/1058?entityType=Course&reportId=422 +DCEE 771,Soil Improvement and Stabilization,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/1277?entityType=Course&reportId=422 +DCEE 772,Engineering Management and Engineering Economy,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/863?entityType=Course&reportId=422 +DCEE 773,Unsaturated Soil Mechanics for Engineering Practice,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/899?entityType=Course&reportId=422 +DCEE 774,Computational Modeling and Instrumentation in Geotechnical Engineering,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/900?entityType=Course&reportId=422 +DCHME 700,Thesis Research,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/1207?entityType=Course&reportId=422 +DCHME 700,0 Thesis Research,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/1208?entityType=Course&reportId=422 +DCHME 701,Current Research Literature,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/906?entityType=Course&reportId=422 +DCHME 752,Polymer Melt Fluid Mechanics and Processing,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/1299?entityType=Course&reportId=422 +DCHME 755,Integrated Computational Materials Engineering,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/1300?entityType=Course&reportId=422 +DCHME 757,Advanced Chemical Engineering Thermodynamics,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/1206?entityType=Course&reportId=422 +DCHME 761,Advanced Chemical Reaction Engineering,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/1267?entityType=Course&reportId=422 +DCHME 762,Advanced Materials Processing,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/907?entityType=Course&reportId=422 +DCHME 763,Advanced Heat and Mass Transfer,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/1231?entityType=Course&reportId=422 +DCHME 766,Advanced Energy Materials and Their Application,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/908?entityType=Course&reportId=422 +DELCE 700,Thesis Research,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/1149?entityType=Course&reportId=422 +DELCE 700,0 Thesis Research,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/1150?entityType=Course&reportId=422 +DELCE 701,Current Research Literature,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/1014?entityType=Course&reportId=422 +DELCE 753,Stochastic Processes,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/963?entityType=Course&reportId=422 +DELCE 758,Advanced Industrial and Commercial Energy Systems,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/1148?entityType=Course&reportId=422 +DELCE 759,Advanced Pattern Recognition and Machine Learning,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/921?entityType=Course&reportId=422 +DELCE 760,Advanced System Modelling and Control,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/1218?entityType=Course&reportId=422 +DELCE 761,Advanced Data Structures and Algorithms,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/1270?entityType=Course&reportId=422 +DELCE 762,Fundamentals of Signal Processing,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/1204?entityType=Course&reportId=422 +DELCE 763,Advanced Electronic Circuits,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/1189?entityType=Course&reportId=422 +DELCE 764,Advanced Semiconductor Process Engineering for VLSI,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/934?entityType=Course&reportId=422 +DENG 700,Thesis Research,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/1107?entityType=Course&reportId=422 +DENG 700,0 Thesis Research,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/1108?entityType=Course&reportId=422 +DENG 701,Current Research Literature,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/1109?entityType=Course&reportId=422 +DENG 782,Research Methods and Ethics,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/950?entityType=Course&reportId=422 +DENG 789,Doctoral Seminar I,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/1110?entityType=Course&reportId=422 +DENG 790,Doctoral Seminar II,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/1111?entityType=Course&reportId=422 +DENG 791,Doctoral Seminar III,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/1112?entityType=Course&reportId=422 +DENG 792,Doctoral Seminar IV,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/1113?entityType=Course&reportId=422 +DENG 793,Doctoral Seminar V,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/1114?entityType=Course&reportId=422 +DMAE 700,Thesis Research,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/1105?entityType=Course&reportId=422 +DMAE 700,0 Thesis Research,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/1106?entityType=Course&reportId=422 +DMAE 701,Advanced Applied Mathematics,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/1043?entityType=Course&reportId=422 +DMAE 702,Current Research Literature,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/915?entityType=Course&reportId=422 +DMAE 703,Numerical Techniques for Engineers,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/1042?entityType=Course&reportId=422 +DMAE 704,Rocket Propulsion Fundamentals,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/1016?entityType=Course&reportId=422 +DMAE 705,Sustainable Vehicle Transportation Systems,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/1017?entityType=Course&reportId=422 +DMAE 750,Advanced Statistics and Probability,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/1312?entityType=Course&reportId=422 +DMAE 751,Modern Control Engineering,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/1147?entityType=Course&reportId=422 +DMAE 753,Computational and Unsteady Aerodynamics,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/954?entityType=Course&reportId=422 +DMAE 756,Design and Manufacturing with Environmental Concern,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/1103?entityType=Course&reportId=422 +DMAE 757,Design Optimization,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/1104?entityType=Course&reportId=422 +DMAE 759,Space Structures Design,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/1049?entityType=Course&reportId=422 +DMAE 763,"Computational Fluid-Structure Interaction: Methods, Models and Applications",https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/1315?entityType=Course&reportId=422 +DMAE 765,Advanced Manufacturing Processes and Strategies,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/945?entityType=Course&reportId=422 +DMAE 769,Advanced Computational Fluid Dynamics (CFD) and Heat Transfer in Mechanical Engineering,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/1316?entityType=Course&reportId=422 +DMAE 770,Digital Transformation Concepts,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/1048?entityType=Course&reportId=422 +DMAE 771,Space Flight Dynamics,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/1044?entityType=Course&reportId=422 +DMAE 772,Advanced Materials and Composites,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/1247?entityType=Course&reportId=422 +DPE 608,Strategic Management and Leadership,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2341?entityType=Course&reportId=422 +DPE 609,Organizational Behavior in the Public Sector,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/472?entityType=Course&reportId=422 +DPE 612,Policy Tools for Addressing Inequality and Demographic Change,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2343?entityType=Course&reportId=422 +DPE 615,Scientific Reasoning for Policy Research,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/1379?entityType=Course&reportId=422 +DPE 620,Circular Economy Strategy,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/1382?entityType=Course&reportId=422 +DPE 628,Good Governance and Corruption,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/122?entityType=Course&reportId=422 +DPE 632,Artificial Intelligence and Governance,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/1374?entityType=Course&reportId=422 +DPE 646,Communications for Public Leadership,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/124?entityType=Course&reportId=422 +DPE 651,Agricultural Policy Analysis,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/3307?entityType=Course&reportId=422 +DPE 660,Health System and Policy,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/3308?entityType=Course&reportId=422 +DPE 671,Game Theory and Public Policy,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/127?entityType=Course&reportId=422 +DPE 673,Development Assistance and Governance,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/123?entityType=Course&reportId=422 +DPE 682,Sustainable Development and Environmental Governance,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/128?entityType=Course&reportId=422 +DPE 683,Energy Systems and Climate Policy,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/692?entityType=Course&reportId=422 +DPE 684,Global Energy Policy,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/693?entityType=Course&reportId=422 +DPE 685,Water Resources Policy and Management,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2338?entityType=Course&reportId=422 +DPM 604,Economic Applications for Public Managers,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/675?entityType=Course&reportId=422 +DPM 610,Program Evaluation,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/674?entityType=Course&reportId=422 +DPP 600,Developing Dissertation Research,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/108?entityType=Course&reportId=422 +DPP 601,Microeconomics in the Public Sector,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/118?entityType=Course&reportId=422 +DPP 602,Macroeconomics in Public Sector,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/120?entityType=Course&reportId=422 +DPP 603,Public Administration and Management,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/3396?entityType=Course&reportId=422 +DPP 611,Statistics in Public Policy,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/121?entityType=Course&reportId=422 +DPP 613,Research Design,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/114?entityType=Course&reportId=422 +DPP 614,Advanced Quantitative Methods,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/116?entityType=Course&reportId=422 +DPP 615,"Law, Governance and Public Policy",https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/3302?entityType=Course&reportId=422 +DPP 616,Advanced Qualitative Methods,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/115?entityType=Course&reportId=422 +DPP 621,Public Policy and Analysis,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/113?entityType=Course&reportId=422 +DPP 631,Politics of Public Policy,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/112?entityType=Course&reportId=422 +DPP 690,Thesis Preparation and Writing,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/109?entityType=Course&reportId=422 +DPP 690,0 Thesis Preparation and Writing,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/110?entityType=Course&reportId=422 +DPP 699,Thesis Design and Written Proposal,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/111?entityType=Course&reportId=422 +DS 501,Fundamentals of Data Science,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/1178?entityType=Course&reportId=422 +DS 502,Probability and Statistics for Data Science,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/1254?entityType=Course&reportId=422 +DS 504,Data Mining and Decision Support,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/1097?entityType=Course&reportId=422 +DS 507,Database Management Systems,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/1313?entityType=Course&reportId=422 +DS 509,Information Retrieval,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/1213?entityType=Course&reportId=422 +DS 551,Process and Project Management,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/944?entityType=Course&reportId=422 +DS 552,Data-Driven Innovation,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/1096?entityType=Course&reportId=422 +DS 693,Thesis Proposal,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/1120?entityType=Course&reportId=422 +DS 694,Thesis,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/1121?entityType=Course&reportId=422 +DS 704,Advanced Data Mining and Decision Support,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/1098?entityType=Course&reportId=422 +DUT 101,Beginning Dutch I,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2725?entityType=Course&reportId=422 +DUT 102,Beginning Dutch II,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2726?entityType=Course&reportId=422 +DUT 201,Intermediate Dutch I,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2727?entityType=Course&reportId=422 +EAS 500,Introduction to Eurasia,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2614?entityType=Course&reportId=422 +EAS 501,General Methodology,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2724?entityType=Course&reportId=422 +EAS 502,Disciplinary Methodologies,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2697?entityType=Course&reportId=422 +EAS 503,Thesis I,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2615?entityType=Course&reportId=422 +EAS 504,Thesis II,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2616?entityType=Course&reportId=422 +EAS 505,Research and Fieldwork Practicum,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2617?entityType=Course&reportId=422 +EAS 506,Qualitative Research Methods,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2696?entityType=Course&reportId=422 +EAS 511,Independent Study in Eurasian Studies,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2609?entityType=Course&reportId=422 +EAS 512,Topics in Eurasian Studies,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2610?entityType=Course&reportId=422 +EAS 513,Critical Issues in Eurasian Studies,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2611?entityType=Course&reportId=422 +EAS 514,Topics in Eurasian Social Sciences,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2612?entityType=Course&reportId=422 +EAS 700,Introduction to Eurasia,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2613?entityType=Course&reportId=422 +EAS 701,Critical Issues in Eurasian Studies,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/3198?entityType=Course&reportId=422 +EAS 702,Elective in Eurasian Humanities,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/3199?entityType=Course&reportId=422 +EAS 703,Elective in Eurasian Social Sciences,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/3200?entityType=Course&reportId=422 +EAS 704,Elective II in Eurasian Humanities,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/3201?entityType=Course&reportId=422 +EAS 705,Elective II in Eurasian Social Sciences,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/3202?entityType=Course&reportId=422 +EAS 708,Proseminar in Eurasian Humanities,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/3203?entityType=Course&reportId=422 +EAS 709,Proseminar in Eurasian Social Sciences,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/3204?entityType=Course&reportId=422 +EAS 710,Independent Study in Eurasian Studies,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/3205?entityType=Course&reportId=422 +EAS 711,Doctoral Seminar in Eurasian Studies I,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/3207?entityType=Course&reportId=422 +EAS 712,Doctoral Seminar in Eurasian Studies II,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/3208?entityType=Course&reportId=422 +EAS 713,Qualifying Examination,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/3206?entityType=Course&reportId=422 +EAS 800,Doctoral Thesis in Eurasian Studies,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/3210?entityType=Course&reportId=422 +EAS 800,0 Dissertation Research in Eurasian Studies,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/3209?entityType=Course&reportId=422 +ECON 101,Introduction to Microeconomics,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2374?entityType=Course&reportId=422 +ECON 102,Introduction to Macroeconomics,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2577?entityType=Course&reportId=422 +ECON 120,Managerial Economics,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2366?entityType=Course&reportId=422 +ECON 201,Intermediate Microeconomics,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2999?entityType=Course&reportId=422 +ECON 202,Intermediate Macroeconomics,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/3291?entityType=Course&reportId=422 +ECON 211,Economic Statistics,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/3248?entityType=Course&reportId=422 +ECON 300,Research Assistance in Economics,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2578?entityType=Course&reportId=422 +ECON 301,Econometrics I,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2995?entityType=Course&reportId=422 +ECON 302,Game Theory and Economic Analysis,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2997?entityType=Course&reportId=422 +ECON 305,Advanced Microeconomics,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2998?entityType=Course&reportId=422 +ECON 318,Applied Econometrics,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/3043?entityType=Course&reportId=422 +ECON 319,Matching Theory and Applications,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2356?entityType=Course&reportId=422 +ECON 320,Money and Banking,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2755?entityType=Course&reportId=422 +ECON 322,Natural Resources and Environmental Economics,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/3290?entityType=Course&reportId=422 +ECON 325,International Trade,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2443?entityType=Course&reportId=422 +ECON 326,The Economics of Financial Markets,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2754?entityType=Course&reportId=422 +ECON 328,Labor Economics,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2576?entityType=Course&reportId=422 +ECON 333,Political Economy,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2357?entityType=Course&reportId=422 +ECON 335,Economics of Information,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2895?entityType=Course&reportId=422 +ECON 336,Programming for Economists,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2985?entityType=Course&reportId=422 +ECON 337,Empirical Finance,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2367?entityType=Course&reportId=422 +ECON 341,Economic Simulation Modeling,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/3289?entityType=Course&reportId=422 +ECON 345,The Economics of Family,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2579?entityType=Course&reportId=422 +ECON 400,Research Assistance in Economics II,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2440?entityType=Course&reportId=422 +ECON 403,Introduction to Contract Theory and Auctions,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2371?entityType=Course&reportId=422 +ECON 413,Econometrics II Time Series,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2854?entityType=Course&reportId=422 +ECON 414,Advanced Monetary Policy,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2400?entityType=Course&reportId=422 +ECON 415,Industrial Organization,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2438?entityType=Course&reportId=422 +ECON 416,Empirical Industrial Organization,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2439?entityType=Course&reportId=422 +ECON 418,Corporate Finance and Governance,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2948?entityType=Course&reportId=422 +ECON 428,Wages and the Labor Market,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2580?entityType=Course&reportId=422 +ECON 433,Econometrics II Panel Data,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/3044?entityType=Course&reportId=422 +ECON 434,Introduction to Big Data Analytics,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2984?entityType=Course&reportId=422 +ECON 443,Topics in Macroeconomic Theory,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2863?entityType=Course&reportId=422 +ECON 449,Quantitative Macroeconomics,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2399?entityType=Course&reportId=422 +ECON 501,Microeconomics I,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2862?entityType=Course&reportId=422 +ECON 502,Macroeconomics I,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2996?entityType=Course&reportId=422 +ECON 503,Introduction to Contract Theory and Auctions,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2372?entityType=Course&reportId=422 +ECON 511,Statistics,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2855?entityType=Course&reportId=422 +ECON 512,Mathematical Economics,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2860?entityType=Course&reportId=422 +ECON 513,Econometrics II Times Series,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2856?entityType=Course&reportId=422 +ECON 514,Advanced Monetary Policy,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2398?entityType=Course&reportId=422 +ECON 515,Industrial Organization,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2441?entityType=Course&reportId=422 +ECON 516,Empirical Industrial Organization,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2442?entityType=Course&reportId=422 +ECON 518,Corporate Finance and Governance,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2949?entityType=Course&reportId=422 +ECON 521,Microeconomic Theory II,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/3249?entityType=Course&reportId=422 +ECON 522,Macroeconomics II,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2397?entityType=Course&reportId=422 +ECON 528,Wages and the Labor Market,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2861?entityType=Course&reportId=422 +ECON 531,Econometrics,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2857?entityType=Course&reportId=422 +ECON 532,Applied Econometrics,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2858?entityType=Course&reportId=422 +ECON 533,Econometrics II Panel Data,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/3045?entityType=Course&reportId=422 +ECON 534,Introduction to Big Data Analytics,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2982?entityType=Course&reportId=422 +ECON 536,Programming for Economists,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2983?entityType=Course&reportId=422 +ECON 543,Topics in Macroeconomic Theory,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2859?entityType=Course&reportId=422 +ECON 547,Advanced Game Theory: Mechanism Design,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2373?entityType=Course&reportId=422 +ECON 548,Structural Methods in Market Design,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/3046?entityType=Course&reportId=422 +ECON 549,Quantitative Macroeconomics,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2396?entityType=Course&reportId=422 +ECON 580,Research and Thesis,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2864?entityType=Course&reportId=422 +ECON 589,Thesis I,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2865?entityType=Course&reportId=422 +ECON 590,Thesis II,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2866?entityType=Course&reportId=422 +EDHE 601,Thesis Seminar I,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/193?entityType=Course&reportId=422 +EDHE 602,Thesis Seminar II,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/194?entityType=Course&reportId=422 +EDHE 603,Thesis Seminar III,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/195?entityType=Course&reportId=422 +EDHE 604,Thesis Seminar IV,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/196?entityType=Course&reportId=422 +EDHE 605,Thesis Submission and Defense,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/197?entityType=Course&reportId=422 +EDHE 625,Fundamentals of Higher Education,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/198?entityType=Course&reportId=422 +EDHE 626,Organization and Governance in Higher Education,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/199?entityType=Course&reportId=422 +EDHE 627,"Student Experience, Learning and Success in Higher Education",https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/200?entityType=Course&reportId=422 +EDHE 628,Higher Education Policy Analysis and Development,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/201?entityType=Course&reportId=422 +EDHE 629,Globalization and International Higher Education,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/202?entityType=Course&reportId=422 +EDHE 631,Sociology of Higher Education,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/203?entityType=Course&reportId=422 +EDIE 601,Thesis Seminar I,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/183?entityType=Course&reportId=422 +EDIE 602,Thesis Seminar II,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/184?entityType=Course&reportId=422 +EDIE 603,Thesis Seminar III,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/185?entityType=Course&reportId=422 +EDIE 604,Thesis Seminar IV,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/186?entityType=Course&reportId=422 +EDIE 605,Thesis Submission and Defense,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/187?entityType=Course&reportId=422 +EDIE 625,Fundamentals of Inclusive Education,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/188?entityType=Course&reportId=422 +EDIE 626,Global Perspectives on Inclusive Education,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/189?entityType=Course&reportId=422 +EDIE 628,Inclusive Curriculum and Assessment,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/190?entityType=Course&reportId=422 +EDIE 629,Gifted and Talented Learners,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/191?entityType=Course&reportId=422 +EDIE 631,Developing Inclusive Schools for an Inclusive Society,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/192?entityType=Course&reportId=422 +EDML 601,Introduction to Linguistics,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/204?entityType=Course&reportId=422 +EDML 602,Academic Literacies for Multilingual Education Professionals I,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/850?entityType=Course&reportId=422 +EDML 603,Academic Literacies for Multilingual Education Professionals II,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/860?entityType=Course&reportId=422 +EDML 610,Foundations of Multilingual Education,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/205?entityType=Course&reportId=422 +EDML 612,Multilingual Society,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/206?entityType=Course&reportId=422 +EDML 613,The Multilingual School,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/207?entityType=Course&reportId=422 +EDML 614,Multilingual Curriculum Development and Assessment,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/208?entityType=Course&reportId=422 +EDML 620,Language and Literacy Development,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/209?entityType=Course&reportId=422 +EDML 622,First and Second Langauge Acquisition,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/210?entityType=Course&reportId=422 +EDML 627,Application of CLIL in Teaching,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/211?entityType=Course&reportId=422 +EDML 629,Multiliteracies: Teaching Language and Literacy for a Multilingual Context,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/212?entityType=Course&reportId=422 +EDML 630,Language Policy and Education Reform Across Contexts,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/213?entityType=Course&reportId=422 +EDML 680,Developing the Thesis Research Topic,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/214?entityType=Course&reportId=422 +EDML 690,Thesis Research I,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/1326?entityType=Course&reportId=422 +EDML 691,Thesis Research II,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/216?entityType=Course&reportId=422 +EDML 692,Thesis Research III,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/1327?entityType=Course&reportId=422 +EDSE 601,Thesis Seminar I,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/173?entityType=Course&reportId=422 +EDSE 602,Thesis Seminar II,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/174?entityType=Course&reportId=422 +EDSE 603,Thesis Seminar III,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/175?entityType=Course&reportId=422 +EDSE 604,Thesis Seminar IV,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/176?entityType=Course&reportId=422 +EDSE 605,Thesis Submission and Defense,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/177?entityType=Course&reportId=422 +EDSE 621,Education Policy Analysis and Development,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/178?entityType=Course&reportId=422 +EDSE 625,Fundamentals of School Education,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/179?entityType=Course&reportId=422 +EDSE 626,Curriculum Development and Implementation,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/180?entityType=Course&reportId=422 +EDSE 629,Teacher Development and Identity,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/181?entityType=Course&reportId=422 +EDSE 632,School Improvement and Effectiveness,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/182?entityType=Course&reportId=422 +EDUC 510,Advanced English for Education Professionals I,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/135?entityType=Course&reportId=422 +EDUC 511,Advanced English for Education Professionals II,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/136?entityType=Course&reportId=422 +EDUC 512,English for Thesis Writing I,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/137?entityType=Course&reportId=422 +EDUC 513,English for Thesis Writing II,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/138?entityType=Course&reportId=422 +EDUC 531,Academic English II,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/140?entityType=Course&reportId=422 +EDUC 535,Academic English I,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/141?entityType=Course&reportId=422 +EDUC 571,Basic Kazakh,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/129?entityType=Course&reportId=422 +EDUC 572,Kazakh for Professional Purposes,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/130?entityType=Course&reportId=422 +EDUC 573,Academic Writing in Kazakh,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/131?entityType=Course&reportId=422 +EDUC 581,Basic Kazakh (MSc),https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/142?entityType=Course&reportId=422 +EDUC 582,Kazakh for Professional Purposes (MSc),https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/143?entityType=Course&reportId=422 +EDUC 583,Academic Writing in Kazakh (MSc),https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/144?entityType=Course&reportId=422 +EDUC 585,Education in Kazakhstan for International Students,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/132?entityType=Course&reportId=422 +EDUC 590,Academic English Writing for Doctoral Students I,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/151?entityType=Course&reportId=422 +EDUC 591,Academic English Writing for Doctoral Students II,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/152?entityType=Course&reportId=422 +EDUC 592,Academic English Writing for Doctoral Students III,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/153?entityType=Course&reportId=422 +EDUC 602,Educational Context and Reform in Kazakhstan,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/139?entityType=Course&reportId=422 +EDUC 606,Introduction to Education Research,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/145?entityType=Course&reportId=422 +EDUC 607,Research Methods I,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/133?entityType=Course&reportId=422 +EDUC 608,Research Methods II,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/134?entityType=Course&reportId=422 +EDUC 610,Management in Education,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/146?entityType=Course&reportId=422 +EDUC 617,Contextualizing Education Reform in Kazakhstan and Beyond,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/147?entityType=Course&reportId=422 +EDUC 618,Educational Leadership,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/148?entityType=Course&reportId=422 +EDUC 627,Psychology of Learning,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/149?entityType=Course&reportId=422 +EDUC 630,Economics and Finance in Education,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/150?entityType=Course&reportId=422 +EDUC 701,Education Research Methods,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/154?entityType=Course&reportId=422 +EDUC 704,Advanced Methods of Education Research,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/155?entityType=Course&reportId=422 +EDUC 705,Advanced Methods of Education Research: Qualitative Methods,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/156?entityType=Course&reportId=422 +EDUC 709,Introduction to Scholarship in Education,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/157?entityType=Course&reportId=422 +EDUC 711,Globalization and Education,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/158?entityType=Course&reportId=422 +EDUC 712,Educational Policy and Reform,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/159?entityType=Course&reportId=422 +EDUC 720,Philosophy of Education,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/160?entityType=Course&reportId=422 +EDUC 727,Exploring the Research Topic I,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/161?entityType=Course&reportId=422 +EDUC 728,Exploring the Research Topic II,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/162?entityType=Course&reportId=422 +EDUC 729,Developing the Research Topic,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/163?entityType=Course&reportId=422 +EDUC 730,Preparation for Proposal Defence,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/164?entityType=Course&reportId=422 +EDUC 733,Thesis Proposal Defense,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/165?entityType=Course&reportId=422 +EDUC 740,Doctoral Seminar I,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/166?entityType=Course&reportId=422 +EDUC 741,Doctoral Seminar II,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/167?entityType=Course&reportId=422 +EDUC 751,Dissertation Research,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/168?entityType=Course&reportId=422 +EDUC 752,Dissertation Research: Independent Study,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/169?entityType=Course&reportId=422 +EDUC 753,Thesis Defense,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/851?entityType=Course&reportId=422 +EDUC 760,"Teaching and Learning in the University Context: Curriculum, Pedagogy and Assessment",https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/171?entityType=Course&reportId=422 +EDUC 800,International Study,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/172?entityType=Course&reportId=422 +ELCE 200,Circuits Theory I,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/1065?entityType=Course&reportId=422 +ELCE 201,Circuits Theory II,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/1066?entityType=Course&reportId=422 +ELCE 201L,Circuits Theory Laboratory,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/1253?entityType=Course&reportId=422 +ELCE 202,Digital Logic Design,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/891?entityType=Course&reportId=422 +ELCE 202L,Digital Logic Design Laboratory,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/892?entityType=Course&reportId=422 +ELCE 203,Signals and Systems,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/1198?entityType=Course&reportId=422 +ELCE 203L,Signals and Systems Laboratory,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/1199?entityType=Course&reportId=422 +ELCE 204,Solid State Devices,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/932?entityType=Course&reportId=422 +ELCE 204L,Solid State Devices Laboratory,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/933?entityType=Course&reportId=422 +ELCE 205,Discrete Mathematics,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/893?entityType=Course&reportId=422 +ELCE 300,Microprocessor Systems,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/1220?entityType=Course&reportId=422 +ELCE 300L,Microprocessor Systems Laboratory,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/1221?entityType=Course&reportId=422 +ELCE 301,Electronic Circuits,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/1192?entityType=Course&reportId=422 +ELCE 301L,Electronic Circuits Laboratory,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/1193?entityType=Course&reportId=422 +ELCE 302,Electrical Machines,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/1214?entityType=Course&reportId=422 +ELCE 302L,Electrical Machines Laboratory,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/1215?entityType=Course&reportId=422 +ELCE 303,Power System Analysis,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/1154?entityType=Course&reportId=422 +ELCE 303L,Power System Analysis Laboratory,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/1155?entityType=Course&reportId=422 +ELCE 304,Computer Networks,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/1224?entityType=Course&reportId=422 +ELCE 304L,Computer Networks Laboratory,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/1225?entityType=Course&reportId=422 +ELCE 305,Data Structures and Algorithms,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/881?entityType=Course&reportId=422 +ELCE 305L,Data Structures and Algorithms Laboratory,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/882?entityType=Course&reportId=422 +ELCE 306,Linear Control Theory,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/1161?entityType=Course&reportId=422 +ELCE 306L,Linear Control Theory Laboratory,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/1162?entityType=Course&reportId=422 +ELCE 307,Digital Signal Processing,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/1200?entityType=Course&reportId=422 +ELCE 307L,Digital Signal Processing Laboratory,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/1201?entityType=Course&reportId=422 +ELCE 308,Communication Systems,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/1222?entityType=Course&reportId=422 +ELCE 308L,Communication Systems Laboratory,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/1223?entityType=Course&reportId=422 +ELCE 309,Individual Study,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/1071?entityType=Course&reportId=422 +ELCE 310L,Electronic Instrumentation and Measurement Laboratory,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/890?entityType=Course&reportId=422 +ELCE 311,Interdisciplinary Design Project,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/1271?entityType=Course&reportId=422 +ELCE 350,Electromagnetics I,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/984?entityType=Course&reportId=422 +ELCE 352,Applied Simulation Laboratory,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/1343?entityType=Course&reportId=422 +ELCE 399,Internship,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/1202?entityType=Course&reportId=422 +ELCE 403,Introduction to Adaptive Signal Processing,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/1203?entityType=Course&reportId=422 +ELCE 450,Modern Characterizations for Semiconductor Industry,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/931?entityType=Course&reportId=422 +ELCE 451,Electric Drives and Motion Control Systems,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/1216?entityType=Course&reportId=422 +ELCE 452,Renewable Energy and Electric Vehicles,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/1151?entityType=Course&reportId=422 +ELCE 453,"High Voltage, AC/DC, FACTS Devices",https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/1152?entityType=Course&reportId=422 +ELCE 454,Power Transmission and Distribution Systems,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/1153?entityType=Course&reportId=422 +ELCE 455,Machine Learning with Python,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/922?entityType=Course&reportId=422 +ELCE 456,Internet of Things,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/964?entityType=Course&reportId=422 +ELCE 457,Introduction to Computer Vision,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/894?entityType=Course&reportId=422 +ELCE 458,Numerical Optimization Techniques and Computer Applications,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/879?entityType=Course&reportId=422 +ELCE 459,Design of Application Specific Electronic Circuits,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/1190?entityType=Course&reportId=422 +ELCE 460,Introduction to Computer Architecture,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/1205?entityType=Course&reportId=422 +ELCE 461,Industrial Automation,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/1163?entityType=Course&reportId=422 +ELCE 461L,Industrial Automation Laboratory,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/1164?entityType=Course&reportId=422 +ELCE 462,Wireless Networks,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/965?entityType=Course&reportId=422 +ELCE 463,Network Science: Methods and Applications,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/880?entityType=Course&reportId=422 +ELCE 465,PCB Design and Manufacturing,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/1273?entityType=Course&reportId=422 +ELCE 466,RF and Microwave Circuits,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/1191?entityType=Course&reportId=422 +ELCE 467,Modern Wireless Communications,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/1272?entityType=Course&reportId=422 +ELCE 468,Robots for Rehabilitation,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/1217?entityType=Course&reportId=422 +ELCE 469,Microfluidics Fundamentals and Applications,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/1072?entityType=Course&reportId=422 +ELCE 470,Optical Communications,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/983?entityType=Course&reportId=422 +ELCE 486,Photonics for Engineering,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/1015?entityType=Course&reportId=422 +EMBAE 704,Project Management,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/370?entityType=Course&reportId=422 +EMBAE 707,Project Finance,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/369?entityType=Course&reportId=422 +EMBAE 709,Consumer Behaviour,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/371?entityType=Course&reportId=422 +EMEM 509,Systems Engineering,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/1050?entityType=Course&reportId=422 +EMEM 527,Globalization and Engineering,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/1130?entityType=Course&reportId=422 +EMEM 533,Engineering Management and Engineering Economy,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/864?entityType=Course&reportId=422 +EMEM 535,Managing Product and Service Development,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/1051?entityType=Course&reportId=422 +EMEM 537,Engineering Decision Tools,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/1230?entityType=Course&reportId=422 +EMEM 538,Knowledge and Innovation Management,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/1131?entityType=Course&reportId=422 +EMEM 539,Big Data and Information Management,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/923?entityType=Course&reportId=422 +EMEM 543,Quality and Lean Management,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/1132?entityType=Course&reportId=422 +EMEM 551,Digital Transformation and Disruption,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/1133?entityType=Course&reportId=422 +EMEM 552,Capstone Project Planning,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/1134?entityType=Course&reportId=422 +EMEM 553,Capstone Project Development,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/1135?entityType=Course&reportId=422 +EMEM 554,Operations and Supply Chain Management,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/1229?entityType=Course&reportId=422 +ENG 100,Introduction to Engineering,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/1069?entityType=Course&reportId=422 +ENG 101,Programming for Engineers,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/1269?entityType=Course&reportId=422 +ENG 102,Engineering Materials I,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/949?entityType=Course&reportId=422 +ENG 103,Engineering Materials II,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/1211?entityType=Course&reportId=422 +ENG 200,Engineering Mathematics III (Differential Equations & Linear Algebra),https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/1011?entityType=Course&reportId=422 +ENG 201,Applied Probability and Statistics,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/961?entityType=Course&reportId=422 +ENG 202,Numerical Methods in Engineering,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/980?entityType=Course&reportId=422 +ENG 300,Interdisciplinary Project,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/1115?entityType=Course&reportId=422 +ENG 400,CHME Capstone Project,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/1022?entityType=Course&reportId=422 +ENG 400,ELCE Capstone Project,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/1064?entityType=Course&reportId=422 +ENG 400,MAE Capstone Project,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/917?entityType=Course&reportId=422 +ENG 400,СEE Capstone Project,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/3440?entityType=Course&reportId=422 +FEAP 010,English for Academic Purposes I,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/723?entityType=Course&reportId=422 +FEAP 020,English for Academic Purposes II,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/725?entityType=Course&reportId=422 +FIN 201,Principles of Finance,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/372?entityType=Course&reportId=422 +FLDP 090,Foundations of Leadership I,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/727?entityType=Course&reportId=422 +FLDP 095,Foundations of Leadership II,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/728?entityType=Course&reportId=422 +FMAT 010,Foundation Mathematics I,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/729?entityType=Course&reportId=422 +FMAT 041,Foundation Mathematics II,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/730?entityType=Course&reportId=422 +FRE 101,Beginning French I,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/3116?entityType=Course&reportId=422 +FRE 102,Beginning French II,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/3117?entityType=Course&reportId=422 +FRE 201,Intermediate French I,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/3118?entityType=Course&reportId=422 +FRE 202,Intermediate French II,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/3119?entityType=Course&reportId=422 +FRE 312,Advanced Expression,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/3120?entityType=Course&reportId=422 +GEOL 101,Fundamentals of Geology,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/222?entityType=Course&reportId=422 +GEOL 201,Mineralogy and Petrology,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/1365?entityType=Course&reportId=422 +GEOL 202,Geologic Maps and Cross-Sections,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/1368?entityType=Course&reportId=422 +GEOL 203,Sedimentary Petrology,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/1369?entityType=Course&reportId=422 +GEOL 204,Sedimentology and Stratigraphy,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/236?entityType=Course&reportId=422 +GEOL 205,Paleontology,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/218?entityType=Course&reportId=422 +GEOL 206,Field Geology I,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/237?entityType=Course&reportId=422 +GEOL 207,Geomorphology,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/233?entityType=Course&reportId=422 +GEOL 301,Igneous and Metamorphic Petrology,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/714?entityType=Course&reportId=422 +GEOL 302,Thermodynamics and Geochemistry,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/1366?entityType=Course&reportId=422 +GEOL 303,Structural Geology and Tectonics,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/245?entityType=Course&reportId=422 +GEOL 304,Geophysics,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/246?entityType=Course&reportId=422 +GEOL 305,Geology of Ore Deposits,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/223?entityType=Course&reportId=422 +GEOL 306,Geodynamics,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/247?entityType=Course&reportId=422 +GEOL 307,Geographic Information Systems,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/219?entityType=Course&reportId=422 +GEOL 308,Environmental Geochemistry,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/248?entityType=Course&reportId=422 +GEOL 309,Hydrogeology,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/230?entityType=Course&reportId=422 +GEOL 310,Field Geology II,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/249?entityType=Course&reportId=422 +GEOL 401,Petroleum Geology and Geochemistry,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/238?entityType=Course&reportId=422 +GEOL 402,Water Resource Management and Environmental Geosciences,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/231?entityType=Course&reportId=422 +GEOL 403,Geostatistics,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/220?entityType=Course&reportId=422 +GEOL 404,Research Project I,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/250?entityType=Course&reportId=422 +GEOL 405,Research Project II,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/251?entityType=Course&reportId=422 +GEOL 407,Exploration Geology and Geophysics,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/234?entityType=Course&reportId=422 +GEOL 408,Remote Sensing of the Earth,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/3424?entityType=Course&reportId=422 +GEOL 410,"Carbonate Systems, Processes and Deposits",https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/239?entityType=Course&reportId=422 +GEOL 411,Photography for Geosciences,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/721?entityType=Course&reportId=422 +GEOL 501,Advanced Economic Geology,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/224?entityType=Course&reportId=422 +GEOL 502,Advanced Petroleum Geology,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/240?entityType=Course&reportId=422 +GEOL 503,Advanced Sedimentology,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/241?entityType=Course&reportId=422 +GEOL 504,Advanced Geochemistry,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/253?entityType=Course&reportId=422 +GEOL 505,Advanced Geophysics,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/254?entityType=Course&reportId=422 +GEOL 506,Advanced Petrology,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/717?entityType=Course&reportId=422 +GEOL 507,Exploration Geosciences,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/235?entityType=Course&reportId=422 +GEOL 508,Advanced Structural Geology and Microtectonics,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/709?entityType=Course&reportId=422 +GEOL 509,Advanced Field Geology,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/256?entityType=Course&reportId=422 +GEOL 601,Advanced Tectonics and Geodynamics,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/257?entityType=Course&reportId=422 +GEOL 602,Advanced GIS,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/221?entityType=Course&reportId=422 +GEOL 603,Advanced Hydrogeology,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/232?entityType=Course&reportId=422 +GEOL 604,Engineering Geology and Geotechnics,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/242?entityType=Course&reportId=422 +GEOL 605,Geology of Central Asia and Metallogenesis,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/225?entityType=Course&reportId=422 +GEOL 690,Thesis Research,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/258?entityType=Course&reportId=422 +GER 101,Beginning German,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2733?entityType=Course&reportId=422 +GER 102,Beginning German II,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2734?entityType=Course&reportId=422 +GER 201,Intermediate German I,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2735?entityType=Course&reportId=422 +GER 202,Intermediate German II,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2736?entityType=Course&reportId=422 +GSBA 620,Financial Accounting,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/374?entityType=Course&reportId=422 +GSBA 622,Managerial Accounting,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/373?entityType=Course&reportId=422 +GSBE 630,Managerial Economics,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/375?entityType=Course&reportId=422 +GSBF 501,Orientation Week (Team Building),https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/386?entityType=Course&reportId=422 +GSBF 511,Financial Data Analytics I,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/376?entityType=Course&reportId=422 +GSBF 512,Principles of Finance,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/662?entityType=Course&reportId=422 +GSBF 513,Financial Data Analytics II,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/387?entityType=Course&reportId=422 +GSBF 514,Corporate Finance,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/379?entityType=Course&reportId=422 +GSBF 515A,Investments I,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/382?entityType=Course&reportId=422 +GSBF 517,Macroeconomics for Finance,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/377?entityType=Course&reportId=422 +GSBF 520,Empirical Asset Pricing,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/383?entityType=Course&reportId=422 +GSBF 523,Special Topics for Coporate Finance,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/380?entityType=Course&reportId=422 +GSBF 541,Residency I: Almaty/NBK,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/389?entityType=Course&reportId=422 +GSBF 542,Residency II: International Financial Center,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/390?entityType=Course&reportId=422 +GSBF 550A,Master's Thesis,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/385?entityType=Course&reportId=422 +GSBF 660,Foundations of Finance,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/378?entityType=Course&reportId=422 +GSBF 661,Corporate Finance,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/381?entityType=Course&reportId=422 +GSBM 640,Management and Organizations,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/393?entityType=Course&reportId=422 +GSBM 641,Quantitative Tools for Managers,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/392?entityType=Course&reportId=422 +GSBM 642,Strategy,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/394?entityType=Course&reportId=422 +GSBM 644,Entrepreneurship,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/399?entityType=Course&reportId=422 +GSBM 646,Advanced Leadership,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/391?entityType=Course&reportId=422 +GSBM 647,Operations Management,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/395?entityType=Course&reportId=422 +GSBM 648,Negotiations,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/396?entityType=Course&reportId=422 +GSBM 649,Raising Capital,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/397?entityType=Course&reportId=422 +GSBM 653,Emerging Markets Strategy,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/400?entityType=Course&reportId=422 +GSBM 654,Capstone Project,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/398?entityType=Course&reportId=422 +GSBMK 650,Marketing Management,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/402?entityType=Course&reportId=422 +GSBMK 652,Marketing Strategy,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/401?entityType=Course&reportId=422 +GSBO 6,Orientation,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/403?entityType=Course&reportId=422 +GSBPHD 01,Foundations of Social Science Research,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/417?entityType=Course&reportId=422 +GSBPHD 02,Qualitative Research Methods,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/418?entityType=Course&reportId=422 +GSBPHD 03,Quantitative Research Methods,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/405?entityType=Course&reportId=422 +GSBPHD 04,Core Research Skills I,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/415?entityType=Course&reportId=422 +GSBPHD 05,Subject Specific Course I,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/406?entityType=Course&reportId=422 +GSBPHD 06,Subject Specific Course II,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/407?entityType=Course&reportId=422 +GSBPHD 07,Subject Specific Course III,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/408?entityType=Course&reportId=422 +GSBPHD 08,Core Research Skills II,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/416?entityType=Course&reportId=422 +GSBPHD 09,GSBPHD Elective 1,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/404?entityType=Course&reportId=422 +GSBPHD 10,GSBPHD Elective 2,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/414?entityType=Course&reportId=422 +GSBPHD 11,GSBPHD Elective 3,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/419?entityType=Course&reportId=422 +GSBPHD 12,GSBPHD Elective 4,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/413?entityType=Course&reportId=422 +GSBPHD 13,PhD Qualifying Exam,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/409?entityType=Course&reportId=422 +GSBPHD 14,Thesis Research Progress,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/410?entityType=Course&reportId=422 +GSBPHD 15,Thesis Proposal Presentation,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/411?entityType=Course&reportId=422 +GSBPHD 16,Final Thesis Defence,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/412?entityType=Course&reportId=422 +GSBPHD 17,Mathematical Economics,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/648?entityType=Course&reportId=422 +GSBPHD 18,Microeconomic Theory,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/650?entityType=Course&reportId=422 +HST 100,History of Kazakhstan,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2603?entityType=Course&reportId=422 +HST 104,Central Asian History II (1700 - 1991),https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2604?entityType=Course&reportId=422 +HST 110,Introduction to World Religions,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2989?entityType=Course&reportId=422 +HST 121,World History I (to 1500),https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2554?entityType=Course&reportId=422 +HST 123,Introduction to the History of Science and Technology,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2635?entityType=Course&reportId=422 +HST 124,Introduction to the History of Medicine,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2636?entityType=Course&reportId=422 +HST 131,European History I (to 1700),https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2555?entityType=Course&reportId=422 +HST 132,European History II (from 1700),https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2556?entityType=Course&reportId=422 +HST 172,Middle Eastern History II (from 1800),https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2769?entityType=Course&reportId=422 +HST 205,The Mongol Empire,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2600?entityType=Course&reportId=422 +HST 206,The Age of the Dragon Throne: Chinese Civilizations from the Mongols through the Qing,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2601?entityType=Course&reportId=422 +HST 224,"WWI and the World: Causes, Conduct and the Consequences of WWI",https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2770?entityType=Course&reportId=422 +HST 226,Soviet Central Asia in WWII,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2602?entityType=Course&reportId=422 +HST 232,Russian History from Riurik to Catherine the Great,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2605?entityType=Course&reportId=422 +HST 233,The Russian Empire from Catherine the Great to the Bolshevik Revolution,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2606?entityType=Course&reportId=422 +HST 236,"Eurasian Revolutions, 1905-1949",https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2962?entityType=Course&reportId=422 +HST 238,"Clash of Civilizations: Communities, Confessions and Empires in Central Europe, 1000-1890",https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2557?entityType=Course&reportId=422 +HST 239,"Modernizing the Periphery: Nationalism, Communism, and Empire in Central Europe, 1890-Present",https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2558?entityType=Course&reportId=422 +HST 242,"History of the Soviet Union: Politics, Society, Culture",https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2961?entityType=Course&reportId=422 +HST 245,Global History of Travel and Travel Literature,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2559?entityType=Course&reportId=422 +HST 261,History of Islam (570s to 1258),https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2599?entityType=Course&reportId=422 +HST 263,The History of Islam II: Islam in the Modern World,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/3081?entityType=Course&reportId=422 +HST 268,Women and Islam in Central Eurasia,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/3082?entityType=Course&reportId=422 +HST 271,History of the Ottoman Empire,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2771?entityType=Course&reportId=422 +HST 272,Modern Turkey: from the Ottoman Empire to the Turkish Republic,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2772?entityType=Course&reportId=422 +HST 273,History of Sufism in the Middle East and Central Asia,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2597?entityType=Course&reportId=422 +HST 274,Texts and Contexts,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2560?entityType=Course&reportId=422 +HST 320,The Crusades,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2598?entityType=Course&reportId=422 +HST 329,Women in Islamic History,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/3083?entityType=Course&reportId=422 +HST 331,The Eurasian Enlightenment,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2561?entityType=Course&reportId=422 +HST 336,The Totalitarian Phenomenon,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2963?entityType=Course&reportId=422 +HST 341,Eastern Christianity,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2607?entityType=Course&reportId=422 +HST 342,Russian Intellectual History (1762 - 1905),https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2608?entityType=Course&reportId=422 +HST 375,Inner Asian Frontiers of China: A Cultural Perspective,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/3182?entityType=Course&reportId=422 +HST 380,Linguistic and Cultural Diversity in Contemporary China: From Beijing to Xinjiang,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/3183?entityType=Course&reportId=422 +HST 399,Independent Study,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2595?entityType=Course&reportId=422 +HST 424,Global Histories,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2634?entityType=Course&reportId=422 +HST 433,"Empire, Nation and Identity in Nineteenth-Century Eurasia",https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2562?entityType=Course&reportId=422 +HST 434,"Neither Red, Nor White: 1917 Revolution as a National Moment",https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2964?entityType=Course&reportId=422 +HST 435,Stalinist Central Asia,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2596?entityType=Course&reportId=422 +HST 440,Religions in the Soviet and Post-Soviet Eras,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2988?entityType=Course&reportId=422 +HST 441,Utopias in Russian and Soviet Literature,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2409?entityType=Course&reportId=422 +HST 443,Imperial Apocalypse: 1917 and 1991 in Comparative Perspective,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2965?entityType=Course&reportId=422 +HST 451,Conversion to Islam in Medieval and Central Asia,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2594?entityType=Course&reportId=422 +HST 454,Turkestan in the Nineteenth Century,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/3211?entityType=Course&reportId=422 +HST 462,History of Islam Under Russian Rule,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/3084?entityType=Course&reportId=422 +HST 498,Directed Reading,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/3085?entityType=Course&reportId=422 +HST 499,History Capstone: Undergraduate Dissertation,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/3086?entityType=Course&reportId=422 +HST 533,"Empire, Nation and Identity in Nineteenth-Century Eurasia",https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2563?entityType=Course&reportId=422 +HST 534,"Neither Red, Nor White: 1917 Revolution as a National Moment",https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2966?entityType=Course&reportId=422 +HST 535,Stalinist Central Asia,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2593?entityType=Course&reportId=422 +HST 540,Religions in the Soviet and Post-Soviet Eras,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2987?entityType=Course&reportId=422 +HST 541,Utopias in Russian and Soviet Literature,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2410?entityType=Course&reportId=422 +HST 543,Imperial Apocalypse: 1917 and 1991 in Comparative Perspective,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2967?entityType=Course&reportId=422 +HST 551,Conversion to Islam in Medieval and Central Asia,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2592?entityType=Course&reportId=422 +HST 552,"Proseminar in Eurasian History, 552-1917",https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/3217?entityType=Course&reportId=422 +HST 554,Turkestan in the Nineteenth Century,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/3212?entityType=Course&reportId=422 +HST 562,History of Islam Under Russian Rule,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/3087?entityType=Course&reportId=422 +HST 733,"Empire, Nation and Identity in Nineteenth-Century Eurasia",https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2564?entityType=Course&reportId=422 +HST 734,"Neither Red, Nor White: 1917 Revolution as a National Moment",https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2968?entityType=Course&reportId=422 +HST 740,Religions in the Soviet and Post-Soviet Eras,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2986?entityType=Course&reportId=422 +HST 741,Utopias in Russian and Soviet Literature,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2411?entityType=Course&reportId=422 +HST 743,Imperial Apocalypse: 1917 and 1991 in Comparative Perspective,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2960?entityType=Course&reportId=422 +HST 751,Conversion to Islam in Medieval and Central Asia,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2591?entityType=Course&reportId=422 +HST 752,"Proseminar in Eurasian History, 552-1917",https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/3218?entityType=Course&reportId=422 +KAZ 150,Basic Kazakh,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/3273?entityType=Course&reportId=422 +KAZ 201,Academic Kazakh I,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2358?entityType=Course&reportId=422 +KAZ 202,Academic Kazakh II,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/3048?entityType=Course&reportId=422 +KAZ 211,Academic Speaking in Kazakh,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/3113?entityType=Course&reportId=422 +KAZ 212,Academic Writing in Kazakh,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/3047?entityType=Course&reportId=422 +KAZ 226,Kazakh for Medical School,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2893?entityType=Course&reportId=422 +KAZ 311,Professional Academic Communication,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/3271?entityType=Course&reportId=422 +KAZ 313,Kazakh for Business,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2762?entityType=Course&reportId=422 +KAZ 349,Kazakh Mythology,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/3272?entityType=Course&reportId=422 +KAZ 351,Kazakh Short Stories,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/3269?entityType=Course&reportId=422 +KAZ 353,Kazakh Poetry,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/3270?entityType=Course&reportId=422 +KAZ 354,Kazakh Drama,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/3215?entityType=Course&reportId=422 +KAZ 356,Kazakh Music History,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2950?entityType=Course&reportId=422 +KAZ 357,Literature of Alash,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/3268?entityType=Course&reportId=422 +KAZ 359,Professional Kazakh for Medicine,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2894?entityType=Course&reportId=422 +KAZ 362,Creative Writing in Kazakh Language,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/3254?entityType=Course&reportId=422 +KAZ 363,Language and Globalization,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2761?entityType=Course&reportId=422 +KAZ 364,Kazakh for Civil Service,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/3213?entityType=Course&reportId=422 +KAZ 365,Abai’s World,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/3214?entityType=Course&reportId=422 +KAZ 366,Kazakh Language for Engineers,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/3292?entityType=Course&reportId=422 +KAZ 368,Onomastics: History and Function of Names,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/3293?entityType=Course&reportId=422 +KAZ 371,Contemporary Kazakh Literature,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/3255?entityType=Course&reportId=422 +KAZ 372,Language and Ethnicity,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/3274?entityType=Course&reportId=422 +KAZ 373,Kazakh Terminology,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/3114?entityType=Course&reportId=422 +KAZ 374,Kazakh Diplomacy,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/3265?entityType=Course&reportId=422 +KAZ 375,Cultural Studies and Kazakh Culture,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/3266?entityType=Course&reportId=422 +KAZ 376,Language and Culture,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/3115?entityType=Course&reportId=422 +KAZ 377,Intercultural Communication through Film,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/3267?entityType=Course&reportId=422 +KAZ 410,Kazakh Literature of the XV-XIX Centuries,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2369?entityType=Course&reportId=422 +KFL 101,Elementary Kazakh as a Foreign Language I,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2359?entityType=Course&reportId=422 +KFL 102,Elementary Kazakh as a Foreign Language II,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2360?entityType=Course&reportId=422 +KFL 201,Intermediate Kazakh as a Foreign Language I,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2361?entityType=Course&reportId=422 +KFL 202,Intermediate Kazakh as a Foreign Language II,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2362?entityType=Course&reportId=422 +KFL 301,Advanced Kazakh as a Foreign Language I,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2363?entityType=Course&reportId=422 +KFL 302,Advanced Kazakh as a Foreign Language II,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2364?entityType=Course&reportId=422 +KOR 101,Beginning Korean I,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2728?entityType=Course&reportId=422 +KOR 102,Beginning Korean II,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2729?entityType=Course&reportId=422 +KOR 201,Intermediate Korean I,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2730?entityType=Course&reportId=422 +KOR 202,Intermediate Korean II,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2731?entityType=Course&reportId=422 +KOR 301,Advanced Korean I,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2732?entityType=Course&reportId=422 +LING 130,Introduction to Language and Communication,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/3237?entityType=Course&reportId=422 +LING 131,Introduction to Linguistics,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/3006?entityType=Course&reportId=422 +LING 140,Language Variation and Change: the Story of English,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/3007?entityType=Course&reportId=422 +LING 240,Introduction to Sociolinguistics,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2694?entityType=Course&reportId=422 +LING 270,Languages of Eurasia,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2423?entityType=Course&reportId=422 +LING 271,Introduction to Cognitive Linguistics,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/3232?entityType=Course&reportId=422 +LING 272,Language and the Mind,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/3233?entityType=Course&reportId=422 +LING 273,Survey of Research Methods in Linguistics,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2424?entityType=Course&reportId=422 +LING 277,Language Diversity and Language Universals,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/3005?entityType=Course&reportId=422 +LING 278,Sounds of the World’s Languages,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2469?entityType=Course&reportId=422 +LING 280,Introduction to Turkic Historical and Comparative Linguistics,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/3216?entityType=Course&reportId=422 +LING 372,Discourse Analysis,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2695?entityType=Course&reportId=422 +LING 374,Language Contact in Central Asia,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2471?entityType=Course&reportId=422 +LING 375,The Art and Science of Analyzing Languages: Morphosyntax of the World's Languages,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2553?entityType=Course&reportId=422 +LING 376,Psycholinguistics,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/3235?entityType=Course&reportId=422 +LING 377,Historical Linguistics,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2470?entityType=Course&reportId=422 +LING 389,Discourse and Grammar,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2752?entityType=Course&reportId=422 +LING 431,Statistics and Computational Methods in Linguistics,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2753?entityType=Course&reportId=422 +LING 461,Experimental Semantics,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/3234?entityType=Course&reportId=422 +LING 470,Multilingualism and Language Contact,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2472?entityType=Course&reportId=422 +LING 472,Language Ideologies,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2690?entityType=Course&reportId=422 +LING 473,Advanced Empirical Methods in Linguistics,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2751?entityType=Course&reportId=422 +LING 482,Language and Worldview,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2693?entityType=Course&reportId=422 +LING 491,Advanced Independent Study,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/3236?entityType=Course&reportId=422 +LING 531,Statistics and Computational Methods in Linguistics,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2748?entityType=Course&reportId=422 +LING 570,Multilingualism and Language Contact,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2473?entityType=Course&reportId=422 +LING 572,Language Ideologies,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2692?entityType=Course&reportId=422 +LING 573,Advanced Empirical Methods in Linguistics,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2747?entityType=Course&reportId=422 +LING 574,Language Contact in Central Asia,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2474?entityType=Course&reportId=422 +LING 589,Discourse and Grammar,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2749?entityType=Course&reportId=422 +LING 731,Statistics and Computational Methods in Linguistics,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2750?entityType=Course&reportId=422 +LING 772,Language Ideologies,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2691?entityType=Course&reportId=422 +LING 773,Advanced Empirical Methods in Linguistics,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2552?entityType=Course&reportId=422 +LING 774,Language Contact in Central Asia,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2468?entityType=Course&reportId=422 +LING 782,Language and Worldview,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2689?entityType=Course&reportId=422 +LING 789,Discourse and Grammar,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2746?entityType=Course&reportId=422 +MAC 960,Acting Internship in Obstetrics and Gynecology,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/485?entityType=Course&reportId=422 +MAC 961,Acting Internship in General Pediatrics,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/483?entityType=Course&reportId=422 +MAC 962,Acting Internship in General Surgery,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/486?entityType=Course&reportId=422 +MAC 963,Acting Internship in Internal Medicine,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/484?entityType=Course&reportId=422 +MAE 201,Computer Aided Design,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/1117?entityType=Course&reportId=422 +MAE 205,Materials and Manufacturing I,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/1038?entityType=Course&reportId=422 +MAE 206,Engineering Dynamics I,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/1146?entityType=Course&reportId=422 +MAE 300,Fluid Mechanics I,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/1236?entityType=Course&reportId=422 +MAE 301,Engineering Thermodynamics,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/1021?entityType=Course&reportId=422 +MAE 302,Machine Elements Design,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/1250?entityType=Course&reportId=422 +MAE 303,Control Systems,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/1145?entityType=Course&reportId=422 +MAE 305,Fluid Mechanics II,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/1237?entityType=Course&reportId=422 +MAE 306,Computer Aided Engineering,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/3448?entityType=Course&reportId=422 +MAE 307,Engineering Dynamics II,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/1249?entityType=Course&reportId=422 +MAE 350,Structural Mechanics II,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/1036?entityType=Course&reportId=422 +MAE 351,Vehicle Propulsion Systems,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/1020?entityType=Course&reportId=422 +MAE 400,Heat Transfer,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/1308?entityType=Course&reportId=422 +MAE 401,Mechanical Systems Design,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/1037?entityType=Course&reportId=422 +MAE 450,Additive Manufacturing,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/1035?entityType=Course&reportId=422 +MAE 451,Unmanned Aerial Vehicle Design,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/958?entityType=Course&reportId=422 +MAE 452,Computer Aided Geometric Design,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/1116?entityType=Course&reportId=422 +MAE 453,Fire Engineering,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/1320?entityType=Course&reportId=422 +MAE 454,Aerodynamics,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/960?entityType=Course&reportId=422 +MAE 455,Flight Performance and Mechanics,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/959?entityType=Course&reportId=422 +MAE 456,Materials and Manufacturing II,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/948?entityType=Course&reportId=422 +MAE 457,Feasibility Analysis of Clean Energy Technologies,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/1309?entityType=Course&reportId=422 +MAE 460,Advanced Topics in Computational Fluid Dynamics,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/1319?entityType=Course&reportId=422 +MAE 462,Multiphase Systems,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/1238?entityType=Course&reportId=422 +MAE 463,Micro-Electro-Mechanical Systems (MEMS) and Microsystems Technology,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/1067?entityType=Course&reportId=422 +MAE 464,Mechanics of Soft Materials,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/1068?entityType=Course&reportId=422 +MAE 465,Introduction to Maintenance Engineering,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/918?entityType=Course&reportId=422 +MAE 466,Application of Optimization in Mechanical and Aerospace Engineering,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/919?entityType=Course&reportId=422 +MAE 500,Unmanned Aerial Vehicle Design,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/957?entityType=Course&reportId=422 +MANS 811,Anesthesiology Clerkship,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/487?entityType=Course&reportId=422 +MATH 109,Mathematical Discovery,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2535?entityType=Course&reportId=422 +MATH 161,Calculus I,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2536?entityType=Course&reportId=422 +MATH 162,Calculus II,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/3282?entityType=Course&reportId=422 +MATH 251,Discrete Mathematics,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2910?entityType=Course&reportId=422 +MATH 263,Calculus III,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2421?entityType=Course&reportId=422 +MATH 273,Linear Algebra with Applications,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2648?entityType=Course&reportId=422 +MATH 274,Introduction to Differential Equations,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/3165?entityType=Course&reportId=422 +MATH 301,Introduction to Number Theory,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2661?entityType=Course&reportId=422 +MATH 302,Abstract Algebra I,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/3295?entityType=Course&reportId=422 +MATH 310,Applied Statistical Methods,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2680?entityType=Course&reportId=422 +MATH 321,Probability,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2365?entityType=Course&reportId=422 +MATH 322,Mathematical Statistics,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2679?entityType=Course&reportId=422 +MATH 323,Actuarial Mathematics I,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2649?entityType=Course&reportId=422 +MATH 350,Research Methods,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2658?entityType=Course&reportId=422 +MATH 351,Numerical Methods with Applications,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2659?entityType=Course&reportId=422 +MATH 361,Real Analysis I,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2660?entityType=Course&reportId=422 +MATH 371,Introduction to Mathematical Biology,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2452?entityType=Course&reportId=422 +MATH 403,Abstract Algebra II,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/3297?entityType=Course&reportId=422 +MATH 407,Graph Theory,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2907?entityType=Course&reportId=422 +MATH 411,Linear Programming,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2351?entityType=Course&reportId=422 +MATH 412,Nonlinear Optimization,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2886?entityType=Course&reportId=422 +MATH 417,Cryptography,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2908?entityType=Course&reportId=422 +MATH 423,Actuarial Mathematics II,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2650?entityType=Course&reportId=422 +MATH 424,Mathematical Finance,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2651?entityType=Course&reportId=422 +MATH 425,Stochastic Processes,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2888?entityType=Course&reportId=422 +MATH 440,Regression Analysis,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/3030?entityType=Course&reportId=422 +MATH 441,Design of Experiments,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/3098?entityType=Course&reportId=422 +MATH 446,Time Series Analysis,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2887?entityType=Course&reportId=422 +MATH 449,Statistical Programming,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/3099?entityType=Course&reportId=422 +MATH 455,Stochastic Calculus,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2347?entityType=Course&reportId=422 +MATH 456,Introduction to Lie Groups and Representations,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/3296?entityType=Course&reportId=422 +MATH 460,Topology,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2686?entityType=Course&reportId=422 +MATH 461,Real Analysis II,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2687?entityType=Course&reportId=422 +MATH 462,Advanced Linear Algebra,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2909?entityType=Course&reportId=422 +MATH 471,Nonlinear Differential Equations,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2453?entityType=Course&reportId=422 +MATH 476,Numerical Methods for Partial Differential Equations,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2379?entityType=Course&reportId=422 +MATH 477,Applied Finite Element Methods,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/3252?entityType=Course&reportId=422 +MATH 480,Complex Analysis,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2380?entityType=Course&reportId=422 +MATH 481,Partial Differential Equations,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2348?entityType=Course&reportId=422 +MATH 482,Fourier Analysis,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/3033?entityType=Course&reportId=422 +MATH 490,Special Topics in Mathematics I,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2381?entityType=Course&reportId=422 +MATH 491,Special Topics in Mathematics II,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2382?entityType=Course&reportId=422 +MATH 497,Directed Study in Mathematics I,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/3034?entityType=Course&reportId=422 +MATH 498,Directed Study in Mathematics II,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/3035?entityType=Course&reportId=422 +MATH 499,Capstone Project,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/3166?entityType=Course&reportId=422 +MATH 510,Measure Theory and Applications,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2420?entityType=Course&reportId=422 +MATH 512,Optimization Methods and Techniques,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2889?entityType=Course&reportId=422 +MATH 514,Operations Research,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2890?entityType=Course&reportId=422 +MATH 515,Theory of Probability,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2891?entityType=Course&reportId=422 +MATH 517,Scientific Modeling and Simulation with Mathematics,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2653?entityType=Course&reportId=422 +MATH 518,Applied Finite Element Analysis,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/3031?entityType=Course&reportId=422 +MATH 519,Scientific Computing,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/3032?entityType=Course&reportId=422 +MATH 540,Statistical Learning,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2350?entityType=Course&reportId=422 +MATH 541,Data Analysis and Statistical Learning,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/3096?entityType=Course&reportId=422 +MATH 542,Statistical Programming,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/3097?entityType=Course&reportId=422 +MATH 551,Advanced Numerical Methods,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/3253?entityType=Course&reportId=422 +MATH 555,Stochastic Analysis,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2688?entityType=Course&reportId=422 +MATH 571,Advanced Nonlinear Differential Equations,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2455?entityType=Course&reportId=422 +MATH 576,Numerical Methods for Partial Differential Equations,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2652?entityType=Course&reportId=422 +MATH 577,Modeling and Numerical Analysis for Incompressible Fluids,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/3038?entityType=Course&reportId=422 +MATH 580,Advanced Complex Analysis,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/3039?entityType=Course&reportId=422 +MATH 582,Mathematical Biology,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2454?entityType=Course&reportId=422 +MATH 592,Research Seminar,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2377?entityType=Course&reportId=422 +MATH 599,Thesis Research Preparation,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2378?entityType=Course&reportId=422 +MATH 601,Analytic Number Theory,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2657?entityType=Course&reportId=422 +MATH 620,Asymptotic Analysis,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2375?entityType=Course&reportId=422 +MATH 676,Advanced Partial Differential Equations with Applications,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2376?entityType=Course&reportId=422 +MATH 677,Introduction to Nonlinear Dispersive and Wave Equations,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2349?entityType=Course&reportId=422 +MATH 680,Potential Theory and Polynomial Approximation,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/3036?entityType=Course&reportId=422 +MATH 682,Applied Functional Analysis,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/3037?entityType=Course&reportId=422 +MATH 691,Thesis Proposal,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2383?entityType=Course&reportId=422 +MATH 692,Thesis,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2384?entityType=Course&reportId=422 +MATH 700,Doctoral Thesis,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2664?entityType=Course&reportId=422 +MATH 700,0 Thesis Research,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2665?entityType=Course&reportId=422 +MATH 701,Real Analysis,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2385?entityType=Course&reportId=422 +MATH 702,Functional Analysis with Applications,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2663?entityType=Course&reportId=422 +MATH 711,Abstract Algebra,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/3294?entityType=Course&reportId=422 +MATH 714,Advanced Operations Research,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2885?entityType=Course&reportId=422 +MATH 721,Nonlinear Differential Equations with Applications,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2451?entityType=Course&reportId=422 +MATH 722,Advanced Partial Differential Equations,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2419?entityType=Course&reportId=422 +MATH 740,Advanced Statistical Learning,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/3100?entityType=Course&reportId=422 +MATH 742,Advanced Statistical Programming,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/3101?entityType=Course&reportId=422 +MATH 751,Probability Theory,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2882?entityType=Course&reportId=422 +MATH 761,Mathematical Finance,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2883?entityType=Course&reportId=422 +MATH 771,Operations Research,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2884?entityType=Course&reportId=422 +MATH 776,Advanced Numerical Methods for Partial Differential Equations,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2647?entityType=Course&reportId=422 +MATH 780,Research Methods and Ethics,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2662?entityType=Course&reportId=422 +MATH 791,Topics in Mathematics,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2911?entityType=Course&reportId=422 +MBA 01,Introduction to General Management Principles,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/420?entityType=Course&reportId=422 +MBA 02,Foundations of Financial and Managerial Accounting,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/426?entityType=Course&reportId=422 +MBA 03,Leadership & Communication,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/421?entityType=Course&reportId=422 +MBA 04,Data Anatytics for Managers,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/423?entityType=Course&reportId=422 +MBA 05,Principles of Finance,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/427?entityType=Course&reportId=422 +MBA 06,Economics for Managers,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/422?entityType=Course&reportId=422 +MBA 07,Management and Organizations,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/424?entityType=Course&reportId=422 +MBA 08,Marketing,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/429?entityType=Course&reportId=422 +MBA 09,Entrepreneurship,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/428?entityType=Course&reportId=422 +MBA 10,Foundations of Strategy,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/425?entityType=Course&reportId=422 +MBA 11,Operations Management,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/430?entityType=Course&reportId=422 +MBA 12,"Environmental, Social and Governance Factors (ESG) for Business",https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/432?entityType=Course&reportId=422 +MBA 13,Practicum Project,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/431?entityType=Course&reportId=422 +MBAE 01,Corporate Finance,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/433?entityType=Course&reportId=422 +MBAE 02,Negotiations,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/434?entityType=Course&reportId=422 +MBAE 03,Investments,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/435?entityType=Course&reportId=422 +MBAE 04,Supply Chain Management,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/436?entityType=Course&reportId=422 +MBAE 05,International Finance,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/437?entityType=Course&reportId=422 +MBAE 06,Strategic Marketing,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/438?entityType=Course&reportId=422 +MBME 600,Research Seminar,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/1009?entityType=Course&reportId=422 +MBME 601,Master Thesis I,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/987?entityType=Course&reportId=422 +MBME 602,Master Thesis II,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/988?entityType=Course&reportId=422 +MBME 603,Biomaterials Science and Engineering,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/989?entityType=Course&reportId=422 +MBME 604,Biosensors,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/1010?entityType=Course&reportId=422 +MBME 607,Advanced Tissue Engineering,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/991?entityType=Course&reportId=422 +MBME 610,Anatomy and Physiology for Biomedical Engineers,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/887?entityType=Course&reportId=422 +MBME 700,Strategies for Controlled Topical Delivery of Drugs,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/888?entityType=Course&reportId=422 +MBME 708,Biomedical Imaging,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/1007?entityType=Course&reportId=422 +MBME 709,Mechanics of Living Tissues,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/990?entityType=Course&reportId=422 +MBME 710,Biophotonics,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/1006?entityType=Course&reportId=422 +MBSB 611,Medical Anatomy,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/490?entityType=Course&reportId=422 +MBSB 612,Human Genetics,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/494?entityType=Course&reportId=422 +MBSB 613,Cellular Pathologic Basis of Disease,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/493?entityType=Course&reportId=422 +MBSB 614,Fuel Metabolism,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/489?entityType=Course&reportId=422 +MBSB 615,Immunology in Health and Disease,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/488?entityType=Course&reportId=422 +MBSB 616,Medical Microbiology,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/491?entityType=Course&reportId=422 +MBSB 617,Pharmacology,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/492?entityType=Course&reportId=422 +MCEE 600,Research Seminar,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/1093?entityType=Course&reportId=422 +MCEE 601,Master Thesis I,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/1094?entityType=Course&reportId=422 +MCEE 602,Master Thesis II,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/1095?entityType=Course&reportId=422 +MCEE 603,Finite Element Methods,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/1033?entityType=Course&reportId=422 +MCEE 604,Advanced Soil Mechanics,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/1282?entityType=Course&reportId=422 +MCEE 605,Advanced Environmental Chemistry,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/1292?entityType=Course&reportId=422 +MCEE 606,Advanced Project Management,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/865?entityType=Course&reportId=422 +MCEE 700,Structural Dynamics and Earthquake Engineering,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/3436?entityType=Course&reportId=422 +MCEE 701,Advanced Concrete Technology,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/1001?entityType=Course&reportId=422 +MCEE 702,Structural Evaluation and Rehabilitation,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/1239?entityType=Course&reportId=422 +MCEE 703,Theory of Physio-chemical Treatment Processes,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/1293?entityType=Course&reportId=422 +MCEE 704,Contaminated Site Management and Soil Treatment Technologies,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/1174?entityType=Course&reportId=422 +MCEE 705,Environmental Modeling Development,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/1056?entityType=Course&reportId=422 +MCEE 706,Concrete Repair and Maintenance,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/1240?entityType=Course&reportId=422 +MCEE 707,Geotechnical Earthquake Engineering,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/1280?entityType=Course&reportId=422 +MCEE 708,Advanced Foundation Engineering,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/896?entityType=Course&reportId=422 +MCEE 709,Building Information Modeling in Construction,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/866?entityType=Course&reportId=422 +MCEE 710,"Green Building - Concept, Design, Construction, and Operation",https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/1055?entityType=Course&reportId=422 +MCEE 711,Soil Improvement and Stabilization,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/1281?entityType=Course&reportId=422 +MCEE 712,"Advanced Pavement Design, Analysis, and Rehabilitation",https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/1002?entityType=Course&reportId=422 +MCEE 713,Advanced Concrete Design,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/1034?entityType=Course&reportId=422 +MCEE 714,Advanced Wastewater Treatment,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/1294?entityType=Course&reportId=422 +MCEE 715,Sustainable Construction and Development,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/1054?entityType=Course&reportId=422 +MCEE 716,Unsaturated Soil Mechanics for Engineering Practice,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/897?entityType=Course&reportId=422 +MCEE 717,Computational Modeling and Instrumentation in Geotechnical Engineering,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/898?entityType=Course&reportId=422 +MCHME 600,Research Seminar,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/1003?entityType=Course&reportId=422 +MCHME 601,Master Thesis I,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/1004?entityType=Course&reportId=422 +MCHME 602,MSc Thesis II,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/1005?entityType=Course&reportId=422 +MCHME 603,Advanced Materials Processing,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/909?entityType=Course&reportId=422 +MCHME 604,Advanced Materials Characterization Methods,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/1323?entityType=Course&reportId=422 +MCHME 605,Advanced Chemical Reaction Engineering,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/1268?entityType=Course&reportId=422 +MCHME 606,Advanced Heat and Mass Transfer,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/1235?entityType=Course&reportId=422 +MCHME 701,Advanced Chemical Thermodynamics,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/1210?entityType=Course&reportId=422 +MCHME 707,Polymer Melt Fluid Mechanics and Processing,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/1302?entityType=Course&reportId=422 +MCHME 713,Advanced Energy Materials and Their Application,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/910?entityType=Course&reportId=422 +MCHME 714,Computational Materials Engineering,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/1303?entityType=Course&reportId=422 +MCPC 933,Cardiac Pathologies Clerkship,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/495?entityType=Course&reportId=422 +MECE 600,Research Seminar,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/1013?entityType=Course&reportId=422 +MECE 601,MSc Thesis I,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/1157?entityType=Course&reportId=422 +MECE 602,MSc Thesis II,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/1158?entityType=Course&reportId=422 +MECE 603,Advanced Data Structures and Algorithms,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/876?entityType=Course&reportId=422 +MECE 605,Probability and Statistics for Electrical and Computer Engineers,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/962?entityType=Course&reportId=422 +MECE 606,Embedded Systems and Applications,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/895?entityType=Course&reportId=422 +MECE 608,Energy Systems Operations and Planning,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/1159?entityType=Course&reportId=422 +MECE 700,Industrial and Commercial Power Systems,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/1160?entityType=Course&reportId=422 +MECE 704,Fundamentals of Signal Processing,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/1197?entityType=Course&reportId=422 +MECE 707,Advanced Electronic Circuits,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/1195?entityType=Course&reportId=422 +MECE 716,Adaptive Signal Processing,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/1196?entityType=Course&reportId=422 +MECE 717,Pattern Recognition and Machine Learning,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/920?entityType=Course&reportId=422 +MECE 724,System Modelling and Control,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/1219?entityType=Course&reportId=422 +MECE 726,Modern Semiconductor Devices and Systems,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/935?entityType=Course&reportId=422 +MECE 727,Biosensors,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/1012?entityType=Course&reportId=422 +MECE 728,Renewable and Sustainable Energy,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/1156?entityType=Course&reportId=422 +MECE 729,Convex Optimization,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/877?entityType=Course&reportId=422 +MECE 730,Microfluidic Devices,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/1070?entityType=Course&reportId=422 +MECE 731,RF and Microwave Circuits,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/1194?entityType=Course&reportId=422 +MECE 732,Optical Communications,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/986?entityType=Course&reportId=422 +MECE 733,Semiconductor Process Engineering for VLSI,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/936?entityType=Course&reportId=422 +MEL 967,Gastroenterology and Hepatology,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/496?entityType=Course&reportId=422 +MFAM 860,Family Medicine Clerkship,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/497?entityType=Course&reportId=422 +MICE 932,Intensive Care and Emergency Medicine Clerkship,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/498?entityType=Course&reportId=422 +MILS 401,Integrated Life Sciences,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/499?entityType=Course&reportId=422 +MINE 201,Mineral Processing,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/270?entityType=Course&reportId=422 +MINE 301,Mine Surveying and GIS,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/272?entityType=Course&reportId=422 +MINE 302,Fundamentals of Geomechanics,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/273?entityType=Course&reportId=422 +MINE 303,Mine Services and Materials Handling,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/264?entityType=Course&reportId=422 +MINE 304,Resource Estimation,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/286?entityType=Course&reportId=422 +MINE 305,Rock Breakage,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/291?entityType=Course&reportId=422 +MINE 306,Underground Mining Systems and Design,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/274?entityType=Course&reportId=422 +MINE 307,Surface Mining System and Design,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/259?entityType=Course&reportId=422 +MINE 401,Mining Geotechnical Engineering,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/260?entityType=Course&reportId=422 +MINE 402,Mine Planning,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/287?entityType=Course&reportId=422 +MINE 403,Coal Mining,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/275?entityType=Course&reportId=422 +MINE 404,"Health, Safety and Sustainability in Mining",https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/296?entityType=Course&reportId=422 +MINE 405,Mine Ventilation,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/297?entityType=Course&reportId=422 +MINE 407,Mining and Environment,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/271?entityType=Course&reportId=422 +MINE 408,Mine Risk Management,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/298?entityType=Course&reportId=422 +MINE 489,Mine Design Project I,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/265?entityType=Course&reportId=422 +MINE 490,Mine Design Project II,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/266?entityType=Course&reportId=422 +MINE 501,Foundations of Mining,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/261?entityType=Course&reportId=422 +MINE 502,Geology for Mining Engineers,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/292?entityType=Course&reportId=422 +MINE 503,Applied Geostatistics,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/288?entityType=Course&reportId=422 +MINE 504,Mine Geomechanics,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/267?entityType=Course&reportId=422 +MINE 505,Advanced Mine Ventilation,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/299?entityType=Course&reportId=422 +MINE 507,Rock Excavation Methods,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/293?entityType=Course&reportId=422 +MINE 508,Advanced Rock Mechanics,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/268?entityType=Course&reportId=422 +MINE 509,Advanced Surface Mine Design,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/262?entityType=Course&reportId=422 +MINE 510,Advanced Underground Mine Design,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/276?entityType=Course&reportId=422 +MINE 601,Mine Management and Sustainability,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/301?entityType=Course&reportId=422 +MINE 602,Mine Automation and Maintenance,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/300?entityType=Course&reportId=422 +MINE 603,Multivariate Geostatistics,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/289?entityType=Course&reportId=422 +MINE 604,Geology of Ore Deposits,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/285?entityType=Course&reportId=422 +MINE 605,Mechanized Excavation and Tunnelling,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/294?entityType=Course&reportId=422 +MINE 606,Underground Hard Rock Geotechnical Engineering,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/277?entityType=Course&reportId=422 +MINE 690,Thesis I,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/278?entityType=Course&reportId=422 +MINE 691,Thesis II,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/279?entityType=Course&reportId=422 +MINE 701,Advanced Mine Geomechanics,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/269?entityType=Course&reportId=422 +MINE 702,Current Research Literature,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/302?entityType=Course&reportId=422 +MINE 703,Advanced Mining Systems,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/280?entityType=Course&reportId=422 +MINE 801,Advanced Mechanized Excavation and Tunnelling,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/295?entityType=Course&reportId=422 +MINE 803,Advanced Geometallurgy,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/290?entityType=Course&reportId=422 +MINE 804,Unconventional Mining Systems,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/281?entityType=Course&reportId=422 +MINE 805,Mine Robotics and Automation,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/282?entityType=Course&reportId=422 +MINE 806,Advanced Numerical Methods,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/263?entityType=Course&reportId=422 +MINE 890,Doctoral Thesis,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/283?entityType=Course&reportId=422 +MINE 890,0 Thesis Research,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/284?entityType=Course&reportId=422 +MINF 821,Infectious Diseases Clerkship,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/500?entityType=Course&reportId=422 +MMAE 600,Research Seminar,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/1305?entityType=Course&reportId=422 +MMAE 601,Master Thesis I,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/1306?entityType=Course&reportId=422 +MMAE 602,Master Thesis II,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/916?entityType=Course&reportId=422 +MMAE 603,Modern Control Engineering,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/1144?entityType=Course&reportId=422 +MMAE 604,Advanced Computational Fluid Dynamics (CFD) and Heat Transfer in Mechanical Engineering,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/1318?entityType=Course&reportId=422 +MMAE 605,Advanced Manufacturing Processes and Strategies,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/947?entityType=Course&reportId=422 +MMAE 606,Research Methods,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/1307?entityType=Course&reportId=422 +MMAE 700,Advanced Statistics and Probability,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/1310?entityType=Course&reportId=422 +MMAE 701,Numerical Techniques for Engineers,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/1046?entityType=Course&reportId=422 +MMAE 702,Sustainable Vehicle Transportation Systems,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/1018?entityType=Course&reportId=422 +MMAE 705,Design Optimization,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/1119?entityType=Course&reportId=422 +MMAE 706,Computational and Unsteady Aerodynamics,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/955?entityType=Course&reportId=422 +MMAE 710,Space Structures Design,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/956?entityType=Course&reportId=422 +MMAE 713,"Computational Fluid-Structure Interaction: Methods, Models and Applications",https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/1317?entityType=Course&reportId=422 +MMAE 714,Space Flight Dynamics,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/1045?entityType=Course&reportId=422 +MMAE 716,Digital Transformation Concepts,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/1052?entityType=Course&reportId=422 +MMAE 717,Advanced Materials and Composites,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/1248?entityType=Course&reportId=422 +MMAE 718,Advanced Systems Engineering,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/1053?entityType=Course&reportId=422 +MMAE 719,Rocket Propulsion Fundamentals,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/1019?entityType=Course&reportId=422 +MMED 820,Medicine Inpatient I Clerkship,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/501?entityType=Course&reportId=422 +MMMM 501,Molecular Basis of Infection,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/513?entityType=Course&reportId=422 +MMMM 502,Molecular Basis of Cancer,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/502?entityType=Course&reportId=422 +MMMM 503,Bioethics in Research: Principles and Practice,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/504?entityType=Course&reportId=422 +MMMM 504,Molecular Basis of Genetic Disease,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/509?entityType=Course&reportId=422 +MMMM 507,Critical Analysis of the Contemporary Concepts in Molecular Medicine,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/514?entityType=Course&reportId=422 +MMMM 508,Molecular Immunology,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/505?entityType=Course&reportId=422 +MMMM 509,From the Bench to the Bedside: Drug Development and Clinical Trials,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/506?entityType=Course&reportId=422 +MMMM 511,Research Methods in Molecular Biomedicine,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/510?entityType=Course&reportId=422 +MMMM 513,Molecular Basis of Neurological Disorders,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/512?entityType=Course&reportId=422 +MMMM 603,Recombinant Drugs,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/507?entityType=Course&reportId=422 +MMMM 605,Developmental Biology,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/511?entityType=Course&reportId=422 +MMMM 610,Natural Drugs,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/503?entityType=Course&reportId=422 +MMMM 690,Master Research Project,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/508?entityType=Course&reportId=422 +MOBG 830,Obstetrics and Gynecology Clerkship,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/515?entityType=Course&reportId=422 +MOSP 641,Neuroscience,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/518?entityType=Course&reportId=422 +MOSP 642,Introduction to Psychiatry,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/526?entityType=Course&reportId=422 +MOSP 731,Endocrine System,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/520?entityType=Course&reportId=422 +MOSP 732,Digestion and Nutrition,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/521?entityType=Course&reportId=422 +MOSP 733,Hematology and Oncology,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/522?entityType=Course&reportId=422 +MOSP 734,Skin and Musculoskeletal System,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/524?entityType=Course&reportId=422 +MOSP 735,Reproductive and Developmental Biology,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/523?entityType=Course&reportId=422 +MOSP 736,Clinical Problem Solving,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/517?entityType=Course&reportId=422 +MOSP 743,Cardiovascular System,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/516?entityType=Course&reportId=422 +MOSP 744,Pulmonary System,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/519?entityType=Course&reportId=422 +MOSP 745,Renal System,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/525?entityType=Course&reportId=422 +MPA 602,Public Administration in Theory and Practice,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/77?entityType=Course&reportId=422 +MPA 603,Economic Applications for Public Managers,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/81?entityType=Course&reportId=422 +MPA 605,Research Methods for Public Managers,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/78?entityType=Course&reportId=422 +MPA 607,Fiscal Management and Budgeting,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/3298?entityType=Course&reportId=422 +MPA 609,Human Resource Policy and Management,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/76?entityType=Course&reportId=422 +MPA 610,Program Evaluation,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/3411?entityType=Course&reportId=422 +MPA 699,Master’s Project with Overseas Component,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/79?entityType=Course&reportId=422 +MPA-O 602I,Public Administration in Theory and Practice,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/738?entityType=Course&reportId=422 +MPA-O 602II, Public Administration in Theory and Practice,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/739?entityType=Course&reportId=422 +MPA-O 603I,Economic Applications for Public Managers,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/848?entityType=Course&reportId=422 +MPA-O 603II,Economic Applications for Public Managers,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/849?entityType=Course&reportId=422 +MPA-O 607I,Fiscal Management and Budgeting,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/740?entityType=Course&reportId=422 +MPA-O 607II,Fiscal Management and Budgeting,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/741?entityType=Course&reportId=422 +MPA-O 609I,Human Resource Policy and Management,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/781?entityType=Course&reportId=422 +MPA-O 609II,Human Resource Policy and Management,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/782?entityType=Course&reportId=422 +MPA-O 610I,Program Evaluation,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/784?entityType=Course&reportId=422 +MPA-O 610II,Program Evaluation,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/785?entityType=Course&reportId=422 +MPCB 601,Introduction to Being a Physician,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/527?entityType=Course&reportId=422 +MPCB 602,Basics of Medical Interviewing,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/528?entityType=Course&reportId=422 +MPCB 603,Basics of Physical Examination,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/531?entityType=Course&reportId=422 +MPCB 605,Clinical Experiences I,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/532?entityType=Course&reportId=422 +MPCB 701,Advanced Physical Examination II,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/530?entityType=Course&reportId=422 +MPCB 703,Advanced Medical Interviewing,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/529?entityType=Course&reportId=422 +MPCB 704,Clinical Procedures,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/533?entityType=Course&reportId=422 +MPCL 800,Pre-clerkship,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/534?entityType=Course&reportId=422 +MPE 600,Internship in Public Policy,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/736?entityType=Course&reportId=422 +MPE 608,Strategic Management and Leadership,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2339?entityType=Course&reportId=422 +MPE 609,Organizational Behavior in the Public Sector,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/473?entityType=Course&reportId=422 +MPE 610,National Identity and Public Policy,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/96?entityType=Course&reportId=422 +MPE 611,Benefit Cost Analysis,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/105?entityType=Course&reportId=422 +MPE 612,Policy Tools for Addressing Inequality and Demographic Change,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2342?entityType=Course&reportId=422 +MPE 615,Scientific Reasoning for Policy Research,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/1378?entityType=Course&reportId=422 +MPE 616,Collaborative Governance,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/691?entityType=Course&reportId=422 +MPE 619,Quantitative Analysis for Managers,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/676?entityType=Course&reportId=422 +MPE 620,Circular Economy Strategy,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/1381?entityType=Course&reportId=422 +MPE 622,Case Study Research Method,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/678?entityType=Course&reportId=422 +MPE 628,Good Governance and Corruption,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/94?entityType=Course&reportId=422 +MPE 632,Artificial Intelligence and Governance,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/1372?entityType=Course&reportId=422 +MPE 633,Big Data Applications in Public Policy,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/3457?entityType=Course&reportId=422 +MPE 635,Innovation Management,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/3458?entityType=Course&reportId=422 +MPE 636,Social Entrepreneurship,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/3459?entityType=Course&reportId=422 +MPE 637,Policy Lab,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/3433?entityType=Course&reportId=422 +MPE 638,Contemporary Security Challenges in Central Asia and Beyond,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/98?entityType=Course&reportId=422 +MPE 641,Globalisation and Public Policy,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/107?entityType=Course&reportId=422 +MPE 646,Communications for Public Leadership,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/99?entityType=Course&reportId=422 +MPE 649,Public Sector Innovation,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/471?entityType=Course&reportId=422 +MPE 651,Agricultural Policy Analysis,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/3304?entityType=Course&reportId=422 +MPE 660,Health System and Policy,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/3305?entityType=Course&reportId=422 +MPE 670,Behavioural Insights and Public Policy,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/103?entityType=Course&reportId=422 +MPE 671,Game Theory and Public Policy,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/104?entityType=Course&reportId=422 +MPE 672,"The Third Sector: civil society, nonprofit organizations, and voluntary action",https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/97?entityType=Course&reportId=422 +MPE 673,Development Assistance and Governance,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/95?entityType=Course&reportId=422 +MPE 681,Natural Resources Policy and Management,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/697?entityType=Course&reportId=422 +MPE 682,Sustainable Development and Environmental Governance,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/106?entityType=Course&reportId=422 +MPE 683,Energy Systems and Climate Policy,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/695?entityType=Course&reportId=422 +MPE 684,Global Energy Policy,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/696?entityType=Course&reportId=422 +MPE 685,Water Resources Policy and Management,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/699?entityType=Course&reportId=422 +MPE 688,New Technologies and Public Policy,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/3399?entityType=Course&reportId=422 +MPED 832,Pediatric Clerkship,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/535?entityType=Course&reportId=422 +MPE-O 611I,Benefit Cost Analysis,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/787?entityType=Course&reportId=422 +MPE-O 611II,Benefit Cost Analysis,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/789?entityType=Course&reportId=422 +MPP 601,Microeconomics and Public Policy,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/89?entityType=Course&reportId=422 +MPP 602,Macroeconomics and Public Policy,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/88?entityType=Course&reportId=422 +MPP 603,Data Analytics in Public Policy,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/3407?entityType=Course&reportId=422 +MPP 611,Statistics for Public Policy,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/91?entityType=Course&reportId=422 +MPP 613,Policy Research Design and Methods,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2344?entityType=Course&reportId=422 +MPP 621,Public Policy and Analysis,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2345?entityType=Course&reportId=422 +MPP 631,Politics and Public Policy,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/84?entityType=Course&reportId=422 +MPP 641,Public Management and Leadership,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/83?entityType=Course&reportId=422 +MPP 699,Policy Analysis Exercise with Overseas Component,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/86?entityType=Course&reportId=422 +MPP 699,0 Policy Analysis Exercise with Overseas Component,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/87?entityType=Course&reportId=422 +MPPS 631,Medical Ethics and Professionalism,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/536?entityType=Course&reportId=422 +MPPS 632,Behavioral Medicine,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/538?entityType=Course&reportId=422 +MPPS 633,English-Russian-Kazakh Medical Terminology,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/539?entityType=Course&reportId=422 +MPPS 733,Health System and Administration,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/537?entityType=Course&reportId=422 +MPSY 850,Psychiatry Clerkship,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/540?entityType=Course&reportId=422 +MPTX 501,Organ System and Common Diseases,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/541?entityType=Course&reportId=422 +MPTX 502,Cellular Communications,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/544?entityType=Course&reportId=422 +MPTX 503,Principles of Pharmacology and Toxicology,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/545?entityType=Course&reportId=422 +MPTX 504,Systemic Pharmacology I,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/554?entityType=Course&reportId=422 +MPTX 505,Drug Discovery I,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/549?entityType=Course&reportId=422 +MPTX 506,Systemic Pharmacology II,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/555?entityType=Course&reportId=422 +MPTX 507,Applied Toxicology,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/546?entityType=Course&reportId=422 +MPTX 508,Research Methods in Pharmacology,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/547?entityType=Course&reportId=422 +MPTX 509,Drug Discovery and Development II,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/550?entityType=Course&reportId=422 +MPTX 511E,Environmental Toxicology,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/542?entityType=Course&reportId=422 +MPTX 601,Biostatistics and Critical Appraisal,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/543?entityType=Course&reportId=422 +MPTX 602,Current Topics in Pharmacology and Toxicology,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/551?entityType=Course&reportId=422 +MPTX 603,Introduction to Research Project,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/552?entityType=Course&reportId=422 +MPTX 604E,Scientific Writing,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/548?entityType=Course&reportId=422 +MPTX 701,Research Project and Thesis,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/553?entityType=Course&reportId=422 +MSC 600,Research Methods and Ethics,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/1241?entityType=Course&reportId=422 +MSC 601,Technical Communication,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/952?entityType=Course&reportId=422 +MSC 602,Advanced Applied Mathematics,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/985?entityType=Course&reportId=422 +MSC 615,Advanced Statistics and Probability,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/1311?entityType=Course&reportId=422 +MSCP 720,Scholarly Project,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/556?entityType=Course&reportId=422 +MSF 01,Financial Modeling,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/480?entityType=Course&reportId=422 +MSF 05,Internship,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/444?entityType=Course&reportId=422 +MSF 06,Banking and Credit,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/481?entityType=Course&reportId=422 +MSF 07,Economic Policy in Global Markets,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/441?entityType=Course&reportId=422 +MSF 08,Microeconomics for Finance,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/442?entityType=Course&reportId=422 +MSF 10,Independent Research,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/446?entityType=Course&reportId=422 +MSF 11,Fintech,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/439?entityType=Course&reportId=422 +MSF 12,Financial Accounting,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/440?entityType=Course&reportId=422 +MSF 13,Industry Insights Initiative,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/698?entityType=Course&reportId=422 +MSMR 501,Musculoskeletal System,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/569?entityType=Course&reportId=422 +MSMR 502,Nutrition in Sports and Exercise,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/566?entityType=Course&reportId=422 +MSMR 503,"Exercise Genetics, Physiology and Endocrinology",https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/557?entityType=Course&reportId=422 +MSMR 504,Pharmacology in Sports and Rehabilitation,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/570?entityType=Course&reportId=422 +MSMR 505,Exercise and Biomechanics,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/572?entityType=Course&reportId=422 +MSMR 506,Seminar in Sports Medicine,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/558?entityType=Course&reportId=422 +MSMR 507,Management and Rehabilitation of Injuries,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/573?entityType=Course&reportId=422 +MSMR 508,Health Fitness and Wellbeing,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/559?entityType=Course&reportId=422 +MSMR 509,Robots for Rehabilitation,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/571?entityType=Course&reportId=422 +MSMR 510,Biostatistics and Epidemiology,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/564?entityType=Course&reportId=422 +MSMR 511,Introduction to Research Project,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/560?entityType=Course&reportId=422 +MSMR 513,Internship in Sports Medicine and Rehabilitation,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/574?entityType=Course&reportId=422 +MSMR 514,Adaptive Physical Activity,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/575?entityType=Course&reportId=422 +MSMR 515,Nutrition for Life,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/567?entityType=Course&reportId=422 +MSMR 516,Clinical and Motivational Interviewing,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/3299?entityType=Course&reportId=422 +MSMR 601,Psychology of Sports and Rehabilitation,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/565?entityType=Course&reportId=422 +MSMR 602,Advances in Sports and Rehabilitation,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/561?entityType=Course&reportId=422 +MSMR 603,Master Research Project,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/562?entityType=Course&reportId=422 +MSMR 701,Ethics and Professionalism,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/568?entityType=Course&reportId=422 +MSMR 702,Master Research Project Thesis,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2340?entityType=Course&reportId=422 +MSN 501,Theoretical Foundations of Advanced Nursing,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/3383?entityType=Course&reportId=422 +MSN 502,Advanced Physical and Health Assessment,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/3373?entityType=Course&reportId=422 +MSN 503,"Statistics for Nursing Research, Evidence-Based Practice, and Quality Improvement",https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/3384?entityType=Course&reportId=422 +MSN 504,Informatics and Healthcare Technologies,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/3385?entityType=Course&reportId=422 +MSN 505,Health Policy and Advocacy,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/3386?entityType=Course&reportId=422 +MSN 506,"Nursing Research, Evidence-Based Practice, and Quality Improvement",https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/829?entityType=Course&reportId=422 +MSN 507,Advanced Pathophysiology,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/830?entityType=Course&reportId=422 +MSN 508,Advanced Pharmacology,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/831?entityType=Course&reportId=422 +MSN 509,Learning and Teaching Strategies and Innovation in Nursing,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/832?entityType=Course&reportId=422 +MSN 510,Population-Based Health Promotion and Clinical Prevention,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/833?entityType=Course&reportId=422 +MSRB 621,Evidence-Based Medicine I and Biostatistics,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/578?entityType=Course&reportId=422 +MSRB 622,Evidence-Based Medicine II and Biostatistics,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/579?entityType=Course&reportId=422 +MSRB 711,Population Health,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/576?entityType=Course&reportId=422 +MSRB 712,Applied Evidence-Based Medicine,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/577?entityType=Course&reportId=422 +MSRG 810,Surgery I Clerkship,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/580?entityType=Course&reportId=422 +NUSM 101,Introduction to Medicine,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/581?entityType=Course&reportId=422 +NUSM 102,Biology for Medical Students I with Lab,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/1346?entityType=Course&reportId=422 +NUSM 301,"Introduction to Immunology, Microbiology and Genetics",https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/582?entityType=Course&reportId=422 +NUSM 302,Introduction to Statistics for Evidence-Based Practice,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/593?entityType=Course&reportId=422 +NUSM 303,Introduction to Anatomy and Histology,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/590?entityType=Course&reportId=422 +NUSM 310,Capstone Project,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/583?entityType=Course&reportId=422 +NUSM 401,Introduction to Being a Physician,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/584?entityType=Course&reportId=422 +NUSM 402,Medical Anatomy,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/587?entityType=Course&reportId=422 +NUSM 403,Human Genetics,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/592?entityType=Course&reportId=422 +NUSM 404,Cellular and Pathologic Basis of Disease,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/591?entityType=Course&reportId=422 +NUSM 405,Fuel Metabolism,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/586?entityType=Course&reportId=422 +NUSM 406,Immunology in Health and Disease,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/585?entityType=Course&reportId=422 +NUSM 407,Medical Microbiology,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/588?entityType=Course&reportId=422 +NUSM 408,Pharmacology,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/589?entityType=Course&reportId=422 +PER 101,Beginning Persian I,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2737?entityType=Course&reportId=422 +PER 102,Beginning Persian II,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2738?entityType=Course&reportId=422 +PER 201,Intermediate Persian I,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2739?entityType=Course&reportId=422 +PER 202,Intermediate Persian II,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2740?entityType=Course&reportId=422 +PETE 201,Fluid Mechanics and Thermodynamics,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/322?entityType=Course&reportId=422 +PETE 202,Transport Phenomena,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/303?entityType=Course&reportId=422 +PETE 203,Drilling Engineering with Laboratories,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/333?entityType=Course&reportId=422 +PETE 204,Reservoir Rock and Fluid Properties with Laboratories,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/318?entityType=Course&reportId=422 +PETE 301,Numerical Methods for Petroleum Engineers,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/323?entityType=Course&reportId=422 +PETE 302,Reservoir Engineering I with Laboratories,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/324?entityType=Course&reportId=422 +PETE 303,Well Logging and Formation Evaluation,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/319?entityType=Course&reportId=422 +PETE 304,Well Completion and Stimulation,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/334?entityType=Course&reportId=422 +PETE 305,Well Test Analysis,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/325?entityType=Course&reportId=422 +PETE 306,Reservoir Engineering II,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/316?entityType=Course&reportId=422 +PETE 307,Production Engineering,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/326?entityType=Course&reportId=422 +PETE 311,Reservoir Simulation,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/338?entityType=Course&reportId=422 +PETE 400,Capstone Design Project I,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/327?entityType=Course&reportId=422 +PETE 404,Petroleum Geomechanics,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/320?entityType=Course&reportId=422 +PETE 405,Enhanced Oil Recovery,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/304?entityType=Course&reportId=422 +PETE 407,Capstone Design Project II,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/328?entityType=Course&reportId=422 +PETE 408,Gas Engineering,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/344?entityType=Course&reportId=422 +PETE 409,Surface Facilities,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/3344?entityType=Course&reportId=422 +PETE 501,Foundations of Petroleum Engineering,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/317?entityType=Course&reportId=422 +PETE 502,Petroleum Geology,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/343?entityType=Course&reportId=422 +PETE 503,Fluid Flow Through Porous Media,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/305?entityType=Course&reportId=422 +PETE 504,Advanced Formation Evaluation,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/321?entityType=Course&reportId=422 +PETE 505,Advanced Drilling Engineering,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/335?entityType=Course&reportId=422 +PETE 506,Advanced Reservoir Simulation,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/339?entityType=Course&reportId=422 +PETE 507,Advanced Reservoir Engineering,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/329?entityType=Course&reportId=422 +PETE 508,Advanced Production Engineering,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/306?entityType=Course&reportId=422 +PETE 509,Advanced Well Testing and Evaluation,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/330?entityType=Course&reportId=422 +PETE 600,Thesis I,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/307?entityType=Course&reportId=422 +PETE 601,Petroleum Data Analytics,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/340?entityType=Course&reportId=422 +PETE 602,Research Seminar,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/336?entityType=Course&reportId=422 +PETE 606,Advanced Enhanced Oil Recovery,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/308?entityType=Course&reportId=422 +PETE 613,Unconventional Resources Drilling,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/337?entityType=Course&reportId=422 +PETE 614,"Introduction to Geospatial, Earth Observation and Surveying Technologies for Onshore and Marine Petroleum and Mining Industry",https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/332?entityType=Course&reportId=422 +PETE 620,Thesis II,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/309?entityType=Course&reportId=422 +PETE 701,Advanced Well Engineering,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/310?entityType=Course&reportId=422 +PETE 702,Current Research Literature,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/311?entityType=Course&reportId=422 +PETE 703,Advanced Reservoir Engineering,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/331?entityType=Course&reportId=422 +PETE 801,Advanced Reservoir Simulation,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/341?entityType=Course&reportId=422 +PETE 802,Fluid Flow Through Porous Media,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/312?entityType=Course&reportId=422 +PETE 803,Advanced Enhanced Oil Recovery,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/313?entityType=Course&reportId=422 +PETE 805,Special Topics in Petroleum Engineering : Data Analytics,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/342?entityType=Course&reportId=422 +PETE 890,Doctoral Thesis,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/314?entityType=Course&reportId=422 +PETE 890,0 Thesis Research,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/315?entityType=Course&reportId=422 +PHDBS 701,Thesis Research,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/598?entityType=Course&reportId=422 +PHDBS 701,0 Thesis Research,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/599?entityType=Course&reportId=422 +PHDBS 702,Molecular Basis of Health and Disease,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/600?entityType=Course&reportId=422 +PHDBS 703,Biomedical Research Analysis and Design of Experimentation,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/601?entityType=Course&reportId=422 +PHDBS 704,NUSOM Seminar (Final Semester),https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/595?entityType=Course&reportId=422 +PHDBS 704,0 NUSOM Seminar,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/596?entityType=Course&reportId=422 +PHDBS 705,"Biosafety, Animal Welfare and Ethics",https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/602?entityType=Course&reportId=422 +PHDBS 706,Advanced Biomedical Research Analysis and Design of Experimentation,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/603?entityType=Course&reportId=422 +PHDBS 707,Publication Analysis,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/604?entityType=Course&reportId=422 +PHDBS 708,Writing for Biomedical Sciences,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/610?entityType=Course&reportId=422 +PHDBS 709,Molecular Basis of Infection,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/606?entityType=Course&reportId=422 +PHDBS 710,Molecular Pathology of Cancer,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/597?entityType=Course&reportId=422 +PHDBS 711,Molecular Basis of Genetic Disease,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/608?entityType=Course&reportId=422 +PHDBS 712,Molecular Immunology,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/607?entityType=Course&reportId=422 +PHDBS 713,From the Bench to the Bedside: Drug Development and Clinical Trials,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/605?entityType=Course&reportId=422 +PHDBS 715,Molecular Basis of Neurological Disorders,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/609?entityType=Course&reportId=422 +PHDBS 717,Organ System and Common Diseases,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/594?entityType=Course&reportId=422 +PHDBS 718,Principles of Pharmacology and Toxicology,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/611?entityType=Course&reportId=422 +PHDBS 719,Systemic Pharmacology,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/612?entityType=Course&reportId=422 +PHDBS 721,Drug Discovery I,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/613?entityType=Course&reportId=422 +PHDBS 722,Systemic Pharmacology II,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/614?entityType=Course&reportId=422 +PHDBS 723,Drug Addiction and Toxicology,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/615?entityType=Course&reportId=422 +PHDGH 701,Thesis Research,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/618?entityType=Course&reportId=422 +PHDGH 701,0 Thesis Research,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/619?entityType=Course&reportId=422 +PHDGH 702,Foundations of Global Health,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/620?entityType=Course&reportId=422 +PHDGH 703,Health Policy and Systems in Global Health,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/621?entityType=Course&reportId=422 +PHDGH 704,NUSOM Seminar in Global Health,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/616?entityType=Course&reportId=422 +PHDGH 704,0 NUSOM Seminar in Global Health,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/617?entityType=Course&reportId=422 +PHDGH 705,Global Health into Action,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/622?entityType=Course&reportId=422 +PHDGH 706,Methods in Global Health Research,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/626?entityType=Course&reportId=422 +PHDGH 707,Implementation Science in Global Health,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/624?entityType=Course&reportId=422 +PHDGH 708,Advanced Methods in Global Health Research,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/627?entityType=Course&reportId=422 +PHDGH 709,Critical Appraisal in Global Health,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/625?entityType=Course&reportId=422 +PHDGH 710,Writing for Health Sciences,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/623?entityType=Course&reportId=422 +PHIL 101,Introduction to Philosophy,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2940?entityType=Course&reportId=422 +PHIL 141,Critical Thinking,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/3140?entityType=Course&reportId=422 +PHIL 160,Philosophy of Religion,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/3141?entityType=Course&reportId=422 +PHIL 210,Ethics,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2807?entityType=Course&reportId=422 +PHIL 223,Philosophy of Science,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2957?entityType=Course&reportId=422 +PHIL 225,Truth and Reality,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2808?entityType=Course&reportId=422 +PHIL 231,Global Justice,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/3133?entityType=Course&reportId=422 +PHIL 232,Philosophy of Law,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/3135?entityType=Course&reportId=422 +PHIL 240,Formal Logic,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/3139?entityType=Course&reportId=422 +PHIL 314,Free Will and Moral Responsibility,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2938?entityType=Course&reportId=422 +PHIL 315,Philosophy of Agency,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2939?entityType=Course&reportId=422 +PHIL 320,"Truth, Knowledge and Belief",https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2809?entityType=Course&reportId=422 +PHIL 361,Philosophy of Language,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2958?entityType=Course&reportId=422 +PHIL 362,Philosophy of Mind,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2959?entityType=Course&reportId=422 +PHIL 431,Human Rights and Cultural Differences,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/3134?entityType=Course&reportId=422 +PHIL 575,Philosophy of Art,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2941?entityType=Course&reportId=422 +PHYS 161,Physics I for Scientists and Engineers with Laboratory,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2572?entityType=Course&reportId=422 +PHYS 162,Physics II for Scientists and Engineers with Laboratory,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2573?entityType=Course&reportId=422 +PHYS 201,Introductory Astronomy I,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2574?entityType=Course&reportId=422 +PHYS 202,Introductory Astrophysics,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2575?entityType=Course&reportId=422 +PHYS 221,Classical Mechanics I,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2623?entityType=Course&reportId=422 +PHYS 222,Classical Mechanics II,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2624?entityType=Course&reportId=422 +PHYS 261,Modern Physics with Laboratory,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2461?entityType=Course&reportId=422 +PHYS 270,Computational Physics with Laboratory,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2677?entityType=Course&reportId=422 +PHYS 280,Thermodynamics and Statistical Physics,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/3283?entityType=Course&reportId=422 +PHYS 291,Introduction to Quantum Technologies,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2448?entityType=Course&reportId=422 +PHYS 299,Research Project and Internship,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2449?entityType=Course&reportId=422 +PHYS 315,Mathematical Methods of Physics,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2465?entityType=Course&reportId=422 +PHYS 361,Classical Electrodynamics I,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2952?entityType=Course&reportId=422 +PHYS 362,Classical Electrodynamics II,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2953?entityType=Course&reportId=422 +PHYS 370,Optics with Laboratory,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2386?entityType=Course&reportId=422 +PHYS 395,Research Methods,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2445?entityType=Course&reportId=422 +PHYS 399,Physics Research Project,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2446?entityType=Course&reportId=422 +PHYS 451,Quantum Mechanics I,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/3127?entityType=Course&reportId=422 +PHYS 452,Quantum Mechanics II,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/3128?entityType=Course&reportId=422 +PHYS 462,Field Theories in Physics,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2627?entityType=Course&reportId=422 +PHYS 463,Astrophysics and General Relativity,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2954?entityType=Course&reportId=422 +PHYS 465,Advanced Experimental Physics,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2387?entityType=Course&reportId=422 +PHYS 470,Introduction to Optoelectronics,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2462?entityType=Course&reportId=422 +PHYS 473,Introduction to Solid State Physics,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/3286?entityType=Course&reportId=422 +PHYS 483,Statistical Mechanics,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2951?entityType=Course&reportId=422 +PHYS 491,Directed Study of Advanced Physics Topics,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2447?entityType=Course&reportId=422 +PHYS 498,Honors Thesis Research,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/3125?entityType=Course&reportId=422 +PHYS 499,Honors Thesis,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/3126?entityType=Course&reportId=422 +PHYS 505,Classical Mechanics,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2626?entityType=Course&reportId=422 +PHYS 510,Quantum Mechanics,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/3123?entityType=Course&reportId=422 +PHYS 511,Computational Modeling and Simulation,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2463?entityType=Course&reportId=422 +PHYS 515,Classical Electrodynamics,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2955?entityType=Course&reportId=422 +PHYS 520,Statistical Physics,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/3284?entityType=Course&reportId=422 +PHYS 530,Solid State Physics,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/3285?entityType=Course&reportId=422 +PHYS 550,Advanced Mathematical Physics,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2464?entityType=Course&reportId=422 +PHYS 563,General Relativity,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2625?entityType=Course&reportId=422 +PHYS 565,Advanced Physics Laboratory,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2388?entityType=Course&reportId=422 +PHYS 570,Introduction to Optoelectronics,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2460?entityType=Course&reportId=422 +PHYS 591,Research Methods,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2444?entityType=Course&reportId=422 +PHYS 600,Thesis Research,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2674?entityType=Course&reportId=422 +PHYS 692,Thesis,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2675?entityType=Course&reportId=422 +PHYS 700,Doctoral Thesis,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2678?entityType=Course&reportId=422 +PHYS 700,0 Thesis Research,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2676?entityType=Course&reportId=422 +PHYS 701,Current Research Literature,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2389?entityType=Course&reportId=422 +PHYS 705,Classical Mechanics,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2622?entityType=Course&reportId=422 +PHYS 710,Quantum Mechanics,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/3124?entityType=Course&reportId=422 +PHYS 711,Computational Modeling and Simulation,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2466?entityType=Course&reportId=422 +PHYS 715,Classical Electrodynamics,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2956?entityType=Course&reportId=422 +PHYS 720,Statistical Physics,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/3287?entityType=Course&reportId=422 +PHYS 730,Solid State Physics,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/3288?entityType=Course&reportId=422 +PHYS 750,Advanced Mathematical Physics,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2467?entityType=Course&reportId=422 +PHYS 763,General Relativity,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2621?entityType=Course&reportId=422 +PHYS 765,Advanced Physics Laboratory,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2390?entityType=Course&reportId=422 +PHYS 770,Introduction to Optoelectronics,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2459?entityType=Course&reportId=422 +PHYS 791,Research Methods and Ethics,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2450?entityType=Course&reportId=422 +PLS 100,Introduction to the Politics of Central Asia,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/3105?entityType=Course&reportId=422 +PLS 101,Introduction to Political Science,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2980?entityType=Course&reportId=422 +PLS 102,The Political Challenges of Technologies and Science: An Introduction,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2810?entityType=Course&reportId=422 +PLS 120,Introduction to Political Theory,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2496?entityType=Course&reportId=422 +PLS 140,Introduction to Comparative Politics,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2638?entityType=Course&reportId=422 +PLS 150,Introduction to International Relations,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2476?entityType=Course&reportId=422 +PLS 210,Political Science Research Methods,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2637?entityType=Course&reportId=422 +PLS 211,Quantitative Methods,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2790?entityType=Course&reportId=422 +PLS 222,Global Justice,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/3131?entityType=Course&reportId=422 +PLS 312,Public Opinion and Elections,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2436?entityType=Course&reportId=422 +PLS 315,Political Game Theory,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2547?entityType=Course&reportId=422 +PLS 321,Major Political Thinkers: Machiavelli,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2813?entityType=Course&reportId=422 +PLS 322,Russian Intellectual History (1762 - 1905),https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2618?entityType=Course&reportId=422 +PLS 324,The Philosophies of Multiculturalism,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2814?entityType=Course&reportId=422 +PLS 326,Modern Political Theory,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2498?entityType=Course&reportId=422 +PLS 327,Contemporary Political Theory,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2499?entityType=Course&reportId=422 +PLS 328,Confucianism and the Development of Chinese Politics,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2590?entityType=Course&reportId=422 +PLS 329,Anarchy,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2497?entityType=Course&reportId=422 +PLS 330,Politics and Governance of Eurasia,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2437?entityType=Course&reportId=422 +PLS 332,Islam and Politics in Eurasia,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/3190?entityType=Course&reportId=422 +PLS 338,U.S. Government and Politics,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2979?entityType=Course&reportId=422 +PLS 340,Politics of East Asia,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2844?entityType=Course&reportId=422 +PLS 341,Politics of Development,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2475?entityType=Course&reportId=422 +PLS 342,Political Corruption,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2978?entityType=Course&reportId=422 +PLS 343,Politics of Sub-Saharan Africa,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2869?entityType=Course&reportId=422 +PLS 345,"Revolutions, Social Movements, and Contentious Politics",https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2506?entityType=Course&reportId=422 +PLS 351,International Political Economy,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2904?entityType=Course&reportId=422 +PLS 352,International Relations Theory,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2641?entityType=Course&reportId=422 +PLS 354,Introduction to International Law,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2391?entityType=Course&reportId=422 +PLS 355,European Union: Institutions and Policies,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2906?entityType=Course&reportId=422 +PLS 356,International Politics of the Korean Peninsula,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2789?entityType=Course&reportId=422 +PLS 360,Foreign Policy Analysis,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2477?entityType=Course&reportId=422 +PLS 361,Memory Politics in East Asia,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2788?entityType=Course&reportId=422 +PLS 362,"Science, Technology and International Affairs",https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/3103?entityType=Course&reportId=422 +PLS 363,Visual Politics,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/3104?entityType=Course&reportId=422 +PLS 365,Civil-Military Relations,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2507?entityType=Course&reportId=422 +PLS 370,"Law, Politics and Society",https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2392?entityType=Course&reportId=422 +PLS 371,Comparative Welfare States,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2905?entityType=Course&reportId=422 +PLS 372,Post-Soviet Public Policy,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2393?entityType=Course&reportId=422 +PLS 373,Comparative Public Policy,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2843?entityType=Course&reportId=422 +PLS 391,Intermediate Special Topics in Comparative Politics,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2781?entityType=Course&reportId=422 +PLS 392,Politics of China,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2981?entityType=Course&reportId=422 +PLS 395,Independent Study,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2842?entityType=Course&reportId=422 +PLS 415,Maximum Likelihood Models,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2502?entityType=Course&reportId=422 +PLS 416,Experimental Political Science,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2787?entityType=Course&reportId=422 +PLS 417,Scientific Research in Political Science,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2546?entityType=Course&reportId=422 +PLS 420,Human Rights and Cultural Differences,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/3132?entityType=Course&reportId=422 +PLS 422,Just War Theory,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2811?entityType=Course&reportId=422 +PLS 423,Immigration Theory,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2495?entityType=Course&reportId=422 +PLS 424,Issues in the Philosophy of Social Science,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2639?entityType=Course&reportId=422 +PLS 425,Feminist Political Theory,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2503?entityType=Course&reportId=422 +PLS 426,"Hannah Arendt on Power, Violence, and Revolution",https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2485?entityType=Course&reportId=422 +PLS 431,Politics and Governance of the Russian Federation,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2435?entityType=Course&reportId=422 +PLS 432,Comparative Democratization,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2640?entityType=Course&reportId=422 +PLS 433,Homeland Security,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2504?entityType=Course&reportId=422 +PLS 434,Text Analysis and Political Communication in the Information Age,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2841?entityType=Course&reportId=422 +PLS 436,State Capacity and State-Building,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2870?entityType=Course&reportId=422 +PLS 437,Nationalism and Multi-Ethnic Governance,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2505?entityType=Course&reportId=422 +PLS 441,Advanced Topics in Comparative Politics,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2780?entityType=Course&reportId=422 +PLS 445,Political Violence,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2642?entityType=Course&reportId=422 +PLS 446,The Politics of Immigration Control,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2520?entityType=Course&reportId=422 +PLS 447,Comparative Political Parties,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2521?entityType=Course&reportId=422 +PLS 448,Comparative Electoral System,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2550?entityType=Course&reportId=422 +PLS 449,Politics and Governance of the Gulf,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2523?entityType=Course&reportId=422 +PLS 451,Advanced Topics in International Relations,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2524?entityType=Course&reportId=422 +PLS 452,The United Nations System,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2480?entityType=Course&reportId=422 +PLS 455,The Politics of Arms in International Relations,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2522?entityType=Course&reportId=422 +PLS 457,International Security and Conflict,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2541?entityType=Course&reportId=422 +PLS 458,International Organization,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2481?entityType=Course&reportId=422 +PLS 459,International Conflict Processes,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2540?entityType=Course&reportId=422 +PLS 460,Environmental Politics,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2832?entityType=Course&reportId=422 +PLS 465,Politics of International Finance,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2902?entityType=Course&reportId=422 +PLS 468,International Relations of Asia,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2835?entityType=Course&reportId=422 +PLS 469,International Relations of Eurasia,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/3107?entityType=Course&reportId=422 +PLS 472,"Negotiation, Cooperation and Strategy",https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2525?entityType=Course&reportId=422 +PLS 474,Public Choice and Public Policy,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2833?entityType=Course&reportId=422 +PLS 495,Research Practicum in PSIR,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2834?entityType=Course&reportId=422 +PLS 510,Quantitative Research Methods,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2549?entityType=Course&reportId=422 +PLS 511,Qualitative Methods in Political Science,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2394?entityType=Course&reportId=422 +PLS 514,Qualitative Research Methods,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/3191?entityType=Course&reportId=422 +PLS 515,Maximum Likelihood Models,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2516?entityType=Course&reportId=422 +PLS 516,Experimental Political Science,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2784?entityType=Course&reportId=422 +PLS 517,Scientific Research in Political Science,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2542?entityType=Course&reportId=422 +PLS 522,Just War Theory,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2815?entityType=Course&reportId=422 +PLS 523,Immigration Theory,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2486?entityType=Course&reportId=422 +PLS 526,"Hannah Arendt on Power, Violence, and Revolution",https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2487?entityType=Course&reportId=422 +PLS 531,Politics and Governance of the Russian Federation,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2515?entityType=Course&reportId=422 +PLS 532,Comparative Democratization,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2644?entityType=Course&reportId=422 +PLS 533,Homeland Security,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2517?entityType=Course&reportId=422 +PLS 534,Text Analysis and Political Communication in the Information Age,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2518?entityType=Course&reportId=422 +PLS 536,State Capacity and State-building,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2871?entityType=Course&reportId=422 +PLS 537,Nationalism and Multi-Ethnic Governance,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2519?entityType=Course&reportId=422 +PLS 540,Core Seminar in Comparative Politics,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2782?entityType=Course&reportId=422 +PLS 541,Advanced Topics in Comparative Politics,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2783?entityType=Course&reportId=422 +PLS 545,Political Violence,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2643?entityType=Course&reportId=422 +PLS 546,The Politics of Immigration Control,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2512?entityType=Course&reportId=422 +PLS 547,Comparative Political Parties,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2513?entityType=Course&reportId=422 +PLS 548,Comparative Electoral System,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2548?entityType=Course&reportId=422 +PLS 549,Politics and Governance of the Gulf,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2514?entityType=Course&reportId=422 +PLS 550,Core Seminar in IR,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2786?entityType=Course&reportId=422 +PLS 551,Advanced Topics in International Relations,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2839?entityType=Course&reportId=422 +PLS 552,The United Nations System,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2478?entityType=Course&reportId=422 +PLS 555,The Politics of Arms in International Relations,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2510?entityType=Course&reportId=422 +PLS 557,International Security and Conflict,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2544?entityType=Course&reportId=422 +PLS 558,International Organization,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2479?entityType=Course&reportId=422 +PLS 559,International Conflict Processes,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2543?entityType=Course&reportId=422 +PLS 560,Environmental Politics,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2840?entityType=Course&reportId=422 +PLS 565,Politics of International Finance,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2903?entityType=Course&reportId=422 +PLS 568,International Relations of Asia,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2838?entityType=Course&reportId=422 +PLS 569,International Relations of Eurasia,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/3106?entityType=Course&reportId=422 +PLS 572,"Negotiation, Cooperation and Strategy",https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2511?entityType=Course&reportId=422 +PLS 574,Public Choice and Public Policy,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2837?entityType=Course&reportId=422 +PLS 581,Independent Study in Political Science,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2488?entityType=Course&reportId=422 +PLS 596,Thesis Preparation,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2489?entityType=Course&reportId=422 +PLS 597,Research for Thesis,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2490?entityType=Course&reportId=422 +PLS 598,MA Thesis I,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2491?entityType=Course&reportId=422 +PLS 599,MA Thesis II,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2492?entityType=Course&reportId=422 +PLS 710,Quantitative Research Methods,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2836?entityType=Course&reportId=422 +PLS 711,Qualitative Methods in Political Science,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2395?entityType=Course&reportId=422 +PLS 715,Maximum Likelihood Models,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2509?entityType=Course&reportId=422 +PLS 716,Experimental Political Science,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2785?entityType=Course&reportId=422 +PLS 717,Scientific Research in Political Science,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2545?entityType=Course&reportId=422 +PLS 722,Just War Theory,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2812?entityType=Course&reportId=422 +PLS 723,Immigration Theory,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2493?entityType=Course&reportId=422 +PLS 726,"Hannah Arendt on Power, Violence, and Revolution",https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2494?entityType=Course&reportId=422 +PLS 731,Politics and Governance of the Russian Federation,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2508?entityType=Course&reportId=422 +PLS 732,Comparative Democratization,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2645?entityType=Course&reportId=422 +PLS 733,Homeland Security,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2527?entityType=Course&reportId=422 +PLS 734,Text Analysis and Political Communication in the Information Age,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2528?entityType=Course&reportId=422 +PLS 737,Nationalism and Multi-Ethnic Governance,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2529?entityType=Course&reportId=422 +PLS 740,Core Seminar in Comparative Politics,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2778?entityType=Course&reportId=422 +PLS 741,Advanced Topics in Comparative Politics,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2779?entityType=Course&reportId=422 +PLS 745,Political Violence,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2646?entityType=Course&reportId=422 +PLS 746,The Politics of Immigration Control,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2530?entityType=Course&reportId=422 +PLS 747,Comparative Political Parties,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2531?entityType=Course&reportId=422 +PLS 748,Comparative Electoral System,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2551?entityType=Course&reportId=422 +PLS 749,Politics and Governance of the Gulf,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2532?entityType=Course&reportId=422 +PLS 750,Core Seminar in IR,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2791?entityType=Course&reportId=422 +PLS 751,Advanced Topics in International Relations,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2828?entityType=Course&reportId=422 +PLS 752,The United Nations System,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2483?entityType=Course&reportId=422 +PLS 755,The Politics of Arms in International Relations,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2526?entityType=Course&reportId=422 +PLS 757,International Security and Conflict,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2538?entityType=Course&reportId=422 +PLS 758,International Organization,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2482?entityType=Course&reportId=422 +PLS 759,International Conflict Processes,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2539?entityType=Course&reportId=422 +PLS 760,Environmental Politics,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2829?entityType=Course&reportId=422 +PLS 765,Politics of International Finance,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2901?entityType=Course&reportId=422 +PLS 768,Internation Relations of Asia,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2830?entityType=Course&reportId=422 +PLS 769,International Relations of Eurasia,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/3102?entityType=Course&reportId=422 +PLS 774,Public Choice and Public Policy,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2831?entityType=Course&reportId=422 +PLS 781,Independent Study in Political Science,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2777?entityType=Course&reportId=422 +POL 101,Beginning Polish,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2714?entityType=Course&reportId=422 +POL 102,Beginning Polish II,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2715?entityType=Course&reportId=422 +PUBH 501,Principles of Public Health Problem Solving,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/641?entityType=Course&reportId=422 +PUBH 511,Inferential Biostatistics,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/632?entityType=Course&reportId=422 +PUBH 512,Biostatistical Modeling and Sampling,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/633?entityType=Course&reportId=422 +PUBH 521,Principles of Analytical Epidemiology,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/644?entityType=Course&reportId=422 +PUBH 522,Advanced Analytical Epidemiology,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/645?entityType=Course&reportId=422 +PUBH 528,Qualitative Research Methods,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/637?entityType=Course&reportId=422 +PUBH 551,"Health Services Management, Health Economics and Health Policy",https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/638?entityType=Course&reportId=422 +PUBH 552,Principles of Heath Economics and Policy,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/639?entityType=Course&reportId=422 +PUBH 561,Graduate Research Seminar I,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/634?entityType=Course&reportId=422 +PUBH 599,Global Nutrition and Sustainability,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/628?entityType=Course&reportId=422 +PUBH 601,Health Education and Promotion,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/642?entityType=Course&reportId=422 +PUBH 631,Social and Behavioral Sciences in Public Health,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/643?entityType=Course&reportId=422 +PUBH 633,Methods in Public Health,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/635?entityType=Course&reportId=422 +PUBH 641,Problem Investigations in Environmental Health,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/636?entityType=Course&reportId=422 +PUBH 651,Risk Management in Health Care,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/640?entityType=Course&reportId=422 +PUBH 652,Applied Methods in Health Services Research,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/631?entityType=Course&reportId=422 +PUBH 681,Master's Project Planning,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/629?entityType=Course&reportId=422 +PUBH 682,Master's Project Implementation,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/630?entityType=Course&reportId=422 +REL 110,Introduction to World Religions,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2991?entityType=Course&reportId=422 +REL 212,Buddhist Religious Traditions,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2992?entityType=Course&reportId=422 +REL 215,What is Islam? Anthropological Perspectives,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/3093?entityType=Course&reportId=422 +REL 261,History of Islam (570s to 1258),https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2586?entityType=Course&reportId=422 +REL 263,The History of Islam II: Islam in the Modern World,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/3091?entityType=Course&reportId=422 +REL 268,Women and Islam in Central Eurasia,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/3092?entityType=Course&reportId=422 +REL 273,History of Sufism in the Middle East and Central Asia,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2587?entityType=Course&reportId=422 +REL 320,The Crusades,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2588?entityType=Course&reportId=422 +REL 329,Women in Islamic History,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/3089?entityType=Course&reportId=422 +REL 332,Islam and Politics in Eurasia,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/3189?entityType=Course&reportId=422 +REL 341,Eastern Christianity,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2619?entityType=Course&reportId=422 +REL 435,The Archaeology of Ritual,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2873?entityType=Course&reportId=422 +REL 440,Religions in the Soviet and Post-Soviet Eras,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2994?entityType=Course&reportId=422 +REL 451,Conversion to Islam in Medieval and Central Asia,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2589?entityType=Course&reportId=422 +REL 462,History of Islam Under Russian Rule,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/3090?entityType=Course&reportId=422 +REL 535,The Archaeology of Ritual,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2874?entityType=Course&reportId=422 +REL 540,Religions in the Soviet and Post-Soviet Eras,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2993?entityType=Course&reportId=422 +REL 551,Conversion to Islam in Medieval and Central Asia,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2584?entityType=Course&reportId=422 +REL 562,History of Islam Under Russian Rule,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/3088?entityType=Course&reportId=422 +REL 740,Religions in the Soviet and Post-Soviet Eras,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2990?entityType=Course&reportId=422 +REL 751,Conversion to Islam in Medieval and Central Asia,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2585?entityType=Course&reportId=422 +ROBT 201,Mechanics: Statics and Dynamics,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/873?entityType=Course&reportId=422 +ROBT 202,System Dynamics and Modeling,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/1139?entityType=Course&reportId=422 +ROBT 203,Electrical and Electronic Circuits I with Laboratory,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/1290?entityType=Course&reportId=422 +ROBT 204,Electrical and Electronic Circuits II with Laboratory,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/1291?entityType=Course&reportId=422 +ROBT 205,Signals and Sensing with Laboratory,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/1138?entityType=Course&reportId=422 +ROBT 206,Microcontrollers with Laboratory,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/978?entityType=Course&reportId=422 +ROBT 301,Mechanical Design I with CAD and Machining Laboratory,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/874?entityType=Course&reportId=422 +ROBT 303,Linear Control Theory with Laboratory,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/1285?entityType=Course&reportId=422 +ROBT 304,Electromechanical Systems with Laboratory,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/875?entityType=Course&reportId=422 +ROBT 305,Embedded Systems,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/912?entityType=Course&reportId=422 +ROBT 307,Power Electronics,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/1288?entityType=Course&reportId=422 +ROBT 308,Industrial Automation,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/1286?entityType=Course&reportId=422 +ROBT 310,Image Processing,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/1322?entityType=Course&reportId=422 +ROBT 312,Robotics I: Kinematics and Dynamics,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/1184?entityType=Course&reportId=422 +ROBT 391,Research Methods,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/1142?entityType=Course&reportId=422 +ROBT 399,Internship,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/1143?entityType=Course&reportId=422 +ROBT 402,Robotic/Mechatronic System Design,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/951?entityType=Course&reportId=422 +ROBT 403,"Robotics II: Control, Modeling and Learning with Laboratory",https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/1183?entityType=Course&reportId=422 +ROBT 407,Machine Learning with Applications,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/977?entityType=Course&reportId=422 +ROBT 414,Human-Robot Interaction,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/924?entityType=Course&reportId=422 +ROBT 491,Capstone Project I,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/1140?entityType=Course&reportId=422 +ROBT 492,Capstone Project II,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/1141?entityType=Course&reportId=422 +ROBT 501,Robot Manipulation and Mobility,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/1185?entityType=Course&reportId=422 +ROBT 502,Robot Perception and Vision,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/1081?entityType=Course&reportId=422 +ROBT 503,Dynamic Systems and Control,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/1082?entityType=Course&reportId=422 +ROBT 611,Industrial Robotics,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/911?entityType=Course&reportId=422 +ROBT 613,Brain-Machine Interfaces,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/979?entityType=Course&reportId=422 +ROBT 615,Optimal Control and Planning,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/1289?entityType=Course&reportId=422 +ROBT 691,Thesis Proposal,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/1283?entityType=Course&reportId=422 +ROBT 692,Thesis,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/1284?entityType=Course&reportId=422 +ROBT 700,Thesis Research,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/1180?entityType=Course&reportId=422 +ROBT 700,0 Thesis Research,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/1181?entityType=Course&reportId=422 +ROBT 701,Advanced Robot Manipulation and Mobility,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/1182?entityType=Course&reportId=422 +ROBT 702,Advanced Robot Perception and Vision,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/1321?entityType=Course&reportId=422 +ROBT 703,Advanced Optimal Control and Planning,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/1287?entityType=Course&reportId=422 +ROBT 709,Advanced Robot Dynamics and Control,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/1080?entityType=Course&reportId=422 +ROBT 722,Current Research Literature,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/1179?entityType=Course&reportId=422 +SEDS 502,Teaching Practicum,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/1175?entityType=Course&reportId=422 +SEDS 503,Laboratory Practicum,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/976?entityType=Course&reportId=422 +SEDS 591,Research Methods,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/925?entityType=Course&reportId=422 +SEDS 592,Research Seminar,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/1079?entityType=Course&reportId=422 +SMG 100,Introduction to Natural Resource Extraction,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/346?entityType=Course&reportId=422 +SMG 200,Resource Economics,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/3455?entityType=Course&reportId=422 +SMG 210,Strength of Materials,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/347?entityType=Course&reportId=422 +SMG 400,Engineering Economics,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/350?entityType=Course&reportId=422 +SMG 710,Advanced Statistics,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/348?entityType=Course&reportId=422 +SOC 101,Introduction to Sociology,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2868?entityType=Course&reportId=422 +SOC 115,Global Social Problems,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/3059?entityType=Course&reportId=422 +SOC 201,Social Science Research Methods,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2872?entityType=Course&reportId=422 +SOC 203,Quantitative Methods in Sociology,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/3060?entityType=Course&reportId=422 +SOC 210,Gender and Society,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2422?entityType=Course&reportId=422 +SOC 212,Food and Society,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2875?entityType=Course&reportId=422 +SOC 213,Work and Society,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2867?entityType=Course&reportId=422 +SOC 214,Qualitative Methods,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/3029?entityType=Course&reportId=422 +SOC 215,Sociology of Race and Ethnicity,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/3110?entityType=Course&reportId=422 +SOC 217,Sociology of the Family,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/3061?entityType=Course&reportId=422 +SOC 220,"Science, Technology, and Society",https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/3062?entityType=Course&reportId=422 +SOC 221,International Migration,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/3112?entityType=Course&reportId=422 +SOC 223,Social Movements: How People Make Change,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2946?entityType=Course&reportId=422 +SOC 301,Classical Sociological Theory,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2743?entityType=Course&reportId=422 +SOC 310,Social Inequality,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2969?entityType=Course&reportId=422 +SOC 317,Economy and Society,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2945?entityType=Course&reportId=422 +SOC 318,Sexuality and Gender in a Transnational World,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/3063?entityType=Course&reportId=422 +SOC 320,Organized Crime and Corruption,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2741?entityType=Course&reportId=422 +SOC 322,Digital Media Research Methods,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2583?entityType=Course&reportId=422 +SOC 375,Inner Asian Frontiers of China: A Cultural Perspective,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/3186?entityType=Course&reportId=422 +SOC 386,Social Challenges of Climate Change,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2944?entityType=Course&reportId=422 +SOC 400,Research Assistance in Sociology,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/3065?entityType=Course&reportId=422 +SOC 401,Research Assistance in Sociology,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/3066?entityType=Course&reportId=422 +SOC 402,Research Assistance in Sociology,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/3067?entityType=Course&reportId=422 +SOC 403,Research Assistance in Sociology,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/3068?entityType=Course&reportId=422 +SOC 404,Research Assistance in Sociology,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/3069?entityType=Course&reportId=422 +SOC 410,Violence Against Women: An Intersectional Approach,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/3111?entityType=Course&reportId=422 +SOC 411,Citizenship in Eurasia: Political Sociology of Civic Belonging,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2947?entityType=Course&reportId=422 +SOC 412,Approaches to Global Development,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/3027?entityType=Course&reportId=422 +SOC 415,Social Problems and Issues in Eurasia,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/3064?entityType=Course&reportId=422 +SOC 416,Sociology of Punishment,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2742?entityType=Course&reportId=422 +SOC 420,Applied Policy Analysis: Tools and Perspectives,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2534?entityType=Course&reportId=422 +SOC 421,Informal Practices and Social Order in Eurasia,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2744?entityType=Course&reportId=422 +SOC 425,Modern Sociological Theory,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2970?entityType=Course&reportId=422 +SOC 482,"Climate Change, Future Energy, and Sustainability",https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/3028?entityType=Course&reportId=422 +SOC 498,Capstone Seminar Part I,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2582?entityType=Course&reportId=422 +SOC 499,Capstone Seminar II,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2406?entityType=Course&reportId=422 +SOC 510,Social Inequality,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/3070?entityType=Course&reportId=422 +SOC 511,Citizenship in Eurasia: Political Sociology of Civic Belonging,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2943?entityType=Course&reportId=422 +SOC 514,Qualitative Research Methods,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/3071?entityType=Course&reportId=422 +SOC 515,Social Problems and Issues in Eurasia,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/3072?entityType=Course&reportId=422 +SOC 521,Informal Practices and Social Order in Eurasia,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2745?entityType=Course&reportId=422 +SOC 710,Social Inequality,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/3073?entityType=Course&reportId=422 +SOC 725,COVID-19: The Sociocultural Aspects of a Pandemic,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/3074?entityType=Course&reportId=422 +SPA 101,Beginning Spanish I,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2668?entityType=Course&reportId=422 +SPA 102,Beginning Spanish II,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2669?entityType=Course&reportId=422 +SPA 201,Intermediate Spanish I,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2670?entityType=Course&reportId=422 +SPA 202,Intermediate Spanish II,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2671?entityType=Course&reportId=422 +SPA 254,"Superstition, Magical Realism, and Horror in Hispanic Culture",https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2818?entityType=Course&reportId=422 +SPA 311,Colloquial Spanish,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2666?entityType=Course&reportId=422 +SPA 314,Advanced Spanish Grammar and Composition,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2667?entityType=Course&reportId=422 +SPA 351,Literature and Film of the Postcolonial World,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2819?entityType=Course&reportId=422 +TUR 100,"Languages, Cultures, and Communities of the Turkic World",https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/3227?entityType=Course&reportId=422 +TUR 224,"WWI and the World: Causes, Conduct and the Consequences of WWI",https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2773?entityType=Course&reportId=422 +TUR 230,Literatures of Central Asia,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/3228?entityType=Course&reportId=422 +TUR 231,Istanbul in Literature and Culture,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2708?entityType=Course&reportId=422 +TUR 235,Turkish Poetry in Translation,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2709?entityType=Course&reportId=422 +TUR 271,History of the Ottoman Empire,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2774?entityType=Course&reportId=422 +TUR 272,Modern Turkey: from the Ottoman Empire to the Turkish Republic,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2775?entityType=Course&reportId=422 +TUR 280,Introduction to Turkic Historical and Comparative Linguistics,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/3250?entityType=Course&reportId=422 +TUR 301,Introduction to Old Turkic,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/3219?entityType=Course&reportId=422 +TUR 305,Introduction to Chagatay and Ottoman Turkish,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/3220?entityType=Course&reportId=422 +TUR 375,Inner Asian Frontiers of China: A Cultural Perspective,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/3184?entityType=Course&reportId=422 +TUR 380,Linguistic and Cultural Diversity in Contemporary China: From Beijing to Xinjiang,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/3185?entityType=Course&reportId=422 +TUR 411,Advanced Readings in Turkish Literature,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2710?entityType=Course&reportId=422 +TUR 412,Advanced Turkish in Media and Politics,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2711?entityType=Course&reportId=422 +TUR 451,The Turkish Novel,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2712?entityType=Course&reportId=422 +TUR 454,Turkestan in the Nineteenth Century,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/3221?entityType=Course&reportId=422 +TUR 455,Eurasian Environments,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/3222?entityType=Course&reportId=422 +TUR 552,"Proseminar in Eurasian History, 552-1917",https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/3225?entityType=Course&reportId=422 +TUR 554,Turkestan in the Nineteenth Century,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/3223?entityType=Course&reportId=422 +TUR 752,"Proseminar in Eurasian History, 552-1917",https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/3226?entityType=Course&reportId=422 +TUR 775,Doctoral Topic in Turkic Studies,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/3224?entityType=Course&reportId=422 +WCS 101,Communication and Society,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/3148?entityType=Course&reportId=422 +WCS 135,Introduction to Visual Communication,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2537?entityType=Course&reportId=422 +WCS 150,Rhetoric and Composition,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/3149?entityType=Course&reportId=422 +WCS 200,Introduction to Public Speaking,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/3150?entityType=Course&reportId=422 +WCS 201,Introduction to Journalism,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/3129?entityType=Course&reportId=422 +WCS 203,Interpersonal Communication,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/3142?entityType=Course&reportId=422 +WCS 204,Gender and Communication,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/3143?entityType=Course&reportId=422 +WCS 205,Intercultural Communication,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/3346?entityType=Course&reportId=422 +WCS 210,Technical and Professional Writing,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/3130?entityType=Course&reportId=422 +WCS 220,Science Writing,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/3251?entityType=Course&reportId=422 +WCS 230,"Say What you Mean: Clarity, Precision, and Style in Academic Writing",https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2816?entityType=Course&reportId=422 +WCS 240,Writing for Digital Media,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2484?entityType=Course&reportId=422 +WCS 250,Advanced Rhetoric and Composition,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2457?entityType=Course&reportId=422 +WCS 260,Creative Writing: Introduction to Fiction Writing I,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2847?entityType=Course&reportId=422 +WCS 270,Academic and Professional Presentations,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/3144?entityType=Course&reportId=422 +WCS 300,Internship: Undergraduate Speaker Consultant I,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/3145?entityType=Course&reportId=422 +WCS 301,Internship: Undergraduate Speaker Consultant II,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/3146?entityType=Course&reportId=422 +WCS 302,Argumentation and Debate,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/3147?entityType=Course&reportId=422 +WCS 360,Poetry Writing Seminar,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2456?entityType=Course&reportId=422 +WCS 361,Advanced Fiction Writing,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2848?entityType=Course&reportId=422 +WCS 363,Writing Speculative Fiction,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2849?entityType=Course&reportId=422 +WCS 390,Writing Fellows I – Composition and Collaboration in Theory and Practice,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2935?entityType=Course&reportId=422 +WCS 391,Writing Fellows II – Practicum in Composition and Collaboration,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2533?entityType=Course&reportId=422 +WCS 392,Writing Fellows III - Research and Practice in Writing and Peer Mentorship,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2352?entityType=Course&reportId=422 +WCS 462,Creative Nonfiction,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2850?entityType=Course&reportId=422 +WCS 465,Creative Writing: Autofiction,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2851?entityType=Course&reportId=422 +WCS 469,Independent Study in Creative Writing,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2852?entityType=Course&reportId=422 +WCS 501,Science Communication,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2776?entityType=Course&reportId=422 +WCS 562,Creative Nonfiction,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2846?entityType=Course&reportId=422 +WCS 730,From Genre to Style and Back: Writing for PhD Students,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2353?entityType=Course&reportId=422 +WLL 102,"Language, Experience, Culture",https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2713?entityType=Course&reportId=422 +WLL 110,Introduction to Literary Studies,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2418?entityType=Course&reportId=422 +WLL 121,Introduction to Cultural Studies,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2817?entityType=Course&reportId=422 +WLL 171,Introduction to Linguistic Anthropology,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/3187?entityType=Course&reportId=422 +WLL 201,World Literature I,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2923?entityType=Course&reportId=422 +WLL 209,Introduction to Translation Studies,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2417?entityType=Course&reportId=422 +WLL 211,World Literature II: from the 18th to the 20th century,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2794?entityType=Course&reportId=422 +WLL 212,Introduction to Poetry,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2924?entityType=Course&reportId=422 +WLL 218,Renaissance in Italy and Beyond,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2925?entityType=Course&reportId=422 +WLL 235,Creative Writing: Introduction to Fiction Writing I,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2845?entityType=Course&reportId=422 +WLL 240,Introduction to the Novel,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2795?entityType=Course&reportId=422 +WLL 241,Survey of Folk Tales,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2720?entityType=Course&reportId=422 +WLL 244,Survey of Nineteenth-Century Russian Literature,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/3262?entityType=Course&reportId=422 +WLL 245,Global History of Travel and Travel Literature,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2565?entityType=Course&reportId=422 +WLL 246,Survey of Contemporary Russian Literature and Culture,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/3263?entityType=Course&reportId=422 +WLL 248,Survey of Soviet Literature and Culture,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/3264?entityType=Course&reportId=422 +WLL 250,From Page to Screen,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2821?entityType=Course&reportId=422 +WLL 254,"Superstition, Magical Realism, and Horror in Hispanic Culture",https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2822?entityType=Course&reportId=422 +WLL 257,Fan Culture in the Age of Convergence,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2823?entityType=Course&reportId=422 +WLL 261,Survey of American Literature,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2796?entityType=Course&reportId=422 +WLL 271,Language and Society,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/3188?entityType=Course&reportId=422 +WLL 274,Texts and Contexts,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2414?entityType=Course&reportId=422 +WLL 313,Dante's Inferno,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2932?entityType=Course&reportId=422 +WLL 315,Shakespeare,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2797?entityType=Course&reportId=422 +WLL 333,Literary Modernism: Tradition and Innovation,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2798?entityType=Course&reportId=422 +WLL 334,Postmodern Fiction: Forms and Messages,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2799?entityType=Course&reportId=422 +WLL 340,The Image of the East in Russophone Literature and Culture: Genesis and Meaning,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/3260?entityType=Course&reportId=422 +WLL 341,Soviet Literature as Multinational Literature,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/3261?entityType=Course&reportId=422 +WLL 342,Russian Intellectual History (1762 - 1905),https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2620?entityType=Course&reportId=422 +WLL 349,Terrible Perfection: Women in 19th-Century Russian Literature,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2415?entityType=Course&reportId=422 +WLL 351,Literature and Film of the Postcolonial World,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2820?entityType=Course&reportId=422 +WLL 359,Terror and Perfection: Soviet Female Writers and Poets,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/3259?entityType=Course&reportId=422 +WLL 360,Poetry Writing Seminar,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2458?entityType=Course&reportId=422 +WLL 363,Writing Speculative Fiction,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2931?entityType=Course&reportId=422 +WLL 375,Philosophy of Art,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2937?entityType=Course&reportId=422 +WLL 385,Postcolonial Theory and its Applications in Eurasia,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2407?entityType=Course&reportId=422 +WLL 399,Independent Study,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2718?entityType=Course&reportId=422 +WLL 400,Research Assistance in Linguistics and Literature,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2698?entityType=Course&reportId=422 +WLL 401,Advanced Topics in World Literature,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2929?entityType=Course&reportId=422 +WLL 412,Poetic Personae,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2930?entityType=Course&reportId=422 +WLL 440,Oral Epic in Central Asia,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2719?entityType=Course&reportId=422 +WLL 441,Utopias in Russian and Soviet Literature,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2416?entityType=Course&reportId=422 +WLL 442,Contemporary Russian and Russophone Literature,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/3258?entityType=Course&reportId=422 +WLL 450,Laughter,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2716?entityType=Course&reportId=422 +WLL 451,The Call of the Evil: Unmasking the Villain(ness) in Popular Culture and Literature,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2824?entityType=Course&reportId=422 +WLL 462,Creative Nonfiction,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2926?entityType=Course&reportId=422 +WLL 465,Creative Writing: Autofiction,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2853?entityType=Course&reportId=422 +WLL 469,Independent Study in Creative Writing,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2717?entityType=Course&reportId=422 +WLL 498,"Languages, Linguistics, and Literatures Capstone I",https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2699?entityType=Course&reportId=422 +WLL 499,"Languages, Linguistics, and Literatures Capstone II",https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2825?entityType=Course&reportId=422 +WLL 501,Advanced Topics in World Literature,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2927?entityType=Course&reportId=422 +WLL 512,Poetic Personae,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2928?entityType=Course&reportId=422 +WLL 540,Oral Epic in Central Asia,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2721?entityType=Course&reportId=422 +WLL 541,Utopias in Russian and Soviet Literature,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2413?entityType=Course&reportId=422 +WLL 542,Contemporary Russian and Russophone Literature,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/3257?entityType=Course&reportId=422 +WLL 550,Laughter,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2723?entityType=Course&reportId=422 +WLL 551,The Call of the Evil: Unmasking the Villain(ness) in Popular Culture and Literature,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2826?entityType=Course&reportId=422 +WLL 562,Creative Nonfiction,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2933?entityType=Course&reportId=422 +WLL 712,Poetic Personae,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2934?entityType=Course&reportId=422 +WLL 740,Oral Epic in Central Asia,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2722?entityType=Course&reportId=422 +WLL 741,Utopias in Russian and Soviet Literature,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2412?entityType=Course&reportId=422 +WLL 751,The Call of the Evil: Unmasking the Villain(ness) in Popular Culture and Literature,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/2827?entityType=Course&reportId=422 +ZCOM 040,Academic Communications for Graduate Studies I,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/7?entityType=Course&reportId=422 +ZCOM 041,Academic Communications for Graduate Studies II,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/8?entityType=Course&reportId=422 +ZLIN 062,Linear Algebra for Graduate Studies,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/14?entityType=Course&reportId=422 +ZRDG 030,Academic Reading for Graduate Studies I,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/9?entityType=Course&reportId=422 +ZRDG 031,Academic Reading for Graduate Studies II,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/10?entityType=Course&reportId=422 +ZSTA 060,Foundational Statistics,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/13?entityType=Course&reportId=422 +ZWRT 020,Academic Writing for Graduate Studies I,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/11?entityType=Course&reportId=422 +ZWRT 021,Academic Writing for Graduate Studies II,https://nukz.curriqunet.com/DynamicReports/AllFieldsReportByEntity/12?entityType=Course&reportId=422 diff --git a/web/public/favicon.svg b/web/public/favicon.svg new file mode 100644 index 00000000..0c279bdd --- /dev/null +++ b/web/public/favicon.svg @@ -0,0 +1,17 @@ + + + + + + + + + + + + + + + + + diff --git a/web/scripts/generate-api-types.mjs b/web/scripts/generate-api-types.mjs new file mode 100644 index 00000000..6e4f7736 --- /dev/null +++ b/web/scripts/generate-api-types.mjs @@ -0,0 +1,99 @@ +#!/usr/bin/env node +/** + * Generates src/api/schema.d.ts from the backend's OpenAPI document. + * + * The backend only serves /api/openapi.json when IS_DEBUG=true (the local + * Docker default) — see backend/main.py. There is no schema endpoint in + * production, which is fine: this is a build-time artifact and the generated + * file is committed. + * + * pnpm api:generate write src/api/schema.d.ts + * pnpm api:check fail if the committed file is out of date + * + * openapi-typescript builds its output through the TypeScript compiler's + * ts.factory AST API, which TypeScript 7 does not expose — importing it under + * TS 7 throws immediately. Since its output is plain text, the generator runs + * in an isolated environment pinned to TS 6 rather than the project compiler. + * Nothing about the emitted .d.ts depends on which version produced it. + */ +import { execFile } from "node:child_process" +import { readFile, writeFile, mkdir } from "node:fs/promises" +import { dirname, resolve } from "node:path" +import { fileURLToPath } from "node:url" +import { promisify } from "node:util" + +const execFileAsync = promisify(execFile) + +const here = dirname(fileURLToPath(import.meta.url)) +const OUT = resolve(here, "../src/api/schema.d.ts") +const SOURCE = process.env.OPENAPI_URL ?? "http://localhost/api/openapi.json" +const check = process.argv.includes("--check") + +// Pinned so regeneration is reproducible across machines and CI. +const GENERATOR = "openapi-typescript@7.13.0" +const GENERATOR_COMPILER = "typescript@6.0.3" + +const BANNER = `/** + * GENERATED FILE — DO NOT EDIT. + * Regenerate with \`pnpm api:generate\` from an offline backend OpenAPI export. + */ + +` + +async function generate() { + const { stdout } = await execFileAsync( + "pnpm", + [ + "dlx", + "--package", + GENERATOR_COMPILER, + "--package", + GENERATOR, + "openapi-typescript", + SOURCE, + ], + { maxBuffer: 64 * 1024 * 1024 } + ) + + // pnpm dlx prefixes resolution progress on stdout in some versions; keep + // everything from the generated banner onward. + const start = stdout.indexOf("/**") + if (start === -1) { + throw new Error( + `Generator produced no schema output:\n${stdout.slice(0, 500)}` + ) + } + return BANNER + stdout.slice(start) +} + +async function main() { + let next + try { + next = await generate() + } catch (cause) { + throw new Error( + `Could not generate types from ${SOURCE}.\n` + + `Export the backend schema and set OPENAPI_URL to its path.`, + { cause } + ) + } + + if (check) { + const current = await readFile(OUT, "utf8").catch(() => null) + if (current !== next) { + console.error( + "src/api/schema.d.ts is out of date with the backend schema.\n" + + "Run `pnpm api:generate` and commit the result." + ) + process.exit(1) + } + console.log("src/api/schema.d.ts is up to date.") + return + } + + await mkdir(dirname(OUT), { recursive: true }) + await writeFile(OUT, next) + console.log(`Wrote ${OUT}`) +} + +await main() diff --git a/web/src/api/client.ts b/web/src/api/client.ts new file mode 100644 index 00000000..63b720c5 --- /dev/null +++ b/web/src/api/client.ts @@ -0,0 +1,93 @@ +import createClient, { type Middleware } from "openapi-fetch" + +import type { paths } from "@/api/schema" + +/** + * Auth is cookie-based and same-origin: nginx proxies /api to FastAPI, and the + * access/refresh/app tokens are httpOnly cookies the browser sends on its own. + * There is no Authorization header to attach and no token for the client to + * hold. `credentials: "include"` keeps that working when the dev server and the + * API are not literally the same origin. + */ +export const api = createClient({ + baseUrl: "/api", + credentials: "include", +}) + +/** A non-2xx response from the API. */ +export class ApiError extends Error { + readonly status: number + /** FastAPI's error body: `{"detail": "..."}` or a 422 validation array. */ + readonly detail: unknown + + constructor(status: number, detail: unknown) { + super(`API request failed with ${status}: ${describe(detail)}`) + this.name = "ApiError" + this.status = status + this.detail = detail + } + + /** True when the session is missing or expired. */ + get isUnauthorized() { + return this.status === 401 + } + + /** True when the user is known but not allowed to do this. */ + get isForbidden() { + return this.status === 403 + } +} + +function isRecord(value: unknown): value is Record { + return typeof value === "object" && value !== null +} + +function describe(detail: unknown): string { + if (typeof detail === "string") return detail + + if (isRecord(detail) && "detail" in detail) { + const inner = detail.detail + if (typeof inner === "string") return inner + + // FastAPI validation errors are [{loc, msg, type}, ...]. + if (Array.isArray(inner)) { + const messages: unknown[] = inner + return messages + .map((issue) => + isRecord(issue) && typeof issue.msg === "string" + ? issue.msg + : JSON.stringify(issue) + ) + .join("; ") + } + } + + return JSON.stringify(detail) +} + +/** + * openapi-fetch returns `{ data, error }` rather than throwing. React Query + * needs a rejected promise to drive its error states, so unwrap at the seam: + * every hook calls this and gets a value or an ApiError. + */ +export async function unwrap( + promise: Promise<{ data?: T; error?: unknown; response: Response }> +): Promise { + const { data, error, response } = await promise + if (error !== undefined || !response.ok) { + throw new ApiError(response.status, error) + } + // 204/205 carry no body by design, and callers of those endpoints type the + // result as void — so widening undefined to T here is intentional. + // oxlint-disable-next-line typescript/no-unsafe-type-assertion + return data as T +} + +/** Adds credentials to every request, including ones openapi-fetch retries. */ +const credentialsMiddleware: Middleware = { + onRequest({ request }) { + return new Request(request, { credentials: "include" }) + }, +} + +api.use(credentialsMiddleware) diff --git a/web/src/api/errors.ts b/web/src/api/errors.ts new file mode 100644 index 00000000..64fc8bb2 --- /dev/null +++ b/web/src/api/errors.ts @@ -0,0 +1,77 @@ +import { ApiError } from "@/api/client" + +/** + * A message safe to put in front of a user for a failed API call. + * + * The old app open-coded this twice — once in `opportunity-form.tsx` and once + * in `sg-members-management.tsx` — as ~70 lines apiece of re-reading the + * `Response` body and re-parsing JSON that the fetch layer had already parsed. + * Both copies rendered whatever `detail` came back, whatever the status. + * + * This one follows the rule established in + * `features/courses/registrar-errors.ts`: **never render a 5xx `detail`.** + * FastAPI runs with `debug=True` locally, so a 500 body is a Python traceback; + * in production it is still an internal description written for developers. + * A 4xx detail is the opposite — deliberate, addressed to the caller, and + * usually the only thing that explains what to fix. + */ +export function apiErrorMessage(error: unknown, fallback: string): string { + if (!(error instanceof ApiError)) { + // Network failures, aborted requests and thrown strings all land here. + return error instanceof Error && error.message.trim() + ? error.message + : fallback + } + + if (error.status >= 500) return fallback + + return extractDetail(error.detail) ?? fallback +} + +/** True when the failure is the session having gone away. */ +export function isUnauthorized(error: unknown): boolean { + return error instanceof ApiError && error.isUnauthorized +} + +function isRecord(value: unknown): value is Record { + return typeof value === "object" && value !== null +} + +/** + * FastAPI answers with `{"detail": "..."}` for a raised HTTPException and with + * `{"detail": [{loc, msg, type}, ...]}` for a request that failed validation. + * Pydantic's `msg` is short and readable ("String should have at most 75 + * characters"); `loc` names the field, which the form has already labelled, so + * only `msg` is worth showing. + */ +function extractDetail(body: unknown): string | null { + const detail = isRecord(body) && "detail" in body ? body.detail : body + + if (typeof detail === "string") { + const trimmed = detail.trim() + return trimmed === "" ? null : trimmed + } + + if (Array.isArray(detail)) { + const issues: unknown[] = detail + const messages = issues + .map((issue) => + isRecord(issue) && typeof issue.msg === "string" ? issue.msg : null + ) + .filter((message) => message !== null) + return messages.length > 0 ? messages.join(". ") : null + } + + /** + * `{"code": "...", "message": "..."}`, which the planner raises on its 409s + * so the caller can branch on `code` while still having something to show. + * Only `message` is for a person; `code` is for a machine and must not reach + * the screen. + */ + if (isRecord(detail) && typeof detail.message === "string") { + const trimmed = detail.message.trim() + return trimmed === "" ? null : trimmed + } + + return null +} diff --git a/web/src/api/query-keys.ts b/web/src/api/query-keys.ts new file mode 100644 index 00000000..7f18bc83 --- /dev/null +++ b/web/src/api/query-keys.ts @@ -0,0 +1,89 @@ +/** + * Every query key in the app is built here. + * + * The previous app had 88 inline `queryKey:` literals with no shared + * convention — ["campusCurrent","communities"] next to ["grade-terms"] next to + * ["sg-members","list"] — which made targeted invalidation guesswork. Keys are + * hierarchical: invalidating `qk.events.all()` clears every events query, + * including lists and details. + */ +export const qk = { + session: () => ["session"] as const, + + events: { + all: () => ["events"] as const, + list: (filters: Record) => + ["events", "list", filters] as const, + detail: (id: number) => ["events", "detail", id] as const, + }, + + communities: { + all: () => ["communities"] as const, + list: (filters: Record) => + ["communities", "list", filters] as const, + detail: (id: number) => ["communities", "detail", id] as const, + mine: () => ["communities", "mine"] as const, + }, + + announcements: { + all: () => ["announcements"] as const, + bundle: () => ["announcements", "bundle"] as const, + telegram: () => ["announcements", "telegram"] as const, + }, + + opportunities: { + all: () => ["opportunities"] as const, + list: (filters: Record) => + ["opportunities", "list", filters] as const, + detail: (id: number) => ["opportunities", "detail", id] as const, + }, + + courses: { + all: () => ["courses"] as const, + catalog: (filters: Record) => + ["courses", "catalog", filters] as const, + registered: () => ["courses", "registered"] as const, + schedule: () => ["courses", "schedule"] as const, + templates: (courseId: number) => + ["courses", "templates", courseId] as const, + requirements: (year: string, name: string, type: string) => + ["courses", "degree-requirements", year, name, type] as const, + semesters: () => ["courses", "semesters"] as const, + gradeTerms: () => ["courses", "grade-terms"] as const, + grades: (filters: Record) => + ["courses", "grades", filters] as const, + degreeAudit: () => ["courses", "degree-audit"] as const, + /** + * One key per plan. A student can keep several schedule variants, and + * caching them under a single key would show the previous plan's courses + * for a frame after switching — the plans differ in exactly the data the + * grid draws. + */ + planner: (scheduleId?: number | null) => + ["courses", "planner", scheduleId ?? "default"] as const, + /** The list of plans, without their courses. */ + plannerPlans: () => ["courses", "planner-plans"] as const, + }, + + sgotinish: { + all: () => ["sgotinish"] as const, + tickets: (filters: Record) => + ["sgotinish", "tickets", filters] as const, + ticket: (id: number) => ["sgotinish", "tickets", id] as const, + messages: (conversationId: number) => + ["sgotinish", "messages", conversationId] as const, + members: () => ["sgotinish", "members"] as const, + list: (filters: Record) => + ["sgotinish", "tickets", "list", filters] as const, + departments: () => ["sgotinish", "departments"] as const, + }, + + notifications: { + all: () => ["notifications"] as const, + list: (filters: Record) => + ["notifications", "list", filters] as const, + }, + + search: (keyword: string, storageName: string) => + ["search", storageName, keyword] as const, +} as const diff --git a/web/src/api/schema.d.ts b/web/src/api/schema.d.ts new file mode 100644 index 00000000..b8cdc73b --- /dev/null +++ b/web/src/api/schema.d.ts @@ -0,0 +1,6970 @@ +/** + * GENERATED FILE — DO NOT EDIT. + * Regenerate with `pnpm api:generate` from an offline backend OpenAPI export. + */ + +/** + * This file was auto-generated by openapi-typescript. + * Do not make direct changes to the file. + */ + +export interface paths { + "/login": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** Login */ + get: operations["login_login_get"]; + put?: never; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/auth/callback": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** Auth Callback */ + get: operations["auth_callback_auth_callback_get"]; + put?: never; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/connect-tg": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + get?: never; + put?: never; + /** + * Bind Tg + * @description Issue a Telegram deeplink binding the caller's own account to a chat. + */ + post: operations["bind_tg_connect_tg_post"]; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/refresh-token": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + get?: never; + put?: never; + /** Refresh Token */ + post: operations["refresh_token_refresh_token_post"]; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/me": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** Get Current User */ + get: operations["get_current_user_me_get"]; + put?: never; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/logout": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** Logout */ + get: operations["logout_logout_get"]; + put?: never; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/communities": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** + * Get Communities + * @description Retrieves a paginated list of communities with flexible filtering. + */ + get: operations["get_communities_communities_get"]; + put?: never; + /** + * Add Community + * @description Create a new community. Any registered user can create communities. + * + * **Access Policy:** + * - Any registered user can create communities + * - Users can only create communities for themselves (head must be "me" or their own sub) + */ + post: operations["add_community_communities_post"]; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/communities/{community_id}": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** + * Get Community + * @description Retrieves a specific community by ID. + */ + get: operations["get_community_communities__community_id__get"]; + put?: never; + post?: never; + /** + * Delete Community + * @description Deletes a community. Admin only. + */ + delete: operations["delete_community_communities__community_id__delete"]; + options?: never; + head?: never; + /** + * Update Community + * @description Updates fields of an existing community. Head or admin only. + */ + patch: operations["update_community_communities__community_id__patch"]; + trace?: never; + }; + "/events": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** + * Get Events + * @description Retrieves a paginated list of events with flexible filtering. + * + * **Access Policy:** + * - Anyone (including guests) can view: Approved, Cancelled + * - Event creator or admin can view all statuses for their own events + * - For users viewing events they don't own: + * - Must explicitly specify status in {approved, cancelled} + */ + get: operations["get_events_events_get"]; + put?: never; + /** + * Add Event + * @description Creates a new event. + * + * **Access Policy:** + * - Any authenticated user can create events for themselves + * - Admin can create events with any configuration + * + * **Data Enrichment:** + * - `status`: approved + * - `tag`: regular (admins can change later) + */ + post: operations["add_event_events_post"]; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/events/{event_id}": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** + * Get Event + * @description Retrieves a specific event by ID. + */ + get: operations["get_event_events__event_id__get"]; + put?: never; + post?: never; + /** + * Delete Event + * @description Deletes a specific event. + * + * **Access Policy:** + * - Event creator or admin + */ + delete: operations["delete_event_events__event_id__delete"]; + options?: never; + head?: never; + /** + * Update Event + * @description Updates fields of an existing event. + * + * **Access Policy:** + * - Admin can update any field + * - Event creator can update most fields except tag + */ + patch: operations["update_event_events__event_id__patch"]; + trace?: never; + }; + "/profile": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** Get Profile */ + get: operations["get_profile_profile_get"]; + put?: never; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/search/": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** + * Full Search + * @description Full search implementation returning complete entity details + */ + get: operations["full_search_search__get"]; + put?: never; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/bucket/upload-url": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + get?: never; + put?: never; + /** + * Generate Upload Url + * @description Generates pre-signed URLs for direct upload to Google Cloud Storage bucket. + */ + post: operations["generate_upload_url_bucket_upload_url_post"]; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/bucket/gcs-hook": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + get?: never; + put?: never; + /** + * Gcs Webhook + * @description Processes GCS notifications from Pub/Sub and upserts media records. + */ + post: operations["gcs_webhook_bucket_gcs_hook_post"]; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/bucket/local-upload/{bucket}/{full_path}": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + get?: never; + /** + * Local Upload Proxy + * @description Dev-only upload proxy: accepts PUT body and uploads to the emulator via storage client. + */ + put: operations["local_upload_proxy_bucket_local_upload__bucket___full_path__put"]; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/bucket/local-download/{bucket}/{full_path}": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** + * Local Download Proxy + * @description Dev-only download proxy: reads from emulator via storage client and returns bytes. + */ + get: operations["local_download_proxy_bucket_local_download__bucket___full_path__get"]; + put?: never; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/webhook": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + get?: never; + put?: never; + /** + * Webhook + * @description Webhook for the bot. Receives updates from Telegram and processes them. + */ + post: operations["webhook_webhook_post"]; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/registered_courses/sync": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + get?: never; + put?: never; + /** + * Sync Courses From Registrar + * @description Syncs courses from the university registrar for the authenticated student. + * + * **Access Policy:** + * - Students can only sync their own courses + * + * **Parameters:** + * - `data`: Registrar credentials (password) + * + * **Returns:** + * - List of synced courses with total count + * + * **Process:** + * 1. Fetches student's schedule from registrar + * 2. Determines current semester based on current date + * 3. For each course in the schedule: + * - Checks if course exists in local database + * - If not, fetches course details from registrar and creates it + * - Creates StudentCourse registration if not already registered + */ + post: operations["sync_courses_from_registrar_registered_courses_sync_post"]; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/registered_courses/sync/pdf": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + get?: never; + put?: never; + /** + * Sync Courses From Schedule Pdf + * @description Sync registered courses from an uploaded registrar personal schedule PDF. + */ + post: operations["sync_courses_from_schedule_pdf_registered_courses_sync_pdf_post"]; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/registered_courses": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** + * Get Registered Courses + * @description Retrieves all courses registered by the authenticated student. + * + * **Access Policy:** + * - Students can only view their own registered courses + * - Admin can view any student's registered courses + * + * **Returns:** + * - List of registered courses with course details, items, and class averages + */ + get: operations["get_registered_courses_registered_courses_get"]; + put?: never; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/registered_courses/schedule": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** + * Get Registered Courses Schedule + * @description Return the most recently synced registrar schedule for the authenticated user. + */ + get: operations["get_registered_courses_schedule_registered_courses_schedule_get"]; + put?: never; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/registered_courses/schedule/google": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + get?: never; + put?: never; + /** + * Export Registered Schedule To Google Calendar + * @description Export the student's latest registrar schedule to Google Calendar. + */ + post: operations["export_registered_schedule_to_google_calendar_registered_courses_schedule_google_post"]; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/registered_courses/schedule/ics": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** + * Export Registered Schedule To Ics + * @description Download the student's latest registrar schedule as an iCalendar (.ics) file. + * Works with Apple Calendar, Outlook, and other apps that import .ics. + */ + get: operations["export_registered_schedule_to_ics_registered_courses_schedule_ics_get"]; + put?: never; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/course_items": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + get?: never; + put?: never; + /** + * Add Course Item + * @description Adds a new course item (assignment, exam, etc.) to a registered course. + * + * **Access Policy:** + * - Students can only add items to their own registered courses + * - Admin can add items to any course + * + * **Parameters:** + * - `course_item_data`: Course item data including student_course_id, name, scores, etc. + * + * **Returns:** + * - Created course item with all details + */ + post: operations["add_course_item_course_items_post"]; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/course_items/{item_id}": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + get?: never; + put?: never; + post?: never; + /** + * Delete Course Item + * @description Deletes a specific course item. + * + * **Access Policy:** + * - Students can only delete items from their own registered courses + * - Admin can delete any course item + * + * **Parameters:** + * - `item_id`: ID of the course item to delete + * + * **Returns:** + * - HTTP 204 No Content on successful deletion + */ + delete: operations["delete_course_item_course_items__item_id__delete"]; + options?: never; + head?: never; + /** + * Update Course Item + * @description Updates an existing course item. + * + * **Access Policy:** + * - Students can only update items in their own registered courses + * - Admin can update any course item + * + * **Parameters:** + * - `item_id`: ID of the course item to update + * - `item_update`: Updated course item data (partial update) + * + * **Returns:** + * - Updated course item with all details + */ + patch: operations["update_course_item_course_items__item_id__patch"]; + trace?: never; + }; + "/courses": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** + * Get Courses + * @description Retrieves a paginated list of all courses with optional filtering and search. + * + * **Access Policy:** + * - Anyone can view courses (no authentication required) + * + * **Parameters:** + * - `page`: Page number to retrieve (default: 1, minimum: 1) + * - `size`: Number of courses per page (default: 20, max: 100, minimum: 1) + * - `term`: Filter courses by specific academic term (optional) + * - `keyword`: Search keyword for course code or course title (optional) + * + * **Returns:** + * - Paginated list of courses with total pages information + */ + get: operations["get_courses_courses_get"]; + put?: never; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/planner/semesters": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** List registrar semesters for planner dropdowns */ + get: operations["list_semesters_planner_semesters_get"]; + put?: never; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/planner/schedules": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** List planner schedule variants for the authenticated student */ + get: operations["list_planner_schedules_planner_schedules_get"]; + put?: never; + /** Create a new planner schedule variant */ + post: operations["create_planner_schedule_planner_schedules_post"]; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/planner/schedules/{schedule_id}/duplicate": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + get?: never; + put?: never; + /** Duplicate a planner schedule variant */ + post: operations["duplicate_planner_schedule_planner_schedules__schedule_id__duplicate_post"]; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/planner/schedules/{schedule_id}": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + get?: never; + put?: never; + post?: never; + /** Delete a planner schedule variant */ + delete: operations["delete_planner_schedule_planner_schedules__schedule_id__delete"]; + options?: never; + head?: never; + /** Rename a planner schedule variant */ + patch: operations["update_planner_schedule_planner_schedules__schedule_id__patch"]; + trace?: never; + }; + "/planner/courses/search": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** Search registrar courses for planner use */ + get: operations["search_courses_for_planner_planner_courses_search_get"]; + put?: never; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/planner/courses/refresh": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + get?: never; + put?: never; + /** Refresh sections for all planner courses */ + post: operations["refresh_all_courses_planner_courses_refresh_post"]; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/planner": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** Get planner schedule for the authenticated student */ + get: operations["get_planner_schedule_planner_get"]; + put?: never; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/planner/courses": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + get?: never; + put?: never; + /** Add a course to planner schedule */ + post: operations["add_course_planner_courses_post"]; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/planner/courses/{course_id}": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + get?: never; + put?: never; + post?: never; + /** Remove a course from planner */ + delete: operations["remove_course_planner_courses__course_id__delete"]; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/planner/courses/{course_id}/sections": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** Fetch registrar sections for a planner course */ + get: operations["get_sections_planner_courses__course_id__sections_get"]; + put?: never; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/planner/courses/{course_id}/sections/select": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + get?: never; + put?: never; + /** Select preferred sections for a course */ + post: operations["select_sections_planner_courses__course_id__sections_select_post"]; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/planner/autobuild": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + get?: never; + put?: never; + /** Automatically build schedule */ + post: operations["auto_build_planner_autobuild_post"]; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/planner/reset": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + get?: never; + put?: never; + /** Reset planner data for the student */ + post: operations["reset_planner_planner_reset_post"]; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/grades/terms": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** + * List Grade Terms + * @description Returns distinct grade report terms (e.g., FA2024, SP2025) for filtering. + */ + get: operations["list_grade_terms_grades_terms_get"]; + put?: never; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/grades": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** + * Get Grades + * @description Retrieves a paginated list of grade reports statistics with optional keyword search. + * + * **Access Policy:** + * - Anyone can view grade reports + * + * **Parameters:** + * - `size`: Number of grade reports per page (default: 20, max: 100) + * - `page`: Page number to retrieve (default: 1) + * - `keyword`: Search term for course code or course title (optional) + * - `term`: Filter results by semester/term code (optional) + * + * **Returns:** + * - List of grade reports and pagination info + * + * **Notes:** + * - When `keyword` is provided, results are ranked by Meilisearch and ordering is preserved + * - When no `keyword` is provided, results are ordered by `created_at` (newest first) + * - Returns an empty list if no results match the search + */ + get: operations["get_grades_grades_get"]; + put?: never; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/templates": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** + * List Templates + * @description List the current user's templates. Optional filter by course_id. + */ + get: operations["list_templates_templates_get"]; + put?: never; + /** + * Create Template + * @description Create a new template for a course. + * Throws 409 error if template already exists for the course and user. + */ + post: operations["create_template_templates_post"]; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/templates/{template_id}": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** + * Get Template + * @description Get a single template by id (must belong to current user). + */ + get: operations["get_template_templates__template_id__get"]; + put?: never; + post?: never; + /** + * Delete Template + * @description Delete a template. + */ + delete: operations["delete_template_templates__template_id__delete"]; + options?: never; + head?: never; + /** + * Update Template + * @description Update an existing template. + */ + patch: operations["update_template_templates__template_id__patch"]; + trace?: never; + }; + "/templates/{template_id}/import": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + get?: never; + put?: never; + /** + * Import Template Into Course + * @description Import template into a student's registered course. + */ + post: operations["import_template_into_course_templates__template_id__import_post"]; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/degree-audit/catalog": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** List available admission years and majors for degree audit */ + get: operations["list_catalog_degree_audit_catalog_get"]; + put?: never; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/degree-audit/audit/registrar": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + get?: never; + put?: never; + /** Run degree audit using registrar transcript (no file upload) */ + post: operations["audit_from_registrar_degree_audit_audit_registrar_post"]; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/degree-audit/audit/pdf": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + get?: never; + put?: never; + /** Run degree audit using PDF file */ + post: operations["audit_from_pdf_degree_audit_audit_pdf_post"]; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/degree-audit/requirements": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** Get degree requirements for a specific year and major */ + get: operations["get_degree_requirements_degree_audit_requirements_get"]; + put?: never; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/degree-audit/result": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** Get cached degree audit result for current user */ + get: operations["get_cached_result_degree_audit_result_get"]; + put?: never; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/notification": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** Get */ + get: operations["get_notification_get"]; + put?: never; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/tickets": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** + * Get Tickets + * @description Retrieves a paginated list of tickets with flexible filtering. + * + * **Access Policy:** + * - SG members and admins can view tickets they have access to + * - Regular users can only view their own tickets + * + * **Parameters:** + * - `size`: Number of tickets per page (default: 20, max: 100) + * - `page`: Page number (default: 1) + * - `category`: Filter by ticket category (optional) + * - `author_sub`: Filter by ticket author (optional) + * - If set to "me", returns the current user's tickets + * **Returns:** + * - List of tickets matching the criteria with pagination info + */ + get: operations["get_tickets_tickets_get"]; + put?: never; + /** + * Create Ticket + * @description Creates a new ticket. + * + * **Access Policy:** + * - Any authenticated user can create tickets + * - Users can only create tickets for themselves + * + * **Parameters:** + * - `ticket_data`: Ticket data including category, title, body, etc. + * + * **Returns:** + * - Created ticket with all its details + * + * **Notes:** + * - `author_sub` can be set to "me" to use the current user's sub. + */ + post: operations["create_ticket_tickets_post"]; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/tickets/{ticket_id}": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** + * Get Ticket + * @description Retrieves a single ticket by its unique ID. + * + * **Access Policy:** + * - SG members and admins can view tickets they have access to + * - Regular users can only view their own tickets + * + * **Parameters:** + * - `ticket_id`: The unique identifier of the ticket to retrieve + * + * **Returns:** + * - A detailed ticket object with all its information + */ + get: operations["get_ticket_tickets__ticket_id__get"]; + put?: never; + post?: never; + delete?: never; + options?: never; + head?: never; + /** + * Update Ticket + * @description Updates a ticket by its unique ID. + */ + patch: operations["update_ticket_tickets__ticket_id__patch"]; + trace?: never; + }; + "/tickets/by-owner-hash": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + get?: never; + put?: never; + /** Get Ticket By Owner Hash */ + post: operations["get_ticket_by_owner_hash_tickets_by_owner_hash_post"]; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/conversations": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + get?: never; + put?: never; + /** + * Create Conversation + * @description Creates a new conversation for a ticket. + * + * **Access Policy:** + * - An SG member with at least ASSIGN access can create a conversation. + * - For all SG members, only one conversation per ticket. + * - Admins can always create conversations. + * + * **Parameters:** + * - `conversation_data`: Conversation data including ticket_id, sg_member_sub + * + * **Returns:** + * - Created conversation with all its details + */ + post: operations["create_conversation_conversations_post"]; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/conversations/{conversation_id}": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + get?: never; + put?: never; + post?: never; + delete?: never; + options?: never; + head?: never; + /** + * Update Conversation + * @description Updates fields of an existing conversation. + * + * **Access Policy:** + * - Admin can update any conversation + * - SG members can update conversations they are part of + * + * **Parameters:** + * - `conversation_id`: ID of the conversation to update + * - `conversation_data`: Updated conversation data + * + * **Returns:** + * - Updated conversation with all its details + */ + patch: operations["update_conversation_conversations__conversation_id__patch"]; + trace?: never; + }; + "/messages": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** + * Get Messages + * @description Retrieves a paginated list of messages with flexible filtering. + * + * **Access Policy:** + * - SG members and admins can view all messages + * - Regular users can only view messages in conversations for their tickets + * + * **Parameters:** + * - `size`: Number of messages per page (default: 20, max: 100) + * - `page`: Page number (default: 1) + * - `conversation_id`: Filter by specific conversation (required) + * + * **Returns:** + * - List of messages matching the criteria with pagination info + */ + get: operations["get_messages_messages_get"]; + put?: never; + /** + * Create Message + * @description Creates a new message in a conversation. + * + * **Access Policy:** + * - The author of the ticket can send messages. + * - SG member with Assign or Delegate permission can send messages. + * - Admins can always send messages. + * + * **Parameters:** + * - `message_data`: Message data including conversation_id, sender_sub, body + * + * **Returns:** + * - Created message with all its details + */ + post: operations["create_message_messages_post"]; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/messages/{message_id}": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** + * Get Message + * @description Retrieves a single message by its unique ID. + * + * **Access Policy:** + * - SG members and admins can view all messages + * - Regular users can only view messages in conversations for their tickets + * + * **Parameters:** + * - `message_id`: The unique identifier of the message to retrieve + * + * **Returns:** + * - A detailed message object with all its information + */ + get: operations["get_message_messages__message_id__get"]; + put?: never; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/messages/{message_id}/read": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + get?: never; + put?: never; + /** + * Mark Message As Read + * @description Marks a message as read by the current user. + * + * **Access Policy:** + * - SG members and admins can mark any message as read + * - Regular users can only mark messages in conversations for their tickets + * + * **Parameters:** + * - `message_id`: The unique identifier of the message to mark as read + * + * **Returns:** + * - Updated message with read status + */ + post: operations["mark_message_as_read_messages__message_id__read_post"]; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/sg-delegation/departments": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** Get Departments */ + get: operations["get_departments_sg_delegation_departments_get"]; + put?: never; + /** Create Department */ + post: operations["create_department_sg_delegation_departments_post"]; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/sg-delegation/departments/{department_id}": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + get?: never; + put?: never; + post?: never; + /** Delete Department */ + delete: operations["delete_department_sg_delegation_departments__department_id__delete"]; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/sg-delegation/users": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** Get Sg Users */ + get: operations["get_sg_users_sg_delegation_users_get"]; + put?: never; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/sg-members/users": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** Search Users For Sg Management */ + get: operations["search_users_for_sg_management_sg_members_users_get"]; + put?: never; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/sg-members": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** List Sg Members */ + get: operations["list_sg_members_sg_members_get"]; + put?: never; + /** Upsert Sg Member */ + post: operations["upsert_sg_member_sg_members_post"]; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/sg-members/{target_user_sub}": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + get?: never; + put?: never; + post?: never; + /** Remove Sg Member */ + delete: operations["remove_sg_member_sg_members__target_user_sub__delete"]; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/sg-members/withdraw": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + get?: never; + put?: never; + /** Withdraw From Sg */ + post: operations["withdraw_from_sg_sg_members_withdraw_post"]; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/tickets/{ticket_id}/delegate": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + get?: never; + put?: never; + /** Delegate Ticket Access */ + post: operations["delegate_ticket_access_tickets__ticket_id__delegate_post"]; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/announcements/telegram": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** + * Get Announcements From Telegram + * @description Get latest announcements from the public Telegram channel. + */ + get: operations["get_announcements_from_telegram_announcements_telegram_get"]; + put?: never; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/announcements/bundle": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** + * Get Announcements Bundle Route + * @description Single endpoint for the announcements landing page to reduce initial request fan-out. + * + * Defaults: + * - events: page=1 size=5 (approved + upcoming) + * - recruitment_events: page=1 size=5 (approved + upcoming + type=recruitment) + */ + get: operations["get_announcements_bundle_route_announcements_bundle_get"]; + put?: never; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/opportunities": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** List Opportunities */ + get: operations["list_opportunities_opportunities_get"]; + put?: never; + /** Create Opportunity */ + post: operations["create_opportunity_opportunities_post"]; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/opportunities/{id}": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** Get Opportunity */ + get: operations["get_opportunity_opportunities__id__get"]; + put?: never; + post?: never; + /** Delete Opportunity */ + delete: operations["delete_opportunity_opportunities__id__delete"]; + options?: never; + head?: never; + /** Update Opportunity */ + patch: operations["update_opportunity_opportunities__id__patch"]; + trace?: never; + }; + "/opportunities/{id}/calendar": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + get?: never; + put?: never; + /** + * Add Opportunity To Calendar + * @description Add a single opportunity to the user's Google Calendar (all-day event on the deadline). + */ + post: operations["add_opportunity_to_calendar_opportunities__id__calendar_post"]; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/elections/counter/stream": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** + * Stream Election Counter + * @description Stream the number of submitted responses for the election survey. + */ + get: operations["stream_election_counter_elections_counter_stream_get"]; + put?: never; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/elections/counter": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** + * Get Election Counter + * @description Get the number of submitted responses for the election survey. + */ + get: operations["get_election_counter_elections_counter_get"]; + put?: never; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; +} +export type webhooks = Record; +export interface components { + schemas: { + /** + * AnnouncementsBundleResponse + * @description Aggregated response for the announcements landing page to reduce request count. + */ + AnnouncementsBundleResponse: { + events: components["schemas"]["ListEventResponse"]; + recruitment_events: components["schemas"]["ListEventResponse"]; + }; + /** AuditProgramResult */ + AuditProgramResult: { + /** Name */ + name: string; + /** Type */ + type: string; + /** Results */ + results: components["schemas"]["AuditRequirementResult"][]; + summary?: components["schemas"]["AuditSummary"] | null; + /** + * Warnings + * @default [] + */ + warnings: string[]; + }; + /** AuditRequestPDF */ + AuditRequestPDF: { + /** Year */ + year: string; + /** Majors */ + majors: string[]; + /** + * Minors + * @default [] + */ + minors: string[]; + /** + * Tc Mappings + * @default [] + */ + tc_mappings: components["schemas"]["TCMapping"][]; + /** + * Pdf File + * @description Base64-encoded PDF payload (decoded into bytes by Pydantic). + */ + pdf_file: string; + }; + /** AuditRequestRegistrar */ + AuditRequestRegistrar: { + /** Year */ + year: string; + /** Majors */ + majors: string[]; + /** + * Minors + * @default [] + */ + minors: string[]; + /** Username */ + username: string; + /** Password */ + password: string; + /** + * Tc Mappings + * @default [] + */ + tc_mappings: components["schemas"]["TCMapping"][]; + }; + /** AuditRequirementResult */ + AuditRequirementResult: { + /** Course Code */ + course_code: string; + /** Course Name */ + course_name: string; + /** Credits Required */ + credits_required: string; + /** Min Grade */ + min_grade: string; + /** Status */ + status: string; + /** Used Courses */ + used_courses: string; + /** Credits Applied */ + credits_applied: string; + /** Credits Remaining */ + credits_remaining: string; + /** Note */ + note: string; + }; + /** AuditResponse */ + AuditResponse: { + /** Year */ + year: string; + /** + * Majors + * @default [] + */ + majors: string[]; + /** + * Minors + * @default [] + */ + minors: string[]; + /** + * Audits + * @default [] + */ + audits: components["schemas"]["AuditProgramResult"][]; + /** + * Unmapped Tc Courses + * @default [] + */ + unmapped_tc_courses: components["schemas"]["TCCourse"][]; + /** + * Csv Base64 + * @description Optional base64 CSV of the audit results + */ + csv_base64?: string | null; + }; + /** AuditSummary */ + AuditSummary: { + /** + * Total Required + * @description Total credits required by the plan + */ + total_required: string; + /** + * Total Applied + * @description Credits applied toward requirements + */ + total_applied: string; + /** + * Total Remaining + * @description Credits still needed + */ + total_remaining: string; + /** + * Total Taken + * @description Credits taken on transcript + */ + total_taken: string; + }; + /** AutoBuildCourseResult */ + AutoBuildCourseResult: { + /** Course Id */ + course_id: number; + /** Registrar Course Id */ + registrar_course_id: string; + /** Course Code */ + course_code: string; + /** Selected Section Id */ + selected_section_id: number | null; + /** Selected Section Code */ + selected_section_code: string | null; + /** Selected Section Ids */ + selected_section_ids?: number[]; + }; + /** BaseCourseItem */ + BaseCourseItem: { + /** Id */ + id: number; + /** Student Course Id */ + student_course_id: number; + /** Item Name */ + item_name: string; + /** Total Weight Pct */ + total_weight_pct: number | null; + /** Obtained Score */ + obtained_score: number | null; + /** Max Score */ + max_score: number | null; + /** + * Created At + * Format: date-time + */ + created_at: string; + /** + * Updated At + * Format: date-time + */ + updated_at: string; + }; + /** BaseCourseSchema */ + BaseCourseSchema: { + /** Id */ + id: number; + /** Registrar Id */ + registrar_id: number; + /** Course Code */ + course_code: string; + /** Pre Req */ + pre_req: string | null; + /** Anti Req */ + anti_req: string | null; + /** Co Req */ + co_req: string | null; + /** Level */ + level: string; + /** School */ + school: string; + /** Description */ + description: string | null; + /** Department */ + department: string | null; + /** Title */ + title: string | null; + /** Credits */ + credits: number | null; + /** Term */ + term: string | null; + /** + * Created At + * Format: date-time + */ + created_at: string; + /** + * Updated At + * Format: date-time + */ + updated_at: string; + }; + /** BaseCourseTemplate */ + BaseCourseTemplate: { + /** Id */ + id: number; + /** Course Id */ + course_id: number; + /** Student Sub */ + student_sub: string; + /** + * Created At + * Format: date-time + */ + created_at: string; + /** + * Updated At + * Format: date-time + */ + updated_at: string; + }; + /** BaseGradeReportSchema */ + BaseGradeReportSchema: { + /** Id */ + id: number; + /** Course Code */ + course_code: string; + /** Course Title */ + course_title: string | null; + /** Section */ + section: string | null; + /** Term */ + term: string | null; + /** Grades Count */ + grades_count: number | null; + /** Avg Gpa */ + avg_gpa: number | null; + /** Std Dev */ + std_dev: number | null; + /** Median Gpa */ + median_gpa: number | null; + /** Pct A */ + pct_A: number | null; + /** Pct B */ + pct_B: number | null; + /** Pct C */ + pct_C: number | null; + /** Pct D */ + pct_D: number | null; + /** Pct F */ + pct_F: number | null; + /** Pct P */ + pct_P: number | null; + /** Pct I */ + pct_I: number | null; + /** Pct Au */ + pct_AU: number | null; + /** Pct W Aw */ + pct_W_AW: number | null; + /** Letters Count */ + letters_count: number | null; + /** Faculty */ + faculty: string | null; + /** + * Created At + * Format: date-time + */ + created_at: string; + /** + * Updated At + * Format: date-time + */ + updated_at: string; + }; + /** + * Message Read Status Base Schema + * @description Base schema for all message read status-related operations + */ + BaseMessageReadStatus: { + /** + * Message Id + * @description ID of the associated message + */ + message_id: number; + /** + * User Sub + * @description User identifier + */ + user_sub: string; + /** + * Read At + * Format: date-time + * @description Timestamp when message was read + */ + read_at: string; + }; + /** BaseNotification */ + BaseNotification: { + /** Id */ + id: number; + /** Title */ + title: string; + /** Message */ + message: string; + notification_source: components["schemas"]["EntityType"]; + /** Receiver Sub */ + receiver_sub: string; + /** Tg Id */ + tg_id: number; + type: components["schemas"]["NotificationType"]; + /** Url */ + url?: string | null; + /** + * Created At + * Format: date-time + */ + created_at: string; + }; + /** BaseTemplateItem */ + BaseTemplateItem: { + /** Id */ + id: number; + /** Template Id */ + template_id: number; + /** Item Name */ + item_name: string; + /** Total Weight Pct */ + total_weight_pct: number | null; + /** + * Created At + * Format: date-time + */ + created_at: string; + /** + * Updated At + * Format: date-time + */ + updated_at: string; + }; + /** CatalogResponse */ + CatalogResponse: { + /** Years */ + years: components["schemas"]["CatalogYear"][]; + /** + * Minors + * @default [] + */ + minors: string[]; + }; + /** CatalogYear */ + CatalogYear: { + /** Year */ + year: string; + /** Majors */ + majors: string[]; + }; + /** + * CommunityCategory + * @enum {string} + */ + CommunityCategory: "academic" | "professional" | "recreational" | "cultural" | "sports" | "social" | "art"; + /** CommunityCreateRequest */ + CommunityCreateRequest: { + /** + * Name + * @description The name of the community + * @example NU Fencing Club + */ + name: string; + /** + * @description The type of the community + * @example club + */ + type: components["schemas"]["CommunityType"]; + /** + * @description The category of the community + * @example academic + */ + category: components["schemas"]["CommunityCategory"]; + /** + * Email + * @description The email of the community + * @example nufencingclub@gmail.com + */ + email?: string | null; + /** + * Description + * @description The description of the community + * @example We are a club that does fencing + */ + description: string; + /** + * Established + * Format: date + * @description The date the community was established + * @example 2025-01-01 + */ + established: string; + /** + * Head + * @description The head of the community (user_sub) + */ + head: string; + /** + * Telegram Url + * @description The Telegram URL of the community + * @example https://t.me/nufencingclub + */ + telegram_url?: string | null; + /** + * Instagram Url + * @description The Instagram URL of the community + * @example https://www.instagram.com/nufencingclub + */ + instagram_url?: string | null; + }; + /** CommunityResponse */ + CommunityResponse: { + /** Id */ + id: number; + /** Name */ + name: string; + type: components["schemas"]["CommunityType"]; + category: components["schemas"]["CommunityCategory"]; + /** Email */ + email?: string | null; + /** Verified */ + verified: boolean; + /** Description */ + description: string; + /** + * Established + * Format: date + */ + established: string; + /** Head */ + head: string; + /** Telegram Url */ + telegram_url?: string | null; + /** Instagram Url */ + instagram_url?: string | null; + /** + * Created At + * Format: date-time + */ + created_at: string; + /** + * Updated At + * Format: date-time + */ + updated_at: string; + head_user: components["schemas"]["ShortUserResponse"]; + /** + * Media + * @default [] + */ + media: components["schemas"]["MediaResponse"][]; + /** + * @default { + * "can_edit": false, + * "can_delete": false, + * "editable_fields": [] + * } + */ + permissions: components["schemas"]["ResourcePermissions"]; + }; + /** + * CommunityType + * @enum {string} + */ + CommunityType: "club" | "university" | "organization"; + /** CommunityUpdateRequest */ + CommunityUpdateRequest: { + /** + * Name + * @description The name of the community + * @example NU Fencing Club + */ + name?: string | null; + /** + * Email + * @description The email of the community + * @example nufencingclub@gmail.com + */ + email?: string | null; + /** + * Established + * @description The date the community was established + * @example 2025-01-01 + */ + established?: string | null; + /** + * Description + * @description The description of the community + * @example We are a club that does fencing + */ + description?: string | null; + /** + * Telegram Url + * @description The Telegram URL of the community + * @example https://t.me/nufencingclub + */ + telegram_url?: string | null; + /** + * Instagram Url + * @description The Instagram URL of the community + * @example https://www.instagram.com/nufencingclub + */ + instagram_url?: string | null; + /** + * Media Ids To Delete + * @description IDs of media attachments to delete as part of this update + */ + media_ids_to_delete?: number[] | null; + }; + /** + * ConversationCreateDTO + * @description Public schema for creating a new conversation. + */ + ConversationCreateDTO: { + /** + * Ticket Id + * @description ID of the ticket this conversation belongs to + */ + ticket_id: number; + }; + /** + * Conversation Base Schema + * @description Base schema for all conversation-related operations + */ + ConversationResponseDTO: { + /** + * Id + * @description Unique identifier of the conversation + */ + id: number; + /** + * Ticket Id + * @description ID of the associated ticket + */ + ticket_id: number; + /** + * Sg Member Sub + * @description SG member identifier + */ + sg_member_sub?: string | null; + /** @description Status of the conversation */ + status: components["schemas"]["ConversationStatus"]; + /** + * Created At + * Format: date-time + * @description Creation timestamp + */ + created_at: string; + /** @description SG member information */ + sg_member?: components["schemas"]["ShortUserResponse"] | null; + /** + * Messages Count + * @description Number of messages in the conversation + * @default 0 + */ + messages_count: number; + /** + * @description User permissions for this conversation + * @default { + * "can_edit": false, + * "can_delete": false, + * "editable_fields": [] + * } + */ + permissions: components["schemas"]["ResourcePermissions"]; + }; + /** + * ConversationStatus + * @enum {string} + */ + ConversationStatus: "active" | "archived"; + /** + * ConversationUpdateDTO + * @description Schema for updating a conversation. + */ + ConversationUpdateDTO: { + /** @description New status of the conversation */ + status?: components["schemas"]["ConversationStatus"] | null; + }; + /** CourseItemCreate */ + CourseItemCreate: { + /** + * Student Course Id + * @description ID of the student's registered course + */ + student_course_id: number; + /** + * Item Name + * @description Name of the assignment/exam + */ + item_name: string; + /** + * Total Weight Pct + * @description Weight must be between 0-100% + */ + total_weight_pct?: number | null; + /** + * Obtained Score + * @description Score must be between 0 and 99999.99 + */ + obtained_score?: number | null; + /** + * Max Score + * @description Score must be between 0 and 99999.99 + */ + max_score?: number | null; + }; + /** CourseItemUpdate */ + CourseItemUpdate: { + /** + * Item Name + * @description Name of the assignment/exam + */ + item_name?: string | null; + /** + * Total Weight Pct + * @description Weight must be between 0-100% + */ + total_weight_pct?: number | null; + /** + * Obtained Score + * @description Score must be between 0 and 99999.99 + */ + obtained_score?: number | null; + /** + * Max Score + * @description Score must be between 0 and 99999.99 + */ + max_score?: number | null; + }; + /** CurrentUserResponse */ + CurrentUserResponse: { + /** User */ + user: { + [key: string]: unknown; + }; + /** Tg Id */ + tg_id?: number | null; + }; + /** DegreeRequirement */ + DegreeRequirement: { + /** Course Code */ + course_code: string; + /** Course Name */ + course_name: string; + /** Credits Need */ + credits_need: number; + /** Min Grade */ + min_grade: string; + /** Comments */ + comments: string; + /** Options */ + options: string[]; + /** Must Haves */ + must_haves: string[]; + /** Excepts */ + excepts: string[]; + }; + /** + * DelegateAccessPayload + * @description Schema for delegating ticket access. + */ + DelegateAccessPayload: { + /** + * Target User Sub + * @description The user sub to grant access to + */ + target_user_sub: string; + /** @description The permission level to grant */ + permission: components["schemas"]["PermissionType"]; + }; + /** + * DepartmentCreatePayload + * @description Payload for creating a new department. + */ + DepartmentCreatePayload: { + /** + * Name + * @description Department name + */ + name: string; + /** + * Is Special + * @description Whether this department is special + * @default false + */ + is_special: boolean; + }; + /** + * DepartmentResponseDTO + * @description DTO for department information. + */ + DepartmentResponseDTO: { + /** Id */ + id: number; + /** Name */ + name: string; + /** + * Is Special + * @default false + */ + is_special: boolean; + }; + /** + * EducationLevel + * @enum {string} + */ + EducationLevel: "UG" | "GrM" | "PhD"; + /** + * EntityType + * @description Enum representing different types of entities (db table names). + * + * ⚠️ IMPORTANT: When adding new values to this enum: + * 1. Add the new value to this Python enum class + * 2. Create a new Alembic migration manually (alembic revision -m "add_new_entity_type") + * 3. In the migration's upgrade() function, add: + * op.execute("ALTER TYPE entity_type ADD VALUE 'your_new_value'") + * 4. Run the migration: alembic upgrade head + * + * Alembic cannot auto-detect enum value changes, so manual migration is required! + * @enum {string} + */ + EntityType: "community_events" | "communities" | "grade_reports" | "courses" | "tickets" | "messages"; + /** EventCreateRequest */ + EventCreateRequest: { + /** + * Creator Sub + * @description The creator sub of the event + * @example me + */ + creator_sub: string; + /** + * @description The policy of the event + * @example open + */ + policy: components["schemas"]["RegistrationPolicy"]; + /** + * Name + * @description The name of the event + * @example nuspace 2025 + */ + name: string; + /** + * Place + * @description The place of the event + * @example NU 3rd block, 3rd floor + */ + place: string; + /** + * Start Datetime + * Format: date-time + * @description Event start as UTC instant (ISO-8601 with offset). Naive values are Asia/Almaty. + * @example 2026-06-12T10:00:00+05:00 + */ + start_datetime: string; + /** + * End Datetime + * Format: date-time + * @description Event end as UTC instant (ISO-8601 with offset). Naive values are Asia/Almaty. + * @example 2026-06-12T12:00:00+05:00 + */ + end_datetime: string; + /** + * Description + * @description The description of the event + * @example nuspace is a community event + */ + description: string; + /** + * @description The type of the event + * @example academic + */ + type: components["schemas"]["EventType"]; + /** + * Registration Link + * @description The registration link for the event + * @example https://forms.google.com/event-registration + */ + registration_link?: string | null; + }; + /** EventResponse */ + EventResponse: { + /** Id */ + id: number; + /** Creator Sub */ + creator_sub: string; + policy: components["schemas"]["RegistrationPolicy"]; + /** Registration Link */ + registration_link?: string | null; + /** Name */ + name: string; + /** Place */ + place: string; + /** + * Start Datetime + * Format: date-time + */ + start_datetime: string; + /** + * End Datetime + * Format: date-time + */ + end_datetime: string; + /** Description */ + description: string; + type: components["schemas"]["EventType"]; + status: components["schemas"]["EventStatus"]; + tag: components["schemas"]["EventTag"]; + /** + * Created At + * Format: date-time + */ + created_at: string; + /** + * Updated At + * Format: date-time + */ + updated_at: string; + /** Media */ + media?: components["schemas"]["MediaResponse"][]; + creator: components["schemas"]["ShortUserResponse"]; + permissions?: components["schemas"]["ResourcePermissions"]; + }; + /** + * EventStatus + * @enum {string} + */ + EventStatus: "pending" | "approved" | "rejected" | "cancelled"; + /** + * EventTag + * @enum {string} + */ + EventTag: "featured" | "promotional" | "regular" | "charity"; + /** + * EventType + * @enum {string} + */ + EventType: "academic" | "professional" | "recreational" | "cultural" | "sports" | "social" | "art" | "recruitment"; + /** EventUpdateRequest */ + EventUpdateRequest: { + /** + * Name + * @description The name of the event + * @example nuspace 2025 + */ + name?: string | null; + /** + * Place + * @description The place of the event + * @example NU 3rd block, 3rd floor + */ + place?: string | null; + /** + * Start Datetime + * @description Event start as UTC instant (ISO-8601 with offset). Naive values are Asia/Almaty. + * @example 2026-06-12T10:00:00+05:00 + */ + start_datetime?: string | null; + /** + * End Datetime + * @description Event end as UTC instant (ISO-8601 with offset). Naive values are Asia/Almaty. + * @example 2026-06-12T12:00:00+05:00 + */ + end_datetime?: string | null; + /** + * Description + * @description The description of the event + * @example nuspace is a community event + */ + description?: string | null; + /** + * @description The policy of the event + * @example open + */ + policy?: components["schemas"]["RegistrationPolicy"] | null; + /** + * @description The status of the event + * @example approved + */ + status?: components["schemas"]["EventStatus"] | null; + /** + * @description The type of the event + * @example academic + */ + type?: components["schemas"]["EventType"] | null; + /** + * Registration Link + * @description The registration link for the event + * @example https://forms.google.com/event-registration + */ + registration_link?: string | null; + /** @description The tag of the event. Admin only */ + tag?: components["schemas"]["EventTag"] | null; + /** + * Media Ids To Delete + * @description IDs of media attachments to delete as part of this update + */ + media_ids_to_delete?: number[] | null; + }; + /** GoogleCalendarExportResponse */ + GoogleCalendarExportResponse: { + /** Created */ + created: number; + /** Skipped */ + skipped: number; + /** + * Missing Dates + * @default [] + */ + missing_dates: string[]; + /** + * Lookup Errors + * @default [] + */ + lookup_errors: string[]; + /** + * Google Errors + * @default [] + */ + google_errors: string[]; + }; + /** HTTPValidationError */ + HTTPValidationError: { + /** Detail */ + detail?: components["schemas"]["ValidationError"][]; + }; + /** ListBaseCourseResponse */ + ListBaseCourseResponse: { + /** Courses */ + courses: components["schemas"]["BaseCourseSchema"][]; + /** + * Total Pages + * @default 1 + */ + total_pages: number; + }; + /** ListCommunity */ + ListCommunity: { + /** Items */ + items?: components["schemas"]["CommunityResponse"][]; + /** + * Total Pages + * @default 1 + */ + total_pages: number; + /** Total */ + total: number; + /** Page */ + page: number; + /** Size */ + size: number; + /** Has Next */ + has_next: boolean; + }; + /** ListEventResponse */ + ListEventResponse: { + /** Items */ + items?: components["schemas"]["EventResponse"][]; + /** + * Total Pages + * @default 1 + */ + total_pages: number; + /** Total */ + total: number; + /** Page */ + page: number; + /** Size */ + size: number; + /** Has Next */ + has_next: boolean; + }; + /** ListGradeReportResponse */ + ListGradeReportResponse: { + /** Items */ + items?: components["schemas"]["BaseGradeReportSchema"][]; + /** + * Total Pages + * @default 1 + */ + total_pages: number; + /** Total */ + total: number; + /** Page */ + page: number; + /** Size */ + size: number; + /** Has Next */ + has_next: boolean; + }; + /** ListGradeTermsResponse */ + ListGradeTermsResponse: { + /** Terms */ + terms?: string[]; + }; + /** + * ListMessageDTO + * @description Response schema for message listings with pagination metadata. + */ + ListMessageDTO: { + /** + * Items + * @description List of messages + */ + items?: components["schemas"]["MessageResponseDTO"][]; + /** + * Total Pages + * @description Total number of pages + */ + total_pages: number; + /** + * Total + * @description Total number of messages + */ + total: number; + /** + * Page + * @description Current page number + */ + page: number; + /** + * Size + * @description Page size used for this response + */ + size: number; + /** + * Has Next + * @description Whether another page exists + */ + has_next: boolean; + }; + /** ListTemplateDTO */ + ListTemplateDTO: { + /** Templates */ + templates: components["schemas"]["TemplateResponse"][]; + /** + * Total Pages + * @default 1 + */ + total_pages: number; + }; + /** + * ListTicketDTO + * @description Response schema for ticket listings with pagination metadata. + */ + ListTicketDTO: { + /** + * Items + * @description List of tickets + */ + items?: components["schemas"]["TicketResponseDTO"][]; + /** + * Total Pages + * @description Total number of pages + */ + total_pages: number; + /** + * Total + * @description Total number of tickets + */ + total: number; + /** + * Page + * @description Current page number + */ + page: number; + /** + * Size + * @description Page size used for this response + */ + size: number; + /** + * Has Next + * @description Whether there is another page available + */ + has_next: boolean; + }; + /** + * MediaFormat + * @enum {string} + */ + MediaFormat: "banner" | "carousel" | "profile"; + /** MediaResponse */ + MediaResponse: { + /** Id */ + id: number; + /** Url */ + url: string; + /** Mime Type */ + mime_type: string; + entity_type: components["schemas"]["EntityType"]; + /** Entity Id */ + entity_id: number; + media_format: components["schemas"]["MediaFormat"]; + /** Media Order */ + media_order: number; + }; + /** + * MessageCreateDTO + * @description Schema for creating a new message. + */ + MessageCreateDTO: { + /** + * Conversation Id + * @description ID of the conversation this message belongs to + */ + conversation_id: number; + /** + * Body + * @description Content of the message + * @example Thank you for your help with this issue. + */ + body: string; + }; + /** + * Message Base Schema + * @description Base schema for all message-related operations + */ + MessageResponseDTO: { + /** + * Id + * @description Unique identifier of the message + */ + id: number; + /** + * Conversation Id + * @description ID of the associated conversation + */ + conversation_id: number; + /** + * Sender Sub + * @description Sender identifier + */ + sender_sub?: string | null; + /** + * Body + * @description Content of the message + */ + body: string; + /** + * Is From Sg Member + * @description Whether this message is from an SG member + */ + is_from_sg_member: boolean; + /** + * Sent At + * Format: date-time + * @description Timestamp when message was sent + */ + sent_at: string; + /** + * Message Read Statuses + * @description Message read status + */ + message_read_statuses?: components["schemas"]["BaseMessageReadStatus"][]; + /** + * Sender + * @description Sender of the message + */ + sender?: components["schemas"]["ShortUserResponse"] | components["schemas"]["SGUserResponse"] | null; + /** + * @description User permissions for this message + * @default { + * "can_edit": false, + * "can_delete": false, + * "editable_fields": [] + * } + */ + permissions: components["schemas"]["ResourcePermissions"]; + }; + /** + * NotificationType + * @description Enum representing different types of notifications. + * + * ⚠️ IMPORTANT: When adding new values to this enum: + * 1. Add the new value to this Python enum class + * 2. Create a new Alembic migration manually (alembic revision -m "add_new_notification_type") + * 3. In the migration's upgrade() function, add: + * op.execute("ALTER TYPE notification_type ADD VALUE 'your_new_value'") + * 4. Run the migration: alembic upgrade head + * + * Alembic cannot auto-detect enum value changes, so manual migration is required! + * @enum {string} + */ + NotificationType: "info"; + /** OpportunityCalendarResponse */ + OpportunityCalendarResponse: { + /** Created */ + created: number; + /** + * Google Errors + * @default [] + */ + google_errors: string[]; + }; + /** OpportunityCreateDto */ + OpportunityCreateDto: { + /** Name */ + name: string; + /** Description */ + description?: string | null; + /** Deadline */ + deadline?: string | null; + /** Host */ + host?: string | null; + type: components["schemas"]["OpportunityType"]; + /** + * Majors + * @default [] + */ + majors: components["schemas"]["OpportunityMajor"][]; + /** Link */ + link?: string | null; + /** Location */ + location?: string | null; + /** Funding */ + funding?: string | null; + /** + * Eligibilities + * @default [] + */ + eligibilities: components["schemas"]["OpportunityEligibilityCreateDto"][]; + }; + /** + * OpportunityEligibilityBase + * @description orm to pydantic mapping. do not use in create/update requests + */ + OpportunityEligibilityBase: { + /** Id */ + id: number; + education_level: components["schemas"]["EducationLevel"]; + /** Year */ + year: number | null; + }; + /** OpportunityEligibilityCreateDto */ + OpportunityEligibilityCreateDto: { + education_level: components["schemas"]["EducationLevel"]; + /** Year */ + year: number | null; + }; + /** + * OpportunityEligibilityUpdateDto + * @description update request of eligibility perform full replacement + */ + OpportunityEligibilityUpdateDto: { + education_level?: components["schemas"]["EducationLevel"] | null; + /** Year */ + year?: number | null; + }; + /** OpportunityListResponse */ + OpportunityListResponse: { + /** Items */ + items: components["schemas"]["OpportunityResponseDto"][]; + /** Total */ + total: number; + /** Page */ + page: number; + /** Size */ + size: number; + /** Total Pages */ + total_pages: number; + /** Has Next */ + has_next: boolean; + }; + /** + * OpportunityMajor + * @enum {string} + */ + OpportunityMajor: "Engineering Management" | "Mechanical and Aerospace Engineering" | "Electrical and Computer Engineering" | "Chemical and Materials Engineering" | "Civil and Environmental Engineering" | "Biomedical Engineering" | "Mining Engineering" | "Petroleum Engineering" | "Robotics and Mechatronics Engineering" | "Computer Science" | "Data Science" | "Applied Mathematics" | "Mathematics" | "Economics" | "Business Administration" | "Finance" | "Life Sciences" | "Biological Sciences" | "Medical Sciences" | "Molecular Medicine" | "Pharmacology and Toxicology" | "Public Health" | "Sports Medicine and Rehabilitation" | "Nursing" | "Doctor of Medicine" | "A Six-Year Medical Program" | "Chemistry" | "Physics" | "Geosciences" | "Geology" | "Political Science and International Relations" | "Public Policy" | "Public Administration" | "Eurasian Studies" | "Sociology" | "Anthropology" | "History" | "Educational Leadership" | "Multilingual Education" | "World Languages, Literature and Culture"; + /** + * OpportunityMajorMapBase + * @description orm to pydantic mapping. do not use in create/update requests + */ + OpportunityMajorMapBase: { + /** Id */ + id: number; + /** Opportunity Id */ + opportunity_id: number; + major: components["schemas"]["OpportunityMajor"]; + }; + /** OpportunityResponseDto */ + OpportunityResponseDto: { + /** Id */ + id: number; + /** Name */ + name: string; + /** Description */ + description?: string | null; + /** Deadline */ + deadline?: string | null; + /** Host */ + host?: string | null; + type: components["schemas"]["OpportunityType"]; + /** + * Majors + * @default [] + */ + majors: components["schemas"]["OpportunityMajorMapBase"][]; + /** Link */ + link?: string | null; + /** Location */ + location?: string | null; + /** Funding */ + funding?: string | null; + /** Eligibilities */ + eligibilities?: components["schemas"]["OpportunityEligibilityBase"][]; + /** + * Created At + * Format: date-time + */ + created_at: string; + /** + * Updated At + * Format: date-time + */ + updated_at: string; + }; + /** + * OpportunityType + * @enum {string} + */ + OpportunityType: "research" | "internship" | "summer_school" | "forum" | "summit" | "grant" | "scholarship" | "conference"; + /** OpportunityUpdateDto */ + OpportunityUpdateDto: { + /** Name */ + name?: string | null; + /** Description */ + description?: string | null; + /** Deadline */ + deadline?: string | null; + /** Host */ + host?: string | null; + type?: components["schemas"]["OpportunityType"] | null; + /** Majors */ + majors?: components["schemas"]["OpportunityMajor"][] | null; + /** Link */ + link?: string | null; + /** Location */ + location?: string | null; + /** Funding */ + funding?: string | null; + /** Eligibilities */ + eligibilities?: components["schemas"]["OpportunityEligibilityUpdateDto"][] | null; + }; + /** + * PermissionType + * @enum {string} + */ + PermissionType: "view" | "assign" | "delegate"; + /** PlannerAutoBuildResponse */ + PlannerAutoBuildResponse: { + /** Scheduled */ + scheduled: components["schemas"]["AutoBuildCourseResult"][]; + /** Unscheduled Courses */ + unscheduled_courses: string[]; + /** Message */ + message: string; + }; + /** PlannerCourseAddRequest */ + PlannerCourseAddRequest: { + /** Course Code */ + course_code: string; + /** Term Value */ + term_value: string; + /** Term Label */ + term_label?: string | null; + /** Level */ + level?: string | null; + }; + /** PlannerCourseResponse */ + PlannerCourseResponse: { + /** Id */ + id: number; + /** Registrar Course Id */ + registrar_course_id: string; + /** Course Code */ + course_code: string; + /** Title */ + title: string | null; + /** Level */ + level: string | null; + /** School */ + school: string | null; + /** Term Value */ + term_value: string | null; + /** Term Label */ + term_label: string | null; + /** Capacity Total */ + capacity_total: number | null; + /** Sections */ + sections: components["schemas"]["PlannerSectionResponse"][]; + /** Pre Req */ + pre_req?: string | null; + /** Co Req */ + co_req?: string | null; + /** Anti Req */ + anti_req?: string | null; + /** Priority 1 */ + priority_1?: string | null; + /** Priority 2 */ + priority_2?: string | null; + /** Priority 3 */ + priority_3?: string | null; + /** Priority 4 */ + priority_4?: string | null; + }; + /** PlannerCourseSearchResponse */ + PlannerCourseSearchResponse: { + /** Items */ + items: components["schemas"]["PlannerCourseSearchResult"][]; + /** Cursor */ + cursor?: number | null; + }; + /** PlannerCourseSearchResult */ + PlannerCourseSearchResult: { + /** Course Code */ + course_code: string; + /** Title */ + title: string; + /** Pre Req */ + pre_req: string; + /** Anti Req */ + anti_req: string; + /** Co Req */ + co_req: string; + /** Level */ + level?: string | null; + /** School */ + school?: string | null; + /** Credits */ + credits?: string | null; + /** Term */ + term?: string | null; + /** Priority 1 */ + priority_1?: string | null; + /** Priority 2 */ + priority_2?: string | null; + /** Priority 3 */ + priority_3?: string | null; + /** Priority 4 */ + priority_4?: string | null; + }; + /** PlannerResetRequest */ + PlannerResetRequest: { + /** Term Value */ + term_value?: string | null; + }; + /** PlannerScheduleCreateRequest */ + PlannerScheduleCreateRequest: { + /** Name */ + name?: string | null; + }; + /** PlannerScheduleDuplicateRequest */ + PlannerScheduleDuplicateRequest: { + /** Name */ + name?: string | null; + }; + /** PlannerScheduleListResponse */ + PlannerScheduleListResponse: { + /** Items */ + items: components["schemas"]["PlannerScheduleSummary"][]; + /** Count */ + count: number; + /** Max Allowed */ + max_allowed: number; + }; + /** PlannerScheduleResponse */ + PlannerScheduleResponse: { + /** Id */ + id: number; + /** Name */ + name: string; + /** Courses */ + courses: components["schemas"]["PlannerCourseResponse"][]; + }; + /** PlannerScheduleSummary */ + PlannerScheduleSummary: { + /** Id */ + id: number; + /** Name */ + name: string; + /** Course Count */ + course_count: number; + }; + /** PlannerScheduleUpdateRequest */ + PlannerScheduleUpdateRequest: { + /** Name */ + name: string; + }; + /** PlannerSectionResponse */ + PlannerSectionResponse: { + /** Id */ + id: number; + /** Section Code */ + section_code: string; + /** Days */ + days: string; + /** Times */ + times: string; + /** Room */ + room?: string | null; + /** Faculty */ + faculty?: string | null; + /** Capacity */ + capacity?: number | null; + /** Enrollment Snapshot */ + enrollment_snapshot?: number | null; + /** Is Selected */ + is_selected: boolean; + /** Selected Count */ + selected_count: number; + }; + /** PlannerSectionSelectionRequest */ + PlannerSectionSelectionRequest: { + /** Section Ids */ + section_ids?: number[]; + }; + /** + * PubSubMessage + * @description Complete Pub/Sub message structure + */ + PubSubMessage: { + /** @description The Pub/Sub message containing the event data */ + message: components["schemas"]["PubSubMessageData"]; + /** + * Subscription + * @description Pub/Sub subscription name + */ + subscription?: string | null; + }; + /** + * PubSubMessageData + * @description Inner structure of the Pub/Sub message data field + */ + PubSubMessageData: { + /** + * Data + * @description Base64 encoded GCS event data + */ + data: string; + /** + * Attributes + * @description Additional message attributes + */ + attributes?: { + [key: string]: unknown; + } | null; + /** + * Messageid + * @description Unique message identifier + */ + messageId?: string | null; + /** + * Publishtime + * @description Message publish timestamp + */ + publishTime?: string | null; + }; + /** RegisteredCourseResponse */ + RegisteredCourseResponse: { + /** Id */ + id: number; + course: components["schemas"]["BaseCourseSchema"]; + /** Items */ + items: components["schemas"]["BaseCourseItem"][]; + /** Class Average */ + class_average?: number | null; + }; + /** RegistrarSyncPdfRequest */ + RegistrarSyncPdfRequest: { + /** + * Pdf File + * @description Base64-encoded registrar personal schedule PDF. + */ + pdf_file: string; + }; + /** RegistrarSyncRequest */ + RegistrarSyncRequest: { + /** + * Password + * @description Registrar password + */ + password: string; + }; + /** RegistrarSyncResponse */ + RegistrarSyncResponse: { + /** Synced Courses */ + synced_courses: components["schemas"]["RegisteredCourseResponse"][]; + /** Total Synced */ + total_synced: number; + /** Added Count */ + added_count: number; + /** Deleted Count */ + deleted_count: number; + /** Kept Count */ + kept_count: number; + schedule?: components["schemas"]["ScheduleResponse"] | null; + /** Term Label */ + term_label?: string | null; + /** Term Value */ + term_value?: string | null; + /** Last Synced At */ + last_synced_at?: string | null; + }; + /** + * RegistrationPolicy + * @enum {string} + */ + RegistrationPolicy: "registration" | "open"; + /** ResourcePermissions */ + ResourcePermissions: { + /** + * Can Edit + * @default false + */ + can_edit: boolean; + /** + * Can Delete + * @default false + */ + can_delete: boolean; + /** + * Editable Fields + * @default [] + */ + editable_fields: string[]; + }; + /** + * SGMemberActionResult + * @description Simple status response for SG membership actions. + */ + SGMemberActionResult: { + /** Detail */ + detail: string; + }; + /** + * SGMemberResponseDTO + * @description Detailed SG member response for management UI. + */ + SGMemberResponseDTO: { + user: components["schemas"]["ShortUserResponse"]; + /** Email */ + email: string; + role: components["schemas"]["UserRole"]; + department?: components["schemas"]["DepartmentResponseDTO"] | null; + /** Sg Assigned At */ + sg_assigned_at?: string | null; + sg_assigned_by?: components["schemas"]["ShortUserResponse"] | null; + }; + /** + * SGMemberSearchResponseDTO + * @description User option for SG membership management. + */ + SGMemberSearchResponseDTO: { + user: components["schemas"]["ShortUserResponse"]; + /** Email */ + email: string; + role: components["schemas"]["UserRole"]; + department?: components["schemas"]["DepartmentResponseDTO"] | null; + }; + /** + * SGMemberUpsertPayload + * @description Create/update SG membership for a Nuspace user. + */ + SGMemberUpsertPayload: { + /** + * Target User Sub + * @description Target user sub to add/update in SG + */ + target_user_sub: string; + /** @description SG role to assign */ + role: components["schemas"]["UserRole"]; + /** + * Department Id + * @description Department to assign the user to + */ + department_id: number; + }; + /** + * SGUserResponse + * @description DTO for SG user information. + */ + SGUserResponse: { + user: components["schemas"]["ShortUserResponse"]; + /** Department Name */ + department_name: string; + role: components["schemas"]["UserRole"]; + }; + /** SchedulePreferences */ + SchedulePreferences: { + /** Classes */ + classes: string[]; + /** Colors */ + colors: { + [key: string]: string; + }; + }; + /** ScheduleResponse */ + ScheduleResponse: { + /** Data */ + data: components["schemas"]["UserScheduleItem"][][]; + preferences: components["schemas"]["SchedulePreferences"]; + }; + /** ScheduleTimeSchema */ + ScheduleTimeSchema: { + start: components["schemas"]["TimeSchema"]; + end: components["schemas"]["TimeSchema"]; + }; + /** SemesterOption */ + SemesterOption: { + /** Label */ + label: string; + /** Value */ + value: string; + }; + /** ShortUserResponse */ + ShortUserResponse: { + /** Sub */ + sub: string; + /** Name */ + name: string; + /** Surname */ + surname: string; + /** Picture */ + picture: string; + }; + /** SignedUrlRequest */ + SignedUrlRequest: { + entity_type: components["schemas"]["EntityType"]; + /** Entity Id */ + entity_id: number; + media_format: components["schemas"]["MediaFormat"]; + /** Media Order */ + media_order: number; + /** Mime Type */ + mime_type: string; + }; + /** SignedUrlResponse */ + SignedUrlResponse: { + /** Filename */ + filename: string; + /** + * Upload Url + * Format: uri + */ + upload_url: string; + entity_type: components["schemas"]["EntityType"]; + /** Entity Id */ + entity_id: number; + media_format: components["schemas"]["MediaFormat"]; + /** Media Order */ + media_order: number; + /** Mime Type */ + mime_type: string; + }; + /** StudentScheduleResponse */ + StudentScheduleResponse: { + /** Term Label */ + term_label: string | null; + /** Term Value */ + term_value: string | null; + /** Last Synced At */ + last_synced_at: string | null; + schedule: components["schemas"]["ScheduleResponse"]; + }; + /** Sub */ + Sub: { + /** Sub */ + sub: string; + }; + /** SurveyResponseCount */ + SurveyResponseCount: { + /** Survey Responses */ + survey_responses: number; + }; + /** TCCourse */ + TCCourse: { + /** Code */ + code: string; + /** Title */ + title: string; + /** Credits */ + credits: number; + }; + /** TCMapping */ + TCMapping: { + /** + * Original Code + * @description Transfer course code from transcript. Code only. + * @example HST 152 + */ + original_code: string; + /** + * Mapped Code + * @description NU course code. Use department + space + number, e.g. HST 152. + * @example HST 152 + */ + mapped_code: string; + /** Mapped Credits */ + mapped_credits: number; + }; + /** TemplateCreate */ + TemplateCreate: { + /** Course Id */ + course_id: number; + /** Student Sub */ + student_sub: string; + /** + * Template Items + * @default [] + */ + template_items: (components["schemas"]["TemplateItemCreate"] | null)[]; + }; + /** TemplateImportResponse */ + TemplateImportResponse: { + /** Student Course Id */ + student_course_id: number; + /** Items */ + items: components["schemas"]["BaseCourseItem"][]; + }; + /** TemplateItemCreate */ + TemplateItemCreate: { + /** Item Name */ + item_name: string; + /** Total Weight Pct */ + total_weight_pct: number | null; + }; + /** TemplateItemUpdate */ + TemplateItemUpdate: { + /** Item Name */ + item_name: string; + /** Total Weight Pct */ + total_weight_pct: number; + }; + /** TemplateResponse */ + TemplateResponse: { + template: components["schemas"]["BaseCourseTemplate"]; + /** Template Items */ + template_items: components["schemas"]["BaseTemplateItem"][]; + student: components["schemas"]["ShortUserResponse"]; + }; + /** TemplateUpdate */ + TemplateUpdate: { + /** Template Items */ + template_items: components["schemas"]["TemplateItemUpdate"][]; + }; + /** TicketAccessEntryDTO */ + TicketAccessEntryDTO: { + user: components["schemas"]["ShortUserResponse"]; + permission: components["schemas"]["PermissionType"]; + granted_by?: components["schemas"]["ShortUserResponse"] | null; + /** + * Granted At + * Format: date-time + */ + granted_at: string; + }; + /** + * TicketCategory + * @enum {string} + */ + TicketCategory: "academic" | "administrative" | "technical" | "complaint" | "suggestion" | "other"; + /** + * TicketCreateDTO + * @description Schema for creating a new ticket. + */ + TicketCreateDTO: { + /** + * Author Sub + * @description Author identifier. Use 'me' for current user, or null if anonymous + * @default me + * @example me + */ + author_sub: string | null; + /** + * @description Category of the ticket + * @example academic + */ + category: components["schemas"]["TicketCategory"]; + /** + * Title + * @description Title of the ticket + * @example Need help with course registration + */ + title: string; + /** + * Body + * @description Detailed description of the issue + * @example I am having trouble registering for CSCI 152 course... + */ + body: string; + /** + * Is Anonymous + * @description Whether the ticket should be anonymous. If true, author_sub will be ignored. + * @default false + * @example false + */ + is_anonymous: boolean; + /** + * Owner Hash + * @description SHA256 hash of the client's secret key for anonymous ticket access. + * @example 2cf24dba5fb0a30e26e83b2ac5b9e29e1b161e5c1fa7425e73043362938b9824 + */ + owner_hash?: string | null; + }; + /** TicketOwnerHashDTO */ + TicketOwnerHashDTO: { + /** + * Owner Hash + * @description SHA256 hash of the client's secret key for anonymous ticket access. + */ + owner_hash: string; + }; + /** + * Ticket Base Schema + * @description Base schema for all ticket-related operations + */ + TicketResponseDTO: { + /** + * Id + * @description Unique identifier of the ticket + */ + id: number; + /** + * Author Sub + * @description Author identifier + */ + author_sub?: string | null; + /** @description Category of the ticket */ + category: components["schemas"]["TicketCategory"]; + /** + * Title + * @description Title of the ticket + */ + title: string; + /** + * Body + * @description Detailed description of the issue + */ + body: string; + /** @description Current status of the ticket */ + status: components["schemas"]["TicketStatus"]; + /** + * Is Anonymous + * @description Whether the ticket is anonymous + */ + is_anonymous: boolean; + /** + * Created At + * Format: date-time + * @description Creation timestamp + */ + created_at: string; + /** + * Updated At + * Format: date-time + * @description Last update timestamp + */ + updated_at: string; + /** @description Author information (null if anonymous or deleted user) */ + author?: components["schemas"]["ShortUserResponse"] | null; + /** + * @description User permissions for this ticket + * @default { + * "can_edit": false, + * "can_delete": false, + * "editable_fields": [] + * } + */ + permissions: components["schemas"]["ResourcePermissions"]; + ticket_access?: components["schemas"]["PermissionType"] | null; + /** + * Unread Count + * @description Number of unread messages in this ticket + * @default 0 + */ + unread_count: number; + /** + * Conversations + * @description List of conversations + * @default [] + */ + conversations: components["schemas"]["ConversationResponseDTO"][]; + /** + * Access List + * @description SG access list for this ticket + */ + access_list?: components["schemas"]["TicketAccessEntryDTO"][]; + }; + /** + * TicketStatus + * @enum {string} + */ + TicketStatus: "open" | "in_progress" | "closed" | "resolved"; + /** + * TicketUpdateDTO + * @description Schema for updating a ticket. + */ + TicketUpdateDTO: { + /** @description Current status of the ticket */ + status?: components["schemas"]["TicketStatus"] | null; + }; + /** + * TimeFilter + * @description Enum for predefined time filters in event queries. + * @enum {string} + */ + TimeFilter: "upcoming" | "today" | "week" | "month"; + /** TimeSchema */ + TimeSchema: { + /** Hh */ + hh: number; + /** Mm */ + mm: number; + }; + /** + * UserRole + * @enum {string} + */ + UserRole: "default" | "admin" | "boss" | "capo" | "soldier" | "community_admin"; + /** UserScheduleItem */ + UserScheduleItem: { + /** Label */ + label: string; + /** Title */ + title: string; + /** Info */ + info: string; + /** Teacher */ + teacher: string; + /** Cab */ + cab: string; + /** Course Code */ + course_code: string; + time: components["schemas"]["ScheduleTimeSchema"]; + }; + /** ValidationError */ + ValidationError: { + /** Location */ + loc: (string | number)[]; + /** Message */ + msg: string; + /** Error Type */ + type: string; + /** Input */ + input?: unknown; + /** Context */ + ctx?: Record; + }; + }; + responses: never; + parameters: never; + requestBodies: never; + headers: never; + pathItems: never; +} +export type $defs = Record; +export interface operations { + login_login_get: { + parameters: { + query?: { + state?: string | null; + return_to?: string | null; + mock_user?: string | null; + reauth?: boolean | null; + }; + header?: never; + path?: never; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description Successful Response */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": unknown; + }; + }; + /** @description Validation Error */ + 422: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["HTTPValidationError"]; + }; + }; + }; + }; + auth_callback_auth_callback_get: { + parameters: { + query?: { + state?: string | null; + }; + header?: never; + path?: never; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description Successful Response */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": unknown; + }; + }; + /** @description Validation Error */ + 422: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["HTTPValidationError"]; + }; + }; + }; + }; + bind_tg_connect_tg_post: { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: { + access_token?: string | null; + refresh_token?: string | null; + app_token?: string | null; + }; + }; + requestBody: { + content: { + "application/json": components["schemas"]["Sub"]; + }; + }; + responses: { + /** @description Successful Response */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": unknown; + }; + }; + /** @description Validation Error */ + 422: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["HTTPValidationError"]; + }; + }; + }; + }; + refresh_token_refresh_token_post: { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: { + refresh_token?: string | null; + }; + }; + requestBody?: never; + responses: { + /** @description Refresh token */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": unknown; + }; + }; + /** @description Validation Error */ + 422: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["HTTPValidationError"]; + }; + }; + }; + }; + get_current_user_me_get: { + parameters: { + query?: { + extended?: boolean; + }; + header?: never; + path?: never; + cookie?: { + access_token?: string | null; + refresh_token?: string | null; + app_token?: string | null; + }; + }; + requestBody?: never; + responses: { + /** @description Successful Response */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["CurrentUserResponse"]; + }; + }; + /** @description Validation Error */ + 422: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["HTTPValidationError"]; + }; + }; + }; + }; + logout_logout_get: { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: { + refresh_token?: string | null; + }; + }; + requestBody?: never; + responses: { + /** @description Successful Response */ + 204: { + headers: { + [name: string]: unknown; + }; + content?: never; + }; + /** @description Validation Error */ + 422: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["HTTPValidationError"]; + }; + }; + }; + }; + get_communities_communities_get: { + parameters: { + query?: { + size?: number; + page?: number; + community_type?: components["schemas"]["CommunityType"] | null; + community_category?: components["schemas"]["CommunityCategory"] | null; + /** @description if 'me' then current user's sub will be used */ + head_sub?: string | null; + /** @description Search keyword for community name or description */ + keyword?: string | null; + }; + header?: never; + path?: never; + cookie?: { + access_token?: string | null; + refresh_token?: string | null; + app_token?: string | null; + }; + }; + requestBody?: never; + responses: { + /** @description Successful Response */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["ListCommunity"]; + }; + }; + /** @description Validation Error */ + 422: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["HTTPValidationError"]; + }; + }; + }; + }; + add_community_communities_post: { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: { + access_token?: string | null; + refresh_token?: string | null; + app_token?: string | null; + }; + }; + requestBody: { + content: { + "application/json": components["schemas"]["CommunityCreateRequest"]; + }; + }; + responses: { + /** @description Successful Response */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["CommunityResponse"]; + }; + }; + /** @description Validation Error */ + 422: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["HTTPValidationError"]; + }; + }; + }; + }; + get_community_communities__community_id__get: { + parameters: { + query?: never; + header?: never; + path: { + community_id: number; + }; + cookie?: { + access_token?: string | null; + refresh_token?: string | null; + app_token?: string | null; + }; + }; + requestBody?: never; + responses: { + /** @description Successful Response */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["CommunityResponse"]; + }; + }; + /** @description Validation Error */ + 422: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["HTTPValidationError"]; + }; + }; + }; + }; + delete_community_communities__community_id__delete: { + parameters: { + query?: never; + header?: never; + path: { + community_id: number; + }; + cookie?: { + access_token?: string | null; + refresh_token?: string | null; + app_token?: string | null; + }; + }; + requestBody?: never; + responses: { + /** @description Successful Response */ + 204: { + headers: { + [name: string]: unknown; + }; + content?: never; + }; + /** @description Validation Error */ + 422: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["HTTPValidationError"]; + }; + }; + }; + }; + update_community_communities__community_id__patch: { + parameters: { + query?: never; + header?: never; + path: { + community_id: number; + }; + cookie?: { + access_token?: string | null; + refresh_token?: string | null; + app_token?: string | null; + }; + }; + requestBody: { + content: { + "application/json": components["schemas"]["CommunityUpdateRequest"]; + }; + }; + responses: { + /** @description Successful Response */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["CommunityResponse"]; + }; + }; + /** @description Validation Error */ + 422: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["HTTPValidationError"]; + }; + }; + }; + }; + get_events_events_get: { + parameters: { + query?: { + size?: number; + page?: number; + registration_policy?: components["schemas"]["RegistrationPolicy"] | null; + event_type?: components["schemas"]["EventType"] | null; + event_status?: components["schemas"]["EventStatus"] | null; + time_filter?: components["schemas"]["TimeFilter"] | null; + start_date?: string | null; + end_date?: string | null; + creator_sub?: string | null; + keyword?: string | null; + }; + header?: never; + path?: never; + cookie?: { + access_token?: string | null; + refresh_token?: string | null; + app_token?: string | null; + }; + }; + requestBody?: never; + responses: { + /** @description Successful Response */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["ListEventResponse"]; + }; + }; + /** @description Validation Error */ + 422: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["HTTPValidationError"]; + }; + }; + }; + }; + add_event_events_post: { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: { + access_token?: string | null; + refresh_token?: string | null; + app_token?: string | null; + }; + }; + requestBody: { + content: { + "application/json": components["schemas"]["EventCreateRequest"]; + }; + }; + responses: { + /** @description Successful Response */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["EventResponse"]; + }; + }; + /** @description Validation Error */ + 422: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["HTTPValidationError"]; + }; + }; + }; + }; + get_event_events__event_id__get: { + parameters: { + query?: never; + header?: never; + path: { + event_id: number; + }; + cookie?: { + access_token?: string | null; + refresh_token?: string | null; + app_token?: string | null; + }; + }; + requestBody?: never; + responses: { + /** @description Successful Response */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["EventResponse"]; + }; + }; + /** @description Validation Error */ + 422: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["HTTPValidationError"]; + }; + }; + }; + }; + delete_event_events__event_id__delete: { + parameters: { + query?: never; + header?: never; + path: { + event_id: number; + }; + cookie?: { + access_token?: string | null; + refresh_token?: string | null; + app_token?: string | null; + }; + }; + requestBody?: never; + responses: { + /** @description Successful Response */ + 204: { + headers: { + [name: string]: unknown; + }; + content?: never; + }; + /** @description Validation Error */ + 422: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["HTTPValidationError"]; + }; + }; + }; + }; + update_event_events__event_id__patch: { + parameters: { + query?: never; + header?: never; + path: { + event_id: number; + }; + cookie?: { + access_token?: string | null; + refresh_token?: string | null; + app_token?: string | null; + }; + }; + requestBody: { + content: { + "application/json": components["schemas"]["EventUpdateRequest"]; + }; + }; + responses: { + /** @description Successful Response */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["EventResponse"]; + }; + }; + /** @description Validation Error */ + 422: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["HTTPValidationError"]; + }; + }; + }; + }; + get_profile_profile_get: { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description Successful Response */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": unknown; + }; + }; + }; + }; + full_search_search__get: { + parameters: { + query: { + keyword: string; + storage_name: components["schemas"]["EntityType"]; + page?: number; + size?: number; + }; + header?: never; + path?: never; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description Successful Response */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": unknown; + }; + }; + /** @description Validation Error */ + 422: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["HTTPValidationError"]; + }; + }; + }; + }; + generate_upload_url_bucket_upload_url_post: { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: { + access_token?: string | null; + refresh_token?: string | null; + app_token?: string | null; + }; + }; + requestBody: { + content: { + "application/json": components["schemas"]["SignedUrlRequest"][]; + }; + }; + responses: { + /** @description Successful Response */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["SignedUrlResponse"][]; + }; + }; + /** @description Validation Error */ + 422: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["HTTPValidationError"]; + }; + }; + }; + }; + gcs_webhook_bucket_gcs_hook_post: { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + requestBody: { + content: { + "application/json": components["schemas"]["PubSubMessage"]; + }; + }; + responses: { + /** @description Successful Response */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": unknown; + }; + }; + /** @description Validation Error */ + 422: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["HTTPValidationError"]; + }; + }; + }; + }; + local_upload_proxy_bucket_local_upload__bucket___full_path__put: { + parameters: { + query?: never; + header?: never; + path: { + bucket: string; + full_path: string; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description Successful Response */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": unknown; + }; + }; + /** @description Validation Error */ + 422: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["HTTPValidationError"]; + }; + }; + }; + }; + local_download_proxy_bucket_local_download__bucket___full_path__get: { + parameters: { + query?: never; + header?: never; + path: { + bucket: string; + full_path: string; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description Successful Response */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": unknown; + }; + }; + /** @description Validation Error */ + 422: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["HTTPValidationError"]; + }; + }; + }; + }; + webhook_webhook_post: { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description Successful Response */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": unknown; + }; + }; + }; + }; + sync_courses_from_registrar_registered_courses_sync_post: { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: { + access_token?: string | null; + refresh_token?: string | null; + app_token?: string | null; + }; + }; + requestBody: { + content: { + "application/json": components["schemas"]["RegistrarSyncRequest"]; + }; + }; + responses: { + /** @description Successful Response */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["RegistrarSyncResponse"]; + }; + }; + /** @description Validation Error */ + 422: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["HTTPValidationError"]; + }; + }; + }; + }; + sync_courses_from_schedule_pdf_registered_courses_sync_pdf_post: { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: { + access_token?: string | null; + refresh_token?: string | null; + app_token?: string | null; + }; + }; + requestBody: { + content: { + "application/json": components["schemas"]["RegistrarSyncPdfRequest"]; + }; + }; + responses: { + /** @description Successful Response */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["RegistrarSyncResponse"]; + }; + }; + /** @description Validation Error */ + 422: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["HTTPValidationError"]; + }; + }; + }; + }; + get_registered_courses_registered_courses_get: { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: { + access_token?: string | null; + refresh_token?: string | null; + app_token?: string | null; + }; + }; + requestBody?: never; + responses: { + /** @description Successful Response */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["RegisteredCourseResponse"][]; + }; + }; + /** @description Validation Error */ + 422: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["HTTPValidationError"]; + }; + }; + }; + }; + get_registered_courses_schedule_registered_courses_schedule_get: { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: { + access_token?: string | null; + refresh_token?: string | null; + app_token?: string | null; + }; + }; + requestBody?: never; + responses: { + /** @description Successful Response */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["StudentScheduleResponse"] | null; + }; + }; + /** @description Validation Error */ + 422: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["HTTPValidationError"]; + }; + }; + }; + }; + export_registered_schedule_to_google_calendar_registered_courses_schedule_google_post: { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: { + access_token?: string | null; + refresh_token?: string | null; + app_token?: string | null; + }; + }; + requestBody?: never; + responses: { + /** @description Successful Response */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["GoogleCalendarExportResponse"]; + }; + }; + /** @description Validation Error */ + 422: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["HTTPValidationError"]; + }; + }; + }; + }; + export_registered_schedule_to_ics_registered_courses_schedule_ics_get: { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: { + access_token?: string | null; + refresh_token?: string | null; + app_token?: string | null; + }; + }; + requestBody?: never; + responses: { + /** @description Successful Response */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": unknown; + }; + }; + /** @description Validation Error */ + 422: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["HTTPValidationError"]; + }; + }; + }; + }; + add_course_item_course_items_post: { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: { + access_token?: string | null; + refresh_token?: string | null; + app_token?: string | null; + }; + }; + requestBody: { + content: { + "application/json": components["schemas"]["CourseItemCreate"]; + }; + }; + responses: { + /** @description Successful Response */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["BaseCourseItem"]; + }; + }; + /** @description Validation Error */ + 422: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["HTTPValidationError"]; + }; + }; + }; + }; + delete_course_item_course_items__item_id__delete: { + parameters: { + query?: never; + header?: never; + path: { + item_id: number; + }; + cookie?: { + access_token?: string | null; + refresh_token?: string | null; + app_token?: string | null; + }; + }; + requestBody?: never; + responses: { + /** @description Successful Response */ + 204: { + headers: { + [name: string]: unknown; + }; + content?: never; + }; + /** @description Validation Error */ + 422: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["HTTPValidationError"]; + }; + }; + }; + }; + update_course_item_course_items__item_id__patch: { + parameters: { + query?: never; + header?: never; + path: { + item_id: number; + }; + cookie?: { + access_token?: string | null; + refresh_token?: string | null; + app_token?: string | null; + }; + }; + requestBody: { + content: { + "application/json": components["schemas"]["CourseItemUpdate"]; + }; + }; + responses: { + /** @description Successful Response */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["BaseCourseItem"]; + }; + }; + /** @description Validation Error */ + 422: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["HTTPValidationError"]; + }; + }; + }; + }; + get_courses_courses_get: { + parameters: { + query?: { + page?: number; + size?: number; + /** @description Filter courses by term */ + term?: string | null; + /** @description Search keyword for course code or course title */ + keyword?: string | null; + }; + header?: never; + path?: never; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description Successful Response */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["ListBaseCourseResponse"]; + }; + }; + /** @description Validation Error */ + 422: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["HTTPValidationError"]; + }; + }; + }; + }; + list_semesters_planner_semesters_get: { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description Successful Response */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["SemesterOption"][]; + }; + }; + }; + }; + list_planner_schedules_planner_schedules_get: { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: { + access_token?: string | null; + refresh_token?: string | null; + app_token?: string | null; + }; + }; + requestBody?: never; + responses: { + /** @description Successful Response */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["PlannerScheduleListResponse"]; + }; + }; + /** @description Validation Error */ + 422: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["HTTPValidationError"]; + }; + }; + }; + }; + create_planner_schedule_planner_schedules_post: { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: { + access_token?: string | null; + refresh_token?: string | null; + app_token?: string | null; + }; + }; + requestBody: { + content: { + "application/json": components["schemas"]["PlannerScheduleCreateRequest"]; + }; + }; + responses: { + /** @description Successful Response */ + 201: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["PlannerScheduleSummary"]; + }; + }; + /** @description Validation Error */ + 422: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["HTTPValidationError"]; + }; + }; + }; + }; + duplicate_planner_schedule_planner_schedules__schedule_id__duplicate_post: { + parameters: { + query?: never; + header?: never; + path: { + schedule_id: number; + }; + cookie?: { + access_token?: string | null; + refresh_token?: string | null; + app_token?: string | null; + }; + }; + requestBody: { + content: { + "application/json": components["schemas"]["PlannerScheduleDuplicateRequest"]; + }; + }; + responses: { + /** @description Successful Response */ + 201: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["PlannerScheduleSummary"]; + }; + }; + /** @description Validation Error */ + 422: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["HTTPValidationError"]; + }; + }; + }; + }; + delete_planner_schedule_planner_schedules__schedule_id__delete: { + parameters: { + query?: never; + header?: never; + path: { + schedule_id: number; + }; + cookie?: { + access_token?: string | null; + refresh_token?: string | null; + app_token?: string | null; + }; + }; + requestBody?: never; + responses: { + /** @description Successful Response */ + 204: { + headers: { + [name: string]: unknown; + }; + content?: never; + }; + /** @description Validation Error */ + 422: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["HTTPValidationError"]; + }; + }; + }; + }; + update_planner_schedule_planner_schedules__schedule_id__patch: { + parameters: { + query?: never; + header?: never; + path: { + schedule_id: number; + }; + cookie?: { + access_token?: string | null; + refresh_token?: string | null; + app_token?: string | null; + }; + }; + requestBody: { + content: { + "application/json": components["schemas"]["PlannerScheduleUpdateRequest"]; + }; + }; + responses: { + /** @description Successful Response */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["PlannerScheduleSummary"]; + }; + }; + /** @description Validation Error */ + 422: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["HTTPValidationError"]; + }; + }; + }; + }; + search_courses_for_planner_planner_courses_search_get: { + parameters: { + query: { + term_value: string; + course_code?: string | null; + page?: number; + size?: number; + }; + header?: never; + path?: never; + cookie?: { + access_token?: string | null; + refresh_token?: string | null; + app_token?: string | null; + }; + }; + requestBody?: never; + responses: { + /** @description Successful Response */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["PlannerCourseSearchResponse"]; + }; + }; + /** @description Validation Error */ + 422: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["HTTPValidationError"]; + }; + }; + }; + }; + refresh_all_courses_planner_courses_refresh_post: { + parameters: { + query?: { + schedule_id?: number | null; + }; + header?: never; + path?: never; + cookie?: { + access_token?: string | null; + refresh_token?: string | null; + app_token?: string | null; + }; + }; + requestBody?: never; + responses: { + /** @description Successful Response */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["PlannerScheduleResponse"]; + }; + }; + /** @description Validation Error */ + 422: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["HTTPValidationError"]; + }; + }; + }; + }; + get_planner_schedule_planner_get: { + parameters: { + query?: { + schedule_id?: number | null; + }; + header?: never; + path?: never; + cookie?: { + access_token?: string | null; + refresh_token?: string | null; + app_token?: string | null; + }; + }; + requestBody?: never; + responses: { + /** @description Successful Response */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["PlannerScheduleResponse"]; + }; + }; + /** @description Validation Error */ + 422: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["HTTPValidationError"]; + }; + }; + }; + }; + add_course_planner_courses_post: { + parameters: { + query?: { + schedule_id?: number | null; + }; + header?: never; + path?: never; + cookie?: { + access_token?: string | null; + refresh_token?: string | null; + app_token?: string | null; + }; + }; + requestBody: { + content: { + "application/json": components["schemas"]["PlannerCourseAddRequest"]; + }; + }; + responses: { + /** @description Successful Response */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["PlannerCourseResponse"]; + }; + }; + /** @description Validation Error */ + 422: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["HTTPValidationError"]; + }; + }; + }; + }; + remove_course_planner_courses__course_id__delete: { + parameters: { + query?: never; + header?: never; + path: { + course_id: number; + }; + cookie?: { + access_token?: string | null; + refresh_token?: string | null; + app_token?: string | null; + }; + }; + requestBody?: never; + responses: { + /** @description Successful Response */ + 204: { + headers: { + [name: string]: unknown; + }; + content?: never; + }; + /** @description Validation Error */ + 422: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["HTTPValidationError"]; + }; + }; + }; + }; + get_sections_planner_courses__course_id__sections_get: { + parameters: { + query?: { + /** @description Force refresh from registrar */ + refresh?: boolean; + }; + header?: never; + path: { + course_id: number; + }; + cookie?: { + access_token?: string | null; + refresh_token?: string | null; + app_token?: string | null; + }; + }; + requestBody?: never; + responses: { + /** @description Successful Response */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["PlannerSectionResponse"][]; + }; + }; + /** @description Validation Error */ + 422: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["HTTPValidationError"]; + }; + }; + }; + }; + select_sections_planner_courses__course_id__sections_select_post: { + parameters: { + query?: never; + header?: never; + path: { + course_id: number; + }; + cookie?: { + access_token?: string | null; + refresh_token?: string | null; + app_token?: string | null; + }; + }; + requestBody: { + content: { + "application/json": components["schemas"]["PlannerSectionSelectionRequest"]; + }; + }; + responses: { + /** @description Successful Response */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["PlannerCourseResponse"]; + }; + }; + /** @description Validation Error */ + 422: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["HTTPValidationError"]; + }; + }; + }; + }; + auto_build_planner_autobuild_post: { + parameters: { + query?: { + schedule_id?: number | null; + }; + header?: never; + path?: never; + cookie?: { + access_token?: string | null; + refresh_token?: string | null; + app_token?: string | null; + }; + }; + requestBody?: never; + responses: { + /** @description Successful Response */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["PlannerAutoBuildResponse"]; + }; + }; + /** @description Validation Error */ + 422: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["HTTPValidationError"]; + }; + }; + }; + }; + reset_planner_planner_reset_post: { + parameters: { + query?: { + schedule_id?: number | null; + }; + header?: never; + path?: never; + cookie?: { + access_token?: string | null; + refresh_token?: string | null; + app_token?: string | null; + }; + }; + requestBody: { + content: { + "application/json": components["schemas"]["PlannerResetRequest"]; + }; + }; + responses: { + /** @description Successful Response */ + 204: { + headers: { + [name: string]: unknown; + }; + content?: never; + }; + /** @description Validation Error */ + 422: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["HTTPValidationError"]; + }; + }; + }; + }; + list_grade_terms_grades_terms_get: { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description Successful Response */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["ListGradeTermsResponse"]; + }; + }; + }; + }; + get_grades_grades_get: { + parameters: { + query?: { + size?: number; + page?: number; + /** @description Search keyword for course code or course title */ + keyword?: string | null; + /** @description Filter by semester/term code (e.g., FA2024) */ + term?: string | null; + }; + header?: never; + path?: never; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description Successful Response */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["ListGradeReportResponse"]; + }; + }; + /** @description Validation Error */ + 422: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["HTTPValidationError"]; + }; + }; + }; + }; + list_templates_templates_get: { + parameters: { + query?: { + course_id?: number | null; + page?: number; + size?: number; + }; + header?: never; + path?: never; + cookie?: { + access_token?: string | null; + refresh_token?: string | null; + app_token?: string | null; + }; + }; + requestBody?: never; + responses: { + /** @description Successful Response */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["ListTemplateDTO"]; + }; + }; + /** @description Validation Error */ + 422: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["HTTPValidationError"]; + }; + }; + }; + }; + create_template_templates_post: { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: { + access_token?: string | null; + refresh_token?: string | null; + app_token?: string | null; + }; + }; + requestBody: { + content: { + "application/json": components["schemas"]["TemplateCreate"]; + }; + }; + responses: { + /** @description Successful Response */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["TemplateResponse"]; + }; + }; + /** @description Validation Error */ + 422: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["HTTPValidationError"]; + }; + }; + }; + }; + get_template_templates__template_id__get: { + parameters: { + query?: never; + header?: never; + path: { + template_id: number; + }; + cookie?: { + access_token?: string | null; + refresh_token?: string | null; + app_token?: string | null; + }; + }; + requestBody?: never; + responses: { + /** @description Successful Response */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["TemplateResponse"]; + }; + }; + /** @description Validation Error */ + 422: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["HTTPValidationError"]; + }; + }; + }; + }; + delete_template_templates__template_id__delete: { + parameters: { + query?: never; + header?: never; + path: { + template_id: number; + }; + cookie?: { + access_token?: string | null; + refresh_token?: string | null; + app_token?: string | null; + }; + }; + requestBody?: never; + responses: { + /** @description Successful Response */ + 204: { + headers: { + [name: string]: unknown; + }; + content?: never; + }; + /** @description Validation Error */ + 422: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["HTTPValidationError"]; + }; + }; + }; + }; + update_template_templates__template_id__patch: { + parameters: { + query?: never; + header?: never; + path: { + template_id: number; + }; + cookie?: { + access_token?: string | null; + refresh_token?: string | null; + app_token?: string | null; + }; + }; + requestBody: { + content: { + "application/json": components["schemas"]["TemplateUpdate"]; + }; + }; + responses: { + /** @description Successful Response */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["TemplateResponse"]; + }; + }; + /** @description Validation Error */ + 422: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["HTTPValidationError"]; + }; + }; + }; + }; + import_template_into_course_templates__template_id__import_post: { + parameters: { + query: { + student_course_id: number; + }; + header?: never; + path: { + template_id: number; + }; + cookie?: { + access_token?: string | null; + refresh_token?: string | null; + app_token?: string | null; + }; + }; + requestBody?: never; + responses: { + /** @description Successful Response */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["TemplateImportResponse"]; + }; + }; + /** @description Validation Error */ + 422: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["HTTPValidationError"]; + }; + }; + }; + }; + list_catalog_degree_audit_catalog_get: { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: { + access_token?: string | null; + refresh_token?: string | null; + app_token?: string | null; + }; + }; + requestBody?: never; + responses: { + /** @description Successful Response */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["CatalogResponse"]; + }; + }; + /** @description Validation Error */ + 422: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["HTTPValidationError"]; + }; + }; + }; + }; + audit_from_registrar_degree_audit_audit_registrar_post: { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: { + access_token?: string | null; + refresh_token?: string | null; + app_token?: string | null; + }; + }; + requestBody: { + content: { + "application/json": components["schemas"]["AuditRequestRegistrar"]; + }; + }; + responses: { + /** @description Successful Response */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["AuditResponse"]; + }; + }; + /** @description Validation Error */ + 422: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["HTTPValidationError"]; + }; + }; + }; + }; + audit_from_pdf_degree_audit_audit_pdf_post: { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: { + access_token?: string | null; + refresh_token?: string | null; + app_token?: string | null; + }; + }; + requestBody: { + content: { + "application/json": components["schemas"]["AuditRequestPDF"]; + }; + }; + responses: { + /** @description Successful Response */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["AuditResponse"]; + }; + }; + /** @description Validation Error */ + 422: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["HTTPValidationError"]; + }; + }; + }; + }; + get_degree_requirements_degree_audit_requirements_get: { + parameters: { + query: { + year: string; + name: string; + type?: string; + }; + header?: never; + path?: never; + cookie?: { + access_token?: string | null; + refresh_token?: string | null; + app_token?: string | null; + }; + }; + requestBody?: never; + responses: { + /** @description Successful Response */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["DegreeRequirement"][]; + }; + }; + /** @description Validation Error */ + 422: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["HTTPValidationError"]; + }; + }; + }; + }; + get_cached_result_degree_audit_result_get: { + parameters: { + query?: { + year?: string | null; + major?: string | null; + }; + header?: never; + path?: never; + cookie?: { + access_token?: string | null; + refresh_token?: string | null; + app_token?: string | null; + }; + }; + requestBody?: never; + responses: { + /** @description Successful Response */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["AuditResponse"] | null; + }; + }; + /** @description Validation Error */ + 422: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["HTTPValidationError"]; + }; + }; + }; + }; + get_notification_get: { + parameters: { + query?: { + page?: number; + size?: number; + }; + header?: never; + path?: never; + cookie?: { + access_token?: string | null; + refresh_token?: string | null; + app_token?: string | null; + }; + }; + requestBody?: never; + responses: { + /** @description Successful Response */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["BaseNotification"][]; + }; + }; + /** @description Validation Error */ + 422: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["HTTPValidationError"]; + }; + }; + }; + }; + get_tickets_tickets_get: { + parameters: { + query?: { + /** @description Number of tickets to return per page for pagination */ + size?: number; + /** @description Page number for ticket listing pagination */ + page?: number; + /** @description If 'me', returns the current user's tickets */ + author_sub?: string | null; + category?: components["schemas"]["TicketCategory"] | null; + }; + header?: never; + path?: never; + cookie?: { + access_token?: string | null; + refresh_token?: string | null; + app_token?: string | null; + }; + }; + requestBody?: never; + responses: { + /** @description Successful Response */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["ListTicketDTO"]; + }; + }; + /** @description Validation Error */ + 422: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["HTTPValidationError"]; + }; + }; + }; + }; + create_ticket_tickets_post: { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: { + access_token?: string | null; + refresh_token?: string | null; + app_token?: string | null; + }; + }; + requestBody: { + content: { + "application/json": components["schemas"]["TicketCreateDTO"]; + }; + }; + responses: { + /** @description Successful Response */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["TicketResponseDTO"]; + }; + }; + /** @description Validation Error */ + 422: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["HTTPValidationError"]; + }; + }; + }; + }; + get_ticket_tickets__ticket_id__get: { + parameters: { + query?: never; + header?: never; + path: { + ticket_id: number; + }; + cookie?: { + access_token?: string | null; + refresh_token?: string | null; + app_token?: string | null; + }; + }; + requestBody?: never; + responses: { + /** @description Successful Response */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["TicketResponseDTO"]; + }; + }; + /** @description Validation Error */ + 422: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["HTTPValidationError"]; + }; + }; + }; + }; + update_ticket_tickets__ticket_id__patch: { + parameters: { + query?: never; + header?: never; + path: { + ticket_id: number; + }; + cookie?: { + access_token?: string | null; + refresh_token?: string | null; + app_token?: string | null; + }; + }; + requestBody: { + content: { + "application/json": components["schemas"]["TicketUpdateDTO"]; + }; + }; + responses: { + /** @description Successful Response */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["TicketResponseDTO"]; + }; + }; + /** @description Validation Error */ + 422: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["HTTPValidationError"]; + }; + }; + }; + }; + get_ticket_by_owner_hash_tickets_by_owner_hash_post: { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: { + access_token?: string | null; + refresh_token?: string | null; + app_token?: string | null; + }; + }; + requestBody: { + content: { + "application/json": components["schemas"]["TicketOwnerHashDTO"]; + }; + }; + responses: { + /** @description Successful Response */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["TicketResponseDTO"]; + }; + }; + /** @description Validation Error */ + 422: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["HTTPValidationError"]; + }; + }; + }; + }; + create_conversation_conversations_post: { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: { + access_token?: string | null; + refresh_token?: string | null; + app_token?: string | null; + }; + }; + requestBody: { + content: { + "application/json": components["schemas"]["ConversationCreateDTO"]; + }; + }; + responses: { + /** @description Successful Response */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["ConversationResponseDTO"]; + }; + }; + /** @description Validation Error */ + 422: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["HTTPValidationError"]; + }; + }; + }; + }; + update_conversation_conversations__conversation_id__patch: { + parameters: { + query?: never; + header?: never; + path: { + conversation_id: number; + }; + cookie?: { + access_token?: string | null; + refresh_token?: string | null; + app_token?: string | null; + }; + }; + requestBody: { + content: { + "application/json": components["schemas"]["ConversationUpdateDTO"]; + }; + }; + responses: { + /** @description Successful Response */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["ConversationResponseDTO"]; + }; + }; + /** @description Validation Error */ + 422: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["HTTPValidationError"]; + }; + }; + }; + }; + get_messages_messages_get: { + parameters: { + query: { + conversation_id: number; + size?: number; + page?: number; + owner_hash?: string | null; + }; + header?: { + "X-Owner-Hash"?: string | null; + }; + path?: never; + cookie?: { + access_token?: string | null; + refresh_token?: string | null; + app_token?: string | null; + }; + }; + requestBody?: never; + responses: { + /** @description Successful Response */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["ListMessageDTO"]; + }; + }; + /** @description Validation Error */ + 422: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["HTTPValidationError"]; + }; + }; + }; + }; + create_message_messages_post: { + parameters: { + query?: { + owner_hash?: string | null; + }; + header?: { + "X-Owner-Hash"?: string | null; + }; + path?: never; + cookie?: { + access_token?: string | null; + refresh_token?: string | null; + app_token?: string | null; + }; + }; + requestBody: { + content: { + "application/json": components["schemas"]["MessageCreateDTO"]; + }; + }; + responses: { + /** @description Successful Response */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["MessageResponseDTO"]; + }; + }; + /** @description Validation Error */ + 422: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["HTTPValidationError"]; + }; + }; + }; + }; + get_message_messages__message_id__get: { + parameters: { + query?: { + owner_hash?: string | null; + }; + header?: { + "X-Owner-Hash"?: string | null; + }; + path: { + message_id: number; + }; + cookie?: { + access_token?: string | null; + refresh_token?: string | null; + app_token?: string | null; + }; + }; + requestBody?: never; + responses: { + /** @description Successful Response */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["MessageResponseDTO"]; + }; + }; + /** @description Validation Error */ + 422: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["HTTPValidationError"]; + }; + }; + }; + }; + mark_message_as_read_messages__message_id__read_post: { + parameters: { + query?: { + owner_hash?: string | null; + }; + header?: { + "X-Owner-Hash"?: string | null; + }; + path: { + message_id: number; + }; + cookie?: { + access_token?: string | null; + refresh_token?: string | null; + app_token?: string | null; + }; + }; + requestBody?: never; + responses: { + /** @description Successful Response */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["MessageResponseDTO"]; + }; + }; + /** @description Validation Error */ + 422: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["HTTPValidationError"]; + }; + }; + }; + }; + get_departments_sg_delegation_departments_get: { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: { + access_token?: string | null; + refresh_token?: string | null; + app_token?: string | null; + }; + }; + requestBody?: never; + responses: { + /** @description Successful Response */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["DepartmentResponseDTO"][]; + }; + }; + /** @description Validation Error */ + 422: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["HTTPValidationError"]; + }; + }; + }; + }; + create_department_sg_delegation_departments_post: { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: { + access_token?: string | null; + refresh_token?: string | null; + app_token?: string | null; + }; + }; + requestBody: { + content: { + "application/json": components["schemas"]["DepartmentCreatePayload"]; + }; + }; + responses: { + /** @description Successful Response */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["DepartmentResponseDTO"]; + }; + }; + /** @description Validation Error */ + 422: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["HTTPValidationError"]; + }; + }; + }; + }; + delete_department_sg_delegation_departments__department_id__delete: { + parameters: { + query?: never; + header?: never; + path: { + department_id: number; + }; + cookie?: { + access_token?: string | null; + refresh_token?: string | null; + app_token?: string | null; + }; + }; + requestBody?: never; + responses: { + /** @description Successful Response */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["SGMemberActionResult"]; + }; + }; + /** @description Validation Error */ + 422: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["HTTPValidationError"]; + }; + }; + }; + }; + get_sg_users_sg_delegation_users_get: { + parameters: { + query: { + department_id: number; + }; + header?: never; + path?: never; + cookie?: { + access_token?: string | null; + refresh_token?: string | null; + app_token?: string | null; + }; + }; + requestBody?: never; + responses: { + /** @description Successful Response */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["SGUserResponse"][]; + }; + }; + /** @description Validation Error */ + 422: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["HTTPValidationError"]; + }; + }; + }; + }; + search_users_for_sg_management_sg_members_users_get: { + parameters: { + query?: { + /** @description Search in name, surname, email */ + q?: string | null; + /** @description Maximum number of users to return */ + limit?: number; + }; + header?: never; + path?: never; + cookie?: { + access_token?: string | null; + refresh_token?: string | null; + app_token?: string | null; + }; + }; + requestBody?: never; + responses: { + /** @description Successful Response */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["SGMemberSearchResponseDTO"][]; + }; + }; + /** @description Validation Error */ + 422: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["HTTPValidationError"]; + }; + }; + }; + }; + list_sg_members_sg_members_get: { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: { + access_token?: string | null; + refresh_token?: string | null; + app_token?: string | null; + }; + }; + requestBody?: never; + responses: { + /** @description Successful Response */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["SGMemberResponseDTO"][]; + }; + }; + /** @description Validation Error */ + 422: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["HTTPValidationError"]; + }; + }; + }; + }; + upsert_sg_member_sg_members_post: { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: { + access_token?: string | null; + refresh_token?: string | null; + app_token?: string | null; + }; + }; + requestBody: { + content: { + "application/json": components["schemas"]["SGMemberUpsertPayload"]; + }; + }; + responses: { + /** @description Successful Response */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["SGMemberResponseDTO"]; + }; + }; + /** @description Validation Error */ + 422: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["HTTPValidationError"]; + }; + }; + }; + }; + remove_sg_member_sg_members__target_user_sub__delete: { + parameters: { + query?: never; + header?: never; + path: { + target_user_sub: string; + }; + cookie?: { + access_token?: string | null; + refresh_token?: string | null; + app_token?: string | null; + }; + }; + requestBody?: never; + responses: { + /** @description Successful Response */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["SGMemberActionResult"]; + }; + }; + /** @description Validation Error */ + 422: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["HTTPValidationError"]; + }; + }; + }; + }; + withdraw_from_sg_sg_members_withdraw_post: { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: { + access_token?: string | null; + refresh_token?: string | null; + app_token?: string | null; + }; + }; + requestBody?: never; + responses: { + /** @description Successful Response */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["SGMemberActionResult"]; + }; + }; + /** @description Validation Error */ + 422: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["HTTPValidationError"]; + }; + }; + }; + }; + delegate_ticket_access_tickets__ticket_id__delegate_post: { + parameters: { + query?: never; + header?: never; + path: { + ticket_id: number; + }; + cookie?: { + access_token?: string | null; + refresh_token?: string | null; + app_token?: string | null; + }; + }; + requestBody: { + content: { + "application/json": components["schemas"]["DelegateAccessPayload"]; + }; + }; + responses: { + /** @description Successful Response */ + 204: { + headers: { + [name: string]: unknown; + }; + content?: never; + }; + /** @description Validation Error */ + 422: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["HTTPValidationError"]; + }; + }; + }; + }; + get_announcements_from_telegram_announcements_telegram_get: { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description Successful Response */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": unknown; + }; + }; + }; + }; + get_announcements_bundle_route_announcements_bundle_get: { + parameters: { + query?: { + events_page?: number; + events_size?: number; + recruitment_events_page?: number; + recruitment_events_size?: number; + }; + header?: never; + path?: never; + cookie?: { + access_token?: string | null; + refresh_token?: string | null; + app_token?: string | null; + }; + }; + requestBody?: never; + responses: { + /** @description Successful Response */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["AnnouncementsBundleResponse"]; + }; + }; + /** @description Validation Error */ + 422: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["HTTPValidationError"]; + }; + }; + }; + }; + list_opportunities_opportunities_get: { + parameters: { + query?: { + /** @description Filter by opportunity types */ + type?: components["schemas"]["OpportunityType"][] | null; + /** @description Filter by majors */ + majors?: components["schemas"]["OpportunityMajor"][] | null; + /** @description Filter by education levels */ + education_level?: components["schemas"]["EducationLevel"][] | null; + /** @description Study years to match (for UG/GrM) */ + years?: number[] | null; + /** @description Search in name/description */ + q?: string | null; + /** @description Hide expired opportunities */ + hide_expired?: boolean; + /** @description Page number (1-indexed) */ + page?: number; + /** @description Page size */ + size?: number; + }; + header?: never; + path?: never; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description Successful Response */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["OpportunityListResponse"]; + }; + }; + /** @description Validation Error */ + 422: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["HTTPValidationError"]; + }; + }; + }; + }; + create_opportunity_opportunities_post: { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: { + access_token?: string | null; + refresh_token?: string | null; + app_token?: string | null; + }; + }; + requestBody: { + content: { + "application/json": components["schemas"]["OpportunityCreateDto"]; + }; + }; + responses: { + /** @description Successful Response */ + 201: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["OpportunityResponseDto"]; + }; + }; + /** @description Validation Error */ + 422: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["HTTPValidationError"]; + }; + }; + }; + }; + get_opportunity_opportunities__id__get: { + parameters: { + query?: never; + header?: never; + path: { + id: number; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description Successful Response */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["OpportunityResponseDto"]; + }; + }; + /** @description Validation Error */ + 422: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["HTTPValidationError"]; + }; + }; + }; + }; + delete_opportunity_opportunities__id__delete: { + parameters: { + query?: never; + header?: never; + path: { + id: number; + }; + cookie?: { + access_token?: string | null; + refresh_token?: string | null; + app_token?: string | null; + }; + }; + requestBody?: never; + responses: { + /** @description Successful Response */ + 204: { + headers: { + [name: string]: unknown; + }; + content?: never; + }; + /** @description Validation Error */ + 422: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["HTTPValidationError"]; + }; + }; + }; + }; + update_opportunity_opportunities__id__patch: { + parameters: { + query?: never; + header?: never; + path: { + id: number; + }; + cookie?: { + access_token?: string | null; + refresh_token?: string | null; + app_token?: string | null; + }; + }; + requestBody: { + content: { + "application/json": components["schemas"]["OpportunityUpdateDto"]; + }; + }; + responses: { + /** @description Successful Response */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["OpportunityResponseDto"]; + }; + }; + /** @description Validation Error */ + 422: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["HTTPValidationError"]; + }; + }; + }; + }; + add_opportunity_to_calendar_opportunities__id__calendar_post: { + parameters: { + query?: never; + header?: never; + path: { + id: number; + }; + cookie?: { + access_token?: string | null; + refresh_token?: string | null; + app_token?: string | null; + }; + }; + requestBody?: never; + responses: { + /** @description Successful Response */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["OpportunityCalendarResponse"]; + }; + }; + /** @description Validation Error */ + 422: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["HTTPValidationError"]; + }; + }; + }; + }; + stream_election_counter_elections_counter_stream_get: { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description Successful Response */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": unknown; + }; + }; + }; + }; + get_election_counter_elections_counter_get: { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description Successful Response */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["SurveyResponseCount"]; + }; + }; + }; + }; +} diff --git a/web/src/app/query-client.ts b/web/src/app/query-client.ts new file mode 100644 index 00000000..cd483c6d --- /dev/null +++ b/web/src/app/query-client.ts @@ -0,0 +1,19 @@ +import { QueryClient } from "@tanstack/react-query" + +/** + * The one and only QueryClient. + * + * It is passed to both QueryClientProvider and the router context, so route + * hooks and components always resolve the same cache. Never construct another + * one — the old app had two, and every imperative invalidateQueries in + * use-user.ts operated on a cache no component was subscribed to. + */ +export const queryClient = new QueryClient({ + defaultOptions: { + queries: { + staleTime: 1000 * 60 * 5, + retry: false, + refetchOnWindowFocus: false, + }, + }, +}) diff --git a/web/src/app/router.tsx b/web/src/app/router.tsx new file mode 100644 index 00000000..9db0ff18 --- /dev/null +++ b/web/src/app/router.tsx @@ -0,0 +1,21 @@ +import { createRouter } from "@tanstack/react-router" + +import { routeTree } from "@/routeTree.gen" +import { queryClient } from "@/app/query-client" +import { NotFound } from "@/components/not-found" + +export const router = createRouter({ + routeTree, + context: { queryClient }, + defaultPreload: "intent", + // Query owns caching; the router should not keep a second copy. + defaultPreloadStaleTime: 0, + scrollRestoration: true, + defaultNotFoundComponent: NotFound, +}) + +declare module "@tanstack/react-router" { + interface Register { + router: typeof router + } +} diff --git a/web/src/assets/events/1.webp b/web/src/assets/events/1.webp new file mode 100644 index 00000000..95bb1705 Binary files /dev/null and b/web/src/assets/events/1.webp differ diff --git a/web/src/assets/events/2.webp b/web/src/assets/events/2.webp new file mode 100644 index 00000000..5811577f Binary files /dev/null and b/web/src/assets/events/2.webp differ diff --git a/web/src/assets/events/3.webp b/web/src/assets/events/3.webp new file mode 100644 index 00000000..98f56238 Binary files /dev/null and b/web/src/assets/events/3.webp differ diff --git a/web/src/assets/events/4.webp b/web/src/assets/events/4.webp new file mode 100644 index 00000000..5f31f4d2 Binary files /dev/null and b/web/src/assets/events/4.webp differ diff --git a/web/src/assets/events/5.webp b/web/src/assets/events/5.webp new file mode 100644 index 00000000..95458279 Binary files /dev/null and b/web/src/assets/events/5.webp differ diff --git a/web/src/assets/nuspace_logo.svg b/web/src/assets/nuspace_logo.svg new file mode 100644 index 00000000..0c279bdd --- /dev/null +++ b/web/src/assets/nuspace_logo.svg @@ -0,0 +1,17 @@ + + + + + + + + + + + + + + + + + diff --git a/web/src/assets/team/adil.jpg b/web/src/assets/team/adil.jpg new file mode 100644 index 00000000..5a03d063 Binary files /dev/null and b/web/src/assets/team/adil.jpg differ diff --git a/web/src/assets/team/aisana.jpg b/web/src/assets/team/aisana.jpg new file mode 100644 index 00000000..b9debd74 Binary files /dev/null and b/web/src/assets/team/aisana.jpg differ diff --git a/web/src/assets/team/alan.jpg b/web/src/assets/team/alan.jpg new file mode 100644 index 00000000..6a474e70 Binary files /dev/null and b/web/src/assets/team/alan.jpg differ diff --git a/web/src/assets/team/bakhtiyar.jpg b/web/src/assets/team/bakhtiyar.jpg new file mode 100644 index 00000000..fc51ab62 Binary files /dev/null and b/web/src/assets/team/bakhtiyar.jpg differ diff --git a/web/src/assets/team/ulan.jpg b/web/src/assets/team/ulan.jpg new file mode 100644 index 00000000..c7100e0f Binary files /dev/null and b/web/src/assets/team/ulan.jpg differ diff --git a/web/src/assets/team/yelnur.jpg b/web/src/assets/team/yelnur.jpg new file mode 100644 index 00000000..c2ca506e Binary files /dev/null and b/web/src/assets/team/yelnur.jpg differ diff --git a/web/src/components/app-sidebar.tsx b/web/src/components/app-sidebar.tsx new file mode 100644 index 00000000..98fe8563 --- /dev/null +++ b/web/src/components/app-sidebar.tsx @@ -0,0 +1,342 @@ +import { useState } from "react" +import { Link, useRouterState } from "@tanstack/react-router" +import { + BookOpenIcon, + BriefcaseIcon, + CalendarIcon, + ChevronLeftIcon, + ChevronRightIcon, + InfoIcon, + LogOutIcon, + MenuIcon, + ShieldIcon, + UsersIcon, +} from "lucide-react" +import type { LinkProps } from "@tanstack/react-router" +import type { LucideIcon } from "lucide-react" + +import logoUrl from "@/assets/nuspace_logo.svg" +import { cn } from "@/lib/utils" +import { useCurrentUser } from "@/features/auth/use-session" +import { useLogout } from "@/features/auth/use-logout" +import { Button } from "@/components/ui/button" +import { ThemeToggle } from "@/components/theme-toggle" +import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar" +import { + Sheet, + SheetContent, + SheetTitle, + SheetTrigger, +} from "@/components/ui/sheet" +import { + Tooltip, + TooltipContent, + TooltipProvider, + TooltipTrigger, +} from "@/components/ui/tooltip" + +interface NavItem { + /** + * Typed against the generated route tree, so a link to a route that does not + * exist fails the build. The old app's Link wrapper took a plain string and + * threw this away. + */ + to: LinkProps["to"] + label: string + icon: LucideIcon +} + +// Home is the logo and profile is the account card, matching the previous app. +const NAV_ITEMS: NavItem[] = [ + { to: "/events", label: "Events", icon: CalendarIcon }, + { to: "/courses", label: "Courses", icon: BookOpenIcon }, + { to: "/communities", label: "Communities", icon: UsersIcon }, + { to: "/opportunities", label: "Opportunities Digest", icon: BriefcaseIcon }, + { to: "/contacts", label: "Contacts", icon: InfoIcon }, + { to: "/sgotinish", label: "SG otinish", icon: ShieldIcon }, +] + +function NavLinks({ + collapsed = false, + onNavigate, +}: { + collapsed?: boolean + onNavigate?: () => void +}) { + const pathname = useRouterState({ select: (s) => s.location.pathname }) + + return ( + + ) +} + +function Brand({ + collapsed = false, + onNavigate, +}: { + collapsed?: boolean + onNavigate?: () => void +}) { + const pathname = useRouterState({ select: (s) => s.location.pathname }) + const isActive = pathname === "/announcements" + const brand = ( + + + + Nuspace + + + ) + + if (!collapsed) return brand + + return ( + + + Announcements + + ) +} + +function AccountCard({ + collapsed = false, + onNavigate, +}: { + collapsed?: boolean + onNavigate?: () => void +}) { + const user = useCurrentUser() + const logout = useLogout() + const initial = user.given_name.charAt(0).toUpperCase() + + const profileLink = ( + + + {user.picture && } + {initial} + + + {user.name} + + {user.email} + + + + ) + + const logoutButton = ( + + ) + + return ( +
+ {collapsed ? ( + + + {user.name} + + ) : ( + profileLink + )} + {collapsed ? ( + + + + {logout.isPending ? "Logging out…" : "Log out"} + + + ) : ( + logoutButton + )} +
+ ) +} + +/** + * App navigation: a fixed rail on desktop, a sheet on mobile. + * + * The old sidebar injected a raw