Skip to content

Commit 4995f96

Browse files
authored
feat: Use BetterAuth (#17)
1 parent f6b948e commit 4995f96

File tree

23 files changed

+551
-417
lines changed

23 files changed

+551
-417
lines changed

.env.example

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
PRIVATE_TRAKT_CLIENT_ID=
22
PRIVATE_TRAKT_CLIENT_SECRET=
3-
PRIVATE_AUTH_SECRET=
3+
PRIVATE_BETTER_AUTH_SECRET=
44
PRIVATE_TMDB_API_KEY=
5-
NEXTAUTH_URL=http://localhost:4173
5+
PUBLIC_BETTER_AUTH_URL=http://localhost:5173

AGENTS.md

Lines changed: 162 additions & 153 deletions
Original file line numberDiff line numberDiff line change
@@ -1,221 +1,230 @@
11
# Agent Guidelines for Annum
22

3-
This document provides coding agents with essential information about the Annum codebase structure, conventions, and workflows.
3+
This document provides essential information for AI coding agents working on this codebase.
44

55
## Project Overview
66

7-
Annum is a SvelteKit application that visualizes Trakt.tv watch history. It uses:
8-
- **SvelteKit** (v5) with TypeScript
9-
- **Vite** for bundling
10-
- **Vitest** for testing
11-
- **pnpm** as package manager
12-
- **Auth.js** (@auth/sveltekit) for authentication
13-
- **Netlify** for deployment
7+
Annum is a SvelteKit application that visualizes Trakt.tv watch history. It uses Svelte 5, TypeScript, Better Auth for authentication, and is deployed to Netlify.
148

15-
## Build, Test & Lint Commands
9+
## Build & Development Commands
1610

11+
### Development
1712
```bash
18-
# Development
19-
pnpm dev # Start dev server on port 5173
20-
pnpm build # Create production build
21-
pnpm preview # Preview production build locally
22-
23-
# Type Checking & Linting
24-
pnpm check # Run svelte-check for type errors
25-
pnpm check:watch # Run svelte-check in watch mode
26-
pnpm typecheck # Run TypeScript compiler without emitting files
27-
pnpm lint # Run ESLint
28-
pnpm lint:fix # Run ESLint with auto-fix
29-
30-
# Testing
31-
pnpm test # Run tests in watch mode
32-
pnpm test:ci # Run tests once (for CI)
33-
pnpm test:coverage # Run tests with coverage report
13+
pnpm dev # Start development server
14+
pnpm preview # Preview production build locally
15+
```
16+
17+
### Building
18+
```bash
19+
pnpm build # Create production build
20+
```
21+
22+
### Code Quality
23+
```bash
24+
pnpm check # Run svelte-check for type checking
25+
pnpm check:watch # Run svelte-check in watch mode
26+
pnpm lint # Run ESLint
27+
pnpm lint:fix # Run ESLint with auto-fix
28+
pnpm typecheck # Run TypeScript type checking
29+
```
30+
31+
### Testing
32+
```bash
33+
pnpm test # Run tests in watch mode
34+
pnpm test:ci # Run tests once (CI mode)
35+
pnpm test:coverage # Run tests with coverage report
3436

3537
# Run a single test file
36-
pnpm vitest src/lib/utils/__tests__/index.ts
38+
pnpm vitest run src/lib/utils/__tests__/index.ts
3739

38-
# Run a single test by name pattern
39-
pnpm vitest -t "chunks"
40+
# Run a single test file in watch mode
41+
pnpm vitest watch src/lib/utils/__tests__/index.ts
4042
```
4143

44+
**Test Configuration:**
45+
- Test files: `src/**/__tests__/*.ts`
46+
- Coverage includes: `src/lib/utils/*.ts` and `src/lib/actions.ts`
47+
- Test environment: happy-dom
48+
- Framework: vitest
49+
4250
## Code Style Guidelines
4351

44-
### ESLint Configuration
52+
### General Formatting
4553

46-
The project uses `@antfu/eslint-config` with custom overrides:
54+
**ESLint Config:** Uses `@antfu/eslint-config` with customizations
4755

48-
- **Indentation**: Tabs (not spaces)
49-
- **Quotes**: Single quotes (avoid escape when necessary)
50-
- **Semicolons**: No semicolons
51-
- **Array Types**: Use generic syntax `Array<T>` not `T[]`
56+
- **Indentation:** Tabs (not spaces)
57+
- **Quotes:** Single quotes (use `avoidEscape: true` for strings with single quotes)
58+
- **Semicolons:** No semicolons
59+
- **Line breaks:** LF (Unix-style)
5260

5361
### TypeScript
5462

55-
- **Strict mode enabled**: All strict TypeScript checks are on
56-
- **No `{}` or `object` types**: Use `Record<string, unknown>` instead
57-
- **Unused variables**: Prefix with underscore `_` to indicate intentionally unused (e.g., `_variable`, `_arg`)
58-
- **Type imports**: Use `import type` for type-only imports
59-
- **No `any`**: Avoid using `any` type; use proper types or `unknown`
60-
61-
### Naming Conventions
63+
**Strict mode enabled** - All TypeScript strict checks are enforced
6264

63-
- **Files**: Use kebab-case for files (e.g., `user-stats.ts`)
64-
- **Components**: PascalCase for Svelte components (e.g., `Primary.svelte`)
65-
- **Functions**: camelCase for functions (e.g., `normalizeItem`)
66-
- **Constants**: SCREAMING_SNAKE_CASE for top-level constants (e.g., `TRAKT_BASE_URL`)
67-
- **Types/Interfaces**: PascalCase (e.g., `TraktHistoryItem`)
65+
**Type Definitions:**
66+
- Use explicit type annotations for function parameters and return types
67+
- Prefer `interface` over `type` for object shapes (allows `with-single-extends`)
68+
- Use `Array<T>` generic syntax instead of `T[]`
69+
- Never use `{}` or `object` - use `Record<string, unknown>` instead
70+
- No wrapper object types (`String`, `Number`, etc.)
6871

69-
### Imports
72+
**Type Imports:**
73+
- Always use `type` keyword for type-only imports:
74+
```typescript
75+
import type { Language, NormalizedItemResponse } from '$lib/types'
76+
import type { PageData } from './$types'
77+
```
7078

71-
Order imports by:
72-
1. Type imports (using `import type`)
73-
2. External dependencies
74-
3. Internal modules (using SvelteKit aliases)
79+
**Unused Variables:**
80+
- Prefix unused variables with underscore: `_variableName` or just `_`
81+
- Applies to function args, variables, and caught errors
7582

76-
Example:
83+
### Naming Conventions
7784

78-
```typescript
79-
import type { Language, NormalizedItemResponse } from '$lib/types'
80-
import { page } from '$app/state'
81-
import { normalizeItem } from '$lib/utils'
82-
import { signIn } from '@auth/sveltekit/client'
83-
```
85+
- **Files:** kebab-case for most files (e.g., `custom-media-queries.css`)
86+
- **Components:** PascalCase for Svelte components (e.g., `Secondary.svelte`)
87+
- **Functions:** camelCase (e.g., `normalizeItem`, `filterForYear`)
88+
- **Constants:** SCREAMING_SNAKE_CASE (e.g., `PAGINATION_LIMIT`, `TRAKT_BASE_URL`)
89+
- **Types/Interfaces:** PascalCase (e.g., `TraktHistoryItem`, `NormalizedItemResponse`)
8490

85-
### SvelteKit Path Aliases
91+
### Imports
8692

87-
- `$lib/*``src/lib/*`
93+
**Path Aliases:**
94+
- `$lib``src/lib`
8895
- `$assets``src/assets`
8996
- `$const``src/const.ts`
90-
- `$app/*` → SvelteKit internals (state, navigation, stores, etc.)
97+
- Use these aliases consistently instead of relative paths
9198

92-
### Environment Variables
93-
94-
- All private environment variables must use `PRIVATE_` prefix (configured in `svelte.config.js`)
95-
- Example: `PRIVATE_TRAKT_CLIENT_ID`, `PRIVATE_AUTH_SECRET`
96-
97-
## Function Documentation
98-
99-
All utility functions should include JSDoc comments with:
100-
- Description of what the function does
101-
- `@example` tag showing usage
99+
**Import Order:**
100+
1. Type imports
101+
2. External dependencies
102+
3. Internal modules (using path aliases)
103+
4. Relative imports
102104

103105
Example:
104-
105106
```typescript
106-
/**
107-
* Split an array into chunks of a given size
108-
* @example chunks([1, 2, 3, 4, 5], 2) => [[1, 2], [3, 4], [5]]
109-
*/
110-
export function chunks<T>(array: Array<T>, number: number | string): Array<Array<T>>
107+
import type { Language, TraktMediaType } from '$lib/types'
108+
import type { RequestHandler } from './$types'
109+
import { DEFAULT_CACHE_HEADER, PAGINATION_LIMIT } from '$const'
110+
import { normalizeItem } from '$lib/utils'
111+
import { error, json } from '@sveltejs/kit'
111112
```
112113

113-
## Error Handling
114-
115-
- Use SvelteKit's `error()` helper for HTTP errors
116-
- Include helpful error messages with context
117-
- Log warnings to console for non-critical issues (e.g., missing TMDB IDs)
114+
### Svelte 5 Conventions
118115

119-
Example:
116+
**Props:**
120117
```typescript
121-
if (!session?.user)
122-
error(401, 'You must sign in to access this route.')
123-
```
124-
125-
## Testing Patterns
118+
interface Props {
119+
data: PageData
120+
}
126121

127-
- **Test files**: Located in `__tests__/` directories alongside source files
128-
- **Fixtures**: Store test data in `__fixtures__/` directories
129-
- **File pattern**: `src/**/__tests__/*.ts`
130-
- **Coverage**: Includes `src/lib/utils/*.ts` and `src/lib/actions.ts`
122+
let { data }: Props = $props()
123+
```
131124

132-
Test structure:
125+
**Reactivity:**
126+
- Use `$state` for reactive variables
127+
- Use `$derived` for computed values
128+
- Use `$effect` for side effects
133129

130+
**Store Usage:**
134131
```typescript
135-
import { describe, expect, it } from 'vitest'
136-
137-
describe('functionName', () => {
138-
it('should describe expected behavior', () => {
139-
const result = functionName(input)
140-
expect(result).toBe(expected)
141-
})
142-
})
132+
import { settings } from '$lib/store/settings'
133+
134+
// Access with $
135+
$settings.hue
136+
settings.set({ ...$settings, hue: 240 })
143137
```
144138

145-
## Svelte 5 Patterns
139+
### CSS/Styling
140+
141+
**PostCSS:** Uses `postcss-preset-env` with custom media queries
146142

147-
- Use Svelte 5 runes: `$state`, `$derived`, `$effect`, `$props`
148-
- Access page store with `page` from `$app/state`
149-
- Use `<script lang='ts'>` for TypeScript in Svelte files
150-
- For PostCSS in styles: `<style lang='postcss'>`
143+
**Custom Media Queries:**
144+
- `--sm` (min-width: 640px)
145+
- `--md` (min-width: 768px)
146+
- `--lg` (min-width: 1024px)
147+
- `--xl` (min-width: 1350px)
151148

152-
## API Routes
149+
**Usage:**
150+
```css
151+
.element {
152+
display: block;
153153

154-
- API routes in `src/routes/api/`
155-
- Use `RequestHandler` type from `./$types`
156-
- Set cache headers using `setHeaders()`
157-
- Return responses with `json()` helper
158-
- Check authentication with `await locals.auth()`
154+
@media (--md) {
155+
display: flex;
156+
}
157+
}
158+
```
159159

160-
Example structure:
160+
**CSS Variables:** Project uses extensive CSS custom properties defined in `src/styles/variables.css`
161161

162-
```typescript
163-
export const GET: RequestHandler = async ({ locals, url, fetch, setHeaders }) => {
164-
const session = await locals.auth()
165-
if (!session?.user)
166-
error(401, 'You must sign in to access this route.')
162+
### Error Handling
167163

168-
setHeaders({ ...DEFAULT_CACHE_HEADER })
164+
**Server Routes:**
165+
```typescript
166+
// Use SvelteKit's error helper
167+
if (!user)
168+
error(401, 'You must sign in to access this route.')
169169

170-
// Implementation
171-
return json({ data })
170+
// Try-catch for async operations
171+
try {
172+
const res = await fetch(url)
173+
if (!res.ok)
174+
throw new Error(`Response not OK: ${res.status}`)
175+
// ... handle response
176+
}
177+
catch (e) {
178+
error(404, `Failed to fetch data. ${e}`)
172179
}
173180
```
174181

175-
## Constants
182+
**Logging:**
183+
- Use `console.warn()` for non-fatal issues
184+
- Include context in log messages (IDs, types, titles)
176185

177-
Define all constants in `src/const.ts` using:
178-
- `as const` for literal types
179-
- `satisfies` for type checking without widening
186+
### Documentation
180187

181-
Example:
188+
**JSDoc Comments:**
189+
- Add JSDoc for utility functions
190+
- Include `@example` usage examples
191+
- Document parameters and return types
182192

193+
Example:
183194
```typescript
184-
export const TMDB_FETCH_DEFAULTS = {
185-
method: 'GET',
186-
headers: { 'user-agent': 'annum' },
187-
} satisfies RequestInit
195+
/**
196+
* Split an array into chunks of a given size
197+
* @example chunks([1, 2, 3, 4, 5], 2) => [[1, 2], [3, 4], [5]]
198+
*/
199+
export function chunks<T>(array: Array<T>, number: number | string): Array<Array<T>>
188200
```
189201

190-
## Common Patterns
202+
## Environment Variables
191203

192-
### Type Guards
204+
**Private Variables:** Prefix with `PRIVATE_` (configured in `svelte.config.js`)
205+
- `PRIVATE_TRAKT_CLIENT_ID`
206+
- `PRIVATE_TRAKT_CLIENT_SECRET`
207+
- `PRIVATE_AUTH_SECRET`
208+
- `PRIVATE_TMDB_API_KEY`
193209

210+
**Public Variables:** Prefix with `PUBLIC_`
211+
- `PUBLIC_BETTER_AUTH_URL`
212+
213+
## Common Patterns
214+
215+
**Type Guards:**
194216
```typescript
195217
function isTraktWatchedItem(item: TraktHistoryItem | TraktWatchedItem): item is TraktWatchedItem {
196218
return !('type' in item)
197219
}
198220
```
199221

200-
### Generic Utility Types
201-
202-
```typescript
203-
type Filter<T> = MapValuesToKeysIfAllowed<T>[keyof T]
204-
export function groupBy<T extends Record<PropertyKey, any>, Key extends Filter<T>>(
205-
arr: Array<T>,
206-
key: Key,
207-
): Record<T[Key], Array<T>>
208-
```
209-
210-
## Git & Deployment
211-
212-
- Deployed on Netlify (adapter: `@sveltejs/adapter-netlify`)
213-
- Redirects configured in `_redirects` file
214-
- Site manifest: `static/site.webmanifest`
215-
216-
## Additional Notes
222+
**API Responses:**
223+
- Set cache headers with `setHeaders()`
224+
- Return JSON with SvelteKit's `json()` helper
225+
- Use URL search params for query parameters
217226

218-
- Use `enhancedImages()` from `@sveltejs/enhanced-img` for optimized images
219-
- Custom media queries defined in `src/styles/custom-media-queries.css`
220-
- CSS variables in `src/styles/variables.css`
221-
- Reset styles in `src/styles/reset.css`
227+
**Authentication:**
228+
- Uses Better Auth with stateless JWT sessions
229+
- Check `locals.user` for authenticated user
230+
- Trakt OAuth for provider authentication

0 commit comments

Comments
 (0)