archy/.planning/codebase/CONVENTIONS.md
2026-07-29 10:25:54 -04:00

160 lines
8.6 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Coding Conventions
**Analysis Date:** 2026-07-29
## Naming Patterns
**Files:**
- TypeScript/Vue: PascalCase for components (e.g., `ToggleSwitch.vue`, `SendBitcoinModal.vue`), camelCase for composables and stores (e.g., `useFileType.ts`, `controller.ts`)
- Rust: snake_case for modules and files (e.g., `bitcoin_rpc.rs`, `storage_crypto.rs`)
- Test files: co-located with source in `__tests__/` subdirectories with `.test.ts` or `.spec.ts` suffix for Vitest, `.bats` for shell tests
- Constants in TypeScript use UPPER_SNAKE_CASE within modules (e.g., `IMAGE_EXTS`, `CATEGORY_COLORS` in `useFileType.ts`)
**Functions:**
- TypeScript/Vue: camelCase for all functions (e.g., `getFileCategory`, `formatSize`, `useFileType`)
- Composables: `use` prefix for Vue composables (e.g., `useFileType`, `useToast`, `useMessageToast`) — exported as named exports or default exports
- Store functions (Pinia): defined with snake_case action names, exported from `defineStore` factory
- Rust: snake_case for all functions and methods (e.g., `doesnt_reallocate`, following Rust conventions)
**Variables:**
- TypeScript: camelCase for local variables and reactive refs (e.g., `modelValue`, `isActive`, `gamepadCount`)
- Refs (Vue 3): prefix not required, but convention is lowercase start (e.g., `const ext = ref('jpg')`)
- Computed properties: camelCase, explicit `.value` suffix in templates when needed
- Parameters: camelCase, typed explicitly in TypeScript (e.g., `password: string`, `isDir: Ref<boolean>`)
**Types:**
- TypeScript: PascalCase for type aliases and interfaces (e.g., `RPCOptions`, `FileCategory`, `CatalogVersionInfo`)
- Union types: PascalCase (e.g., `PendingState = 'pending' | 'sent' | 'approved'`)
- Component props: typed with `defineProps<{ ... }>()` syntax in `<script setup>`
- Rust: PascalCase for structs and enums, snake_case for fields within them
## Code Style
**Formatting:**
- No global Prettier config; code style follows project patterns incrementally
- Vue components: single-file components (`.vue`) with `<template>`, `<script setup>`, optional `<style scoped>`
- TypeScript: indentation is 2 spaces (visible in `vitest.config.ts`, Vue components, test files)
- Line width: no strict enforcement observed; pragmatic wrapping around 80100 characters
- Arrow functions preferred for short callbacks: `(x) => x * 2`
- Template strings for multi-line formatting
**Linting:**
- No `.eslintrc` detected at repo root or `neode-ui/` level
- Rust: Clippy allowances declared at crate level in `main.rs` (`#![allow(...)]`) to suppress stylistic lints and focus CI on correctness issues
- Examples of suppressed Clippy lints: `too_many_arguments`, `type_complexity`, `enum_variant_names`, `unused_io_amount`
## Import Organization
**Order:**
1. Vue framework imports (`import { computed, ref, type Ref } from 'vue'`)
2. Library imports (`import { defineStore } from 'pinia'`, `import { format } from 'date-fns'`)
3. Local module imports (`import { useFileType } from '../useFileType'`, `import { rpcClient } from '../api/rpc-client'`)
4. Types/interfaces (inline in import statements via `type` keyword when needed)
5. No blank lines required between groups in practice
**Path Aliases:**
- TypeScript: `@` alias maps to `src/` (configured in `vitest.config.ts` and `tsconfig.json`)
- Usage: `import { displayVersion } from '@/utils/version'`
- Rust: crate-relative paths (`use crate::module::submodule`) and external crate paths
## Error Handling
**Patterns:**
- TypeScript: explicit try-catch with error type narrowing (e.g., `if (error instanceof Error) { ... }`)
- RPC client (`rpc-client.ts`): catches fetch errors, AbortError, and HTTP errors; distinguishes retryable (502, 503) from permanent (401, 403)
- Rust: `anyhow::Result<T>` for fallible operations; `?` operator for error propagation; `.context("message")` for adding context
- Backend error responses: JSON-RPC format with `error: { code, message, data? }` structure; UI catches and displays via toast system
- Network errors: automatic retry with exponential backoff (600ms × (attempt + 1) with jitter); configurable `maxRetries` per call
## Logging
**Framework:** console object for frontend, `tracing` crate for Rust backend
**Patterns:**
- Frontend: `console.warn`, `console.error` used selectively (e.g., `[RPC]` prefixed logs in `rpc-client.ts` for session/CSRF events)
- Rust: `tracing::info!`, `tracing::warn!` for structured logging; `println!` avoided in production code
- No log levels enforced or documented; pragmatic use based on severity
## Comments
**When to Comment:**
- Explain non-obvious retry logic, timeout decisions, CSRF handling (see `rpc-client.ts` lines 138178 for example)
- Clarify why a workaround exists (e.g., "Already on the login page: redirecting = a full reload")
- Document integration points with backend RPC methods and their expected response shapes
- Avoid redundant comments restating what the code obviously does
**JSDoc/TSDoc:**
- Function parameter types documented inline via TypeScript type annotations (e.g., `ext: Ref<string>`)
- Minimal use of explicit JSDoc blocks; type signature is the primary documentation
- Comments above exports explain purpose in one sentence (e.g., "// RPC Client for connecting to Archipelago backend")
- Optional fields in interfaces documented via property-level comments (e.g., `/** Abort the call from the outside … */`)
## Function Design
**Size:** Functions are typically 550 lines; error-handling paths in `callInner<T>` (`rpc-client.ts`) stretch to 120 lines but remain single-responsibility (retry logic + error classification)
**Parameters:**
- Prefer object parameters for 3+ arguments (e.g., `RPCOptions` object over separate `method, params, timeout`)
- Vue composables accept `Ref<T>` types to maintain reactivity (e.g., `useFileType(ext: Ref<string>, isDir: Ref<boolean>)`)
- Store action functions accept only necessary parameters; broader state via closure
**Return Values:**
- Composables return object with properties (computed values + reactive refs): `{ category, isImage, isAudio, ... }`
- Store actions return `void` or the modified state
- Utilities return simple values or objects (e.g., `formatSize` returns string, `formatDate` returns string)
- Async functions return `Promise<T>` with explicit type parameters (e.g., `async call<T>(options): Promise<T>`)
## Module Design
**Exports:**
- Composables export a single named function and helper functions: `export function useFileType(...)`, `export function getFileCategory(...)`
- Stores export the Pinia store factory: `export const useControllerStore = defineStore(...)`
- RPC client exports as singleton instance: `export const rpcClient = new RPCClient()`
- Utilities export multiple helpers from the same file (e.g., `formatSize`, `formatDate` from same module)
**Barrel Files:**
- Not observed as a primary pattern; each file self-documents its exports
- Imports use direct paths (e.g., `from '../composables/useFileType'`) rather than barrel `index.ts`
- Test files import specific utilities directly to minimize test setup complexity
## Type Safety
**Vue 3 with TypeScript:**
- Components use `<script setup lang="ts">` with `defineProps<{ ... }>()` and `defineEmits<{ ... }>()`
- Props explicitly typed as interfaces/objects with required/optional fields marked
- Events typed as call signatures (e.g., `'update:modelValue': [value: boolean]`)
- Reactive variables typed at declaration: `const isActive = ref<boolean>(false)`, or via inference when obvious
**Rust:**
- Explicit type annotations on public APIs; inference acceptable inside functions
- Generic parameters used to encode interface contracts (e.g., `struct DataUrl<'a>`)
- Pattern matching to handle enums and `Option<T>` / `Result<T, E>` types safely
## Component Architecture (Vue)
**File structure:**
- Single-file components with template → script → (optional) style
- Props come first, emits second, internal state/computed/methods follow
- One component per file (naming matches the file name)
- Slots used minimally; prefer explicit prop configuration over slot forwarding
**Reactivity:**
- `ref()` for primitive/object state; `computed()` for derived values
- `watch()` used for side effects on ref changes (not extensively shown in samples but implied)
- Pinia stores used for global state (authentication, app list, mesh status, etc.)
## Constants and Enums
**Pattern:** Constants are module-level `const` with UPPER_SNAKE_CASE names and immutable type annotations:
```typescript
const IMAGE_EXTS = new Set(['jpg', 'jpeg', ...])
const CATEGORY_COLORS: Record<FileCategory, string> = { ... }
```
**Enums:** Type aliases preferred over TypeScript `enum` keyword (e.g., `type FileCategory = 'folder' | 'image' | ...`)
---
*Convention analysis: 2026-07-29*