diff --git a/.docs/AGENTS.md b/.docs/AGENTS.md new file mode 100644 index 0000000..b5d7aef --- /dev/null +++ b/.docs/AGENTS.md @@ -0,0 +1,41 @@ +# .docs/ -- Engineering Documentation Center + +## Fungsi + +Pusat dokumentasi internal engineering: architecture decisions, technical guides, design docs, testing strategy, deployment, security, AI collaboration. + +## Aturan Penggunaan + +- Baca dokumentasi yang relevan SEBELUM memulai task baru +- Update dokumentasi SETELAH ada keputusan atau perubahan berarti +- Bahasa Indonesia wajib untuk semua dokumentasi + +## Struktur + +| Folder | Isi | +| ------------- | ----------------------------------- | +| product/ | Visi produk, roadmap, feature specs | +| design/ | Design system, UI/UX guidelines | +| technical/ | Tech stack, dependencies | +| engineering/ | Coding standards, workflow | +| architecture/ | System architecture, data flow | +| adr/ | Architecture Decision Records | +| api/ | API documentation | +| testing/ | Testing strategy | +| deployment/ | Deployment pipeline | +| security/ | Security policies | +| ai/ | AI agent collaboration | +| onboarding/ | Developer onboarding | +| glossary/ | Terminology | + +## Best Practices + +- Setiap folder memiliki README.md dengan template +- ADR untuk keputusan arsitektur penting +- Dokumentasi sifatnya living document + +## AI Do's / Don'ts + +- Boleh: membaca .docs/ sebelum task, mengupdate dokumentasi +- Tidak boleh: menghapus dokumentasi tanpa deprecation notice +- Wajib: baca .docs/ai/ sebelum berinteraksi dengan project diff --git a/.docs/README.md b/.docs/README.md new file mode 100644 index 0000000..e775290 --- /dev/null +++ b/.docs/README.md @@ -0,0 +1,87 @@ +# .docs -- Pusat Dokumentasi Engineering + +> Dokumentasi internal untuk pengembangan project ini. +> Bukan dokumentasi user -- ini adalah **engineering knowledge base**. + +--- + +## Tujuan + +Folder `.docs` adalah pusat dokumentasi engineering yang berfungsi sebagai: + +- **Single source of truth** untuk keputusan teknis dan arsitektur +- **Knowledge base** untuk developer dan AI agent +- **Onboarding hub** untuk anggota tim baru +- **Decision log** untuk mencatat why di balik setiap keputusan + +--- + +## Struktur Folder + +| Folder | Fungsi | +| --------------- | -------------------------------------------------------- | +| `product/` | Visi produk, roadmap, user stories, feature specs | +| `design/` | Design system, UI/UX guidelines, component patterns | +| `technical/` | Tech stack, dependencies, konfigurasi, development setup | +| `engineering/` | Engineering practices, code review, coding standards | +| `architecture/` | System architecture, data flow, module boundaries | +| `adr/` | Architecture Decision Records -- keputusan arsitektur | +| `api/` | API documentation, endpoint specs, request/response | +| `testing/` | Testing strategy, test types, coverage goals | +| `deployment/` | Deployment pipeline, environments, infrastructure | +| `security/` | Security policies, auth, authorization, data protection | +| `ai/` | AI agent collaboration guide, prompt conventions | +| `onboarding/` | New developer onboarding, setup guide | +| `glossary/` | Project terminology, abbreviations, definitions | + +--- + +## Cara Menggunakan + +1. **Baca dulu** -- cek folder yang relevan sebelum memulai task baru +2. **Update secara berkala** -- tambahkan insight, keputusan, dan pelajaran baru +3. **Jangan hapus** -- dokumentasi adalah aset. Deprecate, jangan delete +4. **Gunakan template** -- setiap folder menyediakan template untuk konsistensi +5. **Bahasa Indonesia** -- semua dokumentasi wajib Bahasa Indonesia (kecuali kode) + +--- + +## Prinsip Penulisan + +| Prinsip | Penjelasan | +| ---------------- | ------------------------------------------------------- | +| **Professional** | Hindari bahasa informal, emoji, atau subjective opinion | +| **Clear** | Jelaskan konteks, bukan hanya apa yang terjadi | +| **Practical** | Berikan contoh nyata, bukan teori abstrak | +| **Up-to-date** | Jika informasi berubah, update dokumentasi | +| **Searchable** | Gunakan judul dan struktur yang mudah dicari | + +--- + +## Format Dokumen + +Setiap dokumen sebaiknya memiliki struktur: + +```markdown +# Judul Dokumen + +## Tujuan + +[Mengapa dokumen ini dibuat] + +## Konteks + +[Latar belakang, keputusan yang melatarbelakangi] + +## Isi + +[Konten utama] + +## Referensi + +[Link ke dokumen terkait] +``` + +--- + +_Dokumentasi adalah investasi. Setiap menit yang dihabiskan untuk menulis docs menghemat berjam-jam kebingungan di masa depan._ diff --git a/.docs/adr/README.md b/.docs/adr/README.md new file mode 100644 index 0000000..bc0d0f7 --- /dev/null +++ b/.docs/adr/README.md @@ -0,0 +1,125 @@ +# adr/ -- Architecture Decision Records + +> Mencatat keputusan arsitektur penting beserta konteks dan konsekuensinya. + +--- + +## Tujuan + +ADR (Architecture Decision Record) adalah dokumentasi **keputusan arsitektur** yang mencakup: + +- Konteks mengapa keputusan diambil +- Alternatif yang dipertimbangkan +- Keputusan final yang diambil +- Konsekuensi dari keputusan tersebut +- Status keputusan (proposed, accepted, deprecated, superseded) + +--- + +## Format ADR (Y-Statements) + +Setiap ADR menggunakan format **Y-Statement**: + +```markdown +# ADR-[nomor]: [Judul Keputusan] + +## Status + +[Proposed | Accepted | Deprecated | Superseded by ADR-NNN] + +## Context + +[Jelaskan konteks dan problem yang mendorong keputusan ini] + +## Decision + +[Gunakan format "We will..." atau "Kami memutuskan untuk..."] + +## Rationale + +[Jelaskan alasan di balik keputusan] + +## Consequences + +### Positive + +- [Dampak positif] + +### Negative + +- [Dampak negatif] + +### Neutral + +- [Dampak netral] + +## Alternatives Considered + +### Alternative 1: [Nama] + +- Pros: ... +- Cons: ... + +### Alternative 2: [Nama] + +- Pros: ... +- Cons: ... + +## References + +- [Link ke dokumentasi terkait] +- [Link ke issue/PR] +``` + +--- + +## Template ADR (Simple) + +```markdown +# ADR-001: [Judul] + +**Status:** Proposed + +**Context:** +[Problem dan latar belakang] + +**Decision:** +Kami akan [keputusan] + +**Rationale:** +[Alasan] + +**Consequences:** + +- Positif: [...] +- Negatif: [...] + +**Alternatives:** + +1. [Alternatif 1] -- ditolak karena [...] +2. [Alternatif 2] -- ditolak karena [...] +``` + +--- + +## Kapan Harus Membuat ADR + +Buat ADR ketika: + +- Mengadopsi teknologi/library baru +- Mengubah struktur folder atau arsitektur +- Memutuskan design pattern yang signifikan +- Mengubah data model atau schema +- Memutuskan strategi caching, state management, API design +- Mengubah konfigurasi infrastruktur + +--- + +## Best Practices + +1. **Buat sebelum implementasi** -- ADR adalah keputusan, bukan catatan setelah coding +2. **Satu ADR per keputusan** -- Jangan campur beberapa keputusan dalam satu ADR +3. **Nomor berurutan** -- ADR-001, ADR-002, dst. +4. **Immutable setelah accepted** -- Jika berubah, buat ADR baru yang supersede +5. **Kaitkan dengan kode** -- Referensikan ADR di commit messages dan PR descriptions +6. **Review berkala** -- Pastikan ADR yang deprecated ditandai dengan jelas diff --git a/.docs/adr/adr-001-product-pivot-contribution-hub.md b/.docs/adr/adr-001-product-pivot-contribution-hub.md new file mode 100644 index 0000000..c4a177c --- /dev/null +++ b/.docs/adr/adr-001-product-pivot-contribution-hub.md @@ -0,0 +1,83 @@ +# ADR-001: Product Pivot ke Contribution Hub + +## Status + +**Accepted** + +## Context + +Project ini awalnya dimulai sebagai starter foundation project untuk Next.js 16 dengan FSD architecture. ROADMAP.md sebelumnya menyebutkan visi sebagai "platform referensi film modern." Namun, setelah evaluasi lebih lanjut, beberapa faktor mendorong perubahan arah: + +1. **Market terlalu kompetitif** untuk platform referensi film (TMDB, IMDb, MyDramaList sudah dominan) +2. **Tidak ada diferensiasi kuat** yang bisa dibangun di area tersebut dengan sumber daya terbatas +3. **Tim/Founder memiliki latar belakang dan passion di developer tooling** +4. **Kebutuhan nyata** di ekosistem developer Indonesia: tools untuk membantu konsistensi coding dan dokumentasi progress +5. **Foundation project sudah solid** secara teknis dan bisa diarahkan ke domain apapun + +## Decision + +Kami memutuskan untuk mengubah arah produk dari "platform referensi film" menjadi **Contribution Hub: Developer Activity Assistant**. + +## Rationale + +1. **Market need validation**: Banyak developer Indonesia mengalami masalah konsistensi coding, dokumentasi progress, dan personal branding di GitHub. Produk ini menyelesaikan masalah nyata. + +2. **Teknologi alignment**: Tech stack yang sudah ada (Next.js, React Query, Zustand, Zod) sangat cocok untuk membangun developer tooling. GitHub API integration adalah use case yang natural. + +3. **Differentiation**: Tidak ada produk Indonesia yang secara spesifik membahas "developer activity assistant" dengan fokus pada meaningful contribution. + +4. **Scalability**: Produk bisa tumbuh dari personal tool ke team platform ke ecosystem. + +5. **Developer empathy**: Kami memahami masalah ini karena kami juga mengalaminya sebagai developer. + +6. **Foundation preservation**: Struktur FSD, coding standards, dan dokumentasi yang sudah dibangun tetap relevan dan bisa digunakan langsung. + +## Consequences + +### Positive + +- Market niche yang jelas dan underserved +- Developer tooling memiliki recurring usage pattern (harian) +- Potensi community building yang kuat (developer-centric) +- Teknologi stack yang ada sangat cocok +- Bisa dimonetisasi dengan freemium model yang jelas + +### Negative + +- Target pasar lebih kecil dibanding platform referensi film +- Membutuhkan integrasi dengan GitHub API (dependency eksternal) +- Developer tools butuh trust building yang lebih lama +- Kompetisi dengan tools internasional (WakaTime, GitClear) + +### Neutral + +- Nama project (next-app) dan domain perlu diubah +- Dokumentasi produk harus ditulis ulang (dilakukan di ADR ini) +- Branding perlu dibangun dari awal + +## Alternatives Considered + +### Alternative 1: Tetap dengan platform referensi film + +- Pros: Market besar, banyak yang bisa dipelajari +- Cons: Kompetisi ketat, tidak ada diferensiasi, butuh data konten yang besar +- **Ditolak** karena tidak ada competitive advantage + +### Alternative 2: Productivity tool general + +- Pros: Market sangat luas +- Cons: Terlalu generic, susah positioning, banyak kompetitor +- **Ditolak** karena terlalu broad + +### Alternative 3: Coding bootcamp platform + +- Pros: Tren edtech sedang naik +- Cons: Butuh konten edukasi, operasional berat, business model kompleks +- **Ditolak** karena terlalu berat untuk solo developer + +## References + +- [Product Definition](../product/definition.md) +- [Product Vision](../product/vision.md) +- [Product Roadmap](../product/roadmap.md) +- [ADR-002: Meaningful Contribution Philosophy](./adr-002-meaningful-contribution-philosophy.md) diff --git a/.docs/adr/adr-002-meaningful-contribution-philosophy.md b/.docs/adr/adr-002-meaningful-contribution-philosophy.md new file mode 100644 index 0000000..2e4b4f7 --- /dev/null +++ b/.docs/adr/adr-002-meaningful-contribution-philosophy.md @@ -0,0 +1,142 @@ +# ADR-002: Meaningful Contribution Philosophy + +## Status + +**Accepted** + +## Context + +Salah satu risiko terbesar dalam membangun tools yang berhubungan dengan GitHub contributions adalah insentif yang salah. Banyak tools dan layanan yang secara tidak sengaja (atau sengaja) mendorong perilaku spam: + +- Commit kosong untuk mempertahankan streak +- Repository boilerplate yang tidak bermakna +- Automated activity yang tidak merepresentasikan pekerjaan nyata +- "GitHub grass" farming tools + +Kami harus mendefinisikan secara eksplisit apa arti "meaningful contribution" dalam konteks produk kami dan bagaimana memastikan produk tidak disalahgunakan. + +## Decision + +Kami mengadopsi **Meaningful Contribution Philosophy** sebagai prinsip desain inti produk. Filosofi ini memiliki empat pilar: + +### Pilar 1: Authenticity + +Setiap aktivitas yang tercatat di Contribution Hub harus merepresentasikan pekerjaan developer yang autentik. Kami mendefinisikan aktivitas autentik sebagai: + +- **Kode yang ditulis**: Commit yang mengandung perubahan kode berarti (bukan whitespace, rename file tanpa perubahan, atau template boilerplate) +- **Kode yang direview**: PR review dengan komentar substantif +- **Diskusi teknis**: Issue/PR discussion yang mengandung kontribusi pemikiran +- **Dokumentasi**: Perubahan dokumentasi yang menambah nilai + +### Pilar 2: Context + +Setiap aktivitas harus memiliki konteks yang jelas: + +- Commit message yang deskriptif (bukan "update" atau "fix") +- PR dengan deskripsi yang jelas tentang apa dan mengapa +- Issue dengan reproduksi langkah atau proposal yang jelas + +### Pilar 3: Progression + +Aktivitas harus menunjukkan progression: + +- Kode yang berkembang (bukan rewrite berulang) +- Fitur yang selesai (bukan setengah jadi) +- Bug yang diperbaiki (bukan introduced) +- Pengetahuan yang bertambah (learning commits) + +### Pilar 4: Impact + +Aktivitas harus memiliki impact: + +- Digunakan oleh orang lain (downloads, stars, forks) +- Membantu orang lain (answer, review, mentoring) +- Membangun sesuatu yang berguna (bukan tutorial copy-paste) + +## Implementation Rules + +### What We Count + +| Aktivitas | Dihitung? | Syarat | +| ---------------------------- | ----------- | ------------------------------------- | +| Commit dengan perubahan kode | Ya | > 5 lines changed, meaningful message | +| Commit dokumentasi | Ya | Menambah/memperbaiki dokumentasi | +| PR dibuat | Ya | Dengan deskripsi meaningful | +| PR review | Ya | Dengan komentar substantif | +| Issue dibuat | Ya | Dengan deskripsi jelas | +| Code review comment | Ya | Substantif, bukan "LGTM" saja | +| Release | Ya | Dengan release notes | +| Commit "fix typo" | Conditional | Hanya jika ada perubahan berarti | +| Fork repository | Tidak | Tidak menunjukkan aktivitas | +| Star repository | Tidak | Tidak menunjukkan aktivitas | +| Commit di repository kosong | Tidak | Tidak meaningful | +| Force push | Tidak | Sering untuk rewrite history | + +### Anti-Gaming Mechanisms + +1. **Minimum threshold**: Aktivitas harus melewati threshold minimum untuk dihitung (misal: commit harus mengubah minimal 5 baris kode) + +2. **Rate limiting**: Maksimum aktivitas yang bisa dicatat per jam untuk mencegah spam + +3. **Anomaly detection**: Deteksi pola mencurigakan (misal: 50 commit dalam 5 menit) + +4. **Manual flagging**: Pengguna bisa melaporkan aktivitas yang mencurigakan + +5. **Streak freeze, not streak hack**: Grace period untuk streak, bukan cara untuk memperpanjang streak artifisial + +### What We Never Do + +- Tidak pernah membuat commit atas nama pengguna +- Tidak pernah generate aktivitas palsu +- Tidak pernah memanipulasi statistik GitHub +- Tidak pernah mendorong pengguna untuk membuat kontribusi artifisial +- Tidak pernah menyembunyikan atau memfilter data untuk membuat profile terlihat lebih baik dari kenyataan + +## Consequences + +### Positive + +- Produk memiliki diferensiasi yang jelas dari GitHub automation tools lain +- Membangun trust dengan pengguna yang peduli kualitas +- Menarik pengguna yang tepat (quality-seekers, bukan spammer) +- Fondasi etis yang kuat untuk pengembangan jangka panjang + +### Negative + +- Batasan ini mengurangi potensi pertumbuhan jumlah pengguna +- Beberapa pengguna potensial akan memilih tools lain yang lebih "relaxed" +- Perlu investasi lebih dalam deteksi dan filtering aktivitas +- Kompleksitas teknis untuk membedakan meaningful vs non-meaningful activity + +### Neutral + +- Perlu edukasi pengguna tentang filosofi ini +- Beberapa metrik engagement akan lebih rendah secara numerik +- Tapi metrik kualitas (retensi, satisfaksi) seharusnya lebih tinggi + +## Alternatives Considered + +### Alternative 1: Tidak ada filter (hitung semua aktivitas) + +- Pros: Implementasi lebih sederhana, lebih banyak aktivitas tercatat +- Cons: Rentan spam, tidak membedakan kualitas +- **Ditolak** karena bertentangan dengan value produk + +### Alternative 2: Filter ketat (hanya commit kode) + +- Pros: Kualitas terjamin +- Cons: Dokumentasi dan review tidak dihitung, tidak merepresentasikan aktivitas developer holistik +- **Ditolak** karena terlalu sempit + +### Alternative 3: User-defined filters + +- Pros: Fleksibel untuk setiap pengguna +- Cons: Kompleksitas tinggi, sulit konsisten antar pengguna +- **Ditolak** karena akan menyebabkan inkonsistensi dan potensi penyalahgunaan + +## References + +- [ADR-001: Product Pivot](./adr-001-product-pivot-contribution-hub.md) +- [Product Definition](../product/definition.md) +- [Product Vision](../product/vision.md) +- [Habit System Feature Spec](../product/features/habit-system.md) diff --git a/.docs/ai/README.md b/.docs/ai/README.md new file mode 100644 index 0000000..07126cf --- /dev/null +++ b/.docs/ai/README.md @@ -0,0 +1,113 @@ +# ai/ -- AI Agent Collaboration Guide + +> Panduan kolaborasi antara developer dan AI coding agents. +> Dibuat agar AI agents bisa bekerja efektif dan konsisten. + +--- + +## Tujuan + +Folder ini berisi dokumentasi untuk memastikan AI agents (OpenCode, Claude, Copilot, dll.) dapat: + +- Memahami konteks project dengan cepat +- Mengikuti coding standards yang sudah ditetapkan +- Menghasilkan kode yang konsisten dengan arsitektur +- Tidak melanggar boundaries yang sudah ditentukan +- Berkolaborasi efektif dengan developer manusia + +--- + +## Struktur yang Direkomendasikan + +```txt +ai/ +├── README.md # File ini -- panduan folder +├── agent-rules.md # Aturan umum untuk AI agents +├── prompt-templates.md # Template prompt untuk task umum +├── boundaries.md # Apa yang BOLEH dan TIDAK BOLEH dilakukan AI +├── context-files.md # File yang harus dibaca AI untuk konteks +└── review-checklist.md # Checklist untuk AI code review +``` + +--- + +## Template Agent Rules + +```markdown +# Agent Rule: [Judul] + +## Trigger + +[Kapan aturan ini berlaku] + +## Rule + +[Aturan yang harus diikuti] + +## Example + +[Contoh implementasi yang benar] + +## Anti-Example + +[Contoh yang salah] +``` + +--- + +## AI Agent Boundaries + +### Boleh Dilakukan + +- Refactor kode (rename, extract, restructure) +- Menulis unit test, integration test +- Memperbaiki type errors +- Menambah dokumentasi +- Optimasi performa minor +- Membuat komponen baru sesuai pattern existing + +### Tidak Boleh Dilakukan Tanpa Approval + +- Mengubah arsitektur inti +- Menghapus folder atau file existing +- Mengubah konfigurasi core (tsconfig, next.config) +- Menambah dependencies baru +- Mengubah API interface yang sudah dipakai +- Melakukan migration database + +--- + +## Prompt Convention + +Gunakan format berikut saat memberi instruksi ke AI agent: + +```markdown +Task: [Deskripsi singkat] +File: [Path file yang diubah] +Pattern: [Referensi pattern yang sudah ada] +Scope: [Batasan perubahan] +Verify: [Command untuk verifikasi] +``` + +--- + +## Context Hierarchy + +AI agents harus membaca file dalam urutan ini untuk memahami project: + +1. `AGENTS.md` -- General engineering handbook +2. `CLAUDE.md` -- AI-specific instructions +3. `.docs/ai/` -- AI collaboration guide +4. `.docs/architecture/` -- Architecture documentation +5. `.docs/engineering/` -- Coding standards +6. Feature-specific documentation + +--- + +## Best Practices + +1. **Selalu baca AGENTS.md** -- Ini adalah single source of truth untuk engineering practices +2. **Gunakan template prompt** -- Memastikan instruksi jelas dan lengkap +3. **Verifikasi output** -- Jangan trust output AI tanpa review +4. **Update dokumentasi** -- Jika ada pattern baru, update dokumentasi AI +5. **Report issues** -- Jika AI sering membuat kesalahan tertentu, catat di sini diff --git a/.docs/ai/ai-collaboration.md b/.docs/ai/ai-collaboration.md new file mode 100644 index 0000000..27ea8cf --- /dev/null +++ b/.docs/ai/ai-collaboration.md @@ -0,0 +1,130 @@ +# AI Collaboration Rules + +> Aturan kolaborasi AI agent dengan codebase dan dependency usage. + +--- + +## Sebelum Memulai Task + +AI agent WAJIB membaca dokumentasi berikut (urutan prioritas): + +1. `AGENTS.md` -- Engineering handbook root +2. `.docs/AGENTS.md` -- Documentation center guide +3. `.docs/architecture/system-overview.md` -- System architecture +4. `.docs/architecture/folder-structure.md` -- Folder structure +5. `.docs/technical/tech-stack.md` -- Tech stack & dependencies +6. `.docs/engineering/engineering-principles.md` -- Engineering rules +7. `.docs/ai/` -- AI collaboration guide (file ini) + +--- + +## Dependency Usage Rules + +### React Query + +- Query keys: gunakan array `['resource', 'id']` convention +- Stale time: minimal 30s untuk data yang jarang berubah +- Gunakan `useSuspenseQuery` untuk data critical +- Mutation: selalu handle `onError` dengan toast + +### Zustand + +- Store per feature, jangan store global raksasa +- Slice pattern untuk store yang besar +- Jangan fetch data di store -- itu tugas React Query +- Store hanya untuk UI state dan client-only data + +### shadcn/ui + +- Install per-component, jangan bulk install +- Jangan modify komponen shadcn secara langsung +- Kustomisasi melalui `className` dan `cn()` utility +- Contoh: `npx shadcn@latest add button dialog form` + +### React Hook Form + +- Gunakan `@hookform/resolvers/zod` untuk validasi +- Form component terpisah dari page/logic +- `useFormContext` untuk nested form components + +### next-intl + +- Gunakan `useTranslations()` untuk client components +- Gunakan `getTranslations()` untuk server components +- Jangan hardcode string, selalu gunakan message keys + +### next-themes + +- Gunakan `useTheme()` untuk akses theme +- Theme toggle component di header/settings +- CSS variables untuk dark mode styling + +--- + +## AI Agent Boundaries + +### Boleh Dilakukan + +- Mengimplementasikan fitur dengan pattern yang sudah ada +- Menambah komponen shadcn/ui baru +- Menambah query/mutation dengan React Query +- Membuat Zustand store untuk UI state +- Menambah form dengan React Hook Form + Zod +- Membuat provider wrapper baru +- Refactor kecil (rename, extract, type fix) +- Update dokumentasi + +### Tidak Boleh Dilakukan Tanpa Approval + +- Mengganti state management tool (React Query ke SWR, dll) +- Mengganti library inti (shadcn ke MUI, dll) +- Mengubah arsitektur provider +- Menghapus folder atau file existing +- Menambah dependencies baru tanpa dokumentasi +- Mengubah konfigurasi core (tsconfig, tailwind.config, next.config) + +--- + +## Prompt Convention + +``` +Task: [Deskripsi singkat] +File: [Path file yang diubah] +Pattern: [Referensi pattern yang sudah ada] +Dependencies: [Package yang digunakan] +Scope: [Batasan perubahan] +Verify: [Command untuk verifikasi] +``` + +### Contoh + +``` +Task: Tambahkan form login +File: src/features/auth/components/login-form.tsx +Pattern: src/features/auth/components/register-form.tsx (existing) +Dependencies: react-hook-form, zod, @tanstack/react-query +Scope: Hanya file di src/features/auth/ +Verify: bun check-types && bun test +``` + +--- + +## Review Checklist + +Setiap perubahan (terutama yang menggunakan dependencies baru) harus di-review: + +- [ ] Type error? (bun check-types) +- [ ] Pattern konsisten dengan existing code? +- [ ] Dependency digunakan dengan benar? +- [ ] Error handling lengkap? +- [ ] Loading/error states terhandle? +- [ ] i18n string (bukan hardcode)? +- [ ] Aksesibilitas terjaga? +- [ ] Test passing? + +--- + +## Related Documents + +- [Prompting Guidelines](./prompting-guidelines.md) +- [Engineering Principles](../engineering/engineering-principles.md) diff --git a/.docs/ai/prompting-guidelines.md b/.docs/ai/prompting-guidelines.md new file mode 100644 index 0000000..e912cb0 --- /dev/null +++ b/.docs/ai/prompting-guidelines.md @@ -0,0 +1,161 @@ +# Prompting Guidelines + +> Panduan memberikan instruksi yang efektif ke AI agent. + +--- + +## Prompt Structure + +Setiap prompt ke AI agent harus mencakup 5 elemen: + +``` +Task: [Satu kalimat deskriptif] +File: [Exact path file yang diubah/dibuat] +Pattern: [Reference ke pattern existing yang harus diikuti] +Scope: [Batasan jelas -- file/folder mana saja] +Verify: [Command untuk memverifikasi hasil] +``` + +--- + +## Prompt Examples + +### Example 1: Menambah fitur dengan React Query + +``` +Task: Tambahkan halaman daftar user dengan data fetching +File: src/features/users/page.tsx +Pattern: src/features/posts/page.tsx (useSuspenseQuery pattern) +Dependencies: @tanstack/react-query +Scope: src/features/users/ +Exclude: Jangan ubah komponen existing di src/components/ +Verify: bun check-types +``` + +### Example 2: Form dengan React Hook Form + Zod + +``` +Task: Buat form edit profil +File: src/features/profile/components/edit-form.tsx +Pattern: src/features/auth/components/login-form.tsx (RHF + Zod pattern) +Dependencies: react-hook-form, zod, @hookform/resolvers +Scope: src/features/profile/ +Verify: bun check-types && bun test +``` + +### Example 3: Provider wrapper baru + +``` +Task: Buat AnalyticsProvider wrapper +File: src/components/providers/analytics-provider.tsx +Pattern: src/components/providers/theme-provider.tsx (provider pattern) +Scope: src/components/providers/ + update app/layout.tsx +Verify: bun check-types +``` + +--- + +## Anti-Patterns (Jangan Lakukan) + +### ❌ Prompt terlalu abstrak + +``` +Buat fitur login. // Terlalu abstrak, tidak ada detail implementasi +``` + +### ❌ Prompt tanpa referensi pattern + +``` +Buat form di halaman user. // Agent tidak tahu pattern form yang dipakai +``` + +### ❌ Prompt tanpa scope + +``` +Tambahin state management. // Tidak jelas store baru atau modifikasi existing +``` + +### ❌ Prompt tanpa verification + +``` +Benerin error di halaman. // Tidak jelas cara memverifikasi perbaikan +``` + +--- + +## Prompt Templates + +### New Feature + +``` +Task: Tambahkan [fitur] di halaman [page] +File: [path] +Pattern: [reference path] +Dependencies: [daftar package] +Scope: [batasan folder] +Verify: [bun check-types, bun test, dll] +``` + +### Bug Fix + +``` +Task: Fix [bug description] +File: [path where bug exists] +Root Cause: [analyzed root cause] +Pattern: [how similar bugs were fixed] +Scope: [files to modify] +Verify: [bun check-types, manual test scenario] +``` + +### Refactor + +``` +Task: Refactor [component/hook] +File: [path] +Goal: [what the refactor achieves] +Preserve: [behavior that must NOT change] +Pattern: [reference pattern] +Scope: [files affected] +Verify: [existing tests pass, no type errors] +``` + +--- + +## Provider & Refactor Guidelines + +### Saat Membuat Provider Baru + +1. Buat wrapper component di `src/components/providers/` +2. Gunakan `'use client'` +3. Props: minimal `{ children: React.ReactNode }` +4. Export dari `src/components/providers/index.ts` +5. Tambahkan di `app/providers.tsx` dengan urutan yang tepat + +### Saat Refactor + +1. Jangan ubah API interface yang sudah dipakai +2. Ekstrak logic ke custom hook, jangan simpan di komponen +3. Pastikan test tetap passing +4. Update barrel exports (index.ts) jika ada file baru +5. Update AGENTS.md dan dokumentasi jika perlu + +--- + +## Context Files + +Sebelum memulai task besar, baca file berikut: + +| File | Why | +| --------------------------------------------- | ----------------------- | +| `src/app/providers.tsx` | Provider nesting order | +| `src/config/env.ts` | Environment variables | +| `src/features/[x]/index.ts` | Public API feature | +| `src/components/ui/index.ts` | Available UI components | +| `.docs/engineering/engineering-principles.md` | Engineering rules | + +--- + +## Related Documents + +- [AI Collaboration Rules](./ai-collaboration.md) +- [Engineering Principles](../engineering/engineering-principles.md) diff --git a/.docs/api/README.md b/.docs/api/README.md new file mode 100644 index 0000000..e1b1058 --- /dev/null +++ b/.docs/api/README.md @@ -0,0 +1,119 @@ +# api/ -- Dokumentasi API + +> API endpoints, request/response schemas, integrasi eksternal. + +--- + +## Tujuan + +Folder ini berisi dokumentasi API yang mencakup: + +- REST API endpoints dan spesifikasinya +- Request/response schemas +- Authentication dan authorization untuk setiap endpoint +- Error codes dan error responses +- Rate limiting dan throttling +- Integrasi dengan third-party services +- API versioning strategy + +--- + +## Struktur yang Direkomendasikan + +```txt +api/ +├── README.md # File ini -- panduan folder +├── overview.md # API overview, base URL, auth +├── endpoints/ # Dokumentasi per endpoint +│ ├── auth.md +│ ├── users.md +│ └── ... +├── errors.md # Standard error codes +├── webhooks.md # Webhook documentation +├── third-party.md # Third-party API integrations +└── changelog.md # API changelog +``` + +--- + +## Template Endpoint Documentation + +```markdown +# [METHOD] /api/[path] + +## Description + +[Penjelasan endpoint] + +## Authentication + +[Required auth type] + +## Request + +### Headers + +| Header | Value | Required | +| ------ | ----- | -------- | +| | | | + +### Query Parameters + +| Parameter | Type | Required | Default | Description | +| --------- | ---- | -------- | ------- | ----------- | +| | | | | | + +### Request Body + +\`\`\`json +{ +"field": "value" +} +\`\`\` + +## Response + +### Success (200) + +\`\`\`json +{ +"data": {} +} +\`\`\` + +### Error (4xx/5xx) + +\`\`\`json +{ +"error": { +"code": "ERROR_CODE", +"message": "Human readable message" +} +} +\`\`\` + +## Example + +\`\`\`bash +curl -X [METHOD] https://api.example.com/[path] \ + -H "Authorization: Bearer [token]" +\`\`\` +``` + +--- + +## Best Practices + +1. **Consistent response format** -- Semua response harus format yang konsisten +2. **Version your API** -- Gunakan prefix `/api/v1/`, `/api/v2/` +3. **Document errors** -- Setiap endpoint harus mendokumentasikan error codes +4. **Use Zod schemas** -- Dokumentasi API bisa digenerate dari Zod schemas +5. **Keep docs updated** -- API docs harus sinkron dengan implementasi + +--- + +## Tools + +- **API Route Handler** -- Semua API route di `src/app/api/` +- **Zod** -- Validasi request/response dengan Zod schemas +- **OpenAPI/Swagger** -- Untuk auto-generated API documentation (jika diperlukan) diff --git a/.docs/api/api-guidelines.md b/.docs/api/api-guidelines.md new file mode 100644 index 0000000..91fe4fe --- /dev/null +++ b/.docs/api/api-guidelines.md @@ -0,0 +1,261 @@ +# API Guidelines + +> Panduan API development, React Query strategy, dan endpoint design. + +--- + +## API Architecture + +### Routes + +``` +src/app/api/[resource]/route.ts # REST API endpoints +src/app/api/[resource]/[id]/route.ts +``` + +### Data Fetching + +``` +src/lib/api-client.ts # Centralized HTTP client +src/features/[feature]/api/ # Feature-specific API functions +``` + +--- + +## React Query Strategy + +### Query Key Convention + +```tsx +// Format: ['resource', ...identifiers, ...filters] +const keys = { + posts: { + all: ["posts"] as const, + list: (filters?: PostFilters) => ["posts", "list", filters] as const, + detail: (id: string) => ["posts", "detail", id] as const + }, + users: { + all: ["users"] as const, + list: (filters?: UserFilters) => ["users", "list", filters] as const, + detail: (id: string) => ["users", "detail", id] as const + } +} +``` + +### Default Config + +```tsx +const queryClient = new QueryClient({ + defaultOptions: { + queries: { + staleTime: 1000 * 60 * 5, // 5 menit + gcTime: 1000 * 60 * 30, // 30 menit garbage collection + retry: 1, + refetchOnWindowFocus: false // Matikan untuk development + } + } +}) +``` + +### Query Patterns + +```tsx +// ✅ RSC Pattern (server component) +async function PostsPage() { + const posts = await fetch("/api/posts").then((r) => r.json()) + return +} + +// ✅ Client Pattern dengan React Query +function usePosts(filters?: PostFilters) { + return useQuery({ + queryKey: keys.posts.list(filters), + queryFn: () => getPosts(filters), + select: (data) => data.items // Transform response + }) +} + +// ✅ Infinite Query untuk pagination +function useInfinitePosts() { + return useInfiniteQuery({ + queryKey: keys.posts.all, + queryFn: ({ pageParam }) => getPosts({ page: pageParam }), + initialPageParam: 1, + getNextPageParam: (last) => last.nextPage + }) +} +``` + +### Mutation Patterns + +```tsx +// ✅ Standard mutation +function useCreatePost() { + const queryClient = useQueryClient() + + return useMutation({ + mutationFn: createPost, + onSuccess: () => { + queryClient.invalidateQueries({ queryKey: keys.posts.all }) + toast.success("Post berhasil dibuat") + }, + onError: (error) => { + toast.error(error.message) + } + }) +} + +// ✅ Optimistic update +function useUpdatePost() { + const queryClient = useQueryClient() + + return useMutation({ + mutationFn: updatePost, + onMutate: async (updated) => { + await queryClient.cancelQueries({ + queryKey: keys.posts.detail(updated.id) + }) + const previous = queryClient.getQueryData(keys.posts.detail(updated.id)) + queryClient.setQueryData(keys.posts.detail(updated.id), updated) + return { previous } + }, + onError: (err, vars, context) => { + queryClient.setQueryData(keys.posts.detail(vars.id), context?.previous) + toast.error("Gagal update") + }, + onSettled: () => { + queryClient.invalidateQueries({ queryKey: keys.posts.all }) + } + }) +} +``` + +--- + +## API Endpoint Design + +### Standard Response Format + +```tsx +// Success +{ + "data": T | T[], + "meta"?: { + "total": number, + "page": number, + "pageSize": number + } +} + +// Error +{ + "error": { + "code": "VALIDATION_ERROR", + "message": "Human readable message", + "details"?: ZodError[] + } +} +``` + +### Endpoint Structure + +```tsx +// src/app/api/posts/route.ts +export async function GET(req: NextRequest) { + const searchParams = req.nextUrl.searchParams + const schema = z.object({ + page: z.coerce.number().default(1), + limit: z.coerce.number().default(10) + }) + + const parsed = schema.safeParse(Object.fromEntries(searchParams)) + if (!parsed.success) { + return NextResponse.json( + { + error: { + code: "VALIDATION_ERROR", + message: "Invalid params", + details: parsed.error + } + }, + { status: 400 } + ) + } + + const data = await getPosts(parsed.data) + return NextResponse.json({ data }) +} + +export async function POST(req: NextRequest) { + const body = await req.json() + const schema = z.object({ + title: z.string().min(1).max(200), + content: z.string().min(1) + }) + + const parsed = schema.safeParse(body) + if (!parsed.success) { + return NextResponse.json( + { error: { code: "VALIDATION_ERROR", message: "Invalid input" } }, + { status: 400 } + ) + } + + const post = await createPost(parsed.data) + return NextResponse.json({ data: post }, { status: 201 }) +} +``` + +--- + +## Server Actions + +Untuk form submission, prioritaskan Server Actions: + +```tsx +// ✅ Standard Server Action +"use server" + +import { z } from "zod" + +const schema = z.object({ + email: z.string().email(), + password: z.string().min(8) +}) + +export async function login(prev: any, formData: FormData) { + const parsed = schema.safeParse({ + email: formData.get("email"), + password: formData.get("password") + }) + + if (!parsed.success) { + return { error: "Input tidak valid", issues: parsed.error.issues } + } + + try { + await authService.login(parsed.data) + return { success: true } + } catch (error) { + return { error: "Login gagal" } + } +} +``` + +--- + +## API Security + +- Validasi input dengan Zod di semua entry point +- Rate limiting untuk endpoint publik +- CORS configuration yang tepat +- Authentication check di setiap protected endpoint +- Jangan expose internal IDs ke client + +--- + +## Related Documents + +- [System Overview](../architecture/system-overview.md) +- [Engineering Principles](../engineering/engineering-principles.md) +- [Security Baseline](../security/security-baseline.md) diff --git a/.docs/architecture/README.md b/.docs/architecture/README.md new file mode 100644 index 0000000..f6f09e6 --- /dev/null +++ b/.docs/architecture/README.md @@ -0,0 +1,97 @@ +# architecture/ -- Dokumentasi Arsitektur + +> System architecture, data flow, module boundaries, dan architectural patterns. + +--- + +## Tujuan + +Folder ini berisi dokumentasi arsitektur yang mencakup: + +- High-level system architecture +- Module boundaries dan dependency graph +- Data flow diagrams +- Component hierarchy +- State management architecture +- API architecture dan integration patterns +- Error handling strategy +- Performance architecture + +--- + +## Struktur yang Direkomendasikan + +```txt +architecture/ +├── README.md # File ini -- panduan folder +├── overview.md # High-level architecture overview +├── module-boundaries.md # FSD module boundaries dan dependency rules +├── data-flow.md # Data flow diagrams dan penjelasan +├── state-management.md # State management architecture +├── error-handling.md # Error handling strategy +├── performance.md # Performance architecture decisions +└── diagrams/ # Architecture diagrams (Mermaid, Excalidraw) +``` + +--- + +## Template Architecture Document + +```markdown +# [Architecture Concern] + +## Context + +[Latar belakang dan problem statement] + +## Decision + +[Keputusan arsitektur yang diambil] + +## Rationale + +[Mengapa memilih pendekatan ini] + +## Consequences + +[Dampak positif dan negatif dari keputusan ini] + +## Diagram + +[Link atau embed diagram arsitektur] +``` + +--- + +## Architecture Principles + +1. **Separation of Concerns** -- Pisahkan UI, logic, state, dan API +2. **Dependency Inversion** -- Module abstrak tidak boleh dependen pada module konkret +3. **Explicit Boundaries** -- Batas module harus jelas dan ditegakkan +4. **Loose Coupling** -- Module harus bisa berubah secara independen +5. **High Cohesion** -- Module harus berisi hal-hal yang saling terkait +6. **Consistent Patterns** -- Gunakan pola yang sama untuk masalah yang sama + +--- + +## Feature-Sliced Design (FSD) Layers + +``` +app/ -- Entry point, routing, global layout +├── processes/ -- Business processes (multi-step) +├── pages/ -- Halaman aplikasi (composed dari features) +├── features/ -- Feature modules (self-contained) +├── entities/ -- Business entities +└── shared/ -- Shared UI, lib, API, config +``` + +Lihat dokumentasi FSD di `AGENTS.md` untuk detail lebih lanjut. + +--- + +## Tools for Architecture Documentation + +- **Mermaid.js** -- Diagram sebagai kode (sequence, flow, class) +- **Excalidraw** -- Whiteboard-style diagrams (simpan di `diagrams/`) +- **PlantUML** -- Text-based UML diagrams +- **SVG** -- Untuk diagram statis yang complex diff --git a/.docs/architecture/folder-structure.md b/.docs/architecture/folder-structure.md new file mode 100644 index 0000000..bb37535 --- /dev/null +++ b/.docs/architecture/folder-structure.md @@ -0,0 +1,122 @@ +# Folder Structure + +> Struktur folder project berdasarkan Feature-Sliced Design (FSD). + +--- + +## Root Structure + +``` +src/ +├── app/ # Next.js App Router pages & layouts +├── components/ # Shared components +│ ├── ui/ # shadcn/ui atomic components +│ ├── layouts/ # Header, footer, sidebar, container +│ ├── widgets/ # Cards, modals, data tables +│ └── providers/ # Theme, Query, Auth providers +├── config/ # Env, site config, navigation +├── constants/ # Global constants, magic numbers +├── data/ # Static data, lookup tables +├── features/ # Feature-Sliced Design modules +│ └── [feature]/ +│ ├── api/ # API calls + hooks untuk fitur +│ ├── components/ # Komponen spesifik fitur +│ ├── hooks/ # Hooks spesifik fitur +│ ├── stores/ # Zustand stores spesifik fitur +│ ├── types/ # Types spesifik fitur +│ └── utils/ # Utilities spesifik fitur +├── hooks/ # Generic reusable hooks +├── i18n/ # next-intl config & messages +├── lib/ # Third-party integrations +├── styles/ # Global CSS, variables +├── testing/ # Test setup & utilities +├── types/ # Global type definitions +└── utils/ # Pure utility functions +``` + +--- + +## Provider Structure + +``` +src/components/providers/ +├── query-provider.tsx # React Query provider +├── theme-provider.tsx # next-themes provider +├── i18n-provider.tsx # next-intl provider +├── auth-provider.tsx # Auth session provider +└── index.ts # Composed providers export +``` + +Nesting order di `app/layout.tsx`: + +```tsx + + + + {children} + + + +``` + +--- + +## Hooks Structure + +``` +src/hooks/ +├── use-debounce.ts # Debounce value +├── use-media-query.ts # Responsive breakpoint +├── use-local-storage.ts # Persisted state +├── use-intersection-observer.ts # Scroll detection +└── index.ts +``` + +Feature-specific hooks → `src/features/[feature]/hooks/` + +--- + +## Store Structure (Zustand) + +``` +src/features/[feature]/stores/ +├── [feature]-store.ts # Zustand store +└── index.ts +``` + +Global stores jika ada → `src/stores/` + +--- + +## Dependency Flow + +``` +app/ (routing, layout) + │ + ▼ +components/ (UI, layout, widgets, providers) + │ + ├──▶ features/ (FSD modules) + │ │ + │ └──▶ api/, hooks/, stores/, types/, utils/ + │ + ├──▶ hooks/ (generic) + ├──▶ utils/ (pure functions) + ├──▶ lib/ (integrations) + ├──▶ config/ + constants/ + data/ + └──▶ types/ (global) +``` + +**Rules:** + +- features/ tidak boleh import features/ lain secara langsung +- app/ hanya import komponen, bukan implementasi detail +- utils/ harus pure -- tidak boleh import dari folder lain kecuali types/constants +- hooks/ tidak boleh import dari features/ atau components/ + +--- + +## Related Documents + +- [System Overview](./system-overview.md) +- [Tech Stack](../technical/tech-stack.md) diff --git a/.docs/architecture/product-architecture.md b/.docs/architecture/product-architecture.md new file mode 100644 index 0000000..1b2a0b0 --- /dev/null +++ b/.docs/architecture/product-architecture.md @@ -0,0 +1,255 @@ +# Product Architecture -- Contribution Hub + +> Bagaimana product direction Contribution Hub memetakan ke arsitektur sistem berbasis FSD. + +--- + +## Domain Model + +### Core Entities + +```typescript +// Entitas inti dalam domain Contribution Hub +User — Developer yang menggunakan platform +Activity — Aktivitas coding (commit, PR, issue, review) +Repository — Repository GitHub yang dilacak +Streak — Data streak dan konsistensi +Goal — Target harian/mingguan pengguna +Summary — Daily/weekly activity summary +Badge — Achievement badges +``` + +### Entity Relationships + +``` +User (1) ──── (N) Activity +User (1) ──── (N) Repository +User (1) ──── (1) Streak +User (1) ──── (N) Goal +User (1) ──── (N) Summary +User (1) ──── (N) Badge +Repository (1) ──── (N) Activity +``` + +--- + +## Feature Modules Mapping (FSD) + +Setiap fitur Contribution Hub akan diimplementasikan sebagai FSD module di `src/features/`: + +### Fase 1 Features + +``` +src/features/ +├── auth/ # Better Auth integration (GitHub OAuth) +│ ├── api/ # auth API calls +│ ├── components/ # login button, auth guard +│ ├── hooks/ # use-session, use-auth +│ └── types/ # auth types +│ +├── activity/ # GitHub Activity Tracking +│ ├── api/ # GitHub sync, activity CRUD +│ ├── components/ # activity timeline, activity card +│ ├── hooks/ # use-activity-feed, use-activity-stats +│ ├── stores/ # activity filter state +│ ├── types/ # activity types +│ └── utils/ # activity deduplication, categorization +│ +├── dashboard/ # Personal Dashboard +│ ├── components/ # stats cards, charts, overview +│ ├── hooks/ # use-dashboard-data +│ └── types/ # dashboard types +│ +├── habits/ # Developer Habit System +│ ├── api/ # streak API, goal CRUD +│ ├── components/ # streak display, goal tracker, progress bar +│ ├── hooks/ # use-streak, use-goals +│ ├── stores/ # habit config state +│ ├── types/ # streak, goal types +│ └── utils/ # streak calculation algorithm +│ +├── documentation/ # Auto-Documentation +│ ├── api/ # summary CRUD, generation trigger +│ ├── components/ # daily summary card, weekly report +│ ├── hooks/ # use-summary, use-weekly-report +│ ├── types/ # summary types +│ └── utils/ # narrative generation, markdown builder +│ +└── profile/ # GitHub Profile Enhancer + ├── api/ # badge generation, profile deploy + ├── components/ # badge display, profile preview + ├── hooks/ # use-profile-config + ├── stores/ # profile config state + ├── types/ # badge, profile types + └── utils/ # badge SVG generation + +src/components/ +├── ui/ # shadcn/ui components (shared) +├── layouts/ # App shell, sidebar, header +├── widgets/ # ActivityCard, StreakBadge, StatsCard +└── providers/ # QueryProvider, AuthProvider, ThemeProvider +``` + +--- + +## Data Flow Architecture + +### Activity Sync Flow + +``` +GitHub API + │ (polling setiap 15 menit) + ▼ +Background Job (api/cron/route.ts) + │ Fetch events since last_sync + │ + ▼ +Activity Service (src/features/activity/api/) + │ Deduplication logic + │ Kategorisasi aktivitas + │ + ├── Store di database (PostgreSQL) + │ + ▼ +Cache Invalidation + │ React Query cache di-invalidate + │ + ▼ +UI Update (Real-time via React Query refetch) +``` + +### Streak Calculation Flow + +``` +Daily Cron (23:59) + │ + ▼ +Streak Service + │ Ambil aktivitas hari ini + │ Validasi qualifying activity + │ Hitung streak dengan grace period + │ + ├── Update streak data + ├── Cek milestone achievement + └── Generate notification jika streak berubah +``` + +### Documentation Generation Flow + +``` +Daily Summary Cron (23:59) + │ + ▼ +Summary Generator + │ Aggregate aktivitas harian + │ Parse commit messages + │ Generate narrative + │ + ├── Simpan daily summary + │ + ▼ +User Notification (opsional) + │ Toast / Email digest +``` + +--- + +## Shared Layer Dependencies + +### Shared Hooks (`src/hooks/`) + +| Hook | Fungsi | Feature Consumers | +| ----------------- | ---------------------- | ------------------- | +| use-debounce | Debounce value | activity, dashboard | +| use-media-query | Responsive breakpoints | dashboard, habits | +| use-local-storage | Persisted client state | habits, profile | +| use-interval | Polling interval | activity | + +### Shared Utils (`src/utils/`) + +| Util | Fungsi | Feature Consumers | +| ---------------- | ----------------------- | ------------------- | +| format-date | Date formatting | Semua feature | +| cn | Tailwind class merging | Semua feature | +| activity-helpers | Activity categorization | activity, dashboard | +| streak-calc | Streak algorithm | habits | + +### Shared Types (`src/types/`) + +| Type | Description | +| ---------- | ----------------------- | +| Activity | Base activity type | +| Repository | Repository type | +| Streak | Streak data type | +| Goal | Goal configuration type | + +--- + +## State Management Strategy + +| State Type | Tool | Scope | +| -------------------- | ----------------- | ---------------------------------- | +| GitHub Activity Data | React Query | Server state (cache sync results) | +| Streak & Goal Data | React Query | Server state (read mostly) | +| Dashboard Stats | React Query | Server state (aggregated) | +| Filter/Sort State | Zustand | UI state (activity filters) | +| Habit Config | Zustand + persist | Client state (user preferences) | +| Profile Config | Zustand + persist | Client state (display preferences) | +| URL State | Next.js Router | Page params, search params | + +--- + +## API Architecture + +### External Integrations + +| Service | Integration | Authentication | +| ------------ | -------------------------- | -------------- | +| GitHub API | REST API via @octokit/rest | OAuth token | +| GitHub OAuth | Better Auth plugin | OAuth flow | + +### Internal API Routes + +| Endpoint | Method | Feature | +| ------------------------ | --------- | ------------- | +| /api/activities | GET, POST | activity | +| /api/activities/sync | POST | activity | +| /api/activities/stats | GET | dashboard | +| /api/streaks | GET | habits | +| /api/goals | GET, PUT | habits | +| /api/summaries/daily | GET, PUT | documentation | +| /api/summaries/weekly | GET | documentation | +| /api/profile/config | GET, PUT | profile | +| /api/profile/badge/:type | GET | profile | + +--- + +## Background Jobs + +| Job | Schedule | Service | +| ------------------------ | -------------------- | ----------------------- | +| GitHub Activity Sync | Every 15 min | Vercel Cron / node-cron | +| Daily Streak Update | 23:59 daily | node-cron | +| Daily Summary Generation | 23:59 daily | node-cron | +| Weekly Report Generation | Sunday 23:59 | node-cron | +| Profile README Update | Daily (configurable) | GitHub Actions | + +--- + +## Security Considerations + +- GitHub OAuth tokens disimpan encrypted +- Tidak menyimpan data repository private secara permanen +- Rate limiting untuk API endpoints +- Data ekspor hanya dengan izin pengguna +- Privacy-first: pengguna bisa delete semua data kapan saja + +--- + +## Related Documents + +- [System Overview](./system-overview.md) +- [Folder Structure](./folder-structure.md) +- [Tech Stack](../technical/tech-stack.md) +- [Product Definition](../product/definition.md) +- [ADR-001: Product Pivot](../adr/adr-001-product-pivot-contribution-hub.md) diff --git a/.docs/architecture/system-overview.md b/.docs/architecture/system-overview.md new file mode 100644 index 0000000..b55b48a --- /dev/null +++ b/.docs/architecture/system-overview.md @@ -0,0 +1,125 @@ +# System Overview + +> Arsitektur sistem, provider architecture, dan application flow. + +--- + +## High-Level Architecture + +``` +Client (Browser) + │ + ▼ +Next.js App Router (RSC + RCC) + │ + ├── Providers Layer + │ ├── ThemeProvider (next-themes) + │ ├── QueryProvider (React Query) + │ ├── AuthProvider (auth context) + │ └── I18nProvider (next-intl) + │ + ├── Feature Modules (FSD) + │ ├── features/auth/ + │ ├── features/profile/ + │ └── features/content/ + │ + ├── Shared Layer + │ ├── components/ui/ (shadcn) + │ ├── components/layouts/ + │ ├── components/widgets/ + │ ├── hooks/ + │ └── utils/ + │ + └── API Layer + ├── Server Actions + ├── Route Handlers (app/api/) + └── External Services (src/lib/) +``` + +--- + +## Provider Architecture + +Provider nesting order (dari root layout): + +```tsx +// src/app/providers.tsx + + {" "} + // 1. Server state (React Query) + + {" "} + // 2. Theme (next-themes) + + {" "} + // 3. i18n (next-intl) + + {" "} + // 4. Auth (kustom) + {children} + + + + +``` + +### Provider Responsibilities + +| Provider | Package | Responsibility | +| ------------- | --------------------- | ------------------------------------------------------- | +| QueryProvider | @tanstack/react-query | Server state caching, deduplication, optimistic updates | +| ThemeProvider | next-themes | Dark/light mode, system preference, persistence | +| I18nProvider | next-intl | Locale detection, translations, formatting | +| AuthProvider | kustom (Better Auth) | Session management, user context | + +### Rules + +- Provider tidak boleh mengandung business logic +- Provider tidak boleh render UI visual +- Provider harus pure wrapper component +- Setiap provider harus registered di root layout + +--- + +## Application Flow + +### Data Flow Pattern + +``` +User Action + → Client Component (event handler) + → React Query / Server Action + → API Route / External Service + → Response + ← Cache update + ← UI re-render + ← User sees result +``` + +### State Management Strategy + +| State Type | Tool | Scope | +| ------------ | --------------- | ------------------------------------- | +| Server state | React Query | Data dari API, cache, pagination | +| Client state | Zustand | UI state, preferences, form data | +| URL state | Next.js Router | Query params, pathname, search params | +| Form state | React Hook Form | Form fields, validation, submission | +| Theme state | next-themes | Dark/light mode | + +--- + +## Key Architecture Decisions + +1. **RSC by default** -- Gunakan Server Components kecuali butuh interaktivitas +2. **Client boundary** -- 'use client' di leaf components, bukan di layout +3. **Data fetching** -- React Query untuk client-side, Server Actions untuk mutations +4. **Form handling** -- React Hook Form + Zod untuk validasi +5. **i18n** -- next-intl dengan locale detection otomatis + +--- + +## Related Documents + +- [Folder Structure](./folder-structure.md) +- [Tech Stack](../technical/tech-stack.md) +- [State Management Rules](../engineering/engineering-principles.md) diff --git a/.docs/deployment/README.md b/.docs/deployment/README.md new file mode 100644 index 0000000..aba731d --- /dev/null +++ b/.docs/deployment/README.md @@ -0,0 +1,93 @@ +# deployment/ -- Dokumentasi Deployment + +> Deployment pipeline, environments, infrastructure, dan operations untuk Contribution Hub. + +--- + +## Tujuan + +Folder ini berisi dokumentasi deployment untuk Contribution Hub yang mencakup: + +- Deployment pipeline Vercel + GitHub Actions +- Environment management (development, staging, production) +- Database migration strategy +- Background job monitoring +- Monitoring dan alerting (Sentry, Vercel Analytics) +- Rollback procedures +- Domain dan DNS configuration (contribution-hub.dev) + +--- + +## Struktur yang Direkomendasikan + +```txt +deployment/ +├── README.md # File ini -- panduan folder +├── pipeline.md # Deployment pipeline overview +├── environments.md # Environment configuration +├── monitoring.md # Monitoring dan alerting setup +├── backup.md # Backup dan recovery procedures +├── domains.md # Domain, DNS, SSL configuration +└── runbooks.md # Operational runbooks +``` + +--- + +## Template Pipeline Documentation + +```markdown +# Deployment Pipeline + +## Trigger + +[Kapan pipeline dijalankan] + +## Stages + +### Stage 1: [Nama] + +- **Action:** [Apa yang terjadi] +- **Tools:** [Tools yang digunakan] +- **Expected duration:** [Durasi perkiraan] + +### Stage 2: [Nama] + +... + +## Artifacts + +[Apa yang dihasilkan dari pipeline] + +## Environment Variables + +[Env vars yang dibutuhkan di pipeline] +``` + +--- + +## Environment Strategy + +| Environment | Purpose | Deploy Trigger | URL | +| ----------- | ------------------- | -------------- | ------------------- | +| Development | Daily development | Manual / auto | localhost | +| Staging | Integration testing | Push ke branch | staging.example.com | +| Production | Live | Tag/release | example.com | + +--- + +## Deployment Principles + +1. **Automate everything** -- Tidak ada manual steps dalam deployment +2. **Idempotent** -- Deployment yang sama harus menghasilkan hasil yang sama +3. **Rollback ready** -- Setiap deployment harus bisa di-rollback dengan mudah +4. **Zero downtime** -- Gunakan blue-green atau rolling deployment +5. **Immutable infrastructure** -- Jangan modify server langsung, deploy ulang + +--- + +## Tools Umum + +- **Vercel** -- Next.js hosting (default) +- **GitHub Actions** -- CI/CD pipeline +- **Docker** -- Containerization (jika diperlukan) +- **Terraform / Pulumi** -- Infrastructure as Code (jika diperlukan) diff --git a/.docs/deployment/pipeline.md b/.docs/deployment/pipeline.md new file mode 100644 index 0000000..0ea91e5 --- /dev/null +++ b/.docs/deployment/pipeline.md @@ -0,0 +1,176 @@ +# Deployment Pipeline -- Contribution Hub + +> Pipeline deployment, environment strategy, dan infrastructure configuration. + +--- + +## Deployment Platform + +**Primary: Vercel** (Next.js optimization, edge functions, analytics) + +**Secondary: GitHub Pages** (documentation site) + +## Pipeline Overview + +``` +Push ke main branch + │ + ▼ +GitHub Actions: Quality Gates + ├── TypeScript check (tsc --noEmit) + ├── ESLint + ├── Prettier format check + ├── Unit test (vitest run) + └── Build check (next build) + │ + ▼ (semua passing) +Vercel Deployment + ├── Production: main branch + ├── Preview: every PR + └── Development: feature branches +``` + +## GitHub Actions Workflow + +```yaml +# .github/workflows/ci.yml +name: CI + +on: + push: + branches: [main, develop] + pull_request: + branches: [main] + +jobs: + quality: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: oven-sh/setup-bun@v2 + + - name: Install dependencies + run: bun install --frozen-lockfile + + - name: TypeScript check + run: bun check-types + + - name: Lint + run: bun lint + + - name: Format check + run: bun format + + - name: Run tests + run: bun test:run + + - name: Build + run: bun build +``` + +## Environment Strategy + +### Environments + +| Environment | Domain | Deployment Trigger | Database | Purpose | +| ----------- | -------------------------------- | ------------------ | ------------- | --------------------------- | +| Development | localhost:3000 | Manual `bun dev` | Local/Seed | Daily development | +| Preview | {pr}.contribution-hub.vercel.app | PR creation | Preview DB | Integration testing, review | +| Staging | staging.contribution-hub.dev | Push ke develop | Staging DB | Pre-production validation | +| Production | contribution-hub.dev | Push ke main | Production DB | Live | + +### Environment Variables + +```env +# Shared +NEXT_PUBLIC_APP_URL= +NEXT_PUBLIC_SITE_NAME=Contribution Hub + +# Database (Production) +DATABASE_URL= +DIRECT_URL= + +# Auth +AUTH_SECRET= +GITHUB_CLIENT_ID= +GITHUB_CLIENT_SECRET= + +# GitHub API +GITHUB_ACCESS_TOKEN= + +# Analytics +NEXT_PUBLIC_GA_ID= + +# Feature Flags +NEXT_PUBLIC_ENABLE_BETA_FEATURES= +``` + +## Database Migration Strategy + +```bash +# Generate migration +bun db:generate + +# Apply migration +bun db:push # Development +bun db:migrate # Production (via CI) + +# Seed data +bun db:seed +``` + +## Monitoring & Observability + +### Production Monitoring + +| Tool | Purpose | +| ---------------- | -------------------------------------- | +| Vercel Analytics | Page views, web vitals, audience | +| Sentry | Error tracking, performance monitoring | +| Cronitor | Background job monitoring | +| Uptime Robot | Uptime monitoring | + +### Alerts + +| Event | Channel | Severity | +| ---------------------- | ---------------- | -------- | +| Build failure | GitHub + Email | High | +| Error rate > 1% | Sentry + Discord | High | +| Background job failure | Discord | Medium | +| Database connection | Discord | Critical | +| Uptime < 99.9% | PagerDuty | Critical | + +## Rollback Procedure + +```bash +# Vercel: Rollback via dashboard +1. Buka Vercel dashboard +2. Pilih deployment yang stabil +3. Klik "Promote to Production" + +# Database: Migration rollback +1. Identifikasi migration yang bermasalah +2. bun db:rollback +3. Deploy ulang kode sebelum migration +``` + +## Domain & DNS + +| Record | Type | Value | +| ------------------------ | ----- | -------------------- | +| contribution-hub.dev | A | 76.76.21.21 (Vercel) | +| www.contribution-hub.dev | CNAME | cname.vercel-dns.com | +| api.contribution-hub.dev | CNAME | cname.vercel-dns.com | +| \*.contribution-hub.dev | CNAME | cname.vercel-dns.com | + +## Backup Strategy + +- Database: Daily automated backup (RDS / Neon) +- File storage: Not applicable (serverless) +- Source code: GitHub (redundant) + +## Related Documents + +- [Deployment README](./README.md) +- [Technical Stack](../technical/tech-stack.md) +- [CI Workflow](../../.github/workflows/ci.yml) diff --git a/.docs/design/README.md b/.docs/design/README.md new file mode 100644 index 0000000..cf3a50f --- /dev/null +++ b/.docs/design/README.md @@ -0,0 +1,90 @@ +# design/ -- Dokumentasi Desain + +> Design system, UI/UX guidelines, komponen, dan prinsip visual. + +--- + +## Tujuan + +Folder ini berisi dokumentasi desain yang mencakup: + +- Design system dan design tokens +- Component library documentation +- UI/UX guidelines dan best practices +- Aksesibilitas (a11y) standards +- Layout dan spacing system +- Typography dan color palette +- Animasi dan transition patterns +- Responsive design guidelines + +--- + +## Struktur yang Direkomendasikan + +```txt +design/ +├── README.md # File ini -- panduan folder +├── design-tokens.md # Warna, typography, spacing, shadow +├── component-patterns.md # Pattern dan best practices komponen +├── accessibility.md # Standar aksesibilitas +├── responsive.md # Responsive design breakpoints +└── assets/ # Mockups, wireframes, screenshots +``` + +--- + +## Template Component Documentation + +```markdown +# Component: [Nama Komponen] + +## Usage + +[Kapan dan bagaimana komponen ini digunakan] + +## Props + +| Prop | Type | Default | Description | +| ---- | ---- | ------- | ----------- | +| | | | | + +## Variants + +[Variant 1, Variant 2, ...] + +## States + +- Default +- Hover +- Active +- Disabled +- Error +- Loading + +## Examples + +[Kode contoh penggunaan] + +## Accessibility + +[ARIA attributes, keyboard navigation, screen reader notes] +``` + +--- + +## Design Principles + +1. **Consistency** -- Gunakan design tokens, jangan hardcode values +2. **Accessibility first** -- Setiap komponen harus accessible by default +3. **Responsive** -- Mobile-first, semua komponen harus responsive +4. **Performance** -- Hindari layout shift, optimalkan animasi +5. **Progressive enhancement** -- Fungsi dasar tetap berjalan tanpa JS + +--- + +## Integrasi dengan Kode + +- Design tokens diimplementasikan di `tailwind.config` +- Komponen UI ada di `src/components/ui/` +- Layout patterns ada di `src/components/layouts/` +- Pastikan dokumentasi selalu sesuai dengan implementasi diff --git a/.docs/design/guide-style.md b/.docs/design/guide-style.md new file mode 100644 index 0000000..a9ffae9 --- /dev/null +++ b/.docs/design/guide-style.md @@ -0,0 +1,306 @@ +# Style Guide & Design Conventions + +> Panduan penggunaan shadcn/ui, Tailwind CSS v4, dan design tokens. +> **Single source of truth untuk typography di `src/components/ui/typography.tsx`.** + +--- + +## Typography System + +Typography system di project ini menggunakan **Geist** font (dari `next/font/google`) yang dikonfigurasi melalui CSS variables: + +``` +--font-sans → Geist (sans-serif, font default) +--font-mono → Geist Mono (monospace) +--font-serif → Geist (serif fallback) +``` + +**JANGAN gunakan font manual atau hardcode font-family** -- font sudah dikonfigurasi di `src/config/fonts.ts` dan di-inject di root layout. + +### Komponen Typography (WAJIB digunakan) + +Seluruh typography menggunakan komponen terpusat di `src/components/ui/typography.tsx`. Jangan gunakan elemen HTML mentah (`

`, `

`) tanpa komponen ini. + +#### Title Component + +Untuk heading, gunakan komponen `Title` dengan prop `variant` berformat `{level}/{weight}`: + +```tsx +import Typography from "@/components/ui/typography" + +const { Title, Text } = Typography + + // → <h1> text-4xl md:text-5xl font-bold +<Title variant="2/semibold"> // → <h2> text-3xl md:text-4xl font-semibold +<Title variant="3/medium"> // → <h3> text-2xl md:text-3xl font-medium +<Title variant="4/normal"> // → <h4> text-xl md:text-2xl font-normal +<Title variant="5/light"> // → <h5> text-lg md:text-xl +<Title variant="6/bold"> // → <h6> text-base md:text-lg font-bold +``` + +**Level** yang tersedia: `1 | 2 | 3 | 4 | 5 | 6` (memetakan ke `<h1>` hingga `<h6>`) +**Weight** yang tersedia: `normal | medium | semibold | bold | extrabold | black` + +#### Text Component + +Untuk body text, gunakan komponen `Text` dengan prop `variant` berformat `{size}/{weight}`: + +```tsx +<Text variant="xl/normal"> // → text-2xl font-normal +<Text variant="lg/medium"> // → text-xl font-medium +<Text variant="md/semibold"> // → text-lg font-semibol (DEFAULT) +<Text variant="sm/bold"> // → text-base font-bold +<Text variant="xs/normal"> // → text-sm font-normal +``` + +**Size** yang tersedia: `xl | lg | md | sm | xs` +**Weight** yang tersedia: `normal | medium | semibold | bold | black` + +Text component juga menerima prop `as` untuk mengubah elemen HTML: + +```tsx +<Text as="code" variant="sm/medium"> // → <code> dengan styling text +<Text as="span" variant="xs/normal"> // → <span> dengan styling text +<Text as="kbd"> // → <kbd> untuk keyboard shortcut +``` + +### Aturan Penggunaan + +```tsx +// ✅ WAJIB: Gunakan komponen Typography +<Title variant="1/bold">Dashboard +Selamat datang kembali! +Detail aktivitas + +// ❌ DILARANG: Hardcode heading dengan Tailwind manual +

Dashboard

+ +// ❌ DILARANG: Hardcode font-family +

Teks

+ +// ❌ DILARANG: Mengubah font di komponen secara langsung +// Font sudah dikonfigurasi global di src/config/fonts.ts +``` + +### Pengecualian + +Untuk komponen shadcn/ui yang sudah memiliki styling sendiri (Button, Input, Card, dll), gunakan styling default komponen tersebut. Jangan paksakan `` component di dalamnya. + +--- + +## shadcn/ui Usage Convention + +### Installation + +```bash +# Install per-component (jangan bulk install) +npx shadcn@latest add button +npx shadcn@latest add dialog +npx shadcn@latest add form +npx shadcn@latest add table +``` + +### Kustomisasi + +- **Jangan** edit file komponen shadcn secara langsung +- Kustomisasi via `className` prop +- Gunakan `cn()` utility untuk conditional classes +- Jika perlu variant baru, gunakan `cva` (class-variance-authority) + +```tsx +// ✅ Kustomisasi via className + + +// ✅ Conditional classes dengan cn() + +``` + +### Component Pattern + +```tsx +// Import dari @/components/ui/ +import { Button } from "@/components/ui/button" +import { Input } from "@/components/ui/input" + +// Gunakan langsung, jangan buat wrapper +function MyForm() { + return ( +
+ + +
+ ) +} +``` + +--- + +## Tailwind CSS v4 Convention + +### Design Tokens (via CSS Variables) + +Design tokens didefinisikan di `src/styles/globals.css` menggunakan CSS variables dengan prefix `--color-*`, `--radius-*`. Semua token di-binding ke Tailwind `@theme inline` directive: + +```css +/* src/styles/globals.css -- jangan duplikasi di file lain */ +@theme inline { + --color-background: var(--background); + --color-primary: var(--primary); + --radius-lg: var(--radius); +} +``` + +Gunakan token ini di komponen: + +```tsx +// ✅ Gunakan semantic tokens +
+
+ +// ❌ Jangan hardcode warna +
+``` + +### Utility Classes + +```tsx +// ✅ Utility-first approach +
+ Title +
+ +// ❌ Hindari inline styles +
+``` + +### Responsive Pattern (Mobile-First) + +```tsx +// Base = mobile, sm = tablet, md = desktop, lg = wide +
+``` + +### Dark Mode + +```tsx +// Gunakan dark: variant +
+``` + +--- + +## Component Hierarchy + +``` +UI Kit (shadcn/ui) → Button, Input, Badge, Card + ↓ +Layouts → AppShell (sidebar), PageContainer + ↓ +Widgets → StatsCard, ActivityTimeline, StreakBadge + ↓ +Feature Components → LoginForm, ActivityList, DashboardGrid +``` + +--- + +## Spacing & Layout + +| Token | Value | Usage | +| ----------- | ------ | --------------- | +| `gap-2` | 8px | Dense elements | +| `gap-4` | 16px | Default spacing | +| `gap-6` | 24px | Section spacing | +| `p-6` | 24px | Card padding | +| `p-8` | 32px | Page padding | +| `max-w-7xl` | 1280px | Page max width | + +--- + +## Form Convention + +Form menggunakan **React Hook Form + Zod** dengan komponen form dari shadcn/ui. + +Lihat template form di `src/components/widgets/auth-form.tsx` untuk contoh implementasi. + +### Pattern Dasar + +```tsx +"use client" + +import { useForm } from "react-hook-form" + +import { zodResolver } from "@hookform/resolvers/zod" +import { z } from "zod" + +import { Button } from "@/components/ui/button" +import { + Form, + FormControl, + FormField, + FormItem, + FormLabel, + FormMessage +} from "@/components/ui/form" +import { Input } from "@/components/ui/input" + +const formSchema = z.object({ + email: z.string().email("Email tidak valid"), + password: z.string().min(8, "Minimal 8 karakter") +}) + +function LoginForm() { + const form = useForm>({ + resolver: zodResolver(formSchema), + defaultValues: { email: "", password: "" } + }) + + function onSubmit(values: z.infer) { + // Handle submission + } + + return ( +
+ + ( + + Email + + + + + + )} + /> + + + + ) +} +``` + +--- + +## Related Documents + +- [Typography Component Source](../../src/components/ui/typography.tsx) -- Single source of truth +- [Product Design Vision](./product-design-vision.md) +- [Tech Stack](../technical/tech-stack.md) +- [Engineering Principles](../engineering/engineering-principles.md) diff --git a/.docs/design/product-design-vision.md b/.docs/design/product-design-vision.md new file mode 100644 index 0000000..89cfc7b --- /dev/null +++ b/.docs/design/product-design-vision.md @@ -0,0 +1,283 @@ +# Product Design Vision -- Contribution Hub + +> Visi desain, design language, dan prinsip UI/UX untuk Contribution Hub. + +--- + +## Design Philosophy + +Contribution Hub adalah tools untuk developer, oleh developer. Desainnya harus mencerminkan: + +1. **Developer-first** -- Efisien, keyboard-friendly, data-dense +2. **Clean & Focused** -- Tidak ada elemen yang tidak perlu +3. **Dark-mode native** -- Developer menghabiskan banyak waktu di terminal/IDE +4. **Data-rich** -- Informasi disajikan dengan visualisasi yang informatif +5. **Professional** -- Cocok untuk personal branding dan portofolio +6. **Cost-Conscious** -- Desain yang efisien secara resources, mendukung arsitektur free-first + +## Design Principles + +### 1. Less is More + +Setiap elemen di layar harus punya tujuan. Jika sebuah elemen bisa dihapus tanpa mengurangi fungsi, hapus. + +### 2. Information Hierarchy + +Data aktivitas disajikan dengan hierarki yang jelas: + +- **Overview** (angka besar, streak status) -- paling menonjol +- **Detail** (activity list, breakdown) -- bisa diakses dengan scroll/click +- **Insight** (pola, rekomendasi) -- supplemental, tidak mengganggu + +### 3. Progressive Disclosure + +Jangan membebani pengguna dengan semua informasi sekaligus: + +- Dashboard: overview saja +- Klik untuk detail +- Navigasi ke halaman spesifik untuk analisis mendalam + +### 4. Delightful but Not Distracting + +Animasi dan interaksi harus: + +- Memberikan feedback yang jelas (bukan hiasan) +- Meninggalkan kesan premium (smooth, responsive) +- Tidak menghalangi pengguna mencapai tujuannya + +### 5. Mobile-Responsive + +Developer tidak selalu di depan komputer. Dashboard harus bisa diakses dari smartphone untuk quick check. + +## Color Palette + +Palet warna menggunakan CSS variables yang didefinisikan di `src/styles/globals.css`. Gunakan semantic tokens (`bg-background`, `text-foreground`, `border-border`) -- jangan hardcode warna. + +### Tokens Utama (dari globals.css) + +| Token | Dark Mode | Light Mode | Usage | +| -------------------- | ---------------------- | ---------------------- | ----------------------- | +| `--background` | oklch(0.141 0.005 ...) | oklch(1 0 0) | Main background | +| `--foreground` | oklch(0.985 0 0) | oklch(0.141 0.005 ...) | Primary text | +| `--card` | oklch(0.141 0.005 ...) | oklch(1 0 0) | Card/section background | +| `--muted-foreground` | oklch(0.705 0.015 ...) | oklch(0.552 0.016 ...) | Secondary text | +| `--border` | oklch(0.274 0.006 ...) | oklch(0.92 0.004 ...) | Borders, dividers | +| `--primary` | oklch(0.985 0 0) | oklch(0.21 0.006 ...) | Primary accent, buttons | +| `--destructive` | oklch(0.577 0.245 ...) | oklch(0.577 0.245 ...) | Error, negative metric | + +> **Jangan gunakan warna hardcode.** Semua warna diakses melalui semantic tokens. Palet warna hardcode yang tercantum di dokumen sebelumnya sudah obsolete -- gunakan globals.css sebagai single source of truth. + +## Typography + +**Single source of truth:** `src/components/ui/typography.tsx` + +Project ini menggunakan font **Geist** (dari next/font/google) yang dikonfigurasi di `src/config/fonts.ts`. BUKAN Inter. Geist otomatis di-inject melalui CSS variables `--font-sans` dan `--font-mono` di root layout. + +### Komponen Typography + +Semua teks menggunakan komponen terpusat: + +```tsx +import Typography from "@/components/ui/typography" + +const { Title, Text } = Typography + +// Heading: variant = "{level}/{weight}" + // h1, 2.5rem, bold +<Title variant="2/semibold"> // h2, 2.25rem, semibold +<Title variant="3/medium"> // h3, 1.875rem, medium + +// Body: variant = "{size}/{weight}" +<Text variant="md/normal"> // 1.125rem, normal (default) +<Text variant="sm/bold"> // 1rem, bold +<Text variant="xs/normal"> // 0.875rem, normal + +// Ubah elemen HTML dengan prop "as" +<Text as="code" variant="sm/medium"> // <code> element +<Text as="span" variant="xs/normal"> // <span> element +``` + +> **Referensi lengkap** tentang variant yang tersedia ada di `src/components/ui/typography.tsx` dan `.docs/design/guide-style.md`. Jangan menduplikasi informasi typography di dokumen ini. + +### Aturan Penting + +1. Jangan gunakan `<h1>`-`<h6>` atau `<p>` secara langsung. Gunakan `<Title>` dan `<Text>`. +2. Jangan hardcode font-family. Font dikelola oleh config. +3. Jangan hardcode ukuran font yang sudah ada di komponen typography. +4. Untuk komponen shadcn/ui (Button, Input, Card, dll), gunakan styling default mereka. + +## Layout Structure + +``` +┌─────────────────────────────────────────────┐ +│ Header (Logo + Navigation + User Avatar) │ +├──────────────────┬──────────────────────────┤ +│ Sidebar │ Main Content │ +│ ───────── │ ───────────── │ +│ • Dashboard │ [Page Content] │ +│ • Activity │ │ +│ • Streaks │ │ +│ • Reports │ │ +│ • Profile │ │ +│ • Settings │ │ +├──────────────────┴──────────────────────────┤ +│ Footer (minimal) │ +└─────────────────────────────────────────────┘ +``` + +### Responsive Breakpoints + +| Breakpoint | Layout | +| ---------- | -------------------------------------- | +| < 768px | Single column, sidebar jadi bottom nav | +| 768-1024px | Collapsible sidebar | +| max 1280px | Full sidebar + content | + +## Key UI Components + +### Activity Timeline + +Activity timeline adalah komponen sentral. Desainnya harus: + +- Setiap aktivitas memiliki ikon yang jelas (commit, PR, issue, review) +- Timeline vertikal dengan timestamp yang jelas +- Color-coded berdasarkan kategori +- Clickable untuk melihat detail +- Infinite scroll atau pagination + +### Streak Display + +Streak display harus menjadi focal point dashboard: + +``` +┌─────────────────────┐ +│ Current Streak │ +│ 🔥 14 days │ +│ │ +│ ┌─────────────────┐ │ +│ │ Progress Bar │ │ +│ │ ████████░░░░ 60% │ │ +│ │ menuju 30 days │ │ +│ └─────────────────┘ │ +│ │ +│ Longest: 21 days │ +└─────────────────────┘ +``` + +### Stats Cards + +Setiap stat card memiliki: + +- Ikon yang merepresentasikan metrik +- Angka besar (nilai utama) +- Label +- Trend indicator (naik/turun dari periode sebelumnya) + +### Activity Graph + +Heatmap style graph (seperti GitHub contribution graph) tapi: + +- Bisa di-filter per kategori aktivitas +- Tooltip yang informatif (bukan hanya jumlah) +- Weekly breakdown yang lebih detail + +## Micro-Interactions + +### Streak Animation + +Ketika streak bertambah: + +- Angka streak "flip" seperti counter +- Animasi api/glow ringan +- Konfirmasi visual (ceklis atau pulse) + +### Goal Completion + +Ketika goal tercapai: + +- Progress bar mengisi dengan animasi smooth +- Celebratory toast (sonner) yang subtle +- Sound effect (opsional, off by default) + +### Dark/Light Mode Transition + +Smooth transition dengan CSS `transition` pada CSS variables. Durasi 200ms ease. + +## Dashboard Layout (Wireframe) + +``` +┌──────────────────────────────────────────────────┐ +│ 💻 Contribution Hub 👤 Profile ⚙️ │ +├────────┬─────────────────────────────────────────┤ +│ │ Good morning, Rama! │ +│ 📊 │ ┌──────┐ ┌──────┐ ┌──────┐ ┌──────┐ │ +│ Dash │ │🔥 14 │ │📝 156│ │👁 23 │ │⭐ 87%│ │ +│ │ │Streak│ │Commts│ │Review│ │Goals │ │ +│ 📋 │ └──────┘ └──────┘ └──────┘ └──────┘ │ +│ Activ │ │ +│ │ ┌────────────────────────────────────┐ │ +│ 🔥 │ │ Weekly Activity Graph │ │ +│ Streak │ │ ▓▓▓░▓▓▓▓▓░▓▓░▓▓▓▓░▓░░▓▓▓▓░▓▓ │ │ +│ │ │ M T W T F S S │ │ +│ 📋 │ └────────────────────────────────────┘ │ +│ Report │ │ +│ │ ┌────────────────────────────────────┐ │ +│ 👤 │ │ Recent Activity │ │ +│ Profile│ │ ┌──────────────────────────────┐ │ │ +│ │ │ │🚀 Pushed to feature/auth │ │ │ +│ │ │ │📅 2 hours ago • 3 commits │ │ │ +│ │ │ ├──────────────────────────────┤ │ │ +│ ⚙️ │ │ │🔀 Opened PR #42: Add login │ │ │ +│ Sett │ │ │📅 5 hours ago │ │ │ +│ │ │ └──────────────────────────────┘ │ │ +│ │ └────────────────────────────────────┘ │ +└────────┴──────────────────────────────────────────┘ +``` + +## Accessibility + +- Semantic HTML (nav, main, section, article) +- Keyboard navigation (Tab, Enter, Escape) +- Focus indicators yang jelas +- ARIA labels untuk ikon dan interactive elements +- Screen reader friendly untuk data chart/grafik +- Color contrast ratio minimal 4.5:1 + +## Component Patterns + +### ActivityCard + +```tsx +<ActivityCard + type="commit" + repository="user/project" + description="Add authentication middleware" + timestamp={new Date()} + metadata={{ commits: 3, branch: "feat/auth" }} +/> +``` + +### StreakBadge + +```tsx +<StreakBadge current={14} longest={21} type="coding" milestone={30} /> +``` + +### StatsCard + +```tsx +<StatsCard + icon={GitCommit} + value={156} + label="Total Commits" + trend={{ direction: "up", value: 12 }} +/> +``` + +--- + +## Related Documents + +- [Style Guide](./guide-style.md) +- [Tech Stack](../technical/tech-stack.md) +- [Product Definition](../product/definition.md) diff --git a/.docs/engineering/README.md b/.docs/engineering/README.md new file mode 100644 index 0000000..470707c --- /dev/null +++ b/.docs/engineering/README.md @@ -0,0 +1,89 @@ +# engineering/ -- Dokumentasi Engineering + +> Engineering practices, code review, workflow, dan standar coding. + +--- + +## Tujuan + +Folder ini berisi dokumentasi tentang praktik engineering yang digunakan: + +- Coding standards dan style guide +- Code review process +- Git workflow dan branch strategy +- Commit conventions +- Pull request checklist +- Refactoring guidelines +- Code ownership +- Definition of Done + +--- + +## Struktur yang Direkomendasikan + +```txt +engineering/ +├── README.md # File ini -- panduan folder +├── coding-standards.md # TypeScript, React, styling standards +├── code-review.md # Code review checklist dan process +├── git-workflow.md # Branch strategy, commit convention +├── refactoring.md # Refactoring guidelines dan triggers +├── definition-of-done.md # Apa arti "selesai" untuk sebuah task +└── errors-and-learning.md # Catatan error yang pernah terjadi dan solusinya +``` + +--- + +## Template Coding Standard + +```markdown +# [Topik] Standard + +## Rule + +[Aturan yang harus diikuti] + +## Good Example + +\`\`\`typescript +// Contoh yang benar +\`\`\` + +## Bad Example + +\`\`\`typescript +// Contoh yang salah +\`\`\` + +## Rationale + +[Mengapa aturan ini penting] + +## Enforcement + +[Bagaimana aturan ini ditegakkan -- ESLint, Prettier, manual review] +``` + +--- + +## Engineering Principles + +1. **Boy Scout Rule** -- Selalu tinggalkan kode lebih bersih dari sebelumnya +2. **YAGNI** -- You Ain't Gonna Need It. Jangan tambahkan fitur yang belum dibutuhkan +3. **DRY** -- Don't Repeat Yourself. Tapi lebih baik duplikasi daripada premature abstraction +4. **KISS** -- Keep It Simple. Solusi sederhana lebih baik daripada solusi clever +5. **Single Responsibility** -- Satu function/component/module untuk satu tanggung jawab +6. **Fail Fast** -- Deteksi error sedini mungkin, jangan biarkan silent failure + +--- + +## Definition of Done Checklist + +- [ ] Kode sudah di-review +- [ ] Tidak ada TypeScript error +- [ ] Semua test passing +- [ ] Tidak ada TODO/FIXME baru tanpa issue tracker +- [ ] Dokumentasi di-update +- [ ] Tidak ada breaking changes tanpa approval +- [ ] Performance tidak regresi +- [ ] Aksesibilitas tetap terjaga diff --git a/.docs/engineering/engineering-principles.md b/.docs/engineering/engineering-principles.md new file mode 100644 index 0000000..dfdf9c6 --- /dev/null +++ b/.docs/engineering/engineering-principles.md @@ -0,0 +1,166 @@ +# Engineering Principles + +> Prinsip engineering, state management rules, dan async operation guidelines. + +--- + +## Core Principles + +### 1. RSC by Default + +- Gunakan Server Components (RSC) secara default +- Tambahkan `'use client'` hanya jika komponen butuh interaktivitas +- Client boundary di leaf components, bukan di layout/page + +### 2. Data Fetching Strategy + +```tsx +// ✅ RSC: Langsung async component +async function Page() { + const data = await fetchData() + return <List data={data} /> +} + +// ✅ React Query: Client-side dengan caching +function usePosts() { + return useQuery({ + queryKey: ["posts"], + queryFn: getPosts, + staleTime: 5 * 60 * 1000 + }) +} + +// ❌ Jangan fetch di komponen tanpa caching strategy +function Bad() { + const [data, setData] = useState(null) + useEffect(() => { + fetch("/api/data").then(setData) + }, []) +} +``` + +### 3. State Management Rules + +| State Type | Tool | Rules | +| ----------- | --------------- | --------------------------------------- | +| Server data | React Query | Query key convention, staleTime, gcTime | +| UI state | Zustand | Store per feature, slice pattern | +| URL state | Router | Gunakan searchParams, jangan duplikasi | +| Form state | React Hook Form | Zod resolver, controlled components | + +### 4. Async Operation Rules + +- **Mutations**: React Query `useMutation` atau Server Actions +- **Loading states**: Gunakan `isPending` dari React Query, jangan manual +- **Error handling**: Error boundary + toast notification (sonner) +- **Optimistic updates**: React Query `onMutate` untuk instant UI + +```tsx +// ✅ Standard mutation pattern +const mutation = useMutation({ + mutationFn: updatePost, + onMutate: async (newPost) => { + await queryClient.cancelQueries({ queryKey: ["posts"] }) + const previous = queryClient.getQueryData(["posts"]) + queryClient.setQueryData(["posts"], (old) => [...old, newPost]) + return { previous } + }, + onError: (err, newPost, context) => { + queryClient.setQueryData(["posts"], context.previous) + toast.error("Gagal menyimpan") + } +}) +``` + +--- + +## Component Principles + +### UI Component Rules + +- **shadcn/ui**: Install per-component, kustomisasi via className +- **Stateful logic**: Ekstrak ke custom hook, jangan di komponen +- **Props**: Explicit interface, jangan spread sembarangan +- **Accessibility**: ARIA labels, keyboard nav, focus management + +### Provider Rules + +- Provider must be at the top of the tree +- Provider tidak boleh render UI +- Provider harus pure wrapper +- Provider tidak boleh fetch data langsung + +--- + +## Code Organization + +### File Size Limits + +- **Component**: Maks 200 baris. Jika lebih, ekstrak sub-komponen +- **Hook**: Maks 100 baris. Jika lebih, ekstrak utilitas +- **Util function**: Maks 50 baris. Satu fungsi = satu tanggung jawab +- **Store**: Maks 150 baris. Jika lebih, slice pattern + +### Import Rules + +```tsx +// ✅ Urutan import +import type { ReactNode } from "react" // 1. Built-in + +import { useQuery } from "@tanstack/react-query" // 2. Eksternal + +import { anything } from "@/features/auth/..." // Cross-feature import + +import { formatDate } from "@/utils/format-date" // 4. Relative (jika perlu) + +import { Button } from "@/components/ui/button" // 3. Internal (@/ alias) + +// ❌ Dilarang +import { something } from "../../components/..." // Relative path dengan ../../ +``` + +--- + +## Error Handling + +### Strategy + +```tsx +// 1. API Layer: Zod validation +const schema = z.object({ email: z.string().email() }) + +// 2. Service Layer: try/catch dengan typed errors +async function getUsers(): Promise<Result<User[]>> { + try { + const data = await api.get('/users') + return { ok: true, data } + } catch (error) { + return { ok: false, error: parseApiError(error) } + } +} + +// 3. UI Layer: Error boundary + toast +function UsersPage() { + const { data, error } = useQuery(...) + if (error) return <ErrorState message={error.message} /> + return <UserList data={data} /> +} +``` + +--- + +## Performance Rules + +1. **Dynamic import** untuk komponen berat: `next/dynamic` +2. **Image optimization**: next/image, webp format +3. **Debounce/throttle**: usehooks-ts untuk event berat +4. **React.memo**: Hanya untuk list items dengan banyak render +5. **Bundle size**: Monitor dengan next/bundle-analyzer + +--- + +## Related Documents + +- [System Overview](../architecture/system-overview.md) +- [API Guidelines](../api/api-guidelines.md) +- [Testing Strategy](../testing/testing-strategy.md) diff --git a/.docs/glossary/README.md b/.docs/glossary/README.md new file mode 100644 index 0000000..83c91fa --- /dev/null +++ b/.docs/glossary/README.md @@ -0,0 +1,110 @@ +# glossary/ -- Glossary Proyek + +> Daftar istilah, singkatan, dan definisi yang digunakan dalam project. + +--- + +## Tujuan + +Folder ini berisi glosarium yang mencakup: + +- Istilah teknis yang sering digunakan +- Singkatan dan akronim +- Domain-specific terminology +- Definisi konsep arsitektur +- Terminologi bisnis (jika ada) + +--- + +## Struktur yang Direkomendasikan + +```txt +glossary/ +├── README.md # File ini -- panduan folder + glosarium utama +├── technical-terms.md # Istilah teknis +├── domain-terms.md # Istilah domain bisnis +├── abbreviations.md # Daftar singkatan +└── deprecated.md # Istilah yang sudah tidak digunakan +``` + +--- + +## Template Glossary Entry + +Setiap entri glosarium sebaiknya memiliki format: + +```markdown +## [Istilah] + +**Kategori:** [Technical / Domain / Business / Architecture] + +**Singkatan:** [Jika ada] + +**Definisi:** +[Definisi lengkap] + +**Konteks:** +[Di mana istilah ini digunakan] + +**Related:** +[Istilah terkait] +``` + +--- + +## Glossary Dasar + +Berikut adalah istilah-istilah dasar yang digunakan dalam project ini. + +### Arsitektur & Struktur + +| Istilah | Definisi | +| -------------- | ------------------------------------------------------------ | +| **FSD** | Feature-Sliced Design -- metodologi struktur folder frontend | +| **Module** | Unit mandiri dalam FSD yang punya boundary jelas | +| **Feature** | Modul fitur di `src/features/` yang self-contained | +| **Shared** | Layer di FSD untuk kode yang digunakan bersama | +| **App Router** | Routing system Next.js 14+ dengan file-based routing | +| **RSC** | React Server Components -- komponen yang render di server | + +### Teknologi + +| Istilah | Definisi | +| ------------------ | --------------------------------------------------- | +| **Next.js** | React framework untuk production-grade applications | +| **TypeScript** | JavaScript dengan static type system | +| **Tailwind CSS** | Utility-first CSS framework | +| **Shadcn UI** | Component library berbasis Radix UI + Tailwind | +| **TanStack Query** | Server state management untuk React | +| **Zustand** | Lightweight client state management | +| **Zod** | TypeScript-first schema validation | +| **Vitest** | Unit test framework (Vite-native) | +| **Playwright** | E2E browser testing framework | +| **Motion** | Animation library (formerly Framer Motion) | + +### Development + +| Istilah | Definisi | +| --------- | -------------------------------------------------- | +| **ADR** | Architecture Decision Record | +| **PR** | Pull Request | +| **CI/CD** | Continuous Integration / Continuous Deployment | +| **E2E** | End-to-End testing | +| **a11y** | Accessibility (numeronym: a + 11 huruf + y) | +| **i18n** | Internationalization (numeronym: i + 18 huruf + n) | +| **SSR** | Server-Side Rendering | +| **SSG** | Static Site Generation | +| **ISR** | Incremental Static Regeneration | + +--- + +## Cara Menambah Glossary + +1. Cari di folder `glossary/` apakah istilah sudah ada +2. Jika belum ada, tambahkan entri baru dengan format template di atas +3. Jika istilah domain-specific, masukkan di file yang sesuai +4. Jika istilah sudah tidak digunakan, pindahkan ke `deprecated.md` + +--- + +_Glosarium ini hidup. Tambahkan istilah baru saat ditemukan._ diff --git a/.docs/glossary/domain-terms.md b/.docs/glossary/domain-terms.md new file mode 100644 index 0000000..8f775a0 --- /dev/null +++ b/.docs/glossary/domain-terms.md @@ -0,0 +1,191 @@ +# Glossary: Contribution Hub Domain Terms + +> Istilah dan definisi spesifik domain Contribution Hub. + +--- + +## A + +### Activity + +**Kategori:** Domain + +Setiap aksi yang tercatat di platform, termasuk commit, pull request, issue, code review, release, dan aktivitas GitHub lainnya. Setiap activity memiliki tipe, timestamp, repository, dan metadata. + +### Activity Log + +**Kategori:** Domain + +Daftar kronologis semua aktivitas yang pernah dilakukan pengguna. Bisa difilter, dicari, dan diekspor. + +### Activity Sync + +**Kategori:** Technical + +Proses sinkronisasi data aktivitas dari GitHub API ke database lokal. Dilakukan secara periodik (default: setiap 15 menit). + +### Automation Level + +**Kategori:** Domain + +Tingkat otomatisasi yang dipilih pengguna: Manual (pencatatan manual), Semi-Automatic (sistem mendeteksi, user mengonfirmasi), atau Automatic (sistem mencatat otomatis). + +--- + +## B + +### Badge + +**Kategori:** Domain + +Lencana digital yang diberikan sebagai pengakuan atas pencapaian tertentu (streak milestones, kategori aktivitas, dll). Bisa ditampilkan di GitHub profile. + +--- + +## C + +### Coding Streak + +**Kategori:** Domain + +Jumlah hari berturut-turut di mana pengguna memiliki aktivitas coding yang meaningful. Berbeda dengan commit streak karena mempertimbangkan kualitas aktivitas. + +### Contribution + +**Kategori:** Domain + +Aktivitas yang memberikan nilai pada ekosistem pengembangan perangkat lunak. Tidak semua aktivitas GitHub otomatis dianggap contribution -- harus memenuhi kriteria meaningful. + +### Contribution Hub + +**Kategori:** Domain + +Nama produk. Platform personal developer activity assistant untuk membantu konsistensi, dokumentasi, dan personal branding developer. + +--- + +## D + +### Daily Goal + +**Kategori:** Domain + +Target aktivitas yang ditetapkan pengguna per hari (misal: minimal 1 commit meaningful dan 1 PR review). Bisa dikustomisasi per hari (weekday vs weekend). + +### Daily Summary + +**Kategori:** Domain + +Ringkasan aktivitas harian yang di-generate otomatis setiap pukul 23:59. Berisi highlight, metrik, dan narasi aktivitas hari itu. + +--- + +## G + +### Grace Period + +**Kategori:** Domain + +Periode toleransi di mana streak tidak putus meskipun tidak ada aktivitas. Default: 1 hari per minggu. Memberikan fleksibilitas tanpa menghancurkan motivasi. + +--- + +## H + +### Habit System + +**Kategori:** Domain + +Sistem inti produk yang membantu developer membangun kebiasaan coding konsisten melalui goals, streaks, insight, dan positive reinforcement. + +### Heatmap + +**Kategori:** Domain / UI + +Visualisasi aktivitas berbasis grid (seperti GitHub contribution graph) yang menunjukkan intensitas aktivitas per hari dalam periode tertentu. + +--- + +## M + +### Meaningful Contribution + +**Kategori:** Domain + +Aktivitas yang memenuhi kriteria autentisitas, konteks, progresi, dan dampak. Lihat [ADR-002: Meaningful Contribution Philosophy](../adr/adr-002-meaningful-contribution-philosophy.md). + +### Milestone + +**Kategori:** Domain + +Tonggak pencapaian dalam streak system (7, 14, 30, 60, 100, 365 hari). Setiap milestone memberikan badge dan pengakuan. + +--- + +## O + +### Overall Streak + +**Kategori:** Domain + +Streak gabungan dari semua tipe aktivitas (coding, review, learning, management). Representasi paling komprehensif dari konsistensi developer. + +--- + +## P + +### Profile Enhancer + +**Kategori:** Domain + +Fitur untuk meningkatkan kualitas tampilan GitHub profile secara meaningful, termasuk smart README generator, badges, dan activity visualization. + +--- + +## R + +### Review Streak + +**Kategori:** Domain + +Jumlah hari berturut-turut dengan aktivitas code review. Memotivasi developer untuk aktif mereview kode rekan tim/komunitas. + +--- + +## S + +### Smart Deduplication + +**Kategori:** Technical + +Algoritma untuk memastikan aktivitas yang sama tidak tercatat dua kali. Contoh: commit dalam satu push digabung menjadi satu entry, PR updates tidak membuat entry baru. + +### Streak + +**Kategori:** Domain + +Jumlah hari berturut-turut di mana pengguna memenuhi kriteria aktivitas minimum. Tipe streak: coding, review, learning, overall. + +### Streak Freeze + +**Kategori:** Domain + +Fitur yang memungkinkan pengguna "membekukan" streak maksimal 3 hari per bulan. Untuk situasi darurat ketika benar-benar tidak bisa coding. + +--- + +## W + +### Weekly Report + +**Kategori:** Domain + +Laporan mingguan yang di-generate otomatis. Berisi overview, metrik agregat, insight, dan rekomendasi untuk minggu depan. + +--- + +## Related Documents + +- [Glossary Utama](./README.md) +- [Product Definition](../product/definition.md) +- [ADR-002: Meaningful Contribution Philosophy](../adr/adr-002-meaningful-contribution-philosophy.md) diff --git a/.docs/onboarding/README.md b/.docs/onboarding/README.md new file mode 100644 index 0000000..a5c1cea --- /dev/null +++ b/.docs/onboarding/README.md @@ -0,0 +1,155 @@ +# onboarding/ -- Onboarding Guide + +> Panduan untuk developer baru yang bergabung dengan project. + +--- + +## Tujuan + +Folder ini berisi dokumentasi onboarding yang mencakup: + +- Setup environment dari awal +- Panduan menjalankan project secara lokal +- Workflow harian yang harus diikuti +- Struktur project dan navigasi codebase +- Sumber daya untuk belajar teknologi yang digunakan +- Kontak dan channel komunikasi + +--- + +## Struktur yang Direkomendasikan + +```txt +onboarding/ +├── README.md # File ini -- panduan folder +├── quick-start.md # Panduan cepat setup project +├── project-map.md # Navigasi struktur project +├── daily-workflow.md # Workflow harian +├── learning-resources.md # Resources untuk belajar stack +└── faq.md # Frequently asked questions +``` + +--- + +## Template Quick Start + +```markdown +# Quick Start + +## Prerequisites + +- [Tool/software dengan versi minimum] +- [Akun/layanan yang perlu didaftarkan] + +## Installation + +\`\`\`bash + +# Clone repository + +git clone [url] + +# Install dependencies + +bun install + +# Copy environment variables + +cp .env.example .env.local + +# Run development server + +bun dev +\`\`\` + +## Verify + +Buka http://localhost:3000 -- seharusnya halaman utama muncul. +\`\`\`bash +bun check-types # Harus exit 0 +bun test # Harus semua passing +\`\`\` +``` + +--- + +## Daily Workflow Template + +```markdown +# Daily Workflow + +1. **Pull latest changes** + \`\`\`bash + git pull origin main + bun install + \`\`\` + +2. **Create feature branch** + \`\`\`bash + git checkout -b feat/nama-fitur + \`\`\` + +3. **Development** + \`\`\`bash + bun dev # Start development server + bun test # Run tests + bun check-types # Type checking + \`\`\` + +4. **Commit** + \`\`\`bash + git add . + git commit -m "feat(scope): deskripsi perubahan" + \`\`\` + +5. **Push dan buat PR** + \`\`\`bash + git push -u origin feat/nama-fitur + \`\`\` +``` + +--- + +## Project Map Template + +```markdown +# Project Map + +## Root Structure + +| Path | Deskripsi | +| ----------------- | ---------------------------------------- | +| `src/app/` | Entry point, routing, layout | +| `src/components/` | Shared components (ui, layouts, widgets) | +| `src/features/` | Feature modules (self-contained) | +| `src/lib/` | Integrations, API clients | +| `src/utils/` | Utility functions | +| `src/types/` | Global type definitions | +| `.docs/` | Engineering documentation | +``` + +--- + +## Onboarding Checklist + +### Week 1 + +- [ ] Setup development environment +- [ ] Baca `AGENTS.md` -- Engineering handbook +- [ ] Baca `.docs/architecture/` -- Architecture overview +- [ ] Jalankan project dan eksplorasi codebase +- [ ] Kerjakan starter task kecil + +### Week 2 + +- [ ] Pahami Feature-Sliced Design structure +- [ ] Pelajari tech stack yang digunakan +- [ ] Buat contribution pertama (bug fix / small feature) +- [ ] Pahami CI/CD pipeline + +### Month 1 + +- [ ] Mampu mengerjakan fitur secara mandiri +- [ ] Aktif dalam code review +- [ ] Mulai berkontribusi ke dokumentasi +- [ ] Memahami business domain produk diff --git a/.docs/onboarding/getting-started.md b/.docs/onboarding/getting-started.md new file mode 100644 index 0000000..afc2702 --- /dev/null +++ b/.docs/onboarding/getting-started.md @@ -0,0 +1,219 @@ +# Getting Started + +> Panduan setup project dari awal hingga siap development. + +--- + +## Prerequisites + +| Tool | Version Minimum | Cek | +| ------- | --------------- | ---------------- | +| Node.js | 20.x+ | `node --version` | +| npm | 10.x+ | `npm --version` | +| Git | 2.x+ | `git --version` | + +--- + +## Installation + +### 1. Clone Repository + +```bash +git clone <repository-url> +cd <project-directory> +``` + +### 2. Install Dependencies + +```bash +npm install +``` + +### 3. Environment Variables + +```bash +cp .env.example .env.local +``` + +Minimal env variables: + +```env +# App +NEXT_PUBLIC_APP_URL=http://localhost:3000 + +# Database (jika ada) +DATABASE_URL= + +# Auth (jika ada) +AUTH_SECRET= + +# API Keys (jika perlu) +``` + +### 4. Development Server + +```bash +npm run dev +``` + +Buka [http://localhost:3000](http://localhost:3000) + +--- + +## Quality Gates + +```bash +# Type checking +npm run check-types + +# Linting +npm run lint + +# Testing +npm run test + +# Production build +npm run build +``` + +Semua harus pass sebelum commit/push. + +--- + +## Stack Installation Guide + +### Core UI (shadcn/ui) + +```bash +npx shadcn@latest init +npx shadcn@latest add button dialog form input toast table card +``` + +### Theme & Styling + +```bash +npm install next-themes +``` + +### State Management + +```bash +npm install @tanstack/react-query @tanstack/react-query-devtools +npm install zustand +``` + +### Forms & Validation + +```bash +npm install react-hook-form @hookform/resolvers zod +``` + +### Internationalization + +```bash +npm install next-intl +# Setup: https://next-intl.dev/docs/getting-started/app-router +``` + +### Utilities + +```bash +npm install usehooks-ts date-fns +npm install lucide-react sonner +``` + +### Env Validation (Recommended) + +```bash +npm install @t3-oss/env-nextjs +``` + +--- + +## Project Structure Quick Tour + +``` +src/ +├── app/ # Pages & layouts (Next.js App Router) +├── components/ # Shared UI components +│ ├── ui/ # shadcn/ui components +│ ├── layouts/ # Header, footer, sidebar +│ ├── widgets/ # Cards, modals, tables +│ └── providers/ # React Query, Theme, Auth providers +├── config/ # App configuration +├── constants/ # Global constants +├── features/ # Feature-Sliced Design modules +├── hooks/ # Custom React hooks +├── i18n/ # Translations +├── lib/ # Third-party integrations +├── types/ # TypeScript types +├── utils/ # Pure utility functions +└── styles/ # Global CSS +``` + +--- + +## Daily Workflow + +```bash +# 1. Pull latest +git pull origin main +npm install + +# 2. Start development +npm run dev + +# 3. Before commit +npm run check-types +npm run lint +npm run test + +# 4. Commit (Conventional Commits) +git add . +git commit -m "feat(scope): deskripsi perubahan" + +# 5. Push +git push +``` + +--- + +## Style Conventions + +- **Components**: kebab-case filenames, PascalCase component names +- **Hooks**: `use-kebab-case.ts` +- **Utils**: `kebab-case.ts` +- **Constants**: `UPPER_CASE` +- **Imports**: Gunakan `@/` alias, jangan relative `../../` + +--- + +## Troubleshooting + +### Port already in use + +```bash +npx kill-port 3000 +npm run dev +``` + +### Type errors after install + +```bash +npm run check-types +``` + +### Build errors + +```bash +npm run build +# Check error messages for details +``` + +--- + +## Related Documents + +- [Tech Stack](../technical/tech-stack.md) +- [Folder Structure](../architecture/folder-structure.md) +- [Engineering Principles](../engineering/engineering-principles.md) diff --git a/.docs/product/README.md b/.docs/product/README.md new file mode 100644 index 0000000..3ca3004 --- /dev/null +++ b/.docs/product/README.md @@ -0,0 +1,100 @@ +# product/ -- Dokumentasi Produk + +> Visi, roadmap, kebutuhan, dan spesifikasi fitur produk. + +--- + +## Tujuan + +Folder ini berisi dokumentasi yang berkaitan dengan aspek **produk** dari project ini, termasuk: + +- Visi dan misi produk +- Target pengguna dan use cases +- Roadmap dan prioritas fitur +- User stories dan acceptance criteria +- Feature specifications +- Product requirements document (PRD) + +--- + +## Struktur yang Direkomendasikan + +```txt +product/ +├── README.md # File ini -- panduan folder +├── definition.md # Definisi produk +├── vision.md # Visi dan misi produk +├── roadmap.md # Roadmap dan milestone +├── personas.md # User personas +├── features/ # Dokumentasi per fitur +│ ├── fitur-satu.md +│ └── fitur-dua.md +└── archive/ # Fitur yang sudah di-deprecate +``` + +--- + +## Template Feature Spec + +```markdown +# Feature: [Nama Fitur] + +## Deskripsi + +[Penjelasan singkat tentang fitur ini] + +## User Story + +Sebagai [peran], saya ingin [tujuan] sehingga [alasan]. + +## Acceptance Criteria + +- [ ] Kondisi 1 +- [ ] Kondisi 2 +- [ ] Kondisi 3 + +## Technical Notes + +[Catatan teknis yang relevan] + +## Dependencies + +[Fitur atau komponen yang dibutuhkan] +``` + +--- + +## Best Practices + +1. **Tulis sebelum coding** -- dokumentasi produk membantu klarifikasi requirements +2. **Libatkan stakeholder** -- pastikan product owner/user terlibat dalam validasi +3. **Update saat berubah** -- jika scope berubah, update dokumentasi produk +4. **Hubungkan ke ADR** -- keputusan produk yang berdampak teknis harus tercatat di ADR + +--- + +## Template Product Vision + +```markdown +# Product Vision + +## Vision Statement + +[Satu kalimat yang menjelaskan visi produk] + +## Target Audience + +[Siapa yang akan menggunakan produk ini] + +## Key Differentiators + +[Apa yang membuat produk ini unik] + +## Success Metrics + +[Bagaimana mengukur kesuksesan produk] + +## Timeline + +[Fase-fase pengembangan] +``` diff --git a/.docs/product/definition.md b/.docs/product/definition.md new file mode 100644 index 0000000..2d06b31 --- /dev/null +++ b/.docs/product/definition.md @@ -0,0 +1,139 @@ +# Product Definition -- Contribution Hub + +> Definisi produk, problem statement, target pasar, dan value proposition. + +--- + +## Product Name + +**Contribution Hub** -- Developer Activity Assistant + +## Vision Statement + +Menjadi platform personal yang membantu developer menjaga konsistensi aktivitas pengembangan secara otomatis dan meaningful -- bukan sekadar spam commit, tetapi benar-benar mendokumentasikan progress dan membangun kebiasaan engineering yang sehat. + +## Problem Statement + +Developer individu menghadapi beberapa masalah umum yang berdampak pada produktivitas dan personal branding: + +| Problem | Dampak | +| -------------------------------------------- | ----------------------------------------------------------- | +| Coding lokal tapi lupa commit | Progress hilang, streak putus, tidak ada dokumentasi | +| Punya project tapi tidak terdokumentasi | Tidak bisa menunjukkan progress ke rekruiter atau komunitas | +| Kehilangan streak kontribusi | Motivasi menurun, habit consistency hancur | +| Malas bikin issue / PR untuk project sendiri | Project terlihat "mati" di GitHub | +| Profile GitHub terlihat tidak aktif | Personal branding sebagai developer terdampak negatif | +| Tidak ada catatan progress harian | Sulit mengevaluasi produktivitas jangka panjang | + +## Target Audience + +### Primary: Indie Developers & Freelancers + +- Developer solo yang mengerjakan multiple project +- Freelancer yang butuh portofolio GitHub yang aktif +- Umur 20-35 tahun, sudah bekerja 1-5 tahun +- Aktif di GitHub tapi tidak konsisten + +### Secondary: Junior Developers & Career Switchers + +- Developer yang sedang membangun portofolio +- Butuh GitHub profile yang menarik untuk melamar kerja +- Ingin membangun habit coding yang konsisten + +### Tertiary: Open Source Contributors + +- Kontributor yang ingin mendokumentasikan perjalanan kontribusi mereka +- Maintainer yang butuh tools untuk manage aktivitas repository + +## Value Proposition + +Contribution Hub berbeda dari GitHub automation tools lain karena: + +1. **Meaningful by Default** -- Setiap aktivitas yang direkam harus memiliki konteks dan tujuan. Tidak ada commit kosong atau aktivitas artifisial. + +2. **Developer Habit System** -- Bukan sekadar tools, tetapi sistem pembangun kebiasaan yang membantu developer tetap konsisten. + +3. **Progress Documentation** -- Semua aktivitas coding otomatis terdokumentasi menjadi catatan progress yang bisa ditunjukkan. + +4. **Privacy-First** -- Data aktivitas milik developer. Platform hanya sebagai katalis, bukan penambang data. + +5. **Works With Your Workflow** -- Tidak memaksa developer mengubah cara kerja. Integrasi seamless dengan tools yang sudah digunakan. + +## Core Principles + +### 1. Meaningful Contribution + +Setiap aktivitas yang direkam harus memiliki nilai. Contribution Hub tidak akan pernah: + +- Membuat commit kosong atau tidak bermakna +- Generate aktivitas palsu untuk manipulation statistik +- Mendorong perilaku spam demi streak + +### 2. Developer-Centric + +Semua fitur dirancang dari sudut pandang developer: + +- CLI-first untuk power users +- Dashboard visual untuk overview +- API untuk integrasi dengan tools lain + +### 3. Progressive Automation + +Tingkat automation bertahap sesuai preferensi developer: + +- **Manual**: Developer mencatat aktivitas sendiri +- **Semi-automatic**: Sistem mendeteksi aktivitas coding dan menawarkan untuk dicatat +- **Automatic**: Aktivitas coding otomatis terdokumentasi dengan konteks yang tepat + +### 4. Habit Over Hacks + +Fokus utama adalah membangun kebiasaan jangka panjang, bukan mencari jalan pintas: + +- Consistency streaks dengan makna +- Weekly/Daily goals yang realistis +- Progress tracking yang memotivasi + +## Product Scope + +### In Scope (Fase 1) + +- GitHub activity tracking dan logging +- Personal dashboard aktivitas coding +- Streak tracking dengan meaningful milestones +- Auto-dokumentasi aktivitas harian +- Issue/PR suggestion dari aktivitas coding + +### In Scope (Fase 2) + +- Multiple repository management +- Team activity overview +- Integration dengan GitLab, Bitbucket +- Advanced analytics dan insight +- Public profile page + +### Out of Scope (Tidak akan pernah) + +- Spam commit generation +- Fake activity creation +- Streak manipulation +- Data scraping tanpa izin +- Social media integration yang tidak relevan + +## Success Metrics + +| Metrik | Target Fase 1 | Target Fase 2 | +| ------------------------------- | ------------- | ------------- | +| Daily Active Users | 100 | 1,000 | +| Average session duration | 5 menit | 8 menit | +| Streak retention (7+ days) | 40% | 60% | +| User-reported meaningfulness | 4.0/5.0 | 4.5/5.0 | +| GitHub profile improvement rate | 70% users | 85% users | + +--- + +## Related Documents + +- [Product Vision](./vision.md) +- [Product Roadmap](./roadmap.md) +- [User Personas](./personas.md) +- [Architecture Overview](../architecture/product-architecture.md) diff --git a/.docs/product/features/activity-tracking.md b/.docs/product/features/activity-tracking.md new file mode 100644 index 0000000..3f4787d --- /dev/null +++ b/.docs/product/features/activity-tracking.md @@ -0,0 +1,138 @@ +# Feature: GitHub Activity Tracking + +> Fitur inti untuk melacak dan merekam aktivitas GitHub pengguna secara otomatis. + +--- + +## Deskripsi + +Fitur ini memungkinkan pengguna menghubungkan akun GitHub mereka dan secara otomatis melacak semua aktivitas coding yang meaningful. Sistem membaca data dari GitHub API dan menyimpannya dalam format terstruktur untuk analisis dan visualisasi lebih lanjut. + +## User Story + +Sebagai developer, saya ingin semua aktivitas coding saya (commit, PR, issue) otomatis tercatat di satu tempat, sehingga saya tidak perlu manual mencatat apa yang sudah saya kerjakan setiap hari. + +## Acceptance Criteria + +### GitHub Integration + +- [ ] Pengguna dapat login dengan GitHub OAuth +- [ ] Sistem dapat membaca public repositories pengguna +- [ ] Sistem dapat membaca private repositories (dengan izin) +- [ ] Sync aktivitas dilakukan secara otomatis setiap 15 menit +- [ ] Pengguna dapat memilih repository yang dilacak (include/exclude) +- [ ] Rate limit GitHub API di-handle dengan baik + +### Activity Types yang Dilacak + +| Tipe Aktivitas | Sumber Data | Kategori | +| ---------------------- | ----------------- | ------------- | +| Push / Commit | GitHub Events API | Coding | +| Pull Request dibuat | GitHub Events API | Collaboration | +| Pull Request di-review | GitHub Events API | Review | +| Issue dibuat | GitHub Events API | Management | +| Issue comment | GitHub Events API | Discussion | +| Code review comment | GitHub Events API | Review | +| Repository created | GitHub Events API | Management | +| Release published | GitHub Events API | Release | +| Fork | GitHub Events API | Exploration | +| Star | GitHub Events API | Curation | + +### Activity Log + +- [ ] Setiap aktivitas memiliki timestamp, repository, tipe, dan deskripsi +- [ ] Aktivitas ditampilkan dalam timeline kronologis +- [ ] Filter berdasarkan tipe aktivitas, repository, atau tanggal +- [ ] Search di dalam activity log +- [ ] Ekspor activity log ke JSON/CSV/Markdown + +### Smart Deduplication + +- [ ] Aktivitas yang sama tidak dicatat dua kali +- [ ] Commit dalam satu push digabung menjadi satu entry (dengan detail) +- [ ] PR updates tidak membuat entry baru (hanya status change) + +## Technical Notes + +### Data Model + +```typescript +interface Activity { + id: string + userId: string + type: + | "commit" + | "pull_request" + | "issue" + | "review" + | "release" + | "fork" + | "star" + repository: { + name: string + owner: string + url: string + } + timestamp: Date + description: string + metadata: { + commitCount?: number + prNumber?: number + issueNumber?: number + branch?: string + files?: string[] + additions?: number + deletions?: number + } + category: + | "coding" + | "review" + | "management" + | "discussion" + | "exploration" + | "release" + source: "github_api" | "manual" | "git_hook" +} +``` + +### API Endpoints + +| Method | Endpoint | Deskripsi | +| ------ | --------------------- | -------------------------------------- | +| GET | /api/activities | List aktivitas (paginated, filterable) | +| GET | /api/activities/stats | Activity statistics | +| POST | /api/activities/sync | Trigger manual sync | +| POST | /api/activities | Create aktivitas manual | + +### Sync Architecture + +``` +GitHub API (polling) + │ (15 menit) + ▼ +Background Job (Vercel Cron / node-cron) + │ + ├── Fetch events since last sync + ├── Deduplication check + ├── Store di database + └── Update cache (React Query) +``` + +### Database Tables + +- `activities` -- Semua aktivitas yang tercatat +- `activity_sync_log` -- Log sinkronisasi (last sync, status, error) +- `user_repositories` -- Repository yang dipilih pengguna + +## Dependencies + +- Better Auth (GitHub OAuth plugin) +- TanStack React Query (caching aktivitas) +- Background jobs (Vercel Cron / BullMQ) +- Database (PostgreSQL via Prisma / Drizzle) + +## Related Documents + +- [Product Definition](../definition.md) +- [Product Roadmap](../roadmap.md) +- [Architecture Overview](../../architecture/product-architecture.md) diff --git a/.docs/product/features/auto-documentation.md b/.docs/product/features/auto-documentation.md new file mode 100644 index 0000000..cc638ca --- /dev/null +++ b/.docs/product/features/auto-documentation.md @@ -0,0 +1,175 @@ +# Feature: Auto-Documentation + +> Dokumentasi aktivitas coding otomatis yang menghasilkan catatan progress meaningful. + +--- + +## Deskripsi + +Auto-Documentation adalah fitur yang secara otomatis mengubah aktivitas coding mentah menjadi dokumentasi progress yang terstruktur dan mudah dibaca. Fitur ini menjembatani kesenjangan antara "coding" dan "mendokumentasikan apa yang sudah dikerjakan." + +## User Story + +Sebagai developer, saya ingin aktivitas coding saya otomatis terdokumentasi menjadi catatan progress yang rapi, sehingga saya tidak perlu membuang waktu membuat daily report manual dan tetap memiliki portofolio aktivitas yang terorganisir. + +## Acceptance Criteria + +### Daily Activity Summary + +- [ ] Generate otomatis ringkasan aktivitas harian setiap pukul 23:59 +- [ ] Format: markdown yang siap dipakai +- [ ] Konten summary: + - Total commits dan repository yang dikerjakan + - PR yang dibuat/direview + - Issue yang dibuat/ditutup + - Highlight (aktivitas paling signifikan) + - Learning points (dari commit messages yang mengandung keyword "learn", "study", dll) +- [ ] Pengguna bisa edit summary sebelum disimpan +- [ ] Daily summary bisa di-share ke social media (opsional) + +### Weekly Progress Report + +- [ ] Generate otomatis setiap hari Minggu +- [ ] Format yang lebih komprehensif: + - Overview mingguan (total aktivitas per kategori) + - Streak status + - Goals achievement rate + - Repository activity breakdown + - Insight dan rekomendasi +- [ ] Bisa di-export ke PDF/Markdown/HTML +- [ ] Template yang bisa dikustomisasi +- [ ] Email digest (opsional) + +### Auto-Generated Changelog + +- [ ] Generate changelog dari commit messages per repository +- [ ] Grouping berdasarkan tipe (feat, fix, refactor, docs, chore) +- [ ] Link ke commit/PR yang relevan +- [ ] Format mengikuti [Keep a Changelog](https://keepachangelog.com/) +- [ ] Bisa di-push ke repository sebagai file CHANGELOG.md + +### Activity Journal + +- [ ] Timeline view dari semua aktivitas +- [ ] Search dan filter +- [ ] Tagging system untuk kategorisasi manual +- [ ] Notes: pengguna bisa menambahkan catatan personal per hari +- [ ] Export full journal ke berbagai format + +### Portfolio Generation + +- [ ] Generate GitHub profile README dari aktivitas +- [ ] Include metrics yang meaningful (bukan hanya angka) +- [ ] Tema yang bisa dikustomisasi +- [ ] Auto-update setiap minggu +- [ ] Preview sebelum publish + +### Documentation Quality + +- [ ] Commit messages dengan keyword tertentu mendapat highlight khusus +- [ ] Deteksi aktivitas dokumentasi (commit ke docs/, .md files) +- [ ] Kualitas dokumentasi scoring (berdasarkan rasio docs vs code) + +## Technical Notes + +### Data Flow + +``` +Raw Activities (database) + │ + ▼ +Daily Summary Generator (cron: 23:59 daily) + │ Parse commit messages + │ Categorize activities + │ Generate narrative + ▼ +Daily Summary (markdown) + │ + ├── Stored in daily_logs table + ├── Available via API + └── Sent as notification (opsional) + +Weekly Report Generator (cron: Sunday 23:59) + │ Aggregate 7 daily summaries + │ Calculate metrics + │ Generate insight + ▼ +Weekly Report (markdown/PDF) +``` + +### Narrative Generation + +Narasi untuk daily summary dibuat dengan template: + +``` +## [Day], [Date] + +Total aktivitas: [X] commits, [Y] PRs, [Z] reviews + +**Repository: [name]** +- [feat]: [deskripsi dari commit message] +- [fix]: [deskripsi dari commit message] +- Progress: [perubahan signifikan] + +**Highlight:** +[Commit/PR paling signifikan hari ini] + +**Learning:** +[Catatan belajar jika ada] +``` + +### Data Model + +```typescript +interface DailySummary { + id: string + userId: string + date: Date + content: string // generated markdown + edited: boolean // apakah sudah diedit user + highlights: Highlight[] + metrics: { + totalCommits: number + totalPRs: number + totalReviews: number + totalIssues: number + repositories: number + codingMinutes: number + learningPoints: string[] + } +} + +interface WeeklyReport { + id: string + userId: string + weekStart: Date + weekEnd: Date + content: string // generated markdown + metrics: WeeklyMetrics + insight: string[] // generated insight +} +``` + +### API Endpoints + +| Method | Endpoint | Deskripsi | +| ------ | --------------------------- | ------------------------- | +| GET | /api/daily-summary?date= | Get daily summary | +| PUT | /api/daily-summary/:id | Edit daily summary | +| GET | /api/weekly-report?week= | Get weekly report | +| POST | /api/daily-summary/generate | Trigger manual generation | +| GET | /api/journal | Get activity journal | + +## Dependencies + +- date-fns (date formatting) +- next-intl (multi-language summaries) +- recharts / nivo (charts untuk report) +- PDF generation library (jika export PDF) + +## Related Documents + +- [Activity Tracking](./activity-tracking.md) +- [Habit System](./habit-system.md) +- [GitHub Profile Enhancer](./github-profile.md) +- [Product Roadmap](../roadmap.md) diff --git a/.docs/product/features/github-profile.md b/.docs/product/features/github-profile.md new file mode 100644 index 0000000..20d8bac --- /dev/null +++ b/.docs/product/features/github-profile.md @@ -0,0 +1,173 @@ +# Feature: GitHub Profile Enhancer + +> Tools untuk meningkatkan kualitas dan tampilan GitHub profile secara meaningful. + +--- + +## Deskripsi + +GitHub Profile Enhancer membantu developer menampilkan aktivitas coding mereka secara profesional di GitHub. Fokusnya bukan pada menambah kuantitas kontribusi, tetapi pada menyajikan aktivitas yang sudah ada dengan cara yang lebih terstruktur dan informatif. + +## User Story + +Sebagai developer, saya ingin GitHub profile saya mencerminkan aktivitas coding saya yang sebenarnya, sehingga rekruter dan sesama developer bisa melihat kemampuan dan konsistensi saya secara akurat. + +## Acceptance Criteria + +### Smart Profile README Generator + +- [ ] Generate README.md yang dinamis untuk GitHub profile +- [ ] Include metrics yang meaningful: + - Total meaningful contributions (bukan hanya commit count) + - Coding streak saat ini + - Weekly activity breakdown + - Top languages (dari aktivitas nyata, bukan hanya repository) + - Recently active repositories +- [ ] Multiple layout templates +- [ ] Kustomisasi section yang ditampilkan +- [ ] Auto-update via GitHub Actions (daily/weekly) +- [ ] Preview sebelum deploy + +### Activity Badges + +- [ ] Dynamic badges yang bisa dipasang di profile README +- [ ] Jenis badge: + - Current streak badge + - Total contributions badge + - Top category badge (Coder, Reviewer, Maintainer) + - Milestone badges (100 days streak, dll) +- [ ] Shields.io compatible format +- [ ] Real-time update (cache 1 jam) + +### Contribution Graph Enhancement + +- [ ] Custom contribution graph dengan warna/kustomisasi +- [ ] Breakdown graph per kategori (coding vs review vs docs) +- [ ] Embeddable graph widget untuk personal website/portfolio +- [ ] Weekly comparison overlay + +### Portfolio Features + +- [ ] Auto-generate portfolio page dari GitHub data +- [ ] One-click deploy ke Vercel/Netlify +- [ ] Template portfolio yang bisa dikustomisasi +- [ ] Include: project showcase, activity graph, tech stack, contact info + +### Repository Pinning Suggestions + +- [ ] Analisis repository mana yang paling aktif dan meaningful +- [ ] Saran repository untuk dipin di profile +- [ ] Reasoning: "Repository X punya aktivitas terbanyak bulan ini" +- [ ] Skip repository tutorial/starter template + +### Anti-Abuse + +- [ ] Tidak menampilkan repository kosong atau template +- [ ] Tidak menghitung aktivitas bot atau automation +- [ ] Tidak mempromosikan fake contribution +- [ ] Verifikasi bahwa aktivitas sesuai dengan repository yang visible + +## Technical Notes + +### Profile README Generator + +```markdown +<!-- CONTRIBUTION-HUB:START --> +<!-- Bagian ini diupdate otomatis oleh Contribution Hub --> + +### Hi there, I'm [Name] 👋 + +**Coding Streak:** [X] days 🔥 +**Total Contributions:** [X] meaningful contributions + +📊 **Weekly Activity** +``` + +[Activity Graph] + +``` + +🚀 **Recently Active** +- [repo1] - [description] (X commits this week) +- [repo2] - [description] (X commits this week) + +🏆 **Categories** +- Coding: [X]% +- Review: [X]% +- Documentation: [X]% + +<!-- CONTRIBUTION-HUB:END --> +``` + +### Data Model + +```typescript +interface ProfileConfig { + userId: string + template: "minimal" | "detailed" | "creative" | "custom" + sections: { + streak: boolean + weeklyActivity: boolean + categories: boolean + repositories: boolean + badges: boolean + languages: boolean + } + autoUpdate: boolean + updateFrequency: "daily" | "weekly" + customCSS?: string +} + +interface ProfileBadge { + id: string + userId: string + type: "streak" | "contributions" | "milestone" | "category" + label: string + value: string | number + color: string + svg: string +} +``` + +### GitHub Actions Integration + +```yaml +# .github/workflows/contribution-hub.yml +name: Update GitHub Profile +on: + schedule: + - cron: "0 0 * * *" # daily + workflow_dispatch: + +jobs: + update: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - name: Update profile with Contribution Hub + uses: contribution-hub/update-profile@v1 + with: + api-token: ${{ secrets.CH_API_TOKEN }} +``` + +### API Endpoints + +| Method | Endpoint | Deskripsi | +| ------ | ------------------------ | ---------------------- | +| GET | /api/profile/preview | Preview profile README | +| PUT | /api/profile/config | Update profile config | +| POST | /api/profile/deploy | Deploy profile update | +| GET | /api/profile/badge/:type | Generate badge SVG | + +## Dependencies + +- GitHub API (profile update) +- GitHub Actions (auto-update workflow) +- Badge generation (shields.io compatible) +- React (portfolio templates) + +## Related Documents + +- [Activity Tracking](./activity-tracking.md) +- [Auto Documentation](./auto-documentation.md) +- [Product Roadmap](../roadmap.md) diff --git a/.docs/product/features/habit-system.md b/.docs/product/features/habit-system.md new file mode 100644 index 0000000..a09fa22 --- /dev/null +++ b/.docs/product/features/habit-system.md @@ -0,0 +1,152 @@ +# Feature: Developer Habit System + +> Sistem pembangun kebiasaan coding yang konsisten dan meaningful. + +--- + +## Deskripsi + +Habit System adalah inti dari Contribution Hub. Bukan sekadar streak counter biasa, sistem ini dirancang untuk membantu developer membangun kebiasaan coding jangka panjang dengan pendekatan yang realistis dan memotivasi. + +## User Story + +Sebagai developer, saya ingin memiliki sistem yang membantu saya tetap konsisten coding setiap hari, dengan target yang realistis dan feedback yang memotivasi, sehingga coding menjadi kebiasaan alami bukan paksaan. + +## Acceptance Criteria + +### Streak System + +- [ ] Multiple streak types (bukan hanya commit streak): + - **Coding Streak**: Hari berturut-turut dengan aktivitas coding + - **Review Streak**: Hari berturut-turut dengan code review + - **Learning Streak**: Hari berturut-turut dengan aktivitas belajar + - **Overall Streak**: Gabungan semua aktivitas meaningful +- [ ] Grace period: 1 hari "sakit" per minggu (streak tidak putus) +- [ ] Streak freeze: Pengguna bisa freeze streak maksimal 3 hari/bulan +- [ ] Visual progress bar menuju milestone berikutnya +- [ ] Milestone achievements (7, 14, 30, 60, 100, 365 hari) + +### Daily Goals + +- [ ] Pengguna dapat set daily goals (minimal aktivitas per hari) +- [ ] Goal types: + - Minimal 1 commit meaningful (bukan "fix typo") + - Minimal 1 PR review + - Minimal 1 issue/PR dibuat + - Waktu coding minimal (X menit) +- [ ] Goals bisa dikustomisasi per hari (weekday vs weekend) +- [ ] Progress tracking real-time terhadap goals +- [ ] Smart suggestions untuk goal yang belum tercapai + +### Progress Tracking + +- [ ] Daily overview: aktivitas hari ini vs target +- [ ] Weekly summary: total aktivitas, streak status, insight +- [ ] Monthly report: tren, pola, area improvement +- [ ] Yearly heatmap (seperti GitHub contribution graph) +- [ ] Category breakdown (coding vs review vs management) + +### Motivational System + +- [ ] Positive reinforcement (bukan punishment): + - "You've coded for 14 days straight! Great consistency." + - "This week you reviewed 5 PRs -- that's helping the community." +- [ ] Insight yang actionable: + - "You tend to code more on weekends. Try to balance it out." + - "Your best coding hours are 8-10 PM." +- [ ] No negative messaging (tidak ada "streak anda hilang" -- gunakan "ayo mulai lagi") +- [ ] Customizable notification preferences + +### Gamification Elements + +- [ ] Badges untuk pencapaian spesifik +- [ ] Level system berdasarkan total aktivitas +- [ ] Unlockable themes/avatars (opsional) +- [ ] Weekly challenges (opsional, non-binding) + +### Anti-Gaming + +- [ ] Aktivitas harus meaningful untuk dihitung: + - Tidak menghitung commit "update README" berulang + - Tidak menghitung force push ke branch yang sama + - Tidak menghitung aktivitas di repository kosong +- [ ] Minimum threshold untuk setiap tipe aktivitas +- [ ] Anomaly detection untuk aktivitas mencurigakan + +## Technical Notes + +### Streak Calculation Algorithm + +``` +Initialize current_streak = 0 +Initialize longest_streak = 0 +Initialize last_active_date = null + +For each day in descending order (from today): + If day has qualifying activity: + If last_active_date is null or last_active_date == yesterday: + current_streak++ + last_active_date = day + Else if last_active_date > yesterday: + Skip (already counted) + Else: + Break (streak ended) + Else: + If grace_available and grace_not_used: + Use grace (continue streak) + Else: + Break (streak ended) + +longest_streak = max(longest_streak, current_streak) +``` + +### Data Model + +```typescript +interface HabitConfig { + userId: string + dailyGoal: { + minCommits: number + minReviews: number + minCodingMinutes: number + } + weekdayGoal: GoalConfig // Senin-Jumat + weekendGoal: GoalConfig // Sabtu-Minggu + graceDaysUsed: number + streakFreezeUsed: number + notifications: { + dailyDigest: boolean + streakWarning: boolean + goalReminder: boolean + } +} + +interface Streak { + userId: string + type: "coding" | "review" | "learning" | "overall" + currentStreak: number + longestStreak: number + lastActiveDate: Date + milestoneReached: number[] // [7, 14, 30, ...] +} +``` + +### Database Tables + +- `habit_configs` -- Konfigurasi goals per pengguna +- `streaks` -- Data streak per tipe +- `daily_logs` -- Log aktivitas harian (denormalized untuk performa) +- `achievements` -- Badges dan milestones yang sudah diraih + +## Dependencies + +- TanStack React Query (daily stats) +- Zustand (UI state untuk habit config) +- date-fns (date manipulation untuk streak calculation) +- Vitest (unit test untuk streak calculation algorithm) + +## Related Documents + +- [Activity Tracking](./activity-tracking.md) +- [Auto Documentation](./auto-documentation.md) +- [Product Roadmap](../roadmap.md) diff --git a/.docs/product/personas.md b/.docs/product/personas.md new file mode 100644 index 0000000..57ea5d4 --- /dev/null +++ b/.docs/product/personas.md @@ -0,0 +1,208 @@ +# User Personas -- Contribution Hub + +> Profil pengguna target untuk memandu pengambilan keputusan produk dan desain. + +--- + +## Persona 1: Rama -- The Solo Indie Developer + +> "Aku sering coding berjam-jam, tapi lupa commit. Minggu depan lupa apa yang sudah dikerjakan." + +### Demographics + +| Atribut | Detail | +| ----------- | ---------------------------------------- | +| Umur | 27 tahun | +| Pekerjaan | Full-stack developer freelance | +| Lokasi | Jakarta, Indonesia | +| Pengalaman | 4 tahun coding | +| GitHub usia | 3 tahun, 200+ contributions/year | +| Tools | VS Code, Git CLI, multiple side projects | + +### Behavior + +- Bekerja di 2-3 project side secara bergantian +- Sering coding larut malam setelah pekerjaan utama +- Punya kebiasaan lupa commit karena fokus coding +- Jarang bikin issue atau PR untuk project sendiri +- GitHub profile tidak aktif meskipun sering coding + +### Goals + +- Menjaga coding streak setiap hari +- Mendokumentasikan progress coding untuk portofolio +- Membuat GitHub profile terlihat lebih aktif secara natural +- Punya catatan harian tentang apa yang dikerjakan + +### Pain Points + +- Kehilangan konteks ketika kembali ke project setelah beberapa hari +- Merasa GitHub profile-nya tidak mencerminkan kemampuan sebenarnya +- Sulit konsisten karena tidak ada sistem yang membantu +- Capek manually manage multiple repository + +### How Contribution Hub Helps + +- Auto-track aktivitas coding lintas project +- Daily summary mengingatkan apa yang sudah dikerjakan +- Streak tracker yang meaningful memotivasi konsistensi +- Issue/PR suggestion dari code changes yang terdeteksi + +--- + +## Persona 2: Sari -- The Career Switcher + +> "Aku lagi membangun portofolio dari nol. Butuh GitHub profile yang menunjukkan progress." + +### Demographics + +| Atribut | Detail | +| ----------- | -------------------------------------- | +| Umur | 24 tahun | +| Pekerjaan | Bootcamp graduate, job seeking | +| Lokasi | Bandung, Indonesia | +| Pengalaman | 1 tahun coding (otodidak + bootcamp) | +| GitHub usia | 6 bulan, 50 contributions | +| Tools | VS Code, GitHub Desktop, online course | + +### Behavior + +- Belajar coding setiap hari, minimal 2-3 jam +- Mengerjakan tutorial project dan challenge +- Aktif di Discord developer community +- Sering ragu apakah progress-nya cukup baik +- Membandingkan diri dengan developer lain di GitHub + +### Goals + +- Membangun GitHub profile yang menarik untuk rekruter +- Menunjukkan konsistensi belajar coding +- Mempunyai dokumentasi perjalanan belajar yang rapi +- Mendapatkan kepercayaan diri sebagai developer + +### Pain Points + +- Tidak tahu apakah aktivitas belajarnya "cukup" untuk GitHub +- Project tutorial tidak terlihat serius di GitHub +- Streak mudah putus karena belum rutin coding setiap hari +- Bingung cara membuat GitHub profile yang profesional + +### How Contribution Hub Helps + +- Merekam aktivitas belajar sebagai kontribusi yang valid +- Progress tracking menunjukkan perkembangan dari waktu ke waktu +- Streak system yang forgiving (tidak hanya commit streak) +- Public profile page yang bisa dibagikan ke rekruter + +--- + +## Persona 3: Dimas -- The Open Source Contributor + +> "Aku kontribusi ke beberapa open source project. Susah tracking semua aktivitas." + +### Demographics + +| Atribut | Detail | +| ----------- | ------------------------------------------ | +| Umur | 30 tahun | +| Pekerjaan | Software engineer di startup | +| Lokasi | Yogyakarta, Indonesia | +| Pengalaman | 7 tahun coding | +| GitHub usia | 5 tahun, 500+ contributions/year | +| Tools | Neovim, terminal-first, multiple OSS repos | + +### Behavior + +- Kontribusi ke 3-5 open source project secara rutin +- Aktif code review dan discussion di GitHub +- Maintain 1-2 project open source sendiri +- Punya sistem sendiri untuk tracking aktivitas (manual notes) +- Ingin lebih organized tapi tidak punya waktu + +### Goals + +- Tracking semua kontribusi OSS dalam satu tempat +- Mendapatkan insight tentang pola kontribusi +- Membuktikan dampak kontribusi ke komunitas +- Mengelola waktu antara pekerjaan utama dan OSS + +### Pain Points + +- Kontribusi tersebar di banyak repositori +- Sulit melihat gambaran besar dampak kontribusi +- Tidak ada metrics yang menunjukkan progress sebagai contributor +- Kadang lupa follow-up issue/PR yang pending + +### How Contribution Hub Helps + +- Aggregate aktivitas dari multiple repositori +- Cross-repository insight dan analytics +- Tracking issue/PR yang perlu follow-up +- Impact metrics untuk kontribusi OSS + +--- + +## Persona 4: Maya -- The Tech Lead + +> "Saya ingin tim saya lebih konsisten dalam documentation dan code review." + +### Demographics + +| Atribut | Detail | +| ----------- | ----------------------------------- | +| Umur | 34 tahun | +| Pekerjaan | Tech Lead di perusahaan teknologi | +| Lokasi | Surabaya, Indonesia | +| Pengalaman | 10 tahun coding | +| GitHub usia | 8 tahun, 1000+ contributions/year | +| Tools | Multiple tools, enterprise workflow | + +### Behavior + +- Mengelola tim engineer 5-10 orang +- Review code setiap hari +- Ingin membangun budaya engineering yang baik di tim +- Mencari tools untuk meningkatkan konsistensi tim +- Tertarik dengan metrics pengembangan + +### Goals + +- Monitoring aktivitas pengembangan tim secara aggregate +- Mendorong budaya dokumentasi dan code review +- Mengidentifikasi bottleneck dalam workflow tim +- Meningkatkan kualitas kontribusi, bukan kuantitas + +### Pain Points + +- Sulit melihat gambaran aktivitas tim di multiple repositori +- Anggota tim tidak konsisten dalam dokumentasi +- Tidak ada visibility tentang progress project harian +- Metrik kontribusi tim tidak terdokumentasi + +### How Contribution Hub Helps + +- Team dashboard dengan aggregate activity +- Code review tracking dan metrics +- Documentation compliance monitoring +- Weekly team activity report + +--- + +## Anti-Persona: The Spammer + +> Bukan target pengguna. Contribution Hub tidak dirancang untuk: + +- Developer yang ingin meningkatkan kontribusi GitHub secara artifisial +- Pengguna yang mencari bot untuk auto-commit +- Orang yang ingin "game" sistem streak GitHub +- Spammer yang membuat repository kosong untuk statistik + +Produk kami secara aktif mendesain fitur untuk mencegah use case ini. + +--- + +## Related Documents + +- [Product Definition](./definition.md) +- [Product Vision](./vision.md) +- [Product Roadmap](./roadmap.md) diff --git a/.docs/product/roadmap.md b/.docs/product/roadmap.md new file mode 100644 index 0000000..67c8b6e --- /dev/null +++ b/.docs/product/roadmap.md @@ -0,0 +1,160 @@ +# Product Roadmap -- Contribution Hub + +> Roadmap pengembangan Contribution Hub. Timeline bersifat indikatif dan dapat berubah berdasarkan prioritas bisnis dan feedback pengguna. + +--- + +## Fase 0: Foundation (Selesai) + +Foundation project sudah diselesaikan sebagai starter template: + +- [x] Inisialisasi Next.js 16 + TypeScript strict + Tailwind CSS v4 +- [x] Struktur folder Feature-Sliced Design (FSD) +- [x] Konfigurasi ESLint, Prettier, Husky, commitlint +- [x] Integrasi Shadcn UI + Radix UI +- [x] Setup TanStack React Query + Zustand + Zod +- [x] Setup next-intl untuk internasionalisasi +- [x] Setup Vitest + Testing Library + Playwright +- [x] Dokumentasi engineering (.docs/) +- [x] AGENTS.md -- AI collaboration guide +- [x] CI workflow (GitHub Actions) + +--- + +## Fase 1: MVP (Q3 2026) -- Sedang Dikerjakan + +### Product Definition + +- [x] Product vision dan definition documents +- [x] Target user personas +- [ ] Product design system dan UI/UX guidelines +- [ ] Feature specifications detail + +### Core Features + +- [ ] **GitHub Activity Tracker** + - Integrasi GitHub API untuk membaca aktivitas commit, PR, issue + - Personal activity log harian + - Kategorisasi aktivitas (coding, review, documentation, learning) + +- [ ] **Personal Dashboard** + - Overview aktivitas hari ini + - Weekly activity chart + - Streak tracker dengan meaningful milestones + - Recent activity feed + +- [ ] **Habit System** + - Daily goals (minimal commit, PR, atau aktivitas) + - Streak tracking (bukan sekadar commit streak -- aktivitas streak) + - Weekly progress report + - Motivational insights + +- [ ] **Auto-Dokumentasi** + - Generate daily activity summary dari commit messages + - Weekly progress report yang bisa di-export + - Auto-generate changelog dari aktivitas + +### Technical Foundation + +- [ ] Better Auth integration (GitHub OAuth) +- [ ] Database setup untuk menyimpan aktivitas +- [ ] API endpoints untuk CRUD aktivitas +- [ ] Background job untuk sync GitHub activity +- [ ] Landing page dan onboarding flow + +--- + +## Fase 2: Growth (Q4 2026) + +### Feature Enhancements + +- [ ] **Smart Issue/PR Suggestions** + - Mendeteksi pola aktivitas coding dan menyarankan issue/PR yang relevan + - Auto-create issue dari code changes yang terdeteksi + - Branch-to-PR workflow assistant + +- [ ] **Advanced Analytics** + - Productive hours detection + - Language/framework usage breakdown + - Project activity comparison + - Productivity trend analysis + +- [ ] **Multiple Repository Management** + - Monitor multiple GitHub repositories + - Organization activity overview + - Cross-repository insight + +### Integration + +- [ ] GitLab integration +- [ ] Bitbucket integration +- [ ] Local git hooks integration (untuk dev tanpa remote) +- [ ] API publik untuk custom integration + +### Community Features + +- [ ] Public profile page (opsional -- shareable) +- [ ] Developer badges dan milestone achievements +- [ ] Weekly developer challenge (opsional) + +--- + +## Fase 3: Maturity (Q1 2027) + +### Advanced Features + +- [ ] **AI-Powered Insight** + - Natural language daily summary + - Productivity pattern recognition + - Personalized recommendations + - Code impact analysis + +- [ ] **Team Plan** + - Team dashboard + - Activity overview untuk organisasi + - Team analytics + - Member progress tracking + +- [ ] **Developer Portfolio Generator** + - Auto-generate portfolio website dari aktivitas GitHub + - Kustomisasi tema dan layout + - Export ke PDF/HTML + - One-click deploy ke Vercel/Netlify + +### Platform + +- [ ] Mobile app (React Native) untuk tracking on-the-go +- [ ] VS Code extension untuk inline activity tracking +- [ ] Desktop app (Tauri) untuk offline-first experience + +--- + +## Fase 4: Ecosystem (Q2 2027+) + +- [ ] Open source plugin SDK +- [ ] Marketplace untuk custom integrations +- [ ] Developer API v1 publik +- [ ] Enterprise plan +- [ ] Global community events dan hackathons + +--- + +## Prioritization Framework + +Setiap fitur dievaluasi berdasarkan: + +| Kriteria | Bobot | +| ------------------------------------------------------ | ----- | +| Developer impact (seberapa banyak developer terbantu) | 40% | +| Alignment dengan core values (meaningful contribution) | 30% | +| Technical feasibility | 20% | +| Business value | 10% | + +--- + +## Related Documents + +- [Product Definition](./definition.md) +- [Product Vision](./vision.md) +- [User Personas](./personas.md) +- [ADR-001: Product Pivot](../adr/adr-001-product-pivot-contribution-hub.md) diff --git a/.docs/product/vision.md b/.docs/product/vision.md new file mode 100644 index 0000000..1339aaa --- /dev/null +++ b/.docs/product/vision.md @@ -0,0 +1,80 @@ +# Product Vision -- Contribution Hub + +> Visi, misi, nilai, dan arah jangka panjang produk. + +--- + +## Vision Statement + +> Menjadi platform developer activity assistant yang membantu setiap developer di dunia membangun kebiasaan coding yang konsisten, meaningful, dan terdokumentasi dengan baik -- tanpa takut kehilangan progress atau memiliki GitHub profile yang "mati". + +## Mission Statement + +Memberikan tools dan sistem yang memungkinkan developer untuk: + +1. **Merekam** setiap aktivitas coding secara otomatis dan bermakna +2. **Membangun** kebiasaan konsisten melalui streaks, goals, dan insight +3. **Mendokumentasikan** progress pengembangan secara terstruktur +4. **Menampilkan** aktivitas pengembangan secara profesional di GitHub +5. **Meningkatkan** personal branding sebagai developer yang aktif dan konsisten + +## Core Values + +### 1. Authenticity Above All + +Kami percaya developer sejati tidak perlu kontribusi palsu. Setiap commit, issue, dan PR harus mencerminkan pekerjaan nyata. Contribution Hub adalah tools untuk mendokumentasikan dan mengorganisir aktivitas autentik, bukan menciptakan ilusi produktivitas. + +### 2. Consistency Over Intensity + +Seperti latihan fisik, progress dalam engineering datang dari konsistensi, bukan ledakan aktivitas sesaat. Produk kami dirancang untuk membantu developer tetap bergerak setiap hari, dalam skala yang sustainable. + +### 3. Developer Empathy + +Kami membangun tools yang kami sendiri ingin gunakan. Setiap fitur diukur dari pertanyaan: "Apakah ini benar-benar membantu developer?" Jika jawabannya tidak, fitur tersebut tidak layak dibuat. + +### 4. Privacy as Default + +Aktivitas coding adalah data pribadi yang sensitif. Contribution Hub mengadopsi privacy-first approach: data milik pengguna, kontrol ada di tangan pengguna, transparansi adalah keharusan. + +### 5. Progress Over Perfection + +Tidak semua hari coding menghasilkan kode sempurna. Terkadang refactor, baca dokumentasi, atau belajar teknologi baru. Contribution Hub mengakui dan mendokumentasikan semua bentuk progress. + +## Long-Term Goals (3-5 Tahun) + +### Product Goals + +- Menjadi platform standar untuk developer activity documentation +- Ecosystem dengan API publik untuk integrasi tools pihak ketiga +- Team plan untuk organisasi yang ingin memonitor aktivitas pengembangan tim +- AI-powered insight yang membantu developer meningkatkan produktivitas + +### Community Goals + +- 10,000+ active users +- Open source plugin ecosystem +- Kontribusi balik ke komunitas developer (blog, open source tools) +- Partnership dengan bootcamp dan institusi pendidikan + +### Business Goals + +- Freemium model: gratis untuk individual, premium untuk tim +- Revenue dari premium features (analytics lanjutan, team management, priority support) +- Transparan tentang monetisasi -- tidak pernah menjual data pengguna + +## Brand Personality + +| Dimensi | Karakter | +| ----------- | --------------------------------------------------------------------- | +| **Tone** | Professional, hangat, engineering-focused | +| **Voice** | Seperti senior engineer yang membantu junior -- jelas, sabar, praktis | +| **Visual** | Clean, modern, dark-mode friendly, data-rich | +| **Feeling** | "Akhirnya ada tools yang ngerti kebutuhan developer" | + +--- + +## Related Documents + +- [Product Definition](./definition.md) +- [Product Roadmap](./roadmap.md) +- [User Personas](./personas.md) diff --git a/.docs/security/README.md b/.docs/security/README.md new file mode 100644 index 0000000..e120e91 --- /dev/null +++ b/.docs/security/README.md @@ -0,0 +1,105 @@ +# security/ -- Dokumentasi Keamanan + +> Security policies, authentication, authorization, data protection, dan best practices. + +--- + +## Tujuan + +Folder ini berisi dokumentasi keamanan yang mencakup: + +- Authentication dan authorization strategy +- Session management +- Data encryption dan protection +- API security (CORS, CSRF, rate limiting) +- Input validation dan sanitization +- Dependency vulnerability management +- Security incident response plan +- Compliance requirements + +--- + +## Struktur yang Direkomendasikan + +```txt +security/ +├── README.md # File ini -- panduan folder +├── auth.md # Authentication strategy +├── authorization.md # Role-based access control +├── data-protection.md # Data encryption, PII handling +├── api-security.md # CORS, CSRF, rate limiting, headers +├── dependency-security.md # Vulnerability scanning, updates +├── incident-response.md # Security incident response plan +└── checklist.md # Security review checklist +``` + +--- + +## Template Security Policy + +```markdown +# Security Policy: [Topik] + +## Policy + +[Aturan keamanan yang harus diikuti] + +## Implementation + +[Bagaimana aturan ini diimplementasikan] + +## Enforcement + +[Bagaimana kepatuhan dipastikan] + +## Exceptions + +[Jika ada pengecualian, bagaimana prosedurnya] + +## Review Date + +[Kapan kebijakan ini akan direview ulang] +``` + +--- + +## Keamanan Checklist + +### Development + +- [ ] Input validation dengan Zod di semua entry point +- [ ] SQL injection prevention (gunakan parameterized queries) +- [ ] XSS prevention (React handles ini, tapi tetap waspada dengan dangerouslySetInnerHTML) +- [ ] Dependency scanning (npm audit, Snyk) + +### API + +- [ ] CORS configuration yang tepat +- [ ] CSRF protection +- [ ] Rate limiting +- [ ] Request size limiting +- [ ] Security headers (Helmet atau manual) + +### Authentication + +- [ ] Password hashing (bcrypt, argon2) +- [ ] Session management yang aman +- [ ] JWT dengan expiry yang tepat +- [ ] 2FA/MFA (jika diperlukan) + +### Infrastructure + +- [ ] HTTPS everywhere +- [ ] Environment variables untuk secrets +- [ ] No secrets in code atau git history +- [ ] Regular security updates + +--- + +## Prinsip Keamanan + +1. **Defense in depth** -- Jangan bergantung pada satu lapisan keamanan +2. **Least privilege** -- Berikan akses minimum yang diperlukan +3. **Fail secure** -- Jika error, default ke keadaan yang aman +4. **Never trust user input** -- Validasi dan sanitasi semua input +5. **Security by design** -- Pikirkan keamanan dari awal, bukan setelah jadi diff --git a/.docs/security/security-baseline.md b/.docs/security/security-baseline.md new file mode 100644 index 0000000..3b6d567 --- /dev/null +++ b/.docs/security/security-baseline.md @@ -0,0 +1,181 @@ +# Security Baseline + +> Kebijakan keamanan dasar, environment variables rules, dan secure coding practices. + +--- + +## Environment Variables + +### Rules + +- **Jangan commit** `.env.local`, `.env.production` ke git +- **Jangan hardcode** secrets di kode +- Semua env variables validated di startup + +### Naming Convention + +```env +# Public (prefix NEXT_PUBLIC_) +NEXT_PUBLIC_APP_URL=http://localhost:3000 +NEXT_PUBLIC_SITE_NAME=MyApp + +# Private (server-side only) +DATABASE_URL=postgresql://... +AUTH_SECRET=your-secret-here +API_KEY=your-api-key +``` + +### Validation (dengan @t3-oss/env-nextjs) + +```tsx +// src/config/env.ts +import { createEnv } from "@t3-oss/env-nextjs" +import { z } from "zod" + +export const env = createEnv({ + server: { + DATABASE_URL: z.string().url(), + AUTH_SECRET: z.string().min(32) + }, + client: { + NEXT_PUBLIC_APP_URL: z.string().url(), + NEXT_PUBLIC_SITE_NAME: z.string().min(1) + }, + runtimeEnv: { + NEXT_PUBLIC_APP_URL: process.env.NEXT_PUBLIC_APP_URL, + NEXT_PUBLIC_SITE_NAME: process.env.NEXT_PUBLIC_SITE_NAME, + // Server vars hanya diakses di server + DATABASE_URL: process.env.DATABASE_URL, + AUTH_SECRET: process.env.AUTH_SECRET + } +}) +``` + +--- + +## API Security + +### CORS Configuration + +```tsx +// next.config.ts +const nextConfig = { + async headers() { + return [ + { + source: "/api/:path*", + headers: [ + { + key: "Access-Control-Allow-Origin", + value: process.env.NEXT_PUBLIC_APP_URL! + }, + { key: "Access-Control-Allow-Methods", value: "GET,POST,PUT,DELETE" }, + { + key: "Access-Control-Allow-Headers", + value: "Content-Type, Authorization" + } + ] + } + ] + } +} +``` + +### Security Headers + +```tsx +{ + source: '/(.*)', + headers: [ + { key: 'X-Frame-Options', value: 'DENY' }, + { key: 'X-Content-Type-Options', value: 'nosniff' }, + { key: 'Referrer-Policy', value: 'strict-origin-when-cross-origin' }, + { key: 'X-XSS-Protection', value: '1; mode=block' }, + ], +} +``` + +--- + +## Secure Coding Practices + +### Input Validation + +```tsx +// ✅ Selalu validasi input dengan Zod +const schema = z.object({ + email: z.string().email(), + password: z.string().min(8).max(100) +}) + +// ❌ Jangan trust user input langsung +function unsafe(input: string) { + db.query(`SELECT * FROM users WHERE id = ${input}`) // SQL injection! +} +``` + +### Output Encoding + +- React secara otomatis handle XSS via JSX +- Jangan gunakan `dangerouslySetInnerHTML` tanpa sanitasi +- Untuk HTML dari server, gunakan DOMPurify + +### Authentication + +- Jangan simpan password plaintext +- Gunakan bcrypt/argon2 untuk hashing +- Session tokens dengan expiry +- Implement rate limiting untuk login attempts + +--- + +## Data Protection + +### PII Handling + +- Jangan log PII (Personal Identifiable Information) +- Encrypt sensitive data di database +- Minimal data collection principle +- Hapus data user jika diminta + +### Client-side + +```tsx +// ✅ Aman: NEXT_PUBLIC_ prefix +const apiUrl = process.env.NEXT_PUBLIC_APP_URL + +// ❌ Bahaya: server-only variable di client +const secret = process.env.AUTH_SECRET // undefined di client, tapi jangan! +``` + +--- + +## Dependency Security + +### Checklist + +- [ ] `npm audit` secara berkala +- [ ] Review major version updates +- [ ] Hindari dependencies dengan banyak transitive deps +- [ ] Prioritaskan libraries yang aktif maintained +- [ ] Gunakan lockfile (package-lock.json) + +### Commands + +```bash +# Check vulnerabilities +npm audit + +# Fix vulnerabilities +npm audit fix + +# Outdated packages +npm outdated +``` + +--- + +## Related Documents + +- [API Guidelines](../api/api-guidelines.md) +- [Tech Stack](../technical/tech-stack.md) diff --git a/.docs/seo/README.md b/.docs/seo/README.md new file mode 100644 index 0000000..f824cab --- /dev/null +++ b/.docs/seo/README.md @@ -0,0 +1,176 @@ +# SEO Strategy -- Contribution Hub + +> Strategi Search Engine Optimization untuk Contribution Hub. + +--- + +## Target Keywords + +### Primary Keywords + +| Keyword | Search Intent | Target Page | +| --------------------------- | -------------------------- | ---------------- | +| developer activity tracker | Informational / Commercial | Landing page | +| GitHub contribution tracker | Commercial | Features page | +| coding streak tracker | Commercial | Dashboard page | +| developer habit system | Informational | Blog / Features | +| GitHub profile enhancer | Commercial | Profile features | + +### Secondary Keywords + +- "track coding activity" +- "developer productivity tool" +- "GitHub streak tracker" +- "coding habit tracker" +- "developer portfolio generator" +- "auto generate GitHub profile README" +- "meaningful GitHub contributions" + +### Long-tail Keywords + +- "how to maintain coding streak everyday" +- "best tool to track github contributions" +- "developer activity log for portfolio" +- "automatic daily coding summary tool" +- "indonesian developer productivity tool" + +## Content Strategy + +### Landing Page + +- Clear value proposition: "Your personal developer activity assistant" +- Feature highlights dengan screenshot/demo +- Social proof (testimonials, user count) +- CTA untuk sign up dengan GitHub + +### Blog Content (SEO Driver) + +| Topic | Keywords | Frequency | +| ------------------------------------- | ------------------------------ | --------- | +| Cara menjaga coding streak | coding streak, developer habit | Monthly | +| Tips meaningful GitHub contribution | meaningful contribution | Monthly | +| Review tools productivity developer | developer productivity | Bi-weekly | +| Panduan GitHub profile untuk rekruter | GitHub profile, portfolio | Monthly | +| Developer habit building guide | habit system | Bi-weekly | + +### Documentation Pages + +Setiap feature memiliki dedicated page yang bisa di-index: + +- `/features/activity-tracking` +- `/features/habit-system` +- `/features/auto-documentation` +- `/features/github-profile-enhancer` +- `/pricing` +- `/docs/api` + +## Technical SEO + +### Meta Tags + +```tsx +// Konfigurasi SEO per halaman +export const metadata = { + title: "Contribution Hub - Developer Activity Assistant", + description: + "Track your coding activity, maintain streaks, and enhance your GitHub profile automatically. The developer habit system that works with your workflow.", + keywords: [ + "developer activity tracker", + "GitHub contribution tracker", + "coding streak", + "developer habit system" + ], + openGraph: { + title: "Contribution Hub", + description: "Your personal developer activity assistant", + type: "website", + siteName: "Contribution Hub" + }, + twitter: { + card: "summary_large_image", + title: "Contribution Hub", + description: "Your personal developer activity assistant" + } +} +``` + +### Structured Data (JSON-LD) + +```json +{ + "@context": "https://schema.org", + "@type": "SoftwareApplication", + "name": "Contribution Hub", + "applicationCategory": "DeveloperApplication", + "operatingSystem": "Web", + "description": "Developer activity assistant that tracks coding activity, maintains streaks, and enhances GitHub profiles.", + "offers": { + "@type": "Offer", + "price": "0", + "priceCurrency": "USD" + } +} +``` + +### Performance SEO + +- SSR untuk landing page dan content pages +- ISR untuk pages yang jarang berubah (docs, pricing) +- Dynamic import untuk dashboard (client-side, authenticated) +- Image optimization dengan next/image +- Preconnect ke GitHub API dan database + +### Sitemap + +```xml +<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9"> + <url><loc>https://contribution-hub.dev/</loc><priority>1.0</priority></url> + <url><loc>https://contribution-hub.dev/features</loc><priority>0.9</priority></url> + <url><loc>https://contribution-hub.dev/pricing</loc><priority>0.8</priority></url> + <url><loc>https://contribution-hub.dev/docs</loc><priority>0.7</priority></url> + <url><loc>https://contribution-hub.dev/blog</loc><priority>0.8</priority></url> +</urlset> +``` + +## Link Building Strategy + +1. **Open Source**: Product itu sendiri adalah open source -- natural backlinks dari GitHub +2. **Developer Communities**: Posting di dev.to, Reddit r/webdev, r/indonesia +3. **Guest Blogging**: Artikel tentang developer productivity di blog teknologi Indonesia +4. **Directory Listing**: Product Hunt, SaaSHub, AlternativeTo +5. **Tool Integrations**: Menjadi "tool yang direkomendasikan" di ecosystem tools lain + +## Local SEO (Indonesia) + +- Target primary: developer Indonesia +- Content dalam Bahasa Indonesia untuk blog posts +- Community engagement di Discord developer Indonesia +- Partnership dengan bootcamp coding Indonesia +- Event: webinar tentang developer productivity + +## Monitoring & Analytics + +| Tool | Purpose | +| --------------------- | -------------------------------- | +| Google Search Console | Keyword ranking, indexing issues | +| Google Analytics 4 | Traffic, user behavior | +| Ahrefs / SEMrush | Competitor analysis, backlinks | +| Vercel Analytics | Performance, Core Web Vitals | + +## Success Metrics + +| Metrik | Target (3 bulan) | Target (6 bulan) | +| ------------------------ | -------------------- | -------------------- | +| Organic traffic | 1,000 visitors/month | 5,000 visitors/month | +| Keyword ranking (top 10) | 5 keywords | 20 keywords | +| Backlinks | 10 | 50 | +| Blog subscribers | 100 | 500 | +| Conversion rate (signup) | 5% | 8% | + +--- + +## Related Documents + +- [Product Definition](../product/definition.md) +- [Tech Stack](../technical/tech-stack.md) +- [Deployment Pipeline](../deployment/pipeline.md) diff --git a/.docs/technical/README.md b/.docs/technical/README.md new file mode 100644 index 0000000..d1e6b6a --- /dev/null +++ b/.docs/technical/README.md @@ -0,0 +1,87 @@ +# technical/ -- Dokumentasi Teknis + +> Tech stack, dependencies, konfigurasi, dan development environment. + +--- + +## Tujuan + +Folder ini berisi dokumentasi teknis yang mencakup: + +- Tech stack dan alasan pemilihan teknologi +- Dependency management dan versioning +- Development environment setup +- Konfigurasi tooling (ESLint, Prettier, TypeScript) +- Build dan bundle configuration +- Environment variables dan secrets management +- Performance optimization notes +- Known limitations dan trade-offs + +--- + +## Struktur yang Direkomendasikan + +```txt +technical/ +├── README.md # File ini -- panduan folder +├── stack.md # Tech stack dan justifikasi +├── dependencies.md # Daftar dependencies dan alasan +├── environment.md # Environment variables +├── tooling.md # ESLint, Prettier, TypeScript config +├── performance.md # Performance notes dan optimasi +└── troubleshooting.md # Common issues dan solusi +``` + +--- + +## Template Tech Stack Documentation + +```markdown +# [Teknologi] -- [Fungsi] + +## Version + +[Versi yang digunakan] + +## Why + +[Mengapa memilih teknologi ini dibandingkan alternatif] + +## Configuration + +[Konfigurasi penting] + +## Known Limitations + +[Batasan yang perlu diketahui] + +## Migration Path + +[Jika ada rencana migrasi ke teknologi lain] +``` + +--- + +## Best Practices + +1. **Catat alasan** -- Setiap keputusan teknis harus punya justifikasi tertulis +2. **Update konfigurasi** -- Jika mengubah konfigurasi tooling, update dokumentasi +3. **Dokumentasikan workaround** -- Jika ada bug workaround, catat untuk tim +4. **Version matters** -- Catat versi exact dari dependencies penting +5. **Environment parity** -- Pastikan development, staging, production semirip mungkin + +--- + +## Template Dependency Documentation + +```markdown +## [package-name] v[version] + +**Purpose:** [Mengapa package ini dibutuhkan] + +**Alternatives considered:** [Alternatif yang dipertimbangkan] + +**Bundle impact:** [Perkiraan ukuran bundle] + +**When to remove:** [Kondisi ketika package bisa dihapus] +``` diff --git a/.docs/technical/tech-stack.md b/.docs/technical/tech-stack.md new file mode 100644 index 0000000..5be5ff7 --- /dev/null +++ b/.docs/technical/tech-stack.md @@ -0,0 +1,122 @@ +# Tech Stack + +> Dokumentasi teknologi utama yang digunakan dalam project ini. + +--- + +## Core Framework + +| Teknologi | Versi | Fungsi | Justifikasi | +| -------------------- | ----- | ----------------------------- | ------------------------------------------ | +| Next.js (App Router) | ^16 | Routing, SSR, RSC, API Routes | Standard industri, RSC, file-based routing | +| React | ^19 | UI library | Ecosystem terbesar, RSC support | +| TypeScript | ^5 | Type safety | Strict mode, zero tolerance untuk any | +| Tailwind CSS | ^4 | Styling | Utility-first, JIT compiler, performant | + +--- + +## UI & Design System + +| Package | Fungsi | Priority | +| ------------ | ------------------------------ | ----------- | +| shadcn/ui | Reusable accessible components | WAJIB | +| next-themes | Dark mode & theme switching | WAJIB | +| lucide-react | Consistent icon system | RECOMMENDED | +| sonner | Toast notifications | RECOMMENDED | +| motion | UI animations | OPTIONAL | +| cmdk | Command palette | OPTIONAL | + +### shadcn/ui Convention + +- Install component per-component: `npx shadcn@latest add button` +- Customize via className prop, jangan modify source +- Gunakan `cn()` utility untuk conditional classes +- Jangan buat wrapper component untuk shadcn -- langsung import dari `@/components/ui/` + +--- + +## State Management + +| Package | Fungsi | Priority | +| -------------------------- | ------------------------------------------- | -------- | +| @tanstack/react-query | Server state, caching, optimistic updates | WAJIB | +| zustand | Client state (UI preferences, global state) | WAJIB | +| next/router + searchParams | URL state | BUILT-IN | + +### State Boundaries + +| State Type | Tool | Examples | +| ----------- | --------------- | ---------------------------------- | +| Server data | React Query | User list, posts, API responses | +| UI state | Zustand | Sidebar open, modal state, filters | +| URL state | Next.js Router | Page, sort, search query | +| Form state | React Hook Form | Form fields, validation errors | +| Theme state | next-themes | Dark/light mode | +| i18n locale | next-intl | Current language, translations | + +--- + +## Forms & Validation + +| Package | Fungsi | Priority | +| ------------------- | --------------------- | -------- | +| react-hook-form | Form state management | WAJIB | +| @hookform/resolvers | Zod integration | WAJIB | +| zod | Schema validation | WAJIB | + +### Data Flow + +``` +Zod Schema (type + validation) + → React Hook Form (form state) + → Server Action / React Query (submission) + → Response +``` + +--- + +## Internationalization + +| Package | Fungsi | Priority | +| --------- | ----------------------------------- | -------- | +| next-intl | i18n: routing, messages, formatting | WAJIB | + +### i18n Rules + +- Default locale: `id` (Indonesia) +- Messages per locale: `src/i18n/messages/id.json`, `en.json` +- Gunakan ICU message syntax untuk pluralization +- Namespace messages per feature jika besar +- Jangan hardcode user-facing strings + +--- + +## Utilities + +| Package | Fungsi | Priority | +| ------------------ | ------------------------------ | ----------- | +| usehooks-ts | Reusable React hooks | RECOMMENDED | +| date-fns | Date formatting & manipulation | RECOMMENDED | +| hotkeys-js | Keyboard shortcuts | OPTIONAL | +| @t3-oss/env-nextjs | Typed env validation | RECOMMENDED | + +--- + +## Development Tools + +| Tool | Fungsi | +| ------------------- | ---------------------------- | +| ESLint | Code linting & consistency | +| Prettier | Code formatting (via ESLint) | +| TypeScript strict | Static type checking | +| Husky + lint-staged | Pre-commit hooks | +| Vitest | Unit & integration testing | +| Testing Library | React component testing | +| Playwright | E2E browser testing | + +--- + +## Related Documents + +- [System Overview](../architecture/system-overview.md) +- [Installation Guide](../onboarding/getting-started.md) diff --git a/.docs/testing/README.md b/.docs/testing/README.md new file mode 100644 index 0000000..8b72943 --- /dev/null +++ b/.docs/testing/README.md @@ -0,0 +1,119 @@ +# testing/ -- Dokumentasi Testing + +> Testing strategy, test types, coverage goals, dan best practices. + +--- + +## Tujuan + +Folder ini berisi dokumentasi testing yang mencakup: + +- Testing strategy dan philosophy +- Unit testing guidelines +- Integration testing approach +- E2E testing dengan Playwright +- Coverage targets dan monitoring +- Mocking strategy +- Test data management +- CI/CD test integration + +--- + +## Struktur yang Direkomendasikan + +```txt +testing/ +├── README.md # File ini -- panduan folder +├── strategy.md # Testing strategy overview +├── unit-testing.md # Unit testing guidelines +├── integration-testing.md # Integration testing approach +├── e2e-testing.md # E2E testing dengan Playwright +├── mocking.md # Mocking strategy dan tools +├── coverage.md # Coverage targets dan laporan +└── test-data.md # Test fixtures dan factories +``` + +--- + +## Testing Pyramid + +``` + /\ + / \ E2E (Playwright) -- Critical paths + / \ + / \ Integration -- Feature interactions + / \ + /__________\ Unit (Vitest) -- Logic, hooks, utils +``` + +--- + +## Template Test Plan + +```markdown +# Test Plan: [Feature/Module] + +## Scope + +[Apa yang akan di-test] + +## Out of Scope + +[Apa yang TIDAK akan di-test] + +## Test Cases + +### Unit Tests + +- [ ] Test case 1: [deskripsi] +- [ ] Test case 2: [deskripsi] + +### Integration Tests + +- [ ] Test case 1: [deskripsi] + +### E2E Tests + +- [ ] Test case 1: [deskripsi] + +## Test Data + +[Data yang dibutuhkan untuk testing] + +## Success Criteria + +[Kriteria kelulusan] +``` + +--- + +## Testing Principles + +1. **Test behavior, not implementation** -- Fokus pada output, bukan internal detail +2. **Arrange-Act-Assert** -- Pola standar untuk test cases +3. **One assertion per test** -- Idealnya satu logical assertion per test +4. **Independent tests** -- Test harus bisa jalan dalam urutan apapun +5. **Fast feedback** -- Unit test harus selesai dalam milliseconds +6. **Realistic data** -- Gunakan data yang mendekati production + +--- + +## Coverage Targets + +| Layer | Target | +| ---------------------- | ------------ | +| Utils / Pure functions | 90%+ | +| Hooks | 80%+ | +| Components | 70%+ | +| API Routes | 80%+ | +| E2E Critical Paths | 100% covered | + +--- + +## Tools + +- **Vitest** -- Unit dan integration testing +- **Testing Library** -- React component testing +- **Playwright** -- E2E browser testing +- **MSW** -- API mocking (jika digunakan) +- **faker.js / @faker-js** -- Test data generation diff --git a/.docs/testing/testing-strategy.md b/.docs/testing/testing-strategy.md new file mode 100644 index 0000000..850a5e9 --- /dev/null +++ b/.docs/testing/testing-strategy.md @@ -0,0 +1,302 @@ +# Testing Strategy + +> Strategi testing, fokus pada async operations dan provider testing. + +--- + +## Testing Pyramid + +``` + /\ + / \ E2E (Playwright) -- 5% kritis + / \ + / \ Integration -- 20% interaksi fitur + / \ + /__________\ Unit (Vitest) -- 75% logic murni +``` + +--- + +## Unit Testing + +### Pure Functions (Utils) + +```tsx +import { describe, expect, it } from "vitest" + +import { formatDate } from "./format-date" + +describe("formatDate", () => { + it("should format ISO date to Indonesian format", () => { + expect(formatDate("2024-01-15")).toBe("15 Januari 2024") + }) + + it("should handle invalid dates", () => { + expect(formatDate("invalid")).toBe("-") + }) + + it("should handle null/undefined", () => { + expect(formatDate(null)).toBe("-") + expect(formatDate(undefined)).toBe("-") + }) +}) +``` + +### Hooks + +```tsx +import { act, renderHook } from "@testing-library/react" + +import { useDebounce } from "./use-debounce" + +describe("useDebounce", () => { + it("should debounce value changes", async () => { + vi.useFakeTimers() + + const { result, rerender } = renderHook( + ({ value }) => useDebounce(value, 500), + { initialProps: { value: "hello" } } + ) + + expect(result.current).toBe("hello") + + rerender({ value: "world" }) + expect(result.current).toBe("hello") // Belum berubah + + act(() => { + vi.advanceTimersByTime(500) + }) + expect(result.current).toBe("world") // Sudah debounce + + vi.useRealTimers() + }) +}) +``` + +### Zustand Stores + +```tsx +import { useAuthStore } from "./auth-store" + +describe("authStore", () => { + beforeEach(() => { + useAuthStore.setState({ user: null, isAuthenticated: false }) + }) + + it("should set user on login", () => { + const user = { id: "1", name: "John" } + act(() => useAuthStore.getState().login(user)) + + expect(useAuthStore.getState().user).toEqual(user) + expect(useAuthStore.getState().isAuthenticated).toBe(true) + }) + + it("should clear user on logout", () => { + act(() => useAuthStore.getState().login({ id: "1", name: "John" })) + act(() => useAuthStore.getState().logout()) + + expect(useAuthStore.getState().user).toBeNull() + expect(useAuthStore.getState().isAuthenticated).toBe(false) + }) +}) +``` + +--- + +## Integration Testing + +### React Query Components + +```tsx +import { QueryClient, QueryClientProvider } from "@tanstack/react-query" +import { render, screen, waitFor } from "@testing-library/react" + +import { UserList } from "./user-list" + +function createWrapper() { + const queryClient = new QueryClient({ + defaultOptions: { queries: { retry: false } } + }) + + return function Wrapper({ children }: { children: React.ReactNode }) { + return ( + <QueryClientProvider client={queryClient}>{children}</QueryClientProvider> + ) + } +} + +// Mock API +vi.mock("@/features/users/api/get-users", () => ({ + getUsers: vi.fn().mockResolvedValue([ + { id: "1", name: "John" }, + { id: "2", name: "Jane" } + ]) +})) + +describe("UserList", () => { + it("should render list of users", async () => { + render(<UserList />, { wrapper: createWrapper() }) + + expect(screen.getByText(/loading/i)).toBeInTheDocument() + + await waitFor(() => { + expect(screen.getByText("John")).toBeInTheDocument() + expect(screen.getByText("Jane")).toBeInTheDocument() + }) + }) + + it("should handle error state", async () => { + vi.mocked(getUsers).mockRejectedValueOnce(new Error("Failed")) + + render(<UserList />, { wrapper: createWrapper() }) + + await waitFor(() => { + expect(screen.getByText(/error/i)).toBeInTheDocument() + }) + }) +}) +``` + +### Provider Testing + +```tsx +import { useTheme } from "next-themes" + +import { render, screen } from "@testing-library/react" + +import { ThemeProvider } from "./theme-provider" + +function TestComponent() { + const { theme, setTheme } = useTheme() + return ( + <div> + <span data-testid="theme">{theme}</span> + <button onClick={() => setTheme("dark")}>Dark</button> + </div> + ) +} + +describe("ThemeProvider", () => { + it("should provide theme context", () => { + render( + <ThemeProvider> + <TestComponent /> + </ThemeProvider> + ) + + expect(screen.getByTestId("theme")).toHaveTextContent("light") + }) +}) +``` + +--- + +## Async Testing Patterns + +### Loading States + +```tsx +it("should show loading state during fetch", () => { + render(<DataComponent />, { wrapper }) + + // Assert loading UI muncul + expect(screen.getByRole("status")).toBeInTheDocument() + expect(screen.getByText(/memuat/i)).toBeInTheDocument() +}) +``` + +### Error States + +```tsx +it("should show error state on failure", async () => { + // Trigger error + render(<DataComponent />, { wrapper }) + + await waitFor(() => { + expect(screen.getByRole("alert")).toBeInTheDocument() + expect(screen.getByText(/gagal/i)).toBeInTheDocument() + }) +}) +``` + +### Empty States + +```tsx +it("should show empty state when no data", async () => { + render(<DataComponent />, { wrapper }) + + await waitFor(() => { + expect(screen.getByText(/tidak ada data/i)).toBeInTheDocument() + }) +}) +``` + +--- + +## Mocking Strategy + +### API Calls + +```tsx +// Global mock di setup +import { http, HttpResponse } from "msw" +import { setupServer } from "msw/node" + +export const handlers = [ + http.get("/api/users", () => { + return HttpResponse.json([{ id: "1", name: "John" }]) + }) +] + +export const server = setupServer(...handlers) +``` + +### Modules + +```tsx +vi.mock("@/lib/api-client", () => ({ + apiClient: { + get: vi.fn(), + post: vi.fn() + } +})) +``` + +--- + +## Coverage Targets + +| Layer | Target | +| ---------------------- | ------------------- | +| Utils / Pure functions | 90%+ | +| Zustand stores | 90%+ | +| Custom hooks | 80%+ | +| Feature components | 70%+ | +| API route handlers | 80%+ | +| Page components | 60%+ | +| shadcn/ui components | Covered by upstream | + +--- + +## Running Tests + +```bash +# All tests +npm run test + +# Watch mode +npm run test -- --watch + +# Specific file +npm run test -- src/utils/format-date.test.ts + +# Coverage +npm run test -- --coverage +``` + +--- + +## Related Documents + +- [Engineering Principles](../engineering/engineering-principles.md) +- [API Guidelines](../api/api-guidelines.md) diff --git a/.env.example b/.env.example new file mode 100644 index 0000000..f8408d4 --- /dev/null +++ b/.env.example @@ -0,0 +1,40 @@ +# ============================================================================= +# Environment Variables +# ============================================================================= +# Salin file ini menjadi .env lalu isi nilai-nilai yang sesuai. +# Jangan commit .env ke repository. File ini hanya template. +# +# Cara pakai: +# cp .env.example .env +# +# Semua variabel wajib diisi kecuali ditandai (opsional). +# ============================================================================= + +# --- Next.js --- +# URL aplikasi saat development. Ubah jika menggunakan port berbeda. +NEXT_PUBLIC_APP_URL=http://localhost:3000 + +# --- Better Auth --- +# Secret key untuk signing token. Generate dengan: `openssl rand -hex 32` +# AUTH_SECRET= + +# --- Database --- +# Connection string untuk database (PostgreSQL / SQLite / MySQL). +# Contoh PostgreSQL: postgresql://user:password@localhost:5432/film_gueh +# DATABASE_URL= + +# --- OAuth Providers (opsional) --- +# Google +# AUTH_GOOGLE_ID= +# AUTH_GOOGLE_SECRET= + +# GitHub +# AUTH_GITHUB_ID= +# AUTH_GITHUB_SECRET= + +# --- i18n (opsional) --- +# Lokalisasi default. Nilai: id, en, dll. +# NEXT_PUBLIC_DEFAULT_LOCALE=id + +# --- Analytics (opsional) --- +# NEXT_PUBLIC_GA_ID= diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..e66f216 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,42 @@ +# AGENTS.md — Lihat AGENTS.md untuk panduan kolaborasi AI. +# Konfigurasi ini mengikuti praktik standar CI untuk Next.js + TypeScript. + +name: CI + +on: + push: + branches: [main] + pull_request: + branches: [main] + +jobs: + quality: + name: Quality Gates + runs-on: ubuntu-latest + + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Setup Bun + uses: oven-sh/setup-bun@v2 + with: + bun-version: latest + + - name: Install dependencies + run: bun install --frozen-lockfile + + - name: Check types + run: bun check-types + + - name: Lint + run: bun lint + + - name: Format + run: bun format:fix + + - name: Format check + run: bun format + + - name: Build + run: bun run build diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..dc32fea --- /dev/null +++ b/.gitignore @@ -0,0 +1,43 @@ +# See https://help.github.com/articles/ignoring-files/ for more about ignoring files. + +# dependencies +/node_modules +/.pnp +.pnp.js + +# testing +/coverage +/test-results/ +/playwright-report/ +/blob-report/ +/playwright/.cache/ +/e2e/.auth/ + +# storybook +migration-storybook.log +storybook.log +storybook-static + + +# production +/dist + +# misc +.DS_Store +.env +.env.local +.env.development.local +.env.test.local +.env.production.local + +npm-debug.log* +yarn-debug.log* +yarn-error.log* + + +# local +mocked-db.json + +/.next +/.vite +tsconfig.tsbuildinfo \ No newline at end of file diff --git a/.husky/pre-commit b/.husky/pre-commit new file mode 100644 index 0000000..2312dc5 --- /dev/null +++ b/.husky/pre-commit @@ -0,0 +1 @@ +npx lint-staged diff --git a/.husky/pre-push b/.husky/pre-push new file mode 100644 index 0000000..144afe7 --- /dev/null +++ b/.husky/pre-push @@ -0,0 +1 @@ +npx tsc --noEmit && npm test diff --git a/.prettierrc b/.prettierrc new file mode 100644 index 0000000..db9551b --- /dev/null +++ b/.prettierrc @@ -0,0 +1,48 @@ +{ + "semi": false, + "singleQuote": false, + "useTabs": true, + "tabWidth": 2, + "printWidth": 80, + "trailingComma": "none", + "endOfLine": "auto", + "arrowParens": "always", + "quoteProps": "as-needed", + "bracketSpacing": true, + "htmlWhitespaceSensitivity": "ignore", + + "importOrder": [ + "^(react/(.*)$)|^(react$)|^(react-(.*)$)", + "^(next/(.*)$)|^(next$)|^(next-(.*)$)", + "^(@next)/(.*)$", + "", + "^(@radix-ui)/(.*)$", + "^(@tanstack)/(.*)$", + "^(zustand/(.*)$)|^(zustand$)", + "<THIRD_PARTY_MODULES>", + "", + "^@/types", + "^@/config", + "^@/constants", + "^@/data", + "", + "^@/hooks", + "^@/lib", + "^@/utils", + "", + "^@/components/ui", + "^@/components/widgets", + "^@/components/layouts", + "^@/components/providers", + "", + "^[./]" + ], + "importOrderCaseSensitive": false, + + "plugins": [ + "@ianvs/prettier-plugin-sort-imports", + "prettier-plugin-tailwindcss" + ], + "tailwindStylesheet": "./src/styles/globals.css", + "tailwindFunctions": ["clsx", "cva"] +} diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 0000000..3cfa54d --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,391 @@ +# AGENTS.md — General Engineering Handbook + +## Project Overview + +**Platform**: Next.js 14 (App Router) + Tailwind CSS v4 + TypeScript (Strict) +**Arsitektur**: Feature-Sliced Design (FSD) — modular, scalable, future-proof +**Target**: General-purpose web application foundation yang siap dikembangkan ke domain apapun + +Repositori ini adalah **starter foundation project** — bukan template kosong, bukan production app, tapi sweet spot di tengah. Struktur sudah matang, boundary jelas, dokumentasi tersedia, dan siap untuk developer maupun AI coding agent. + +--- + +## Tech Stack & Justifikasi + +| Teknologi | Fungsi | Alasan | +| ---------------------------- | ----------------------------- | ------------------------------------------ | +| **Next.js 14** (App Router) | Routing, SSR, RSC, API Routes | Standard industri React framework | +| **TypeScript** (Strict) | Type safety | Zero tolerance untuk `any` | +| **Tailwind CSS v4** | Styling | Utility-first, performant | +| **Shadcn UI / Radix** | Component primitives | Accessible, customizable, headless | +| **TanStack React Query** | Server state | Caching, deduplication, optimistic updates | +| **Zustand** | Client state | Ringan, no boilerplate | +| **Zod** | Validation | Type-safe runtime validation | +| **next-intl** | i18n | Standard untuk Next.js internasionalisasi | +| **Vitest + Testing Library** | Unit/integration test | Fast, modern, React-friendly | +| **Playwright** | E2E test | Reliable browser automation | +| **Motion** | Animasi | Declarative, performant | + +--- + +## Architecture Principles + +### 1. Feature-Based Modular + +Setiap fitur adalah module mandiri di `src/features/`. Module punya dependency sendiri (api, hooks, types, components) dan tidak boleh saling import secara langsung. + +### 2. Type-First Development + +- Tulis type/interface sebelum implementasi +- Semua data yang masuk/keluar harus punya type +- Inferensi type lebih baik daripada explicit type di semua tempat + +### 3. Separation of Concerns + +- **UI layer**: Hanya rendering, tidak ada business logic +- **Logic layer**: Hooks, services, utils — tidak ada JSX +- **State layer**: Zustand stores, React Query — tidak ada UI +- **API layer**: fetch, axios — tidak ada business logic + +### 4. Barrel Export + +Setiap folder publik memiliki `index.ts` sebagai barrel export. Internal module tidak boleh di-import langsung dari file深处. + +### 5. No Circular Dependencies + +Import graph harus tetap DAG (Directed Acyclic Graph). Tidak boleh ada: + +- Component import dari feature lain secara langsung +- Store import dari component +- Lib import dari utils (atau sebaliknya) + +--- + +## Coding Standards + +### TypeScript + +- `strict: true` di tsconfig +- **Dilarang** menggunakan `as any`, `@ts-ignore`, `@ts-expect-error` tanpa alasan tertulis +- Gunakan `satisfies` operator untuk validasi object literal +- Utility types lebih baik daripada manual union types + +### Naming Conventions + +| Entitas | Format | Contoh | +| ----------------- | ---------------------- | -------------------------------------- | +| Component file | `kebab-case.tsx` | `button.tsx`, `user-card.tsx` | +| Component widgets | `pascal-case.tsx` | `Button.tsx`, `UserCard.tsx` | +| Provider | `pascal-case.tsx` | `ThemeProvider.tsx`, `AuthContext.tsx` | +| Props type | `XxxProps` | `ButtonProps`, `UserCardProps` | +| Type/Interface | `XxxType` / PascalCase | `UserType`, `ApiResponse` | +| Hook | `use-kebab-case.ts` | `use-debounce.ts` | +| Utility | `kebab-case.ts` | `format-date.ts` | +| Store | `kebab-case.ts` | `auth-store.ts` | +| API function | `kebab-case.ts` | `get-users.ts` | +| Constants | `UPPER_CASE` | `MAX_RETRY_COUNT` | + +### Import Rules + +- Gunakan `@/` alias, jangan relative path (`../../`) kecuali di baris terakhir grup `^[./]` +- Imports diurutkan otomatis oleh `@ianvs/prettier-plugin-sort-imports` +- Pisahkan import type dengan `import type { ... }` +- Setiap grup import dipisahkan baris kosong (`importOrderSeparation: true`) +- Barrel export hanya dari `index.ts`, jangan dari file lain + +### Styling + +- Tailwind CSS utility classes +- Hindari inline styles, CSS Modules, styled-components +- Gunakan `cn()` utility dari `@/utils/cn` untuk conditional classes +- Komponen Shadcn UI bisa di-customize via `className` prop + +--- + +## AI Collaboration Rules + +### Memberi Instruksi ke AI Agent + +1. **Spesifik**: Sebutkan file path, function name, dan expected behavior +2. **Contexual**: Berikan referensi pattern yang sudah ada +3. **Boundary**: Jelaskan scope — apa yang BOLEH dan TIDAK BOLEH diubah +4. **Verification**: Sebutkan cara memverifikasi hasil (test command, typecheck, dll) + +### Prompt Convention + +``` +Task: [deskripsi singkat] +File: [path file yang diubah] +Pattern: [referensi pattern yang sudah ada] +Scope: [batasan perubahan] +Verify: [command untuk verifikasi] +``` + +### Review Process + +- Setiap perubahan wajib di-review +- Ceklist review: type error?, logic error?, edge case terhandle?, test passing? +- Jangan deploy tanpa review + +### AI Agent Boundaries + +- Boleh: refactor, rename, extract function, add types, write tests +- Tidak boleh: ganti arsitektur tanpa approval, hapus folder existing, ubah konfigurasi inti + +--- + +## Folder Ownership + +| Folder | Pemilik | Fungsi | +| --------------------------- | -------------------- | ----------------------------------------- | +| `src/app/` | Routing & Layout | Entry point, global layout, providers | +| `src/components/ui/` | UI Kit | Atomic components (button, input, badge) | +| `src/components/layouts/` | Layout | Header, footer, sidebar, container | +| `src/components/widgets/` | Widgets | Composite components (card, modal, table) | +| `src/components/providers/` | Providers | Theme, query, auth wrappers | +| `src/config/` | Configuration | env, site config, paths | +| `src/constants/` | Constants | Global constants | +| `src/features/` | Feature Module | Self-contained feature module | +| `src/lib/` | Integrations | API client, third-party wrappers | +| `src/hooks/` | Shared Hooks | Generic reusable hooks | +| `src/utils/` | Utilities | Pure functions, helpers | +| `src/types/` | Global Types | Shared type definitions | +| `src/data/` | Static Data | Constants, enums, mock data | +| `src/testing/` | Test Setup | Test utilities, mocks, setup | +| `src/styles/` | Styles | Global CSS, CSS variables | +| `src/i18n/` | Internationalization | i18n config, routing, request | +| `.docs/` | Engineering Docs | Architecture, decisions, guides | + +### `.docs/` — Engineering Documentation Center + +Pusat dokumentasi internal engineering dengan struktur sebagai berikut: + +| Sub-folder | Isi | +| --------------- | -------------------------------------------------------- | +| `product/` | Visi produk, roadmap, user stories, feature specs | +| `design/` | Design system, UI/UX guidelines, component patterns | +| `technical/` | Tech stack, dependencies, konfigurasi, development setup | +| `engineering/` | Engineering practices, code review, coding standards | +| `architecture/` | System architecture, data flow, module boundaries | +| `adr/` | Architecture Decision Records — keputusan arsitektur | +| `api/` | API documentation, endpoint specs, request/response | +| `testing/` | Testing strategy, test types, coverage goals | +| `deployment/` | Deployment pipeline, environments, infrastructure | +| `security/` | Security policies, auth, authorization, data protection | +| `ai/` | AI agent collaboration guide, prompt conventions | +| `onboarding/` | New developer onboarding, setup guide | +| `glossary/` | Project terminology, abbreviations, definitions | + +Setiap sub-folder memiliki `README.md` dengan template dan panduan pengisian. Dokumentasi bersifat **living document** — update seiring perkembangan project. + +## Features Structure + +### Feature Structure + +Each feature should be self-contained: + +``` +src/features/awesome-feature/ +├── api/ # API calls and hooks for this feature +├── components/ # Feature-specific components +├── hooks/ # Feature-specific hooks +├── data/ # Feature-specific data +├── shared/ # Feature-specific shared components/hooks/utils +├── stores/ # Feature-specific state +├── types/ # Feature-specific types +└── utils/ # Feature-specific utilities +``` + +--- + +## Documentation Rules + +1. **Semua dokumentasi WAJIB Bahasa Indonesia** +2. **AGENTS.md** di setiap folder penting — menjelaskan fungsi folder, aturan, boundary +3. **`.docs/`** untuk engineering documentation — architecture decision records, design docs, technical guides +4. **README.md** untuk onboarding — cara setup, develop, build, deploy +5. **CHANGELOG.md** untuk catatan perubahan — format Keep a Changelog +6. Jangan gunakan emoji di dokumentasi teknis +7. Tone: professional, clean, practical, engineering-focused + +--- + +## Development Workflow + +### Daily Development + +```bash +# Setup +bun install # Install dependencies +bun dev # Start development server + +# Quality Gates +bun check-types # TypeScript type checking +bun lint # ESLint +bun format # Prettier +bun test # Vitest (unit + integration) + +# Build +bun build # Production build +``` + +### Git Hooks (Husky) + +- **Pre-commit**: lint-staged (ESLint + Prettier) +- **Pre-push**: typecheck + test + +Semua harus lolos sebelum push. + +### Branch Strategy + +- `main` — production-ready +- `feat/*` — fitur baru +- `fix/*` — bug fix +- `refactor/*` — refactoring +- `docs/*` — dokumentasi + +--- + +## Commit Convention + +Gunakan **Conventional Commits**: + +``` +<type>(<scope>): <description> + +[optional body] +[optional footer] +``` + +### Type + +- `feat`: Fitur baru +- `fix`: Bug fix +- `refactor`: Refactoring (bukan fitur/fix) +- `docs`: Dokumentasi +- `chore`: Build, config, tooling +- `style`: Formatting (bukan logic change) +- `test`: Test (tambah/ubah) +- `perf`: Performance improvement + +### Scope + +- `ui`: component, styling +- `api`: api integration +- `hooks`: custom hooks +- `store`: state management +- `config`: configuration +- `i18n`: internationalization +- `types`: type definitions + +### Contoh + +```bash +feat(auth): add login with email/password +fix(ui): resolve button overflow on mobile +refactor(hooks): extract use-debounce to shared hooks +docs(api): update endpoint documentation +``` + +--- + +## Quality Standards + +### Testing + +- **Unit test**: Setiap utility function, hook, dan komponen murni +- **Integration test**: Alur utama aplikasi (login, CRUD, navigasi) +- **E2E test**: Critical path dengan Playwright +- Coverage minimal: 70% (unit + integration) + +### Accessibility + +- Setiap komponen HTML semantic (button, nav, main, section) +- Keyboard navigasi berfungsi +- Focus trap untuk modal/dialog +- Screen reader friendly (aria-label, role) + +### Performance + +- Gambar pakai `next/image` +- Dynamic import untuk komponen berat +- React.memo untuk list items +- Debounce/throttle untuk event berat (scroll, resize, search) + +### Error Handling + +- Setiap API call punya error boundary +- User-facing error messages harus informatif +- Global error boundary di layout +- Sentry atau error tracking (opsional) + +--- + +## Expansion Strategy + +### Cara Nambah Feature Baru + +1. Buat folder di `src/features/<nama-feature>/` +2. Struktur: `api/`, `components/`, `hooks/`, `stores/`, `types/`, `utils/` +3. Tambah route di `src/app/` (App Router) +4. Export dari `index.ts` +5. Test feature secara independen + +### Cara Nambah Halaman + +1. Buat folder di `src/app/<route>/` +2. Buat `page.tsx`, `layout.tsx` jika perlu +3. Gunakan komponen dari feature modules + +### Cara Nambah Komponen UI + +1. Komponen atomik → `src/components/ui/` +2. Komponen komposit → `src/components/widgets/` +3. Komponen spesifik feature → `src/features/<feature>/components/` + +### Cara Nambah API Route + +1. Buat file di `src/app/api/<route>/route.ts` +2. Export GET, POST, PUT, PATCH, DELETE handler +3. Validasi input dengan Zod + +--- + +## Refactor Rules + +### Kapan Refactor Dilakukan + +- Duplikasi kode > 3 kali +- Function terlalu panjang (> 50 baris) +- Component terlalu banyak responsibility +- Import graph mulai complex +- Test sulit ditulis karena coupling + +### Batasan Refactor + +- Jangan ubah API interface yang sudah dipakai +- Jangan ubah struktur folder tanpa approval +- Jangan hapus file existing — deprecate dulu +- Satu refactor = satu PR + +### Dokumentasi Perubahan + +- Update AGENTS.md yang terdampak +- Update CHANGELOG.md +- Beri komentar di kode jika ada breaking change +- Pastikan semua test tetap passing + +--- + +## Scalability Principles + +1. **Module independence**: Setiap feature bisa di-develop, di-test, di-deploy secara independen +2. **Lazy by default**: Import berat pakai `next/dynamic`, query besar pakai pagination +3. **Caching strategy**: React Query untuk server state, Zustand persist untuk client state +4. **API abstraction**: Semua external API lewat `src/lib/`, bukan langsung di komponen +5. **Type safety**: Zod untuk runtime validation, TypeScript untuk compile-time safety +6. **Monitoring**: Error boundary di setiap layer, logging terstruktur + +--- + +_Handbook ini hidup — update jika ada keputusan arsitektur baru._ diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..9e0e2bb --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,6 @@ +# Changelog + +All notable changes to this project will be documented in this file. + +The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), +and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 0000000..43c994c --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1 @@ +@AGENTS.md diff --git a/LICENSE b/LICENSE index 57d5413..fa4a2ef 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ MIT License -Copyright (c) 2026 Fajar Fathurrahman +Copyright (c) 2026 Fajar Fathurrahman Ramdhani Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/README.md b/README.md new file mode 100644 index 0000000..abc078d --- /dev/null +++ b/README.md @@ -0,0 +1,117 @@ +# Next App + +Foundation project Next.js 16 dengan arsitektur Feature-Sliced Design (FSD), +TypeScript strict, Tailwind CSS v4, dan tooling modern. + +## Tech Stack + +| Teknologi | Fungsi | +| ---------------------------- | ----------------------------- | +| **Next.js 16** (App Router) | Routing, SSR, RSC, API Routes | +| **TypeScript** (Strict) | Type safety | +| **Tailwind CSS v4** | Styling utility-first | +| **Shadcn UI / Radix** | Component primitives | +| **TanStack React Query** | Server state management | +| **Zustand** | Client state management | +| **Zod** | Runtime validation | +| **next-intl** | Internasionalisasi | +| **Motion** | Animasi | +| **Vitest + Testing Library** | Unit / integration test | +| **Playwright** | E2E test | + +## Struktur Folder + +``` +src/ +├── app/ # Routing & layout (App Router) +├── components/ # Shared components (ui/, layouts/, widgets/, providers/) +├── config/ # App configuration +├── constants/ # Global constants +├── data/ # Static data & mock +├── features/ # Feature modules (FSD) +├── hooks/ # Shared hooks +├── i18n/ # Internationalization +├── lib/ # Third-party integrations +├── styles/ # Global styles +├── testing/ # Test utilities & setup +├── types/ # Global type definitions +└── utils/ # Pure utility functions +``` + +## Prasyarat + +- [Bun](https://bun.sh) >= 1.2 (atau Node.js >= 20) +- Git + +## Setup Development + +```bash +# 1. Clone repository +git clone https://github.com/fjrfthrrhmn/next-app.git +cd next-app + +# 2. Install dependencies +bun install + +# 3. Copy environment variables +cp .env.example .env +# Edit .env sesuai kebutuhan + +# 4. Jalankan development server +bun dev +``` + +Buka [http://localhost:3000](http://localhost:3000) di browser. + +## Scripts + +| Perintah | Deskripsi | +| ----------------- | ------------------------ | +| `bun dev` | Start development server | +| `bun build` | Production build | +| `bun start` | Start production server | +| `bun lint` | ESLint check | +| `bun format` | Prettier check | +| `bun format:fix` | Prettier format | +| `bun check-types` | TypeScript type checking | +| `bun test` | Vitest (watch mode) | +| `bun test:run` | Vitest (single run) | + +## Quality Gates + +Sebelum push, pastikan semua lolos: + +```bash +bun check-types # Type checking +bun lint # ESLint +bun format # Prettier +bun test:run # Unit & integration tests +bun build # Production build +``` + +Git hooks (Husky) akan menjalankan lint-staged otomatis di pre-commit +dan typecheck + test di pre-push. + +## Dokumentasi + +Seluruh dokumentasi engineering tersedia di `.docs/`: + +| Sub-folder | Isi | +| --------------- | ------------------------------------ | +| `architecture/` | System architecture, data flow | +| `technical/` | Tech stack, dependencies, setup | +| `engineering/` | Coding standards, code review | +| `adr/` | Architecture Decision Records | +| `api/` | API documentation | +| `testing/` | Testing strategy & coverage goals | +| `deployment/` | Deployment pipeline & environments | +| `security/` | Auth, authorization, data protection | +| `ai/` | AI collaboration guide | +| `product/` | Product vision & roadmap | +| `design/` | Design system & UI/UX guidelines | +| `glossary/` | Project terminology | +| `onboarding/` | Developer onboarding guide | + +## Lisensi + +Distributed under the MIT License. Lihat `LICENSE` untuk informasi lebih lanjut. diff --git a/ROADMAP.md b/ROADMAP.md new file mode 100644 index 0000000..f3f1315 --- /dev/null +++ b/ROADMAP.md @@ -0,0 +1,106 @@ +# ROADMAP + +> Roadmap ini adalah dokumen hidup yang merefleksikan arah pengembangan proyek. +> Prioritas dapat berubah seiring kebutuhan. Semua timeline bersifat indikatif. +> +> **Product direction: Contribution Hub** -- Developer Activity Assistant +> Platform personal untuk membantu developer menjaga konsistensi aktivitas GitHub secara meaningful. +> Lihat dokumentasi produk di `.docs/product/` untuk detail lengkap. + +## Fase 0: Foundation (Selesai) + +- [x] Inisialisasi proyek Next.js 16 + TypeScript strict +- [x] Struktur folder Feature-Sliced Design (FSD) +- [x] Konfigurasi ESLint, Prettier, Husky, commitlint +- [x] Integrasi Tailwind CSS v4 + Shadcn UI +- [x] Setup TanStack React Query + Zustand + Zod +- [x] Setup next-intl untuk internasionalisasi +- [x] Setup Vitest + Testing Library + Playwright +- [x] Dokumentasi engineering (.docs/) +- [x] AGENTS.md -- AI collaboration guide +- [x] Lisensi (MIT), Changelog, Roadmap +- [x] CI workflow (GitHub Actions) + +## Fase 1: MVP (Q3 2026) + +### Product Definition + +- [x] Product vision dan definition documents +- [x] Target user personas +- [ ] Product design system dan UI/UX guidelines +- [ ] Feature specifications detail + +### Core Features + +- [ ] **GitHub Activity Tracker** + - Integrasi GitHub API untuk membaca aktivitas commit, PR, issue + - Personal activity log harian + - Kategorisasi aktivitas (coding, review, documentation, learning) + +- [ ] **Personal Dashboard** + - Overview aktivitas hari ini + - Weekly activity chart + - Streak tracker dengan meaningful milestones + - Recent activity feed + +- [ ] **Habit System** + - Daily goals (minimal commit, PR, atau aktivitas) + - Streak tracking dengan multiple streak types + - Weekly progress report + - Motivational insights + +- [ ] **Auto-Dokumentasi** + - Generate daily activity summary dari commit messages + - Weekly progress report yang bisa di-export + - Auto-generate changelog dari aktivitas + +### Technical Foundation + +- [ ] Better Auth integration (GitHub OAuth) +- [ ] Database setup untuk menyimpan aktivitas +- [ ] API endpoints untuk CRUD aktivitas +- [ ] Background job untuk sync GitHub activity +- [ ] Landing page dan onboarding flow + +## Fase 2: Growth (Q4 2026) + +### Feature Enhancements + +- [ ] Smart Issue/PR Suggestions +- [ ] Advanced Analytics (productive hours, language breakdown) +- [ ] Multiple Repository Management +- [ ] GitLab / Bitbucket integration + +### Community + +- [ ] Public profile page (opsional -- shareable) +- [ ] Developer badges dan milestone achievements +- [ ] Weekly developer challenge (opsional) + +## Fase 3: Maturity (Q1 2027) + +- [ ] AI-Powered Insight (natural language summary, pattern recognition) +- [ ] Team Plan (team dashboard, member tracking) +- [ ] Developer Portfolio Generator +- [ ] VS Code extension +- [ ] Mobile app (React Native) + +## Vision + +Menjadi platform developer activity assistant yang membantu setiap developer di Indonesia dan dunia untuk: + +- Menjaga konsistensi coding dengan habit system yang meaningful +- Mendokumentasikan progress pengembangan secara otomatis +- Meningkatkan personal branding melalui GitHub profile yang aktif +- Membangun kebiasaan engineering yang sehat dan sustainable + +Dengan fondasi: + +- Arsitektur yang maintainable dan scalable (FSD) +- Kualitas kode yang teruji (type-safe, tested, documented) +- Kolaborasi AI-first dengan dokumentasi yang lengkap +- Fokus pada meaningful contribution, bukan spam atau aktivitas artifisial + +--- + +> Detail roadmap dan product documentation tersedia di `.docs/product/roadmap.md` diff --git a/bun.lock b/bun.lock new file mode 100644 index 0000000..3fb0897 --- /dev/null +++ b/bun.lock @@ -0,0 +1,1567 @@ +{ + "lockfileVersion": 1, + "configVersion": 1, + "workspaces": { + "": { + "name": "next-app", + "dependencies": { + "@hookform/resolvers": "^5.2.2", + "@t3-oss/env-nextjs": "^0.13.11", + "@tanstack/react-query": "^5.100.10", + "@vercel/speed-insights": "^2.0.0", + "class-variance-authority": "^0.7.1", + "clsx": "^2.1.1", + "cmdk": "^1.1.1", + "date-fns": "^4.1.0", + "hotkeys-js": "^4.0.4", + "lightningcss": "^1.32.0", + "lucide-react": "^1.16.0", + "motion": "^12.38.0", + "next": "16.2.6", + "next-intl": "^4.12.0", + "next-themes": "^0.4.6", + "radix-ui": "^1.4.3", + "react": "19.2.4", + "react-dom": "19.2.4", + "react-hook-form": "^7.76.0", + "sonner": "^2.0.7", + "tailwind-merge": "^3.6.0", + "usehooks-ts": "^3.1.1", + "zod": "^4.4.3", + "zustand": "^5.0.13", + }, + "devDependencies": { + "@commitlint/cli": "^21.0.1", + "@commitlint/config-conventional": "^21.0.1", + "@ianvs/prettier-plugin-sort-imports": "^4.7.1", + "@rolldown/binding-win32-x64-msvc": "^1.0.1", + "@tailwindcss/postcss": "^4", + "@tanstack/react-query-devtools": "^5.100.10", + "@types/node": "^20", + "@types/react": "^19", + "@types/react-dom": "^19", + "@vitejs/plugin-react": "^6.0.2", + "eslint": "^9", + "eslint-config-next": "16.2.6", + "husky": "^9.1.7", + "lint-staged": "^17.0.4", + "prettier": "^3.8.3", + "prettier-plugin-tailwindcss": "^0.8.0", + "tailwindcss": "^4", + "typescript": "^5", + "vitest": "^4.1.6", + }, + }, + }, + "packages": { + "@alloc/quick-lru": ["@alloc/quick-lru@5.2.0", "", {}, "sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw=="], + + "@babel/code-frame": ["@babel/code-frame@7.29.0", "", { "dependencies": { "@babel/helper-validator-identifier": "^7.28.5", "js-tokens": "^4.0.0", "picocolors": "^1.1.1" } }, "sha512-9NhCeYjq9+3uxgdtp20LSiJXJvN0FeCtNGpJxuMFZ1Kv3cWUNb6DOhJwUvcVCzKGR66cw4njwM6hrJLqgOwbcw=="], + + "@babel/compat-data": ["@babel/compat-data@7.29.3", "", {}, "sha512-LIVqM46zQWZhj17qA8wb4nW/ixr2y1Nw+r1etiAWgRM6U1IqP+LNhL1yg440jYZR72jCWcWbLWzIosH+uP1fqg=="], + + "@babel/core": ["@babel/core@7.29.0", "", { "dependencies": { "@babel/code-frame": "^7.29.0", "@babel/generator": "^7.29.0", "@babel/helper-compilation-targets": "^7.28.6", "@babel/helper-module-transforms": "^7.28.6", "@babel/helpers": "^7.28.6", "@babel/parser": "^7.29.0", "@babel/template": "^7.28.6", "@babel/traverse": "^7.29.0", "@babel/types": "^7.29.0", "@jridgewell/remapping": "^2.3.5", "convert-source-map": "^2.0.0", "debug": "^4.1.0", "gensync": "^1.0.0-beta.2", "json5": "^2.2.3", "semver": "^6.3.1" } }, "sha512-CGOfOJqWjg2qW/Mb6zNsDm+u5vFQ8DxXfbM09z69p5Z6+mE1ikP2jUXw+j42Pf1XTYED2Rni5f95npYeuwMDQA=="], + + "@babel/generator": ["@babel/generator@7.29.1", "", { "dependencies": { "@babel/parser": "^7.29.0", "@babel/types": "^7.29.0", "@jridgewell/gen-mapping": "^0.3.12", "@jridgewell/trace-mapping": "^0.3.28", "jsesc": "^3.0.2" } }, "sha512-qsaF+9Qcm2Qv8SRIMMscAvG4O3lJ0F1GuMo5HR/Bp02LopNgnZBC/EkbevHFeGs4ls/oPz9v+Bsmzbkbe+0dUw=="], + + "@babel/helper-compilation-targets": ["@babel/helper-compilation-targets@7.28.6", "", { "dependencies": { "@babel/compat-data": "^7.28.6", "@babel/helper-validator-option": "^7.27.1", "browserslist": "^4.24.0", "lru-cache": "^5.1.1", "semver": "^6.3.1" } }, "sha512-JYtls3hqi15fcx5GaSNL7SCTJ2MNmjrkHXg4FSpOA/grxK8KwyZ5bubHsCq8FXCkua6xhuaaBit+3b7+VZRfcA=="], + + "@babel/helper-globals": ["@babel/helper-globals@7.28.0", "", {}, "sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw=="], + + "@babel/helper-module-imports": ["@babel/helper-module-imports@7.28.6", "", { "dependencies": { "@babel/traverse": "^7.28.6", "@babel/types": "^7.28.6" } }, "sha512-l5XkZK7r7wa9LucGw9LwZyyCUscb4x37JWTPz7swwFE/0FMQAGpiWUZn8u9DzkSBWEcK25jmvubfpw2dnAMdbw=="], + + "@babel/helper-module-transforms": ["@babel/helper-module-transforms@7.28.6", "", { "dependencies": { "@babel/helper-module-imports": "^7.28.6", "@babel/helper-validator-identifier": "^7.28.5", "@babel/traverse": "^7.28.6" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "sha512-67oXFAYr2cDLDVGLXTEABjdBJZ6drElUSI7WKp70NrpyISso3plG9SAGEF6y7zbha/wOzUByWWTJvEDVNIUGcA=="], + + "@babel/helper-string-parser": ["@babel/helper-string-parser@7.27.1", "", {}, "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA=="], + + "@babel/helper-validator-identifier": ["@babel/helper-validator-identifier@7.28.5", "", {}, "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q=="], + + "@babel/helper-validator-option": ["@babel/helper-validator-option@7.27.1", "", {}, "sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg=="], + + "@babel/helpers": ["@babel/helpers@7.29.2", "", { "dependencies": { "@babel/template": "^7.28.6", "@babel/types": "^7.29.0" } }, "sha512-HoGuUs4sCZNezVEKdVcwqmZN8GoHirLUcLaYVNBK2J0DadGtdcqgr3BCbvH8+XUo4NGjNl3VOtSjEKNzqfFgKw=="], + + "@babel/parser": ["@babel/parser@7.29.3", "", { "dependencies": { "@babel/types": "^7.29.0" }, "bin": "./bin/babel-parser.js" }, "sha512-b3ctpQwp+PROvU/cttc4OYl4MzfJUWy6FZg+PMXfzmt/+39iHVF0sDfqay8TQM3JA2EUOyKcFZt75jWriQijsA=="], + + "@babel/template": ["@babel/template@7.28.6", "", { "dependencies": { "@babel/code-frame": "^7.28.6", "@babel/parser": "^7.28.6", "@babel/types": "^7.28.6" } }, "sha512-YA6Ma2KsCdGb+WC6UpBVFJGXL58MDA6oyONbjyF/+5sBgxY/dwkhLogbMT2GXXyU84/IhRw/2D1Os1B/giz+BQ=="], + + "@babel/traverse": ["@babel/traverse@7.29.0", "", { "dependencies": { "@babel/code-frame": "^7.29.0", "@babel/generator": "^7.29.0", "@babel/helper-globals": "^7.28.0", "@babel/parser": "^7.29.0", "@babel/template": "^7.28.6", "@babel/types": "^7.29.0", "debug": "^4.3.1" } }, "sha512-4HPiQr0X7+waHfyXPZpWPfWL/J7dcN1mx9gL6WdQVMbPnF3+ZhSMs8tCxN7oHddJE9fhNE7+lxdnlyemKfJRuA=="], + + "@babel/types": ["@babel/types@7.29.0", "", { "dependencies": { "@babel/helper-string-parser": "^7.27.1", "@babel/helper-validator-identifier": "^7.28.5" } }, "sha512-LwdZHpScM4Qz8Xw2iKSzS+cfglZzJGvofQICy7W7v4caru4EaAmyUuO6BGrbyQ2mYV11W0U8j5mBhd14dd3B0A=="], + + "@commitlint/cli": ["@commitlint/cli@21.0.1", "", { "dependencies": { "@commitlint/format": "^21.0.1", "@commitlint/lint": "^21.0.1", "@commitlint/load": "^21.0.1", "@commitlint/read": "^21.0.1", "@commitlint/types": "^21.0.1", "tinyexec": "^1.0.0", "yargs": "^18.0.0" }, "bin": { "commitlint": "cli.js" } }, "sha512-8vq10krmbJwBkvzXKhbs4o4JQEVscd3pqOlWuDUaDBwbeL694/P33UC29tZQFTAgPU9fVJ2+f2m3zw16yKWxHg=="], + + "@commitlint/config-conventional": ["@commitlint/config-conventional@21.0.1", "", { "dependencies": { "@commitlint/types": "^21.0.1", "conventional-changelog-conventionalcommits": "^9.2.0" } }, "sha512-gRorrkfWOh/+V5X8GYWWbQvrzPczopGMS4CCNrQdHkK4xWElv82BDvIsDhJZWTlI7TazOlYea6VATufCsFs+sw=="], + + "@commitlint/config-validator": ["@commitlint/config-validator@21.0.1", "", { "dependencies": { "@commitlint/types": "^21.0.1", "ajv": "^8.11.0" } }, "sha512-Zd2UFdndeMMaW2O96HK0tdfT4gOImUvidMpAd/pws2zZ4m1nrAZ/9b/v2JYuE8fs86GpXv9F7LNaIuCIWhY+pA=="], + + "@commitlint/ensure": ["@commitlint/ensure@21.0.1", "", { "dependencies": { "@commitlint/types": "^21.0.1", "es-toolkit": "^1.46.0" } }, "sha512-jJ1037967wU7YN/xkv+iRlOBlmaOXPhPO5KQSqya6GyXzBlwuLzELBFao16DVg9dZyqmNrhewzwZ3SAibetHBQ=="], + + "@commitlint/execute-rule": ["@commitlint/execute-rule@21.0.1", "", {}, "sha512-RifH+FmImozKBE6mozhF4K3r2RRKP7SMi/Q/zLCmExtp5e05lhHOUYqGBlFBAGNHaZxU/WYw1XuugYK9jQzqnA=="], + + "@commitlint/format": ["@commitlint/format@21.0.1", "", { "dependencies": { "@commitlint/types": "^21.0.1", "picocolors": "^1.1.1" } }, "sha512-ksmG2+cHGtuDPQQbhBbC4unwm444+6TiPw0d1bKf67hntgZqZ8E0g1MuYKUuyT5IH4IMmXZhKq22/Z3jBvtQIw=="], + + "@commitlint/is-ignored": ["@commitlint/is-ignored@21.0.1", "", { "dependencies": { "@commitlint/types": "^21.0.1", "semver": "^7.6.0" } }, "sha512-iNDP8SFdw8JEkM0CHZ2XFnhTN4Zg5jKUY2d8kBOSFrI2aA+3YJI7fcqVpfgbpJ9xtxFVYpi+DBATU5AvhoTq8g=="], + + "@commitlint/lint": ["@commitlint/lint@21.0.1", "", { "dependencies": { "@commitlint/is-ignored": "^21.0.1", "@commitlint/parse": "^21.0.1", "@commitlint/rules": "^21.0.1", "@commitlint/types": "^21.0.1" } }, "sha512-gF+iYtUw1gBG3HUH9z3VxwUjGg2R2G5j+nmvPs8aIeYkiB7TtneBu3wO85I0bUl93bYNsvsCNI9Nte2fmDUMww=="], + + "@commitlint/load": ["@commitlint/load@21.0.1", "", { "dependencies": { "@commitlint/config-validator": "^21.0.1", "@commitlint/execute-rule": "^21.0.1", "@commitlint/resolve-extends": "^21.0.1", "@commitlint/types": "^21.0.1", "cosmiconfig": "^9.0.1", "cosmiconfig-typescript-loader": "^6.1.0", "es-toolkit": "^1.46.0", "is-plain-obj": "^4.1.0", "picocolors": "^1.1.1" } }, "sha512-Btg1q1mKmiihN4W3x0EsPDrJMOQfMa9NIqlzlJyXAfxvsOGdGXOW5p3R3RcSxDCaY7JabY9flIl+Om1af3PSrw=="], + + "@commitlint/message": ["@commitlint/message@21.0.1", "", {}, "sha512-R3dVQeJQ0B6yqrZEjkUHD4r7UJYLV9Lvk2xs3PTOmtWk2G3mI6Xgc+YdRxL1PwcDfBiUjv2SkIkW4AUc976w1w=="], + + "@commitlint/parse": ["@commitlint/parse@21.0.1", "", { "dependencies": { "@commitlint/types": "^21.0.1", "conventional-changelog-angular": "^8.2.0", "conventional-commits-parser": "^6.3.0" } }, "sha512-oh/nCSOqdoeQNA1tO8aAmxkq5EBo8/NzcFQRvv66AWc9HpED28sL2iSicCKU6hPintWuscL6BJEWi77Wq1LPMQ=="], + + "@commitlint/read": ["@commitlint/read@21.0.1", "", { "dependencies": { "@commitlint/top-level": "^21.0.1", "@commitlint/types": "^21.0.1", "git-raw-commits": "^5.0.0", "tinyexec": "^1.0.0" } }, "sha512-pMEu4lbpC8W0ZgKJj2U6WaobXIZWdFlULpIEewYhkPXx+WZcnoO53YrVPc7QErQuNolq2Me8dP58Wu7YAVXVOA=="], + + "@commitlint/resolve-extends": ["@commitlint/resolve-extends@21.0.1", "", { "dependencies": { "@commitlint/config-validator": "^21.0.1", "@commitlint/types": "^21.0.1", "es-toolkit": "^1.46.0", "global-directory": "^5.0.0", "resolve-from": "^5.0.0" } }, "sha512-0DhjYWL6uYrY16Efa032fYk3woGJDU4AGWiG1XXltT9AMUNYKyb5cIZU2ivbaMZ3+kKFqUjikD2cjh66Sbh/Sg=="], + + "@commitlint/rules": ["@commitlint/rules@21.0.1", "", { "dependencies": { "@commitlint/ensure": "^21.0.1", "@commitlint/message": "^21.0.1", "@commitlint/to-lines": "^21.0.1", "@commitlint/types": "^21.0.1" } }, "sha512-VMooYpz4nJg7xlaUso6CCOWEz8D/ChkvsvZUMARcoJ1ZpfKPyFCGrHNha2tbsETNAb6ErgiRuCr2DvghrvPDYQ=="], + + "@commitlint/to-lines": ["@commitlint/to-lines@21.0.1", "", {}, "sha512-bd1BFII7p1EQZre9Kaj+kKaMFP3cFCdt21K7DItVux9XP5WjLgJ0/Uy1pJJh9aPwVJ6SKg62PxqlZaHI8hQAXw=="], + + "@commitlint/top-level": ["@commitlint/top-level@21.0.1", "", { "dependencies": { "escalade": "^3.2.0" } }, "sha512-4esUYqzY7K0FCgcJ/1xWEZekV7Ch4yZT1+xjEb7KzqbJ05XEkxHVsTfC8ADKNNtlCE2pj98KEbPGZWw9WwEnVw=="], + + "@commitlint/types": ["@commitlint/types@21.0.1", "", { "dependencies": { "conventional-commits-parser": "^6.3.0", "picocolors": "^1.1.1" } }, "sha512-4u7w8jcoCUFWhjWnASYzZHAP34OqOtuFBN87nQmFvqda03YU0T6z+yB4w0gSAMpekiRqqGk5rt+qSlW+a2vSEg=="], + + "@conventional-changelog/git-client": ["@conventional-changelog/git-client@2.7.0", "", { "dependencies": { "@simple-libs/child-process-utils": "^1.0.0", "@simple-libs/stream-utils": "^1.2.0", "semver": "^7.5.2" }, "peerDependencies": { "conventional-commits-filter": "^5.0.0", "conventional-commits-parser": "^6.4.0" }, "optionalPeers": ["conventional-commits-filter", "conventional-commits-parser"] }, "sha512-j7A8/LBEQ+3rugMzPXoKYzyUPpw/0CBQCyvtTR7Lmu4olG4yRC/Tfkq79Mr3yuPs0SUitlO2HwGP3gitMJnRFw=="], + + "@emnapi/core": ["@emnapi/core@1.10.0", "", { "dependencies": { "@emnapi/wasi-threads": "1.2.1", "tslib": "^2.4.0" } }, "sha512-yq6OkJ4p82CAfPl0u9mQebQHKPJkY7WrIuk205cTYnYe+k2Z8YBh11FrbRG/H6ihirqcacOgl2BIO8oyMQLeXw=="], + + "@emnapi/runtime": ["@emnapi/runtime@1.10.0", "", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-ewvYlk86xUoGI0zQRNq/mC+16R1QeDlKQy21Ki3oSYXNgLb45GV1P6A0M+/s6nyCuNDqe5VpaY84BzXGwVbwFA=="], + + "@emnapi/wasi-threads": ["@emnapi/wasi-threads@1.2.1", "", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-uTII7OYF+/Mes/MrcIOYp5yOtSMLBWSIoLPpcgwipoiKbli6k322tcoFsxoIIxPDqW01SQGAgko4EzZi2BNv2w=="], + + "@eslint-community/eslint-utils": ["@eslint-community/eslint-utils@4.9.1", "", { "dependencies": { "eslint-visitor-keys": "^3.4.3" }, "peerDependencies": { "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" } }, "sha512-phrYmNiYppR7znFEdqgfWHXR6NCkZEK7hwWDHZUjit/2/U0r6XvkDl0SYnoM51Hq7FhCGdLDT6zxCCOY1hexsQ=="], + + "@eslint-community/regexpp": ["@eslint-community/regexpp@4.12.2", "", {}, "sha512-EriSTlt5OC9/7SXkRSCAhfSxxoSUgBm33OH+IkwbdpgoqsSsUg7y3uh+IICI/Qg4BBWr3U2i39RpmycbxMq4ew=="], + + "@eslint/config-array": ["@eslint/config-array@0.21.2", "", { "dependencies": { "@eslint/object-schema": "^2.1.7", "debug": "^4.3.1", "minimatch": "^3.1.5" } }, "sha512-nJl2KGTlrf9GjLimgIru+V/mzgSK0ABCDQRvxw5BjURL7WfH5uoWmizbH7QB6MmnMBd8cIC9uceWnezL1VZWWw=="], + + "@eslint/config-helpers": ["@eslint/config-helpers@0.4.2", "", { "dependencies": { "@eslint/core": "^0.17.0" } }, "sha512-gBrxN88gOIf3R7ja5K9slwNayVcZgK6SOUORm2uBzTeIEfeVaIhOpCtTox3P6R7o2jLFwLFTLnC7kU/RGcYEgw=="], + + "@eslint/core": ["@eslint/core@0.17.0", "", { "dependencies": { "@types/json-schema": "^7.0.15" } }, "sha512-yL/sLrpmtDaFEiUj1osRP4TI2MDz1AddJL+jZ7KSqvBuliN4xqYY54IfdN8qD8Toa6g1iloph1fxQNkjOxrrpQ=="], + + "@eslint/eslintrc": ["@eslint/eslintrc@3.3.5", "", { "dependencies": { "ajv": "^6.14.0", "debug": "^4.3.2", "espree": "^10.0.1", "globals": "^14.0.0", "ignore": "^5.2.0", "import-fresh": "^3.2.1", "js-yaml": "^4.1.1", "minimatch": "^3.1.5", "strip-json-comments": "^3.1.1" } }, "sha512-4IlJx0X0qftVsN5E+/vGujTRIFtwuLbNsVUe7TO6zYPDR1O6nFwvwhIKEKSrl6dZchmYBITazxKoUYOjdtjlRg=="], + + "@eslint/js": ["@eslint/js@9.39.4", "", {}, "sha512-nE7DEIchvtiFTwBw4Lfbu59PG+kCofhjsKaCWzxTpt4lfRjRMqG6uMBzKXuEcyXhOHoUp9riAm7/aWYGhXZ9cw=="], + + "@eslint/object-schema": ["@eslint/object-schema@2.1.7", "", {}, "sha512-VtAOaymWVfZcmZbp6E2mympDIHvyjXs/12LqWYjVw6qjrfF+VK+fyG33kChz3nnK+SU5/NeHOqrTEHS8sXO3OA=="], + + "@eslint/plugin-kit": ["@eslint/plugin-kit@0.4.1", "", { "dependencies": { "@eslint/core": "^0.17.0", "levn": "^0.4.1" } }, "sha512-43/qtrDUokr7LJqoF2c3+RInu/t4zfrpYdoSDfYyhg52rwLV6TnOvdG4fXm7IkSB3wErkcmJS9iEhjVtOSEjjA=="], + + "@floating-ui/core": ["@floating-ui/core@1.7.5", "", { "dependencies": { "@floating-ui/utils": "^0.2.11" } }, "sha512-1Ih4WTWyw0+lKyFMcBHGbb5U5FtuHJuujoyyr5zTaWS5EYMeT6Jb2AuDeftsCsEuchO+mM2ij5+q9crhydzLhQ=="], + + "@floating-ui/dom": ["@floating-ui/dom@1.7.6", "", { "dependencies": { "@floating-ui/core": "^1.7.5", "@floating-ui/utils": "^0.2.11" } }, "sha512-9gZSAI5XM36880PPMm//9dfiEngYoC6Am2izES1FF406YFsjvyBMmeJ2g4SAju3xWwtuynNRFL2s9hgxpLI5SQ=="], + + "@floating-ui/react-dom": ["@floating-ui/react-dom@2.1.8", "", { "dependencies": { "@floating-ui/dom": "^1.7.6" }, "peerDependencies": { "react": ">=16.8.0", "react-dom": ">=16.8.0" } }, "sha512-cC52bHwM/n/CxS87FH0yWdngEZrjdtLW/qVruo68qg+prK7ZQ4YGdut2GyDVpoGeAYe/h899rVeOVm6Oi40k2A=="], + + "@floating-ui/utils": ["@floating-ui/utils@0.2.11", "", {}, "sha512-RiB/yIh78pcIxl6lLMG0CgBXAZ2Y0eVHqMPYugu+9U0AeT6YBeiJpf7lbdJNIugFP5SIjwNRgo4DhR1Qxi26Gg=="], + + "@formatjs/fast-memoize": ["@formatjs/fast-memoize@3.1.5", "", {}, "sha512-KLi3fan6WnCHmigd9pmEEN8Hid0v4wiFBW576M/d07KMWYecf1CvyMI3n34vCmHT4AoVqG2n702kiHbXjzZX2A=="], + + "@formatjs/icu-messageformat-parser": ["@formatjs/icu-messageformat-parser@3.5.9", "", { "dependencies": { "@formatjs/icu-skeleton-parser": "2.1.9" } }, "sha512-PZm6O9JI/gUPtQV9r2eaMuLb4yWqV2vz+ot03ORHWTKO343LSpZi0TqeXLB2ZZGDXLCw2SbfgsQ0GxoxXMl79g=="], + + "@formatjs/icu-skeleton-parser": ["@formatjs/icu-skeleton-parser@2.1.9", "", {}, "sha512-rsxswgHMfU1zUgB2byc08fesf83wLGjFnzLCEtuf00mx2doiqc6pYrf67raI37XqdRcGUviQepk2UKGqpng74Q=="], + + "@formatjs/intl-localematcher": ["@formatjs/intl-localematcher@0.8.8", "", { "dependencies": { "@formatjs/fast-memoize": "3.1.5" } }, "sha512-pBr2hVKWvkHVnfXegW+53NT9U2uaVQCc+EgzLPCCwXqBA3nvM5fPbK9IcJlNjV+NMKGyZ2F3ZSG78iGdxAAqbA=="], + + "@hookform/resolvers": ["@hookform/resolvers@5.2.2", "", { "dependencies": { "@standard-schema/utils": "^0.3.0" }, "peerDependencies": { "react-hook-form": "^7.55.0" } }, "sha512-A/IxlMLShx3KjV/HeTcTfaMxdwy690+L/ZADoeaTltLx+CVuzkeVIPuybK3jrRfw7YZnmdKsVVHAlEPIAEUNlA=="], + + "@humanfs/core": ["@humanfs/core@0.19.2", "", { "dependencies": { "@humanfs/types": "^0.15.0" } }, "sha512-UhXNm+CFMWcbChXywFwkmhqjs3PRCmcSa/hfBgLIb7oQ5HNb1wS0icWsGtSAUNgefHeI+eBrA8I1fxmbHsGdvA=="], + + "@humanfs/node": ["@humanfs/node@0.16.8", "", { "dependencies": { "@humanfs/core": "^0.19.2", "@humanfs/types": "^0.15.0", "@humanwhocodes/retry": "^0.4.0" } }, "sha512-gE1eQNZ3R++kTzFUpdGlpmy8kDZD/MLyHqDwqjkVQI0JMdI1D51sy1H958PNXYkM2rAac7e5/CnIKZrHtPh3BQ=="], + + "@humanfs/types": ["@humanfs/types@0.15.0", "", {}, "sha512-ZZ1w0aoQkwuUuC7Yf+7sdeaNfqQiiLcSRbfI08oAxqLtpXQr9AIVX7Ay7HLDuiLYAaFPu8oBYNq/QIi9URHJ3Q=="], + + "@humanwhocodes/module-importer": ["@humanwhocodes/module-importer@1.0.1", "", {}, "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA=="], + + "@humanwhocodes/retry": ["@humanwhocodes/retry@0.4.3", "", {}, "sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ=="], + + "@ianvs/prettier-plugin-sort-imports": ["@ianvs/prettier-plugin-sort-imports@4.7.1", "", { "dependencies": { "@babel/generator": "^7.26.2", "@babel/parser": "^7.26.2", "@babel/traverse": "^7.25.9", "@babel/types": "^7.26.0", "semver": "^7.5.2" }, "peerDependencies": { "@prettier/plugin-oxc": "^0.0.4 || ^0.1.0", "@vue/compiler-sfc": "2.7.x || 3.x", "content-tag": "^4.0.0", "prettier": "2 || 3 || ^4.0.0-0", "prettier-plugin-ember-template-tag": "^2.1.0" }, "optionalPeers": ["@prettier/plugin-oxc", "@vue/compiler-sfc", "content-tag", "prettier-plugin-ember-template-tag"] }, "sha512-jmTNYGlg95tlsoG3JLCcuC4BrFELJtLirLAkQW/71lXSyOhVt/Xj7xWbbGcuVbNq1gwWgSyMrPjJc9Z30hynVw=="], + + "@img/colour": ["@img/colour@1.1.0", "", {}, "sha512-Td76q7j57o/tLVdgS746cYARfSyxk8iEfRxewL9h4OMzYhbW4TAcppl0mT4eyqXddh6L/jwoM75mo7ixa/pCeQ=="], + + "@img/sharp-darwin-arm64": ["@img/sharp-darwin-arm64@0.34.5", "", { "optionalDependencies": { "@img/sharp-libvips-darwin-arm64": "1.2.4" }, "os": "darwin", "cpu": "arm64" }, "sha512-imtQ3WMJXbMY4fxb/Ndp6HBTNVtWCUI0WdobyheGf5+ad6xX8VIDO8u2xE4qc/fr08CKG/7dDseFtn6M6g/r3w=="], + + "@img/sharp-darwin-x64": ["@img/sharp-darwin-x64@0.34.5", "", { "optionalDependencies": { "@img/sharp-libvips-darwin-x64": "1.2.4" }, "os": "darwin", "cpu": "x64" }, "sha512-YNEFAF/4KQ/PeW0N+r+aVVsoIY0/qxxikF2SWdp+NRkmMB7y9LBZAVqQ4yhGCm/H3H270OSykqmQMKLBhBJDEw=="], + + "@img/sharp-libvips-darwin-arm64": ["@img/sharp-libvips-darwin-arm64@1.2.4", "", { "os": "darwin", "cpu": "arm64" }, "sha512-zqjjo7RatFfFoP0MkQ51jfuFZBnVE2pRiaydKJ1G/rHZvnsrHAOcQALIi9sA5co5xenQdTugCvtb1cuf78Vf4g=="], + + "@img/sharp-libvips-darwin-x64": ["@img/sharp-libvips-darwin-x64@1.2.4", "", { "os": "darwin", "cpu": "x64" }, "sha512-1IOd5xfVhlGwX+zXv2N93k0yMONvUlANylbJw1eTah8K/Jtpi15KC+WSiaX/nBmbm2HxRM1gZ0nSdjSsrZbGKg=="], + + "@img/sharp-libvips-linux-arm": ["@img/sharp-libvips-linux-arm@1.2.4", "", { "os": "linux", "cpu": "arm" }, "sha512-bFI7xcKFELdiNCVov8e44Ia4u2byA+l3XtsAj+Q8tfCwO6BQ8iDojYdvoPMqsKDkuoOo+X6HZA0s0q11ANMQ8A=="], + + "@img/sharp-libvips-linux-arm64": ["@img/sharp-libvips-linux-arm64@1.2.4", "", { "os": "linux", "cpu": "arm64" }, "sha512-excjX8DfsIcJ10x1Kzr4RcWe1edC9PquDRRPx3YVCvQv+U5p7Yin2s32ftzikXojb1PIFc/9Mt28/y+iRklkrw=="], + + "@img/sharp-libvips-linux-ppc64": ["@img/sharp-libvips-linux-ppc64@1.2.4", "", { "os": "linux", "cpu": "ppc64" }, "sha512-FMuvGijLDYG6lW+b/UvyilUWu5Ayu+3r2d1S8notiGCIyYU/76eig1UfMmkZ7vwgOrzKzlQbFSuQfgm7GYUPpA=="], + + "@img/sharp-libvips-linux-riscv64": ["@img/sharp-libvips-linux-riscv64@1.2.4", "", { "os": "linux", "cpu": "none" }, "sha512-oVDbcR4zUC0ce82teubSm+x6ETixtKZBh/qbREIOcI3cULzDyb18Sr/Wcyx7NRQeQzOiHTNbZFF1UwPS2scyGA=="], + + "@img/sharp-libvips-linux-s390x": ["@img/sharp-libvips-linux-s390x@1.2.4", "", { "os": "linux", "cpu": "s390x" }, "sha512-qmp9VrzgPgMoGZyPvrQHqk02uyjA0/QrTO26Tqk6l4ZV0MPWIW6LTkqOIov+J1yEu7MbFQaDpwdwJKhbJvuRxQ=="], + + "@img/sharp-libvips-linux-x64": ["@img/sharp-libvips-linux-x64@1.2.4", "", { "os": "linux", "cpu": "x64" }, "sha512-tJxiiLsmHc9Ax1bz3oaOYBURTXGIRDODBqhveVHonrHJ9/+k89qbLl0bcJns+e4t4rvaNBxaEZsFtSfAdquPrw=="], + + "@img/sharp-libvips-linuxmusl-arm64": ["@img/sharp-libvips-linuxmusl-arm64@1.2.4", "", { "os": "linux", "cpu": "arm64" }, "sha512-FVQHuwx1IIuNow9QAbYUzJ+En8KcVm9Lk5+uGUQJHaZmMECZmOlix9HnH7n1TRkXMS0pGxIJokIVB9SuqZGGXw=="], + + "@img/sharp-libvips-linuxmusl-x64": ["@img/sharp-libvips-linuxmusl-x64@1.2.4", "", { "os": "linux", "cpu": "x64" }, "sha512-+LpyBk7L44ZIXwz/VYfglaX/okxezESc6UxDSoyo2Ks6Jxc4Y7sGjpgU9s4PMgqgjj1gZCylTieNamqA1MF7Dg=="], + + "@img/sharp-linux-arm": ["@img/sharp-linux-arm@0.34.5", "", { "optionalDependencies": { "@img/sharp-libvips-linux-arm": "1.2.4" }, "os": "linux", "cpu": "arm" }, "sha512-9dLqsvwtg1uuXBGZKsxem9595+ujv0sJ6Vi8wcTANSFpwV/GONat5eCkzQo/1O6zRIkh0m/8+5BjrRr7jDUSZw=="], + + "@img/sharp-linux-arm64": ["@img/sharp-linux-arm64@0.34.5", "", { "optionalDependencies": { "@img/sharp-libvips-linux-arm64": "1.2.4" }, "os": "linux", "cpu": "arm64" }, "sha512-bKQzaJRY/bkPOXyKx5EVup7qkaojECG6NLYswgktOZjaXecSAeCWiZwwiFf3/Y+O1HrauiE3FVsGxFg8c24rZg=="], + + "@img/sharp-linux-ppc64": ["@img/sharp-linux-ppc64@0.34.5", "", { "optionalDependencies": { "@img/sharp-libvips-linux-ppc64": "1.2.4" }, "os": "linux", "cpu": "ppc64" }, "sha512-7zznwNaqW6YtsfrGGDA6BRkISKAAE1Jo0QdpNYXNMHu2+0dTrPflTLNkpc8l7MUP5M16ZJcUvysVWWrMefZquA=="], + + "@img/sharp-linux-riscv64": ["@img/sharp-linux-riscv64@0.34.5", "", { "optionalDependencies": { "@img/sharp-libvips-linux-riscv64": "1.2.4" }, "os": "linux", "cpu": "none" }, "sha512-51gJuLPTKa7piYPaVs8GmByo7/U7/7TZOq+cnXJIHZKavIRHAP77e3N2HEl3dgiqdD/w0yUfiJnII77PuDDFdw=="], + + "@img/sharp-linux-s390x": ["@img/sharp-linux-s390x@0.34.5", "", { "optionalDependencies": { "@img/sharp-libvips-linux-s390x": "1.2.4" }, "os": "linux", "cpu": "s390x" }, "sha512-nQtCk0PdKfho3eC5MrbQoigJ2gd1CgddUMkabUj+rBevs8tZ2cULOx46E7oyX+04WGfABgIwmMC0VqieTiR4jg=="], + + "@img/sharp-linux-x64": ["@img/sharp-linux-x64@0.34.5", "", { "optionalDependencies": { "@img/sharp-libvips-linux-x64": "1.2.4" }, "os": "linux", "cpu": "x64" }, "sha512-MEzd8HPKxVxVenwAa+JRPwEC7QFjoPWuS5NZnBt6B3pu7EG2Ge0id1oLHZpPJdn3OQK+BQDiw9zStiHBTJQQQQ=="], + + "@img/sharp-linuxmusl-arm64": ["@img/sharp-linuxmusl-arm64@0.34.5", "", { "optionalDependencies": { "@img/sharp-libvips-linuxmusl-arm64": "1.2.4" }, "os": "linux", "cpu": "arm64" }, "sha512-fprJR6GtRsMt6Kyfq44IsChVZeGN97gTD331weR1ex1c1rypDEABN6Tm2xa1wE6lYb5DdEnk03NZPqA7Id21yg=="], + + "@img/sharp-linuxmusl-x64": ["@img/sharp-linuxmusl-x64@0.34.5", "", { "optionalDependencies": { "@img/sharp-libvips-linuxmusl-x64": "1.2.4" }, "os": "linux", "cpu": "x64" }, "sha512-Jg8wNT1MUzIvhBFxViqrEhWDGzqymo3sV7z7ZsaWbZNDLXRJZoRGrjulp60YYtV4wfY8VIKcWidjojlLcWrd8Q=="], + + "@img/sharp-wasm32": ["@img/sharp-wasm32@0.34.5", "", { "dependencies": { "@emnapi/runtime": "^1.7.0" }, "cpu": "none" }, "sha512-OdWTEiVkY2PHwqkbBI8frFxQQFekHaSSkUIJkwzclWZe64O1X4UlUjqqqLaPbUpMOQk6FBu/HtlGXNblIs0huw=="], + + "@img/sharp-win32-arm64": ["@img/sharp-win32-arm64@0.34.5", "", { "os": "win32", "cpu": "arm64" }, "sha512-WQ3AgWCWYSb2yt+IG8mnC6Jdk9Whs7O0gxphblsLvdhSpSTtmu69ZG1Gkb6NuvxsNACwiPV6cNSZNzt0KPsw7g=="], + + "@img/sharp-win32-ia32": ["@img/sharp-win32-ia32@0.34.5", "", { "os": "win32", "cpu": "ia32" }, "sha512-FV9m/7NmeCmSHDD5j4+4pNI8Cp3aW+JvLoXcTUo0IqyjSfAZJ8dIUmijx1qaJsIiU+Hosw6xM5KijAWRJCSgNg=="], + + "@img/sharp-win32-x64": ["@img/sharp-win32-x64@0.34.5", "", { "os": "win32", "cpu": "x64" }, "sha512-+29YMsqY2/9eFEiW93eqWnuLcWcufowXewwSNIT6UwZdUUCrM3oFjMWH/Z6/TMmb4hlFenmfAVbpWeup2jryCw=="], + + "@jridgewell/gen-mapping": ["@jridgewell/gen-mapping@0.3.13", "", { "dependencies": { "@jridgewell/sourcemap-codec": "^1.5.0", "@jridgewell/trace-mapping": "^0.3.24" } }, "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA=="], + + "@jridgewell/remapping": ["@jridgewell/remapping@2.3.5", "", { "dependencies": { "@jridgewell/gen-mapping": "^0.3.5", "@jridgewell/trace-mapping": "^0.3.24" } }, "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ=="], + + "@jridgewell/resolve-uri": ["@jridgewell/resolve-uri@3.1.2", "", {}, "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw=="], + + "@jridgewell/sourcemap-codec": ["@jridgewell/sourcemap-codec@1.5.5", "", {}, "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og=="], + + "@jridgewell/trace-mapping": ["@jridgewell/trace-mapping@0.3.31", "", { "dependencies": { "@jridgewell/resolve-uri": "^3.1.0", "@jridgewell/sourcemap-codec": "^1.4.14" } }, "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw=="], + + "@napi-rs/wasm-runtime": ["@napi-rs/wasm-runtime@1.1.4", "", { "dependencies": { "@tybys/wasm-util": "^0.10.1" }, "peerDependencies": { "@emnapi/core": "^1.7.1", "@emnapi/runtime": "^1.7.1" } }, "sha512-3NQNNgA1YSlJb/kMH1ildASP9HW7/7kYnRI2szWJaofaS1hWmbGI4H+d3+22aGzXXN9IJ+n+GiFVcGipJP18ow=="], + + "@next/env": ["@next/env@16.2.6", "", {}, "sha512-gd8HoHN4ufj73WmR3JmVolrpJR47ILK6LouP5xElPglaVxir6e1a7VzvTvDWkOoPXT9rkkTzyCxBu4yeZfZwcw=="], + + "@next/eslint-plugin-next": ["@next/eslint-plugin-next@16.2.6", "", { "dependencies": { "fast-glob": "3.3.1" } }, "sha512-Z8l6o4JWKUl755x4R+wogD86KPeU+Ckw4K+SYG4kHeOJtRenDeK+OSbGcqZpDtbwn9DsJVdir2UxmwXuinUbUw=="], + + "@next/swc-darwin-arm64": ["@next/swc-darwin-arm64@16.2.6", "", { "os": "darwin", "cpu": "arm64" }, "sha512-ZJGkkcNfYgrrMkqOdZ7zoLa1TOy0qpcMfk/z4Mh/FKUz40gVO+HNQWqmLxf67Z5WB64DRp0dhEbyHfel+6sJUg=="], + + "@next/swc-darwin-x64": ["@next/swc-darwin-x64@16.2.6", "", { "os": "darwin", "cpu": "x64" }, "sha512-v/YLBHIY132Ced3puBJ7YJKw1lqsCrgcNo2aRJlCEyQrrCeRJlvGlnmxhPxNQI3KE3N1DN5r9TPNPvka3nq5RQ=="], + + "@next/swc-linux-arm64-gnu": ["@next/swc-linux-arm64-gnu@16.2.6", "", { "os": "linux", "cpu": "arm64" }, "sha512-RPOvqlYBbcQjkz9VQQDZ2T2bARIjXZV1KFlt+V2Mr6SW/e4I9fcKsaA0hdyf2FHoTlsV2xnBd5Y912rP/1Ce6w=="], + + "@next/swc-linux-arm64-musl": ["@next/swc-linux-arm64-musl@16.2.6", "", { "os": "linux", "cpu": "arm64" }, "sha512-URUTu1+dMkxJsPFgm+OeEvq9wf5sujw0EvgYy80TDGHTSLTnIHeqb0Eu8A3sC95IRgjejQL+kC4mw+4yPxiAXA=="], + + "@next/swc-linux-x64-gnu": ["@next/swc-linux-x64-gnu@16.2.6", "", { "os": "linux", "cpu": "x64" }, "sha512-DOj182mPV8G3UkrayLoREM5YEYI+Dk5wv7Ox9xl1fFibAELEsFD0lDPfHIeILlutMMfdyhlzYPELG3peuKaurw=="], + + "@next/swc-linux-x64-musl": ["@next/swc-linux-x64-musl@16.2.6", "", { "os": "linux", "cpu": "x64" }, "sha512-HKQ5SP/V/ub73UvF7n/zeJlxk2kLmtL7Wzrg4WfmkjmNos5onJ2tKu7yZOPdL18A6Svfn3max29ym+ry7NkK4g=="], + + "@next/swc-win32-arm64-msvc": ["@next/swc-win32-arm64-msvc@16.2.6", "", { "os": "win32", "cpu": "arm64" }, "sha512-LZXpTlPyS5v7HhSmnvsLGP3iIYgYOBnc8r8ArlT55sGHV89bR2HlDdBjWQ+PY6SJMmk8TuVGFuxalnP3k/0Dwg=="], + + "@next/swc-win32-x64-msvc": ["@next/swc-win32-x64-msvc@16.2.6", "", { "os": "win32", "cpu": "x64" }, "sha512-F0+4i0h9J6C4eE3EAPWsoCk7UW/dbzOjyzxY0qnDUOYFu6FFmdZ6l97/XdV3/Nz3VYyO7UWjyEJUXkGqcoXfMA=="], + + "@nodelib/fs.scandir": ["@nodelib/fs.scandir@2.1.5", "", { "dependencies": { "@nodelib/fs.stat": "2.0.5", "run-parallel": "^1.1.9" } }, "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g=="], + + "@nodelib/fs.stat": ["@nodelib/fs.stat@2.0.5", "", {}, "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A=="], + + "@nodelib/fs.walk": ["@nodelib/fs.walk@1.2.8", "", { "dependencies": { "@nodelib/fs.scandir": "2.1.5", "fastq": "^1.6.0" } }, "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg=="], + + "@nolyfill/is-core-module": ["@nolyfill/is-core-module@1.0.39", "", {}, "sha512-nn5ozdjYQpUCZlWGuxcJY/KpxkWQs4DcbMCmKojjyrYDEAGy4Ce19NN4v5MduafTwJlbKc99UA8YhSVqq9yPZA=="], + + "@oxc-project/types": ["@oxc-project/types@0.130.0", "", {}, "sha512-ibD2usx9JRu7f5pu2tMKMI4cpA4NgXJQoYRP4pQ7Pxmn1l6k/53qWtQWZayhYy3X4QZkt90Ot+mJEaeXouio6Q=="], + + "@parcel/watcher": ["@parcel/watcher@2.5.6", "", { "dependencies": { "detect-libc": "^2.0.3", "is-glob": "^4.0.3", "node-addon-api": "^7.0.0", "picomatch": "^4.0.3" }, "optionalDependencies": { "@parcel/watcher-android-arm64": "2.5.6", "@parcel/watcher-darwin-arm64": "2.5.6", "@parcel/watcher-darwin-x64": "2.5.6", "@parcel/watcher-freebsd-x64": "2.5.6", "@parcel/watcher-linux-arm-glibc": "2.5.6", "@parcel/watcher-linux-arm-musl": "2.5.6", "@parcel/watcher-linux-arm64-glibc": "2.5.6", "@parcel/watcher-linux-arm64-musl": "2.5.6", "@parcel/watcher-linux-x64-glibc": "2.5.6", "@parcel/watcher-linux-x64-musl": "2.5.6", "@parcel/watcher-win32-arm64": "2.5.6", "@parcel/watcher-win32-ia32": "2.5.6", "@parcel/watcher-win32-x64": "2.5.6" } }, "sha512-tmmZ3lQxAe/k/+rNnXQRawJ4NjxO2hqiOLTHvWchtGZULp4RyFeh6aU4XdOYBFe2KE1oShQTv4AblOs2iOrNnQ=="], + + "@parcel/watcher-android-arm64": ["@parcel/watcher-android-arm64@2.5.6", "", { "os": "android", "cpu": "arm64" }, "sha512-YQxSS34tPF/6ZG7r/Ih9xy+kP/WwediEUsqmtf0cuCV5TPPKw/PQHRhueUo6JdeFJaqV3pyjm0GdYjZotbRt/A=="], + + "@parcel/watcher-darwin-arm64": ["@parcel/watcher-darwin-arm64@2.5.6", "", { "os": "darwin", "cpu": "arm64" }, "sha512-Z2ZdrnwyXvvvdtRHLmM4knydIdU9adO3D4n/0cVipF3rRiwP+3/sfzpAwA/qKFL6i1ModaabkU7IbpeMBgiVEA=="], + + "@parcel/watcher-darwin-x64": ["@parcel/watcher-darwin-x64@2.5.6", "", { "os": "darwin", "cpu": "x64" }, "sha512-HgvOf3W9dhithcwOWX9uDZyn1lW9R+7tPZ4sug+NGrGIo4Rk1hAXLEbcH1TQSqxts0NYXXlOWqVpvS1SFS4fRg=="], + + "@parcel/watcher-freebsd-x64": ["@parcel/watcher-freebsd-x64@2.5.6", "", { "os": "freebsd", "cpu": "x64" }, "sha512-vJVi8yd/qzJxEKHkeemh7w3YAn6RJCtYlE4HPMoVnCpIXEzSrxErBW5SJBgKLbXU3WdIpkjBTeUNtyBVn8TRng=="], + + "@parcel/watcher-linux-arm-glibc": ["@parcel/watcher-linux-arm-glibc@2.5.6", "", { "os": "linux", "cpu": "arm" }, "sha512-9JiYfB6h6BgV50CCfasfLf/uvOcJskMSwcdH1PHH9rvS1IrNy8zad6IUVPVUfmXr+u+Km9IxcfMLzgdOudz9EQ=="], + + "@parcel/watcher-linux-arm-musl": ["@parcel/watcher-linux-arm-musl@2.5.6", "", { "os": "linux", "cpu": "arm" }, "sha512-Ve3gUCG57nuUUSyjBq/MAM0CzArtuIOxsBdQ+ftz6ho8n7s1i9E1Nmk/xmP323r2YL0SONs1EuwqBp2u1k5fxg=="], + + "@parcel/watcher-linux-arm64-glibc": ["@parcel/watcher-linux-arm64-glibc@2.5.6", "", { "os": "linux", "cpu": "arm64" }, "sha512-f2g/DT3NhGPdBmMWYoxixqYr3v/UXcmLOYy16Bx0TM20Tchduwr4EaCbmxh1321TABqPGDpS8D/ggOTaljijOA=="], + + "@parcel/watcher-linux-arm64-musl": ["@parcel/watcher-linux-arm64-musl@2.5.6", "", { "os": "linux", "cpu": "arm64" }, "sha512-qb6naMDGlbCwdhLj6hgoVKJl2odL34z2sqkC7Z6kzir8b5W65WYDpLB6R06KabvZdgoHI/zxke4b3zR0wAbDTA=="], + + "@parcel/watcher-linux-x64-glibc": ["@parcel/watcher-linux-x64-glibc@2.5.6", "", { "os": "linux", "cpu": "x64" }, "sha512-kbT5wvNQlx7NaGjzPFu8nVIW1rWqV780O7ZtkjuWaPUgpv2NMFpjYERVi0UYj1msZNyCzGlaCWEtzc+exjMGbQ=="], + + "@parcel/watcher-linux-x64-musl": ["@parcel/watcher-linux-x64-musl@2.5.6", "", { "os": "linux", "cpu": "x64" }, "sha512-1JRFeC+h7RdXwldHzTsmdtYR/Ku8SylLgTU/reMuqdVD7CtLwf0VR1FqeprZ0eHQkO0vqsbvFLXUmYm/uNKJBg=="], + + "@parcel/watcher-win32-arm64": ["@parcel/watcher-win32-arm64@2.5.6", "", { "os": "win32", "cpu": "arm64" }, "sha512-3ukyebjc6eGlw9yRt678DxVF7rjXatWiHvTXqphZLvo7aC5NdEgFufVwjFfY51ijYEWpXbqF5jtrK275z52D4Q=="], + + "@parcel/watcher-win32-ia32": ["@parcel/watcher-win32-ia32@2.5.6", "", { "os": "win32", "cpu": "ia32" }, "sha512-k35yLp1ZMwwee3Ez/pxBi5cf4AoBKYXj00CZ80jUz5h8prpiaQsiRPKQMxoLstNuqe2vR4RNPEAEcjEFzhEz/g=="], + + "@parcel/watcher-win32-x64": ["@parcel/watcher-win32-x64@2.5.6", "", { "os": "win32", "cpu": "x64" }, "sha512-hbQlYcCq5dlAX9Qx+kFb0FHue6vbjlf0FrNzSKdYK2APUf7tGfGxQCk2ihEREmbR6ZMc0MVAD5RIX/41gpUzTw=="], + + "@radix-ui/number": ["@radix-ui/number@1.1.1", "", {}, "sha512-MkKCwxlXTgz6CFoJx3pCwn07GKp36+aZyu/u2Ln2VrA5DcdyCZkASEDBTd8x5whTQQL5CiYf4prXKLcgQdv29g=="], + + "@radix-ui/primitive": ["@radix-ui/primitive@1.1.3", "", {}, "sha512-JTF99U/6XIjCBo0wqkU5sK10glYe27MRRsfwoiq5zzOEZLHU3A3KCMa5X/azekYRCJ0HlwI0crAXS/5dEHTzDg=="], + + "@radix-ui/react-accessible-icon": ["@radix-ui/react-accessible-icon@1.1.7", "", { "dependencies": { "@radix-ui/react-visually-hidden": "1.2.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-XM+E4WXl0OqUJFovy6GjmxxFyx9opfCAIUku4dlKRd5YEPqt4kALOkQOp0Of6reHuUkJuiPBEc5k0o4z4lTC8A=="], + + "@radix-ui/react-accordion": ["@radix-ui/react-accordion@1.2.12", "", { "dependencies": { "@radix-ui/primitive": "1.1.3", "@radix-ui/react-collapsible": "1.1.12", "@radix-ui/react-collection": "1.1.7", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-direction": "1.1.1", "@radix-ui/react-id": "1.1.1", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-use-controllable-state": "1.2.2" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-T4nygeh9YE9dLRPhAHSeOZi7HBXo+0kYIPJXayZfvWOWA0+n3dESrZbjfDPUABkUNym6Hd+f2IR113To8D2GPA=="], + + "@radix-ui/react-alert-dialog": ["@radix-ui/react-alert-dialog@1.1.15", "", { "dependencies": { "@radix-ui/primitive": "1.1.3", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-dialog": "1.1.15", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-slot": "1.2.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-oTVLkEw5GpdRe29BqJ0LSDFWI3qu0vR1M0mUkOQWDIUnY/QIkLpgDMWuKxP94c2NAC2LGcgVhG1ImF3jkZ5wXw=="], + + "@radix-ui/react-arrow": ["@radix-ui/react-arrow@1.1.7", "", { "dependencies": { "@radix-ui/react-primitive": "2.1.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-F+M1tLhO+mlQaOWspE8Wstg+z6PwxwRd8oQ8IXceWz92kfAmalTRf0EjrouQeo7QssEPfCn05B4Ihs1K9WQ/7w=="], + + "@radix-ui/react-aspect-ratio": ["@radix-ui/react-aspect-ratio@1.1.7", "", { "dependencies": { "@radix-ui/react-primitive": "2.1.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-Yq6lvO9HQyPwev1onK1daHCHqXVLzPhSVjmsNjCa2Zcxy2f7uJD2itDtxknv6FzAKCwD1qQkeVDmX/cev13n/g=="], + + "@radix-ui/react-avatar": ["@radix-ui/react-avatar@1.1.10", "", { "dependencies": { "@radix-ui/react-context": "1.1.2", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-use-callback-ref": "1.1.1", "@radix-ui/react-use-is-hydrated": "0.1.0", "@radix-ui/react-use-layout-effect": "1.1.1" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-V8piFfWapM5OmNCXTzVQY+E1rDa53zY+MQ4Y7356v4fFz6vqCyUtIz2rUD44ZEdwg78/jKmMJHj07+C/Z/rcog=="], + + "@radix-ui/react-checkbox": ["@radix-ui/react-checkbox@1.3.3", "", { "dependencies": { "@radix-ui/primitive": "1.1.3", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-presence": "1.1.5", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-use-controllable-state": "1.2.2", "@radix-ui/react-use-previous": "1.1.1", "@radix-ui/react-use-size": "1.1.1" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-wBbpv+NQftHDdG86Qc0pIyXk5IR3tM8Vd0nWLKDcX8nNn4nXFOFwsKuqw2okA/1D/mpaAkmuyndrPJTYDNZtFw=="], + + "@radix-ui/react-collapsible": ["@radix-ui/react-collapsible@1.1.12", "", { "dependencies": { "@radix-ui/primitive": "1.1.3", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-id": "1.1.1", "@radix-ui/react-presence": "1.1.5", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-use-controllable-state": "1.2.2", "@radix-ui/react-use-layout-effect": "1.1.1" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-Uu+mSh4agx2ib1uIGPP4/CKNULyajb3p92LsVXmH2EHVMTfZWpll88XJ0j4W0z3f8NK1eYl1+Mf/szHPmcHzyA=="], + + "@radix-ui/react-collection": ["@radix-ui/react-collection@1.1.7", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-slot": "1.2.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-Fh9rGN0MoI4ZFUNyfFVNU4y9LUz93u9/0K+yLgA2bwRojxM8JU1DyvvMBabnZPBgMWREAJvU2jjVzq+LrFUglw=="], + + "@radix-ui/react-compose-refs": ["@radix-ui/react-compose-refs@1.1.2", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-z4eqJvfiNnFMHIIvXP3CY57y2WJs5g2v3X0zm9mEJkrkNv4rDxu+sg9Jh8EkXyeqBkB7SOcboo9dMVqhyrACIg=="], + + "@radix-ui/react-context": ["@radix-ui/react-context@1.1.2", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-jCi/QKUM2r1Ju5a3J64TH2A5SpKAgh0LpknyqdQ4m6DCV0xJ2HG1xARRwNGPQfi1SLdLWZ1OJz6F4OMBBNiGJA=="], + + "@radix-ui/react-context-menu": ["@radix-ui/react-context-menu@2.2.16", "", { "dependencies": { "@radix-ui/primitive": "1.1.3", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-menu": "2.1.16", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-use-callback-ref": "1.1.1", "@radix-ui/react-use-controllable-state": "1.2.2" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-O8morBEW+HsVG28gYDZPTrT9UUovQUlJue5YO836tiTJhuIWBm/zQHc7j388sHWtdH/xUZurK9olD2+pcqx5ww=="], + + "@radix-ui/react-dialog": ["@radix-ui/react-dialog@1.1.15", "", { "dependencies": { "@radix-ui/primitive": "1.1.3", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-dismissable-layer": "1.1.11", "@radix-ui/react-focus-guards": "1.1.3", "@radix-ui/react-focus-scope": "1.1.7", "@radix-ui/react-id": "1.1.1", "@radix-ui/react-portal": "1.1.9", "@radix-ui/react-presence": "1.1.5", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-slot": "1.2.3", "@radix-ui/react-use-controllable-state": "1.2.2", "aria-hidden": "^1.2.4", "react-remove-scroll": "^2.6.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-TCglVRtzlffRNxRMEyR36DGBLJpeusFcgMVD9PZEzAKnUs1lKCgX5u9BmC2Yg+LL9MgZDugFFs1Vl+Jp4t/PGw=="], + + "@radix-ui/react-direction": ["@radix-ui/react-direction@1.1.1", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-1UEWRX6jnOA2y4H5WczZ44gOOjTEmlqv1uNW4GAJEO5+bauCBhv8snY65Iw5/VOS/ghKN9gr2KjnLKxrsvoMVw=="], + + "@radix-ui/react-dismissable-layer": ["@radix-ui/react-dismissable-layer@1.1.11", "", { "dependencies": { "@radix-ui/primitive": "1.1.3", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-use-callback-ref": "1.1.1", "@radix-ui/react-use-escape-keydown": "1.1.1" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-Nqcp+t5cTB8BinFkZgXiMJniQH0PsUt2k51FUhbdfeKvc4ACcG2uQniY/8+h1Yv6Kza4Q7lD7PQV0z0oicE0Mg=="], + + "@radix-ui/react-dropdown-menu": ["@radix-ui/react-dropdown-menu@2.1.16", "", { "dependencies": { "@radix-ui/primitive": "1.1.3", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-id": "1.1.1", "@radix-ui/react-menu": "2.1.16", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-use-controllable-state": "1.2.2" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-1PLGQEynI/3OX/ftV54COn+3Sud/Mn8vALg2rWnBLnRaGtJDduNW/22XjlGgPdpcIbiQxjKtb7BkcjP00nqfJw=="], + + "@radix-ui/react-focus-guards": ["@radix-ui/react-focus-guards@1.1.3", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-0rFg/Rj2Q62NCm62jZw0QX7a3sz6QCQU0LpZdNrJX8byRGaGVTqbrW9jAoIAHyMQqsNpeZ81YgSizOt5WXq0Pw=="], + + "@radix-ui/react-focus-scope": ["@radix-ui/react-focus-scope@1.1.7", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-use-callback-ref": "1.1.1" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-t2ODlkXBQyn7jkl6TNaw/MtVEVvIGelJDCG41Okq/KwUsJBwQ4XVZsHAVUkK4mBv3ewiAS3PGuUWuY2BoK4ZUw=="], + + "@radix-ui/react-form": ["@radix-ui/react-form@0.1.8", "", { "dependencies": { "@radix-ui/primitive": "1.1.3", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-id": "1.1.1", "@radix-ui/react-label": "2.1.7", "@radix-ui/react-primitive": "2.1.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-QM70k4Zwjttifr5a4sZFts9fn8FzHYvQ5PiB19O2HsYibaHSVt9fH9rzB0XZo/YcM+b7t/p7lYCT/F5eOeF5yQ=="], + + "@radix-ui/react-hover-card": ["@radix-ui/react-hover-card@1.1.15", "", { "dependencies": { "@radix-ui/primitive": "1.1.3", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-dismissable-layer": "1.1.11", "@radix-ui/react-popper": "1.2.8", "@radix-ui/react-portal": "1.1.9", "@radix-ui/react-presence": "1.1.5", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-use-controllable-state": "1.2.2" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-qgTkjNT1CfKMoP0rcasmlH2r1DAiYicWsDsufxl940sT2wHNEWWv6FMWIQXWhVdmC1d/HYfbhQx60KYyAtKxjg=="], + + "@radix-ui/react-id": ["@radix-ui/react-id@1.1.1", "", { "dependencies": { "@radix-ui/react-use-layout-effect": "1.1.1" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-kGkGegYIdQsOb4XjsfM97rXsiHaBwco+hFI66oO4s9LU+PLAC5oJ7khdOVFxkhsmlbpUqDAvXw11CluXP+jkHg=="], + + "@radix-ui/react-label": ["@radix-ui/react-label@2.1.7", "", { "dependencies": { "@radix-ui/react-primitive": "2.1.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-YT1GqPSL8kJn20djelMX7/cTRp/Y9w5IZHvfxQTVHrOqa2yMl7i/UfMqKRU5V7mEyKTrUVgJXhNQPVCG8PBLoQ=="], + + "@radix-ui/react-menu": ["@radix-ui/react-menu@2.1.16", "", { "dependencies": { "@radix-ui/primitive": "1.1.3", "@radix-ui/react-collection": "1.1.7", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-direction": "1.1.1", "@radix-ui/react-dismissable-layer": "1.1.11", "@radix-ui/react-focus-guards": "1.1.3", "@radix-ui/react-focus-scope": "1.1.7", "@radix-ui/react-id": "1.1.1", "@radix-ui/react-popper": "1.2.8", "@radix-ui/react-portal": "1.1.9", "@radix-ui/react-presence": "1.1.5", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-roving-focus": "1.1.11", "@radix-ui/react-slot": "1.2.3", "@radix-ui/react-use-callback-ref": "1.1.1", "aria-hidden": "^1.2.4", "react-remove-scroll": "^2.6.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-72F2T+PLlphrqLcAotYPp0uJMr5SjP5SL01wfEspJbru5Zs5vQaSHb4VB3ZMJPimgHHCHG7gMOeOB9H3Hdmtxg=="], + + "@radix-ui/react-menubar": ["@radix-ui/react-menubar@1.1.16", "", { "dependencies": { "@radix-ui/primitive": "1.1.3", "@radix-ui/react-collection": "1.1.7", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-direction": "1.1.1", "@radix-ui/react-id": "1.1.1", "@radix-ui/react-menu": "2.1.16", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-roving-focus": "1.1.11", "@radix-ui/react-use-controllable-state": "1.2.2" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-EB1FktTz5xRRi2Er974AUQZWg2yVBb1yjip38/lgwtCVRd3a+maUoGHN/xs9Yv8SY8QwbSEb+YrxGadVWbEutA=="], + + "@radix-ui/react-navigation-menu": ["@radix-ui/react-navigation-menu@1.2.14", "", { "dependencies": { "@radix-ui/primitive": "1.1.3", "@radix-ui/react-collection": "1.1.7", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-direction": "1.1.1", "@radix-ui/react-dismissable-layer": "1.1.11", "@radix-ui/react-id": "1.1.1", "@radix-ui/react-presence": "1.1.5", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-use-callback-ref": "1.1.1", "@radix-ui/react-use-controllable-state": "1.2.2", "@radix-ui/react-use-layout-effect": "1.1.1", "@radix-ui/react-use-previous": "1.1.1", "@radix-ui/react-visually-hidden": "1.2.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-YB9mTFQvCOAQMHU+C/jVl96WmuWeltyUEpRJJky51huhds5W2FQr1J8D/16sQlf0ozxkPK8uF3niQMdUwZPv5w=="], + + "@radix-ui/react-one-time-password-field": ["@radix-ui/react-one-time-password-field@0.1.8", "", { "dependencies": { "@radix-ui/number": "1.1.1", "@radix-ui/primitive": "1.1.3", "@radix-ui/react-collection": "1.1.7", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-direction": "1.1.1", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-roving-focus": "1.1.11", "@radix-ui/react-use-controllable-state": "1.2.2", "@radix-ui/react-use-effect-event": "0.0.2", "@radix-ui/react-use-is-hydrated": "0.1.0", "@radix-ui/react-use-layout-effect": "1.1.1" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-ycS4rbwURavDPVjCb5iS3aG4lURFDILi6sKI/WITUMZ13gMmn/xGjpLoqBAalhJaDk8I3UbCM5GzKHrnzwHbvg=="], + + "@radix-ui/react-password-toggle-field": ["@radix-ui/react-password-toggle-field@0.1.3", "", { "dependencies": { "@radix-ui/primitive": "1.1.3", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-id": "1.1.1", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-use-controllable-state": "1.2.2", "@radix-ui/react-use-effect-event": "0.0.2", "@radix-ui/react-use-is-hydrated": "0.1.0" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-/UuCrDBWravcaMix4TdT+qlNdVwOM1Nck9kWx/vafXsdfj1ChfhOdfi3cy9SGBpWgTXwYCuboT/oYpJy3clqfw=="], + + "@radix-ui/react-popover": ["@radix-ui/react-popover@1.1.15", "", { "dependencies": { "@radix-ui/primitive": "1.1.3", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-dismissable-layer": "1.1.11", "@radix-ui/react-focus-guards": "1.1.3", "@radix-ui/react-focus-scope": "1.1.7", "@radix-ui/react-id": "1.1.1", "@radix-ui/react-popper": "1.2.8", "@radix-ui/react-portal": "1.1.9", "@radix-ui/react-presence": "1.1.5", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-slot": "1.2.3", "@radix-ui/react-use-controllable-state": "1.2.2", "aria-hidden": "^1.2.4", "react-remove-scroll": "^2.6.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-kr0X2+6Yy/vJzLYJUPCZEc8SfQcf+1COFoAqauJm74umQhta9M7lNJHP7QQS3vkvcGLQUbWpMzwrXYwrYztHKA=="], + + "@radix-ui/react-popper": ["@radix-ui/react-popper@1.2.8", "", { "dependencies": { "@floating-ui/react-dom": "^2.0.0", "@radix-ui/react-arrow": "1.1.7", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-use-callback-ref": "1.1.1", "@radix-ui/react-use-layout-effect": "1.1.1", "@radix-ui/react-use-rect": "1.1.1", "@radix-ui/react-use-size": "1.1.1", "@radix-ui/rect": "1.1.1" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-0NJQ4LFFUuWkE7Oxf0htBKS6zLkkjBH+hM1uk7Ng705ReR8m/uelduy1DBo0PyBXPKVnBA6YBlU94MBGXrSBCw=="], + + "@radix-ui/react-portal": ["@radix-ui/react-portal@1.1.9", "", { "dependencies": { "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-use-layout-effect": "1.1.1" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-bpIxvq03if6UNwXZ+HTK71JLh4APvnXntDc6XOX8UVq4XQOVl7lwok0AvIl+b8zgCw3fSaVTZMpAPPagXbKmHQ=="], + + "@radix-ui/react-presence": ["@radix-ui/react-presence@1.1.5", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-use-layout-effect": "1.1.1" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-/jfEwNDdQVBCNvjkGit4h6pMOzq8bHkopq458dPt2lMjx+eBQUohZNG9A7DtO/O5ukSbxuaNGXMjHicgwy6rQQ=="], + + "@radix-ui/react-primitive": ["@radix-ui/react-primitive@2.1.4", "", { "dependencies": { "@radix-ui/react-slot": "1.2.4" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-9hQc4+GNVtJAIEPEqlYqW5RiYdrr8ea5XQ0ZOnD6fgru+83kqT15mq2OCcbe8KnjRZl5vF3ks69AKz3kh1jrhg=="], + + "@radix-ui/react-progress": ["@radix-ui/react-progress@1.1.7", "", { "dependencies": { "@radix-ui/react-context": "1.1.2", "@radix-ui/react-primitive": "2.1.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-vPdg/tF6YC/ynuBIJlk1mm7Le0VgW6ub6J2UWnTQ7/D23KXcPI1qy+0vBkgKgd38RCMJavBXpB83HPNFMTb0Fg=="], + + "@radix-ui/react-radio-group": ["@radix-ui/react-radio-group@1.3.8", "", { "dependencies": { "@radix-ui/primitive": "1.1.3", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-direction": "1.1.1", "@radix-ui/react-presence": "1.1.5", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-roving-focus": "1.1.11", "@radix-ui/react-use-controllable-state": "1.2.2", "@radix-ui/react-use-previous": "1.1.1", "@radix-ui/react-use-size": "1.1.1" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-VBKYIYImA5zsxACdisNQ3BjCBfmbGH3kQlnFVqlWU4tXwjy7cGX8ta80BcrO+WJXIn5iBylEH3K6ZTlee//lgQ=="], + + "@radix-ui/react-roving-focus": ["@radix-ui/react-roving-focus@1.1.11", "", { "dependencies": { "@radix-ui/primitive": "1.1.3", "@radix-ui/react-collection": "1.1.7", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-direction": "1.1.1", "@radix-ui/react-id": "1.1.1", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-use-callback-ref": "1.1.1", "@radix-ui/react-use-controllable-state": "1.2.2" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-7A6S9jSgm/S+7MdtNDSb+IU859vQqJ/QAtcYQcfFC6W8RS4IxIZDldLR0xqCFZ6DCyrQLjLPsxtTNch5jVA4lA=="], + + "@radix-ui/react-scroll-area": ["@radix-ui/react-scroll-area@1.2.10", "", { "dependencies": { "@radix-ui/number": "1.1.1", "@radix-ui/primitive": "1.1.3", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-direction": "1.1.1", "@radix-ui/react-presence": "1.1.5", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-use-callback-ref": "1.1.1", "@radix-ui/react-use-layout-effect": "1.1.1" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-tAXIa1g3sM5CGpVT0uIbUx/U3Gs5N8T52IICuCtObaos1S8fzsrPXG5WObkQN3S6NVl6wKgPhAIiBGbWnvc97A=="], + + "@radix-ui/react-select": ["@radix-ui/react-select@2.2.6", "", { "dependencies": { "@radix-ui/number": "1.1.1", "@radix-ui/primitive": "1.1.3", "@radix-ui/react-collection": "1.1.7", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-direction": "1.1.1", "@radix-ui/react-dismissable-layer": "1.1.11", "@radix-ui/react-focus-guards": "1.1.3", "@radix-ui/react-focus-scope": "1.1.7", "@radix-ui/react-id": "1.1.1", "@radix-ui/react-popper": "1.2.8", "@radix-ui/react-portal": "1.1.9", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-slot": "1.2.3", "@radix-ui/react-use-callback-ref": "1.1.1", "@radix-ui/react-use-controllable-state": "1.2.2", "@radix-ui/react-use-layout-effect": "1.1.1", "@radix-ui/react-use-previous": "1.1.1", "@radix-ui/react-visually-hidden": "1.2.3", "aria-hidden": "^1.2.4", "react-remove-scroll": "^2.6.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-I30RydO+bnn2PQztvo25tswPH+wFBjehVGtmagkU78yMdwTwVf12wnAOF+AeP8S2N8xD+5UPbGhkUfPyvT+mwQ=="], + + "@radix-ui/react-separator": ["@radix-ui/react-separator@1.1.7", "", { "dependencies": { "@radix-ui/react-primitive": "2.1.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-0HEb8R9E8A+jZjvmFCy/J4xhbXy3TV+9XSnGJ3KvTtjlIUy/YQ/p6UYZvi7YbeoeXdyU9+Y3scizK6hkY37baA=="], + + "@radix-ui/react-slider": ["@radix-ui/react-slider@1.3.6", "", { "dependencies": { "@radix-ui/number": "1.1.1", "@radix-ui/primitive": "1.1.3", "@radix-ui/react-collection": "1.1.7", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-direction": "1.1.1", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-use-controllable-state": "1.2.2", "@radix-ui/react-use-layout-effect": "1.1.1", "@radix-ui/react-use-previous": "1.1.1", "@radix-ui/react-use-size": "1.1.1" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-JPYb1GuM1bxfjMRlNLE+BcmBC8onfCi60Blk7OBqi2MLTFdS+8401U4uFjnwkOr49BLmXxLC6JHkvAsx5OJvHw=="], + + "@radix-ui/react-slot": ["@radix-ui/react-slot@1.2.3", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.2" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A=="], + + "@radix-ui/react-switch": ["@radix-ui/react-switch@1.2.6", "", { "dependencies": { "@radix-ui/primitive": "1.1.3", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-use-controllable-state": "1.2.2", "@radix-ui/react-use-previous": "1.1.1", "@radix-ui/react-use-size": "1.1.1" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-bByzr1+ep1zk4VubeEVViV592vu2lHE2BZY5OnzehZqOOgogN80+mNtCqPkhn2gklJqOpxWgPoYTSnhBCqpOXQ=="], + + "@radix-ui/react-tabs": ["@radix-ui/react-tabs@1.1.13", "", { "dependencies": { "@radix-ui/primitive": "1.1.3", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-direction": "1.1.1", "@radix-ui/react-id": "1.1.1", "@radix-ui/react-presence": "1.1.5", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-roving-focus": "1.1.11", "@radix-ui/react-use-controllable-state": "1.2.2" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-7xdcatg7/U+7+Udyoj2zodtI9H/IIopqo+YOIcZOq1nJwXWBZ9p8xiu5llXlekDbZkca79a/fozEYQXIA4sW6A=="], + + "@radix-ui/react-toast": ["@radix-ui/react-toast@1.2.15", "", { "dependencies": { "@radix-ui/primitive": "1.1.3", "@radix-ui/react-collection": "1.1.7", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-dismissable-layer": "1.1.11", "@radix-ui/react-portal": "1.1.9", "@radix-ui/react-presence": "1.1.5", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-use-callback-ref": "1.1.1", "@radix-ui/react-use-controllable-state": "1.2.2", "@radix-ui/react-use-layout-effect": "1.1.1", "@radix-ui/react-visually-hidden": "1.2.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-3OSz3TacUWy4WtOXV38DggwxoqJK4+eDkNMl5Z/MJZaoUPaP4/9lf81xXMe1I2ReTAptverZUpbPY4wWwWyL5g=="], + + "@radix-ui/react-toggle": ["@radix-ui/react-toggle@1.1.10", "", { "dependencies": { "@radix-ui/primitive": "1.1.3", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-use-controllable-state": "1.2.2" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-lS1odchhFTeZv3xwHH31YPObmJn8gOg7Lq12inrr0+BH/l3Tsq32VfjqH1oh80ARM3mlkfMic15n0kg4sD1poQ=="], + + "@radix-ui/react-toggle-group": ["@radix-ui/react-toggle-group@1.1.11", "", { "dependencies": { "@radix-ui/primitive": "1.1.3", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-direction": "1.1.1", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-roving-focus": "1.1.11", "@radix-ui/react-toggle": "1.1.10", "@radix-ui/react-use-controllable-state": "1.2.2" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-5umnS0T8JQzQT6HbPyO7Hh9dgd82NmS36DQr+X/YJ9ctFNCiiQd6IJAYYZ33LUwm8M+taCz5t2ui29fHZc4Y6Q=="], + + "@radix-ui/react-toolbar": ["@radix-ui/react-toolbar@1.1.11", "", { "dependencies": { "@radix-ui/primitive": "1.1.3", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-direction": "1.1.1", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-roving-focus": "1.1.11", "@radix-ui/react-separator": "1.1.7", "@radix-ui/react-toggle-group": "1.1.11" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-4ol06/1bLoFu1nwUqzdD4Y5RZ9oDdKeiHIsntug54Hcr1pgaHiPqHFEaXI1IFP/EsOfROQZ8Mig9VTIRza6Tjg=="], + + "@radix-ui/react-tooltip": ["@radix-ui/react-tooltip@1.2.8", "", { "dependencies": { "@radix-ui/primitive": "1.1.3", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-dismissable-layer": "1.1.11", "@radix-ui/react-id": "1.1.1", "@radix-ui/react-popper": "1.2.8", "@radix-ui/react-portal": "1.1.9", "@radix-ui/react-presence": "1.1.5", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-slot": "1.2.3", "@radix-ui/react-use-controllable-state": "1.2.2", "@radix-ui/react-visually-hidden": "1.2.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-tY7sVt1yL9ozIxvmbtN5qtmH2krXcBCfjEiCgKGLqunJHvgvZG2Pcl2oQ3kbcZARb1BGEHdkLzcYGO8ynVlieg=="], + + "@radix-ui/react-use-callback-ref": ["@radix-ui/react-use-callback-ref@1.1.1", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-FkBMwD+qbGQeMu1cOHnuGB6x4yzPjho8ap5WtbEJ26umhgqVXbhekKUQO+hZEL1vU92a3wHwdp0HAcqAUF5iDg=="], + + "@radix-ui/react-use-controllable-state": ["@radix-ui/react-use-controllable-state@1.2.2", "", { "dependencies": { "@radix-ui/react-use-effect-event": "0.0.2", "@radix-ui/react-use-layout-effect": "1.1.1" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-BjasUjixPFdS+NKkypcyyN5Pmg83Olst0+c6vGov0diwTEo6mgdqVR6hxcEgFuh4QrAs7Rc+9KuGJ9TVCj0Zzg=="], + + "@radix-ui/react-use-effect-event": ["@radix-ui/react-use-effect-event@0.0.2", "", { "dependencies": { "@radix-ui/react-use-layout-effect": "1.1.1" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-Qp8WbZOBe+blgpuUT+lw2xheLP8q0oatc9UpmiemEICxGvFLYmHm9QowVZGHtJlGbS6A6yJ3iViad/2cVjnOiA=="], + + "@radix-ui/react-use-escape-keydown": ["@radix-ui/react-use-escape-keydown@1.1.1", "", { "dependencies": { "@radix-ui/react-use-callback-ref": "1.1.1" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-Il0+boE7w/XebUHyBjroE+DbByORGR9KKmITzbR7MyQ4akpORYP/ZmbhAr0DG7RmmBqoOnZdy2QlvajJ2QA59g=="], + + "@radix-ui/react-use-is-hydrated": ["@radix-ui/react-use-is-hydrated@0.1.0", "", { "dependencies": { "use-sync-external-store": "^1.5.0" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-U+UORVEq+cTnRIaostJv9AGdV3G6Y+zbVd+12e18jQ5A3c0xL03IhnHuiU4UV69wolOQp5GfR58NW/EgdQhwOA=="], + + "@radix-ui/react-use-layout-effect": ["@radix-ui/react-use-layout-effect@1.1.1", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-RbJRS4UWQFkzHTTwVymMTUv8EqYhOp8dOOviLj2ugtTiXRaRQS7GLGxZTLL1jWhMeoSCf5zmcZkqTl9IiYfXcQ=="], + + "@radix-ui/react-use-previous": ["@radix-ui/react-use-previous@1.1.1", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-2dHfToCj/pzca2Ck724OZ5L0EVrr3eHRNsG/b3xQJLA2hZpVCS99bLAX+hm1IHXDEnzU6by5z/5MIY794/a8NQ=="], + + "@radix-ui/react-use-rect": ["@radix-ui/react-use-rect@1.1.1", "", { "dependencies": { "@radix-ui/rect": "1.1.1" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-QTYuDesS0VtuHNNvMh+CjlKJ4LJickCMUAqjlE3+j8w+RlRpwyX3apEQKGFzbZGdo7XNG1tXa+bQqIE7HIXT2w=="], + + "@radix-ui/react-use-size": ["@radix-ui/react-use-size@1.1.1", "", { "dependencies": { "@radix-ui/react-use-layout-effect": "1.1.1" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-ewrXRDTAqAXlkl6t/fkXWNAhFX9I+CkKlw6zjEwk86RSPKwZr3xpBRso655aqYafwtnbpHLj6toFzmd6xdVptQ=="], + + "@radix-ui/react-visually-hidden": ["@radix-ui/react-visually-hidden@1.2.3", "", { "dependencies": { "@radix-ui/react-primitive": "2.1.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-pzJq12tEaaIhqjbzpCuv/OypJY/BPavOofm+dbab+MHLajy277+1lLm6JFcGgF5eskJ6mquGirhXY2GD/8u8Ug=="], + + "@radix-ui/rect": ["@radix-ui/rect@1.1.1", "", {}, "sha512-HPwpGIzkl28mWyZqG52jiqDJ12waP11Pa1lGoiyUkIEuMLBP0oeK/C89esbXrxsky5we7dfd8U58nm0SgAWpVw=="], + + "@rolldown/binding-android-arm64": ["@rolldown/binding-android-arm64@1.0.1", "", { "os": "android", "cpu": "arm64" }, "sha512-fJI3I0r3C3Oj/zdBCpaCmBRZYf07xpaq4yCfDDoSFm+beWNzbIl26puW8RraUdugoJw/95zerNOn6jasAhzSmg=="], + + "@rolldown/binding-darwin-arm64": ["@rolldown/binding-darwin-arm64@1.0.1", "", { "os": "darwin", "cpu": "arm64" }, "sha512-cKnAhWEsV7TPcA/5EAteDp6KcJZBQ2G+BqE7zayMMi7kMvwRsbv7WT9aOnn0WNl4SKEIf43vjS31iUPu80nzXg=="], + + "@rolldown/binding-darwin-x64": ["@rolldown/binding-darwin-x64@1.0.1", "", { "os": "darwin", "cpu": "x64" }, "sha512-YKrVwQjIRBPo+5G/u03wGjbdy4q7pyzCe93DK9VJ7zkVmeg8LJ7GbgsiHWdR4xSoe4CAXRD7Bcjgbtr64bkXNg=="], + + "@rolldown/binding-freebsd-x64": ["@rolldown/binding-freebsd-x64@1.0.1", "", { "os": "freebsd", "cpu": "x64" }, "sha512-z/oBsREo46SsFqBwYtFe0kpJeBijAT48O/WXLI4suiCLBkr03RTtTJMCzSdDd2znlh8VJizL09XVkQgk8IZonw=="], + + "@rolldown/binding-linux-arm-gnueabihf": ["@rolldown/binding-linux-arm-gnueabihf@1.0.1", "", { "os": "linux", "cpu": "arm" }, "sha512-ik8q7GM11zxvYxFc2PeDcT6TBvhCQMaUxfph/M5l9sKuTs/Sjg3L+Byw0F7w0ZVLBZmx30P+gG0ECzzN+MFcmQ=="], + + "@rolldown/binding-linux-arm64-gnu": ["@rolldown/binding-linux-arm64-gnu@1.0.1", "", { "os": "linux", "cpu": "arm64" }, "sha512-QoSx2EkyrrdZ6kcyE8stqZ62t0Yra8Fs5ia9lOxJrh6TMQJK7gQKmscdTHf7pOXKREKrVwOtJcQG3qVSfc866A=="], + + "@rolldown/binding-linux-arm64-musl": ["@rolldown/binding-linux-arm64-musl@1.0.1", "", { "os": "linux", "cpu": "arm64" }, "sha512-uwNwFpwKeNiZawfAWBgg0VIztPTV3ihhh1vV334h9ivnNLorxnQMU6Fz8wG1Zb4Qh9LC1/MkcyT3YlDXG3Rsgg=="], + + "@rolldown/binding-linux-ppc64-gnu": ["@rolldown/binding-linux-ppc64-gnu@1.0.1", "", { "os": "linux", "cpu": "ppc64" }, "sha512-zY1bul7OWr7DFBiJ++wofXvnr8B45ce3QsQUhKrIhXsygAh7bTkwyeM1bi1a2g5C/yC/N8TZyGDEoMfm/l9mpg=="], + + "@rolldown/binding-linux-s390x-gnu": ["@rolldown/binding-linux-s390x-gnu@1.0.1", "", { "os": "linux", "cpu": "s390x" }, "sha512-0frlsT/f4Ft6I7SMESTKnF3cZsdicQn1dCMkF/jT9wDLE+gGoiQfv1nmT9e+s7s/fekvvy6tZM2jHvI2tkbJDQ=="], + + "@rolldown/binding-linux-x64-gnu": ["@rolldown/binding-linux-x64-gnu@1.0.1", "", { "os": "linux", "cpu": "x64" }, "sha512-XABVmGp9Tg0WspTVvwduTc4fpqy6JnAUrSQe6OuyqD/03nI7r0O9OWUkMIwFrjKAIqolvqoA4ZrJppgwE0Gxmw=="], + + "@rolldown/binding-linux-x64-musl": ["@rolldown/binding-linux-x64-musl@1.0.1", "", { "os": "linux", "cpu": "x64" }, "sha512-bV4fzswuzVcKD90o/VM6QqKxnxlDq0g2BISDLNVmxrnhpv1DDbyPhCIjYfvzYLV+MvkKKnQt2Q6AO86SEBULUQ=="], + + "@rolldown/binding-openharmony-arm64": ["@rolldown/binding-openharmony-arm64@1.0.1", "", { "os": "none", "cpu": "arm64" }, "sha512-/Mh0Zhq3OP7fVs0kcQHZP6lZEthMGTaSf8UBQYSFEZDWGXXlEC+nJ6EqenaK2t4LBXMe3A+K/G2BVXXdtOr4PQ=="], + + "@rolldown/binding-wasm32-wasi": ["@rolldown/binding-wasm32-wasi@1.0.1", "", { "dependencies": { "@emnapi/core": "1.10.0", "@emnapi/runtime": "1.10.0", "@napi-rs/wasm-runtime": "^1.1.4" }, "cpu": "none" }, "sha512-+1xc9X45l8ufsBAm6Gjvx2qDRIY9lTVt0cgWNcJ+1gdhXvkbxePA60yRTwSTuXL09CMhyJmjpV7E3NoyxbqFQQ=="], + + "@rolldown/binding-win32-arm64-msvc": ["@rolldown/binding-win32-arm64-msvc@1.0.1", "", { "os": "win32", "cpu": "arm64" }, "sha512-1D+UqZdfnuR+Jy1GgMJwi85bD40H21uNmOPRWQhw4oRSuolZ/B5rixZ45DK2KXOTCvmVCecauWgEhbw8bI7tOw=="], + + "@rolldown/binding-win32-x64-msvc": ["@rolldown/binding-win32-x64-msvc@1.0.1", "", { "os": "win32", "cpu": "x64" }, "sha512-INAycaWuhlOK3wk4mRHGsdgwYWmd9cChdPdE9bwWmy6rn9VqVNYNFGhOdXrofXUxwHIncSiPNb8tNm8knDVIeQ=="], + + "@rolldown/pluginutils": ["@rolldown/pluginutils@1.0.1", "", {}, "sha512-2j9bGt5Jh8hj+vPtgzPtl72j0yRxHAyumoo6TNfAjsLB04UtpSvPbPcDcBMxz7n+9CYB0c1GxQFxYRg2jimqGw=="], + + "@rtsao/scc": ["@rtsao/scc@1.1.0", "", {}, "sha512-zt6OdqaDoOnJ1ZYsCYGt9YmWzDXl4vQdKTyJev62gFhRGKdx7mcT54V9KIjg+d2wi9EXsPvAPKe7i7WjfVWB8g=="], + + "@schummar/icu-type-parser": ["@schummar/icu-type-parser@1.21.5", "", {}, "sha512-bXHSaW5jRTmke9Vd0h5P7BtWZG9Znqb8gSDxZnxaGSJnGwPLDPfS+3g0BKzeWqzgZPsIVZkM7m2tbo18cm5HBw=="], + + "@simple-libs/child-process-utils": ["@simple-libs/child-process-utils@1.0.2", "", { "dependencies": { "@simple-libs/stream-utils": "^1.2.0" } }, "sha512-/4R8QKnd/8agJynkNdJmNw2MBxuFTRcNFnE5Sg/G+jkSsV8/UBgULMzhizWWW42p8L5H7flImV2ATi79Ove2Tw=="], + + "@simple-libs/stream-utils": ["@simple-libs/stream-utils@1.2.0", "", {}, "sha512-KxXvfapcixpz6rVEB6HPjOUZT22yN6v0vI0urQSk1L8MlEWPDFCZkhw2xmkyoTGYeFw7tWTZd7e3lVzRZRN/EA=="], + + "@standard-schema/spec": ["@standard-schema/spec@1.1.0", "", {}, "sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w=="], + + "@standard-schema/utils": ["@standard-schema/utils@0.3.0", "", {}, "sha512-e7Mew686owMaPJVNNLs55PUvgz371nKgwsc4vxE49zsODpJEnxgxRo2y/OKrqueavXgZNMDVj3DdHFlaSAeU8g=="], + + "@swc/core": ["@swc/core@1.15.33", "", { "dependencies": { "@swc/counter": "^0.1.3", "@swc/types": "^0.1.26" }, "optionalDependencies": { "@swc/core-darwin-arm64": "1.15.33", "@swc/core-darwin-x64": "1.15.33", "@swc/core-linux-arm-gnueabihf": "1.15.33", "@swc/core-linux-arm64-gnu": "1.15.33", "@swc/core-linux-arm64-musl": "1.15.33", "@swc/core-linux-ppc64-gnu": "1.15.33", "@swc/core-linux-s390x-gnu": "1.15.33", "@swc/core-linux-x64-gnu": "1.15.33", "@swc/core-linux-x64-musl": "1.15.33", "@swc/core-win32-arm64-msvc": "1.15.33", "@swc/core-win32-ia32-msvc": "1.15.33", "@swc/core-win32-x64-msvc": "1.15.33" }, "peerDependencies": { "@swc/helpers": ">=0.5.17" }, "optionalPeers": ["@swc/helpers"] }, "sha512-jOlwnFV2xhuuZeAUILGFULeR6vDPfijEJ57evfocwznQldLU3w2cZ9bSDryY9ip+AsM3r1NJKzf47V2NXebkeQ=="], + + "@swc/core-darwin-arm64": ["@swc/core-darwin-arm64@1.15.33", "", { "os": "darwin", "cpu": "arm64" }, "sha512-N+L0uXhuO7FIfzqwgxmzv0zIpV0qEp8wPX3QQs2p4atjMoywup2JTeDlXPw+z9pWJGCae3JjM+tZ6myclI+2gA=="], + + "@swc/core-darwin-x64": ["@swc/core-darwin-x64@1.15.33", "", { "os": "darwin", "cpu": "x64" }, "sha512-/Il4QHSOhV4FekbsDtkrNmKbsX26oSysvgrRswa/RYOHXAkwXDbB4jaeKq6PsJLSPkzJ2KzQ061gtBnk0vNHfA=="], + + "@swc/core-linux-arm-gnueabihf": ["@swc/core-linux-arm-gnueabihf@1.15.33", "", { "os": "linux", "cpu": "arm" }, "sha512-C64hBnBxq4viOPQ8hlx+2lJ23bzZBGnjw7ryALmS+0Q3zHmwO8lw1/DArLENw4Q18/0w5wdEO1k3m1wWNtKGqQ=="], + + "@swc/core-linux-arm64-gnu": ["@swc/core-linux-arm64-gnu@1.15.33", "", { "os": "linux", "cpu": "arm64" }, "sha512-TRJfnJbX3jqpxRDRoieMzRiCBS5jOmXNb3iQXmcgjFEHKLnAgK1RZRU8Cq1MsPqO4jAJp/ld1G4O3fXuxv85uw=="], + + "@swc/core-linux-arm64-musl": ["@swc/core-linux-arm64-musl@1.15.33", "", { "os": "linux", "cpu": "arm64" }, "sha512-il7tYM+CpUNzieQbwAjFT1P8zqAhmGWNAGhQZBnxurXZ0aNn+5nqYFTEUKNZl7QibtT0uQXzTZrNGHCIj6Y1Og=="], + + "@swc/core-linux-ppc64-gnu": ["@swc/core-linux-ppc64-gnu@1.15.33", "", { "os": "linux", "cpu": "ppc64" }, "sha512-ZtNBwN0Z7CFj9Il0FcPaKdjgP7URyKu/3RfH46vq+0paOBqLj4NYldD6Qo//Duif/7IOtAraUfDOmp0PLAufog=="], + + "@swc/core-linux-s390x-gnu": ["@swc/core-linux-s390x-gnu@1.15.33", "", { "os": "linux", "cpu": "s390x" }, "sha512-De1IyajoOmhOYYjw/lx66bKlyDpHZTueqwpDrWgf5O7T6d1ODeJJO9/OqMBmrBQc5C+dNnlmIufHsp4QVCWufA=="], + + "@swc/core-linux-x64-gnu": ["@swc/core-linux-x64-gnu@1.15.33", "", { "os": "linux", "cpu": "x64" }, "sha512-mGTH0YxmUN+x6vRN/I6NOk5X0ogNktkwPnJ94IMvR7QjhRDwL0O8RXEDhyUM0YtwWrryBOqaJQBX4zruxEPRGw=="], + + "@swc/core-linux-x64-musl": ["@swc/core-linux-x64-musl@1.15.33", "", { "os": "linux", "cpu": "x64" }, "sha512-hj628ZkSEJf6zMf5VMbYrG2O6QqyTIp2qwY6VlCjvIa9lAEZ5c2lfPblCLVGYubTeLJDxadLB/CxqQYOQABeEQ=="], + + "@swc/core-win32-arm64-msvc": ["@swc/core-win32-arm64-msvc@1.15.33", "", { "os": "win32", "cpu": "arm64" }, "sha512-GV2oohtN2/5+KSccl86VULu3aT+LrISC8uzgSq0FRnikpD+Zwc+sBlXmoKQ+Db6jI57ITUOIB8jRkdGMABC29g=="], + + "@swc/core-win32-ia32-msvc": ["@swc/core-win32-ia32-msvc@1.15.33", "", { "os": "win32", "cpu": "ia32" }, "sha512-gtyvzSNR8DHKfFEA2uqb8Ld1myqi6uEg2jyeUq3ikn5ytYs7H8RpZYC8mdy4NXr8hfcdJfCLXPlYaqqfBXpoEQ=="], + + "@swc/core-win32-x64-msvc": ["@swc/core-win32-x64-msvc@1.15.33", "", { "os": "win32", "cpu": "x64" }, "sha512-d6fRqQSkJI+kmMEBWaDQ7TMl8+YjLYbwRUPZQ9DY0ORBJeTzOrG0twvfvlZ2xgw6jA0ScQKgfBm4vHLSLl5Hqg=="], + + "@swc/counter": ["@swc/counter@0.1.3", "", {}, "sha512-e2BR4lsJkkRlKZ/qCHPw9ZaSxc0MVUd7gtbtaB7aMvHeJVYe8sOB8DBZkP2DtISHGSku9sCK6T6cnY0CtXrOCQ=="], + + "@swc/helpers": ["@swc/helpers@0.5.15", "", { "dependencies": { "tslib": "^2.8.0" } }, "sha512-JQ5TuMi45Owi4/BIMAJBoSQoOJu12oOk/gADqlcUL9JEdHB8vyjUSsxqeNXnmXHjYKMi2WcYtezGEEhqUI/E2g=="], + + "@swc/types": ["@swc/types@0.1.26", "", { "dependencies": { "@swc/counter": "^0.1.3" } }, "sha512-lyMwd7WGgG79RS7EERZV3T8wMdmPq3xwyg+1nmAM64kIhx5yl+juO2PYIHb7vTiPgPCj8LYjsNV2T5wiQHUEaw=="], + + "@t3-oss/env-core": ["@t3-oss/env-core@0.13.11", "", { "peerDependencies": { "arktype": "^2.1.0", "typescript": ">=5.0.0", "valibot": "^1.0.0-beta.7 || ^1.0.0", "zod": "^3.24.0 || ^4.0.0" }, "optionalPeers": ["arktype", "typescript", "valibot", "zod"] }, "sha512-sM7GYY+KL7H/Hl0BE0inWfk3nRHZOLhmVn7sHGxaZt9FAR6KqREXAE+6TqKfiavfXmpRxO/OZ2QgKRd+oiBYRQ=="], + + "@t3-oss/env-nextjs": ["@t3-oss/env-nextjs@0.13.11", "", { "dependencies": { "@t3-oss/env-core": "0.13.11" }, "peerDependencies": { "arktype": "^2.1.0", "typescript": ">=5.0.0", "valibot": "^1.0.0-beta.7 || ^1.0.0", "zod": "^3.24.0 || ^4.0.0" }, "optionalPeers": ["arktype", "typescript", "valibot", "zod"] }, "sha512-NC+3j7YWgpzdFu1t5y/8wqibTK0lm5RS4bjXA1n8uwik3wIR4iZM4Fa+U2BaMa5k3Qk8RZiYhoAIX0WogmGkzg=="], + + "@tailwindcss/node": ["@tailwindcss/node@4.3.0", "", { "dependencies": { "@jridgewell/remapping": "^2.3.5", "enhanced-resolve": "^5.21.0", "jiti": "^2.6.1", "lightningcss": "1.32.0", "magic-string": "^0.30.21", "source-map-js": "^1.2.1", "tailwindcss": "4.3.0" } }, "sha512-aFb4gUhFOgdh9AXo4IzBEOzBkkAxm9VigwDJnMIYv3lcfXCJVesNfbEaBl4BNgVRyid92AmdviqwBUBRKSeY3g=="], + + "@tailwindcss/oxide": ["@tailwindcss/oxide@4.3.0", "", { "optionalDependencies": { "@tailwindcss/oxide-android-arm64": "4.3.0", "@tailwindcss/oxide-darwin-arm64": "4.3.0", "@tailwindcss/oxide-darwin-x64": "4.3.0", "@tailwindcss/oxide-freebsd-x64": "4.3.0", "@tailwindcss/oxide-linux-arm-gnueabihf": "4.3.0", "@tailwindcss/oxide-linux-arm64-gnu": "4.3.0", "@tailwindcss/oxide-linux-arm64-musl": "4.3.0", "@tailwindcss/oxide-linux-x64-gnu": "4.3.0", "@tailwindcss/oxide-linux-x64-musl": "4.3.0", "@tailwindcss/oxide-wasm32-wasi": "4.3.0", "@tailwindcss/oxide-win32-arm64-msvc": "4.3.0", "@tailwindcss/oxide-win32-x64-msvc": "4.3.0" } }, "sha512-F7HZGBeN9I0/AuuJS5PwcD8xayx5ri5GhjYUDBEVYUkexyA/giwbDNjRVrxSezE3T250OU2K/wp/ltWx3UOefg=="], + + "@tailwindcss/oxide-android-arm64": ["@tailwindcss/oxide-android-arm64@4.3.0", "", { "os": "android", "cpu": "arm64" }, "sha512-TJPiq67tKlLuObP6RkwvVGDoxCMBVtDgKkLfa/uyj7/FyxvQwHS+UOnVrXXgbEsfUaMgiVvC4KbJnRr26ho4Ng=="], + + "@tailwindcss/oxide-darwin-arm64": ["@tailwindcss/oxide-darwin-arm64@4.3.0", "", { "os": "darwin", "cpu": "arm64" }, "sha512-oMN/WZRb+SO37BmUElEgeEWuU8E/HXRkiODxJxLe1UTHVXLrdVSgfaJV7pSlhRGMSOiXLuxTIjfsF3wYvz8cgQ=="], + + "@tailwindcss/oxide-darwin-x64": ["@tailwindcss/oxide-darwin-x64@4.3.0", "", { "os": "darwin", "cpu": "x64" }, "sha512-N6CUmu4a6bKVADfw77p+iw6Yd9Q3OBhe0veaDX+QazfuVYlQsHfDgxBrsjQ/IW+zywL8mTrNd0SdJT/zgtvMdA=="], + + "@tailwindcss/oxide-freebsd-x64": ["@tailwindcss/oxide-freebsd-x64@4.3.0", "", { "os": "freebsd", "cpu": "x64" }, "sha512-zDL5hBkQdH5C6MpqbK3gQAgP80tsMwSI26vjOzjJtNCMUo0lFgOItzHKBIupOZNQxt3ouPH7RPhvNhiTfCe5CQ=="], + + "@tailwindcss/oxide-linux-arm-gnueabihf": ["@tailwindcss/oxide-linux-arm-gnueabihf@4.3.0", "", { "os": "linux", "cpu": "arm" }, "sha512-R06HdNi7A7OEoMsf6d4tjZ71RCWnZQPHj2mnotSFURjNLdBC+cIgXQ7l81CqeoiQftjf6OOblxXMInMgN2VzMA=="], + + "@tailwindcss/oxide-linux-arm64-gnu": ["@tailwindcss/oxide-linux-arm64-gnu@4.3.0", "", { "os": "linux", "cpu": "arm64" }, "sha512-qTJHELX8jetjhRQHCLilkVLmybpzNQAtaI/gaoVoidn/ufbNDbAo8KlK2J+yPoc8wQxvDxCmh/5lr8nC1+lTbg=="], + + "@tailwindcss/oxide-linux-arm64-musl": ["@tailwindcss/oxide-linux-arm64-musl@4.3.0", "", { "os": "linux", "cpu": "arm64" }, "sha512-Z6sukiQsngnWO+l39X4pPbiWT81IC+PLKF+PHxIlyZbGNb9MODfYlXEVlFvej5BOZInWX01kVyzeLvHsXhfczQ=="], + + "@tailwindcss/oxide-linux-x64-gnu": ["@tailwindcss/oxide-linux-x64-gnu@4.3.0", "", { "os": "linux", "cpu": "x64" }, "sha512-DRNdQRpSGzRGfARVuVkxvM8Q12nh19l4BF/G7zGA1oe+9wcC6saFBHTISrpIcKzhiXtSrlSrluCfvMuledoCTQ=="], + + "@tailwindcss/oxide-linux-x64-musl": ["@tailwindcss/oxide-linux-x64-musl@4.3.0", "", { "os": "linux", "cpu": "x64" }, "sha512-Z0IADbDo8bh6I7h2IQMx601AdXBLfFpEdUotft86evd/8ZPflZe9COPO8Q1vw+pfLWIUo9zN/JGZvwuAJqduqg=="], + + "@tailwindcss/oxide-wasm32-wasi": ["@tailwindcss/oxide-wasm32-wasi@4.3.0", "", { "dependencies": { "@emnapi/core": "^1.10.0", "@emnapi/runtime": "^1.10.0", "@emnapi/wasi-threads": "^1.2.1", "@napi-rs/wasm-runtime": "^1.1.4", "@tybys/wasm-util": "^0.10.1", "tslib": "^2.8.1" }, "cpu": "none" }, "sha512-HNZGOUxEmElksYR7S6sC5jTeNGpobAsy9u7Gu0AskJ8/20FR9GqebUyB+HBcU/ax6BHuiuJi+Oda4B+YX6H1yA=="], + + "@tailwindcss/oxide-win32-arm64-msvc": ["@tailwindcss/oxide-win32-arm64-msvc@4.3.0", "", { "os": "win32", "cpu": "arm64" }, "sha512-Pe+RPVTi1T+qymuuRpcdvwSVZjnll/f7n8gBxMMh3xLTctMDKqpdfGimbMyioqtLhUYZxdJ9wGNhV7MKHvgZsQ=="], + + "@tailwindcss/oxide-win32-x64-msvc": ["@tailwindcss/oxide-win32-x64-msvc@4.3.0", "", { "os": "win32", "cpu": "x64" }, "sha512-Mvrf2kXW/yeW/OTezZlCGOirXRcUuLIBx/5Y12BaPM7wJoryG6dfS/NJL8aBPqtTEx/Vm4T4vKzFUcKDT+TKUA=="], + + "@tailwindcss/postcss": ["@tailwindcss/postcss@4.3.0", "", { "dependencies": { "@alloc/quick-lru": "^5.2.0", "@tailwindcss/node": "4.3.0", "@tailwindcss/oxide": "4.3.0", "postcss": "^8.5.10", "tailwindcss": "4.3.0" } }, "sha512-Jm05Tjx+9yCLGv5qw1c+84Psds8MnyrEQYCB+FFk2lgGiUjlRqdxke4mVTuYrj2xnVZqKim2Apr5ySuQRYAw/w=="], + + "@tanstack/query-core": ["@tanstack/query-core@5.100.10", "", {}, "sha512-8UR0yJR+GiQ40m3lPhUr0xbfAupe6GSQiksSBSa9SM2NjezFyxXCIA69/lz8cSoNKZLrw1/PktIyQBJcVeMi3w=="], + + "@tanstack/query-devtools": ["@tanstack/query-devtools@5.100.10", "", {}, "sha512-3DmJf25hDPus5IpVvp6ujXv6bKV2zPzI9vpbAmpJigsL/H6DPvPjmf7/Q9yVKEke//8fgeQ45abjgnLuyYxAiw=="], + + "@tanstack/react-query": ["@tanstack/react-query@5.100.10", "", { "dependencies": { "@tanstack/query-core": "5.100.10" }, "peerDependencies": { "react": "^18 || ^19" } }, "sha512-FLaZf2RCrA/Zgp4aiu5tG3TyasTRO7aZ99skxQpr3Hg/zXOhu6yq5FZCYQ/tRaJtM9ylnoK8tFK7PolXQadv6Q=="], + + "@tanstack/react-query-devtools": ["@tanstack/react-query-devtools@5.100.10", "", { "dependencies": { "@tanstack/query-devtools": "5.100.10" }, "peerDependencies": { "@tanstack/react-query": "^5.100.10", "react": "^18 || ^19" } }, "sha512-zes0+o9ef5rAZXJ9f/SeaLs2nufJaeVkZkl/Or9NGrWVF41kL9Od9ED9nCwtQlgiF2VGtrzhEw5AU/igAO+aAg=="], + + "@tybys/wasm-util": ["@tybys/wasm-util@0.10.2", "", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-RoBvJ2X0wuKlWFIjrwffGw1IqZHKQqzIchKaadZZfnNpsAYp2mM0h36JtPCjNDAHGgYez/15uMBpfGwchhiMgg=="], + + "@types/chai": ["@types/chai@5.2.3", "", { "dependencies": { "@types/deep-eql": "*", "assertion-error": "^2.0.1" } }, "sha512-Mw558oeA9fFbv65/y4mHtXDs9bPnFMZAL/jxdPFUpOHHIXX91mcgEHbS5Lahr+pwZFR8A7GQleRWeI6cGFC2UA=="], + + "@types/deep-eql": ["@types/deep-eql@4.0.2", "", {}, "sha512-c9h9dVVMigMPc4bwTvC5dxqtqJZwQPePsWjPlpSOnojbor6pGqdk541lfA7AqFQr5pB1BRdq0juY9db81BwyFw=="], + + "@types/estree": ["@types/estree@1.0.9", "", {}, "sha512-GhdPgy1el4/ImP05X05Uw4cw2/M93BCUmnEvWZNStlCzEKME4Fkk+YpoA5OiHNQmoS7Cafb8Xa3Pya8m1Qrzeg=="], + + "@types/json-schema": ["@types/json-schema@7.0.15", "", {}, "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA=="], + + "@types/json5": ["@types/json5@0.0.29", "", {}, "sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ=="], + + "@types/node": ["@types/node@20.19.41", "", { "dependencies": { "undici-types": "~6.21.0" } }, "sha512-ECymXOukMnOoVkC2bb1Vc/w/836DXncOg5m8Xj1RH7xSHZJWNYY6Zh7EH477vcnD5egKNNfy2RpNOmuChhFPgQ=="], + + "@types/react": ["@types/react@19.2.14", "", { "dependencies": { "csstype": "^3.2.2" } }, "sha512-ilcTH/UniCkMdtexkoCN0bI7pMcJDvmQFPvuPvmEaYA/NSfFTAgdUSLAoVjaRJm7+6PvcM+q1zYOwS4wTYMF9w=="], + + "@types/react-dom": ["@types/react-dom@19.2.3", "", { "peerDependencies": { "@types/react": "^19.2.0" } }, "sha512-jp2L/eY6fn+KgVVQAOqYItbF0VY/YApe5Mz2F0aykSO8gx31bYCZyvSeYxCHKvzHG5eZjc+zyaS5BrBWya2+kQ=="], + + "@typescript-eslint/eslint-plugin": ["@typescript-eslint/eslint-plugin@8.59.3", "", { "dependencies": { "@eslint-community/regexpp": "^4.12.2", "@typescript-eslint/scope-manager": "8.59.3", "@typescript-eslint/type-utils": "8.59.3", "@typescript-eslint/utils": "8.59.3", "@typescript-eslint/visitor-keys": "8.59.3", "ignore": "^7.0.5", "natural-compare": "^1.4.0", "ts-api-utils": "^2.5.0" }, "peerDependencies": { "@typescript-eslint/parser": "^8.59.3", "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", "typescript": ">=4.8.4 <6.1.0" } }, "sha512-PwFvSKsXGShKGW6n5bZOhGHEcCZXM8HofLK9fNsEwZXzFRjoY+XT1Vsf1zgyXdwTr0ZYz1/2tkZ0DBTT9jZjhw=="], + + "@typescript-eslint/parser": ["@typescript-eslint/parser@8.59.3", "", { "dependencies": { "@typescript-eslint/scope-manager": "8.59.3", "@typescript-eslint/types": "8.59.3", "@typescript-eslint/typescript-estree": "8.59.3", "@typescript-eslint/visitor-keys": "8.59.3", "debug": "^4.4.3" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", "typescript": ">=4.8.4 <6.1.0" } }, "sha512-HPwA+hVkfcriajbNvTmZv4VRauibay+cWArYUYq7u7W7PmGShMxbPxLvrwDme55a6d5alG3nrYfhyJ/G28XlLg=="], + + "@typescript-eslint/project-service": ["@typescript-eslint/project-service@8.59.3", "", { "dependencies": { "@typescript-eslint/tsconfig-utils": "^8.59.3", "@typescript-eslint/types": "^8.59.3", "debug": "^4.4.3" }, "peerDependencies": { "typescript": ">=4.8.4 <6.1.0" } }, "sha512-ECiUWa/KYRGDFUqTNehaRgzDshnJfkTABJxVemHk4ko22gcr0ukloKjWvyQ64g8YCV/UI47kN1dbmjf/GaQYng=="], + + "@typescript-eslint/scope-manager": ["@typescript-eslint/scope-manager@8.59.3", "", { "dependencies": { "@typescript-eslint/types": "8.59.3", "@typescript-eslint/visitor-keys": "8.59.3" } }, "sha512-t2LvZnoEfzKtnPjgeEu41xw5gxq9mQVfYy4OoZ4Vlt0sk3JwxmhCca/AR7DwOiHrjWgjAj6as4AhRLKSDfvZIA=="], + + "@typescript-eslint/tsconfig-utils": ["@typescript-eslint/tsconfig-utils@8.59.3", "", { "peerDependencies": { "typescript": ">=4.8.4 <6.1.0" } }, "sha512-PcIJHjmaREXLgIAIzLnSY9VucEzz8FKXsRgFa1DmdGCK/5tJpW03TKJF01Q6VZd1lLdz2sIKPWaDUZN9dp//dw=="], + + "@typescript-eslint/type-utils": ["@typescript-eslint/type-utils@8.59.3", "", { "dependencies": { "@typescript-eslint/types": "8.59.3", "@typescript-eslint/typescript-estree": "8.59.3", "@typescript-eslint/utils": "8.59.3", "debug": "^4.4.3", "ts-api-utils": "^2.5.0" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", "typescript": ">=4.8.4 <6.1.0" } }, "sha512-g71d8QD8UaiHGvrJwyIS1hCX5r63w6Jll+4VEYhEAHXTDIqX1JgxhTAbEHtKntL9kuc4jRo7/GWw5xfCepSccQ=="], + + "@typescript-eslint/types": ["@typescript-eslint/types@8.59.3", "", {}, "sha512-ePFoH0g4ludssdRFqqDxQePCxU4WQyRa9+XVwjm7yLn0FKhMeoetC+qBEEI1Eyb1pGSDveTIT09Bvw2WhlGayg=="], + + "@typescript-eslint/typescript-estree": ["@typescript-eslint/typescript-estree@8.59.3", "", { "dependencies": { "@typescript-eslint/project-service": "8.59.3", "@typescript-eslint/tsconfig-utils": "8.59.3", "@typescript-eslint/types": "8.59.3", "@typescript-eslint/visitor-keys": "8.59.3", "debug": "^4.4.3", "minimatch": "^10.2.2", "semver": "^7.7.3", "tinyglobby": "^0.2.15", "ts-api-utils": "^2.5.0" }, "peerDependencies": { "typescript": ">=4.8.4 <6.1.0" } }, "sha512-CbRjVRAf7Lr9Kr8RopKcbY45p2VfmmHrm0ygOCYFi7oU8q19m0Fs/6iHS7kNOmwpp+ob07ZVcAqlxUod9lYdmg=="], + + "@typescript-eslint/utils": ["@typescript-eslint/utils@8.59.3", "", { "dependencies": { "@eslint-community/eslint-utils": "^4.9.1", "@typescript-eslint/scope-manager": "8.59.3", "@typescript-eslint/types": "8.59.3", "@typescript-eslint/typescript-estree": "8.59.3" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", "typescript": ">=4.8.4 <6.1.0" } }, "sha512-JAvT14goBzRzzzZyqq3P9BLArIxTtQURUtFgQ/V7FO+eU+Gg6ES+5ymOPP1wRxXcxAYeivCk4uS3jCKWI1K8Zg=="], + + "@typescript-eslint/visitor-keys": ["@typescript-eslint/visitor-keys@8.59.3", "", { "dependencies": { "@typescript-eslint/types": "8.59.3", "eslint-visitor-keys": "^5.0.0" } }, "sha512-f1UQF7ggd42YiwI5wGrRaPsa+P0CINBlrkLPmGfpq/u/I/oVtecoEIfFR9ag/oa1sLOsRNZ6xehf6qMZhQGBDg=="], + + "@unrs/resolver-binding-android-arm-eabi": ["@unrs/resolver-binding-android-arm-eabi@1.11.1", "", { "os": "android", "cpu": "arm" }, "sha512-ppLRUgHVaGRWUx0R0Ut06Mjo9gBaBkg3v/8AxusGLhsIotbBLuRk51rAzqLC8gq6NyyAojEXglNjzf6R948DNw=="], + + "@unrs/resolver-binding-android-arm64": ["@unrs/resolver-binding-android-arm64@1.11.1", "", { "os": "android", "cpu": "arm64" }, "sha512-lCxkVtb4wp1v+EoN+HjIG9cIIzPkX5OtM03pQYkG+U5O/wL53LC4QbIeazgiKqluGeVEeBlZahHalCaBvU1a2g=="], + + "@unrs/resolver-binding-darwin-arm64": ["@unrs/resolver-binding-darwin-arm64@1.11.1", "", { "os": "darwin", "cpu": "arm64" }, "sha512-gPVA1UjRu1Y/IsB/dQEsp2V1pm44Of6+LWvbLc9SDk1c2KhhDRDBUkQCYVWe6f26uJb3fOK8saWMgtX8IrMk3g=="], + + "@unrs/resolver-binding-darwin-x64": ["@unrs/resolver-binding-darwin-x64@1.11.1", "", { "os": "darwin", "cpu": "x64" }, "sha512-cFzP7rWKd3lZaCsDze07QX1SC24lO8mPty9vdP+YVa3MGdVgPmFc59317b2ioXtgCMKGiCLxJ4HQs62oz6GfRQ=="], + + "@unrs/resolver-binding-freebsd-x64": ["@unrs/resolver-binding-freebsd-x64@1.11.1", "", { "os": "freebsd", "cpu": "x64" }, "sha512-fqtGgak3zX4DCB6PFpsH5+Kmt/8CIi4Bry4rb1ho6Av2QHTREM+47y282Uqiu3ZRF5IQioJQ5qWRV6jduA+iGw=="], + + "@unrs/resolver-binding-linux-arm-gnueabihf": ["@unrs/resolver-binding-linux-arm-gnueabihf@1.11.1", "", { "os": "linux", "cpu": "arm" }, "sha512-u92mvlcYtp9MRKmP+ZvMmtPN34+/3lMHlyMj7wXJDeXxuM0Vgzz0+PPJNsro1m3IZPYChIkn944wW8TYgGKFHw=="], + + "@unrs/resolver-binding-linux-arm-musleabihf": ["@unrs/resolver-binding-linux-arm-musleabihf@1.11.1", "", { "os": "linux", "cpu": "arm" }, "sha512-cINaoY2z7LVCrfHkIcmvj7osTOtm6VVT16b5oQdS4beibX2SYBwgYLmqhBjA1t51CarSaBuX5YNsWLjsqfW5Cw=="], + + "@unrs/resolver-binding-linux-arm64-gnu": ["@unrs/resolver-binding-linux-arm64-gnu@1.11.1", "", { "os": "linux", "cpu": "arm64" }, "sha512-34gw7PjDGB9JgePJEmhEqBhWvCiiWCuXsL9hYphDF7crW7UgI05gyBAi6MF58uGcMOiOqSJ2ybEeCvHcq0BCmQ=="], + + "@unrs/resolver-binding-linux-arm64-musl": ["@unrs/resolver-binding-linux-arm64-musl@1.11.1", "", { "os": "linux", "cpu": "arm64" }, "sha512-RyMIx6Uf53hhOtJDIamSbTskA99sPHS96wxVE/bJtePJJtpdKGXO1wY90oRdXuYOGOTuqjT8ACccMc4K6QmT3w=="], + + "@unrs/resolver-binding-linux-ppc64-gnu": ["@unrs/resolver-binding-linux-ppc64-gnu@1.11.1", "", { "os": "linux", "cpu": "ppc64" }, "sha512-D8Vae74A4/a+mZH0FbOkFJL9DSK2R6TFPC9M+jCWYia/q2einCubX10pecpDiTmkJVUH+y8K3BZClycD8nCShA=="], + + "@unrs/resolver-binding-linux-riscv64-gnu": ["@unrs/resolver-binding-linux-riscv64-gnu@1.11.1", "", { "os": "linux", "cpu": "none" }, "sha512-frxL4OrzOWVVsOc96+V3aqTIQl1O2TjgExV4EKgRY09AJ9leZpEg8Ak9phadbuX0BA4k8U5qtvMSQQGGmaJqcQ=="], + + "@unrs/resolver-binding-linux-riscv64-musl": ["@unrs/resolver-binding-linux-riscv64-musl@1.11.1", "", { "os": "linux", "cpu": "none" }, "sha512-mJ5vuDaIZ+l/acv01sHoXfpnyrNKOk/3aDoEdLO/Xtn9HuZlDD6jKxHlkN8ZhWyLJsRBxfv9GYM2utQ1SChKew=="], + + "@unrs/resolver-binding-linux-s390x-gnu": ["@unrs/resolver-binding-linux-s390x-gnu@1.11.1", "", { "os": "linux", "cpu": "s390x" }, "sha512-kELo8ebBVtb9sA7rMe1Cph4QHreByhaZ2QEADd9NzIQsYNQpt9UkM9iqr2lhGr5afh885d/cB5QeTXSbZHTYPg=="], + + "@unrs/resolver-binding-linux-x64-gnu": ["@unrs/resolver-binding-linux-x64-gnu@1.11.1", "", { "os": "linux", "cpu": "x64" }, "sha512-C3ZAHugKgovV5YvAMsxhq0gtXuwESUKc5MhEtjBpLoHPLYM+iuwSj3lflFwK3DPm68660rZ7G8BMcwSro7hD5w=="], + + "@unrs/resolver-binding-linux-x64-musl": ["@unrs/resolver-binding-linux-x64-musl@1.11.1", "", { "os": "linux", "cpu": "x64" }, "sha512-rV0YSoyhK2nZ4vEswT/QwqzqQXw5I6CjoaYMOX0TqBlWhojUf8P94mvI7nuJTeaCkkds3QE4+zS8Ko+GdXuZtA=="], + + "@unrs/resolver-binding-wasm32-wasi": ["@unrs/resolver-binding-wasm32-wasi@1.11.1", "", { "dependencies": { "@napi-rs/wasm-runtime": "^0.2.11" }, "cpu": "none" }, "sha512-5u4RkfxJm+Ng7IWgkzi3qrFOvLvQYnPBmjmZQ8+szTK/b31fQCnleNl1GgEt7nIsZRIf5PLhPwT0WM+q45x/UQ=="], + + "@unrs/resolver-binding-win32-arm64-msvc": ["@unrs/resolver-binding-win32-arm64-msvc@1.11.1", "", { "os": "win32", "cpu": "arm64" }, "sha512-nRcz5Il4ln0kMhfL8S3hLkxI85BXs3o8EYoattsJNdsX4YUU89iOkVn7g0VHSRxFuVMdM4Q1jEpIId1Ihim/Uw=="], + + "@unrs/resolver-binding-win32-ia32-msvc": ["@unrs/resolver-binding-win32-ia32-msvc@1.11.1", "", { "os": "win32", "cpu": "ia32" }, "sha512-DCEI6t5i1NmAZp6pFonpD5m7i6aFrpofcp4LA2i8IIq60Jyo28hamKBxNrZcyOwVOZkgsRp9O2sXWBWP8MnvIQ=="], + + "@unrs/resolver-binding-win32-x64-msvc": ["@unrs/resolver-binding-win32-x64-msvc@1.11.1", "", { "os": "win32", "cpu": "x64" }, "sha512-lrW200hZdbfRtztbygyaq/6jP6AKE8qQN2KvPcJ+x7wiD038YtnYtZ82IMNJ69GJibV7bwL3y9FgK+5w/pYt6g=="], + + "@vercel/speed-insights": ["@vercel/speed-insights@2.0.0", "", { "peerDependencies": { "@sveltejs/kit": "^1 || ^2", "next": ">= 13", "nuxt": ">= 3", "react": "^18 || ^19 || ^19.0.0-rc", "svelte": ">= 4", "vue": "^3", "vue-router": "^4" }, "optionalPeers": ["@sveltejs/kit", "next", "nuxt", "react", "svelte", "vue", "vue-router"] }, "sha512-jwkNcrTeafWxjmWq4AHBaptSqZiJkYU5adLC9QBSqeim0GcqDMgN5Ievh8OG1rJ6W3A4l1oiP7qr9CWxGuzu3w=="], + + "@vitejs/plugin-react": ["@vitejs/plugin-react@6.0.2", "", { "dependencies": { "@rolldown/pluginutils": "^1.0.0" }, "peerDependencies": { "@rolldown/plugin-babel": "^0.1.7 || ^0.2.0", "babel-plugin-react-compiler": "^1.0.0", "vite": "^8.0.0" }, "optionalPeers": ["@rolldown/plugin-babel", "babel-plugin-react-compiler"] }, "sha512-DlSMqo4WhThw4vB8Mpn0Woe9J+Jfq1geJ61AKW0QEgLzGMNwtIMdxbDUzLxcun8W7NbJO0e2Jg/Nxm3cCSVzzg=="], + + "@vitest/expect": ["@vitest/expect@4.1.6", "", { "dependencies": { "@standard-schema/spec": "^1.1.0", "@types/chai": "^5.2.2", "@vitest/spy": "4.1.6", "@vitest/utils": "4.1.6", "chai": "^6.2.2", "tinyrainbow": "^3.1.0" } }, "sha512-7EHDquPthALSV0jhhjgEW8FXaviMx7rSqu8W6oqCoAuOhKov814P99QDV1pxMA3QPv21YudvJngIhjrNI4opLg=="], + + "@vitest/mocker": ["@vitest/mocker@4.1.6", "", { "dependencies": { "@vitest/spy": "4.1.6", "estree-walker": "^3.0.3", "magic-string": "^0.30.21" }, "peerDependencies": { "msw": "^2.4.9", "vite": "^6.0.0 || ^7.0.0 || ^8.0.0" }, "optionalPeers": ["msw", "vite"] }, "sha512-MCFc63czMjEInOlcY2cpQCvCN+KgbAn+60xu9cMgP4sKaLC5JNAKw7JH8QdAnoAC88hW1IiSNZ+GgVXlN1UcMQ=="], + + "@vitest/pretty-format": ["@vitest/pretty-format@4.1.6", "", { "dependencies": { "tinyrainbow": "^3.1.0" } }, "sha512-h5SxD/IzNhZYnrSZRsUZQIC+vD0GY8cUvq0iwsmkFKixRCKLLWqCXa/FIQ4S1R+sI+PGoojkHsdNrbZiM9Qpgw=="], + + "@vitest/runner": ["@vitest/runner@4.1.6", "", { "dependencies": { "@vitest/utils": "4.1.6", "pathe": "^2.0.3" } }, "sha512-nOPCmn2+yD0ZNmKdsXGv/UxMMWbMuKeD6GyYncNwdkYDxpQvrPSKYj2rWuDjC2Y4b6w6hjip5dBKFzEUuZe3vA=="], + + "@vitest/snapshot": ["@vitest/snapshot@4.1.6", "", { "dependencies": { "@vitest/pretty-format": "4.1.6", "@vitest/utils": "4.1.6", "magic-string": "^0.30.21", "pathe": "^2.0.3" } }, "sha512-YhsdE6xAVfTDmzjxL2ZDUvjj+ZsgyOKe+TdQzqkD72wIOmHka8NuGQ6NpTNZv9D2Z63fbwWKJPeVpEw4EQgYxw=="], + + "@vitest/spy": ["@vitest/spy@4.1.6", "", {}, "sha512-JFKxMx6udhwKh/Ldo270e17QX710vgunMkuPAvXjHSvC6oqLWAHhVhjg/I71q0u0CBSErIODV1Kjv0FQNSWjdg=="], + + "@vitest/utils": ["@vitest/utils@4.1.6", "", { "dependencies": { "@vitest/pretty-format": "4.1.6", "convert-source-map": "^2.0.0", "tinyrainbow": "^3.1.0" } }, "sha512-FxIY+U81R3LGKCxaHHFRQ5+g6/iRgGLmeHWdp2Amj4ljQRrEIWHmZyDfDYBRZlpyqA7qKxtS9DD1dhk8RnRIVQ=="], + + "acorn": ["acorn@8.16.0", "", { "bin": { "acorn": "bin/acorn" } }, "sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw=="], + + "acorn-jsx": ["acorn-jsx@5.3.2", "", { "peerDependencies": { "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" } }, "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ=="], + + "ajv": ["ajv@6.15.0", "", { "dependencies": { "fast-deep-equal": "^3.1.1", "fast-json-stable-stringify": "^2.0.0", "json-schema-traverse": "^0.4.1", "uri-js": "^4.2.2" } }, "sha512-fgFx7Hfoq60ytK2c7DhnF8jIvzYgOMxfugjLOSMHjLIPgenqa7S7oaagATUq99mV6IYvN2tRmC0wnTYX6iPbMw=="], + + "ansi-escapes": ["ansi-escapes@7.3.0", "", { "dependencies": { "environment": "^1.0.0" } }, "sha512-BvU8nYgGQBxcmMuEeUEmNTvrMVjJNSH7RgW24vXexN4Ven6qCvy4TntnvlnwnMLTVlcRQQdbRY8NKnaIoeWDNg=="], + + "ansi-regex": ["ansi-regex@6.2.2", "", {}, "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg=="], + + "ansi-styles": ["ansi-styles@4.3.0", "", { "dependencies": { "color-convert": "^2.0.1" } }, "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg=="], + + "argparse": ["argparse@2.0.1", "", {}, "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q=="], + + "aria-hidden": ["aria-hidden@1.2.6", "", { "dependencies": { "tslib": "^2.0.0" } }, "sha512-ik3ZgC9dY/lYVVM++OISsaYDeg1tb0VtP5uL3ouh1koGOaUMDPpbFIei4JkFimWUFPn90sbMNMXQAIVOlnYKJA=="], + + "aria-query": ["aria-query@5.3.2", "", {}, "sha512-COROpnaoap1E2F000S62r6A60uHZnmlvomhfyT2DlTcrY1OrBKn2UhH7qn5wTC9zMvD0AY7csdPSNwKP+7WiQw=="], + + "array-buffer-byte-length": ["array-buffer-byte-length@1.0.2", "", { "dependencies": { "call-bound": "^1.0.3", "is-array-buffer": "^3.0.5" } }, "sha512-LHE+8BuR7RYGDKvnrmcuSq3tDcKv9OFEXQt/HpbZhY7V6h0zlUXutnAD82GiFx9rdieCMjkvtcsPqBwgUl1Iiw=="], + + "array-ify": ["array-ify@1.0.0", "", {}, "sha512-c5AMf34bKdvPhQ7tBGhqkgKNUzMr4WUs+WDtC2ZUGOUncbxKMTvqxYctiseW3+L4bA8ec+GcZ6/A/FW4m8ukng=="], + + "array-includes": ["array-includes@3.1.9", "", { "dependencies": { "call-bind": "^1.0.8", "call-bound": "^1.0.4", "define-properties": "^1.2.1", "es-abstract": "^1.24.0", "es-object-atoms": "^1.1.1", "get-intrinsic": "^1.3.0", "is-string": "^1.1.1", "math-intrinsics": "^1.1.0" } }, "sha512-FmeCCAenzH0KH381SPT5FZmiA/TmpndpcaShhfgEN9eCVjnFBqq3l1xrI42y8+PPLI6hypzou4GXw00WHmPBLQ=="], + + "array.prototype.findlast": ["array.prototype.findlast@1.2.5", "", { "dependencies": { "call-bind": "^1.0.7", "define-properties": "^1.2.1", "es-abstract": "^1.23.2", "es-errors": "^1.3.0", "es-object-atoms": "^1.0.0", "es-shim-unscopables": "^1.0.2" } }, "sha512-CVvd6FHg1Z3POpBLxO6E6zr+rSKEQ9L6rZHAaY7lLfhKsWYUBBOuMs0e9o24oopj6H+geRCX0YJ+TJLBK2eHyQ=="], + + "array.prototype.findlastindex": ["array.prototype.findlastindex@1.2.6", "", { "dependencies": { "call-bind": "^1.0.8", "call-bound": "^1.0.4", "define-properties": "^1.2.1", "es-abstract": "^1.23.9", "es-errors": "^1.3.0", "es-object-atoms": "^1.1.1", "es-shim-unscopables": "^1.1.0" } }, "sha512-F/TKATkzseUExPlfvmwQKGITM3DGTK+vkAsCZoDc5daVygbJBnjEUCbgkAvVFsgfXfX4YIqZ/27G3k3tdXrTxQ=="], + + "array.prototype.flat": ["array.prototype.flat@1.3.3", "", { "dependencies": { "call-bind": "^1.0.8", "define-properties": "^1.2.1", "es-abstract": "^1.23.5", "es-shim-unscopables": "^1.0.2" } }, "sha512-rwG/ja1neyLqCuGZ5YYrznA62D4mZXg0i1cIskIUKSiqF3Cje9/wXAls9B9s1Wa2fomMsIv8czB8jZcPmxCXFg=="], + + "array.prototype.flatmap": ["array.prototype.flatmap@1.3.3", "", { "dependencies": { "call-bind": "^1.0.8", "define-properties": "^1.2.1", "es-abstract": "^1.23.5", "es-shim-unscopables": "^1.0.2" } }, "sha512-Y7Wt51eKJSyi80hFrJCePGGNo5ktJCslFuboqJsbf57CCPcm5zztluPlc4/aD8sWsKvlwatezpV4U1efk8kpjg=="], + + "array.prototype.tosorted": ["array.prototype.tosorted@1.1.4", "", { "dependencies": { "call-bind": "^1.0.7", "define-properties": "^1.2.1", "es-abstract": "^1.23.3", "es-errors": "^1.3.0", "es-shim-unscopables": "^1.0.2" } }, "sha512-p6Fx8B7b7ZhL/gmUsAy0D15WhvDccw3mnGNbZpi3pmeJdxtWsj2jEaI4Y6oo3XiHfzuSgPwKc04MYt6KgvC/wA=="], + + "arraybuffer.prototype.slice": ["arraybuffer.prototype.slice@1.0.4", "", { "dependencies": { "array-buffer-byte-length": "^1.0.1", "call-bind": "^1.0.8", "define-properties": "^1.2.1", "es-abstract": "^1.23.5", "es-errors": "^1.3.0", "get-intrinsic": "^1.2.6", "is-array-buffer": "^3.0.4" } }, "sha512-BNoCY6SXXPQ7gF2opIP4GBE+Xw7U+pHMYKuzjgCN3GwiaIR09UUeKfheyIry77QtrCBlC0KK0q5/TER/tYh3PQ=="], + + "assertion-error": ["assertion-error@2.0.1", "", {}, "sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA=="], + + "ast-types-flow": ["ast-types-flow@0.0.8", "", {}, "sha512-OH/2E5Fg20h2aPrbe+QL8JZQFko0YZaF+j4mnQ7BGhfavO7OpSLa8a0y9sBwomHdSbkhTS8TQNayBfnW5DwbvQ=="], + + "async-function": ["async-function@1.0.0", "", {}, "sha512-hsU18Ae8CDTR6Kgu9DYf0EbCr/a5iGL0rytQDobUcdpYOKokk8LEjVphnXkDkgpi0wYVsqrXuP0bZxJaTqdgoA=="], + + "available-typed-arrays": ["available-typed-arrays@1.0.7", "", { "dependencies": { "possible-typed-array-names": "^1.0.0" } }, "sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ=="], + + "axe-core": ["axe-core@4.11.4", "", {}, "sha512-KunSNx+TVpkAw/6ULfhnx+HWRecjqZGTOyquAoWHYLRSdK1tB5Ihce1ZW+UY3fj33bYAFWPu7W/GRSmmrCGuxA=="], + + "axobject-query": ["axobject-query@4.1.0", "", {}, "sha512-qIj0G9wZbMGNLjLmg1PT6v2mE9AH2zlnADJD/2tC6E00hgmhUOfEB6greHPAfLRSufHqROIUTkw6E+M3lH0PTQ=="], + + "balanced-match": ["balanced-match@1.0.2", "", {}, "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="], + + "baseline-browser-mapping": ["baseline-browser-mapping@2.10.30", "", { "bin": { "baseline-browser-mapping": "dist/cli.cjs" } }, "sha512-xjOFN16Ha1+Rz4nFYKqHU/LSB+gx/Vi3yQLX7r7sAW+Wa+8hhF2h4pvqTrTMc8+WcDBEunnUurr46Jvv0jk3Vg=="], + + "brace-expansion": ["brace-expansion@1.1.14", "", { "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" } }, "sha512-MWPGfDxnyzKU7rNOW9SP/c50vi3xrmrua/+6hfPbCS2ABNWfx24vPidzvC7krjU/RTo235sV776ymlsMtGKj8g=="], + + "braces": ["braces@3.0.3", "", { "dependencies": { "fill-range": "^7.1.1" } }, "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA=="], + + "browserslist": ["browserslist@4.28.2", "", { "dependencies": { "baseline-browser-mapping": "^2.10.12", "caniuse-lite": "^1.0.30001782", "electron-to-chromium": "^1.5.328", "node-releases": "^2.0.36", "update-browserslist-db": "^1.2.3" }, "bin": { "browserslist": "cli.js" } }, "sha512-48xSriZYYg+8qXna9kwqjIVzuQxi+KYWp2+5nCYnYKPTr0LvD89Jqk2Or5ogxz0NUMfIjhh2lIUX/LyX9B4oIg=="], + + "call-bind": ["call-bind@1.0.9", "", { "dependencies": { "call-bind-apply-helpers": "^1.0.2", "es-define-property": "^1.0.1", "get-intrinsic": "^1.3.0", "set-function-length": "^1.2.2" } }, "sha512-a/hy+pNsFUTR+Iz8TCJvXudKVLAnz/DyeSUo10I5yvFDQJBFU2s9uqQpoSrJlroHUKoKqzg+epxyP9lqFdzfBQ=="], + + "call-bind-apply-helpers": ["call-bind-apply-helpers@1.0.2", "", { "dependencies": { "es-errors": "^1.3.0", "function-bind": "^1.1.2" } }, "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ=="], + + "call-bound": ["call-bound@1.0.4", "", { "dependencies": { "call-bind-apply-helpers": "^1.0.2", "get-intrinsic": "^1.3.0" } }, "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg=="], + + "callsites": ["callsites@3.1.0", "", {}, "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ=="], + + "caniuse-lite": ["caniuse-lite@1.0.30001793", "", {}, "sha512-iwSsYWaCOoh26cV8NwNRViHlrfUvYsHDfRVcbtmw0Kg6PJIZZXwMkj1442FYLBGkeUf1juAsU3DTfxW579mrPA=="], + + "chai": ["chai@6.2.2", "", {}, "sha512-NUPRluOfOiTKBKvWPtSD4PhFvWCqOi0BGStNWs57X9js7XGTprSmFoz5F0tWhR4WPjNeR9jXqdC7/UpSJTnlRg=="], + + "chalk": ["chalk@4.1.2", "", { "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" } }, "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA=="], + + "class-variance-authority": ["class-variance-authority@0.7.1", "", { "dependencies": { "clsx": "^2.1.1" } }, "sha512-Ka+9Trutv7G8M6WT6SeiRWz792K5qEqIGEGzXKhAE6xOWAY6pPH8U+9IY3oCMv6kqTmLsv7Xh/2w2RigkePMsg=="], + + "cli-cursor": ["cli-cursor@5.0.0", "", { "dependencies": { "restore-cursor": "^5.0.0" } }, "sha512-aCj4O5wKyszjMmDT4tZj93kxyydN/K5zPWSCe6/0AV/AA1pqe5ZBIw0a2ZfPQV7lL5/yb5HsUreJ6UFAF1tEQw=="], + + "cli-truncate": ["cli-truncate@5.2.0", "", { "dependencies": { "slice-ansi": "^8.0.0", "string-width": "^8.2.0" } }, "sha512-xRwvIOMGrfOAnM1JYtqQImuaNtDEv9v6oIYAs4LIHwTiKee8uwvIi363igssOC0O5U04i4AlENs79LQLu9tEMw=="], + + "client-only": ["client-only@0.0.1", "", {}, "sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA=="], + + "cliui": ["cliui@9.0.1", "", { "dependencies": { "string-width": "^7.2.0", "strip-ansi": "^7.1.0", "wrap-ansi": "^9.0.0" } }, "sha512-k7ndgKhwoQveBL+/1tqGJYNz097I7WOvwbmmU2AR5+magtbjPWQTS1C5vzGkBC8Ym8UWRzfKUzUUqFLypY4Q+w=="], + + "clsx": ["clsx@2.1.1", "", {}, "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA=="], + + "cmdk": ["cmdk@1.1.1", "", { "dependencies": { "@radix-ui/react-compose-refs": "^1.1.1", "@radix-ui/react-dialog": "^1.1.6", "@radix-ui/react-id": "^1.1.0", "@radix-ui/react-primitive": "^2.0.2" }, "peerDependencies": { "react": "^18 || ^19 || ^19.0.0-rc", "react-dom": "^18 || ^19 || ^19.0.0-rc" } }, "sha512-Vsv7kFaXm+ptHDMZ7izaRsP70GgrW9NBNGswt9OZaVBLlE0SNpDq8eu/VGXyF9r7M0azK3Wy7OlYXsuyYLFzHg=="], + + "color-convert": ["color-convert@2.0.1", "", { "dependencies": { "color-name": "~1.1.4" } }, "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ=="], + + "color-name": ["color-name@1.1.4", "", {}, "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="], + + "compare-func": ["compare-func@2.0.0", "", { "dependencies": { "array-ify": "^1.0.0", "dot-prop": "^5.1.0" } }, "sha512-zHig5N+tPWARooBnb0Zx1MFcdfpyJrfTJ3Y5L+IFvUm8rM74hHz66z0gw0x4tijh5CorKkKUCnW82R2vmpeCRA=="], + + "concat-map": ["concat-map@0.0.1", "", {}, "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg=="], + + "conventional-changelog-angular": ["conventional-changelog-angular@8.3.1", "", { "dependencies": { "compare-func": "^2.0.0" } }, "sha512-6gfI3otXK5Ph5DfCOI1dblr+kN3FAm5a97hYoQkqNZxOaYa5WKfXH+AnpsmS+iUH2mgVC2Cg2Qw9m5OKcmNrIg=="], + + "conventional-changelog-conventionalcommits": ["conventional-changelog-conventionalcommits@9.3.1", "", { "dependencies": { "compare-func": "^2.0.0" } }, "sha512-dTYtpIacRpcZgrvBYvBfArMmK2xvIpv2TaxM0/ZI5CBtNUzvF2x0t15HsbRABWprS6UPmvj+PzHVjSx4qAVKyw=="], + + "conventional-commits-parser": ["conventional-commits-parser@6.4.0", "", { "dependencies": { "@simple-libs/stream-utils": "^1.2.0", "meow": "^13.0.0" }, "bin": { "conventional-commits-parser": "dist/cli/index.js" } }, "sha512-tvRg7FIBNlyPzjdG8wWRlPHQJJHI7DylhtRGeU9Lq+JuoPh5BKpPRX83ZdLrvXuOSu5Eo/e7SzOQhU4Hd2Miuw=="], + + "convert-source-map": ["convert-source-map@2.0.0", "", {}, "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg=="], + + "cosmiconfig": ["cosmiconfig@9.0.1", "", { "dependencies": { "env-paths": "^2.2.1", "import-fresh": "^3.3.0", "js-yaml": "^4.1.0", "parse-json": "^5.2.0" }, "peerDependencies": { "typescript": ">=4.9.5" }, "optionalPeers": ["typescript"] }, "sha512-hr4ihw+DBqcvrsEDioRO31Z17x71pUYoNe/4h6Z0wB72p7MU7/9gH8Q3s12NFhHPfYBBOV3qyfUxmr/Yn3shnQ=="], + + "cosmiconfig-typescript-loader": ["cosmiconfig-typescript-loader@6.3.0", "", { "dependencies": { "jiti": "2.6.1" }, "peerDependencies": { "@types/node": "*", "cosmiconfig": ">=9", "typescript": ">=5" } }, "sha512-Akr82WH1Wfqatyiqpj8HDkO2o2KmJRu1FhKfSNJP3K4IdXwHfEyL7MOb62i1AGQVLtIQM+iCE9CGOtrfhR+mmA=="], + + "cross-spawn": ["cross-spawn@7.0.6", "", { "dependencies": { "path-key": "^3.1.0", "shebang-command": "^2.0.0", "which": "^2.0.1" } }, "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA=="], + + "csstype": ["csstype@3.2.3", "", {}, "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ=="], + + "damerau-levenshtein": ["damerau-levenshtein@1.0.8", "", {}, "sha512-sdQSFB7+llfUcQHUQO3+B8ERRj0Oa4w9POWMI/puGtuf7gFywGmkaLCElnudfTiKZV+NvHqL0ifzdrI8Ro7ESA=="], + + "data-view-buffer": ["data-view-buffer@1.0.2", "", { "dependencies": { "call-bound": "^1.0.3", "es-errors": "^1.3.0", "is-data-view": "^1.0.2" } }, "sha512-EmKO5V3OLXh1rtK2wgXRansaK1/mtVdTUEiEI0W8RkvgT05kfxaH29PliLnpLP73yYO6142Q72QNa8Wx/A5CqQ=="], + + "data-view-byte-length": ["data-view-byte-length@1.0.2", "", { "dependencies": { "call-bound": "^1.0.3", "es-errors": "^1.3.0", "is-data-view": "^1.0.2" } }, "sha512-tuhGbE6CfTM9+5ANGf+oQb72Ky/0+s3xKUpHvShfiz2RxMFgFPjsXuRLBVMtvMs15awe45SRb83D6wH4ew6wlQ=="], + + "data-view-byte-offset": ["data-view-byte-offset@1.0.1", "", { "dependencies": { "call-bound": "^1.0.2", "es-errors": "^1.3.0", "is-data-view": "^1.0.1" } }, "sha512-BS8PfmtDGnrgYdOonGZQdLZslWIeCGFP9tpan0hi1Co2Zr2NKADsvGYA8XxuG/4UWgJ6Cjtv+YJnB6MM69QGlQ=="], + + "date-fns": ["date-fns@4.1.0", "", {}, "sha512-Ukq0owbQXxa/U3EGtsdVBkR1w7KOQ5gIBqdH2hkvknzZPYvBxb/aa6E8L7tmjFtkwZBu3UXBbjIgPo/Ez4xaNg=="], + + "debug": ["debug@4.4.3", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA=="], + + "deep-is": ["deep-is@0.1.4", "", {}, "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ=="], + + "define-data-property": ["define-data-property@1.1.4", "", { "dependencies": { "es-define-property": "^1.0.0", "es-errors": "^1.3.0", "gopd": "^1.0.1" } }, "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A=="], + + "define-properties": ["define-properties@1.2.1", "", { "dependencies": { "define-data-property": "^1.0.1", "has-property-descriptors": "^1.0.0", "object-keys": "^1.1.1" } }, "sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg=="], + + "detect-libc": ["detect-libc@2.1.2", "", {}, "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ=="], + + "detect-node-es": ["detect-node-es@1.1.0", "", {}, "sha512-ypdmJU/TbBby2Dxibuv7ZLW3Bs1QEmM7nHjEANfohJLvE0XVujisn1qPJcZxg+qDucsr+bP6fLD1rPS3AhJ7EQ=="], + + "doctrine": ["doctrine@2.1.0", "", { "dependencies": { "esutils": "^2.0.2" } }, "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw=="], + + "dot-prop": ["dot-prop@5.3.0", "", { "dependencies": { "is-obj": "^2.0.0" } }, "sha512-QM8q3zDe58hqUqjraQOmzZ1LIH9SWQJTlEKCH4kJ2oQvLZk7RbQXvtDM2XEq3fwkV9CCvvH4LA0AV+ogFsBM2Q=="], + + "dunder-proto": ["dunder-proto@1.0.1", "", { "dependencies": { "call-bind-apply-helpers": "^1.0.1", "es-errors": "^1.3.0", "gopd": "^1.2.0" } }, "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A=="], + + "electron-to-chromium": ["electron-to-chromium@1.5.357", "", {}, "sha512-NHlTIQDK8fmVwHwuIzmXYEJ1Ewq3D9wDNc0cWXxDGysP6Pb21giwGNkxiTifyKy/4SoPuN5l6GLP1W9Sv7zB2g=="], + + "emoji-regex": ["emoji-regex@9.2.2", "", {}, "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg=="], + + "enhanced-resolve": ["enhanced-resolve@5.21.3", "", { "dependencies": { "graceful-fs": "^4.2.4", "tapable": "^2.3.3" } }, "sha512-QyL119InA+XXEkNLNTPCXPugSvOfhwv0JOlGNzvxs0hZaiHLNvXSpudUWsOlsXGWJh8G6ckCScEkVHfX3kw/2Q=="], + + "env-paths": ["env-paths@2.2.1", "", {}, "sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A=="], + + "environment": ["environment@1.1.0", "", {}, "sha512-xUtoPkMggbz0MPyPiIWr1Kp4aeWJjDZ6SMvURhimjdZgsRuDplF5/s9hcgGhyXMhs+6vpnuoiZ2kFiu3FMnS8Q=="], + + "error-ex": ["error-ex@1.3.4", "", { "dependencies": { "is-arrayish": "^0.2.1" } }, "sha512-sqQamAnR14VgCr1A618A3sGrygcpK+HEbenA/HiEAkkUwcZIIB/tgWqHFxWgOyDh4nB4JCRimh79dR5Ywc9MDQ=="], + + "es-abstract": ["es-abstract@1.24.2", "", { "dependencies": { "array-buffer-byte-length": "^1.0.2", "arraybuffer.prototype.slice": "^1.0.4", "available-typed-arrays": "^1.0.7", "call-bind": "^1.0.8", "call-bound": "^1.0.4", "data-view-buffer": "^1.0.2", "data-view-byte-length": "^1.0.2", "data-view-byte-offset": "^1.0.1", "es-define-property": "^1.0.1", "es-errors": "^1.3.0", "es-object-atoms": "^1.1.1", "es-set-tostringtag": "^2.1.0", "es-to-primitive": "^1.3.0", "function.prototype.name": "^1.1.8", "get-intrinsic": "^1.3.0", "get-proto": "^1.0.1", "get-symbol-description": "^1.1.0", "globalthis": "^1.0.4", "gopd": "^1.2.0", "has-property-descriptors": "^1.0.2", "has-proto": "^1.2.0", "has-symbols": "^1.1.0", "hasown": "^2.0.2", "internal-slot": "^1.1.0", "is-array-buffer": "^3.0.5", "is-callable": "^1.2.7", "is-data-view": "^1.0.2", "is-negative-zero": "^2.0.3", "is-regex": "^1.2.1", "is-set": "^2.0.3", "is-shared-array-buffer": "^1.0.4", "is-string": "^1.1.1", "is-typed-array": "^1.1.15", "is-weakref": "^1.1.1", "math-intrinsics": "^1.1.0", "object-inspect": "^1.13.4", "object-keys": "^1.1.1", "object.assign": "^4.1.7", "own-keys": "^1.0.1", "regexp.prototype.flags": "^1.5.4", "safe-array-concat": "^1.1.3", "safe-push-apply": "^1.0.0", "safe-regex-test": "^1.1.0", "set-proto": "^1.0.0", "stop-iteration-iterator": "^1.1.0", "string.prototype.trim": "^1.2.10", "string.prototype.trimend": "^1.0.9", "string.prototype.trimstart": "^1.0.8", "typed-array-buffer": "^1.0.3", "typed-array-byte-length": "^1.0.3", "typed-array-byte-offset": "^1.0.4", "typed-array-length": "^1.0.7", "unbox-primitive": "^1.1.0", "which-typed-array": "^1.1.19" } }, "sha512-2FpH9Q5i2RRwyEP1AylXe6nYLR5OhaJTZwmlcP0dL/+JCbgg7yyEo/sEK6HeGZRf3dFpWwThaRHVApXSkW3xeg=="], + + "es-define-property": ["es-define-property@1.0.1", "", {}, "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g=="], + + "es-errors": ["es-errors@1.3.0", "", {}, "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw=="], + + "es-iterator-helpers": ["es-iterator-helpers@1.3.2", "", { "dependencies": { "call-bind": "^1.0.9", "call-bound": "^1.0.4", "define-properties": "^1.2.1", "es-abstract": "^1.24.2", "es-errors": "^1.3.0", "es-set-tostringtag": "^2.1.0", "function-bind": "^1.1.2", "get-intrinsic": "^1.3.0", "globalthis": "^1.0.4", "gopd": "^1.2.0", "has-property-descriptors": "^1.0.2", "has-proto": "^1.2.0", "has-symbols": "^1.1.0", "internal-slot": "^1.1.0", "iterator.prototype": "^1.1.5", "math-intrinsics": "^1.1.0" } }, "sha512-HVLACW1TppGYjJ8H6/jqH/pqOtKRw6wMlrB23xfExmFWxFquAIWCmwoLsOyN96K4a5KbmOf5At9ZUO3GZbetAw=="], + + "es-module-lexer": ["es-module-lexer@2.1.0", "", {}, "sha512-n27zTYMjYu1aj4MjCWzSP7G9r75utsaoc8m61weK+W8JMBGGQybd43GstCXZ3WNmSFtGT9wi59qQTW6mhTR5LQ=="], + + "es-object-atoms": ["es-object-atoms@1.1.1", "", { "dependencies": { "es-errors": "^1.3.0" } }, "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA=="], + + "es-set-tostringtag": ["es-set-tostringtag@2.1.0", "", { "dependencies": { "es-errors": "^1.3.0", "get-intrinsic": "^1.2.6", "has-tostringtag": "^1.0.2", "hasown": "^2.0.2" } }, "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA=="], + + "es-shim-unscopables": ["es-shim-unscopables@1.1.0", "", { "dependencies": { "hasown": "^2.0.2" } }, "sha512-d9T8ucsEhh8Bi1woXCf+TIKDIROLG5WCkxg8geBCbvk22kzwC5G2OnXVMO6FUsvQlgUUXQ2itephWDLqDzbeCw=="], + + "es-to-primitive": ["es-to-primitive@1.3.0", "", { "dependencies": { "is-callable": "^1.2.7", "is-date-object": "^1.0.5", "is-symbol": "^1.0.4" } }, "sha512-w+5mJ3GuFL+NjVtJlvydShqE1eN3h3PbI7/5LAsYJP/2qtuMXjfL2LpHSRqo4b4eSF5K/DH1JXKUAHSB2UW50g=="], + + "es-toolkit": ["es-toolkit@1.46.1", "", {}, "sha512-5eNtXOs3tbfxXOj04tjjseeWkRWaoCjdEI+96DgwzZoe6c9juL49pXlzAFTI72aWC9Y8p7168g6XIKjh7k6pyQ=="], + + "escalade": ["escalade@3.2.0", "", {}, "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA=="], + + "escape-string-regexp": ["escape-string-regexp@4.0.0", "", {}, "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA=="], + + "eslint": ["eslint@9.39.4", "", { "dependencies": { "@eslint-community/eslint-utils": "^4.8.0", "@eslint-community/regexpp": "^4.12.1", "@eslint/config-array": "^0.21.2", "@eslint/config-helpers": "^0.4.2", "@eslint/core": "^0.17.0", "@eslint/eslintrc": "^3.3.5", "@eslint/js": "9.39.4", "@eslint/plugin-kit": "^0.4.1", "@humanfs/node": "^0.16.6", "@humanwhocodes/module-importer": "^1.0.1", "@humanwhocodes/retry": "^0.4.2", "@types/estree": "^1.0.6", "ajv": "^6.14.0", "chalk": "^4.0.0", "cross-spawn": "^7.0.6", "debug": "^4.3.2", "escape-string-regexp": "^4.0.0", "eslint-scope": "^8.4.0", "eslint-visitor-keys": "^4.2.1", "espree": "^10.4.0", "esquery": "^1.5.0", "esutils": "^2.0.2", "fast-deep-equal": "^3.1.3", "file-entry-cache": "^8.0.0", "find-up": "^5.0.0", "glob-parent": "^6.0.2", "ignore": "^5.2.0", "imurmurhash": "^0.1.4", "is-glob": "^4.0.0", "json-stable-stringify-without-jsonify": "^1.0.1", "lodash.merge": "^4.6.2", "minimatch": "^3.1.5", "natural-compare": "^1.4.0", "optionator": "^0.9.3" }, "peerDependencies": { "jiti": "*" }, "optionalPeers": ["jiti"], "bin": { "eslint": "bin/eslint.js" } }, "sha512-XoMjdBOwe/esVgEvLmNsD3IRHkm7fbKIUGvrleloJXUZgDHig2IPWNniv+GwjyJXzuNqVjlr5+4yVUZjycJwfQ=="], + + "eslint-config-next": ["eslint-config-next@16.2.6", "", { "dependencies": { "@next/eslint-plugin-next": "16.2.6", "eslint-import-resolver-node": "^0.3.6", "eslint-import-resolver-typescript": "^3.5.2", "eslint-plugin-import": "^2.32.0", "eslint-plugin-jsx-a11y": "^6.10.0", "eslint-plugin-react": "^7.37.0", "eslint-plugin-react-hooks": "^7.0.0", "globals": "16.4.0", "typescript-eslint": "^8.46.0" }, "peerDependencies": { "eslint": ">=9.0.0", "typescript": ">=3.3.1" }, "optionalPeers": ["typescript"] }, "sha512-z2ELYSkyrrJ6cuunTU8vhsT/RpouPkjaSah06nVW6Rg2Hpg0Vs8s497/e5s8G8qtdp4ccsiovz5P1rv+5VSW2Q=="], + + "eslint-import-resolver-node": ["eslint-import-resolver-node@0.3.10", "", { "dependencies": { "debug": "^3.2.7", "is-core-module": "^2.16.1", "resolve": "^2.0.0-next.6" } }, "sha512-tRrKqFyCaKict5hOd244sL6EQFNycnMQnBe+j8uqGNXYzsImGbGUU4ibtoaBmv5FLwJwcFJNeg1GeVjQfbMrDQ=="], + + "eslint-import-resolver-typescript": ["eslint-import-resolver-typescript@3.10.1", "", { "dependencies": { "@nolyfill/is-core-module": "1.0.39", "debug": "^4.4.0", "get-tsconfig": "^4.10.0", "is-bun-module": "^2.0.0", "stable-hash": "^0.0.5", "tinyglobby": "^0.2.13", "unrs-resolver": "^1.6.2" }, "peerDependencies": { "eslint": "*", "eslint-plugin-import": "*", "eslint-plugin-import-x": "*" }, "optionalPeers": ["eslint-plugin-import", "eslint-plugin-import-x"] }, "sha512-A1rHYb06zjMGAxdLSkN2fXPBwuSaQ0iO5M/hdyS0Ajj1VBaRp0sPD3dn1FhME3c/JluGFbwSxyCfqdSbtQLAHQ=="], + + "eslint-module-utils": ["eslint-module-utils@2.12.1", "", { "dependencies": { "debug": "^3.2.7" } }, "sha512-L8jSWTze7K2mTg0vos/RuLRS5soomksDPoJLXIslC7c8Wmut3bx7CPpJijDcBZtxQ5lrbUdM+s0OlNbz0DCDNw=="], + + "eslint-plugin-import": ["eslint-plugin-import@2.32.0", "", { "dependencies": { "@rtsao/scc": "^1.1.0", "array-includes": "^3.1.9", "array.prototype.findlastindex": "^1.2.6", "array.prototype.flat": "^1.3.3", "array.prototype.flatmap": "^1.3.3", "debug": "^3.2.7", "doctrine": "^2.1.0", "eslint-import-resolver-node": "^0.3.9", "eslint-module-utils": "^2.12.1", "hasown": "^2.0.2", "is-core-module": "^2.16.1", "is-glob": "^4.0.3", "minimatch": "^3.1.2", "object.fromentries": "^2.0.8", "object.groupby": "^1.0.3", "object.values": "^1.2.1", "semver": "^6.3.1", "string.prototype.trimend": "^1.0.9", "tsconfig-paths": "^3.15.0" }, "peerDependencies": { "eslint": "^2 || ^3 || ^4 || ^5 || ^6 || ^7.2.0 || ^8 || ^9" } }, "sha512-whOE1HFo/qJDyX4SnXzP4N6zOWn79WhnCUY/iDR0mPfQZO8wcYE4JClzI2oZrhBnnMUCBCHZhO6VQyoBU95mZA=="], + + "eslint-plugin-jsx-a11y": ["eslint-plugin-jsx-a11y@6.10.2", "", { "dependencies": { "aria-query": "^5.3.2", "array-includes": "^3.1.8", "array.prototype.flatmap": "^1.3.2", "ast-types-flow": "^0.0.8", "axe-core": "^4.10.0", "axobject-query": "^4.1.0", "damerau-levenshtein": "^1.0.8", "emoji-regex": "^9.2.2", "hasown": "^2.0.2", "jsx-ast-utils": "^3.3.5", "language-tags": "^1.0.9", "minimatch": "^3.1.2", "object.fromentries": "^2.0.8", "safe-regex-test": "^1.0.3", "string.prototype.includes": "^2.0.1" }, "peerDependencies": { "eslint": "^3 || ^4 || ^5 || ^6 || ^7 || ^8 || ^9" } }, "sha512-scB3nz4WmG75pV8+3eRUQOHZlNSUhFNq37xnpgRkCCELU3XMvXAxLk1eqWWyE22Ki4Q01Fnsw9BA3cJHDPgn2Q=="], + + "eslint-plugin-react": ["eslint-plugin-react@7.37.5", "", { "dependencies": { "array-includes": "^3.1.8", "array.prototype.findlast": "^1.2.5", "array.prototype.flatmap": "^1.3.3", "array.prototype.tosorted": "^1.1.4", "doctrine": "^2.1.0", "es-iterator-helpers": "^1.2.1", "estraverse": "^5.3.0", "hasown": "^2.0.2", "jsx-ast-utils": "^2.4.1 || ^3.0.0", "minimatch": "^3.1.2", "object.entries": "^1.1.9", "object.fromentries": "^2.0.8", "object.values": "^1.2.1", "prop-types": "^15.8.1", "resolve": "^2.0.0-next.5", "semver": "^6.3.1", "string.prototype.matchall": "^4.0.12", "string.prototype.repeat": "^1.0.0" }, "peerDependencies": { "eslint": "^3 || ^4 || ^5 || ^6 || ^7 || ^8 || ^9.7" } }, "sha512-Qteup0SqU15kdocexFNAJMvCJEfa2xUKNV4CC1xsVMrIIqEy3SQ/rqyxCWNzfrd3/ldy6HMlD2e0JDVpDg2qIA=="], + + "eslint-plugin-react-hooks": ["eslint-plugin-react-hooks@7.1.1", "", { "dependencies": { "@babel/core": "^7.24.4", "@babel/parser": "^7.24.4", "hermes-parser": "^0.25.1", "zod": "^3.25.0 || ^4.0.0", "zod-validation-error": "^3.5.0 || ^4.0.0" }, "peerDependencies": { "eslint": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0 || ^9.0.0 || ^10.0.0" } }, "sha512-f2I7Gw6JbvCexzIInuSbZpfdQ44D7iqdWX01FKLvrPgqxoE7oMj8clOfto8U6vYiz4yd5oKu39rRSVOe1zRu0g=="], + + "eslint-scope": ["eslint-scope@8.4.0", "", { "dependencies": { "esrecurse": "^4.3.0", "estraverse": "^5.2.0" } }, "sha512-sNXOfKCn74rt8RICKMvJS7XKV/Xk9kA7DyJr8mJik3S7Cwgy3qlkkmyS2uQB3jiJg6VNdZd/pDBJu0nvG2NlTg=="], + + "eslint-visitor-keys": ["eslint-visitor-keys@4.2.1", "", {}, "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ=="], + + "espree": ["espree@10.4.0", "", { "dependencies": { "acorn": "^8.15.0", "acorn-jsx": "^5.3.2", "eslint-visitor-keys": "^4.2.1" } }, "sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ=="], + + "esquery": ["esquery@1.7.0", "", { "dependencies": { "estraverse": "^5.1.0" } }, "sha512-Ap6G0WQwcU/LHsvLwON1fAQX9Zp0A2Y6Y/cJBl9r/JbW90Zyg4/zbG6zzKa2OTALELarYHmKu0GhpM5EO+7T0g=="], + + "esrecurse": ["esrecurse@4.3.0", "", { "dependencies": { "estraverse": "^5.2.0" } }, "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag=="], + + "estraverse": ["estraverse@5.3.0", "", {}, "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA=="], + + "estree-walker": ["estree-walker@3.0.3", "", { "dependencies": { "@types/estree": "^1.0.0" } }, "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g=="], + + "esutils": ["esutils@2.0.3", "", {}, "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g=="], + + "eventemitter3": ["eventemitter3@5.0.4", "", {}, "sha512-mlsTRyGaPBjPedk6Bvw+aqbsXDtoAyAzm5MO7JgU+yVRyMQ5O8bD4Kcci7BS85f93veegeCPkL8R4GLClnjLFw=="], + + "expect-type": ["expect-type@1.3.0", "", {}, "sha512-knvyeauYhqjOYvQ66MznSMs83wmHrCycNEN6Ao+2AeYEfxUIkuiVxdEa1qlGEPK+We3n0THiDciYSsCcgW/DoA=="], + + "fast-deep-equal": ["fast-deep-equal@3.1.3", "", {}, "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q=="], + + "fast-glob": ["fast-glob@3.3.1", "", { "dependencies": { "@nodelib/fs.stat": "^2.0.2", "@nodelib/fs.walk": "^1.2.3", "glob-parent": "^5.1.2", "merge2": "^1.3.0", "micromatch": "^4.0.4" } }, "sha512-kNFPyjhh5cKjrUltxs+wFx+ZkbRaxxmZ+X0ZU31SOsxCEtP9VPgtq2teZw1DebupL5GmDaNQ6yKMMVcM41iqDg=="], + + "fast-json-stable-stringify": ["fast-json-stable-stringify@2.1.0", "", {}, "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw=="], + + "fast-levenshtein": ["fast-levenshtein@2.0.6", "", {}, "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw=="], + + "fast-uri": ["fast-uri@3.1.2", "", {}, "sha512-rVjf7ArG3LTk+FS6Yw81V1DLuZl1bRbNrev6Tmd/9RaroeeRRJhAt7jg/6YFxbvAQXUCavSoZhPPj6oOx+5KjQ=="], + + "fastq": ["fastq@1.20.1", "", { "dependencies": { "reusify": "^1.0.4" } }, "sha512-GGToxJ/w1x32s/D2EKND7kTil4n8OVk/9mycTc4VDza13lOvpUZTGX3mFSCtV9ksdGBVzvsyAVLM6mHFThxXxw=="], + + "fdir": ["fdir@6.5.0", "", { "peerDependencies": { "picomatch": "^3 || ^4" }, "optionalPeers": ["picomatch"] }, "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg=="], + + "file-entry-cache": ["file-entry-cache@8.0.0", "", { "dependencies": { "flat-cache": "^4.0.0" } }, "sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ=="], + + "fill-range": ["fill-range@7.1.1", "", { "dependencies": { "to-regex-range": "^5.0.1" } }, "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg=="], + + "find-up": ["find-up@5.0.0", "", { "dependencies": { "locate-path": "^6.0.0", "path-exists": "^4.0.0" } }, "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng=="], + + "flat-cache": ["flat-cache@4.0.1", "", { "dependencies": { "flatted": "^3.2.9", "keyv": "^4.5.4" } }, "sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw=="], + + "flatted": ["flatted@3.4.2", "", {}, "sha512-PjDse7RzhcPkIJwy5t7KPWQSZ9cAbzQXcafsetQoD7sOJRQlGikNbx7yZp2OotDnJyrDcbyRq3Ttb18iYOqkxA=="], + + "for-each": ["for-each@0.3.5", "", { "dependencies": { "is-callable": "^1.2.7" } }, "sha512-dKx12eRCVIzqCxFGplyFKJMPvLEWgmNtUrpTiJIR5u97zEhRG8ySrtboPHZXx7daLxQVrl643cTzbab2tkQjxg=="], + + "framer-motion": ["framer-motion@12.38.0", "", { "dependencies": { "motion-dom": "^12.38.0", "motion-utils": "^12.36.0", "tslib": "^2.4.0" }, "peerDependencies": { "@emotion/is-prop-valid": "*", "react": "^18.0.0 || ^19.0.0", "react-dom": "^18.0.0 || ^19.0.0" }, "optionalPeers": ["@emotion/is-prop-valid", "react", "react-dom"] }, "sha512-rFYkY/pigbcswl1XQSb7q424kSTQ8q6eAC+YUsSKooHQYuLdzdHjrt6uxUC+PRAO++q5IS7+TamgIw1AphxR+g=="], + + "fsevents": ["fsevents@2.3.3", "", { "os": "darwin" }, "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw=="], + + "function-bind": ["function-bind@1.1.2", "", {}, "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA=="], + + "function.prototype.name": ["function.prototype.name@1.1.8", "", { "dependencies": { "call-bind": "^1.0.8", "call-bound": "^1.0.3", "define-properties": "^1.2.1", "functions-have-names": "^1.2.3", "hasown": "^2.0.2", "is-callable": "^1.2.7" } }, "sha512-e5iwyodOHhbMr/yNrc7fDYG4qlbIvI5gajyzPnb5TCwyhjApznQh1BMFou9b30SevY43gCJKXycoCBjMbsuW0Q=="], + + "functions-have-names": ["functions-have-names@1.2.3", "", {}, "sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ=="], + + "generator-function": ["generator-function@2.0.1", "", {}, "sha512-SFdFmIJi+ybC0vjlHN0ZGVGHc3lgE0DxPAT0djjVg+kjOnSqclqmj0KQ7ykTOLP6YxoqOvuAODGdcHJn+43q3g=="], + + "gensync": ["gensync@1.0.0-beta.2", "", {}, "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg=="], + + "get-caller-file": ["get-caller-file@2.0.5", "", {}, "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg=="], + + "get-east-asian-width": ["get-east-asian-width@1.6.0", "", {}, "sha512-QRbvDIbx6YklUe6RxeTeleMR0yv3cYH6PsPZHcnVn7xv7zO1BHN8r0XETu8n6Ye3Q+ahtSarc3WgtNWmehIBfA=="], + + "get-intrinsic": ["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.1", "function-bind": "^1.1.2", "get-proto": "^1.0.1", "gopd": "^1.2.0", "has-symbols": "^1.1.0", "hasown": "^2.0.2", "math-intrinsics": "^1.1.0" } }, "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ=="], + + "get-nonce": ["get-nonce@1.0.1", "", {}, "sha512-FJhYRoDaiatfEkUK8HKlicmu/3SGFD51q3itKDGoSTysQJBnfOcxU5GxnhE1E6soB76MbT0MBtnKJuXyAx+96Q=="], + + "get-proto": ["get-proto@1.0.1", "", { "dependencies": { "dunder-proto": "^1.0.1", "es-object-atoms": "^1.0.0" } }, "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g=="], + + "get-symbol-description": ["get-symbol-description@1.1.0", "", { "dependencies": { "call-bound": "^1.0.3", "es-errors": "^1.3.0", "get-intrinsic": "^1.2.6" } }, "sha512-w9UMqWwJxHNOvoNzSJ2oPF5wvYcvP7jUvYzhp67yEhTi17ZDBBC1z9pTdGuzjD+EFIqLSYRweZjqfiPzQ06Ebg=="], + + "get-tsconfig": ["get-tsconfig@4.14.0", "", { "dependencies": { "resolve-pkg-maps": "^1.0.0" } }, "sha512-yTb+8DXzDREzgvYmh6s9vHsSVCHeC0G3PI5bEXNBHtmshPnO+S5O7qgLEOn0I5QvMy6kpZN8K1NKGyilLb93wA=="], + + "git-raw-commits": ["git-raw-commits@5.0.1", "", { "dependencies": { "@conventional-changelog/git-client": "^2.6.0", "meow": "^13.0.0" }, "bin": { "git-raw-commits": "src/cli.js" } }, "sha512-Y+csSm2GD/PCSh6Isd/WiMjNAydu0VBiG9J7EdQsNA5P9uXvLayqjmTsNlK5Gs9IhblFZqOU0yid5Il5JPoLiQ=="], + + "glob-parent": ["glob-parent@6.0.2", "", { "dependencies": { "is-glob": "^4.0.3" } }, "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A=="], + + "global-directory": ["global-directory@5.0.0", "", { "dependencies": { "ini": "6.0.0" } }, "sha512-1pgFdhK3J2LeM+dVf2Pd424yHx2ou338lC0ErNP2hPx4j8eW1Sp0XqSjNxtk6Tc4Kr5wlWtSvz8cn2yb7/SG/w=="], + + "globals": ["globals@16.4.0", "", {}, "sha512-ob/2LcVVaVGCYN+r14cnwnoDPUufjiYgSqRhiFD0Q1iI4Odora5RE8Iv1D24hAz5oMophRGkGz+yuvQmmUMnMw=="], + + "globalthis": ["globalthis@1.0.4", "", { "dependencies": { "define-properties": "^1.2.1", "gopd": "^1.0.1" } }, "sha512-DpLKbNU4WylpxJykQujfCcwYWiV/Jhm50Goo0wrVILAv5jOr9d+H+UR3PhSCD2rCCEIg0uc+G+muBTwD54JhDQ=="], + + "gopd": ["gopd@1.2.0", "", {}, "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg=="], + + "graceful-fs": ["graceful-fs@4.2.11", "", {}, "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ=="], + + "has-bigints": ["has-bigints@1.1.0", "", {}, "sha512-R3pbpkcIqv2Pm3dUwgjclDRVmWpTJW2DcMzcIhEXEx1oh/CEMObMm3KLmRJOdvhM7o4uQBnwr8pzRK2sJWIqfg=="], + + "has-flag": ["has-flag@4.0.0", "", {}, "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ=="], + + "has-property-descriptors": ["has-property-descriptors@1.0.2", "", { "dependencies": { "es-define-property": "^1.0.0" } }, "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg=="], + + "has-proto": ["has-proto@1.2.0", "", { "dependencies": { "dunder-proto": "^1.0.0" } }, "sha512-KIL7eQPfHQRC8+XluaIw7BHUwwqL19bQn4hzNgdr+1wXoU0KKj6rufu47lhY7KbJR2C6T6+PfyN0Ea7wkSS+qQ=="], + + "has-symbols": ["has-symbols@1.1.0", "", {}, "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ=="], + + "has-tostringtag": ["has-tostringtag@1.0.2", "", { "dependencies": { "has-symbols": "^1.0.3" } }, "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw=="], + + "hasown": ["hasown@2.0.3", "", { "dependencies": { "function-bind": "^1.1.2" } }, "sha512-ej4AhfhfL2Q2zpMmLo7U1Uv9+PyhIZpgQLGT1F9miIGmiCJIoCgSmczFdrc97mWT4kVY72KA+WnnhJ5pghSvSg=="], + + "hermes-estree": ["hermes-estree@0.25.1", "", {}, "sha512-0wUoCcLp+5Ev5pDW2OriHC2MJCbwLwuRx+gAqMTOkGKJJiBCLjtrvy4PWUGn6MIVefecRpzoOZ/UV6iGdOr+Cw=="], + + "hermes-parser": ["hermes-parser@0.25.1", "", { "dependencies": { "hermes-estree": "0.25.1" } }, "sha512-6pEjquH3rqaI6cYAXYPcz9MS4rY6R4ngRgrgfDshRptUZIc3lw0MCIJIGDj9++mfySOuPTHB4nrSW99BCvOPIA=="], + + "hotkeys-js": ["hotkeys-js@4.0.4", "", {}, "sha512-hseNiqaskxSnujuGp8aRMLJfcjaFiTSS0I2GQhqru82N/sx6CGyUf6pvU5X1iycvw2EqmvILkFIb5OzYFXY+9A=="], + + "husky": ["husky@9.1.7", "", { "bin": { "husky": "bin.js" } }, "sha512-5gs5ytaNjBrh5Ow3zrvdUUY+0VxIuWVL4i9irt6friV+BqdCfmV11CQTWMiBYWHbXhco+J1kHfTOUkePhCDvMA=="], + + "icu-minify": ["icu-minify@4.12.0", "", { "dependencies": { "@formatjs/icu-messageformat-parser": "^3.4.0" } }, "sha512-zDmM05uav3t3+kxSfRrNlmyXOdj2b+uHA+p04CG32eJabtaHbugXujuL+YfRkwP9joAnf0Uh+RMGCKD5NLa5rQ=="], + + "ignore": ["ignore@5.3.2", "", {}, "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g=="], + + "import-fresh": ["import-fresh@3.3.1", "", { "dependencies": { "parent-module": "^1.0.0", "resolve-from": "^4.0.0" } }, "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ=="], + + "imurmurhash": ["imurmurhash@0.1.4", "", {}, "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA=="], + + "ini": ["ini@6.0.0", "", {}, "sha512-IBTdIkzZNOpqm7q3dRqJvMaldXjDHWkEDfrwGEQTs5eaQMWV+djAhR+wahyNNMAa+qpbDUhBMVt4ZKNwpPm7xQ=="], + + "internal-slot": ["internal-slot@1.1.0", "", { "dependencies": { "es-errors": "^1.3.0", "hasown": "^2.0.2", "side-channel": "^1.1.0" } }, "sha512-4gd7VpWNQNB4UKKCFFVcp1AVv+FMOgs9NKzjHKusc8jTMhd5eL1NqQqOpE0KzMds804/yHlglp3uxgluOqAPLw=="], + + "intl-messageformat": ["intl-messageformat@11.2.6", "", { "dependencies": { "@formatjs/fast-memoize": "3.1.5", "@formatjs/icu-messageformat-parser": "3.5.9" } }, "sha512-afAN2yNN7zjB77G1ZC5L8GtLrEshyBvOQXz88flxCO/ocTIQist98gu0r/O6H/SSiQhQsOOtWPxmCEvtDABXXQ=="], + + "is-array-buffer": ["is-array-buffer@3.0.5", "", { "dependencies": { "call-bind": "^1.0.8", "call-bound": "^1.0.3", "get-intrinsic": "^1.2.6" } }, "sha512-DDfANUiiG2wC1qawP66qlTugJeL5HyzMpfr8lLK+jMQirGzNod0B12cFB/9q838Ru27sBwfw78/rdoU7RERz6A=="], + + "is-arrayish": ["is-arrayish@0.2.1", "", {}, "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg=="], + + "is-async-function": ["is-async-function@2.1.1", "", { "dependencies": { "async-function": "^1.0.0", "call-bound": "^1.0.3", "get-proto": "^1.0.1", "has-tostringtag": "^1.0.2", "safe-regex-test": "^1.1.0" } }, "sha512-9dgM/cZBnNvjzaMYHVoxxfPj2QXt22Ev7SuuPrs+xav0ukGB0S6d4ydZdEiM48kLx5kDV+QBPrpVnFyefL8kkQ=="], + + "is-bigint": ["is-bigint@1.1.0", "", { "dependencies": { "has-bigints": "^1.0.2" } }, "sha512-n4ZT37wG78iz03xPRKJrHTdZbe3IicyucEtdRsV5yglwc3GyUfbAfpSeD0FJ41NbUNSt5wbhqfp1fS+BgnvDFQ=="], + + "is-boolean-object": ["is-boolean-object@1.2.2", "", { "dependencies": { "call-bound": "^1.0.3", "has-tostringtag": "^1.0.2" } }, "sha512-wa56o2/ElJMYqjCjGkXri7it5FbebW5usLw/nPmCMs5DeZ7eziSYZhSmPRn0txqeW4LnAmQQU7FgqLpsEFKM4A=="], + + "is-bun-module": ["is-bun-module@2.0.0", "", { "dependencies": { "semver": "^7.7.1" } }, "sha512-gNCGbnnnnFAUGKeZ9PdbyeGYJqewpmc2aKHUEMO5nQPWU9lOmv7jcmQIv+qHD8fXW6W7qfuCwX4rY9LNRjXrkQ=="], + + "is-callable": ["is-callable@1.2.7", "", {}, "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA=="], + + "is-core-module": ["is-core-module@2.16.2", "", { "dependencies": { "hasown": "^2.0.3" } }, "sha512-evOr8xfXKxE6qSR0hSXL2r3sd7ALj8+7jQEUvPYcm5sgZFdJ+AYzT6yNmJenvIYQBgIGwfwz08sL8zoL7yq2BA=="], + + "is-data-view": ["is-data-view@1.0.2", "", { "dependencies": { "call-bound": "^1.0.2", "get-intrinsic": "^1.2.6", "is-typed-array": "^1.1.13" } }, "sha512-RKtWF8pGmS87i2D6gqQu/l7EYRlVdfzemCJN/P3UOs//x1QE7mfhvzHIApBTRf7axvT6DMGwSwBXYCT0nfB9xw=="], + + "is-date-object": ["is-date-object@1.1.0", "", { "dependencies": { "call-bound": "^1.0.2", "has-tostringtag": "^1.0.2" } }, "sha512-PwwhEakHVKTdRNVOw+/Gyh0+MzlCl4R6qKvkhuvLtPMggI1WAHt9sOwZxQLSGpUaDnrdyDsomoRgNnCfKNSXXg=="], + + "is-extglob": ["is-extglob@2.1.1", "", {}, "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ=="], + + "is-finalizationregistry": ["is-finalizationregistry@1.1.1", "", { "dependencies": { "call-bound": "^1.0.3" } }, "sha512-1pC6N8qWJbWoPtEjgcL2xyhQOP491EQjeUo3qTKcmV8YSDDJrOepfG8pcC7h/QgnQHYSv0mJ3Z/ZWxmatVrysg=="], + + "is-fullwidth-code-point": ["is-fullwidth-code-point@5.1.0", "", { "dependencies": { "get-east-asian-width": "^1.3.1" } }, "sha512-5XHYaSyiqADb4RnZ1Bdad6cPp8Toise4TzEjcOYDHZkTCbKgiUl7WTUCpNWHuxmDt91wnsZBc9xinNzopv3JMQ=="], + + "is-generator-function": ["is-generator-function@1.1.2", "", { "dependencies": { "call-bound": "^1.0.4", "generator-function": "^2.0.0", "get-proto": "^1.0.1", "has-tostringtag": "^1.0.2", "safe-regex-test": "^1.1.0" } }, "sha512-upqt1SkGkODW9tsGNG5mtXTXtECizwtS2kA161M+gJPc1xdb/Ax629af6YrTwcOeQHbewrPNlE5Dx7kzvXTizA=="], + + "is-glob": ["is-glob@4.0.3", "", { "dependencies": { "is-extglob": "^2.1.1" } }, "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg=="], + + "is-map": ["is-map@2.0.3", "", {}, "sha512-1Qed0/Hr2m+YqxnM09CjA2d/i6YZNfF6R2oRAOj36eUdS6qIV/huPJNSEpKbupewFs+ZsJlxsjjPbc0/afW6Lw=="], + + "is-negative-zero": ["is-negative-zero@2.0.3", "", {}, "sha512-5KoIu2Ngpyek75jXodFvnafB6DJgr3u8uuK0LEZJjrU19DrMD3EVERaR8sjz8CCGgpZvxPl9SuE1GMVPFHx1mw=="], + + "is-number": ["is-number@7.0.0", "", {}, "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng=="], + + "is-number-object": ["is-number-object@1.1.1", "", { "dependencies": { "call-bound": "^1.0.3", "has-tostringtag": "^1.0.2" } }, "sha512-lZhclumE1G6VYD8VHe35wFaIif+CTy5SJIi5+3y4psDgWu4wPDoBhF8NxUOinEc7pHgiTsT6MaBb92rKhhD+Xw=="], + + "is-obj": ["is-obj@2.0.0", "", {}, "sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w=="], + + "is-plain-obj": ["is-plain-obj@4.1.0", "", {}, "sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg=="], + + "is-regex": ["is-regex@1.2.1", "", { "dependencies": { "call-bound": "^1.0.2", "gopd": "^1.2.0", "has-tostringtag": "^1.0.2", "hasown": "^2.0.2" } }, "sha512-MjYsKHO5O7mCsmRGxWcLWheFqN9DJ/2TmngvjKXihe6efViPqc274+Fx/4fYj/r03+ESvBdTXK0V6tA3rgez1g=="], + + "is-set": ["is-set@2.0.3", "", {}, "sha512-iPAjerrse27/ygGLxw+EBR9agv9Y6uLeYVJMu+QNCoouJ1/1ri0mGrcWpfCqFZuzzx3WjtwxG098X+n4OuRkPg=="], + + "is-shared-array-buffer": ["is-shared-array-buffer@1.0.4", "", { "dependencies": { "call-bound": "^1.0.3" } }, "sha512-ISWac8drv4ZGfwKl5slpHG9OwPNty4jOWPRIhBpxOoD+hqITiwuipOQ2bNthAzwA3B4fIjO4Nln74N0S9byq8A=="], + + "is-string": ["is-string@1.1.1", "", { "dependencies": { "call-bound": "^1.0.3", "has-tostringtag": "^1.0.2" } }, "sha512-BtEeSsoaQjlSPBemMQIrY1MY0uM6vnS1g5fmufYOtnxLGUZM2178PKbhsk7Ffv58IX+ZtcvoGwccYsh0PglkAA=="], + + "is-symbol": ["is-symbol@1.1.1", "", { "dependencies": { "call-bound": "^1.0.2", "has-symbols": "^1.1.0", "safe-regex-test": "^1.1.0" } }, "sha512-9gGx6GTtCQM73BgmHQXfDmLtfjjTUDSyoxTCbp5WtoixAhfgsDirWIcVQ/IHpvI5Vgd5i/J5F7B9cN/WlVbC/w=="], + + "is-typed-array": ["is-typed-array@1.1.15", "", { "dependencies": { "which-typed-array": "^1.1.16" } }, "sha512-p3EcsicXjit7SaskXHs1hA91QxgTw46Fv6EFKKGS5DRFLD8yKnohjF3hxoju94b/OcMZoQukzpPpBE9uLVKzgQ=="], + + "is-weakmap": ["is-weakmap@2.0.2", "", {}, "sha512-K5pXYOm9wqY1RgjpL3YTkF39tni1XajUIkawTLUo9EZEVUFga5gSQJF8nNS7ZwJQ02y+1YCNYcMh+HIf1ZqE+w=="], + + "is-weakref": ["is-weakref@1.1.1", "", { "dependencies": { "call-bound": "^1.0.3" } }, "sha512-6i9mGWSlqzNMEqpCp93KwRS1uUOodk2OJ6b+sq7ZPDSy2WuI5NFIxp/254TytR8ftefexkWn5xNiHUNpPOfSew=="], + + "is-weakset": ["is-weakset@2.0.4", "", { "dependencies": { "call-bound": "^1.0.3", "get-intrinsic": "^1.2.6" } }, "sha512-mfcwb6IzQyOKTs84CQMrOwW4gQcaTOAWJ0zzJCl2WSPDrWk/OzDaImWFH3djXhb24g4eudZfLRozAvPGw4d9hQ=="], + + "isarray": ["isarray@2.0.5", "", {}, "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw=="], + + "isexe": ["isexe@2.0.0", "", {}, "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw=="], + + "iterator.prototype": ["iterator.prototype@1.1.5", "", { "dependencies": { "define-data-property": "^1.1.4", "es-object-atoms": "^1.0.0", "get-intrinsic": "^1.2.6", "get-proto": "^1.0.0", "has-symbols": "^1.1.0", "set-function-name": "^2.0.2" } }, "sha512-H0dkQoCa3b2VEeKQBOxFph+JAbcrQdE7KC0UkqwpLmv2EC4P41QXP+rqo9wYodACiG5/WM5s9oDApTU8utwj9g=="], + + "jiti": ["jiti@2.7.0", "", { "bin": { "jiti": "lib/jiti-cli.mjs" } }, "sha512-AC/7JofJvZGrrneWNaEnJeOLUx+JlGt7tNa0wZiRPT4MY1wmfKjt2+6O2p2uz2+skll8OZZmJMNqeke7kKbNgQ=="], + + "js-tokens": ["js-tokens@4.0.0", "", {}, "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ=="], + + "js-yaml": ["js-yaml@4.1.1", "", { "dependencies": { "argparse": "^2.0.1" }, "bin": { "js-yaml": "bin/js-yaml.js" } }, "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA=="], + + "jsesc": ["jsesc@3.1.0", "", { "bin": { "jsesc": "bin/jsesc" } }, "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA=="], + + "json-buffer": ["json-buffer@3.0.1", "", {}, "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ=="], + + "json-parse-even-better-errors": ["json-parse-even-better-errors@2.3.1", "", {}, "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w=="], + + "json-schema-traverse": ["json-schema-traverse@0.4.1", "", {}, "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg=="], + + "json-stable-stringify-without-jsonify": ["json-stable-stringify-without-jsonify@1.0.1", "", {}, "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw=="], + + "json5": ["json5@1.0.2", "", { "dependencies": { "minimist": "^1.2.0" }, "bin": { "json5": "lib/cli.js" } }, "sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA=="], + + "jsx-ast-utils": ["jsx-ast-utils@3.3.5", "", { "dependencies": { "array-includes": "^3.1.6", "array.prototype.flat": "^1.3.1", "object.assign": "^4.1.4", "object.values": "^1.1.6" } }, "sha512-ZZow9HBI5O6EPgSJLUb8n2NKgmVWTwCvHGwFuJlMjvLFqlGG6pjirPhtdsseaLZjSibD8eegzmYpUZwoIlj2cQ=="], + + "keyv": ["keyv@4.5.4", "", { "dependencies": { "json-buffer": "3.0.1" } }, "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw=="], + + "language-subtag-registry": ["language-subtag-registry@0.3.23", "", {}, "sha512-0K65Lea881pHotoGEa5gDlMxt3pctLi2RplBb7Ezh4rRdLEOtgi7n4EwK9lamnUCkKBqaeKRVebTq6BAxSkpXQ=="], + + "language-tags": ["language-tags@1.0.9", "", { "dependencies": { "language-subtag-registry": "^0.3.20" } }, "sha512-MbjN408fEndfiQXbFQ1vnd+1NoLDsnQW41410oQBXiyXDMYH5z505juWa4KUE1LqxRC7DgOgZDbKLxHIwm27hA=="], + + "levn": ["levn@0.4.1", "", { "dependencies": { "prelude-ls": "^1.2.1", "type-check": "~0.4.0" } }, "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ=="], + + "lightningcss": ["lightningcss@1.32.0", "", { "dependencies": { "detect-libc": "^2.0.3" }, "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" } }, "sha512-NXYBzinNrblfraPGyrbPoD19C1h9lfI/1mzgWYvXUTe414Gz/X1FD2XBZSZM7rRTrMA8JL3OtAaGifrIKhQ5yQ=="], + + "lightningcss-android-arm64": ["lightningcss-android-arm64@1.32.0", "", { "os": "android", "cpu": "arm64" }, "sha512-YK7/ClTt4kAK0vo6w3X+Pnm0D2cf2vPHbhOXdoNti1Ga0al1P4TBZhwjATvjNwLEBCnKvjJc2jQgHXH0NEwlAg=="], + + "lightningcss-darwin-arm64": ["lightningcss-darwin-arm64@1.32.0", "", { "os": "darwin", "cpu": "arm64" }, "sha512-RzeG9Ju5bag2Bv1/lwlVJvBE3q6TtXskdZLLCyfg5pt+HLz9BqlICO7LZM7VHNTTn/5PRhHFBSjk5lc4cmscPQ=="], + + "lightningcss-darwin-x64": ["lightningcss-darwin-x64@1.32.0", "", { "os": "darwin", "cpu": "x64" }, "sha512-U+QsBp2m/s2wqpUYT/6wnlagdZbtZdndSmut/NJqlCcMLTWp5muCrID+K5UJ6jqD2BFshejCYXniPDbNh73V8w=="], + + "lightningcss-freebsd-x64": ["lightningcss-freebsd-x64@1.32.0", "", { "os": "freebsd", "cpu": "x64" }, "sha512-JCTigedEksZk3tHTTthnMdVfGf61Fky8Ji2E4YjUTEQX14xiy/lTzXnu1vwiZe3bYe0q+SpsSH/CTeDXK6WHig=="], + + "lightningcss-linux-arm-gnueabihf": ["lightningcss-linux-arm-gnueabihf@1.32.0", "", { "os": "linux", "cpu": "arm" }, "sha512-x6rnnpRa2GL0zQOkt6rts3YDPzduLpWvwAF6EMhXFVZXD4tPrBkEFqzGowzCsIWsPjqSK+tyNEODUBXeeVHSkw=="], + + "lightningcss-linux-arm64-gnu": ["lightningcss-linux-arm64-gnu@1.32.0", "", { "os": "linux", "cpu": "arm64" }, "sha512-0nnMyoyOLRJXfbMOilaSRcLH3Jw5z9HDNGfT/gwCPgaDjnx0i8w7vBzFLFR1f6CMLKF8gVbebmkUN3fa/kQJpQ=="], + + "lightningcss-linux-arm64-musl": ["lightningcss-linux-arm64-musl@1.32.0", "", { "os": "linux", "cpu": "arm64" }, "sha512-UpQkoenr4UJEzgVIYpI80lDFvRmPVg6oqboNHfoH4CQIfNA+HOrZ7Mo7KZP02dC6LjghPQJeBsvXhJod/wnIBg=="], + + "lightningcss-linux-x64-gnu": ["lightningcss-linux-x64-gnu@1.32.0", "", { "os": "linux", "cpu": "x64" }, "sha512-V7Qr52IhZmdKPVr+Vtw8o+WLsQJYCTd8loIfpDaMRWGUZfBOYEJeyJIkqGIDMZPwPx24pUMfwSxxI8phr/MbOA=="], + + "lightningcss-linux-x64-musl": ["lightningcss-linux-x64-musl@1.32.0", "", { "os": "linux", "cpu": "x64" }, "sha512-bYcLp+Vb0awsiXg/80uCRezCYHNg1/l3mt0gzHnWV9XP1W5sKa5/TCdGWaR/zBM2PeF/HbsQv/j2URNOiVuxWg=="], + + "lightningcss-win32-arm64-msvc": ["lightningcss-win32-arm64-msvc@1.32.0", "", { "os": "win32", "cpu": "arm64" }, "sha512-8SbC8BR40pS6baCM8sbtYDSwEVQd4JlFTOlaD3gWGHfThTcABnNDBda6eTZeqbofalIJhFx0qKzgHJmcPTnGdw=="], + + "lightningcss-win32-x64-msvc": ["lightningcss-win32-x64-msvc@1.32.0", "", { "os": "win32", "cpu": "x64" }, "sha512-Amq9B/SoZYdDi1kFrojnoqPLxYhQ4Wo5XiL8EVJrVsB8ARoC1PWW6VGtT0WKCemjy8aC+louJnjS7U18x3b06Q=="], + + "lines-and-columns": ["lines-and-columns@1.2.4", "", {}, "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg=="], + + "lint-staged": ["lint-staged@17.0.5", "", { "dependencies": { "listr2": "^10.2.1", "picomatch": "^4.0.4", "string-argv": "^0.3.2", "tinyexec": "^1.1.2" }, "optionalDependencies": { "yaml": "^2.8.4" }, "bin": { "lint-staged": "bin/lint-staged.js" } }, "sha512-d12yC+/e8RhBjZtaxZn71FyrgU/P5e+uAPifhCLwdosQZP/zamSdKRWDC30ocVIbzDKiFG1McHc/LUgB92GIPw=="], + + "listr2": ["listr2@10.2.1", "", { "dependencies": { "cli-truncate": "^5.2.0", "eventemitter3": "^5.0.4", "log-update": "^6.1.0", "rfdc": "^1.4.1", "wrap-ansi": "^10.0.0" } }, "sha512-7I5knELsJKTUjXG+A6BkKAiGkW1i25fNa/xlUl9hFtk15WbE9jndA89xu5FzQKrY5llajE1hfZZFMILXkDHk/Q=="], + + "locate-path": ["locate-path@6.0.0", "", { "dependencies": { "p-locate": "^5.0.0" } }, "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw=="], + + "lodash.debounce": ["lodash.debounce@4.0.8", "", {}, "sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow=="], + + "lodash.merge": ["lodash.merge@4.6.2", "", {}, "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ=="], + + "log-update": ["log-update@6.1.0", "", { "dependencies": { "ansi-escapes": "^7.0.0", "cli-cursor": "^5.0.0", "slice-ansi": "^7.1.0", "strip-ansi": "^7.1.0", "wrap-ansi": "^9.0.0" } }, "sha512-9ie8ItPR6tjY5uYJh8K/Zrv/RMZ5VOlOWvtZdEHYSTFKZfIBPQa9tOAEeAWhd+AnIneLJ22w5fjOYtoutpWq5w=="], + + "loose-envify": ["loose-envify@1.4.0", "", { "dependencies": { "js-tokens": "^3.0.0 || ^4.0.0" }, "bin": { "loose-envify": "cli.js" } }, "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q=="], + + "lru-cache": ["lru-cache@5.1.1", "", { "dependencies": { "yallist": "^3.0.2" } }, "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w=="], + + "lucide-react": ["lucide-react@1.16.0", "", { "peerDependencies": { "react": "^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, "sha512-dYwyPzb4MEKpGUmNYk3WKWPnMrHs3FKM+q94kAnJrcDIqqn1hq2xY8scaS2ovsOCM5D51ey2gaRG3PBb1vgoYQ=="], + + "magic-string": ["magic-string@0.30.21", "", { "dependencies": { "@jridgewell/sourcemap-codec": "^1.5.5" } }, "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ=="], + + "math-intrinsics": ["math-intrinsics@1.1.0", "", {}, "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g=="], + + "meow": ["meow@13.2.0", "", {}, "sha512-pxQJQzB6djGPXh08dacEloMFopsOqGVRKFPYvPOt9XDZ1HasbgDZA74CJGreSU4G3Ak7EFJGoiH2auq+yXISgA=="], + + "merge2": ["merge2@1.4.1", "", {}, "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg=="], + + "micromatch": ["micromatch@4.0.8", "", { "dependencies": { "braces": "^3.0.3", "picomatch": "^2.3.1" } }, "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA=="], + + "mimic-function": ["mimic-function@5.0.1", "", {}, "sha512-VP79XUPxV2CigYP3jWwAUFSku2aKqBH7uTAapFWCBqutsbmDo96KY5o8uh6U+/YSIn5OxJnXp73beVkpqMIGhA=="], + + "minimatch": ["minimatch@3.1.5", "", { "dependencies": { "brace-expansion": "^1.1.7" } }, "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w=="], + + "minimist": ["minimist@1.2.8", "", {}, "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA=="], + + "motion": ["motion@12.38.0", "", { "dependencies": { "framer-motion": "^12.38.0", "tslib": "^2.4.0" }, "peerDependencies": { "@emotion/is-prop-valid": "*", "react": "^18.0.0 || ^19.0.0", "react-dom": "^18.0.0 || ^19.0.0" }, "optionalPeers": ["@emotion/is-prop-valid", "react", "react-dom"] }, "sha512-uYfXzeHlgThchzwz5Te47dlv5JOUC7OB4rjJ/7XTUgtBZD8CchMN8qEJ4ZVsUmTyYA44zjV0fBwsiktRuFnn+w=="], + + "motion-dom": ["motion-dom@12.38.0", "", { "dependencies": { "motion-utils": "^12.36.0" } }, "sha512-pdkHLD8QYRp8VfiNLb8xIBJis1byQ9gPT3Jnh2jqfFtAsWUA3dEepDlsWe/xMpO8McV+VdpKVcp+E+TGJEtOoA=="], + + "motion-utils": ["motion-utils@12.36.0", "", {}, "sha512-eHWisygbiwVvf6PZ1vhaHCLamvkSbPIeAYxWUuL3a2PD/TROgE7FvfHWTIH4vMl798QLfMw15nRqIaRDXTlYRg=="], + + "ms": ["ms@2.1.3", "", {}, "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="], + + "nanoid": ["nanoid@3.3.12", "", { "bin": { "nanoid": "bin/nanoid.cjs" } }, "sha512-ZB9RH/39qpq5Vu6Y+NmUaFhQR6pp+M2Xt76XBnEwDaGcVAqhlvxrl3B2bKS5D3NH3QR76v3aSrKaF/Kiy7lEtQ=="], + + "napi-postinstall": ["napi-postinstall@0.3.4", "", { "bin": { "napi-postinstall": "lib/cli.js" } }, "sha512-PHI5f1O0EP5xJ9gQmFGMS6IZcrVvTjpXjz7Na41gTE7eE2hK11lg04CECCYEEjdc17EV4DO+fkGEtt7TpTaTiQ=="], + + "natural-compare": ["natural-compare@1.4.0", "", {}, "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw=="], + + "negotiator": ["negotiator@1.0.0", "", {}, "sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg=="], + + "next": ["next@16.2.6", "", { "dependencies": { "@next/env": "16.2.6", "@swc/helpers": "0.5.15", "baseline-browser-mapping": "^2.9.19", "caniuse-lite": "^1.0.30001579", "postcss": "8.4.31", "styled-jsx": "5.1.6" }, "optionalDependencies": { "@next/swc-darwin-arm64": "16.2.6", "@next/swc-darwin-x64": "16.2.6", "@next/swc-linux-arm64-gnu": "16.2.6", "@next/swc-linux-arm64-musl": "16.2.6", "@next/swc-linux-x64-gnu": "16.2.6", "@next/swc-linux-x64-musl": "16.2.6", "@next/swc-win32-arm64-msvc": "16.2.6", "@next/swc-win32-x64-msvc": "16.2.6", "sharp": "^0.34.5" }, "peerDependencies": { "@opentelemetry/api": "^1.1.0", "@playwright/test": "^1.51.1", "babel-plugin-react-compiler": "*", "react": "^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0", "react-dom": "^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0", "sass": "^1.3.0" }, "optionalPeers": ["@opentelemetry/api", "@playwright/test", "babel-plugin-react-compiler", "sass"], "bin": { "next": "dist/bin/next" } }, "sha512-qOVgKJg1+At15NpeUP+eJgCHvTCgXsogweq87Ri/Ix7PkqQHg4sdaXmSFqKlgaIXE4kW0g25LE68W87UANlHtw=="], + + "next-intl": ["next-intl@4.12.0", "", { "dependencies": { "@formatjs/intl-localematcher": "^0.8.1", "@parcel/watcher": "^2.4.1", "@swc/core": "^1.15.2", "icu-minify": "^4.12.0", "negotiator": "^1.0.0", "next-intl-swc-plugin-extractor": "^4.12.0", "po-parser": "^2.1.1", "use-intl": "^4.12.0" }, "peerDependencies": { "next": "^12.0.0 || ^13.0.0 || ^14.0.0 || ^15.0.0 || ^16.0.0", "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || >=19.0.0-rc <19.0.0 || ^19.0.0" } }, "sha512-v8KpppWG0yLLlChJ3Of6uoPew9LeRDBAtY6vpJmF7YJmBZlHEzzoEL4w1g1dAU+VleEPNoXNm9hg1eEsKWV5hw=="], + + "next-intl-swc-plugin-extractor": ["next-intl-swc-plugin-extractor@4.12.0", "", {}, "sha512-jUxVEu1Nryjt4YgaDktSys7ioOgQfcNPF/SF2dbPNxbVb6U+P1INRgHeCVN+EC59H2rnTFIQwbddmOCrUWFr3g=="], + + "next-themes": ["next-themes@0.4.6", "", { "peerDependencies": { "react": "^16.8 || ^17 || ^18 || ^19 || ^19.0.0-rc", "react-dom": "^16.8 || ^17 || ^18 || ^19 || ^19.0.0-rc" } }, "sha512-pZvgD5L0IEvX5/9GWyHMf3m8BKiVQwsCMHfoFosXtXBMnaS0ZnIJ9ST4b4NqLVKDEm8QBxoNNGNaBv2JNF6XNA=="], + + "node-addon-api": ["node-addon-api@7.1.1", "", {}, "sha512-5m3bsyrjFWE1xf7nz7YXdN4udnVtXK6/Yfgn5qnahL6bCkf2yKt4k3nuTKAtT4r3IG8JNR2ncsIMdZuAzJjHQQ=="], + + "node-exports-info": ["node-exports-info@1.6.0", "", { "dependencies": { "array.prototype.flatmap": "^1.3.3", "es-errors": "^1.3.0", "object.entries": "^1.1.9", "semver": "^6.3.1" } }, "sha512-pyFS63ptit/P5WqUkt+UUfe+4oevH+bFeIiPPdfb0pFeYEu/1ELnJu5l+5EcTKYL5M7zaAa7S8ddywgXypqKCw=="], + + "node-releases": ["node-releases@2.0.44", "", {}, "sha512-5WUyunoPMsvvEhS8AxHtRzP+oA8UCkJ7YRxatWKjngndhDGLiqEVAQKWjFAiAiuL8zMRGzGSJxFnLetoa43qGQ=="], + + "object-assign": ["object-assign@4.1.1", "", {}, "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg=="], + + "object-inspect": ["object-inspect@1.13.4", "", {}, "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew=="], + + "object-keys": ["object-keys@1.1.1", "", {}, "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA=="], + + "object.assign": ["object.assign@4.1.7", "", { "dependencies": { "call-bind": "^1.0.8", "call-bound": "^1.0.3", "define-properties": "^1.2.1", "es-object-atoms": "^1.0.0", "has-symbols": "^1.1.0", "object-keys": "^1.1.1" } }, "sha512-nK28WOo+QIjBkDduTINE4JkF/UJJKyf2EJxvJKfblDpyg0Q+pkOHNTL0Qwy6NP6FhE/EnzV73BxxqcJaXY9anw=="], + + "object.entries": ["object.entries@1.1.9", "", { "dependencies": { "call-bind": "^1.0.8", "call-bound": "^1.0.4", "define-properties": "^1.2.1", "es-object-atoms": "^1.1.1" } }, "sha512-8u/hfXFRBD1O0hPUjioLhoWFHRmt6tKA4/vZPyckBr18l1KE9uHrFaFaUi8MDRTpi4uak2goyPTSNJLXX2k2Hw=="], + + "object.fromentries": ["object.fromentries@2.0.8", "", { "dependencies": { "call-bind": "^1.0.7", "define-properties": "^1.2.1", "es-abstract": "^1.23.2", "es-object-atoms": "^1.0.0" } }, "sha512-k6E21FzySsSK5a21KRADBd/NGneRegFO5pLHfdQLpRDETUNJueLXs3WCzyQ3tFRDYgbq3KHGXfTbi2bs8WQ6rQ=="], + + "object.groupby": ["object.groupby@1.0.3", "", { "dependencies": { "call-bind": "^1.0.7", "define-properties": "^1.2.1", "es-abstract": "^1.23.2" } }, "sha512-+Lhy3TQTuzXI5hevh8sBGqbmurHbbIjAi0Z4S63nthVLmLxfbj4T54a4CfZrXIrt9iP4mVAPYMo/v99taj3wjQ=="], + + "object.values": ["object.values@1.2.1", "", { "dependencies": { "call-bind": "^1.0.8", "call-bound": "^1.0.3", "define-properties": "^1.2.1", "es-object-atoms": "^1.0.0" } }, "sha512-gXah6aZrcUxjWg2zR2MwouP2eHlCBzdV4pygudehaKXSGW4v2AsRQUK+lwwXhii6KFZcunEnmSUoYp5CXibxtA=="], + + "obug": ["obug@2.1.1", "", {}, "sha512-uTqF9MuPraAQ+IsnPf366RG4cP9RtUi7MLO1N3KEc+wb0a6yKpeL0lmk2IB1jY5KHPAlTc6T/JRdC/YqxHNwkQ=="], + + "onetime": ["onetime@7.0.0", "", { "dependencies": { "mimic-function": "^5.0.0" } }, "sha512-VXJjc87FScF88uafS3JllDgvAm+c/Slfz06lorj2uAY34rlUu0Nt+v8wreiImcrgAjjIHp1rXpTDlLOGw29WwQ=="], + + "optionator": ["optionator@0.9.4", "", { "dependencies": { "deep-is": "^0.1.3", "fast-levenshtein": "^2.0.6", "levn": "^0.4.1", "prelude-ls": "^1.2.1", "type-check": "^0.4.0", "word-wrap": "^1.2.5" } }, "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g=="], + + "own-keys": ["own-keys@1.0.1", "", { "dependencies": { "get-intrinsic": "^1.2.6", "object-keys": "^1.1.1", "safe-push-apply": "^1.0.0" } }, "sha512-qFOyK5PjiWZd+QQIh+1jhdb9LpxTF0qs7Pm8o5QHYZ0M3vKqSqzsZaEB6oWlxZ+q2sJBMI/Ktgd2N5ZwQoRHfg=="], + + "p-limit": ["p-limit@3.1.0", "", { "dependencies": { "yocto-queue": "^0.1.0" } }, "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ=="], + + "p-locate": ["p-locate@5.0.0", "", { "dependencies": { "p-limit": "^3.0.2" } }, "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw=="], + + "parent-module": ["parent-module@1.0.1", "", { "dependencies": { "callsites": "^3.0.0" } }, "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g=="], + + "parse-json": ["parse-json@5.2.0", "", { "dependencies": { "@babel/code-frame": "^7.0.0", "error-ex": "^1.3.1", "json-parse-even-better-errors": "^2.3.0", "lines-and-columns": "^1.1.6" } }, "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg=="], + + "path-exists": ["path-exists@4.0.0", "", {}, "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w=="], + + "path-key": ["path-key@3.1.1", "", {}, "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q=="], + + "path-parse": ["path-parse@1.0.7", "", {}, "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw=="], + + "pathe": ["pathe@2.0.3", "", {}, "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w=="], + + "picocolors": ["picocolors@1.1.1", "", {}, "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA=="], + + "picomatch": ["picomatch@4.0.4", "", {}, "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A=="], + + "po-parser": ["po-parser@2.1.1", "", {}, "sha512-ECF4zHLbUItpUgE3OTtLKlPjeBN+fKEczj2zYjDfCGOzicNs0GK3Vg2IoAYwx7LH/XYw43fZQP6xnZ4TkNxSLQ=="], + + "possible-typed-array-names": ["possible-typed-array-names@1.1.0", "", {}, "sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg=="], + + "postcss": ["postcss@8.5.14", "", { "dependencies": { "nanoid": "^3.3.11", "picocolors": "^1.1.1", "source-map-js": "^1.2.1" } }, "sha512-SoSL4+OSEtR99LHFZQiJLkT59C5B1amGO1NzTwj7TT1qCUgUO6hxOvzkOYxD+vMrXBM3XJIKzokoERdqQq/Zmg=="], + + "prelude-ls": ["prelude-ls@1.2.1", "", {}, "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g=="], + + "prettier": ["prettier@3.8.3", "", { "bin": { "prettier": "bin/prettier.cjs" } }, "sha512-7igPTM53cGHMW8xWuVTydi2KO233VFiTNyF5hLJqpilHfmn8C8gPf+PS7dUT64YcXFbiMGZxS9pCSxL/Dxm/Jw=="], + + "prettier-plugin-tailwindcss": ["prettier-plugin-tailwindcss@0.8.0", "", { "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": "*" }, "optionalPeers": ["@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-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"] }, "sha512-V8ITGH87yuBDF6JpEZTOVlUz/saAwqb8f3HRgUj8Lh+tGCcrmorhsLpYqzygwFwK0PE2Ib6Mv3M7T/uE2tZV1g=="], + + "prop-types": ["prop-types@15.8.1", "", { "dependencies": { "loose-envify": "^1.4.0", "object-assign": "^4.1.1", "react-is": "^16.13.1" } }, "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg=="], + + "punycode": ["punycode@2.3.1", "", {}, "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg=="], + + "queue-microtask": ["queue-microtask@1.2.3", "", {}, "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A=="], + + "radix-ui": ["radix-ui@1.4.3", "", { "dependencies": { "@radix-ui/primitive": "1.1.3", "@radix-ui/react-accessible-icon": "1.1.7", "@radix-ui/react-accordion": "1.2.12", "@radix-ui/react-alert-dialog": "1.1.15", "@radix-ui/react-arrow": "1.1.7", "@radix-ui/react-aspect-ratio": "1.1.7", "@radix-ui/react-avatar": "1.1.10", "@radix-ui/react-checkbox": "1.3.3", "@radix-ui/react-collapsible": "1.1.12", "@radix-ui/react-collection": "1.1.7", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-context-menu": "2.2.16", "@radix-ui/react-dialog": "1.1.15", "@radix-ui/react-direction": "1.1.1", "@radix-ui/react-dismissable-layer": "1.1.11", "@radix-ui/react-dropdown-menu": "2.1.16", "@radix-ui/react-focus-guards": "1.1.3", "@radix-ui/react-focus-scope": "1.1.7", "@radix-ui/react-form": "0.1.8", "@radix-ui/react-hover-card": "1.1.15", "@radix-ui/react-label": "2.1.7", "@radix-ui/react-menu": "2.1.16", "@radix-ui/react-menubar": "1.1.16", "@radix-ui/react-navigation-menu": "1.2.14", "@radix-ui/react-one-time-password-field": "0.1.8", "@radix-ui/react-password-toggle-field": "0.1.3", "@radix-ui/react-popover": "1.1.15", "@radix-ui/react-popper": "1.2.8", "@radix-ui/react-portal": "1.1.9", "@radix-ui/react-presence": "1.1.5", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-progress": "1.1.7", "@radix-ui/react-radio-group": "1.3.8", "@radix-ui/react-roving-focus": "1.1.11", "@radix-ui/react-scroll-area": "1.2.10", "@radix-ui/react-select": "2.2.6", "@radix-ui/react-separator": "1.1.7", "@radix-ui/react-slider": "1.3.6", "@radix-ui/react-slot": "1.2.3", "@radix-ui/react-switch": "1.2.6", "@radix-ui/react-tabs": "1.1.13", "@radix-ui/react-toast": "1.2.15", "@radix-ui/react-toggle": "1.1.10", "@radix-ui/react-toggle-group": "1.1.11", "@radix-ui/react-toolbar": "1.1.11", "@radix-ui/react-tooltip": "1.2.8", "@radix-ui/react-use-callback-ref": "1.1.1", "@radix-ui/react-use-controllable-state": "1.2.2", "@radix-ui/react-use-effect-event": "0.0.2", "@radix-ui/react-use-escape-keydown": "1.1.1", "@radix-ui/react-use-is-hydrated": "0.1.0", "@radix-ui/react-use-layout-effect": "1.1.1", "@radix-ui/react-use-size": "1.1.1", "@radix-ui/react-visually-hidden": "1.2.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-aWizCQiyeAenIdUbqEpXgRA1ya65P13NKn/W8rWkcN0OPkRDxdBVLWnIEDsS2RpwCK2nobI7oMUSmexzTDyAmA=="], + + "react": ["react@19.2.4", "", {}, "sha512-9nfp2hYpCwOjAN+8TZFGhtWEwgvWHXqESH8qT89AT/lWklpLON22Lc8pEtnpsZz7VmawabSU0gCjnj8aC0euHQ=="], + + "react-dom": ["react-dom@19.2.4", "", { "dependencies": { "scheduler": "^0.27.0" }, "peerDependencies": { "react": "^19.2.4" } }, "sha512-AXJdLo8kgMbimY95O2aKQqsz2iWi9jMgKJhRBAxECE4IFxfcazB2LmzloIoibJI3C12IlY20+KFaLv+71bUJeQ=="], + + "react-hook-form": ["react-hook-form@7.76.0", "", { "peerDependencies": { "react": "^16.8.0 || ^17 || ^18 || ^19" } }, "sha512-eKtLGgFeSgkHqQD8J59AMZ9a4uD1D83iSIzt4YlTGD7liDen5rrjcUO1rVIGd9yC1gofryjtHbv+4ny4hkLWlw=="], + + "react-is": ["react-is@16.13.1", "", {}, "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ=="], + + "react-remove-scroll": ["react-remove-scroll@2.7.2", "", { "dependencies": { "react-remove-scroll-bar": "^2.3.7", "react-style-singleton": "^2.2.3", "tslib": "^2.1.0", "use-callback-ref": "^1.3.3", "use-sidecar": "^1.1.3" }, "peerDependencies": { "@types/react": "*", "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-Iqb9NjCCTt6Hf+vOdNIZGdTiH1QSqr27H/Ek9sv/a97gfueI/5h1s3yRi1nngzMUaOOToin5dI1dXKdXiF+u0Q=="], + + "react-remove-scroll-bar": ["react-remove-scroll-bar@2.3.8", "", { "dependencies": { "react-style-singleton": "^2.2.2", "tslib": "^2.0.0" }, "peerDependencies": { "@types/react": "*", "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" }, "optionalPeers": ["@types/react"] }, "sha512-9r+yi9+mgU33AKcj6IbT9oRCO78WriSj6t/cF8DWBZJ9aOGPOTEDvdUDz1FwKim7QXWwmHqtdHnRJfhAxEG46Q=="], + + "react-style-singleton": ["react-style-singleton@2.2.3", "", { "dependencies": { "get-nonce": "^1.0.0", "tslib": "^2.0.0" }, "peerDependencies": { "@types/react": "*", "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-b6jSvxvVnyptAiLjbkWLE/lOnR4lfTtDAl+eUC7RZy+QQWc6wRzIV2CE6xBuMmDxc2qIihtDCZD5NPOFl7fRBQ=="], + + "reflect.getprototypeof": ["reflect.getprototypeof@1.0.10", "", { "dependencies": { "call-bind": "^1.0.8", "define-properties": "^1.2.1", "es-abstract": "^1.23.9", "es-errors": "^1.3.0", "es-object-atoms": "^1.0.0", "get-intrinsic": "^1.2.7", "get-proto": "^1.0.1", "which-builtin-type": "^1.2.1" } }, "sha512-00o4I+DVrefhv+nX0ulyi3biSHCPDe+yLv5o/p6d/UVlirijB8E16FtfwSAi4g3tcqrQ4lRAqQSoFEZJehYEcw=="], + + "regexp.prototype.flags": ["regexp.prototype.flags@1.5.4", "", { "dependencies": { "call-bind": "^1.0.8", "define-properties": "^1.2.1", "es-errors": "^1.3.0", "get-proto": "^1.0.1", "gopd": "^1.2.0", "set-function-name": "^2.0.2" } }, "sha512-dYqgNSZbDwkaJ2ceRd9ojCGjBq+mOm9LmtXnAnEGyHhN/5R7iDW2TRw3h+o/jCFxus3P2LfWIIiwowAjANm7IA=="], + + "require-from-string": ["require-from-string@2.0.2", "", {}, "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw=="], + + "resolve": ["resolve@2.0.0-next.7", "", { "dependencies": { "es-errors": "^1.3.0", "is-core-module": "^2.16.2", "node-exports-info": "^1.6.0", "object-keys": "^1.1.1", "path-parse": "^1.0.7", "supports-preserve-symlinks-flag": "^1.0.0" }, "bin": { "resolve": "bin/resolve" } }, "sha512-tqt+NBWwyaMgw3zDsnygx4CByWjQEJHOPMdslYhppaQSJUtL/D4JO9CcBBlhPoI8lz9oJIDXkwXfhF4aWqP8xQ=="], + + "resolve-from": ["resolve-from@5.0.0", "", {}, "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw=="], + + "resolve-pkg-maps": ["resolve-pkg-maps@1.0.0", "", {}, "sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw=="], + + "restore-cursor": ["restore-cursor@5.1.0", "", { "dependencies": { "onetime": "^7.0.0", "signal-exit": "^4.1.0" } }, "sha512-oMA2dcrw6u0YfxJQXm342bFKX/E4sG9rbTzO9ptUcR/e8A33cHuvStiYOwH7fszkZlZ1z/ta9AAoPk2F4qIOHA=="], + + "reusify": ["reusify@1.1.0", "", {}, "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw=="], + + "rfdc": ["rfdc@1.4.1", "", {}, "sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA=="], + + "rolldown": ["rolldown@1.0.1", "", { "dependencies": { "@oxc-project/types": "=0.130.0", "@rolldown/pluginutils": "^1.0.0" }, "optionalDependencies": { "@rolldown/binding-android-arm64": "1.0.1", "@rolldown/binding-darwin-arm64": "1.0.1", "@rolldown/binding-darwin-x64": "1.0.1", "@rolldown/binding-freebsd-x64": "1.0.1", "@rolldown/binding-linux-arm-gnueabihf": "1.0.1", "@rolldown/binding-linux-arm64-gnu": "1.0.1", "@rolldown/binding-linux-arm64-musl": "1.0.1", "@rolldown/binding-linux-ppc64-gnu": "1.0.1", "@rolldown/binding-linux-s390x-gnu": "1.0.1", "@rolldown/binding-linux-x64-gnu": "1.0.1", "@rolldown/binding-linux-x64-musl": "1.0.1", "@rolldown/binding-openharmony-arm64": "1.0.1", "@rolldown/binding-wasm32-wasi": "1.0.1", "@rolldown/binding-win32-arm64-msvc": "1.0.1", "@rolldown/binding-win32-x64-msvc": "1.0.1" }, "bin": { "rolldown": "bin/cli.mjs" } }, "sha512-X0KQHljNnEkWNqqiz9zJrGunh1B0HgOxLXvnFpCOcadzcy5qohZ3tqMEUg00vncoRovXuK3ZqCT9KnnKzoInFQ=="], + + "run-parallel": ["run-parallel@1.2.0", "", { "dependencies": { "queue-microtask": "^1.2.2" } }, "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA=="], + + "safe-array-concat": ["safe-array-concat@1.1.4", "", { "dependencies": { "call-bind": "^1.0.9", "call-bound": "^1.0.4", "get-intrinsic": "^1.3.0", "has-symbols": "^1.1.0", "isarray": "^2.0.5" } }, "sha512-wtZlHyOje6OZTGqAoaDKxFkgRtkF9CnHAVnCHKfuj200wAgL+bSJhdsCD2l0Qx/2ekEXjPWcyKkfGb5CPboslg=="], + + "safe-push-apply": ["safe-push-apply@1.0.0", "", { "dependencies": { "es-errors": "^1.3.0", "isarray": "^2.0.5" } }, "sha512-iKE9w/Z7xCzUMIZqdBsp6pEQvwuEebH4vdpjcDWnyzaI6yl6O9FHvVpmGelvEHNsoY6wGblkxR6Zty/h00WiSA=="], + + "safe-regex-test": ["safe-regex-test@1.1.0", "", { "dependencies": { "call-bound": "^1.0.2", "es-errors": "^1.3.0", "is-regex": "^1.2.1" } }, "sha512-x/+Cz4YrimQxQccJf5mKEbIa1NzeCRNI5Ecl/ekmlYaampdNLPalVyIcCZNNH3MvmqBugV5TMYZXv0ljslUlaw=="], + + "scheduler": ["scheduler@0.27.0", "", {}, "sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q=="], + + "semver": ["semver@7.8.0", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-AcM7dV/5ul4EekoQ29Agm5vri8JNqRyj39o0qpX6vDF2GZrtutZl5RwgD1XnZjiTAfncsJhMI48QQH3sN87YNA=="], + + "set-function-length": ["set-function-length@1.2.2", "", { "dependencies": { "define-data-property": "^1.1.4", "es-errors": "^1.3.0", "function-bind": "^1.1.2", "get-intrinsic": "^1.2.4", "gopd": "^1.0.1", "has-property-descriptors": "^1.0.2" } }, "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg=="], + + "set-function-name": ["set-function-name@2.0.2", "", { "dependencies": { "define-data-property": "^1.1.4", "es-errors": "^1.3.0", "functions-have-names": "^1.2.3", "has-property-descriptors": "^1.0.2" } }, "sha512-7PGFlmtwsEADb0WYyvCMa1t+yke6daIG4Wirafur5kcf+MhUnPms1UeR0CKQdTZD81yESwMHbtn+TR+dMviakQ=="], + + "set-proto": ["set-proto@1.0.0", "", { "dependencies": { "dunder-proto": "^1.0.1", "es-errors": "^1.3.0", "es-object-atoms": "^1.0.0" } }, "sha512-RJRdvCo6IAnPdsvP/7m6bsQqNnn1FCBX5ZNtFL98MmFF/4xAIJTIg1YbHW5DC2W5SKZanrC6i4HsJqlajw/dZw=="], + + "sharp": ["sharp@0.34.5", "", { "dependencies": { "@img/colour": "^1.0.0", "detect-libc": "^2.1.2", "semver": "^7.7.3" }, "optionalDependencies": { "@img/sharp-darwin-arm64": "0.34.5", "@img/sharp-darwin-x64": "0.34.5", "@img/sharp-libvips-darwin-arm64": "1.2.4", "@img/sharp-libvips-darwin-x64": "1.2.4", "@img/sharp-libvips-linux-arm": "1.2.4", "@img/sharp-libvips-linux-arm64": "1.2.4", "@img/sharp-libvips-linux-ppc64": "1.2.4", "@img/sharp-libvips-linux-riscv64": "1.2.4", "@img/sharp-libvips-linux-s390x": "1.2.4", "@img/sharp-libvips-linux-x64": "1.2.4", "@img/sharp-libvips-linuxmusl-arm64": "1.2.4", "@img/sharp-libvips-linuxmusl-x64": "1.2.4", "@img/sharp-linux-arm": "0.34.5", "@img/sharp-linux-arm64": "0.34.5", "@img/sharp-linux-ppc64": "0.34.5", "@img/sharp-linux-riscv64": "0.34.5", "@img/sharp-linux-s390x": "0.34.5", "@img/sharp-linux-x64": "0.34.5", "@img/sharp-linuxmusl-arm64": "0.34.5", "@img/sharp-linuxmusl-x64": "0.34.5", "@img/sharp-wasm32": "0.34.5", "@img/sharp-win32-arm64": "0.34.5", "@img/sharp-win32-ia32": "0.34.5", "@img/sharp-win32-x64": "0.34.5" } }, "sha512-Ou9I5Ft9WNcCbXrU9cMgPBcCK8LiwLqcbywW3t4oDV37n1pzpuNLsYiAV8eODnjbtQlSDwZ2cUEeQz4E54Hltg=="], + + "shebang-command": ["shebang-command@2.0.0", "", { "dependencies": { "shebang-regex": "^3.0.0" } }, "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA=="], + + "shebang-regex": ["shebang-regex@3.0.0", "", {}, "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A=="], + + "side-channel": ["side-channel@1.1.0", "", { "dependencies": { "es-errors": "^1.3.0", "object-inspect": "^1.13.3", "side-channel-list": "^1.0.0", "side-channel-map": "^1.0.1", "side-channel-weakmap": "^1.0.2" } }, "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw=="], + + "side-channel-list": ["side-channel-list@1.0.1", "", { "dependencies": { "es-errors": "^1.3.0", "object-inspect": "^1.13.4" } }, "sha512-mjn/0bi/oUURjc5Xl7IaWi/OJJJumuoJFQJfDDyO46+hBWsfaVM65TBHq2eoZBhzl9EchxOijpkbRC8SVBQU0w=="], + + "side-channel-map": ["side-channel-map@1.0.1", "", { "dependencies": { "call-bound": "^1.0.2", "es-errors": "^1.3.0", "get-intrinsic": "^1.2.5", "object-inspect": "^1.13.3" } }, "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA=="], + + "side-channel-weakmap": ["side-channel-weakmap@1.0.2", "", { "dependencies": { "call-bound": "^1.0.2", "es-errors": "^1.3.0", "get-intrinsic": "^1.2.5", "object-inspect": "^1.13.3", "side-channel-map": "^1.0.1" } }, "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A=="], + + "siginfo": ["siginfo@2.0.0", "", {}, "sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g=="], + + "signal-exit": ["signal-exit@4.1.0", "", {}, "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw=="], + + "slice-ansi": ["slice-ansi@8.0.0", "", { "dependencies": { "ansi-styles": "^6.2.3", "is-fullwidth-code-point": "^5.1.0" } }, "sha512-stxByr12oeeOyY2BlviTNQlYV5xOj47GirPr4yA1hE9JCtxfQN0+tVbkxwCtYDQWhEKWFHsEK48ORg5jrouCAg=="], + + "sonner": ["sonner@2.0.7", "", { "peerDependencies": { "react": "^18.0.0 || ^19.0.0 || ^19.0.0-rc", "react-dom": "^18.0.0 || ^19.0.0 || ^19.0.0-rc" } }, "sha512-W6ZN4p58k8aDKA4XPcx2hpIQXBRAgyiWVkYhT7CvK6D3iAu7xjvVyhQHg2/iaKJZ1XVJ4r7XuwGL+WGEK37i9w=="], + + "source-map-js": ["source-map-js@1.2.1", "", {}, "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA=="], + + "stable-hash": ["stable-hash@0.0.5", "", {}, "sha512-+L3ccpzibovGXFK+Ap/f8LOS0ahMrHTf3xu7mMLSpEGU0EO9ucaysSylKo9eRDFNhWve/y275iPmIZ4z39a9iA=="], + + "stackback": ["stackback@0.0.2", "", {}, "sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw=="], + + "std-env": ["std-env@4.1.0", "", {}, "sha512-Rq7ybcX2RuC55r9oaPVEW7/xu3tj8u4GeBYHBWCychFtzMIr86A7e3PPEBPT37sHStKX3+TiX/Fr/ACmJLVlLQ=="], + + "stop-iteration-iterator": ["stop-iteration-iterator@1.1.0", "", { "dependencies": { "es-errors": "^1.3.0", "internal-slot": "^1.1.0" } }, "sha512-eLoXW/DHyl62zxY4SCaIgnRhuMr6ri4juEYARS8E6sCEqzKpOiE521Ucofdx+KnDZl5xmvGYaaKCk5FEOxJCoQ=="], + + "string-argv": ["string-argv@0.3.2", "", {}, "sha512-aqD2Q0144Z+/RqG52NeHEkZauTAUWJO8c6yTftGJKO3Tja5tUgIfmIl6kExvhtxSDP7fXB6DvzkfMpCd/F3G+Q=="], + + "string-width": ["string-width@7.2.0", "", { "dependencies": { "emoji-regex": "^10.3.0", "get-east-asian-width": "^1.0.0", "strip-ansi": "^7.1.0" } }, "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ=="], + + "string.prototype.includes": ["string.prototype.includes@2.0.1", "", { "dependencies": { "call-bind": "^1.0.7", "define-properties": "^1.2.1", "es-abstract": "^1.23.3" } }, "sha512-o7+c9bW6zpAdJHTtujeePODAhkuicdAryFsfVKwA+wGw89wJ4GTY484WTucM9hLtDEOpOvI+aHnzqnC5lHp4Rg=="], + + "string.prototype.matchall": ["string.prototype.matchall@4.0.12", "", { "dependencies": { "call-bind": "^1.0.8", "call-bound": "^1.0.3", "define-properties": "^1.2.1", "es-abstract": "^1.23.6", "es-errors": "^1.3.0", "es-object-atoms": "^1.0.0", "get-intrinsic": "^1.2.6", "gopd": "^1.2.0", "has-symbols": "^1.1.0", "internal-slot": "^1.1.0", "regexp.prototype.flags": "^1.5.3", "set-function-name": "^2.0.2", "side-channel": "^1.1.0" } }, "sha512-6CC9uyBL+/48dYizRf7H7VAYCMCNTBeM78x/VTUe9bFEaxBepPJDa1Ow99LqI/1yF7kuy7Q3cQsYMrcjGUcskA=="], + + "string.prototype.repeat": ["string.prototype.repeat@1.0.0", "", { "dependencies": { "define-properties": "^1.1.3", "es-abstract": "^1.17.5" } }, "sha512-0u/TldDbKD8bFCQ/4f5+mNRrXwZ8hg2w7ZR8wa16e8z9XpePWl3eGEcUD0OXpEH/VJH/2G3gjUtR3ZOiBe2S/w=="], + + "string.prototype.trim": ["string.prototype.trim@1.2.10", "", { "dependencies": { "call-bind": "^1.0.8", "call-bound": "^1.0.2", "define-data-property": "^1.1.4", "define-properties": "^1.2.1", "es-abstract": "^1.23.5", "es-object-atoms": "^1.0.0", "has-property-descriptors": "^1.0.2" } }, "sha512-Rs66F0P/1kedk5lyYyH9uBzuiI/kNRmwJAR9quK6VOtIpZ2G+hMZd+HQbbv25MgCA6gEffoMZYxlTod4WcdrKA=="], + + "string.prototype.trimend": ["string.prototype.trimend@1.0.9", "", { "dependencies": { "call-bind": "^1.0.8", "call-bound": "^1.0.2", "define-properties": "^1.2.1", "es-object-atoms": "^1.0.0" } }, "sha512-G7Ok5C6E/j4SGfyLCloXTrngQIQU3PWtXGst3yM7Bea9FRURf1S42ZHlZZtsNque2FN2PoUhfZXYLNWwEr4dLQ=="], + + "string.prototype.trimstart": ["string.prototype.trimstart@1.0.8", "", { "dependencies": { "call-bind": "^1.0.7", "define-properties": "^1.2.1", "es-object-atoms": "^1.0.0" } }, "sha512-UXSH262CSZY1tfu3G3Secr6uGLCFVPMhIqHjlgCUtCCcgihYc/xKs9djMTMUOb2j1mVSeU8EU6NWc/iQKU6Gfg=="], + + "strip-ansi": ["strip-ansi@7.2.0", "", { "dependencies": { "ansi-regex": "^6.2.2" } }, "sha512-yDPMNjp4WyfYBkHnjIRLfca1i6KMyGCtsVgoKe/z1+6vukgaENdgGBZt+ZmKPc4gavvEZ5OgHfHdrazhgNyG7w=="], + + "strip-bom": ["strip-bom@3.0.0", "", {}, "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA=="], + + "strip-json-comments": ["strip-json-comments@3.1.1", "", {}, "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig=="], + + "styled-jsx": ["styled-jsx@5.1.6", "", { "dependencies": { "client-only": "0.0.1" }, "peerDependencies": { "react": ">= 16.8.0 || 17.x.x || ^18.0.0-0 || ^19.0.0-0" } }, "sha512-qSVyDTeMotdvQYoHWLNGwRFJHC+i+ZvdBRYosOFgC+Wg1vx4frN2/RG/NA7SYqqvKNLf39P2LSRA2pu6n0XYZA=="], + + "supports-color": ["supports-color@7.2.0", "", { "dependencies": { "has-flag": "^4.0.0" } }, "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw=="], + + "supports-preserve-symlinks-flag": ["supports-preserve-symlinks-flag@1.0.0", "", {}, "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w=="], + + "tailwind-merge": ["tailwind-merge@3.6.0", "", {}, "sha512-uxL7qAVQriqRQPAyK3pj66VqskWqoZ37PW94jwOTwNfq/z9oyu1V+eqrZqtR2+fCiXdYOZe/Modt8GtvqNzu+w=="], + + "tailwindcss": ["tailwindcss@4.3.0", "", {}, "sha512-y6nxMGB1nMW9R6k96e5gdIFzcfL/gTJRNaqGes1YvkLnPVXzWgbqFF2yLC0T8G774n24cx3Pe8XrKoniCOAH+Q=="], + + "tapable": ["tapable@2.3.3", "", {}, "sha512-uxc/zpqFg6x7C8vOE7lh6Lbda8eEL9zmVm/PLeTPBRhh1xCgdWaQ+J1CUieGpIfm2HdtsUpRv+HshiasBMcc6A=="], + + "tinybench": ["tinybench@2.9.0", "", {}, "sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg=="], + + "tinyexec": ["tinyexec@1.1.2", "", {}, "sha512-dAqSqE/RabpBKI8+h26GfLq6Vb3JVXs30XYQjdMjaj/c2tS8IYYMbIzP599KtRj7c57/wYApb3QjgRgXmrCukA=="], + + "tinyglobby": ["tinyglobby@0.2.16", "", { "dependencies": { "fdir": "^6.5.0", "picomatch": "^4.0.4" } }, "sha512-pn99VhoACYR8nFHhxqix+uvsbXineAasWm5ojXoN8xEwK5Kd3/TrhNn1wByuD52UxWRLy8pu+kRMniEi6Eq9Zg=="], + + "tinyrainbow": ["tinyrainbow@3.1.0", "", {}, "sha512-Bf+ILmBgretUrdJxzXM0SgXLZ3XfiaUuOj/IKQHuTXip+05Xn+uyEYdVg0kYDipTBcLrCVyUzAPz7QmArb0mmw=="], + + "to-regex-range": ["to-regex-range@5.0.1", "", { "dependencies": { "is-number": "^7.0.0" } }, "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ=="], + + "ts-api-utils": ["ts-api-utils@2.5.0", "", { "peerDependencies": { "typescript": ">=4.8.4" } }, "sha512-OJ/ibxhPlqrMM0UiNHJ/0CKQkoKF243/AEmplt3qpRgkW8VG7IfOS41h7V8TjITqdByHzrjcS/2si+y4lIh8NA=="], + + "tsconfig-paths": ["tsconfig-paths@3.15.0", "", { "dependencies": { "@types/json5": "^0.0.29", "json5": "^1.0.2", "minimist": "^1.2.6", "strip-bom": "^3.0.0" } }, "sha512-2Ac2RgzDe/cn48GvOe3M+o82pEFewD3UPbyoUHHdKasHwJKjds4fLXWf/Ux5kATBKN20oaFGu+jbElp1pos0mg=="], + + "tslib": ["tslib@2.8.1", "", {}, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="], + + "type-check": ["type-check@0.4.0", "", { "dependencies": { "prelude-ls": "^1.2.1" } }, "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew=="], + + "typed-array-buffer": ["typed-array-buffer@1.0.3", "", { "dependencies": { "call-bound": "^1.0.3", "es-errors": "^1.3.0", "is-typed-array": "^1.1.14" } }, "sha512-nAYYwfY3qnzX30IkA6AQZjVbtK6duGontcQm1WSG1MD94YLqK0515GNApXkoxKOWMusVssAHWLh9SeaoefYFGw=="], + + "typed-array-byte-length": ["typed-array-byte-length@1.0.3", "", { "dependencies": { "call-bind": "^1.0.8", "for-each": "^0.3.3", "gopd": "^1.2.0", "has-proto": "^1.2.0", "is-typed-array": "^1.1.14" } }, "sha512-BaXgOuIxz8n8pIq3e7Atg/7s+DpiYrxn4vdot3w9KbnBhcRQq6o3xemQdIfynqSeXeDrF32x+WvfzmOjPiY9lg=="], + + "typed-array-byte-offset": ["typed-array-byte-offset@1.0.4", "", { "dependencies": { "available-typed-arrays": "^1.0.7", "call-bind": "^1.0.8", "for-each": "^0.3.3", "gopd": "^1.2.0", "has-proto": "^1.2.0", "is-typed-array": "^1.1.15", "reflect.getprototypeof": "^1.0.9" } }, "sha512-bTlAFB/FBYMcuX81gbL4OcpH5PmlFHqlCCpAl8AlEzMz5k53oNDvN8p1PNOWLEmI2x4orp3raOFB51tv9X+MFQ=="], + + "typed-array-length": ["typed-array-length@1.0.7", "", { "dependencies": { "call-bind": "^1.0.7", "for-each": "^0.3.3", "gopd": "^1.0.1", "is-typed-array": "^1.1.13", "possible-typed-array-names": "^1.0.0", "reflect.getprototypeof": "^1.0.6" } }, "sha512-3KS2b+kL7fsuk/eJZ7EQdnEmQoaho/r6KUef7hxvltNA5DR8NAUM+8wJMbJyZ4G9/7i3v5zPBIMN5aybAh2/Jg=="], + + "typescript": ["typescript@5.9.3", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw=="], + + "typescript-eslint": ["typescript-eslint@8.59.3", "", { "dependencies": { "@typescript-eslint/eslint-plugin": "8.59.3", "@typescript-eslint/parser": "8.59.3", "@typescript-eslint/typescript-estree": "8.59.3", "@typescript-eslint/utils": "8.59.3" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", "typescript": ">=4.8.4 <6.1.0" } }, "sha512-KgusgyDgG4LI8Ih/sWaCtZ06tckLAS5CvT5A4D1Q7bYVoAAyzwiZvE4BmwDHkhRVkvhRBepKeASoFzQetha7Fg=="], + + "unbox-primitive": ["unbox-primitive@1.1.0", "", { "dependencies": { "call-bound": "^1.0.3", "has-bigints": "^1.0.2", "has-symbols": "^1.1.0", "which-boxed-primitive": "^1.1.1" } }, "sha512-nWJ91DjeOkej/TA8pXQ3myruKpKEYgqvpw9lz4OPHj/NWFNluYrjbz9j01CJ8yKQd2g4jFoOkINCTW2I5LEEyw=="], + + "undici-types": ["undici-types@6.21.0", "", {}, "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ=="], + + "unrs-resolver": ["unrs-resolver@1.11.1", "", { "dependencies": { "napi-postinstall": "^0.3.0" }, "optionalDependencies": { "@unrs/resolver-binding-android-arm-eabi": "1.11.1", "@unrs/resolver-binding-android-arm64": "1.11.1", "@unrs/resolver-binding-darwin-arm64": "1.11.1", "@unrs/resolver-binding-darwin-x64": "1.11.1", "@unrs/resolver-binding-freebsd-x64": "1.11.1", "@unrs/resolver-binding-linux-arm-gnueabihf": "1.11.1", "@unrs/resolver-binding-linux-arm-musleabihf": "1.11.1", "@unrs/resolver-binding-linux-arm64-gnu": "1.11.1", "@unrs/resolver-binding-linux-arm64-musl": "1.11.1", "@unrs/resolver-binding-linux-ppc64-gnu": "1.11.1", "@unrs/resolver-binding-linux-riscv64-gnu": "1.11.1", "@unrs/resolver-binding-linux-riscv64-musl": "1.11.1", "@unrs/resolver-binding-linux-s390x-gnu": "1.11.1", "@unrs/resolver-binding-linux-x64-gnu": "1.11.1", "@unrs/resolver-binding-linux-x64-musl": "1.11.1", "@unrs/resolver-binding-wasm32-wasi": "1.11.1", "@unrs/resolver-binding-win32-arm64-msvc": "1.11.1", "@unrs/resolver-binding-win32-ia32-msvc": "1.11.1", "@unrs/resolver-binding-win32-x64-msvc": "1.11.1" } }, "sha512-bSjt9pjaEBnNiGgc9rUiHGKv5l4/TGzDmYw3RhnkJGtLhbnnA/5qJj7x3dNDCRx/PJxu774LlH8lCOlB4hEfKg=="], + + "update-browserslist-db": ["update-browserslist-db@1.2.3", "", { "dependencies": { "escalade": "^3.2.0", "picocolors": "^1.1.1" }, "peerDependencies": { "browserslist": ">= 4.21.0" }, "bin": { "update-browserslist-db": "cli.js" } }, "sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w=="], + + "uri-js": ["uri-js@4.4.1", "", { "dependencies": { "punycode": "^2.1.0" } }, "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg=="], + + "use-callback-ref": ["use-callback-ref@1.3.3", "", { "dependencies": { "tslib": "^2.0.0" }, "peerDependencies": { "@types/react": "*", "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-jQL3lRnocaFtu3V00JToYz/4QkNWswxijDaCVNZRiRTO3HQDLsdu1ZtmIUvV4yPp+rvWm5j0y0TG/S61cuijTg=="], + + "use-intl": ["use-intl@4.12.0", "", { "dependencies": { "@formatjs/fast-memoize": "^3.1.0", "@schummar/icu-type-parser": "1.21.5", "icu-minify": "^4.12.0", "intl-messageformat": "^11.1.0" }, "peerDependencies": { "react": "^17.0.0 || ^18.0.0 || >=19.0.0-rc <19.0.0 || ^19.0.0" } }, "sha512-r+qVb7UI1+kiOhjYsmsNUCY+jrnjVopwGeFlmMyQj4YInlwZzgMeMSv9n8MqnWWy77HL5BVM8K2WgX50SbtcpA=="], + + "use-sidecar": ["use-sidecar@1.1.3", "", { "dependencies": { "detect-node-es": "^1.1.0", "tslib": "^2.0.0" }, "peerDependencies": { "@types/react": "*", "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-Fedw0aZvkhynoPYlA5WXrMCAMm+nSWdZt6lzJQ7Ok8S6Q+VsHmHpRWndVRJ8Be0ZbkfPc5LRYH+5XrzXcEeLRQ=="], + + "use-sync-external-store": ["use-sync-external-store@1.6.0", "", { "peerDependencies": { "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, "sha512-Pp6GSwGP/NrPIrxVFAIkOQeyw8lFenOHijQWkUTrDvrF4ALqylP2C/KCkeS9dpUM3KvYRQhna5vt7IL95+ZQ9w=="], + + "usehooks-ts": ["usehooks-ts@3.1.1", "", { "dependencies": { "lodash.debounce": "^4.0.8" }, "peerDependencies": { "react": "^16.8.0 || ^17 || ^18 || ^19 || ^19.0.0-rc" } }, "sha512-I4diPp9Cq6ieSUH2wu+fDAVQO43xwtulo+fKEidHUwZPnYImbtkTjzIJYcDcJqxgmX31GVqNFURodvcgHcW0pA=="], + + "vite": ["vite@8.0.13", "", { "dependencies": { "lightningcss": "^1.32.0", "picomatch": "^4.0.4", "postcss": "^8.5.14", "rolldown": "1.0.1", "tinyglobby": "^0.2.16" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "peerDependencies": { "@types/node": "^20.19.0 || >=22.12.0", "@vitejs/devtools": "^0.1.18", "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" }, "optionalPeers": ["@types/node", "@vitejs/devtools", "esbuild", "jiti", "less", "sass", "sass-embedded", "stylus", "sugarss", "terser", "tsx", "yaml"], "bin": { "vite": "bin/vite.js" } }, "sha512-MFtjBYgzmSxmgA4RAfjIyXWpGe1oALnjgUTzzV7QLx/TKxCzjtMH6Fd9/eVK+5Fg1qNoz5VAwsmMs/NofrmJvw=="], + + "vitest": ["vitest@4.1.6", "", { "dependencies": { "@vitest/expect": "4.1.6", "@vitest/mocker": "4.1.6", "@vitest/pretty-format": "4.1.6", "@vitest/runner": "4.1.6", "@vitest/snapshot": "4.1.6", "@vitest/spy": "4.1.6", "@vitest/utils": "4.1.6", "es-module-lexer": "^2.0.0", "expect-type": "^1.3.0", "magic-string": "^0.30.21", "obug": "^2.1.1", "pathe": "^2.0.3", "picomatch": "^4.0.3", "std-env": "^4.0.0-rc.1", "tinybench": "^2.9.0", "tinyexec": "^1.0.2", "tinyglobby": "^0.2.15", "tinyrainbow": "^3.1.0", "vite": "^6.0.0 || ^7.0.0 || ^8.0.0", "why-is-node-running": "^2.3.0" }, "peerDependencies": { "@edge-runtime/vm": "*", "@opentelemetry/api": "^1.9.0", "@types/node": "^20.0.0 || ^22.0.0 || >=24.0.0", "@vitest/browser-playwright": "4.1.6", "@vitest/browser-preview": "4.1.6", "@vitest/browser-webdriverio": "4.1.6", "@vitest/coverage-istanbul": "4.1.6", "@vitest/coverage-v8": "4.1.6", "@vitest/ui": "4.1.6", "happy-dom": "*", "jsdom": "*" }, "optionalPeers": ["@edge-runtime/vm", "@opentelemetry/api", "@types/node", "@vitest/browser-playwright", "@vitest/browser-preview", "@vitest/browser-webdriverio", "@vitest/coverage-istanbul", "@vitest/coverage-v8", "@vitest/ui", "happy-dom", "jsdom"], "bin": { "vitest": "vitest.mjs" } }, "sha512-6lvjbS3p9b4CrdCmguzbh2/4uoXhGE2q71R4OX5sqF9R1bo9Xd6fGrMAfvp5wnCzlBnFVdCOp6onuTQVbo8iUQ=="], + + "which": ["which@2.0.2", "", { "dependencies": { "isexe": "^2.0.0" }, "bin": { "node-which": "./bin/node-which" } }, "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA=="], + + "which-boxed-primitive": ["which-boxed-primitive@1.1.1", "", { "dependencies": { "is-bigint": "^1.1.0", "is-boolean-object": "^1.2.1", "is-number-object": "^1.1.1", "is-string": "^1.1.1", "is-symbol": "^1.1.1" } }, "sha512-TbX3mj8n0odCBFVlY8AxkqcHASw3L60jIuF8jFP78az3C2YhmGvqbHBpAjTRH2/xqYunrJ9g1jSyjCjpoWzIAA=="], + + "which-builtin-type": ["which-builtin-type@1.2.1", "", { "dependencies": { "call-bound": "^1.0.2", "function.prototype.name": "^1.1.6", "has-tostringtag": "^1.0.2", "is-async-function": "^2.0.0", "is-date-object": "^1.1.0", "is-finalizationregistry": "^1.1.0", "is-generator-function": "^1.0.10", "is-regex": "^1.2.1", "is-weakref": "^1.0.2", "isarray": "^2.0.5", "which-boxed-primitive": "^1.1.0", "which-collection": "^1.0.2", "which-typed-array": "^1.1.16" } }, "sha512-6iBczoX+kDQ7a3+YJBnh3T+KZRxM/iYNPXicqk66/Qfm1b93iu+yOImkg0zHbj5LNOcNv1TEADiZ0xa34B4q6Q=="], + + "which-collection": ["which-collection@1.0.2", "", { "dependencies": { "is-map": "^2.0.3", "is-set": "^2.0.3", "is-weakmap": "^2.0.2", "is-weakset": "^2.0.3" } }, "sha512-K4jVyjnBdgvc86Y6BkaLZEN933SwYOuBFkdmBu9ZfkcAbdVbpITnDmjvZ/aQjRXQrv5EPkTnD1s39GiiqbngCw=="], + + "which-typed-array": ["which-typed-array@1.1.20", "", { "dependencies": { "available-typed-arrays": "^1.0.7", "call-bind": "^1.0.8", "call-bound": "^1.0.4", "for-each": "^0.3.5", "get-proto": "^1.0.1", "gopd": "^1.2.0", "has-tostringtag": "^1.0.2" } }, "sha512-LYfpUkmqwl0h9A2HL09Mms427Q1RZWuOHsukfVcKRq9q95iQxdw0ix1JQrqbcDR9PH1QDwf5Qo8OZb5lksZ8Xg=="], + + "why-is-node-running": ["why-is-node-running@2.3.0", "", { "dependencies": { "siginfo": "^2.0.0", "stackback": "0.0.2" }, "bin": { "why-is-node-running": "cli.js" } }, "sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w=="], + + "word-wrap": ["word-wrap@1.2.5", "", {}, "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA=="], + + "wrap-ansi": ["wrap-ansi@10.0.0", "", { "dependencies": { "ansi-styles": "^6.2.3", "string-width": "^8.2.0", "strip-ansi": "^7.1.2" } }, "sha512-SGcvg80f0wUy2/fXES19feHMz8E0JoXv2uNgHOu4Dgi2OrCy1lqwFYEJz1BLbDI0exjPMe/ZdzZ/YpGECBG/aQ=="], + + "y18n": ["y18n@5.0.8", "", {}, "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA=="], + + "yallist": ["yallist@3.1.1", "", {}, "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g=="], + + "yaml": ["yaml@2.9.0", "", { "bin": { "yaml": "bin.mjs" } }, "sha512-2AvhNX3mb8zd6Zy7INTtSpl1F15HW6Wnqj0srWlkKLcpYl/gMIMJiyuGq2KeI2YFxUPjdlB+3Lc10seMLtL4cA=="], + + "yargs": ["yargs@18.0.0", "", { "dependencies": { "cliui": "^9.0.1", "escalade": "^3.1.1", "get-caller-file": "^2.0.5", "string-width": "^7.2.0", "y18n": "^5.0.5", "yargs-parser": "^22.0.0" } }, "sha512-4UEqdc2RYGHZc7Doyqkrqiln3p9X2DZVxaGbwhn2pi7MrRagKaOcIKe8L3OxYcbhXLgLFUS3zAYuQjKBQgmuNg=="], + + "yargs-parser": ["yargs-parser@22.0.0", "", {}, "sha512-rwu/ClNdSMpkSrUb+d6BRsSkLUq1fmfsY6TOpYzTwvwkg1/NRG85KBy3kq++A8LKQwX6lsu+aWad+2khvuXrqw=="], + + "yocto-queue": ["yocto-queue@0.1.0", "", {}, "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q=="], + + "zod": ["zod@4.4.3", "", {}, "sha512-ytENFjIJFl2UwYglde2jchW2Hwm4GJFLDiSXWdTrJQBIN9Fcyp7n4DhxJEiWNAJMV1/BqWfW/kkg71UDcHJyTQ=="], + + "zod-validation-error": ["zod-validation-error@4.0.2", "", { "peerDependencies": { "zod": "^3.25.0 || ^4.0.0" } }, "sha512-Q6/nZLe6jxuU80qb/4uJ4t5v2VEZ44lzQjPDhYJNztRQ4wyWc6VF3D3Kb/fAuPetZQnhS3hnajCf9CsWesghLQ=="], + + "zustand": ["zustand@5.0.13", "", { "peerDependencies": { "@types/react": ">=18.0.0", "immer": ">=9.0.6", "react": ">=18.0.0", "use-sync-external-store": ">=1.2.0" }, "optionalPeers": ["@types/react", "immer", "react", "use-sync-external-store"] }, "sha512-efI2tVaVQPqtOh114loML/Z80Y4NP3yc+Ff0fYiZJPauNeWZeIp/bRFD7I9bfmCOYBh/PHxlglQ9+wvlwnPikQ=="], + + "@babel/core/json5": ["json5@2.2.3", "", { "bin": { "json5": "lib/cli.js" } }, "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg=="], + + "@babel/core/semver": ["semver@6.3.1", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA=="], + + "@babel/helper-compilation-targets/semver": ["semver@6.3.1", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA=="], + + "@commitlint/config-validator/ajv": ["ajv@8.20.0", "", { "dependencies": { "fast-deep-equal": "^3.1.3", "fast-uri": "^3.0.1", "json-schema-traverse": "^1.0.0", "require-from-string": "^2.0.2" } }, "sha512-Thbli+OlOj+iMPYFBVBfJ3OmCAnaSyNn4M1vz9T6Gka5Jt9ba/HIR56joy65tY6kx/FCF5VXNB819Y7/GUrBGA=="], + + "@eslint-community/eslint-utils/eslint-visitor-keys": ["eslint-visitor-keys@3.4.3", "", {}, "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag=="], + + "@eslint/eslintrc/globals": ["globals@14.0.0", "", {}, "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ=="], + + "@radix-ui/react-accordion/@radix-ui/react-primitive": ["@radix-ui/react-primitive@2.1.3", "", { "dependencies": { "@radix-ui/react-slot": "1.2.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-m9gTwRkhy2lvCPe6QJp4d3G1TYEUHn/FzJUtq9MjH46an1wJU+GdoGC5VLof8RX8Ft/DlpshApkhswDLZzHIcQ=="], + + "@radix-ui/react-alert-dialog/@radix-ui/react-primitive": ["@radix-ui/react-primitive@2.1.3", "", { "dependencies": { "@radix-ui/react-slot": "1.2.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-m9gTwRkhy2lvCPe6QJp4d3G1TYEUHn/FzJUtq9MjH46an1wJU+GdoGC5VLof8RX8Ft/DlpshApkhswDLZzHIcQ=="], + + "@radix-ui/react-arrow/@radix-ui/react-primitive": ["@radix-ui/react-primitive@2.1.3", "", { "dependencies": { "@radix-ui/react-slot": "1.2.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-m9gTwRkhy2lvCPe6QJp4d3G1TYEUHn/FzJUtq9MjH46an1wJU+GdoGC5VLof8RX8Ft/DlpshApkhswDLZzHIcQ=="], + + "@radix-ui/react-aspect-ratio/@radix-ui/react-primitive": ["@radix-ui/react-primitive@2.1.3", "", { "dependencies": { "@radix-ui/react-slot": "1.2.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-m9gTwRkhy2lvCPe6QJp4d3G1TYEUHn/FzJUtq9MjH46an1wJU+GdoGC5VLof8RX8Ft/DlpshApkhswDLZzHIcQ=="], + + "@radix-ui/react-avatar/@radix-ui/react-primitive": ["@radix-ui/react-primitive@2.1.3", "", { "dependencies": { "@radix-ui/react-slot": "1.2.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-m9gTwRkhy2lvCPe6QJp4d3G1TYEUHn/FzJUtq9MjH46an1wJU+GdoGC5VLof8RX8Ft/DlpshApkhswDLZzHIcQ=="], + + "@radix-ui/react-checkbox/@radix-ui/react-primitive": ["@radix-ui/react-primitive@2.1.3", "", { "dependencies": { "@radix-ui/react-slot": "1.2.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-m9gTwRkhy2lvCPe6QJp4d3G1TYEUHn/FzJUtq9MjH46an1wJU+GdoGC5VLof8RX8Ft/DlpshApkhswDLZzHIcQ=="], + + "@radix-ui/react-collapsible/@radix-ui/react-primitive": ["@radix-ui/react-primitive@2.1.3", "", { "dependencies": { "@radix-ui/react-slot": "1.2.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-m9gTwRkhy2lvCPe6QJp4d3G1TYEUHn/FzJUtq9MjH46an1wJU+GdoGC5VLof8RX8Ft/DlpshApkhswDLZzHIcQ=="], + + "@radix-ui/react-collection/@radix-ui/react-primitive": ["@radix-ui/react-primitive@2.1.3", "", { "dependencies": { "@radix-ui/react-slot": "1.2.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-m9gTwRkhy2lvCPe6QJp4d3G1TYEUHn/FzJUtq9MjH46an1wJU+GdoGC5VLof8RX8Ft/DlpshApkhswDLZzHIcQ=="], + + "@radix-ui/react-context-menu/@radix-ui/react-primitive": ["@radix-ui/react-primitive@2.1.3", "", { "dependencies": { "@radix-ui/react-slot": "1.2.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-m9gTwRkhy2lvCPe6QJp4d3G1TYEUHn/FzJUtq9MjH46an1wJU+GdoGC5VLof8RX8Ft/DlpshApkhswDLZzHIcQ=="], + + "@radix-ui/react-dialog/@radix-ui/react-primitive": ["@radix-ui/react-primitive@2.1.3", "", { "dependencies": { "@radix-ui/react-slot": "1.2.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-m9gTwRkhy2lvCPe6QJp4d3G1TYEUHn/FzJUtq9MjH46an1wJU+GdoGC5VLof8RX8Ft/DlpshApkhswDLZzHIcQ=="], + + "@radix-ui/react-dismissable-layer/@radix-ui/react-primitive": ["@radix-ui/react-primitive@2.1.3", "", { "dependencies": { "@radix-ui/react-slot": "1.2.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-m9gTwRkhy2lvCPe6QJp4d3G1TYEUHn/FzJUtq9MjH46an1wJU+GdoGC5VLof8RX8Ft/DlpshApkhswDLZzHIcQ=="], + + "@radix-ui/react-dropdown-menu/@radix-ui/react-primitive": ["@radix-ui/react-primitive@2.1.3", "", { "dependencies": { "@radix-ui/react-slot": "1.2.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-m9gTwRkhy2lvCPe6QJp4d3G1TYEUHn/FzJUtq9MjH46an1wJU+GdoGC5VLof8RX8Ft/DlpshApkhswDLZzHIcQ=="], + + "@radix-ui/react-focus-scope/@radix-ui/react-primitive": ["@radix-ui/react-primitive@2.1.3", "", { "dependencies": { "@radix-ui/react-slot": "1.2.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-m9gTwRkhy2lvCPe6QJp4d3G1TYEUHn/FzJUtq9MjH46an1wJU+GdoGC5VLof8RX8Ft/DlpshApkhswDLZzHIcQ=="], + + "@radix-ui/react-form/@radix-ui/react-primitive": ["@radix-ui/react-primitive@2.1.3", "", { "dependencies": { "@radix-ui/react-slot": "1.2.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-m9gTwRkhy2lvCPe6QJp4d3G1TYEUHn/FzJUtq9MjH46an1wJU+GdoGC5VLof8RX8Ft/DlpshApkhswDLZzHIcQ=="], + + "@radix-ui/react-hover-card/@radix-ui/react-primitive": ["@radix-ui/react-primitive@2.1.3", "", { "dependencies": { "@radix-ui/react-slot": "1.2.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-m9gTwRkhy2lvCPe6QJp4d3G1TYEUHn/FzJUtq9MjH46an1wJU+GdoGC5VLof8RX8Ft/DlpshApkhswDLZzHIcQ=="], + + "@radix-ui/react-label/@radix-ui/react-primitive": ["@radix-ui/react-primitive@2.1.3", "", { "dependencies": { "@radix-ui/react-slot": "1.2.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-m9gTwRkhy2lvCPe6QJp4d3G1TYEUHn/FzJUtq9MjH46an1wJU+GdoGC5VLof8RX8Ft/DlpshApkhswDLZzHIcQ=="], + + "@radix-ui/react-menu/@radix-ui/react-primitive": ["@radix-ui/react-primitive@2.1.3", "", { "dependencies": { "@radix-ui/react-slot": "1.2.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-m9gTwRkhy2lvCPe6QJp4d3G1TYEUHn/FzJUtq9MjH46an1wJU+GdoGC5VLof8RX8Ft/DlpshApkhswDLZzHIcQ=="], + + "@radix-ui/react-menubar/@radix-ui/react-primitive": ["@radix-ui/react-primitive@2.1.3", "", { "dependencies": { "@radix-ui/react-slot": "1.2.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-m9gTwRkhy2lvCPe6QJp4d3G1TYEUHn/FzJUtq9MjH46an1wJU+GdoGC5VLof8RX8Ft/DlpshApkhswDLZzHIcQ=="], + + "@radix-ui/react-navigation-menu/@radix-ui/react-primitive": ["@radix-ui/react-primitive@2.1.3", "", { "dependencies": { "@radix-ui/react-slot": "1.2.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-m9gTwRkhy2lvCPe6QJp4d3G1TYEUHn/FzJUtq9MjH46an1wJU+GdoGC5VLof8RX8Ft/DlpshApkhswDLZzHIcQ=="], + + "@radix-ui/react-one-time-password-field/@radix-ui/react-primitive": ["@radix-ui/react-primitive@2.1.3", "", { "dependencies": { "@radix-ui/react-slot": "1.2.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-m9gTwRkhy2lvCPe6QJp4d3G1TYEUHn/FzJUtq9MjH46an1wJU+GdoGC5VLof8RX8Ft/DlpshApkhswDLZzHIcQ=="], + + "@radix-ui/react-password-toggle-field/@radix-ui/react-primitive": ["@radix-ui/react-primitive@2.1.3", "", { "dependencies": { "@radix-ui/react-slot": "1.2.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-m9gTwRkhy2lvCPe6QJp4d3G1TYEUHn/FzJUtq9MjH46an1wJU+GdoGC5VLof8RX8Ft/DlpshApkhswDLZzHIcQ=="], + + "@radix-ui/react-popover/@radix-ui/react-primitive": ["@radix-ui/react-primitive@2.1.3", "", { "dependencies": { "@radix-ui/react-slot": "1.2.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-m9gTwRkhy2lvCPe6QJp4d3G1TYEUHn/FzJUtq9MjH46an1wJU+GdoGC5VLof8RX8Ft/DlpshApkhswDLZzHIcQ=="], + + "@radix-ui/react-popper/@radix-ui/react-primitive": ["@radix-ui/react-primitive@2.1.3", "", { "dependencies": { "@radix-ui/react-slot": "1.2.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-m9gTwRkhy2lvCPe6QJp4d3G1TYEUHn/FzJUtq9MjH46an1wJU+GdoGC5VLof8RX8Ft/DlpshApkhswDLZzHIcQ=="], + + "@radix-ui/react-portal/@radix-ui/react-primitive": ["@radix-ui/react-primitive@2.1.3", "", { "dependencies": { "@radix-ui/react-slot": "1.2.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-m9gTwRkhy2lvCPe6QJp4d3G1TYEUHn/FzJUtq9MjH46an1wJU+GdoGC5VLof8RX8Ft/DlpshApkhswDLZzHIcQ=="], + + "@radix-ui/react-primitive/@radix-ui/react-slot": ["@radix-ui/react-slot@1.2.4", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.2" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-Jl+bCv8HxKnlTLVrcDE8zTMJ09R9/ukw4qBs/oZClOfoQk/cOTbDn+NceXfV7j09YPVQUryJPHurafcSg6EVKA=="], + + "@radix-ui/react-progress/@radix-ui/react-primitive": ["@radix-ui/react-primitive@2.1.3", "", { "dependencies": { "@radix-ui/react-slot": "1.2.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-m9gTwRkhy2lvCPe6QJp4d3G1TYEUHn/FzJUtq9MjH46an1wJU+GdoGC5VLof8RX8Ft/DlpshApkhswDLZzHIcQ=="], + + "@radix-ui/react-radio-group/@radix-ui/react-primitive": ["@radix-ui/react-primitive@2.1.3", "", { "dependencies": { "@radix-ui/react-slot": "1.2.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-m9gTwRkhy2lvCPe6QJp4d3G1TYEUHn/FzJUtq9MjH46an1wJU+GdoGC5VLof8RX8Ft/DlpshApkhswDLZzHIcQ=="], + + "@radix-ui/react-roving-focus/@radix-ui/react-primitive": ["@radix-ui/react-primitive@2.1.3", "", { "dependencies": { "@radix-ui/react-slot": "1.2.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-m9gTwRkhy2lvCPe6QJp4d3G1TYEUHn/FzJUtq9MjH46an1wJU+GdoGC5VLof8RX8Ft/DlpshApkhswDLZzHIcQ=="], + + "@radix-ui/react-scroll-area/@radix-ui/react-primitive": ["@radix-ui/react-primitive@2.1.3", "", { "dependencies": { "@radix-ui/react-slot": "1.2.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-m9gTwRkhy2lvCPe6QJp4d3G1TYEUHn/FzJUtq9MjH46an1wJU+GdoGC5VLof8RX8Ft/DlpshApkhswDLZzHIcQ=="], + + "@radix-ui/react-select/@radix-ui/react-primitive": ["@radix-ui/react-primitive@2.1.3", "", { "dependencies": { "@radix-ui/react-slot": "1.2.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-m9gTwRkhy2lvCPe6QJp4d3G1TYEUHn/FzJUtq9MjH46an1wJU+GdoGC5VLof8RX8Ft/DlpshApkhswDLZzHIcQ=="], + + "@radix-ui/react-separator/@radix-ui/react-primitive": ["@radix-ui/react-primitive@2.1.3", "", { "dependencies": { "@radix-ui/react-slot": "1.2.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-m9gTwRkhy2lvCPe6QJp4d3G1TYEUHn/FzJUtq9MjH46an1wJU+GdoGC5VLof8RX8Ft/DlpshApkhswDLZzHIcQ=="], + + "@radix-ui/react-slider/@radix-ui/react-primitive": ["@radix-ui/react-primitive@2.1.3", "", { "dependencies": { "@radix-ui/react-slot": "1.2.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-m9gTwRkhy2lvCPe6QJp4d3G1TYEUHn/FzJUtq9MjH46an1wJU+GdoGC5VLof8RX8Ft/DlpshApkhswDLZzHIcQ=="], + + "@radix-ui/react-switch/@radix-ui/react-primitive": ["@radix-ui/react-primitive@2.1.3", "", { "dependencies": { "@radix-ui/react-slot": "1.2.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-m9gTwRkhy2lvCPe6QJp4d3G1TYEUHn/FzJUtq9MjH46an1wJU+GdoGC5VLof8RX8Ft/DlpshApkhswDLZzHIcQ=="], + + "@radix-ui/react-tabs/@radix-ui/react-primitive": ["@radix-ui/react-primitive@2.1.3", "", { "dependencies": { "@radix-ui/react-slot": "1.2.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-m9gTwRkhy2lvCPe6QJp4d3G1TYEUHn/FzJUtq9MjH46an1wJU+GdoGC5VLof8RX8Ft/DlpshApkhswDLZzHIcQ=="], + + "@radix-ui/react-toast/@radix-ui/react-primitive": ["@radix-ui/react-primitive@2.1.3", "", { "dependencies": { "@radix-ui/react-slot": "1.2.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-m9gTwRkhy2lvCPe6QJp4d3G1TYEUHn/FzJUtq9MjH46an1wJU+GdoGC5VLof8RX8Ft/DlpshApkhswDLZzHIcQ=="], + + "@radix-ui/react-toggle/@radix-ui/react-primitive": ["@radix-ui/react-primitive@2.1.3", "", { "dependencies": { "@radix-ui/react-slot": "1.2.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-m9gTwRkhy2lvCPe6QJp4d3G1TYEUHn/FzJUtq9MjH46an1wJU+GdoGC5VLof8RX8Ft/DlpshApkhswDLZzHIcQ=="], + + "@radix-ui/react-toggle-group/@radix-ui/react-primitive": ["@radix-ui/react-primitive@2.1.3", "", { "dependencies": { "@radix-ui/react-slot": "1.2.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-m9gTwRkhy2lvCPe6QJp4d3G1TYEUHn/FzJUtq9MjH46an1wJU+GdoGC5VLof8RX8Ft/DlpshApkhswDLZzHIcQ=="], + + "@radix-ui/react-toolbar/@radix-ui/react-primitive": ["@radix-ui/react-primitive@2.1.3", "", { "dependencies": { "@radix-ui/react-slot": "1.2.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-m9gTwRkhy2lvCPe6QJp4d3G1TYEUHn/FzJUtq9MjH46an1wJU+GdoGC5VLof8RX8Ft/DlpshApkhswDLZzHIcQ=="], + + "@radix-ui/react-tooltip/@radix-ui/react-primitive": ["@radix-ui/react-primitive@2.1.3", "", { "dependencies": { "@radix-ui/react-slot": "1.2.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-m9gTwRkhy2lvCPe6QJp4d3G1TYEUHn/FzJUtq9MjH46an1wJU+GdoGC5VLof8RX8Ft/DlpshApkhswDLZzHIcQ=="], + + "@radix-ui/react-visually-hidden/@radix-ui/react-primitive": ["@radix-ui/react-primitive@2.1.3", "", { "dependencies": { "@radix-ui/react-slot": "1.2.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-m9gTwRkhy2lvCPe6QJp4d3G1TYEUHn/FzJUtq9MjH46an1wJU+GdoGC5VLof8RX8Ft/DlpshApkhswDLZzHIcQ=="], + + "@tailwindcss/oxide-wasm32-wasi/@emnapi/core": ["@emnapi/core@1.10.0", "", { "dependencies": { "@emnapi/wasi-threads": "1.2.1", "tslib": "^2.4.0" }, "bundled": true }, "sha512-yq6OkJ4p82CAfPl0u9mQebQHKPJkY7WrIuk205cTYnYe+k2Z8YBh11FrbRG/H6ihirqcacOgl2BIO8oyMQLeXw=="], + + "@tailwindcss/oxide-wasm32-wasi/@emnapi/runtime": ["@emnapi/runtime@1.10.0", "", { "dependencies": { "tslib": "^2.4.0" }, "bundled": true }, "sha512-ewvYlk86xUoGI0zQRNq/mC+16R1QeDlKQy21Ki3oSYXNgLb45GV1P6A0M+/s6nyCuNDqe5VpaY84BzXGwVbwFA=="], + + "@tailwindcss/oxide-wasm32-wasi/@emnapi/wasi-threads": ["@emnapi/wasi-threads@1.2.1", "", { "dependencies": { "tslib": "^2.4.0" }, "bundled": true }, "sha512-uTII7OYF+/Mes/MrcIOYp5yOtSMLBWSIoLPpcgwipoiKbli6k322tcoFsxoIIxPDqW01SQGAgko4EzZi2BNv2w=="], + + "@tailwindcss/oxide-wasm32-wasi/@napi-rs/wasm-runtime": ["@napi-rs/wasm-runtime@1.1.4", "", { "dependencies": { "@tybys/wasm-util": "^0.10.1" }, "peerDependencies": { "@emnapi/core": "^1.7.1", "@emnapi/runtime": "^1.7.1" }, "bundled": true }, "sha512-3NQNNgA1YSlJb/kMH1ildASP9HW7/7kYnRI2szWJaofaS1hWmbGI4H+d3+22aGzXXN9IJ+n+GiFVcGipJP18ow=="], + + "@tailwindcss/oxide-wasm32-wasi/@tybys/wasm-util": ["@tybys/wasm-util@0.10.2", "", { "dependencies": { "tslib": "^2.4.0" }, "bundled": true }, "sha512-RoBvJ2X0wuKlWFIjrwffGw1IqZHKQqzIchKaadZZfnNpsAYp2mM0h36JtPCjNDAHGgYez/15uMBpfGwchhiMgg=="], + + "@tailwindcss/oxide-wasm32-wasi/tslib": ["tslib@2.8.1", "", { "bundled": true }, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="], + + "@typescript-eslint/eslint-plugin/ignore": ["ignore@7.0.5", "", {}, "sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg=="], + + "@typescript-eslint/typescript-estree/minimatch": ["minimatch@10.2.5", "", { "dependencies": { "brace-expansion": "^5.0.5" } }, "sha512-MULkVLfKGYDFYejP07QOurDLLQpcjk7Fw+7jXS2R2czRQzR56yHRveU5NDJEOviH+hETZKSkIk5c+T23GjFUMg=="], + + "@typescript-eslint/visitor-keys/eslint-visitor-keys": ["eslint-visitor-keys@5.0.1", "", {}, "sha512-tD40eHxA35h0PEIZNeIjkHoDR4YjjJp34biM0mDvplBe//mB+IHCqHDGV7pxF+7MklTvighcCPPZC7ynWyjdTA=="], + + "@unrs/resolver-binding-wasm32-wasi/@napi-rs/wasm-runtime": ["@napi-rs/wasm-runtime@0.2.12", "", { "dependencies": { "@emnapi/core": "^1.4.3", "@emnapi/runtime": "^1.4.3", "@tybys/wasm-util": "^0.10.0" } }, "sha512-ZVWUcfwY4E/yPitQJl481FjFo3K22D6qF0DuFH6Y/nbnE11GY5uguDxZMGXPQ8WQ0128MXQD7TnfHyK4oWoIJQ=="], + + "cli-truncate/string-width": ["string-width@8.2.1", "", { "dependencies": { "get-east-asian-width": "^1.5.0", "strip-ansi": "^7.1.2" } }, "sha512-IIaP0g3iy9Cyy18w3M9YcaDudujEAVHKt3a3QJg1+sr/oX96TbaGUubG0hJyCjCBThFH+tFpcIyoUHUn1ogaLA=="], + + "cliui/wrap-ansi": ["wrap-ansi@9.0.2", "", { "dependencies": { "ansi-styles": "^6.2.1", "string-width": "^7.0.0", "strip-ansi": "^7.1.0" } }, "sha512-42AtmgqjV+X1VpdOfyTGOYRi0/zsoLqtXQckTmqTeybT+BDIbM/Guxo7x3pE2vtpr1ok6xRqM9OpBe+Jyoqyww=="], + + "cosmiconfig-typescript-loader/jiti": ["jiti@2.6.1", "", { "bin": { "jiti": "lib/jiti-cli.mjs" } }, "sha512-ekilCSN1jwRvIbgeg/57YFh8qQDNbwDb9xT/qu2DAHbFFZUicIl4ygVaAvzveMhMVr3LnpSKTNnwt8PoOfmKhQ=="], + + "eslint-import-resolver-node/debug": ["debug@3.2.7", "", { "dependencies": { "ms": "^2.1.1" } }, "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ=="], + + "eslint-module-utils/debug": ["debug@3.2.7", "", { "dependencies": { "ms": "^2.1.1" } }, "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ=="], + + "eslint-plugin-import/debug": ["debug@3.2.7", "", { "dependencies": { "ms": "^2.1.1" } }, "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ=="], + + "eslint-plugin-import/semver": ["semver@6.3.1", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA=="], + + "eslint-plugin-react/semver": ["semver@6.3.1", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA=="], + + "fast-glob/glob-parent": ["glob-parent@5.1.2", "", { "dependencies": { "is-glob": "^4.0.1" } }, "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow=="], + + "import-fresh/resolve-from": ["resolve-from@4.0.0", "", {}, "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g=="], + + "log-update/slice-ansi": ["slice-ansi@7.1.2", "", { "dependencies": { "ansi-styles": "^6.2.1", "is-fullwidth-code-point": "^5.0.0" } }, "sha512-iOBWFgUX7caIZiuutICxVgX1SdxwAVFFKwt1EvMYYec/NWO5meOJ6K5uQxhrYBdQJne4KxiqZc+KptFOWFSI9w=="], + + "log-update/wrap-ansi": ["wrap-ansi@9.0.2", "", { "dependencies": { "ansi-styles": "^6.2.1", "string-width": "^7.0.0", "strip-ansi": "^7.1.0" } }, "sha512-42AtmgqjV+X1VpdOfyTGOYRi0/zsoLqtXQckTmqTeybT+BDIbM/Guxo7x3pE2vtpr1ok6xRqM9OpBe+Jyoqyww=="], + + "micromatch/picomatch": ["picomatch@2.3.2", "", {}, "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA=="], + + "next/postcss": ["postcss@8.4.31", "", { "dependencies": { "nanoid": "^3.3.6", "picocolors": "^1.0.0", "source-map-js": "^1.0.2" } }, "sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ=="], + + "node-exports-info/semver": ["semver@6.3.1", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA=="], + + "radix-ui/@radix-ui/react-primitive": ["@radix-ui/react-primitive@2.1.3", "", { "dependencies": { "@radix-ui/react-slot": "1.2.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-m9gTwRkhy2lvCPe6QJp4d3G1TYEUHn/FzJUtq9MjH46an1wJU+GdoGC5VLof8RX8Ft/DlpshApkhswDLZzHIcQ=="], + + "slice-ansi/ansi-styles": ["ansi-styles@6.2.3", "", {}, "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg=="], + + "string-width/emoji-regex": ["emoji-regex@10.6.0", "", {}, "sha512-toUI84YS5YmxW219erniWD0CIVOo46xGKColeNQRgOzDorgBi1v4D71/OFzgD9GO2UGKIv1C3Sp8DAn0+j5w7A=="], + + "wrap-ansi/ansi-styles": ["ansi-styles@6.2.3", "", {}, "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg=="], + + "wrap-ansi/string-width": ["string-width@8.2.1", "", { "dependencies": { "get-east-asian-width": "^1.5.0", "strip-ansi": "^7.1.2" } }, "sha512-IIaP0g3iy9Cyy18w3M9YcaDudujEAVHKt3a3QJg1+sr/oX96TbaGUubG0hJyCjCBThFH+tFpcIyoUHUn1ogaLA=="], + + "@commitlint/config-validator/ajv/json-schema-traverse": ["json-schema-traverse@1.0.0", "", {}, "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug=="], + + "@typescript-eslint/typescript-estree/minimatch/brace-expansion": ["brace-expansion@5.0.6", "", { "dependencies": { "balanced-match": "^4.0.2" } }, "sha512-kLpxurY4Z4r9sgMsyG0Z9uzsBlgiU/EFKhj/h91/8yHu0edo7XuixOIH3VcJ8kkxs6/jPzoI6U9Vj3WqbMQ94g=="], + + "cliui/wrap-ansi/ansi-styles": ["ansi-styles@6.2.3", "", {}, "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg=="], + + "log-update/slice-ansi/ansi-styles": ["ansi-styles@6.2.3", "", {}, "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg=="], + + "log-update/wrap-ansi/ansi-styles": ["ansi-styles@6.2.3", "", {}, "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg=="], + + "@typescript-eslint/typescript-estree/minimatch/brace-expansion/balanced-match": ["balanced-match@4.0.4", "", {}, "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA=="], + } +} diff --git a/commitlint.config.mjs b/commitlint.config.mjs new file mode 100644 index 0000000..4356867 --- /dev/null +++ b/commitlint.config.mjs @@ -0,0 +1,11 @@ +export default { + extends: ["@commitlint/config-conventional"], + rules: { + "type-enum": [ + 2, + "always", + ["feat", "fix", "refactor", "docs", "chore", "style", "test", "perf"] + ], + "scope-empty": [2, "never"] + } +} diff --git a/components.json b/components.json new file mode 100644 index 0000000..863d85a --- /dev/null +++ b/components.json @@ -0,0 +1,20 @@ +{ + "$schema": "https://ui.shadcn.com/schema.json", + "style": "new-york", + "rsc": true, + "tsx": true, + "tailwind": { + "config": "", + "css": "src/styles/globals.css", + "baseColor": "zinc", + "cssVariables": true + }, + "aliases": { + "components": "@/components", + "utils": "@/utils/cn", + "ui": "@/components/ui", + "lib": "@/lib", + "hooks": "@/hooks" + }, + "iconLibrary": "lucide" +} diff --git a/eslint.config.mjs b/eslint.config.mjs new file mode 100644 index 0000000..99d9bd6 --- /dev/null +++ b/eslint.config.mjs @@ -0,0 +1,18 @@ +import nextVitals from "eslint-config-next/core-web-vitals" +import nextTs from "eslint-config-next/typescript" +import { defineConfig, globalIgnores } from "eslint/config" + +const eslintConfig = defineConfig([ + ...nextVitals, + ...nextTs, + // Override default ignores of eslint-config-next. + globalIgnores([ + // Default ignores of eslint-config-next: + ".next/**", + "out/**", + "build/**", + "next-env.d.ts" + ]) +]) + +export default eslintConfig diff --git a/next-env.d.ts b/next-env.d.ts new file mode 100644 index 0000000..7a70f65 --- /dev/null +++ b/next-env.d.ts @@ -0,0 +1,6 @@ +/// <reference types="next" /> +/// <reference types="next/image-types/global" /> +import "./.next/types/routes.d.ts" + +// NOTE: This file should not be edited +// see https://nextjs.org/docs/app/api-reference/config/typescript for more information. diff --git a/next.config.ts b/next.config.ts new file mode 100644 index 0000000..9da1646 --- /dev/null +++ b/next.config.ts @@ -0,0 +1,7 @@ +import type { NextConfig } from "next" + +const nextConfig: NextConfig = { + /* config options here */ +} + +export default nextConfig diff --git a/package.json b/package.json new file mode 100644 index 0000000..783303a --- /dev/null +++ b/package.json @@ -0,0 +1,79 @@ +{ + "name": "next-app", + "version": "0.1.0", + "private": true, + "scripts": { + "dev": "next dev", + "build": "next build", + "start": "next start", + "lint": "eslint", + "check-types": "tsc --noEmit", + "format": "prettier --check .", + "format:fix": "prettier --write .", + "test": "vitest --passWithNoTests", + "test:run": "vitest run", + "test:ui": "vitest --ui", + "prepare": "husky" + }, + "dependencies": { + "@hookform/resolvers": "^5.2.2", + "@t3-oss/env-nextjs": "^0.13.11", + "@tanstack/react-query": "^5.100.10", + "@vercel/speed-insights": "^2.0.0", + "class-variance-authority": "^0.7.1", + "clsx": "^2.1.1", + "cmdk": "^1.1.1", + "date-fns": "^4.1.0", + "hotkeys-js": "^4.0.4", + "lightningcss": "^1.32.0", + "lucide-react": "^1.16.0", + "motion": "^12.38.0", + "next": "16.2.6", + "next-intl": "^4.12.0", + "next-themes": "^0.4.6", + "radix-ui": "^1.4.3", + "react": "19.2.4", + "react-dom": "19.2.4", + "react-hook-form": "^7.76.0", + "sonner": "^2.0.7", + "tailwind-merge": "^3.6.0", + "usehooks-ts": "^3.1.1", + "zod": "^4.4.3", + "zustand": "^5.0.13" + }, + "lint-staged": { + "*.{ts,tsx}": [ + "eslint --fix", + "prettier --write" + ], + "*.{json,css,md}": [ + "prettier --write" + ] + }, + "commitlint": { + "extends": [ + "@commitlint/config-conventional" + ] + }, + "devDependencies": { + "@commitlint/cli": "^21.0.1", + "@commitlint/config-conventional": "^21.0.1", + "@ianvs/prettier-plugin-sort-imports": "^4.7.1", + "@rolldown/binding-win32-x64-msvc": "^1.0.1", + "@tailwindcss/postcss": "^4", + "@tanstack/react-query-devtools": "^5.100.10", + "@types/node": "^20", + "@types/react": "^19", + "@types/react-dom": "^19", + "@vitejs/plugin-react": "^6.0.2", + "eslint": "^9", + "eslint-config-next": "16.2.6", + "husky": "^9.1.7", + "lint-staged": "^17.0.4", + "prettier": "^3.8.3", + "prettier-plugin-tailwindcss": "^0.8.0", + "tailwindcss": "^4", + "typescript": "^5", + "vitest": "^4.1.6" + } +} diff --git a/postcss.config.mjs b/postcss.config.mjs new file mode 100644 index 0000000..07cf205 --- /dev/null +++ b/postcss.config.mjs @@ -0,0 +1,7 @@ +const config = { + plugins: { + "@tailwindcss/postcss": {} + } +} + +export default config diff --git a/public/AGENTS.md b/public/AGENTS.md new file mode 100644 index 0000000..2ff532a --- /dev/null +++ b/public/AGENTS.md @@ -0,0 +1,30 @@ +# public/ -- Public Static Assets + +## Fungsi + +Static assets yang diserve langsung oleh web server: images, fonts, icons, manifest files, robots.txt, favicon. + +## Aturan Development + +- Semua file di public/ bisa diakses via `/filename` +- Jangan taruh sensitive data di public/ +- Optimasi gambar sebelum commit (ukuran, format) + +## Convention + +- Nama file: kebab-case (`logo.svg`, `icon-192.png`, `og-image.jpg`) +- Folder grouping: `images/`, `fonts/`, `icons/`, `docs/` +- Favicon: `favicon.ico` di root public/ + +## Best Practices + +- Gunakan format WebP atau AVIF untuk gambar +- next/image untuk optimasi gambar dari public/ +- SVG harus di-optimasi (SVGO) sebelum commit +- robots.txt untuk SEO control +- manifest.json untuk PWA support + +## AI Do's / Don'ts + +- Boleh: mengupdate favicon, menambah static assets +- Tidak boleh: menaruh kode aplikasi atau secrets di public/ diff --git a/public/file.svg b/public/file.svg new file mode 100644 index 0000000..004145c --- /dev/null +++ b/public/file.svg @@ -0,0 +1 @@ +<svg fill="none" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg"><path d="M14.5 13.5V5.41a1 1 0 0 0-.3-.7L9.8.29A1 1 0 0 0 9.08 0H1.5v13.5A2.5 2.5 0 0 0 4 16h8a2.5 2.5 0 0 0 2.5-2.5m-1.5 0v-7H8v-5H3v12a1 1 0 0 0 1 1h8a1 1 0 0 0 1-1M9.5 5V2.12L12.38 5zM5.13 5h-.62v1.25h2.12V5zm-.62 3h7.12v1.25H4.5zm.62 3h-.62v1.25h7.12V11z" clip-rule="evenodd" fill="#666" fill-rule="evenodd"/></svg> \ No newline at end of file diff --git a/public/globe.svg b/public/globe.svg new file mode 100644 index 0000000..567f17b --- /dev/null +++ b/public/globe.svg @@ -0,0 +1 @@ +<svg fill="none" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><g clip-path="url(#a)"><path fill-rule="evenodd" clip-rule="evenodd" d="M10.27 14.1a6.5 6.5 0 0 0 3.67-3.45q-1.24.21-2.7.34-.31 1.83-.97 3.1M8 16A8 8 0 1 0 8 0a8 8 0 0 0 0 16m.48-1.52a7 7 0 0 1-.96 0H7.5a4 4 0 0 1-.84-1.32q-.38-.89-.63-2.08a40 40 0 0 0 3.92 0q-.25 1.2-.63 2.08a4 4 0 0 1-.84 1.31zm2.94-4.76q1.66-.15 2.95-.43a7 7 0 0 0 0-2.58q-1.3-.27-2.95-.43a18 18 0 0 1 0 3.44m-1.27-3.54a17 17 0 0 1 0 3.64 39 39 0 0 1-4.3 0 17 17 0 0 1 0-3.64 39 39 0 0 1 4.3 0m1.1-1.17q1.45.13 2.69.34a6.5 6.5 0 0 0-3.67-3.44q.65 1.26.98 3.1M8.48 1.5l.01.02q.41.37.84 1.31.38.89.63 2.08a40 40 0 0 0-3.92 0q.25-1.2.63-2.08a4 4 0 0 1 .85-1.32 7 7 0 0 1 .96 0m-2.75.4a6.5 6.5 0 0 0-3.67 3.44 29 29 0 0 1 2.7-.34q.31-1.83.97-3.1M4.58 6.28q-1.66.16-2.95.43a7 7 0 0 0 0 2.58q1.3.27 2.95.43a18 18 0 0 1 0-3.44m.17 4.71q-1.45-.12-2.69-.34a6.5 6.5 0 0 0 3.67 3.44q-.65-1.27-.98-3.1" fill="#666"/></g><defs><clipPath id="a"><path fill="#fff" d="M0 0h16v16H0z"/></clipPath></defs></svg> \ No newline at end of file diff --git a/public/next.svg b/public/next.svg new file mode 100644 index 0000000..5174b28 --- /dev/null +++ b/public/next.svg @@ -0,0 +1 @@ +<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 394 80"><path fill="#000" d="M262 0h68.5v12.7h-27.2v66.6h-13.6V12.7H262V0ZM149 0v12.7H94v20.4h44.3v12.6H94v21h55v12.6H80.5V0h68.7zm34.3 0h-17.8l63.8 79.4h17.9l-32-39.7 32-39.6h-17.9l-23 28.6-23-28.6zm18.3 56.7-9-11-27.1 33.7h17.8l18.3-22.7z"/><path fill="#000" d="M81 79.3 17 0H0v79.3h13.6V17l50.2 62.3H81Zm252.6-.4c-1 0-1.8-.4-2.5-1s-1.1-1.6-1.1-2.6.3-1.8 1-2.5 1.6-1 2.6-1 1.8.3 2.5 1a3.4 3.4 0 0 1 .6 4.3 3.7 3.7 0 0 1-3 1.8zm23.2-33.5h6v23.3c0 2.1-.4 4-1.3 5.5a9.1 9.1 0 0 1-3.8 3.5c-1.6.8-3.5 1.3-5.7 1.3-2 0-3.7-.4-5.3-1s-2.8-1.8-3.7-3.2c-.9-1.3-1.4-3-1.4-5h6c.1.8.3 1.6.7 2.2s1 1.2 1.6 1.5c.7.4 1.5.5 2.4.5 1 0 1.8-.2 2.4-.6a4 4 0 0 0 1.6-1.8c.3-.8.5-1.8.5-3V45.5zm30.9 9.1a4.4 4.4 0 0 0-2-3.3 7.5 7.5 0 0 0-4.3-1.1c-1.3 0-2.4.2-3.3.5-.9.4-1.6 1-2 1.6a3.5 3.5 0 0 0-.3 4c.3.5.7.9 1.3 1.2l1.8 1 2 .5 3.2.8c1.3.3 2.5.7 3.7 1.2a13 13 0 0 1 3.2 1.8 8.1 8.1 0 0 1 3 6.5c0 2-.5 3.7-1.5 5.1a10 10 0 0 1-4.4 3.5c-1.8.8-4.1 1.2-6.8 1.2-2.6 0-4.9-.4-6.8-1.2-2-.8-3.4-2-4.5-3.5a10 10 0 0 1-1.7-5.6h6a5 5 0 0 0 3.5 4.6c1 .4 2.2.6 3.4.6 1.3 0 2.5-.2 3.5-.6 1-.4 1.8-1 2.4-1.7a4 4 0 0 0 .8-2.4c0-.9-.2-1.6-.7-2.2a11 11 0 0 0-2.1-1.4l-3.2-1-3.8-1c-2.8-.7-5-1.7-6.6-3.2a7.2 7.2 0 0 1-2.4-5.7 8 8 0 0 1 1.7-5 10 10 0 0 1 4.3-3.5c2-.8 4-1.2 6.4-1.2 2.3 0 4.4.4 6.2 1.2 1.8.8 3.2 2 4.3 3.4 1 1.4 1.5 3 1.5 5h-5.8z"/></svg> \ No newline at end of file diff --git a/public/vercel.svg b/public/vercel.svg new file mode 100644 index 0000000..7705396 --- /dev/null +++ b/public/vercel.svg @@ -0,0 +1 @@ +<svg fill="none" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1155 1000"><path d="m577.3 0 577.4 1000H0z" fill="#fff"/></svg> \ No newline at end of file diff --git a/public/window.svg b/public/window.svg new file mode 100644 index 0000000..b2b2a44 --- /dev/null +++ b/public/window.svg @@ -0,0 +1 @@ +<svg fill="none" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><path fill-rule="evenodd" clip-rule="evenodd" d="M1.5 2.5h13v10a1 1 0 0 1-1 1h-11a1 1 0 0 1-1-1zM0 1h16v11.5a2.5 2.5 0 0 1-2.5 2.5h-11A2.5 2.5 0 0 1 0 12.5zm3.75 4.5a.75.75 0 1 0 0-1.5.75.75 0 0 0 0 1.5M7 4.75a.75.75 0 1 1-1.5 0 .75.75 0 0 1 1.5 0m1.75.75a.75.75 0 1 0 0-1.5.75.75 0 0 0 0 1.5" fill="#666"/></svg> \ No newline at end of file diff --git a/src/__tests__/basic.test.ts b/src/__tests__/basic.test.ts new file mode 100644 index 0000000..e82e23e --- /dev/null +++ b/src/__tests__/basic.test.ts @@ -0,0 +1,7 @@ +import { describe, expect, it } from "vitest" + +describe("basic test", () => { + it("should pass", () => { + expect(true).toBe(true) + }) +}) diff --git a/src/app/AGENTS.md b/src/app/AGENTS.md new file mode 100644 index 0000000..ff9612e --- /dev/null +++ b/src/app/AGENTS.md @@ -0,0 +1,34 @@ +# src/app/ -- Routing & Layout Layer + +## Fungsi + +Entry point aplikasi, routing system (Next.js App Router), global layout, page components, dan providers. + +## Aturan Development + +- File-based routing: setiap folder = route segment +- `layout.tsx` untuk persistent UI (header, footer, sidebar) +- `page.tsx` untuk halaman spesifik +- `loading.tsx`, `error.tsx`, `not-found.tsx` untuk states + +## Convention + +- Segmen route menggunakan kebab-case +- API routes di `app/api/` dengan route.ts handlers +- Gunakan RSC (React Server Components) sebagai default + +## Dependency Boundaries + +- Boleh import dari: components, features, lib, hooks, utils +- Tidak boleh: import dari app/ folder lain secara langsung + +## Best Practices + +- Minimal logic di page.tsx -- delegate ke feature modules +- Gunakan generateMetadata untuk SEO +- Layout terluar di `app/layout.tsx` (root layout) + +## AI Do's / Don'ts + +- Boleh: membuat route baru, halaman baru, layout baru +- Tidak boleh: mengubah root layout tanpa approval diff --git a/src/app/favicon.ico b/src/app/favicon.ico new file mode 100644 index 0000000..718d6fe Binary files /dev/null and b/src/app/favicon.ico differ diff --git a/src/app/layout.tsx b/src/app/layout.tsx new file mode 100644 index 0000000..9510e51 --- /dev/null +++ b/src/app/layout.tsx @@ -0,0 +1,33 @@ +import { Toaster } from "sonner" + +import { fontVariables, siteMetadata } from "@/config" + +import "@/styles/globals.css" + +import { SpeedInsights } from "@vercel/speed-insights/next" + +import { Providers } from "./providers" + +export const metadata = siteMetadata + +export default function RootLayout({ + children +}: Readonly<{ + children: React.ReactNode +}>) { + return ( + <html + lang="id" + className={`${fontVariables} h-full antialiased`} + suppressHydrationWarning + > + <body className="flex min-h-full flex-col"> + <Providers> + <SpeedInsights /> + {children} + <Toaster richColors closeButton position="top-right" /> + </Providers> + </body> + </html> + ) +} diff --git a/src/app/page.tsx b/src/app/page.tsx new file mode 100644 index 0000000..401c706 --- /dev/null +++ b/src/app/page.tsx @@ -0,0 +1,65 @@ +import Image from "next/image" + +export default function Home() { + return ( + <div className="flex flex-1 flex-col items-center justify-center font-sans"> + <main className="flex w-full max-w-3xl flex-1 flex-col items-center justify-between px-16 py-32 sm:items-start"> + <Image + className="dark:invert" + src="/next.svg" + alt="Next.js logo" + width={100} + height={20} + priority + /> + <div className="flex flex-col items-center gap-6 text-center sm:items-start sm:text-left"> + <h1 className="max-w-xs text-3xl leading-10 font-semibold tracking-tight text-black dark:text-zinc-50"> + To get started, edit the page.tsx file. + </h1> + <p className="max-w-md text-lg leading-8 text-zinc-600 dark:text-zinc-400"> + Looking for a starting point or more instructions? Head over to{" "} + <a + href="https://vercel.com/templates?framework=next.js&utm_source=create-next-app&utm_medium=appdir-template-tw&utm_campaign=create-next-app" + className="font-medium text-zinc-950 dark:text-zinc-50" + > + Templates + </a>{" "} + or the{" "} + <a + href="https://nextjs.org/learn?utm_source=create-next-app&utm_medium=appdir-template-tw&utm_campaign=create-next-app" + className="font-medium text-zinc-950 dark:text-zinc-50" + > + Learning + </a>{" "} + center. + </p> + </div> + <div className="flex flex-col gap-4 text-base font-medium sm:flex-row"> + <a + className="flex h-12 w-full items-center justify-center gap-2 rounded-full bg-foreground px-5 text-background transition-colors hover:bg-[#383838] md:w-[158px] dark:hover:bg-[#ccc]" + href="https://vercel.com/new?utm_source=create-next-app&utm_medium=appdir-template-tw&utm_campaign=create-next-app" + target="_blank" + rel="noopener noreferrer" + > + <Image + className="dark:invert" + src="/vercel.svg" + alt="Vercel logomark" + width={16} + height={16} + /> + Deploy Now + </a> + <a + className="flex h-12 w-full items-center justify-center rounded-full border border-solid border-black/[.08] px-5 transition-colors hover:border-transparent hover:bg-black/[.04] md:w-[158px] dark:border-white/[.145] dark:hover:bg-[#1a1a1a]" + href="https://nextjs.org/docs?utm_source=create-next-app&utm_medium=appdir-template-tw&utm_campaign=create-next-app" + target="_blank" + rel="noopener noreferrer" + > + Documentation + </a> + </div> + </main> + </div> + ) +} diff --git a/src/app/providers.tsx b/src/app/providers.tsx new file mode 100644 index 0000000..b6d5965 --- /dev/null +++ b/src/app/providers.tsx @@ -0,0 +1,39 @@ +"use client" + +import { type ReactNode } from "react" + +import { TooltipProvider } from "@/components/ui" +import { + AuthProvider, + I18nProvider, + QueryProvider, + ThemeProvider +} from "@/components/providers" + +type ProvidersProps = { + children: ReactNode +} + +/** + * Composed providers wrapping the entire application. + * + * Nesting order: + * 1. QueryProvider — server state (React Query) + * 2. ThemeProvider — dark/light mode (next-themes) + * 3. TooltipProvider — tooltip primitives (Radix UI) + * 4. I18nProvider — internationalization (next-intl) + * 5. AuthProvider — authentication (Better Auth) + */ +export function Providers({ children }: ProvidersProps) { + return ( + <QueryProvider> + <ThemeProvider> + <TooltipProvider> + <I18nProvider> + <AuthProvider>{children}</AuthProvider> + </I18nProvider> + </TooltipProvider> + </ThemeProvider> + </QueryProvider> + ) +} diff --git a/src/components/AGENTS.md b/src/components/AGENTS.md new file mode 100644 index 0000000..e5ef7f4 --- /dev/null +++ b/src/components/AGENTS.md @@ -0,0 +1,36 @@ +# src/components/ -- Shared Components + +## Fungsi + +Pusat komponen shared yang digunakan di seluruh aplikasi. Dibagi menjadi 4 sub-folder berdasarkan tingkat kompleksitas. + +## Struktur + +``` +components/ +├── ui/ # Atomic UI kit (button, input, badge) +├── layouts/ # Layout components (header, footer, sidebar) +├── widgets/ # Composite widgets (card, modal, table) +└── providers/ # Provider wrappers (theme, query, auth) +``` + +## Aturan Development + +- Komponen spesifik fitur → `src/features/<fitur>/components/` +- Komponen yang dipakai 2+ fitur → shared components +- Gunakan `cn()` utility untuk conditional classes + +## Convention + +- Naming: kebab-case.tsx untuk file, PascalCase untuk component +- Setiap komponen bisa punya `*.types.ts` untuk props + +## Dependency Boundaries + +- Boleh import: dari sesama components (ui → layouts), utils, hooks +- Tidak boleh: import dari features/, app/ + +## AI Do's / Don'ts + +- Boleh: membuat komponen UI baru sesuai pattern yang ada +- Tidak boleh: menambah dependencies baru untuk komponen diff --git a/src/components/index.ts b/src/components/index.ts new file mode 100644 index 0000000..f27cc7b --- /dev/null +++ b/src/components/index.ts @@ -0,0 +1,93 @@ +// Re-export from sub-barrels for convenient top-level access +export { + Avatar, + AvatarImage, + AvatarFallback, + Badge, + Button, + Card, + CardHeader, + CardFooter, + CardTitle, + CardDescription, + CardContent, + Dialog, + DialogPortal, + DialogOverlay, + DialogTrigger, + DialogClose, + DialogContent, + DialogHeader, + DialogFooter, + DialogTitle, + DialogDescription, + DropdownMenu, + DropdownMenuTrigger, + DropdownMenuContent, + DropdownMenuItem, + DropdownMenuCheckboxItem, + DropdownMenuRadioItem, + DropdownMenuLabel, + DropdownMenuSeparator, + DropdownMenuShortcut, + DropdownMenuSub, + DropdownMenuSubTrigger, + DropdownMenuSubContent, + DropdownMenuGroup, + DropdownMenuPortal, + DropdownMenuRadioGroup, + Form, + FormItem, + FormLabel, + FormControl, + FormDescription, + FormMessage, + FormField, + Input, + Label, + Select, + SelectGroup, + SelectValue, + SelectTrigger, + SelectContent, + SelectLabel, + SelectItem, + SelectSeparator, + SelectScrollUpButton, + SelectScrollDownButton, + Separator, + Sheet, + SheetTrigger, + SheetClose, + SheetContent, + SheetHeader, + SheetFooter, + SheetTitle, + SheetDescription, + Skeleton, + SonnerToaster, + Table, + TableHeader, + TableBody, + TableFooter, + TableHead, + TableRow, + TableCell, + TableCaption, + Tabs, + TabsList, + TabsTrigger, + TabsContent, + Textarea, + Tooltip, + TooltipTrigger, + TooltipContent, + TooltipProvider +} from "@/components/ui" +export { + QueryProvider, + ThemeProvider, + I18nProvider, + AuthProvider, + useAuth +} from "@/components/providers" diff --git a/src/components/layouts/AGENTS.md b/src/components/layouts/AGENTS.md new file mode 100644 index 0000000..30882df --- /dev/null +++ b/src/components/layouts/AGENTS.md @@ -0,0 +1,33 @@ +# src/components/layouts/ -- Layout Components + +## Fungsi + +Komponen layout yang mendefinisikan struktur halaman: header, footer, sidebar, container, shell layout. + +## Aturan Development + +- Layout components hanya mengatur struktur, bukan konten +- Gunakan `children` prop / slot pattern untuk konten dinamis +- Responsive: mobile-first dengan breakpoints Tailwind + +## Convention + +- Nama: `app-header.tsx`, `app-footer.tsx`, `sidebar.tsx`, `page-container.tsx` +- Satu layout component = satu file + +## Dependency Boundaries + +- Boleh import: ui/ components, utils +- Tidak boleh: import dari features/, stores +- Tidak boleh: data fetching langsung + +## Best Practices + +- Layout harus tetap stabil saat navigasi (no re-mount) +- Hindari layout shift dengan dimensi eksplisit +- Gunakan CSS Grid atau Flexbox untuk struktur + +## AI Do's / Don'ts + +- Boleh: membuat layout baru, memperbaiki responsive layout +- Tidak boleh: menempatkan business logic di layout components diff --git a/src/components/layouts/index.ts b/src/components/layouts/index.ts new file mode 100644 index 0000000..91fb7ae --- /dev/null +++ b/src/components/layouts/index.ts @@ -0,0 +1,6 @@ +// Layout components barrel +// Export layout components saat sudah dibuat: +// export { AppHeader } from "./app-header" +// export { AppFooter } from "./app-footer" +// export { Sidebar } from "./sidebar" +// export { PageContainer } from "./page-container" diff --git a/src/components/providers/AGENTS.md b/src/components/providers/AGENTS.md new file mode 100644 index 0000000..da56448 --- /dev/null +++ b/src/components/providers/AGENTS.md @@ -0,0 +1,33 @@ +# src/components/providers/ -- Provider Wrappers + +## Fungsi + +Wrapper komponen yang menyediakan context ke seluruh aplikasi: ThemeProvider, QueryProvider, AuthProvider, dll. + +## Aturan Development + +- Provider = komponen Client Component (perlu 'use client') +- Satu provider = satu file +- Provider bisa dikomposisi (nested) di root layout + +## Convention + +- Nama: `theme-provider.tsx`, `query-provider.tsx`, `auth-provider.tsx` +- Nama component: `ThemeProvider`, `QueryProvider`, `AuthProvider` +- Props minimal: `children: React.ReactNode` + +## Dependency Boundaries + +- Boleh import: lib/, config/, stores/ +- Tidak boleh: import dari ui/, layouts/, widgets/, features/ + +## Best Practices + +- Urutan nesting penting: Auth → Query → Theme → Router +- Provider harus pure wrapper -- no visual UI +- Lazy initialization untuk provider berat + +## AI Do's / Don'ts + +- Boleh: membuat provider baru, refactor provider logic +- Tidak boleh: menempatkan provider di tengah component tree diff --git a/src/components/providers/auth-provider.tsx b/src/components/providers/auth-provider.tsx new file mode 100644 index 0000000..004d6ba --- /dev/null +++ b/src/components/providers/auth-provider.tsx @@ -0,0 +1,58 @@ +"use client" + +import { createContext, useContext, useState, type ReactNode } from "react" + +type User = { + id: string + name: string + email: string +} | null + +type AuthContextType = { + user: User + isLoading: boolean + login: (email: string, password: string) => Promise<void> + logout: () => void +} + +const AuthContext = createContext<AuthContextType | undefined>(undefined) + +type AuthProviderProps = { + children: ReactNode +} + +/** + * AuthProvider placeholder untuk integrasi Better Auth. + * + * TODO: Implementasi penuh setelah Better Auth dikonfigurasi: + * - src/lib/auth.ts (server) + * - src/lib/auth-client.ts (client) + * - src/features/auth/api/ + */ +export function AuthProvider({ children }: AuthProviderProps) { + const [user, setUser] = useState<User>(null) + const [isLoading] = useState(false) + + const login = async (_email: string, _password: string) => { + // Placeholder: implementasi dengan Better Auth + console.warn("AuthProvider: login belum diimplementasikan") + } + + const logout = () => { + setUser(null) + } + + return ( + <AuthContext.Provider value={{ user, isLoading, login, logout }}> + {children} + </AuthContext.Provider> + ) +} + +export function useAuth() { + const context = useContext(AuthContext) + if (!context) { + throw new Error("useAuth must be used within an AuthProvider") + } + return context +} diff --git a/src/components/providers/i18n-provider.tsx b/src/components/providers/i18n-provider.tsx new file mode 100644 index 0000000..f0fa9dd --- /dev/null +++ b/src/components/providers/i18n-provider.tsx @@ -0,0 +1,19 @@ +import type { ReactNode } from "react" + +type I18nProviderProps = { + children: ReactNode +} + +/** + * I18nProvider placeholder untuk next-intl. + * + * TODO: Implementasi penuh setelah next-intl routing dikonfigurasi: + * - src/i18n/routing.ts + * - src/i18n/request.ts + * - app/[locale]/ layout + * + * Referensi: https://next-intl.dev/docs/getting-started/app-router + */ +export function I18nProvider({ children }: I18nProviderProps) { + return <>{children}</> +} diff --git a/src/components/providers/index.ts b/src/components/providers/index.ts new file mode 100644 index 0000000..38772de --- /dev/null +++ b/src/components/providers/index.ts @@ -0,0 +1,4 @@ +export { QueryProvider } from "./query-provider" +export { ThemeProvider } from "./theme-provider" +export { I18nProvider } from "./i18n-provider" +export { AuthProvider, useAuth } from "./auth-provider" diff --git a/src/components/providers/query-provider.tsx b/src/components/providers/query-provider.tsx new file mode 100644 index 0000000..966e381 --- /dev/null +++ b/src/components/providers/query-provider.tsx @@ -0,0 +1,46 @@ +"use client" + +import { useState, type ReactNode } from "react" + +import { QueryClient, QueryClientProvider } from "@tanstack/react-query" +import { ReactQueryDevtools } from "@tanstack/react-query-devtools" + +function makeQueryClient() { + return new QueryClient({ + defaultOptions: { + queries: { + staleTime: 1000 * 60 * 5, + gcTime: 1000 * 60 * 30, + retry: 1, + refetchOnWindowFocus: false + } + } + }) +} + +let browserQueryClient: QueryClient | undefined + +function getQueryClient() { + if (typeof window === "undefined") { + return makeQueryClient() + } + if (!browserQueryClient) { + browserQueryClient = makeQueryClient() + } + return browserQueryClient +} + +type QueryProviderProps = { + children: ReactNode +} + +export function QueryProvider({ children }: QueryProviderProps) { + const queryClient = getQueryClient() + + return ( + <QueryClientProvider client={queryClient}> + {children} + <ReactQueryDevtools initialIsOpen={false} buttonPosition="bottom-right" /> + </QueryClientProvider> + ) +} diff --git a/src/components/providers/theme-provider.tsx b/src/components/providers/theme-provider.tsx new file mode 100644 index 0000000..b5e471b --- /dev/null +++ b/src/components/providers/theme-provider.tsx @@ -0,0 +1,21 @@ +"use client" + +import type { ReactNode } from "react" +import { ThemeProvider as NextThemesProvider } from "next-themes" + +type ThemeProviderProps = { + children: ReactNode +} + +export function ThemeProvider({ children }: ThemeProviderProps) { + return ( + <NextThemesProvider + attribute="class" + defaultTheme="system" + enableSystem + disableTransitionOnChange + > + {children} + </NextThemesProvider> + ) +} diff --git a/src/components/ui/AGENTS.md b/src/components/ui/AGENTS.md new file mode 100644 index 0000000..5c7b0e9 --- /dev/null +++ b/src/components/ui/AGENTS.md @@ -0,0 +1,35 @@ +# src/components/ui/ -- UI Kit + +## Fungsi + +Atomic UI components -- building block terkecil dari sistem desain. Berbasis shadcn/ui + Radix UI primitives. + +## Aturan Development + +- Satu komponen = satu file +- Aksesible by default (ARIA, keyboard nav, focus management) +- Menggunakan Tailwind utility classes, NO inline styles +- Props selalu extends dari HTMLAttributes atau specific interface + +## Convention + +- Nama file: kebab-case (button.tsx, input.tsx, badge.tsx) +- Nama component: PascalCase (Button, Input, Badge) +- Props type: `ButtonProps`, `InputProps` + +## Dependency Boundaries + +- Boleh import: utils (cn), hooks (use-id) +- Tidak boleh: import dari features/, stores, lib +- Tidak boleh: business logic, API calls + +## Best Practices + +- Forward ref dengan `forwardRef` +- Spread props pattern untuk flexibility +- Gunakan `cva` (class-variance-authority) untuk variants + +## AI Do's / Don'ts + +- Boleh: menambah variant, memperbaiki aksesibilitas +- Tidak boleh: menambah business logic di komponen UI diff --git a/src/components/ui/avatar.tsx b/src/components/ui/avatar.tsx new file mode 100644 index 0000000..17291bb --- /dev/null +++ b/src/components/ui/avatar.tsx @@ -0,0 +1,110 @@ +"use client" + +import * as React from "react" + +import { Avatar as AvatarPrimitive } from "radix-ui" + +import { cn } from "@/utils" + +function Avatar({ + className, + size = "default", + ...props +}: React.ComponentProps<typeof AvatarPrimitive.Root> & { + size?: "default" | "sm" | "lg" +}) { + return ( + <AvatarPrimitive.Root + data-slot="avatar" + data-size={size} + className={cn( + "group/avatar relative flex size-8 shrink-0 overflow-hidden rounded-full select-none data-[size=lg]:size-10 data-[size=sm]:size-6", + className + )} + {...props} + /> + ) +} + +function AvatarImage({ + className, + ...props +}: React.ComponentProps<typeof AvatarPrimitive.Image>) { + return ( + <AvatarPrimitive.Image + data-slot="avatar-image" + className={cn("aspect-square size-full", className)} + {...props} + /> + ) +} + +function AvatarFallback({ + className, + ...props +}: React.ComponentProps<typeof AvatarPrimitive.Fallback>) { + return ( + <AvatarPrimitive.Fallback + data-slot="avatar-fallback" + className={cn( + "flex size-full items-center justify-center rounded-full bg-muted text-sm text-muted-foreground group-data-[size=sm]/avatar:text-xs", + className + )} + {...props} + /> + ) +} + +function AvatarBadge({ className, ...props }: React.ComponentProps<"span">) { + return ( + <span + data-slot="avatar-badge" + className={cn( + "absolute right-0 bottom-0 z-10 inline-flex items-center justify-center rounded-full bg-primary text-primary-foreground ring-2 ring-background select-none", + "group-data-[size=sm]/avatar:size-2 group-data-[size=sm]/avatar:[&>svg]:hidden", + "group-data-[size=default]/avatar:size-2.5 group-data-[size=default]/avatar:[&>svg]:size-2", + "group-data-[size=lg]/avatar:size-3 group-data-[size=lg]/avatar:[&>svg]:size-2", + className + )} + {...props} + /> + ) +} + +function AvatarGroup({ className, ...props }: React.ComponentProps<"div">) { + return ( + <div + data-slot="avatar-group" + className={cn( + "group/avatar-group flex -space-x-2 *:data-[slot=avatar]:ring-2 *:data-[slot=avatar]:ring-background", + className + )} + {...props} + /> + ) +} + +function AvatarGroupCount({ + className, + ...props +}: React.ComponentProps<"div">) { + return ( + <div + data-slot="avatar-group-count" + className={cn( + "relative flex size-8 shrink-0 items-center justify-center rounded-full bg-muted text-sm text-muted-foreground ring-2 ring-background group-has-data-[size=lg]/avatar-group:size-10 group-has-data-[size=sm]/avatar-group:size-6 [&>svg]:size-4 group-has-data-[size=lg]/avatar-group:[&>svg]:size-5 group-has-data-[size=sm]/avatar-group:[&>svg]:size-3", + className + )} + {...props} + /> + ) +} + +export { + Avatar, + AvatarImage, + AvatarFallback, + AvatarBadge, + AvatarGroup, + AvatarGroupCount +} diff --git a/src/components/ui/badge.tsx b/src/components/ui/badge.tsx new file mode 100644 index 0000000..cd96c87 --- /dev/null +++ b/src/components/ui/badge.tsx @@ -0,0 +1,49 @@ +import * as React from "react" + +import { cva, type VariantProps } from "class-variance-authority" +import { Slot } from "radix-ui" + +import { cn } from "@/utils" + +const badgeVariants = cva( + "inline-flex w-fit shrink-0 items-center justify-center gap-1 overflow-hidden rounded-full border border-transparent px-2 py-0.5 text-xs font-medium whitespace-nowrap transition-[color,box-shadow] focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50 aria-invalid:border-destructive aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 [&>svg]:pointer-events-none [&>svg]:size-3", + { + variants: { + variant: { + default: "bg-primary text-primary-foreground [a&]:hover:bg-primary/90", + secondary: + "bg-secondary text-secondary-foreground [a&]:hover:bg-secondary/90", + destructive: + "bg-destructive text-white focus-visible:ring-destructive/20 dark:bg-destructive/60 dark:focus-visible:ring-destructive/40 [a&]:hover:bg-destructive/90", + outline: + "border-border text-foreground [a&]:hover:bg-accent [a&]:hover:text-accent-foreground", + ghost: "[a&]:hover:bg-accent [a&]:hover:text-accent-foreground", + link: "text-primary underline-offset-4 [a&]:hover:underline" + } + }, + defaultVariants: { + variant: "default" + } + } +) + +function Badge({ + className, + variant = "default", + asChild = false, + ...props +}: React.ComponentProps<"span"> & + VariantProps<typeof badgeVariants> & { asChild?: boolean }) { + const Comp = asChild ? Slot.Root : "span" + + return ( + <Comp + data-slot="badge" + data-variant={variant} + className={cn(badgeVariants({ variant }), className)} + {...props} + /> + ) +} + +export { Badge, badgeVariants } diff --git a/src/components/ui/button.tsx b/src/components/ui/button.tsx new file mode 100644 index 0000000..26a3f00 --- /dev/null +++ b/src/components/ui/button.tsx @@ -0,0 +1,65 @@ +import * as React from "react" + +import { cva, type VariantProps } from "class-variance-authority" +import { Slot } from "radix-ui" + +import { cn } from "@/utils" + +const buttonVariants = cva( + "inline-flex shrink-0 items-center justify-center gap-2 rounded-md text-sm font-medium whitespace-nowrap transition-all outline-none focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50 disabled:pointer-events-none disabled:opacity-50 aria-invalid:border-destructive aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4", + { + variants: { + variant: { + default: "bg-primary text-primary-foreground hover:bg-primary/90", + destructive: + "bg-destructive text-white hover:bg-destructive/90 focus-visible:ring-destructive/20 dark:bg-destructive/60 dark:focus-visible:ring-destructive/40", + outline: + "border bg-background shadow-xs hover:bg-accent hover:text-accent-foreground dark:border-input dark:bg-input/30 dark:hover:bg-input/50", + secondary: + "bg-secondary text-secondary-foreground hover:bg-secondary/80", + ghost: + "hover:bg-accent hover:text-accent-foreground dark:hover:bg-accent/50", + link: "text-primary underline-offset-4 hover:underline" + }, + size: { + default: "h-9 px-4 py-2 has-[>svg]:px-3", + xs: "h-6 gap-1 rounded-md px-2 text-xs has-[>svg]:px-1.5 [&_svg:not([class*='size-'])]:size-3", + sm: "h-8 gap-1.5 rounded-md px-3 has-[>svg]:px-2.5", + lg: "h-10 rounded-md px-6 has-[>svg]:px-4", + icon: "size-9", + "icon-xs": "size-6 rounded-md [&_svg:not([class*='size-'])]:size-3", + "icon-sm": "size-8", + "icon-lg": "size-10" + } + }, + defaultVariants: { + variant: "default", + size: "default" + } + } +) + +function Button({ + className, + variant = "default", + size = "default", + asChild = false, + ...props +}: React.ComponentProps<"button"> & + VariantProps<typeof buttonVariants> & { + asChild?: boolean + }) { + const Comp = asChild ? Slot.Root : "button" + + return ( + <Comp + data-slot="button" + data-variant={variant} + data-size={size} + className={cn(buttonVariants({ variant, size, className }))} + {...props} + /> + ) +} + +export { Button, buttonVariants } diff --git a/src/components/ui/card.tsx b/src/components/ui/card.tsx new file mode 100644 index 0000000..1a7b8b6 --- /dev/null +++ b/src/components/ui/card.tsx @@ -0,0 +1,92 @@ +import * as React from "react" + +import { cn } from "@/utils" + +function Card({ className, ...props }: React.ComponentProps<"div">) { + return ( + <div + data-slot="card" + className={cn( + "flex flex-col gap-6 rounded-xl border bg-card py-6 text-card-foreground shadow-sm", + className + )} + {...props} + /> + ) +} + +function CardHeader({ className, ...props }: React.ComponentProps<"div">) { + return ( + <div + data-slot="card-header" + className={cn( + "@container/card-header grid auto-rows-min grid-rows-[auto_auto] items-start gap-2 px-6 has-data-[slot=card-action]:grid-cols-[1fr_auto] [.border-b]:pb-6", + className + )} + {...props} + /> + ) +} + +function CardTitle({ className, ...props }: React.ComponentProps<"div">) { + return ( + <div + data-slot="card-title" + className={cn("leading-none font-semibold", className)} + {...props} + /> + ) +} + +function CardDescription({ className, ...props }: React.ComponentProps<"div">) { + return ( + <div + data-slot="card-description" + className={cn("text-sm text-muted-foreground", className)} + {...props} + /> + ) +} + +function CardAction({ className, ...props }: React.ComponentProps<"div">) { + return ( + <div + data-slot="card-action" + className={cn( + "col-start-2 row-span-2 row-start-1 self-start justify-self-end", + className + )} + {...props} + /> + ) +} + +function CardContent({ className, ...props }: React.ComponentProps<"div">) { + return ( + <div + data-slot="card-content" + className={cn("px-6", className)} + {...props} + /> + ) +} + +function CardFooter({ className, ...props }: React.ComponentProps<"div">) { + return ( + <div + data-slot="card-footer" + className={cn("flex items-center px-6 [.border-t]:pt-6", className)} + {...props} + /> + ) +} + +export { + Card, + CardHeader, + CardFooter, + CardTitle, + CardAction, + CardDescription, + CardContent +} diff --git a/src/components/ui/dialog.tsx b/src/components/ui/dialog.tsx new file mode 100644 index 0000000..a85d099 --- /dev/null +++ b/src/components/ui/dialog.tsx @@ -0,0 +1,160 @@ +"use client" + +import * as React from "react" + +import { XIcon } from "lucide-react" +import { Dialog as DialogPrimitive } from "radix-ui" + +import { cn } from "@/utils" + +import { Button } from "@/components/ui" + +function Dialog({ + ...props +}: React.ComponentProps<typeof DialogPrimitive.Root>) { + return <DialogPrimitive.Root data-slot="dialog" {...props} /> +} + +function DialogTrigger({ + ...props +}: React.ComponentProps<typeof DialogPrimitive.Trigger>) { + return <DialogPrimitive.Trigger data-slot="dialog-trigger" {...props} /> +} + +function DialogPortal({ + ...props +}: React.ComponentProps<typeof DialogPrimitive.Portal>) { + return <DialogPrimitive.Portal data-slot="dialog-portal" {...props} /> +} + +function DialogClose({ + ...props +}: React.ComponentProps<typeof DialogPrimitive.Close>) { + return <DialogPrimitive.Close data-slot="dialog-close" {...props} /> +} + +function DialogOverlay({ + className, + ...props +}: React.ComponentProps<typeof DialogPrimitive.Overlay>) { + return ( + <DialogPrimitive.Overlay + data-slot="dialog-overlay" + className={cn( + "data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:animate-in data-[state=open]:fade-in-0 fixed inset-0 z-50 bg-black/50", + className + )} + {...props} + /> + ) +} + +function DialogContent({ + className, + children, + showCloseButton = true, + ...props +}: React.ComponentProps<typeof DialogPrimitive.Content> & { + showCloseButton?: boolean +}) { + return ( + <DialogPortal data-slot="dialog-portal"> + <DialogOverlay /> + <DialogPrimitive.Content + data-slot="dialog-content" + className={cn( + "data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95 data-[state=open]:animate-in data-[state=open]:fade-in-0 data-[state=open]:zoom-in-95 fixed top-[50%] left-[50%] z-50 grid w-full max-w-[calc(100%-2rem)] translate-x-[-50%] translate-y-[-50%] gap-4 rounded-lg border bg-background p-6 shadow-lg duration-200 outline-none sm:max-w-lg", + className + )} + {...props} + > + {children} + {showCloseButton && ( + <DialogPrimitive.Close + data-slot="dialog-close" + className="absolute top-4 right-4 rounded-xs opacity-70 ring-offset-background transition-opacity hover:opacity-100 focus:ring-2 focus:ring-ring focus:ring-offset-2 focus:outline-hidden disabled:pointer-events-none data-[state=open]:bg-accent data-[state=open]:text-muted-foreground [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4" + > + <XIcon /> + <span className="sr-only">Close</span> + </DialogPrimitive.Close> + )} + </DialogPrimitive.Content> + </DialogPortal> + ) +} + +function DialogHeader({ className, ...props }: React.ComponentProps<"div">) { + return ( + <div + data-slot="dialog-header" + className={cn("flex flex-col gap-2 text-center sm:text-left", className)} + {...props} + /> + ) +} + +function DialogFooter({ + className, + showCloseButton = false, + children, + ...props +}: React.ComponentProps<"div"> & { + showCloseButton?: boolean +}) { + return ( + <div + data-slot="dialog-footer" + className={cn( + "flex flex-col-reverse gap-2 sm:flex-row sm:justify-end", + className + )} + {...props} + > + {children} + {showCloseButton && ( + <DialogPrimitive.Close asChild> + <Button variant="outline">Close</Button> + </DialogPrimitive.Close> + )} + </div> + ) +} + +function DialogTitle({ + className, + ...props +}: React.ComponentProps<typeof DialogPrimitive.Title>) { + return ( + <DialogPrimitive.Title + data-slot="dialog-title" + className={cn("text-lg leading-none font-semibold", className)} + {...props} + /> + ) +} + +function DialogDescription({ + className, + ...props +}: React.ComponentProps<typeof DialogPrimitive.Description>) { + return ( + <DialogPrimitive.Description + data-slot="dialog-description" + className={cn("text-sm text-muted-foreground", className)} + {...props} + /> + ) +} + +export { + Dialog, + DialogClose, + DialogContent, + DialogDescription, + DialogFooter, + DialogHeader, + DialogOverlay, + DialogPortal, + DialogTitle, + DialogTrigger +} diff --git a/src/components/ui/dropdown-menu.tsx b/src/components/ui/dropdown-menu.tsx new file mode 100644 index 0000000..4f1b8ad --- /dev/null +++ b/src/components/ui/dropdown-menu.tsx @@ -0,0 +1,258 @@ +"use client" + +import * as React from "react" + +import { CheckIcon, ChevronRightIcon, CircleIcon } from "lucide-react" +import { DropdownMenu as DropdownMenuPrimitive } from "radix-ui" + +import { cn } from "@/utils" + +function DropdownMenu({ + ...props +}: React.ComponentProps<typeof DropdownMenuPrimitive.Root>) { + return <DropdownMenuPrimitive.Root data-slot="dropdown-menu" {...props} /> +} + +function DropdownMenuPortal({ + ...props +}: React.ComponentProps<typeof DropdownMenuPrimitive.Portal>) { + return ( + <DropdownMenuPrimitive.Portal data-slot="dropdown-menu-portal" {...props} /> + ) +} + +function DropdownMenuTrigger({ + ...props +}: React.ComponentProps<typeof DropdownMenuPrimitive.Trigger>) { + return ( + <DropdownMenuPrimitive.Trigger + data-slot="dropdown-menu-trigger" + {...props} + /> + ) +} + +function DropdownMenuContent({ + className, + sideOffset = 4, + ...props +}: React.ComponentProps<typeof DropdownMenuPrimitive.Content>) { + return ( + <DropdownMenuPrimitive.Portal> + <DropdownMenuPrimitive.Content + data-slot="dropdown-menu-content" + sideOffset={sideOffset} + className={cn( + "data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95 data-[state=open]:animate-in data-[state=open]:fade-in-0 data-[state=open]:zoom-in-95 z-50 max-h-(--radix-dropdown-menu-content-available-height) min-w-[8rem] origin-(--radix-dropdown-menu-content-transform-origin) overflow-x-hidden overflow-y-auto rounded-md border bg-popover p-1 text-popover-foreground shadow-md", + className + )} + {...props} + /> + </DropdownMenuPrimitive.Portal> + ) +} + +function DropdownMenuGroup({ + ...props +}: React.ComponentProps<typeof DropdownMenuPrimitive.Group>) { + return ( + <DropdownMenuPrimitive.Group data-slot="dropdown-menu-group" {...props} /> + ) +} + +function DropdownMenuItem({ + className, + inset, + variant = "default", + ...props +}: React.ComponentProps<typeof DropdownMenuPrimitive.Item> & { + inset?: boolean + variant?: "default" | "destructive" +}) { + return ( + <DropdownMenuPrimitive.Item + data-slot="dropdown-menu-item" + data-inset={inset} + data-variant={variant} + className={cn( + "relative flex cursor-default items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-hidden select-none focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50 data-[inset]:pl-8 data-[variant=destructive]:text-destructive data-[variant=destructive]:focus:bg-destructive/10 data-[variant=destructive]:focus:text-destructive dark:data-[variant=destructive]:focus:bg-destructive/20 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4 [&_svg:not([class*='text-'])]:text-muted-foreground data-[variant=destructive]:*:[svg]:text-destructive!", + className + )} + {...props} + /> + ) +} + +function DropdownMenuCheckboxItem({ + className, + children, + checked, + ...props +}: React.ComponentProps<typeof DropdownMenuPrimitive.CheckboxItem>) { + return ( + <DropdownMenuPrimitive.CheckboxItem + data-slot="dropdown-menu-checkbox-item" + className={cn( + "relative flex cursor-default items-center gap-2 rounded-sm py-1.5 pr-2 pl-8 text-sm outline-hidden select-none focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4", + className + )} + checked={checked} + {...props} + > + <span className="pointer-events-none absolute left-2 flex size-3.5 items-center justify-center"> + <DropdownMenuPrimitive.ItemIndicator> + <CheckIcon className="size-4" /> + </DropdownMenuPrimitive.ItemIndicator> + </span> + {children} + </DropdownMenuPrimitive.CheckboxItem> + ) +} + +function DropdownMenuRadioGroup({ + ...props +}: React.ComponentProps<typeof DropdownMenuPrimitive.RadioGroup>) { + return ( + <DropdownMenuPrimitive.RadioGroup + data-slot="dropdown-menu-radio-group" + {...props} + /> + ) +} + +function DropdownMenuRadioItem({ + className, + children, + ...props +}: React.ComponentProps<typeof DropdownMenuPrimitive.RadioItem>) { + return ( + <DropdownMenuPrimitive.RadioItem + data-slot="dropdown-menu-radio-item" + className={cn( + "relative flex cursor-default items-center gap-2 rounded-sm py-1.5 pr-2 pl-8 text-sm outline-hidden select-none focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4", + className + )} + {...props} + > + <span className="pointer-events-none absolute left-2 flex size-3.5 items-center justify-center"> + <DropdownMenuPrimitive.ItemIndicator> + <CircleIcon className="size-2 fill-current" /> + </DropdownMenuPrimitive.ItemIndicator> + </span> + {children} + </DropdownMenuPrimitive.RadioItem> + ) +} + +function DropdownMenuLabel({ + className, + inset, + ...props +}: React.ComponentProps<typeof DropdownMenuPrimitive.Label> & { + inset?: boolean +}) { + return ( + <DropdownMenuPrimitive.Label + data-slot="dropdown-menu-label" + data-inset={inset} + className={cn( + "px-2 py-1.5 text-sm font-medium data-[inset]:pl-8", + className + )} + {...props} + /> + ) +} + +function DropdownMenuSeparator({ + className, + ...props +}: React.ComponentProps<typeof DropdownMenuPrimitive.Separator>) { + return ( + <DropdownMenuPrimitive.Separator + data-slot="dropdown-menu-separator" + className={cn("-mx-1 my-1 h-px bg-border", className)} + {...props} + /> + ) +} + +function DropdownMenuShortcut({ + className, + ...props +}: React.ComponentProps<"span">) { + return ( + <span + data-slot="dropdown-menu-shortcut" + className={cn( + "ml-auto text-xs tracking-widest text-muted-foreground", + className + )} + {...props} + /> + ) +} + +function DropdownMenuSub({ + ...props +}: React.ComponentProps<typeof DropdownMenuPrimitive.Sub>) { + return <DropdownMenuPrimitive.Sub data-slot="dropdown-menu-sub" {...props} /> +} + +function DropdownMenuSubTrigger({ + className, + inset, + children, + ...props +}: React.ComponentProps<typeof DropdownMenuPrimitive.SubTrigger> & { + inset?: boolean +}) { + return ( + <DropdownMenuPrimitive.SubTrigger + data-slot="dropdown-menu-sub-trigger" + data-inset={inset} + className={cn( + "flex cursor-default items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-hidden select-none focus:bg-accent focus:text-accent-foreground data-[inset]:pl-8 data-[state=open]:bg-accent data-[state=open]:text-accent-foreground [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4 [&_svg:not([class*='text-'])]:text-muted-foreground", + className + )} + {...props} + > + {children} + <ChevronRightIcon className="ml-auto size-4" /> + </DropdownMenuPrimitive.SubTrigger> + ) +} + +function DropdownMenuSubContent({ + className, + ...props +}: React.ComponentProps<typeof DropdownMenuPrimitive.SubContent>) { + return ( + <DropdownMenuPrimitive.SubContent + data-slot="dropdown-menu-sub-content" + className={cn( + "data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95 data-[state=open]:animate-in data-[state=open]:fade-in-0 data-[state=open]:zoom-in-95 z-50 min-w-[8rem] origin-(--radix-dropdown-menu-content-transform-origin) overflow-hidden rounded-md border bg-popover p-1 text-popover-foreground shadow-lg", + className + )} + {...props} + /> + ) +} + +export { + DropdownMenu, + DropdownMenuPortal, + DropdownMenuTrigger, + DropdownMenuContent, + DropdownMenuGroup, + DropdownMenuLabel, + DropdownMenuItem, + DropdownMenuCheckboxItem, + DropdownMenuRadioGroup, + DropdownMenuRadioItem, + DropdownMenuSeparator, + DropdownMenuShortcut, + DropdownMenuSub, + DropdownMenuSubTrigger, + DropdownMenuSubContent +} diff --git a/src/components/ui/form.tsx b/src/components/ui/form.tsx new file mode 100644 index 0000000..aed39fa --- /dev/null +++ b/src/components/ui/form.tsx @@ -0,0 +1,169 @@ +"use client" + +import * as React from "react" +import { + Controller, + FormProvider, + useFormContext, + useFormState, + type ControllerProps, + type FieldPath, + type FieldValues +} from "react-hook-form" + +import type { Label as LabelPrimitive } from "radix-ui" +import { Slot } from "radix-ui" + +import { cn } from "@/utils" + +import { Label } from "@/components/ui" + +const Form = FormProvider + +type FormFieldContextValue< + TFieldValues extends FieldValues = FieldValues, + TName extends FieldPath<TFieldValues> = FieldPath<TFieldValues> +> = { + name: TName +} + +const FormFieldContext = React.createContext<FormFieldContextValue>( + {} as FormFieldContextValue +) + +const FormField = < + TFieldValues extends FieldValues = FieldValues, + TName extends FieldPath<TFieldValues> = FieldPath<TFieldValues> +>({ + ...props +}: ControllerProps<TFieldValues, TName>) => { + return ( + <FormFieldContext.Provider value={{ name: props.name }}> + <Controller {...props} /> + </FormFieldContext.Provider> + ) +} + +const useFormField = () => { + const fieldContext = React.useContext(FormFieldContext) + const itemContext = React.useContext(FormItemContext) + const { getFieldState } = useFormContext() + const formState = useFormState({ name: fieldContext.name }) + const fieldState = getFieldState(fieldContext.name, formState) + + if (!fieldContext) { + throw new Error("useFormField should be used within <FormField>") + } + + const { id } = itemContext + + return { + id, + name: fieldContext.name, + formItemId: `${id}-form-item`, + formDescriptionId: `${id}-form-item-description`, + formMessageId: `${id}-form-item-message`, + ...fieldState + } +} + +type FormItemContextValue = { + id: string +} + +const FormItemContext = React.createContext<FormItemContextValue>( + {} as FormItemContextValue +) + +function FormItem({ className, ...props }: React.ComponentProps<"div">) { + const id = React.useId() + + return ( + <FormItemContext.Provider value={{ id }}> + <div + data-slot="form-item" + className={cn("grid gap-2", className)} + {...props} + /> + </FormItemContext.Provider> + ) +} + +function FormLabel({ + className, + ...props +}: React.ComponentProps<typeof LabelPrimitive.Root>) { + const { error, formItemId } = useFormField() + + return ( + <Label + data-slot="form-label" + data-error={!!error} + className={cn("data-[error=true]:text-destructive", className)} + htmlFor={formItemId} + {...props} + /> + ) +} + +function FormControl({ ...props }: React.ComponentProps<typeof Slot.Root>) { + const { error, formItemId, formDescriptionId, formMessageId } = useFormField() + + return ( + <Slot.Root + data-slot="form-control" + id={formItemId} + aria-describedby={ + !error + ? `${formDescriptionId}` + : `${formDescriptionId} ${formMessageId}` + } + aria-invalid={!!error} + {...props} + /> + ) +} + +function FormDescription({ className, ...props }: React.ComponentProps<"p">) { + const { formDescriptionId } = useFormField() + + return ( + <p + data-slot="form-description" + id={formDescriptionId} + className={cn("text-sm text-muted-foreground", className)} + {...props} + /> + ) +} + +function FormMessage({ className, ...props }: React.ComponentProps<"p">) { + const { error, formMessageId } = useFormField() + const body = error ? String(error?.message ?? "") : props.children + + if (!body) { + return null + } + + return ( + <p + data-slot="form-message" + id={formMessageId} + className={cn("text-sm text-destructive", className)} + {...props} + > + {body} + </p> + ) +} + +export { + useFormField, + Form, + FormItem, + FormLabel, + FormControl, + FormDescription, + FormMessage, + FormField +} diff --git a/src/components/ui/index.ts b/src/components/ui/index.ts new file mode 100644 index 0000000..22f2d91 --- /dev/null +++ b/src/components/ui/index.ts @@ -0,0 +1,95 @@ +export { Avatar, AvatarFallback, AvatarImage } from "./avatar" +export { Badge } from "./badge" +export { Button } from "./button" +export { + Card, + CardContent, + CardDescription, + CardFooter, + CardHeader, + CardTitle +} from "./card" +export { + Dialog, + DialogClose, + DialogContent, + DialogDescription, + DialogFooter, + DialogHeader, + DialogOverlay, + DialogPortal, + DialogTitle, + DialogTrigger +} from "./dialog" +export { + DropdownMenu, + DropdownMenuCheckboxItem, + DropdownMenuContent, + DropdownMenuGroup, + DropdownMenuItem, + DropdownMenuLabel, + DropdownMenuPortal, + DropdownMenuRadioGroup, + DropdownMenuRadioItem, + DropdownMenuSeparator, + DropdownMenuShortcut, + DropdownMenuSub, + DropdownMenuSubContent, + DropdownMenuSubTrigger, + DropdownMenuTrigger +} from "./dropdown-menu" +export { + Form, + FormControl, + FormDescription, + FormField, + FormItem, + FormLabel, + FormMessage +} from "./form" +export { Input } from "./input" +export { Label } from "./label" +export { + Select, + SelectContent, + SelectGroup, + SelectItem, + SelectLabel, + SelectScrollDownButton, + SelectScrollUpButton, + SelectSeparator, + SelectTrigger, + SelectValue +} from "./select" +export { Separator } from "./separator" +export { + Sheet, + SheetClose, + SheetContent, + SheetDescription, + SheetFooter, + SheetHeader, + SheetTitle, + SheetTrigger +} from "./sheet" +export { Skeleton } from "./skeleton" +export { Toaster as SonnerToaster } from "./sonner" +export { + Table, + TableBody, + TableCaption, + TableCell, + TableFooter, + TableHead, + TableHeader, + TableRow +} from "./table" +export { Tabs, TabsContent, TabsList, TabsTrigger } from "./tabs" +export { Textarea } from "./textarea" +export { + Tooltip, + TooltipContent, + TooltipProvider, + TooltipTrigger +} from "./tooltip" +export {} from "./typography" diff --git a/src/components/ui/input.tsx b/src/components/ui/input.tsx new file mode 100644 index 0000000..44a237c --- /dev/null +++ b/src/components/ui/input.tsx @@ -0,0 +1,21 @@ +import * as React from "react" + +import { cn } from "@/utils" + +function Input({ className, type, ...props }: React.ComponentProps<"input">) { + return ( + <input + type={type} + data-slot="input" + className={cn( + "h-9 w-full min-w-0 rounded-md border border-input bg-transparent px-3 py-1 text-base shadow-xs transition-[color,box-shadow] outline-none selection:bg-primary selection:text-primary-foreground file:inline-flex file:h-7 file:border-0 file:bg-transparent file:text-sm file:font-medium file:text-foreground placeholder:text-muted-foreground disabled:pointer-events-none disabled:cursor-not-allowed disabled:opacity-50 md:text-sm dark:bg-input/30", + "focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50", + "aria-invalid:border-destructive aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40", + className + )} + {...props} + /> + ) +} + +export { Input } diff --git a/src/components/ui/label.tsx b/src/components/ui/label.tsx new file mode 100644 index 0000000..a261da5 --- /dev/null +++ b/src/components/ui/label.tsx @@ -0,0 +1,25 @@ +"use client" + +import * as React from "react" + +import { Label as LabelPrimitive } from "radix-ui" + +import { cn } from "@/utils" + +function Label({ + className, + ...props +}: React.ComponentProps<typeof LabelPrimitive.Root>) { + return ( + <LabelPrimitive.Root + data-slot="label" + className={cn( + "flex items-center gap-2 text-sm leading-none font-medium select-none group-data-[disabled=true]:pointer-events-none group-data-[disabled=true]:opacity-50 peer-disabled:cursor-not-allowed peer-disabled:opacity-50", + className + )} + {...props} + /> + ) +} + +export { Label } diff --git a/src/components/ui/select.tsx b/src/components/ui/select.tsx new file mode 100644 index 0000000..e8e3372 --- /dev/null +++ b/src/components/ui/select.tsx @@ -0,0 +1,191 @@ +"use client" + +import * as React from "react" + +import { CheckIcon, ChevronDownIcon, ChevronUpIcon } from "lucide-react" +import { Select as SelectPrimitive } from "radix-ui" + +import { cn } from "@/utils" + +function Select({ + ...props +}: React.ComponentProps<typeof SelectPrimitive.Root>) { + return <SelectPrimitive.Root data-slot="select" {...props} /> +} + +function SelectGroup({ + ...props +}: React.ComponentProps<typeof SelectPrimitive.Group>) { + return <SelectPrimitive.Group data-slot="select-group" {...props} /> +} + +function SelectValue({ + ...props +}: React.ComponentProps<typeof SelectPrimitive.Value>) { + return <SelectPrimitive.Value data-slot="select-value" {...props} /> +} + +function SelectTrigger({ + className, + size = "default", + children, + ...props +}: React.ComponentProps<typeof SelectPrimitive.Trigger> & { + size?: "sm" | "default" +}) { + return ( + <SelectPrimitive.Trigger + data-slot="select-trigger" + data-size={size} + className={cn( + "flex w-fit items-center justify-between gap-2 rounded-md border border-input bg-transparent px-3 py-2 text-sm whitespace-nowrap shadow-xs transition-[color,box-shadow] outline-none focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50 disabled:cursor-not-allowed disabled:opacity-50 aria-invalid:border-destructive aria-invalid:ring-destructive/20 data-[placeholder]:text-muted-foreground data-[size=default]:h-9 data-[size=sm]:h-8 *:data-[slot=select-value]:line-clamp-1 *:data-[slot=select-value]:flex *:data-[slot=select-value]:items-center *:data-[slot=select-value]:gap-2 dark:bg-input/30 dark:hover:bg-input/50 dark:aria-invalid:ring-destructive/40 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4 [&_svg:not([class*='text-'])]:text-muted-foreground", + className + )} + {...props} + > + {children} + <SelectPrimitive.Icon asChild> + <ChevronDownIcon className="size-4 opacity-50" /> + </SelectPrimitive.Icon> + </SelectPrimitive.Trigger> + ) +} + +function SelectContent({ + className, + children, + position = "item-aligned", + align = "center", + ...props +}: React.ComponentProps<typeof SelectPrimitive.Content>) { + return ( + <SelectPrimitive.Portal> + <SelectPrimitive.Content + data-slot="select-content" + className={cn( + "data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95 data-[state=open]:animate-in data-[state=open]:fade-in-0 data-[state=open]:zoom-in-95 relative z-50 max-h-(--radix-select-content-available-height) min-w-[8rem] origin-(--radix-select-content-transform-origin) overflow-x-hidden overflow-y-auto rounded-md border bg-popover text-popover-foreground shadow-md", + position === "popper" && + "data-[side=bottom]:translate-y-1 data-[side=left]:-translate-x-1 data-[side=right]:translate-x-1 data-[side=top]:-translate-y-1", + className + )} + position={position} + align={align} + {...props} + > + <SelectScrollUpButton /> + <SelectPrimitive.Viewport + className={cn( + "p-1", + position === "popper" && + "h-[var(--radix-select-trigger-height)] w-full min-w-[var(--radix-select-trigger-width)] scroll-my-1" + )} + > + {children} + </SelectPrimitive.Viewport> + <SelectScrollDownButton /> + </SelectPrimitive.Content> + </SelectPrimitive.Portal> + ) +} + +function SelectLabel({ + className, + ...props +}: React.ComponentProps<typeof SelectPrimitive.Label>) { + return ( + <SelectPrimitive.Label + data-slot="select-label" + className={cn("px-2 py-1.5 text-xs text-muted-foreground", className)} + {...props} + /> + ) +} + +function SelectItem({ + className, + children, + ...props +}: React.ComponentProps<typeof SelectPrimitive.Item>) { + return ( + <SelectPrimitive.Item + data-slot="select-item" + className={cn( + "relative flex w-full cursor-default items-center gap-2 rounded-sm py-1.5 pr-8 pl-2 text-sm outline-hidden select-none focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4 [&_svg:not([class*='text-'])]:text-muted-foreground *:[span]:last:flex *:[span]:last:items-center *:[span]:last:gap-2", + className + )} + {...props} + > + <span + data-slot="select-item-indicator" + className="absolute right-2 flex size-3.5 items-center justify-center" + > + <SelectPrimitive.ItemIndicator> + <CheckIcon className="size-4" /> + </SelectPrimitive.ItemIndicator> + </span> + <SelectPrimitive.ItemText>{children}</SelectPrimitive.ItemText> + </SelectPrimitive.Item> + ) +} + +function SelectSeparator({ + className, + ...props +}: React.ComponentProps<typeof SelectPrimitive.Separator>) { + return ( + <SelectPrimitive.Separator + data-slot="select-separator" + className={cn("pointer-events-none -mx-1 my-1 h-px bg-border", className)} + {...props} + /> + ) +} + +function SelectScrollUpButton({ + className, + ...props +}: React.ComponentProps<typeof SelectPrimitive.ScrollUpButton>) { + return ( + <SelectPrimitive.ScrollUpButton + data-slot="select-scroll-up-button" + className={cn( + "flex cursor-default items-center justify-center py-1", + className + )} + {...props} + > + <ChevronUpIcon className="size-4" /> + </SelectPrimitive.ScrollUpButton> + ) +} + +function SelectScrollDownButton({ + className, + ...props +}: React.ComponentProps<typeof SelectPrimitive.ScrollDownButton>) { + return ( + <SelectPrimitive.ScrollDownButton + data-slot="select-scroll-down-button" + className={cn( + "flex cursor-default items-center justify-center py-1", + className + )} + {...props} + > + <ChevronDownIcon className="size-4" /> + </SelectPrimitive.ScrollDownButton> + ) +} + +export { + Select, + SelectContent, + SelectGroup, + SelectItem, + SelectLabel, + SelectScrollDownButton, + SelectScrollUpButton, + SelectSeparator, + SelectTrigger, + SelectValue +} diff --git a/src/components/ui/separator.tsx b/src/components/ui/separator.tsx new file mode 100644 index 0000000..6ce01ae --- /dev/null +++ b/src/components/ui/separator.tsx @@ -0,0 +1,29 @@ +"use client" + +import * as React from "react" + +import { Separator as SeparatorPrimitive } from "radix-ui" + +import { cn } from "@/utils" + +function Separator({ + className, + orientation = "horizontal", + decorative = true, + ...props +}: React.ComponentProps<typeof SeparatorPrimitive.Root>) { + return ( + <SeparatorPrimitive.Root + data-slot="separator" + decorative={decorative} + orientation={orientation} + className={cn( + "shrink-0 bg-border data-[orientation=horizontal]:h-px data-[orientation=horizontal]:w-full data-[orientation=vertical]:h-full data-[orientation=vertical]:w-px", + className + )} + {...props} + /> + ) +} + +export { Separator } diff --git a/src/components/ui/sheet.tsx b/src/components/ui/sheet.tsx new file mode 100644 index 0000000..4d24358 --- /dev/null +++ b/src/components/ui/sheet.tsx @@ -0,0 +1,144 @@ +"use client" + +import * as React from "react" + +import { XIcon } from "lucide-react" +import { Dialog as SheetPrimitive } from "radix-ui" + +import { cn } from "@/utils" + +function Sheet({ ...props }: React.ComponentProps<typeof SheetPrimitive.Root>) { + return <SheetPrimitive.Root data-slot="sheet" {...props} /> +} + +function SheetTrigger({ + ...props +}: React.ComponentProps<typeof SheetPrimitive.Trigger>) { + return <SheetPrimitive.Trigger data-slot="sheet-trigger" {...props} /> +} + +function SheetClose({ + ...props +}: React.ComponentProps<typeof SheetPrimitive.Close>) { + return <SheetPrimitive.Close data-slot="sheet-close" {...props} /> +} + +function SheetPortal({ + ...props +}: React.ComponentProps<typeof SheetPrimitive.Portal>) { + return <SheetPrimitive.Portal data-slot="sheet-portal" {...props} /> +} + +function SheetOverlay({ + className, + ...props +}: React.ComponentProps<typeof SheetPrimitive.Overlay>) { + return ( + <SheetPrimitive.Overlay + data-slot="sheet-overlay" + className={cn( + "data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:animate-in data-[state=open]:fade-in-0 fixed inset-0 z-50 bg-black/50", + className + )} + {...props} + /> + ) +} + +function SheetContent({ + className, + children, + side = "right", + showCloseButton = true, + ...props +}: React.ComponentProps<typeof SheetPrimitive.Content> & { + side?: "top" | "right" | "bottom" | "left" + showCloseButton?: boolean +}) { + return ( + <SheetPortal> + <SheetOverlay /> + <SheetPrimitive.Content + data-slot="sheet-content" + className={cn( + "data-[state=closed]:animate-out data-[state=open]:animate-in fixed z-50 flex flex-col gap-4 bg-background shadow-lg transition ease-in-out data-[state=closed]:duration-300 data-[state=open]:duration-500", + side === "right" && + "data-[state=closed]:slide-out-to-right data-[state=open]:slide-in-from-right inset-y-0 right-0 h-full w-3/4 border-l sm:max-w-sm", + side === "left" && + "data-[state=closed]:slide-out-to-left data-[state=open]:slide-in-from-left inset-y-0 left-0 h-full w-3/4 border-r sm:max-w-sm", + side === "top" && + "data-[state=closed]:slide-out-to-top data-[state=open]:slide-in-from-top inset-x-0 top-0 h-auto border-b", + side === "bottom" && + "data-[state=closed]:slide-out-to-bottom data-[state=open]:slide-in-from-bottom inset-x-0 bottom-0 h-auto border-t", + className + )} + {...props} + > + {children} + {showCloseButton && ( + <SheetPrimitive.Close className="absolute top-4 right-4 rounded-xs opacity-70 ring-offset-background transition-opacity hover:opacity-100 focus:ring-2 focus:ring-ring focus:ring-offset-2 focus:outline-hidden disabled:pointer-events-none data-[state=open]:bg-secondary"> + <XIcon className="size-4" /> + <span className="sr-only">Close</span> + </SheetPrimitive.Close> + )} + </SheetPrimitive.Content> + </SheetPortal> + ) +} + +function SheetHeader({ className, ...props }: React.ComponentProps<"div">) { + return ( + <div + data-slot="sheet-header" + className={cn("flex flex-col gap-1.5 p-4", className)} + {...props} + /> + ) +} + +function SheetFooter({ className, ...props }: React.ComponentProps<"div">) { + return ( + <div + data-slot="sheet-footer" + className={cn("mt-auto flex flex-col gap-2 p-4", className)} + {...props} + /> + ) +} + +function SheetTitle({ + className, + ...props +}: React.ComponentProps<typeof SheetPrimitive.Title>) { + return ( + <SheetPrimitive.Title + data-slot="sheet-title" + className={cn("font-semibold text-foreground", className)} + {...props} + /> + ) +} + +function SheetDescription({ + className, + ...props +}: React.ComponentProps<typeof SheetPrimitive.Description>) { + return ( + <SheetPrimitive.Description + data-slot="sheet-description" + className={cn("text-sm text-muted-foreground", className)} + {...props} + /> + ) +} + +export { + Sheet, + SheetTrigger, + SheetClose, + SheetContent, + SheetHeader, + SheetFooter, + SheetTitle, + SheetDescription +} diff --git a/src/components/ui/skeleton.tsx b/src/components/ui/skeleton.tsx new file mode 100644 index 0000000..04a80f2 --- /dev/null +++ b/src/components/ui/skeleton.tsx @@ -0,0 +1,13 @@ +import { cn } from "@/utils" + +function Skeleton({ className, ...props }: React.ComponentProps<"div">) { + return ( + <div + data-slot="skeleton" + className={cn("animate-pulse rounded-md bg-accent", className)} + {...props} + /> + ) +} + +export { Skeleton } diff --git a/src/components/ui/sonner.tsx b/src/components/ui/sonner.tsx new file mode 100644 index 0000000..3c3ba64 --- /dev/null +++ b/src/components/ui/sonner.tsx @@ -0,0 +1,41 @@ +"use client" + +import { useTheme } from "next-themes" + +import { + CircleCheckIcon, + InfoIcon, + Loader2Icon, + OctagonXIcon, + TriangleAlertIcon +} from "lucide-react" +import { Toaster as Sonner, type ToasterProps } from "sonner" + +const Toaster = ({ ...props }: ToasterProps) => { + const { theme = "system" } = useTheme() + + return ( + <Sonner + theme={theme as ToasterProps["theme"]} + className="toaster group" + icons={{ + success: <CircleCheckIcon className="size-4" />, + info: <InfoIcon className="size-4" />, + warning: <TriangleAlertIcon className="size-4" />, + error: <OctagonXIcon className="size-4" />, + loading: <Loader2Icon className="size-4 animate-spin" /> + }} + style={ + { + "--normal-bg": "var(--popover)", + "--normal-text": "var(--popover-foreground)", + "--normal-border": "var(--border)", + "--border-radius": "var(--radius)" + } as React.CSSProperties + } + {...props} + /> + ) +} + +export { Toaster } diff --git a/src/components/ui/table.tsx b/src/components/ui/table.tsx new file mode 100644 index 0000000..1f52efb --- /dev/null +++ b/src/components/ui/table.tsx @@ -0,0 +1,116 @@ +"use client" + +import * as React from "react" + +import { cn } from "@/utils" + +function Table({ className, ...props }: React.ComponentProps<"table">) { + return ( + <div + data-slot="table-container" + className="relative w-full overflow-x-auto" + > + <table + data-slot="table" + className={cn("w-full caption-bottom text-sm", className)} + {...props} + /> + </div> + ) +} + +function TableHeader({ className, ...props }: React.ComponentProps<"thead">) { + return ( + <thead + data-slot="table-header" + className={cn("[&_tr]:border-b", className)} + {...props} + /> + ) +} + +function TableBody({ className, ...props }: React.ComponentProps<"tbody">) { + return ( + <tbody + data-slot="table-body" + className={cn("[&_tr:last-child]:border-0", className)} + {...props} + /> + ) +} + +function TableFooter({ className, ...props }: React.ComponentProps<"tfoot">) { + return ( + <tfoot + data-slot="table-footer" + className={cn( + "border-t bg-muted/50 font-medium [&>tr]:last:border-b-0", + className + )} + {...props} + /> + ) +} + +function TableRow({ className, ...props }: React.ComponentProps<"tr">) { + return ( + <tr + data-slot="table-row" + className={cn( + "border-b transition-colors hover:bg-muted/50 has-aria-expanded:bg-muted/50 data-[state=selected]:bg-muted", + className + )} + {...props} + /> + ) +} + +function TableHead({ className, ...props }: React.ComponentProps<"th">) { + return ( + <th + data-slot="table-head" + className={cn( + "h-10 px-2 text-left align-middle font-medium whitespace-nowrap text-foreground [&:has([role=checkbox])]:pr-0 [&>[role=checkbox]]:translate-y-[2px]", + className + )} + {...props} + /> + ) +} + +function TableCell({ className, ...props }: React.ComponentProps<"td">) { + return ( + <td + data-slot="table-cell" + className={cn( + "p-2 align-middle whitespace-nowrap [&:has([role=checkbox])]:pr-0 [&>[role=checkbox]]:translate-y-[2px]", + className + )} + {...props} + /> + ) +} + +function TableCaption({ + className, + ...props +}: React.ComponentProps<"caption">) { + return ( + <caption + data-slot="table-caption" + className={cn("mt-4 text-sm text-muted-foreground", className)} + {...props} + /> + ) +} + +export { + Table, + TableHeader, + TableBody, + TableFooter, + TableHead, + TableRow, + TableCell, + TableCaption +} diff --git a/src/components/ui/tabs.tsx b/src/components/ui/tabs.tsx new file mode 100644 index 0000000..d47c2dc --- /dev/null +++ b/src/components/ui/tabs.tsx @@ -0,0 +1,92 @@ +"use client" + +import * as React from "react" + +import { cva, type VariantProps } from "class-variance-authority" +import { Tabs as TabsPrimitive } from "radix-ui" + +import { cn } from "@/utils" + +function Tabs({ + className, + orientation = "horizontal", + ...props +}: React.ComponentProps<typeof TabsPrimitive.Root>) { + return ( + <TabsPrimitive.Root + data-slot="tabs" + data-orientation={orientation} + orientation={orientation} + className={cn( + "group/tabs flex gap-2 data-[orientation=horizontal]:flex-col", + className + )} + {...props} + /> + ) +} + +const tabsListVariants = cva( + "group/tabs-list inline-flex w-fit items-center justify-center rounded-lg p-[3px] text-muted-foreground group-data-[orientation=horizontal]/tabs:h-9 group-data-[orientation=vertical]/tabs:h-fit group-data-[orientation=vertical]/tabs:flex-col data-[variant=line]:rounded-none", + { + variants: { + variant: { + default: "bg-muted", + line: "gap-1 bg-transparent" + } + }, + defaultVariants: { + variant: "default" + } + } +) + +function TabsList({ + className, + variant = "default", + ...props +}: React.ComponentProps<typeof TabsPrimitive.List> & + VariantProps<typeof tabsListVariants>) { + return ( + <TabsPrimitive.List + data-slot="tabs-list" + data-variant={variant} + className={cn(tabsListVariants({ variant }), className)} + {...props} + /> + ) +} + +function TabsTrigger({ + className, + ...props +}: React.ComponentProps<typeof TabsPrimitive.Trigger>) { + return ( + <TabsPrimitive.Trigger + data-slot="tabs-trigger" + className={cn( + "relative inline-flex h-[calc(100%-1px)] flex-1 items-center justify-center gap-1.5 rounded-md border border-transparent px-2 py-1 text-sm font-medium whitespace-nowrap text-foreground/60 transition-all group-data-[orientation=vertical]/tabs:w-full group-data-[orientation=vertical]/tabs:justify-start hover:text-foreground focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50 focus-visible:outline-1 focus-visible:outline-ring disabled:pointer-events-none disabled:opacity-50 group-data-[variant=default]/tabs-list:data-[state=active]:shadow-sm group-data-[variant=line]/tabs-list:data-[state=active]:shadow-none dark:text-muted-foreground dark:hover:text-foreground [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4", + "group-data-[variant=line]/tabs-list:bg-transparent group-data-[variant=line]/tabs-list:data-[state=active]:bg-transparent dark:group-data-[variant=line]/tabs-list:data-[state=active]:border-transparent dark:group-data-[variant=line]/tabs-list:data-[state=active]:bg-transparent", + "data-[state=active]:bg-background data-[state=active]:text-foreground dark:data-[state=active]:border-input dark:data-[state=active]:bg-input/30 dark:data-[state=active]:text-foreground", + "after:absolute after:bg-foreground after:opacity-0 after:transition-opacity group-data-[orientation=horizontal]/tabs:after:inset-x-0 group-data-[orientation=horizontal]/tabs:after:bottom-[-5px] group-data-[orientation=horizontal]/tabs:after:h-0.5 group-data-[orientation=vertical]/tabs:after:inset-y-0 group-data-[orientation=vertical]/tabs:after:-right-1 group-data-[orientation=vertical]/tabs:after:w-0.5 group-data-[variant=line]/tabs-list:data-[state=active]:after:opacity-100", + className + )} + {...props} + /> + ) +} + +function TabsContent({ + className, + ...props +}: React.ComponentProps<typeof TabsPrimitive.Content>) { + return ( + <TabsPrimitive.Content + data-slot="tabs-content" + className={cn("flex-1 outline-none", className)} + {...props} + /> + ) +} + +export { Tabs, TabsList, TabsTrigger, TabsContent, tabsListVariants } diff --git a/src/components/ui/textarea.tsx b/src/components/ui/textarea.tsx new file mode 100644 index 0000000..80b264e --- /dev/null +++ b/src/components/ui/textarea.tsx @@ -0,0 +1,18 @@ +import * as React from "react" + +import { cn } from "@/utils" + +function Textarea({ className, ...props }: React.ComponentProps<"textarea">) { + return ( + <textarea + data-slot="textarea" + className={cn( + "flex field-sizing-content min-h-16 w-full rounded-md border border-input bg-transparent px-3 py-2 text-base shadow-xs transition-[color,box-shadow] outline-none placeholder:text-muted-foreground focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50 disabled:cursor-not-allowed disabled:opacity-50 aria-invalid:border-destructive aria-invalid:ring-destructive/20 md:text-sm dark:bg-input/30 dark:aria-invalid:ring-destructive/40", + className + )} + {...props} + /> + ) +} + +export { Textarea } diff --git a/src/components/ui/tooltip.tsx b/src/components/ui/tooltip.tsx new file mode 100644 index 0000000..042488f --- /dev/null +++ b/src/components/ui/tooltip.tsx @@ -0,0 +1,58 @@ +"use client" + +import * as React from "react" + +import { Tooltip as TooltipPrimitive } from "radix-ui" + +import { cn } from "@/utils" + +function TooltipProvider({ + delayDuration = 0, + ...props +}: React.ComponentProps<typeof TooltipPrimitive.Provider>) { + return ( + <TooltipPrimitive.Provider + data-slot="tooltip-provider" + delayDuration={delayDuration} + {...props} + /> + ) +} + +function Tooltip({ + ...props +}: React.ComponentProps<typeof TooltipPrimitive.Root>) { + return <TooltipPrimitive.Root data-slot="tooltip" {...props} /> +} + +function TooltipTrigger({ + ...props +}: React.ComponentProps<typeof TooltipPrimitive.Trigger>) { + return <TooltipPrimitive.Trigger data-slot="tooltip-trigger" {...props} /> +} + +function TooltipContent({ + className, + sideOffset = 0, + children, + ...props +}: React.ComponentProps<typeof TooltipPrimitive.Content>) { + return ( + <TooltipPrimitive.Portal> + <TooltipPrimitive.Content + data-slot="tooltip-content" + sideOffset={sideOffset} + className={cn( + "animate-in fade-in-0 zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95 z-50 w-fit origin-(--radix-tooltip-content-transform-origin) rounded-md bg-foreground px-3 py-1.5 text-xs text-balance text-background", + className + )} + {...props} + > + {children} + <TooltipPrimitive.Arrow className="z-50 size-2.5 translate-y-[calc(-50%_-_2px)] rotate-45 rounded-[2px] bg-foreground fill-foreground" /> + </TooltipPrimitive.Content> + </TooltipPrimitive.Portal> + ) +} + +export { Tooltip, TooltipTrigger, TooltipContent, TooltipProvider } diff --git a/src/components/ui/typography.tsx b/src/components/ui/typography.tsx new file mode 100644 index 0000000..2f15a7d --- /dev/null +++ b/src/components/ui/typography.tsx @@ -0,0 +1,120 @@ +import React, { forwardRef, JSX } from "react" + +import { cva, VariantProps } from "class-variance-authority" + +import { cn } from "@/utils" + +type TitleStyleProps = VariantProps<typeof TitleStyles> + +interface TitleProps + extends + React.HTMLAttributes<HTMLHeadingElement>, + Omit<TitleStyleProps, "level" | "weight"> { + variant: `${NonNullable<TitleStyleProps["level"]>}/${NonNullable<TitleStyleProps["weight"]>}` +} + +const Title = forwardRef<HTMLHeadingElement, TitleProps>((props, ref) => { + const { children, variant, className, ...rest } = props + + const [level, weight] = variant.split("/") as [ + TitleStyleProps["level"], + TitleStyleProps["weight"] + ] + + // eslint-disable-next-line @typescript-eslint/no-explicit-any + const Comp = `h${level}` as any + + return ( + <Comp + ref={ref} + className={cn(TitleStyles({ level, weight }), className)} + {...rest} + > + {children} + </Comp> + ) +}) + +type TextStyleProps = VariantProps<typeof TextStyles> + +interface TextProps + extends + React.HTMLAttributes<HTMLParagraphElement>, + Omit<TextStyleProps, "size" | "weight"> { + variant?: `${NonNullable<TextStyleProps["size"]>}/${NonNullable<TextStyleProps["weight"]>}` + as?: keyof Pick< + JSX.IntrinsicElements, + "p" | "code" | "strong" | "span" | "small" | "mark" | "kbd" + > +} + +const Text = forwardRef<HTMLParagraphElement, TextProps>((props, ref) => { + const { + as: Tag = "p", + children, + className, + variant = "md/normal", + ...rest + } = props + + const [size, weight] = variant?.split("/") as [ + TextStyleProps["size"], + TextStyleProps["weight"] + ] + + return ( + <Tag + ref={ref} + className={cn(TextStyles({ size, weight, className }))} + {...rest} + > + {children} + </Tag> + ) +}) + +const TitleStyles = cva(["leading-none text-pretty"], { + variants: { + level: { + 1: "text-4xl md:text-5xl", + 2: "text-3xl md:text-4xl", + 3: "text-2xl md:text-3xl", + 4: "text-xl md:text-2xl", + 5: "text-lg md:text-xl", + 6: "text-base md:text-lg" + }, + weight: { + normal: "font-normal", + medium: "font-medium", + semibold: "font-semibold", + bold: "font-bold", + extrabold: "font-extrabold", + black: "font-black" + } + } +}) + +const TextStyles = cva(["text-paragraph leading-normal text-pretty"], { + variants: { + size: { + xl: "text-2xl", + lg: "text-xl", + md: "text-lg", + sm: "text-base", + xs: "text-sm" + }, + weight: { + normal: "font-normal", + medium: "font-medium", + semibold: "font-semibold", + bold: "font-bold", + black: "font-black" + } + } +}) + +Title.displayName = "Title" +Text.displayName = "Text" + +const Typography = { Title, Text } +export default Typography diff --git a/src/components/widgets/AGENTS.md b/src/components/widgets/AGENTS.md new file mode 100644 index 0000000..b0cf84c --- /dev/null +++ b/src/components/widgets/AGENTS.md @@ -0,0 +1,33 @@ +# src/components/widgets/ -- Composite Widgets + +## Fungsi + +Komponen komposit yang menggabungkan beberapa UI primitives menjadi satu unit fungsional: cards, modals, data tables, form groups, search bars. + +## Aturan Development + +- Widget bisa punya state internal sendiri (form state, open/close) +- Props harus explicit interface, jangan spread sembarangan +- Satu widget = satu tanggung jawab spesifik + +## Convention + +- Nama: `user-card.tsx`, `data-table.tsx`, `confirm-modal.tsx` +- Props type: `UserCardProps`, `DataTableProps` +- File type terpisah jika types kompleks: `data-table.types.ts` + +## Dependency Boundaries + +- Boleh import: ui/, layouts/, hooks, utils +- Tidak boleh: import dari features/ secara langsung + +## Best Practices + +- Widget boleh komposisi dari UI components +- Data fetching via props/passing, jangan langsung di widget +- Test dengan stories untuk visual regression (jika ada) + +## AI Do's / Don'ts + +- Boleh: membuat widget baru dari UI primitives +- Tidak boleh: fetch data langsung dari widget diff --git a/src/components/widgets/index.ts b/src/components/widgets/index.ts new file mode 100644 index 0000000..0a1f57e --- /dev/null +++ b/src/components/widgets/index.ts @@ -0,0 +1,5 @@ +// Widget components barrel +// Export composite widgets saat sudah dibuat: +// export { UserCard } from "./user-card" +// export { DataTable } from "./data-table" +// export { ConfirmModal } from "./confirm-modal" diff --git a/src/config/AGENTS.md b/src/config/AGENTS.md new file mode 100644 index 0000000..f0d5ceb --- /dev/null +++ b/src/config/AGENTS.md @@ -0,0 +1,34 @@ +# src/config/ -- Application Configuration + +## Fungsi + +Konfigurasi aplikasi: environment variables, site config, navigation config, API base URLs, feature flags. + +## Aturan Development + +- Semua config bersifat read-only setelah inisialisasi +- Gunakan Zod untuk validasi env variables di runtime +- Prefix NEXT*PUBLIC* untuk client-side env vars + +## Convention + +- Nama file: `site.ts`, `env.ts`, `nav.ts`, `api.ts`, `features.ts` +- Export named constants, jangan export default +- Group config berdasarkan domain + +## Dependency Boundaries + +- Boleh import: constants/ +- Tidak boleh: import dari app/, components/, features/, hooks/ +- Tidak boleh: import React atau JSX + +## Best Practices + +- Jangan hardcode secrets -- gunakan env variables +- Config object immutable (gunakan `as const` atau `Object.freeze`) +- Validasi env di startup, jangan di runtime + +## AI Do's / Don'ts + +- Boleh: menambah config baru, validasi env +- Tidak boleh: menyimpan secrets di kode diff --git a/src/config/fonts.ts b/src/config/fonts.ts new file mode 100644 index 0000000..9b94912 --- /dev/null +++ b/src/config/fonts.ts @@ -0,0 +1,18 @@ +import { Geist, Geist_Mono } from "next/font/google" + +const geistSans = Geist({ + variable: "--font-sans", + subsets: ["latin"] +}) + +const geistSerif = Geist({ + variable: "--font-serif", + subsets: ["latin"] +}) + +const geistMono = Geist_Mono({ + variable: "--font-mono", + subsets: ["latin"] +}) + +export const fontVariables = `${geistSans.variable} ${geistMono.variable} ${geistSerif.variable}` diff --git a/src/config/index.ts b/src/config/index.ts new file mode 100644 index 0000000..b78a70d --- /dev/null +++ b/src/config/index.ts @@ -0,0 +1,3 @@ +// Application configuration barrel +export { fontVariables } from "./fonts" +export { siteMetadata } from "./site" diff --git a/src/config/site.ts b/src/config/site.ts new file mode 100644 index 0000000..3949794 --- /dev/null +++ b/src/config/site.ts @@ -0,0 +1,6 @@ +import type { Metadata } from "next" + +export const siteMetadata = { + title: "Next App", + description: "Generated by create next app" +} satisfies Metadata diff --git a/src/constants/AGENTS.md b/src/constants/AGENTS.md new file mode 100644 index 0000000..0cb80eb --- /dev/null +++ b/src/constants/AGENTS.md @@ -0,0 +1,33 @@ +# src/constants/ -- Global Constants + +## Fungsi + +Constants global yang digunakan di seluruh aplikasi: magic numbers, enum values, default values, regex patterns, limit constants. + +## Aturan Development + +- Semua constants menggunakan UPPER_CASE dengan underscore +- Group constants dalam object atau namespace +- Gunakan `as const` untuk literal type inference + +## Convention + +- Nama file: `index.ts`, `api.ts`, `limits.ts`, `regex.ts` +- Export: `MAX_RETRY_COUNT`, `DEFAULT_PAGE_SIZE`, `EMAIL_REGEX` +- Gunakan `export const` untuk setiap constant + +## Dependency Boundaries + +- Tidak boleh import dari folder manapun (constants harus independent) +- Tidak boleh import React atau library eksternal + +## Best Practices + +- Eliminasi magic numbers -- semua angka misterius jadi constant +- Gunakan enum atau union type untuk finite values +- Constants yang sering berubah → pindahkan ke config/ + +## AI Do's / Don'ts + +- Boleh: menambah constants baru, merefactor magic numbers +- Tidak boleh: menaruh business logic di constants diff --git a/src/constants/index.ts b/src/constants/index.ts new file mode 100644 index 0000000..21348e4 --- /dev/null +++ b/src/constants/index.ts @@ -0,0 +1,4 @@ +// Global constants barrel +// Export constants saat sudah dibuat: +// export { MAX_RETRY_COUNT, DEFAULT_PAGE_SIZE } from "./api" +// export { EMAIL_REGEX, PASSWORD_REGEX } from "./regex" diff --git a/src/data/AGENTS.md b/src/data/AGENTS.md new file mode 100644 index 0000000..2c386ef --- /dev/null +++ b/src/data/AGENTS.md @@ -0,0 +1,34 @@ +# src/data/ -- Static Data + +## Fungsi + +Data statis dan lookup tables: array of options, enum values sebagai data, default data, seed data, mapping objects. + +## Aturan Development + +- Data immutable: gunakan `as const` atau Object.freeze +- Pisahkan antara data dan logic +- Data besar → file terpisah, import selective + +## Convention + +- Nama file: `countries.ts`, `categories.ts`, `status-options.ts` +- Data array of objects dengan type explicit +- Barrel export dari index.ts + +## Dependency Boundaries + +- Boleh import: types/, constants/ +- Tidak boleh: import dari app/, components/, features/, lib/ +- Tidak boleh: React, hooks, atau side effects + +## Best Practices + +- Bedakan dengan constants/: constants = project config, data = domain data +- Gunakan Zod untuk validasi data statis yang complex +- Jangan taruh data user-specific atau dynamic di sini + +## AI Do's / Don'ts + +- Boleh: menambah data statis baru, menambah lookup tables +- Tidak boleh: menaruh data yang berasal dari API di sini diff --git a/src/data/index.ts b/src/data/index.ts new file mode 100644 index 0000000..70e9c83 --- /dev/null +++ b/src/data/index.ts @@ -0,0 +1,5 @@ +// Static data barrel +// Export static data saat sudah dibuat: +// export { COUNTRIES } from "./countries" +// export { CATEGORIES } from "./categories" +// export { STATUS_OPTIONS } from "./status-options" diff --git a/src/features/AGENTS.md b/src/features/AGENTS.md new file mode 100644 index 0000000..8a0d9c3 --- /dev/null +++ b/src/features/AGENTS.md @@ -0,0 +1,50 @@ +# src/features/ -- Feature Modules + +## Fungsi + +Feature modules mandiri berdasarkan Feature-Sliced Design (FSD). Setiap fitur adalah module lengkap dengan dependencies internal sendiri. + +## Struktur Feature + +``` +src/features/awesome-feature/ +├── api/ # API calls dan hooks untuk fitur ini +├── components/ # Komponen spesifik fitur +├── hooks/ # Hooks spesifik fitur +├── data/ # Data dan constants spesifik fitur +├── shared/ # Shared dalam satu fitur (components, hooks, utils) +├── stores/ # State management spesifik fitur +├── types/ # Type definitions spesifik fitur +├── utils/ # Utility functions spesifik fitur +└── index.ts # Barrel export -- public API module +``` + +## Aturan Development + +- Feature harus self-contained -- bisa berdiri sendiri +- Tidak boleh import dari feature lain secara langsung +- Gunakan shared/ layer untuk intra-feature sharing +- Public API via index.ts barrel export + +## Convention + +- Nama folder fitur: kebab-case (`auth`, `user-profile`, `movie-list`) +- Barrel export hanya dari index.ts +- Internal module tidak boleh di-import dari luar + +## Dependency Boundaries + +- Boleh import: shared/, features lain via @x pattern (jika di-allow) +- Tidak boleh: circular dependencies antar features +- Tidak boleh: import langsung dari internal file feature lain + +## Best Practices + +- Fitur kecil lebih baik dari fitur besar +- Satu fitur = satu tanggung jawab bisnis +- Feature bisa di-test secara independen + +## AI Do's / Don'ts + +- Boleh: membuat feature baru dengan struktur lengkap +- Tidak boleh: import langsung dari internal file feature lain diff --git a/src/features/index.ts b/src/features/index.ts new file mode 100644 index 0000000..981172b --- /dev/null +++ b/src/features/index.ts @@ -0,0 +1,10 @@ +// Feature modules barrel +// Setiap feature memiliki index.ts sendiri. +// Jangan export feature modules di sini -- gunakan barrel per-feature. +// +// Contoh struktur feature: +// src/features/auth/ +// ├── index.ts ← public API module +// ├── api/ +// ├── components/ +// └── hooks/ diff --git a/src/hooks/AGENTS.md b/src/hooks/AGENTS.md new file mode 100644 index 0000000..e790b6d --- /dev/null +++ b/src/hooks/AGENTS.md @@ -0,0 +1,32 @@ +# src/hooks/ -- Shared Custom Hooks + +## Fungsi + +Generic reusable React hooks yang digunakan di seluruh aplikasi: use-debounce, use-media-query, use-local-storage, use-intersection-observer. + +## Aturan Development + +- Hooks harus generic -- tidak terikat domain tertentu +- Hooks spesifik fitur -> `src/features/<fitur>/hooks/` +- Setiap hook = satu file + +## Convention + +- Nama file: `use-debounce.ts`, `use-media-query.ts` +- Nama hook: `useDebounce`, `useMediaQuery` +- Return type harus explicit interface + +## Dependency Boundaries + +- Boleh import: types/, utils/ +- Tidak boleh: import dari app/, components/, features/, stores/ + +## Best Practices + +- Cleanup di useEffect untuk memory leak +- Test hooks dengan renderHook dari Testing Library + +## AI Do's / Don'ts + +- Boleh: membuat generic hooks baru +- Tidak boleh: menaruh feature-specific logic di hooks shared diff --git a/src/hooks/index.ts b/src/hooks/index.ts new file mode 100644 index 0000000..61b136a --- /dev/null +++ b/src/hooks/index.ts @@ -0,0 +1,6 @@ +// Shared hooks barrel +// Export generic hooks saat sudah dibuat: +// export { useDebounce } from "./use-debounce" +// export { useMediaQuery } from "./use-media-query" +// export { useLocalStorage } from "./use-local-storage" +// export { useIntersectionObserver } from "./use-intersection-observer" diff --git a/src/i18n/AGENTS.md b/src/i18n/AGENTS.md new file mode 100644 index 0000000..4033464 --- /dev/null +++ b/src/i18n/AGENTS.md @@ -0,0 +1,35 @@ +# src/i18n/ -- Internationalization + +## Fungsi + +Internationalization setup: next-intl configuration, locale routing, message files, date/number formatting, RSC/RCC i18n patterns. + +## Aturan Development + +- Messages di folder per locale: `en.json`, `id.json` +- Gunakan ICU message syntax untuk pluralization +- Server Components: i18n via `next-intl/server` +- Client Components: i18n via `next-intl/client` + +## Convention + +- Nama file: `routing.ts`, `request.ts`, `config.ts` +- Translation keys: nested object dengan dot notation +- Namespace messages per feature jika besar + +## Dependency Boundaries + +- Boleh import: config/, types/ +- Tidak boleh: import dari app/, components/, features/ secara langsung + +## Best Practices + +- Default locale = id (Indonesia) +- Fallback locale untuk key yang belum di-translate +- Gunakan namespace untuk messages yang besar +- Hindari HTML di translation strings + +## AI Do's / Don'ts + +- Boleh: menambah locale baru, memperbaiki translation +- Tidak boleh: hardcode user-facing strings di komponen diff --git a/src/i18n/index.ts b/src/i18n/index.ts new file mode 100644 index 0000000..c08425a --- /dev/null +++ b/src/i18n/index.ts @@ -0,0 +1,5 @@ +// Internationalization barrel +// Export i18n utilities saat sudah dikonfigurasi: +// export { routing } from "./routing" +// export { request } from "./request" +// export { locales, defaultLocale } from "./config" diff --git a/src/lib/AGENTS.md b/src/lib/AGENTS.md new file mode 100644 index 0000000..2298c82 --- /dev/null +++ b/src/lib/AGENTS.md @@ -0,0 +1,34 @@ +# src/lib/ -- Integrations & Library Code + +## Fungsi + +Integrasi dengan pihak ketiga: API client initialization, database clients, third-party service wrappers, utility libraries. + +## Aturan Development + +- Setiap integration = satu file atau satu folder +- Wrapper pattern: abstraction di atas library eksternal +- Error handling yang robust di setiap integration point + +## Convention + +- Nama file: `api-client.ts`, `db.ts`, `auth.ts`, `email.ts` +- Instance singleton pattern untuk client (api, db) +- Config via parameter, jangan hardcode + +## Dependency Boundaries + +- Boleh import: config/, types/, constants/ +- Tidak boleh: import dari app/, components/, features/ +- Tidak boleh: React atau JSX + +## Best Practices + +- Jangan expose library internal ke luar lib/ +- Retry logic untuk network calls +- Timeout handling untuk semua external calls + +## AI Do's / Don'ts + +- Boleh: menambah integration baru, refactor client code +- Tidak boleh: bypass abstraction dan panggil library langsung diff --git a/src/lib/index.ts b/src/lib/index.ts new file mode 100644 index 0000000..3ab6d1d --- /dev/null +++ b/src/lib/index.ts @@ -0,0 +1,4 @@ +// Integrations & library code barrel +// Export third-party wrappers saat sudah dibuat: +// export { apiClient } from "./api-client" +// export { auth } from "./auth" diff --git a/src/styles/AGENTS.md b/src/styles/AGENTS.md new file mode 100644 index 0000000..dd050f7 --- /dev/null +++ b/src/styles/AGENTS.md @@ -0,0 +1,33 @@ +# src/styles/ -- Global Styles + +## Fungsi + +Global styles, Tailwind CSS configuration extensions, CSS custom properties, font definitions, animation keyframes. + +## Aturan Development + +- Component-specific styles → inline Tailwind di component file +- Global styles hanya untuk: CSS variables, font faces, base resets +- Tailwind config extensions di file terpisah + +## Convention + +- Nama file: `globals.css`, `variables.css`, `fonts.css`, `animations.css` +- Naming CSS variables: `--color-*`, `--spacing-*`, `--radius-*` +- CSS classes: kebab-case + +## Dependency Boundaries + +- Tidak boleh import dari folder project manapun +- CSS imports hanya dari node_modules atau file css lain + +## Best Practices + +- Hindari !important -- gunakan specificity yang tepat +- CSS variables untuk design tokens yang reused +- Test dark mode dengan Tailwind `dark:` variant + +## AI Do's / Don'ts + +- Boleh: menambah CSS variables, global styles +- Tidak boleh: menaruh component styles di global CSS diff --git a/src/styles/globals.css b/src/styles/globals.css new file mode 100644 index 0000000..66ed88b --- /dev/null +++ b/src/styles/globals.css @@ -0,0 +1,103 @@ +@import "tailwindcss"; + +@custom-variant dark (&:is(.dark *)); + +@theme inline { + --color-background: var(--background); + --color-foreground: var(--foreground); + --font-sans: var(--font-sans); + --font-mono: var(--font-mono); + --font-serif: var(--font-serif); + + --color-card: var(--card); + --color-card-foreground: var(--card-foreground); + --color-popover: var(--popover); + --color-popover-foreground: var(--popover-foreground); + --color-primary: var(--primary); + --color-primary-foreground: var(--primary-foreground); + --color-secondary: var(--secondary); + --color-secondary-foreground: var(--secondary-foreground); + --color-muted: var(--muted); + --color-muted-foreground: var(--muted-foreground); + --color-accent: var(--accent); + --color-accent-foreground: var(--accent-foreground); + --color-destructive: var(--destructive); + --color-destructive-foreground: var(--destructive-foreground); + --color-border: var(--border); + --color-input: var(--input); + --color-ring: var(--ring); + --color-chart-1: var(--chart-1); + --color-chart-2: var(--chart-2); + --color-chart-3: var(--chart-3); + --color-chart-4: var(--chart-4); + --color-chart-5: var(--chart-5); + --radius-sm: calc(var(--radius) - 4px); + --radius-md: calc(var(--radius) - 2px); + --radius-lg: var(--radius); + --radius-xl: calc(var(--radius) + 4px); +} + +:root { + --background: oklch(1 0 0); + --foreground: oklch(0.141 0.005 285.823); + --card: oklch(1 0 0); + --card-foreground: oklch(0.141 0.005 285.823); + --popover: oklch(1 0 0); + --popover-foreground: oklch(0.141 0.005 285.823); + --primary: oklch(0.21 0.006 285.885); + --primary-foreground: oklch(0.985 0 0); + --secondary: oklch(0.967 0.001 286.375); + --secondary-foreground: oklch(0.21 0.006 285.885); + --muted: oklch(0.967 0.001 286.375); + --muted-foreground: oklch(0.552 0.016 285.938); + --accent: oklch(0.967 0.001 286.375); + --accent-foreground: oklch(0.21 0.006 285.885); + --destructive: oklch(0.577 0.245 27.325); + --destructive-foreground: oklch(0.577 0.245 27.325); + --border: oklch(0.92 0.004 286.32); + --input: oklch(0.92 0.004 286.32); + --ring: oklch(0.705 0.015 286.067); + --radius: 0.625rem; + --chart-1: oklch(0.646 0.222 41.116); + --chart-2: oklch(0.6 0.118 184.704); + --chart-3: oklch(0.398 0.07 227.392); + --chart-4: oklch(0.828 0.189 84.429); + --chart-5: oklch(0.769 0.188 70.08); +} + +.dark { + --background: oklch(0.141 0.005 285.823); + --foreground: oklch(0.985 0 0); + --card: oklch(0.141 0.005 285.823); + --card-foreground: oklch(0.985 0 0); + --popover: oklch(0.141 0.005 285.823); + --popover-foreground: oklch(0.985 0 0); + --primary: oklch(0.985 0 0); + --primary-foreground: oklch(0.21 0.006 285.885); + --secondary: oklch(0.274 0.006 286.033); + --secondary-foreground: oklch(0.985 0 0); + --muted: oklch(0.274 0.006 286.033); + --muted-foreground: oklch(0.705 0.015 286.067); + --accent: oklch(0.274 0.006 286.033); + --accent-foreground: oklch(0.985 0 0); + --destructive: oklch(0.577 0.245 27.325); + --destructive-foreground: oklch(0.577 0.245 27.325); + --border: oklch(0.274 0.006 286.033); + --input: oklch(0.274 0.006 286.033); + --ring: oklch(0.871 0.006 286.286); + --chart-1: oklch(0.646 0.222 41.116); + --chart-2: oklch(0.6 0.118 184.704); + --chart-3: oklch(0.398 0.07 227.392); + --chart-4: oklch(0.828 0.189 84.429); + --chart-5: oklch(0.769 0.188 70.08); +} + +@layer base { + * { + @apply border-border; + } + body { + @apply bg-background text-foreground; + font-family: Arial, Helvetica, sans-serif; + } +} diff --git a/src/testing/AGENTS.md b/src/testing/AGENTS.md new file mode 100644 index 0000000..21ef082 --- /dev/null +++ b/src/testing/AGENTS.md @@ -0,0 +1,34 @@ +# src/testing/ -- Test Infrastructure + +## Fungsi + +Setup dan utilities untuk testing: Vitest configuration, Testing Library setup, custom matchers, test fixtures, mock data factories, MSW handlers. + +## Aturan Development + +- Test files diletakkan berdampingan dengan source file (\*.test.ts) +- Folder ini hanya untuk shared test utilities, bukan test files +- Coverage target: utils 90%+, hooks 80%+, components 70%+ + +## Convention + +- Nama file: `setup.ts`, `test-utils.tsx`, `factories.ts`, `mocks.ts` +- Helper functions: `renderWithProviders`, `createMockUser`, `mockApiResponse` +- Test data factories untuk generate data konsisten + +## Dependency Boundaries + +- Boleh import: semua folder (untuk setup dan mocking) +- Tidak boleh: mengandung test logic aplikasi + +## Best Practices + +- AAA pattern: Arrange → Act → Assert +- Satu logical assertion per test case +- Mock eksternal API calls di test setup +- Gunakan fakery/test data, jangan hardcode values di test + +## AI Do's / Don'ts + +- Boleh: menambah test utilities, setup helpers +- Tidak boleh: mengubah production code via testing folder diff --git a/src/testing/index.ts b/src/testing/index.ts new file mode 100644 index 0000000..401b240 --- /dev/null +++ b/src/testing/index.ts @@ -0,0 +1,4 @@ +// Test infrastructure barrel +// Export test utilities saat sudah dibuat: +// export { renderWithProviders } from "./test-utils" +// export { createMockUser } from "./factories" diff --git a/src/types/AGENTS.md b/src/types/AGENTS.md new file mode 100644 index 0000000..bd5bf53 --- /dev/null +++ b/src/types/AGENTS.md @@ -0,0 +1,34 @@ +# src/types/ -- Global Type Definitions + +## Fungsi + +Type definitions global yang digunakan lintas fitur: shared interfaces, utility types, module augmentation, global type helpers. + +## Aturan Development + +- Feature-specific types → taruh di `src/features/<fitur>/types/` +- Global shared types → taruh di sini +- Gunakan `import type` untuk import type-only + +## Convention + +- Nama file: `index.ts`, `next.ts`, `utility.ts`, `global.d.ts` +- Interface: PascalCase (`ApiResponse`, `PaginatedResult`) +- Type utility: camelCase dengan Type suffix +- Module augmentation: `*.d.ts` files + +## Dependency Boundaries + +- Boleh import: constants/ +- Tidak boleh: import dari app/, components/, features/, lib/ + +## Best Practices + +- Prefer type over interface (kecuali untuk declaration merging) +- Gunakan utility types (Pick, Omit, Partial, Record) +- Hindari `any` -- gunakan `unknown` jika tipe tidak diketahui + +## AI Do's / Don'ts + +- Boleh: menambah global types, utility types +- Tidak boleh: menempatkan feature-specific types di sini diff --git a/src/types/index.ts b/src/types/index.ts new file mode 100644 index 0000000..7e2f120 --- /dev/null +++ b/src/types/index.ts @@ -0,0 +1,4 @@ +// Global type definitions barrel +// Export shared types saat sudah dibuat: +// export type { ApiResponse, PaginatedResult } from "./api" +// export type { User, Session } from "./auth" diff --git a/src/utils/AGENTS.md b/src/utils/AGENTS.md new file mode 100644 index 0000000..7368ab9 --- /dev/null +++ b/src/utils/AGENTS.md @@ -0,0 +1,34 @@ +# src/utils/ -- Utility Functions + +## Fungsi + +Pure functions yang dapat di-test secara unit: formatters (date, number, currency), validators, string utils, array utils, cn() helper. + +## Aturan Development + +- Pure function = same input always = same output +- No side effects, no React imports, no browser APIs +- Setiap function minimal 1 unit test (target coverage 90%+) + +## Convention + +- Nama file: `format-date.ts`, `cn.ts`, `validate-email.ts`, `string-utils.ts` +- Satu file = satu kategori fungsi +- Function names: camelCase, deskriptif + +## Dependency Boundaries + +- Boleh import: types/, constants/ +- Tidak boleh: import dari app/, components/, features/, lib/, hooks/ +- Tidak boleh: import React atau browser-specific APIs + +## Best Practices + +- Gunakan default parameter, jangan mutasi arguments +- Error handling via return value, jangan throw untuk expected cases +- Tree-shakeable exports (named exports, jangan default export object) + +## AI Do's / Don'ts + +- Boleh: menambah utility functions, unit test +- Tidak boleh: menaruh side effects atau API calls di utils diff --git a/src/utils/cn.ts b/src/utils/cn.ts new file mode 100644 index 0000000..9a7122c --- /dev/null +++ b/src/utils/cn.ts @@ -0,0 +1,6 @@ +import { clsx, type ClassValue } from "clsx" +import { twMerge } from "tailwind-merge" + +export function cn(...inputs: ClassValue[]) { + return twMerge(clsx(inputs)) +} diff --git a/src/utils/index.ts b/src/utils/index.ts new file mode 100644 index 0000000..9e24258 --- /dev/null +++ b/src/utils/index.ts @@ -0,0 +1,6 @@ +export { cn } from "./cn" + +// Utility functions barrel +// Tambahkan export utility functions baru di sini: +// export { formatDate } from "./format-date" +// export { formatCurrency } from "./format-currency" diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..76e73ca --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,34 @@ +{ + "compilerOptions": { + "target": "ES2017", + "lib": ["dom", "dom.iterable", "esnext"], + "allowJs": true, + "skipLibCheck": true, + "strict": true, + "noEmit": true, + "esModuleInterop": true, + "module": "esnext", + "moduleResolution": "bundler", + "resolveJsonModule": true, + "isolatedModules": true, + "jsx": "react-jsx", + "incremental": true, + "plugins": [ + { + "name": "next" + } + ], + "paths": { + "@/*": ["./src/*"] + } + }, + "include": [ + "next-env.d.ts", + "**/*.ts", + "**/*.tsx", + ".next/types/**/*.ts", + ".next/dev/types/**/*.ts", + "**/*.mts" + ], + "exclude": ["node_modules"] +}