Add ModeTemplate with Svelte 5 frontend and components

- Create reusable components: auth, api-client, shared-types
- ModeTemplate with Hono backend and SvelteKit frontend
- Auth store refactored to class-based Svelte 5 pattern
- SSR-safe hydrate() pattern for localStorage access
- Frontend builds successfully with Svelte 5 runes
This commit is contained in:
2026-01-21 05:12:25 +00:00
parent bd7842799c
commit 05f8e2e3b6
120 changed files with 12252 additions and 0 deletions
+51
View File
@@ -0,0 +1,51 @@
# Shared Types
Common types used by both frontend and backend. Keeps API contracts in sync.
## Usage
```typescript
import type { User, ApiResult, PaginatedResponse } from './components/shared-types';
// Backend endpoint
app.get('/api/users', async (c): Promise<ApiResult<PaginatedResponse<User>>> => {
const users = await getUsers();
return c.json({ success: true, data: users });
});
// Frontend call
const result = await api.get<ApiResult<PaginatedResponse<User>>>('/users');
if (result.ok && result.data?.success) {
const users = result.data.data.items;
}
```
## Types Included
**User/Auth:**
- `User` - User record
- `AuthState` - Frontend auth state
**API Responses:**
- `SuccessResponse<T>` - `{ success: true, data: T }`
- `ErrorResponse` - `{ success: false, error: string }`
- `ApiResult<T>` - Either success or error
- `PaginatedResponse<T>` - List with pagination metadata
**Request Params:**
- `PaginationParams` - `{ page?, perPage? }`
- `SortParams` - `{ sort?, order? }`
- `ListParams` - Combined pagination + sort
**Utility Types:**
- `CreateInput<T>` - Omits id and timestamps for creating
- `UpdateInput<T>` - Partial without id/timestamps for updating
## Copying to a Mode
1. Copy `components/shared-types/` to your mode root as `types/`
2. Import from `./types` in both frontend and backend
## Dependencies
None - pure TypeScript types.
+15
View File
@@ -0,0 +1,15 @@
export type {
User,
AuthState,
SuccessResponse,
ErrorResponse,
ApiResult,
PaginatedResponse,
PaginationParams,
SortParams,
ListParams,
PartialBy,
RequiredBy,
CreateInput,
UpdateInput,
} from './types';
+87
View File
@@ -0,0 +1,87 @@
/**
* Shared Types
*
* Common types used by both frontend and backend.
* Copy this to any Mode to ensure type consistency.
*/
// ============================================
// USER / AUTH
// ============================================
export interface User {
id: string;
email: string;
displayName: string;
role?: string;
createdAt?: string;
updatedAt?: string;
}
export interface AuthState {
isAuthenticated: boolean;
user: User | null;
token: string | null;
}
// ============================================
// API RESPONSES
// ============================================
/** Standard success response */
export interface SuccessResponse<T = unknown> {
success: true;
data: T;
}
/** Standard error response */
export interface ErrorResponse {
success: false;
error: string;
code?: string;
}
/** Combined response type */
export type ApiResult<T = unknown> = SuccessResponse<T> | ErrorResponse;
/** Paginated list response */
export interface PaginatedResponse<T> {
items: T[];
page: number;
perPage: number;
totalItems: number;
totalPages: number;
}
// ============================================
// REQUEST TYPES
// ============================================
/** Standard pagination params */
export interface PaginationParams {
page?: number;
perPage?: number;
}
/** Standard sort params */
export interface SortParams {
sort?: string;
order?: 'asc' | 'desc';
}
/** Combined list params */
export type ListParams = PaginationParams & SortParams;
// ============================================
// UTILITY TYPES
// ============================================
/** Make specific properties optional */
export type PartialBy<T, K extends keyof T> = Omit<T, K> & Partial<Pick<T, K>>;
/** Make specific properties required */
export type RequiredBy<T, K extends keyof T> = T & Required<Pick<T, K>>;
/** Create/Update types - id is optional for create */
export type CreateInput<T extends { id: string }> = Omit<T, 'id' | 'createdAt' | 'updatedAt'>;
export type UpdateInput<T extends { id: string }> = Partial<Omit<T, 'id' | 'createdAt' | 'updatedAt'>>;