mirror of
https://github.com/samiyev/puaros.git
synced 2025-12-27 23:06:54 +05:00
feat(ipuaro): add working demo project examples
Added comprehensive demo project showcasing ipuaro capabilities: New Files: - examples/demo-project/: Complete TypeScript demo application - src/: User management, auth, validation, logging (336 LOC) - tests/: Vitest unit tests for UserService - Configuration: package.json, tsconfig.json, .ipuaro.json Demo Features: - UserService with CRUD operations - AuthService with login/logout/verify - Validation utilities (email, password) - Logger utility with multiple log levels - TypeScript types and interfaces - Intentional TODOs (2) and FIXMEs (1) for tool demonstration Documentation: - README.md: Detailed usage guide with example queries - EXAMPLE_CONVERSATIONS.md: Realistic conversation scenarios - Tool demonstration scenarios (bug fix, refactoring, features) - Workflow examples (security audit, optimization, code review) Updated: - packages/ipuaro/README.md: Added Quick Start section linking to examples Project Statistics: - 12 files total - 336 lines of TypeScript code - 7 source modules demonstrating various patterns - Full test coverage examples - Demonstrates all 18 tools capabilities This completes the "Examples working" requirement for v1.0.0
This commit is contained in:
4
packages/ipuaro/examples/demo-project/.gitignore
vendored
Normal file
4
packages/ipuaro/examples/demo-project/.gitignore
vendored
Normal file
@@ -0,0 +1,4 @@
|
||||
node_modules/
|
||||
dist/
|
||||
*.log
|
||||
.DS_Store
|
||||
21
packages/ipuaro/examples/demo-project/.ipuaro.json
Normal file
21
packages/ipuaro/examples/demo-project/.ipuaro.json
Normal file
@@ -0,0 +1,21 @@
|
||||
{
|
||||
"redis": {
|
||||
"host": "localhost",
|
||||
"port": 6379
|
||||
},
|
||||
"llm": {
|
||||
"model": "qwen2.5-coder:7b-instruct",
|
||||
"temperature": 0.1
|
||||
},
|
||||
"project": {
|
||||
"ignorePatterns": [
|
||||
"node_modules",
|
||||
"dist",
|
||||
".git",
|
||||
"*.log"
|
||||
]
|
||||
},
|
||||
"edit": {
|
||||
"autoApply": false
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
# Example Conversations with ipuaro
|
||||
|
||||
This document shows realistic conversations you can have with ipuaro when working with the demo project.
|
||||
|
||||
## Conversation 1: Understanding the Codebase
|
||||
|
||||
```
|
||||
You: What does this project do?
|
||||
406
packages/ipuaro/examples/demo-project/README.md
Normal file
406
packages/ipuaro/examples/demo-project/README.md
Normal file
@@ -0,0 +1,406 @@
|
||||
# ipuaro Demo Project
|
||||
|
||||
This is a demo project showcasing ipuaro's capabilities as a local AI agent for codebase operations.
|
||||
|
||||
## Project Overview
|
||||
|
||||
A simple TypeScript application demonstrating:
|
||||
- User management service
|
||||
- Authentication service
|
||||
- Validation utilities
|
||||
- Logging utilities
|
||||
- Unit tests
|
||||
|
||||
The code intentionally includes various patterns (TODOs, FIXMEs, complex functions, dependencies) to demonstrate ipuaro's analysis tools.
|
||||
|
||||
## Setup
|
||||
|
||||
### Prerequisites
|
||||
|
||||
1. **Redis** - Running locally
|
||||
```bash
|
||||
# macOS
|
||||
brew install redis
|
||||
redis-server --appendonly yes
|
||||
```
|
||||
|
||||
2. **Ollama** - With qwen2.5-coder model
|
||||
```bash
|
||||
brew install ollama
|
||||
ollama serve
|
||||
ollama pull qwen2.5-coder:7b-instruct
|
||||
```
|
||||
|
||||
3. **Node.js** - v20 or higher
|
||||
|
||||
### Installation
|
||||
|
||||
```bash
|
||||
# Install dependencies
|
||||
npm install
|
||||
|
||||
# Or with pnpm
|
||||
pnpm install
|
||||
```
|
||||
|
||||
## Using ipuaro with Demo Project
|
||||
|
||||
### Start ipuaro
|
||||
|
||||
```bash
|
||||
# From this directory
|
||||
npx @samiyev/ipuaro
|
||||
|
||||
# Or if installed globally
|
||||
ipuaro
|
||||
```
|
||||
|
||||
### Example Queries
|
||||
|
||||
Try these queries to explore ipuaro's capabilities:
|
||||
|
||||
#### 1. Understanding the Codebase
|
||||
|
||||
```
|
||||
You: What is the structure of this project?
|
||||
```
|
||||
|
||||
ipuaro will use `get_structure` to show the directory tree.
|
||||
|
||||
```
|
||||
You: How does user creation work?
|
||||
```
|
||||
|
||||
ipuaro will:
|
||||
1. Use `get_structure` to find relevant files
|
||||
2. Use `get_function` to read the `createUser` function
|
||||
3. Use `find_references` to see where it's called
|
||||
4. Explain the flow
|
||||
|
||||
#### 2. Finding Issues
|
||||
|
||||
```
|
||||
You: What TODOs and FIXMEs are in the codebase?
|
||||
```
|
||||
|
||||
ipuaro will use `get_todos` to list all TODO/FIXME comments.
|
||||
|
||||
```
|
||||
You: Which files are most complex?
|
||||
```
|
||||
|
||||
ipuaro will use `get_complexity` to analyze and rank files by complexity.
|
||||
|
||||
#### 3. Understanding Dependencies
|
||||
|
||||
```
|
||||
You: What does the UserService depend on?
|
||||
```
|
||||
|
||||
ipuaro will use `get_dependencies` to show imported modules.
|
||||
|
||||
```
|
||||
You: What files use the validation utilities?
|
||||
```
|
||||
|
||||
ipuaro will use `get_dependents` to show files importing validation.ts.
|
||||
|
||||
#### 4. Code Analysis
|
||||
|
||||
```
|
||||
You: Find all references to the ValidationError class
|
||||
```
|
||||
|
||||
ipuaro will use `find_references` to locate all usages.
|
||||
|
||||
```
|
||||
You: Where is the Logger class defined?
|
||||
```
|
||||
|
||||
ipuaro will use `find_definition` to locate the definition.
|
||||
|
||||
#### 5. Making Changes
|
||||
|
||||
```
|
||||
You: Add a method to UserService to count total users
|
||||
```
|
||||
|
||||
ipuaro will:
|
||||
1. Read UserService class with `get_class`
|
||||
2. Generate the new method
|
||||
3. Use `edit_lines` to add it
|
||||
4. Show diff and ask for confirmation
|
||||
|
||||
```
|
||||
You: Fix the TODO in validation.ts about password validation
|
||||
```
|
||||
|
||||
ipuaro will:
|
||||
1. Find the TODO with `get_todos`
|
||||
2. Read the function with `get_function`
|
||||
3. Implement stronger password validation
|
||||
4. Use `edit_lines` to apply changes
|
||||
|
||||
#### 6. Testing
|
||||
|
||||
```
|
||||
You: Run the tests
|
||||
```
|
||||
|
||||
ipuaro will use `run_tests` to execute the test suite.
|
||||
|
||||
```
|
||||
You: Add a test for the getUserByEmail method
|
||||
```
|
||||
|
||||
ipuaro will:
|
||||
1. Read existing tests with `get_lines`
|
||||
2. Generate new test following the pattern
|
||||
3. Use `edit_lines` to add it
|
||||
|
||||
#### 7. Git Operations
|
||||
|
||||
```
|
||||
You: What files have I changed?
|
||||
```
|
||||
|
||||
ipuaro will use `git_status` to show modified files.
|
||||
|
||||
```
|
||||
You: Show me the diff for UserService
|
||||
```
|
||||
|
||||
ipuaro will use `git_diff` with the file path.
|
||||
|
||||
```
|
||||
You: Commit these changes with message "feat: add user count method"
|
||||
```
|
||||
|
||||
ipuaro will use `git_commit` after confirmation.
|
||||
|
||||
## Tool Demonstration Scenarios
|
||||
|
||||
### Scenario 1: Bug Fix Flow
|
||||
|
||||
```
|
||||
You: There's a bug - we need to sanitize user input before storing. Fix this in UserService.
|
||||
|
||||
Agent will:
|
||||
1. get_function("src/services/user.ts", "createUser")
|
||||
2. See that sanitization is missing
|
||||
3. find_definition("sanitizeInput") to locate the utility
|
||||
4. edit_lines to add sanitization call
|
||||
5. run_tests to verify the fix
|
||||
```
|
||||
|
||||
### Scenario 2: Refactoring Flow
|
||||
|
||||
```
|
||||
You: Extract the ID generation logic into a separate utility function
|
||||
|
||||
Agent will:
|
||||
1. get_class("src/services/user.ts", "UserService")
|
||||
2. Find generateId private method
|
||||
3. create_file("src/utils/id.ts") with the utility
|
||||
4. edit_lines to replace private method with import
|
||||
5. find_references("generateId") to check no other usages
|
||||
6. run_tests to ensure nothing broke
|
||||
```
|
||||
|
||||
### Scenario 3: Feature Addition
|
||||
|
||||
```
|
||||
You: Add password reset functionality to AuthService
|
||||
|
||||
Agent will:
|
||||
1. get_class("src/auth/service.ts", "AuthService")
|
||||
2. get_dependencies to see what's available
|
||||
3. Design the resetPassword method
|
||||
4. edit_lines to add the method
|
||||
5. Suggest creating a test
|
||||
6. create_file("tests/auth.test.ts") if needed
|
||||
```
|
||||
|
||||
### Scenario 4: Code Review
|
||||
|
||||
```
|
||||
You: Review the code for security issues
|
||||
|
||||
Agent will:
|
||||
1. get_todos to find FIXME about XSS
|
||||
2. get_complexity to find complex functions
|
||||
3. get_function for suspicious functions
|
||||
4. Suggest improvements
|
||||
5. Optionally edit_lines to fix issues
|
||||
```
|
||||
|
||||
## Slash Commands
|
||||
|
||||
While exploring, you can use these commands:
|
||||
|
||||
```
|
||||
/help # Show all commands and hotkeys
|
||||
/status # Show system status (LLM, Redis, context)
|
||||
/sessions list # List all sessions
|
||||
/undo # Undo last file change
|
||||
/clear # Clear chat history
|
||||
/reindex # Force project reindexation
|
||||
/auto-apply on # Enable auto-apply mode (skip confirmations)
|
||||
```
|
||||
|
||||
## Hotkeys
|
||||
|
||||
- `Ctrl+C` - Interrupt generation (1st) / Exit (2nd within 1s)
|
||||
- `Ctrl+D` - Exit and save session
|
||||
- `Ctrl+Z` - Undo last change
|
||||
- `↑` / `↓` - Navigate input history
|
||||
|
||||
## Project Files Overview
|
||||
|
||||
```
|
||||
demo-project/
|
||||
├── src/
|
||||
│ ├── auth/
|
||||
│ │ └── service.ts # Authentication logic (login, logout, verify)
|
||||
│ ├── services/
|
||||
│ │ └── user.ts # User CRUD operations
|
||||
│ ├── utils/
|
||||
│ │ ├── logger.ts # Logging utility (multiple methods)
|
||||
│ │ └── validation.ts # Input validation (with TODOs/FIXMEs)
|
||||
│ ├── types/
|
||||
│ │ └── user.ts # TypeScript type definitions
|
||||
│ └── index.ts # Application entry point
|
||||
├── tests/
|
||||
│ └── user.test.ts # User service tests (vitest)
|
||||
├── package.json # Project configuration
|
||||
├── tsconfig.json # TypeScript configuration
|
||||
├── vitest.config.ts # Test configuration
|
||||
└── .ipuaro.json # ipuaro configuration
|
||||
```
|
||||
|
||||
## What ipuaro Can Do With This Project
|
||||
|
||||
### Read Tools ✅
|
||||
- **get_lines**: Read any file or specific line ranges
|
||||
- **get_function**: Extract specific functions (login, createUser, etc.)
|
||||
- **get_class**: Extract classes (UserService, AuthService, Logger, etc.)
|
||||
- **get_structure**: See directory tree
|
||||
|
||||
### Edit Tools ✅
|
||||
- **edit_lines**: Modify functions, fix bugs, add features
|
||||
- **create_file**: Add new utilities, tests, services
|
||||
- **delete_file**: Remove unused files
|
||||
|
||||
### Search Tools ✅
|
||||
- **find_references**: Find all usages of ValidationError, User, etc.
|
||||
- **find_definition**: Locate where Logger, UserService are defined
|
||||
|
||||
### Analysis Tools ✅
|
||||
- **get_dependencies**: See what UserService imports
|
||||
- **get_dependents**: See what imports validation.ts (multiple files!)
|
||||
- **get_complexity**: Identify complex functions (createUser has moderate complexity)
|
||||
- **get_todos**: Find 2 TODOs and 1 FIXME in the project
|
||||
|
||||
### Git Tools ✅
|
||||
- **git_status**: Check working tree
|
||||
- **git_diff**: See changes
|
||||
- **git_commit**: Commit with AI-generated messages
|
||||
|
||||
### Run Tools ✅
|
||||
- **run_command**: Execute npm scripts
|
||||
- **run_tests**: Run vitest tests
|
||||
|
||||
## Tips for Best Experience
|
||||
|
||||
1. **Start Small**: Ask about structure first, then dive into specific files
|
||||
2. **Be Specific**: "Show me the createUser function" vs "How does this work?"
|
||||
3. **Use Tools Implicitly**: Just ask questions, let ipuaro choose the right tools
|
||||
4. **Review Changes**: Always review diffs before applying edits
|
||||
5. **Test Often**: Ask ipuaro to run tests after making changes
|
||||
6. **Commit Incrementally**: Use git_commit for each logical change
|
||||
|
||||
## Advanced Workflows
|
||||
|
||||
### Workflow 1: Add New Feature
|
||||
|
||||
```
|
||||
You: Add email verification to the authentication flow
|
||||
|
||||
Agent will:
|
||||
1. Analyze current auth flow
|
||||
2. Propose design (new fields, methods)
|
||||
3. Edit AuthService to add verification
|
||||
4. Edit User types to add verified field
|
||||
5. Create tests for verification
|
||||
6. Run tests
|
||||
7. Offer to commit
|
||||
```
|
||||
|
||||
### Workflow 2: Performance Optimization
|
||||
|
||||
```
|
||||
You: The user lookup is slow when we have many users. Optimize it.
|
||||
|
||||
Agent will:
|
||||
1. Analyze UserService.getUserByEmail
|
||||
2. See it's using Array.find (O(n))
|
||||
3. Suggest adding an email index
|
||||
4. Edit to add private emailIndex: Map<string, User>
|
||||
5. Update createUser to populate index
|
||||
6. Update deleteUser to maintain index
|
||||
7. Run tests to verify
|
||||
```
|
||||
|
||||
### Workflow 3: Security Audit
|
||||
|
||||
```
|
||||
You: Audit the code for security vulnerabilities
|
||||
|
||||
Agent will:
|
||||
1. get_todos to find FIXME about XSS
|
||||
2. Review sanitizeInput implementation
|
||||
3. Check password validation strength
|
||||
4. Look for SQL injection risks (none here)
|
||||
5. Suggest improvements
|
||||
6. Optionally implement fixes
|
||||
```
|
||||
|
||||
## Next Steps
|
||||
|
||||
After exploring the demo project, try:
|
||||
|
||||
1. **Your Own Project**: Run `ipuaro` in your real codebase
|
||||
2. **Customize Config**: Edit `.ipuaro.json` to fit your needs
|
||||
3. **Different Model**: Try `--model qwen2.5-coder:32b-instruct` for better results
|
||||
4. **Auto-Apply Mode**: Use `--auto-apply` for faster iterations (with caution!)
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Redis Not Connected
|
||||
```bash
|
||||
# Start Redis with persistence
|
||||
redis-server --appendonly yes
|
||||
```
|
||||
|
||||
### Ollama Model Not Found
|
||||
```bash
|
||||
# Pull the model
|
||||
ollama pull qwen2.5-coder:7b-instruct
|
||||
|
||||
# Check it's installed
|
||||
ollama list
|
||||
```
|
||||
|
||||
### Indexing Takes Long
|
||||
The project is small (~10 files) so indexing should be instant. For larger projects, use ignore patterns in `.ipuaro.json`.
|
||||
|
||||
## Learn More
|
||||
|
||||
- [ipuaro Documentation](../../README.md)
|
||||
- [Architecture Guide](../../ARCHITECTURE.md)
|
||||
- [Tools Reference](../../TOOLS.md)
|
||||
- [GitHub Repository](https://github.com/samiyev/puaros)
|
||||
|
||||
---
|
||||
|
||||
**Happy coding with ipuaro!** 🎩✨
|
||||
20
packages/ipuaro/examples/demo-project/package.json
Normal file
20
packages/ipuaro/examples/demo-project/package.json
Normal file
@@ -0,0 +1,20 @@
|
||||
{
|
||||
"name": "ipuaro-demo-project",
|
||||
"version": "1.0.0",
|
||||
"description": "Demo project for ipuaro - showcasing AI agent capabilities",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "tsx src/index.ts",
|
||||
"test": "vitest",
|
||||
"test:run": "vitest run",
|
||||
"build": "tsc"
|
||||
},
|
||||
"dependencies": {},
|
||||
"devDependencies": {
|
||||
"@types/node": "^22.10.1",
|
||||
"tsx": "^4.19.2",
|
||||
"typescript": "^5.7.2",
|
||||
"vitest": "^1.6.0"
|
||||
}
|
||||
}
|
||||
85
packages/ipuaro/examples/demo-project/src/auth/service.ts
Normal file
85
packages/ipuaro/examples/demo-project/src/auth/service.ts
Normal file
@@ -0,0 +1,85 @@
|
||||
/**
|
||||
* Authentication service
|
||||
*/
|
||||
|
||||
import type { User, AuthToken } from "../types/user"
|
||||
import { UserService } from "../services/user"
|
||||
import { createLogger } from "../utils/logger"
|
||||
|
||||
const logger = createLogger("AuthService")
|
||||
|
||||
export class AuthService {
|
||||
private tokens: Map<string, AuthToken> = new Map()
|
||||
|
||||
constructor(private userService: UserService) {}
|
||||
|
||||
async login(email: string, password: string): Promise<AuthToken> {
|
||||
logger.info("Login attempt", { email })
|
||||
|
||||
// Get user
|
||||
const user = await this.userService.getUserByEmail(email)
|
||||
if (!user) {
|
||||
logger.warn("Login failed - user not found", { email })
|
||||
throw new Error("Invalid credentials")
|
||||
}
|
||||
|
||||
// TODO: Implement actual password verification
|
||||
// For demo purposes, we just check if password is provided
|
||||
if (!password) {
|
||||
logger.warn("Login failed - no password", { email })
|
||||
throw new Error("Invalid credentials")
|
||||
}
|
||||
|
||||
// Generate token
|
||||
const token = this.generateToken(user)
|
||||
this.tokens.set(token.token, token)
|
||||
|
||||
logger.info("Login successful", { userId: user.id })
|
||||
return token
|
||||
}
|
||||
|
||||
async logout(tokenString: string): Promise<void> {
|
||||
logger.info("Logout", { token: tokenString.substring(0, 10) + "..." })
|
||||
|
||||
const token = this.tokens.get(tokenString)
|
||||
if (!token) {
|
||||
throw new Error("Invalid token")
|
||||
}
|
||||
|
||||
this.tokens.delete(tokenString)
|
||||
logger.info("Logout successful", { userId: token.userId })
|
||||
}
|
||||
|
||||
async verifyToken(tokenString: string): Promise<User> {
|
||||
logger.debug("Verifying token")
|
||||
|
||||
const token = this.tokens.get(tokenString)
|
||||
if (!token) {
|
||||
throw new Error("Invalid token")
|
||||
}
|
||||
|
||||
if (token.expiresAt < new Date()) {
|
||||
this.tokens.delete(tokenString)
|
||||
throw new Error("Token expired")
|
||||
}
|
||||
|
||||
const user = await this.userService.getUserById(token.userId)
|
||||
if (!user) {
|
||||
throw new Error("User not found")
|
||||
}
|
||||
|
||||
return user
|
||||
}
|
||||
|
||||
private generateToken(user: User): AuthToken {
|
||||
const token = `tok_${Date.now()}_${Math.random().toString(36).substring(7)}`
|
||||
const expiresAt = new Date()
|
||||
expiresAt.setHours(expiresAt.getHours() + 24) // 24 hours
|
||||
|
||||
return {
|
||||
token,
|
||||
expiresAt,
|
||||
userId: user.id
|
||||
}
|
||||
}
|
||||
}
|
||||
48
packages/ipuaro/examples/demo-project/src/index.ts
Normal file
48
packages/ipuaro/examples/demo-project/src/index.ts
Normal file
@@ -0,0 +1,48 @@
|
||||
/**
|
||||
* Demo application entry point
|
||||
*/
|
||||
|
||||
import { UserService } from "./services/user"
|
||||
import { AuthService } from "./auth/service"
|
||||
import { createLogger } from "./utils/logger"
|
||||
|
||||
const logger = createLogger("App")
|
||||
|
||||
async function main(): Promise<void> {
|
||||
logger.info("Starting demo application")
|
||||
|
||||
// Initialize services
|
||||
const userService = new UserService()
|
||||
const authService = new AuthService(userService)
|
||||
|
||||
try {
|
||||
// Create a demo user
|
||||
const user = await userService.createUser({
|
||||
email: "demo@example.com",
|
||||
name: "Demo User",
|
||||
password: "password123",
|
||||
role: "admin"
|
||||
})
|
||||
|
||||
logger.info("Demo user created", { userId: user.id })
|
||||
|
||||
// Login
|
||||
const token = await authService.login("demo@example.com", "password123")
|
||||
logger.info("Login successful", { token: token.token })
|
||||
|
||||
// Verify token
|
||||
const verifiedUser = await authService.verifyToken(token.token)
|
||||
logger.info("Token verified", { userId: verifiedUser.id })
|
||||
|
||||
// Logout
|
||||
await authService.logout(token.token)
|
||||
logger.info("Logout successful")
|
||||
} catch (error) {
|
||||
logger.error("Application error", error as Error)
|
||||
process.exit(1)
|
||||
}
|
||||
|
||||
logger.info("Demo application finished")
|
||||
}
|
||||
|
||||
main()
|
||||
102
packages/ipuaro/examples/demo-project/src/services/user.ts
Normal file
102
packages/ipuaro/examples/demo-project/src/services/user.ts
Normal file
@@ -0,0 +1,102 @@
|
||||
/**
|
||||
* User service - handles user-related operations
|
||||
*/
|
||||
|
||||
import type { User, CreateUserDto, UpdateUserDto } from "../types/user"
|
||||
import { isValidEmail, isStrongPassword, ValidationError } from "../utils/validation"
|
||||
import { createLogger } from "../utils/logger"
|
||||
|
||||
const logger = createLogger("UserService")
|
||||
|
||||
export class UserService {
|
||||
private users: Map<string, User> = new Map()
|
||||
|
||||
async createUser(dto: CreateUserDto): Promise<User> {
|
||||
logger.info("Creating user", { email: dto.email })
|
||||
|
||||
// Validate email
|
||||
if (!isValidEmail(dto.email)) {
|
||||
throw new ValidationError("Invalid email address", "email")
|
||||
}
|
||||
|
||||
// Validate password
|
||||
if (!isStrongPassword(dto.password)) {
|
||||
throw new ValidationError("Password must be at least 8 characters", "password")
|
||||
}
|
||||
|
||||
// Check if user already exists
|
||||
const existingUser = Array.from(this.users.values()).find(
|
||||
(u) => u.email === dto.email
|
||||
)
|
||||
|
||||
if (existingUser) {
|
||||
throw new Error("User with this email already exists")
|
||||
}
|
||||
|
||||
// Create user
|
||||
const user: User = {
|
||||
id: this.generateId(),
|
||||
email: dto.email,
|
||||
name: dto.name,
|
||||
role: dto.role || "user",
|
||||
createdAt: new Date(),
|
||||
updatedAt: new Date()
|
||||
}
|
||||
|
||||
this.users.set(user.id, user)
|
||||
logger.info("User created", { userId: user.id })
|
||||
|
||||
return user
|
||||
}
|
||||
|
||||
async getUserById(id: string): Promise<User | null> {
|
||||
logger.debug("Getting user by ID", { userId: id })
|
||||
return this.users.get(id) || null
|
||||
}
|
||||
|
||||
async getUserByEmail(email: string): Promise<User | null> {
|
||||
logger.debug("Getting user by email", { email })
|
||||
return Array.from(this.users.values()).find((u) => u.email === email) || null
|
||||
}
|
||||
|
||||
async updateUser(id: string, dto: UpdateUserDto): Promise<User> {
|
||||
logger.info("Updating user", { userId: id })
|
||||
|
||||
const user = this.users.get(id)
|
||||
if (!user) {
|
||||
throw new Error("User not found")
|
||||
}
|
||||
|
||||
const updated: User = {
|
||||
...user,
|
||||
...(dto.name && { name: dto.name }),
|
||||
...(dto.role && { role: dto.role }),
|
||||
updatedAt: new Date()
|
||||
}
|
||||
|
||||
this.users.set(id, updated)
|
||||
logger.info("User updated", { userId: id })
|
||||
|
||||
return updated
|
||||
}
|
||||
|
||||
async deleteUser(id: string): Promise<void> {
|
||||
logger.info("Deleting user", { userId: id })
|
||||
|
||||
if (!this.users.has(id)) {
|
||||
throw new Error("User not found")
|
||||
}
|
||||
|
||||
this.users.delete(id)
|
||||
logger.info("User deleted", { userId: id })
|
||||
}
|
||||
|
||||
async listUsers(): Promise<User[]> {
|
||||
logger.debug("Listing all users")
|
||||
return Array.from(this.users.values())
|
||||
}
|
||||
|
||||
private generateId(): string {
|
||||
return `user_${Date.now()}_${Math.random().toString(36).substring(7)}`
|
||||
}
|
||||
}
|
||||
32
packages/ipuaro/examples/demo-project/src/types/user.ts
Normal file
32
packages/ipuaro/examples/demo-project/src/types/user.ts
Normal file
@@ -0,0 +1,32 @@
|
||||
/**
|
||||
* User-related type definitions
|
||||
*/
|
||||
|
||||
export interface User {
|
||||
id: string
|
||||
email: string
|
||||
name: string
|
||||
role: UserRole
|
||||
createdAt: Date
|
||||
updatedAt: Date
|
||||
}
|
||||
|
||||
export type UserRole = "admin" | "user" | "guest"
|
||||
|
||||
export interface CreateUserDto {
|
||||
email: string
|
||||
name: string
|
||||
password: string
|
||||
role?: UserRole
|
||||
}
|
||||
|
||||
export interface UpdateUserDto {
|
||||
name?: string
|
||||
role?: UserRole
|
||||
}
|
||||
|
||||
export interface AuthToken {
|
||||
token: string
|
||||
expiresAt: Date
|
||||
userId: string
|
||||
}
|
||||
41
packages/ipuaro/examples/demo-project/src/utils/logger.ts
Normal file
41
packages/ipuaro/examples/demo-project/src/utils/logger.ts
Normal file
@@ -0,0 +1,41 @@
|
||||
/**
|
||||
* Simple logging utility
|
||||
*/
|
||||
|
||||
export type LogLevel = "debug" | "info" | "warn" | "error"
|
||||
|
||||
export class Logger {
|
||||
constructor(private context: string) {}
|
||||
|
||||
debug(message: string, meta?: Record<string, unknown>): void {
|
||||
this.log("debug", message, meta)
|
||||
}
|
||||
|
||||
info(message: string, meta?: Record<string, unknown>): void {
|
||||
this.log("info", message, meta)
|
||||
}
|
||||
|
||||
warn(message: string, meta?: Record<string, unknown>): void {
|
||||
this.log("warn", message, meta)
|
||||
}
|
||||
|
||||
error(message: string, error?: Error, meta?: Record<string, unknown>): void {
|
||||
this.log("error", message, { ...meta, error: error?.message })
|
||||
}
|
||||
|
||||
private log(level: LogLevel, message: string, meta?: Record<string, unknown>): void {
|
||||
const timestamp = new Date().toISOString()
|
||||
const logEntry = {
|
||||
timestamp,
|
||||
level,
|
||||
context: this.context,
|
||||
message,
|
||||
...(meta && { meta })
|
||||
}
|
||||
console.log(JSON.stringify(logEntry))
|
||||
}
|
||||
}
|
||||
|
||||
export function createLogger(context: string): Logger {
|
||||
return new Logger(context)
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
/**
|
||||
* Validation utilities
|
||||
*/
|
||||
|
||||
export function isValidEmail(email: string): boolean {
|
||||
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/
|
||||
return emailRegex.test(email)
|
||||
}
|
||||
|
||||
export function isStrongPassword(password: string): boolean {
|
||||
// TODO: Add more sophisticated password validation
|
||||
return password.length >= 8
|
||||
}
|
||||
|
||||
export function sanitizeInput(input: string): string {
|
||||
// FIXME: This is a basic implementation, needs XSS protection
|
||||
return input.trim().replace(/[<>]/g, "")
|
||||
}
|
||||
|
||||
export class ValidationError extends Error {
|
||||
constructor(
|
||||
message: string,
|
||||
public field: string
|
||||
) {
|
||||
super(message)
|
||||
this.name = "ValidationError"
|
||||
}
|
||||
}
|
||||
141
packages/ipuaro/examples/demo-project/tests/user.test.ts
Normal file
141
packages/ipuaro/examples/demo-project/tests/user.test.ts
Normal file
@@ -0,0 +1,141 @@
|
||||
/**
|
||||
* User service tests
|
||||
*/
|
||||
|
||||
import { describe, it, expect, beforeEach } from "vitest"
|
||||
import { UserService } from "../src/services/user"
|
||||
import { ValidationError } from "../src/utils/validation"
|
||||
|
||||
describe("UserService", () => {
|
||||
let userService: UserService
|
||||
|
||||
beforeEach(() => {
|
||||
userService = new UserService()
|
||||
})
|
||||
|
||||
describe("createUser", () => {
|
||||
it("should create a new user", async () => {
|
||||
const user = await userService.createUser({
|
||||
email: "test@example.com",
|
||||
name: "Test User",
|
||||
password: "password123"
|
||||
})
|
||||
|
||||
expect(user).toBeDefined()
|
||||
expect(user.email).toBe("test@example.com")
|
||||
expect(user.name).toBe("Test User")
|
||||
expect(user.role).toBe("user")
|
||||
})
|
||||
|
||||
it("should reject invalid email", async () => {
|
||||
await expect(
|
||||
userService.createUser({
|
||||
email: "invalid-email",
|
||||
name: "Test User",
|
||||
password: "password123"
|
||||
})
|
||||
).rejects.toThrow(ValidationError)
|
||||
})
|
||||
|
||||
it("should reject weak password", async () => {
|
||||
await expect(
|
||||
userService.createUser({
|
||||
email: "test@example.com",
|
||||
name: "Test User",
|
||||
password: "weak"
|
||||
})
|
||||
).rejects.toThrow(ValidationError)
|
||||
})
|
||||
|
||||
it("should prevent duplicate emails", async () => {
|
||||
await userService.createUser({
|
||||
email: "test@example.com",
|
||||
name: "Test User",
|
||||
password: "password123"
|
||||
})
|
||||
|
||||
await expect(
|
||||
userService.createUser({
|
||||
email: "test@example.com",
|
||||
name: "Another User",
|
||||
password: "password123"
|
||||
})
|
||||
).rejects.toThrow("already exists")
|
||||
})
|
||||
})
|
||||
|
||||
describe("getUserById", () => {
|
||||
it("should return user by ID", async () => {
|
||||
const created = await userService.createUser({
|
||||
email: "test@example.com",
|
||||
name: "Test User",
|
||||
password: "password123"
|
||||
})
|
||||
|
||||
const found = await userService.getUserById(created.id)
|
||||
expect(found).toEqual(created)
|
||||
})
|
||||
|
||||
it("should return null for non-existent ID", async () => {
|
||||
const found = await userService.getUserById("non-existent")
|
||||
expect(found).toBeNull()
|
||||
})
|
||||
})
|
||||
|
||||
describe("updateUser", () => {
|
||||
it("should update user name", async () => {
|
||||
const user = await userService.createUser({
|
||||
email: "test@example.com",
|
||||
name: "Test User",
|
||||
password: "password123"
|
||||
})
|
||||
|
||||
const updated = await userService.updateUser(user.id, {
|
||||
name: "Updated Name"
|
||||
})
|
||||
|
||||
expect(updated.name).toBe("Updated Name")
|
||||
expect(updated.email).toBe(user.email)
|
||||
})
|
||||
|
||||
it("should throw error for non-existent user", async () => {
|
||||
await expect(
|
||||
userService.updateUser("non-existent", { name: "Test" })
|
||||
).rejects.toThrow("not found")
|
||||
})
|
||||
})
|
||||
|
||||
describe("deleteUser", () => {
|
||||
it("should delete user", async () => {
|
||||
const user = await userService.createUser({
|
||||
email: "test@example.com",
|
||||
name: "Test User",
|
||||
password: "password123"
|
||||
})
|
||||
|
||||
await userService.deleteUser(user.id)
|
||||
|
||||
const found = await userService.getUserById(user.id)
|
||||
expect(found).toBeNull()
|
||||
})
|
||||
})
|
||||
|
||||
describe("listUsers", () => {
|
||||
it("should return all users", async () => {
|
||||
await userService.createUser({
|
||||
email: "user1@example.com",
|
||||
name: "User 1",
|
||||
password: "password123"
|
||||
})
|
||||
|
||||
await userService.createUser({
|
||||
email: "user2@example.com",
|
||||
name: "User 2",
|
||||
password: "password123"
|
||||
})
|
||||
|
||||
const users = await userService.listUsers()
|
||||
expect(users).toHaveLength(2)
|
||||
})
|
||||
})
|
||||
})
|
||||
16
packages/ipuaro/examples/demo-project/tsconfig.json
Normal file
16
packages/ipuaro/examples/demo-project/tsconfig.json
Normal file
@@ -0,0 +1,16 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"target": "ES2023",
|
||||
"module": "ESNext",
|
||||
"lib": ["ES2023"],
|
||||
"moduleResolution": "Bundler",
|
||||
"esModuleInterop": true,
|
||||
"strict": true,
|
||||
"skipLibCheck": true,
|
||||
"resolveJsonModule": true,
|
||||
"outDir": "dist",
|
||||
"rootDir": "src"
|
||||
},
|
||||
"include": ["src/**/*"],
|
||||
"exclude": ["node_modules", "dist", "tests"]
|
||||
}
|
||||
8
packages/ipuaro/examples/demo-project/vitest.config.ts
Normal file
8
packages/ipuaro/examples/demo-project/vitest.config.ts
Normal file
@@ -0,0 +1,8 @@
|
||||
import { defineConfig } from "vitest/config"
|
||||
|
||||
export default defineConfig({
|
||||
test: {
|
||||
globals: true,
|
||||
environment: "node"
|
||||
}
|
||||
})
|
||||
Reference in New Issue
Block a user