@regression-io/claude-config 0.14.16

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (66) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +286 -0
  3. package/cli.js +260 -0
  4. package/config-loader.js +1556 -0
  5. package/package.json +62 -0
  6. package/scripts/postinstall.js +50 -0
  7. package/scripts/sync-version.js +65 -0
  8. package/shared/mcp-registry.json +117 -0
  9. package/templates/composites/fastapi-react-js/rules/backend-python.md +54 -0
  10. package/templates/composites/fastapi-react-js/rules/frontend-react.md +69 -0
  11. package/templates/composites/fastapi-react-js/rules/monorepo.md +77 -0
  12. package/templates/composites/fastapi-react-js/template.json +7 -0
  13. package/templates/composites/fastapi-react-ts/rules/backend-python.md +54 -0
  14. package/templates/composites/fastapi-react-ts/rules/frontend-react.md +64 -0
  15. package/templates/composites/fastapi-react-ts/rules/monorepo.md +82 -0
  16. package/templates/composites/fastapi-react-ts/template.json +7 -0
  17. package/templates/frameworks/fastapi/rules/dependencies.md +89 -0
  18. package/templates/frameworks/fastapi/rules/endpoints.md +86 -0
  19. package/templates/frameworks/fastapi/rules/errors.md +101 -0
  20. package/templates/frameworks/fastapi/rules/structure.md +97 -0
  21. package/templates/frameworks/fastapi/template.json +6 -0
  22. package/templates/frameworks/mcp-python/rules/resources.md +93 -0
  23. package/templates/frameworks/mcp-python/rules/structure.md +74 -0
  24. package/templates/frameworks/mcp-python/rules/tools.md +80 -0
  25. package/templates/frameworks/mcp-python/template.json +6 -0
  26. package/templates/frameworks/python-cli/rules/commands.md +103 -0
  27. package/templates/frameworks/python-cli/rules/output.md +107 -0
  28. package/templates/frameworks/python-cli/rules/structure.md +91 -0
  29. package/templates/frameworks/python-cli/template.json +6 -0
  30. package/templates/frameworks/react-js/rules/components.md +84 -0
  31. package/templates/frameworks/react-js/rules/hooks.md +98 -0
  32. package/templates/frameworks/react-js/template.json +6 -0
  33. package/templates/frameworks/react-ts/rules/components.md +72 -0
  34. package/templates/frameworks/react-ts/rules/hooks.md +87 -0
  35. package/templates/frameworks/react-ts/rules/state.md +93 -0
  36. package/templates/frameworks/react-ts/template.json +6 -0
  37. package/templates/languages/javascript/rules/patterns.md +126 -0
  38. package/templates/languages/javascript/rules/style.md +92 -0
  39. package/templates/languages/javascript/template.json +6 -0
  40. package/templates/languages/python/rules/dependencies.md +77 -0
  41. package/templates/languages/python/rules/patterns.md +95 -0
  42. package/templates/languages/python/rules/style.md +63 -0
  43. package/templates/languages/python/template.json +6 -0
  44. package/templates/languages/typescript/rules/config.md +95 -0
  45. package/templates/languages/typescript/rules/patterns.md +119 -0
  46. package/templates/languages/typescript/rules/style.md +82 -0
  47. package/templates/languages/typescript/template.json +6 -0
  48. package/templates/universal/commands/commit.md +53 -0
  49. package/templates/universal/commands/debug.md +53 -0
  50. package/templates/universal/commands/document.md +54 -0
  51. package/templates/universal/commands/review.md +45 -0
  52. package/templates/universal/commands/security-review.md +52 -0
  53. package/templates/universal/commands/test.md +46 -0
  54. package/templates/universal/rules/api-design.md +38 -0
  55. package/templates/universal/rules/code-quality.md +40 -0
  56. package/templates/universal/rules/documentation.md +38 -0
  57. package/templates/universal/rules/error-handling.md +37 -0
  58. package/templates/universal/rules/git-workflow.md +39 -0
  59. package/templates/universal/rules/security.md +39 -0
  60. package/templates/universal/rules/testing.md +38 -0
  61. package/templates/universal/template.json +6 -0
  62. package/ui/dist/assets/index-C5apzulu.css +32 -0
  63. package/ui/dist/assets/index-CBNCwCnY.js +489 -0
  64. package/ui/dist/index.html +14 -0
  65. package/ui/server.cjs +2237 -0
  66. package/ui/terminal-server.cjs +160 -0
@@ -0,0 +1,103 @@
1
+ # CLI Command Patterns
2
+
3
+ ## Command Definition
4
+ ```python
5
+ # src/my_cli/commands/init.py
6
+ import typer
7
+ from pathlib import Path
8
+ from rich.console import Console
9
+
10
+ app = typer.Typer(help="Initialize a new project")
11
+ console = Console()
12
+
13
+ @app.command()
14
+ def project(
15
+ name: str = typer.Argument(..., help="Project name"),
16
+ path: Path = typer.Option(".", "--path", "-p", help="Target directory"),
17
+ force: bool = typer.Option(False, "--force", "-f", help="Overwrite existing"),
18
+ ):
19
+ """Initialize a new project with the given name."""
20
+ target = path / name
21
+
22
+ if target.exists() and not force:
23
+ console.print(f"[red]Error:[/red] {target} already exists. Use --force to overwrite.")
24
+ raise typer.Exit(1)
25
+
26
+ # Create project structure
27
+ target.mkdir(parents=True, exist_ok=True)
28
+ (target / "README.md").write_text(f"# {name}\n")
29
+
30
+ console.print(f"[green]✓[/green] Created project at {target}")
31
+ ```
32
+
33
+ ## Arguments and Options
34
+ ```python
35
+ @app.command()
36
+ def process(
37
+ # Required argument
38
+ input_file: Path = typer.Argument(..., help="Input file to process"),
39
+
40
+ # Optional argument with default
41
+ output: Path = typer.Argument("output.txt", help="Output file"),
42
+
43
+ # Options
44
+ verbose: bool = typer.Option(False, "--verbose", "-v"),
45
+ count: int = typer.Option(10, "--count", "-n", min=1, max=100),
46
+ format: str = typer.Option("json", "--format", "-f", help="Output format"),
47
+ ):
48
+ ...
49
+ ```
50
+
51
+ ## Choice Options
52
+ ```python
53
+ from enum import Enum
54
+
55
+ class OutputFormat(str, Enum):
56
+ json = "json"
57
+ yaml = "yaml"
58
+ text = "text"
59
+
60
+ @app.command()
61
+ def export(
62
+ format: OutputFormat = typer.Option(OutputFormat.json, "--format", "-f"),
63
+ ):
64
+ if format == OutputFormat.json:
65
+ ...
66
+ ```
67
+
68
+ ## Interactive Prompts
69
+ ```python
70
+ @app.command()
71
+ def configure():
72
+ name = typer.prompt("Project name")
73
+ description = typer.prompt("Description", default="")
74
+ confirm = typer.confirm("Create project?")
75
+
76
+ if not confirm:
77
+ raise typer.Abort()
78
+
79
+ # Create project...
80
+ ```
81
+
82
+ ## Progress and Output
83
+ ```python
84
+ from rich.progress import track
85
+ from rich.table import Table
86
+
87
+ @app.command()
88
+ def process_files(files: list[Path] = typer.Argument(...)):
89
+ results = []
90
+ for file in track(files, description="Processing..."):
91
+ result = process_file(file)
92
+ results.append(result)
93
+
94
+ # Display results table
95
+ table = Table(title="Results")
96
+ table.add_column("File")
97
+ table.add_column("Status")
98
+
99
+ for r in results:
100
+ table.add_row(r.file, r.status)
101
+
102
+ console.print(table)
103
+ ```
@@ -0,0 +1,107 @@
1
+ # CLI Output Best Practices
2
+
3
+ ## Rich Console Output
4
+ ```python
5
+ from rich.console import Console
6
+ from rich.panel import Panel
7
+ from rich.syntax import Syntax
8
+
9
+ console = Console()
10
+
11
+ # Colors and styles
12
+ console.print("[green]Success![/green] Operation completed.")
13
+ console.print("[red]Error:[/red] Something went wrong.")
14
+ console.print("[yellow]Warning:[/yellow] Proceed with caution.")
15
+ console.print("[bold]Important:[/bold] Read this carefully.")
16
+
17
+ # Panels for grouped info
18
+ console.print(Panel("This is important information", title="Notice"))
19
+
20
+ # Code highlighting
21
+ code = Syntax(source_code, "python", theme="monokai")
22
+ console.print(code)
23
+ ```
24
+
25
+ ## Structured Output
26
+ ```python
27
+ from rich.table import Table
28
+
29
+ def show_results(items: list[Item]):
30
+ table = Table(title="Results", show_header=True)
31
+ table.add_column("ID", style="cyan")
32
+ table.add_column("Name")
33
+ table.add_column("Status", justify="center")
34
+
35
+ for item in items:
36
+ status_color = "green" if item.active else "red"
37
+ table.add_row(
38
+ str(item.id),
39
+ item.name,
40
+ f"[{status_color}]{'Active' if item.active else 'Inactive'}[/]"
41
+ )
42
+
43
+ console.print(table)
44
+ ```
45
+
46
+ ## Progress Indicators
47
+ ```python
48
+ from rich.progress import Progress, SpinnerColumn, TextColumn
49
+
50
+ # Simple progress
51
+ for item in track(items, description="Processing..."):
52
+ process(item)
53
+
54
+ # Custom progress
55
+ with Progress(
56
+ SpinnerColumn(),
57
+ TextColumn("[progress.description]{task.description}"),
58
+ ) as progress:
59
+ task = progress.add_task("Loading...", total=None)
60
+ result = long_running_operation()
61
+ progress.update(task, completed=True)
62
+ ```
63
+
64
+ ## JSON/Machine Output
65
+ ```python
66
+ import json
67
+
68
+ @app.command()
69
+ def list_items(
70
+ json_output: bool = typer.Option(False, "--json", help="Output as JSON"),
71
+ ):
72
+ items = get_items()
73
+
74
+ if json_output:
75
+ # Machine-readable output
76
+ print(json.dumps([i.dict() for i in items], indent=2))
77
+ else:
78
+ # Human-readable output
79
+ show_results(items)
80
+ ```
81
+
82
+ ## Exit Codes
83
+ ```python
84
+ # Success
85
+ raise typer.Exit(0)
86
+
87
+ # General error
88
+ raise typer.Exit(1)
89
+
90
+ # User abort
91
+ raise typer.Abort()
92
+
93
+ # Specific exit codes
94
+ EXIT_SUCCESS = 0
95
+ EXIT_ERROR = 1
96
+ EXIT_USAGE = 2
97
+ EXIT_CONFIG = 3
98
+ ```
99
+
100
+ ## Error Messages
101
+ ```python
102
+ def handle_error(e: Exception):
103
+ console.print(f"[red]Error:[/red] {e}")
104
+ if verbose:
105
+ console.print_exception()
106
+ raise typer.Exit(1)
107
+ ```
@@ -0,0 +1,91 @@
1
+ # Python CLI Project Structure
2
+
3
+ ## Directory Layout
4
+ ```
5
+ my-cli/
6
+ ├── src/
7
+ │ └── my_cli/
8
+ │ ├── __init__.py
9
+ │ ├── __main__.py # Entry point
10
+ │ ├── cli.py # CLI definition
11
+ │ ├── commands/
12
+ │ │ ├── __init__.py
13
+ │ │ ├── init.py
14
+ │ │ └── run.py
15
+ │ ├── config.py
16
+ │ └── utils.py
17
+ ├── tests/
18
+ │ └── test_commands.py
19
+ ├── pyproject.toml
20
+ └── README.md
21
+ ```
22
+
23
+ ## Main Entry Point
24
+ ```python
25
+ # src/my_cli/__main__.py
26
+ from .cli import app
27
+
28
+ if __name__ == "__main__":
29
+ app()
30
+ ```
31
+
32
+ ## CLI Definition (Typer)
33
+ ```python
34
+ # src/my_cli/cli.py
35
+ import typer
36
+ from typing import Optional
37
+
38
+ from .commands import init, run
39
+
40
+ app = typer.Typer(
41
+ name="my-cli",
42
+ help="My CLI tool description",
43
+ add_completion=False,
44
+ )
45
+
46
+ # Add subcommands
47
+ app.add_typer(init.app, name="init")
48
+ app.add_typer(run.app, name="run")
49
+
50
+ @app.callback()
51
+ def main(
52
+ verbose: bool = typer.Option(False, "--verbose", "-v", help="Verbose output"),
53
+ config: Optional[str] = typer.Option(None, "--config", "-c", help="Config file"),
54
+ ):
55
+ """My CLI tool - does useful things."""
56
+ if verbose:
57
+ # Set up verbose logging
58
+ pass
59
+ ```
60
+
61
+ ## pyproject.toml
62
+ ```toml
63
+ [project]
64
+ name = "my-cli"
65
+ version = "0.1.0"
66
+ requires-python = ">=3.10"
67
+ dependencies = [
68
+ "typer>=0.9.0",
69
+ "rich>=13.0.0",
70
+ ]
71
+
72
+ [project.scripts]
73
+ my-cli = "my_cli.cli:app"
74
+
75
+ [project.optional-dependencies]
76
+ dev = [
77
+ "pytest>=7.0",
78
+ "pytest-cov>=4.0",
79
+ ]
80
+ ```
81
+
82
+ ## Installation
83
+ ```bash
84
+ # Development install
85
+ pip install -e ".[dev]"
86
+
87
+ # Run
88
+ my-cli --help
89
+ my-cli init
90
+ my-cli run --verbose
91
+ ```
@@ -0,0 +1,6 @@
1
+ {
2
+ "name": "python-cli",
3
+ "description": "Python CLI application",
4
+ "includes": ["universal", "languages/python"],
5
+ "mcpDefaults": ["github", "filesystem"]
6
+ }
@@ -0,0 +1,84 @@
1
+ # React JavaScript Component Rules
2
+
3
+ ## Component Structure
4
+ ```javascript
5
+ // Functional components only
6
+ export function UserCard({ user, onEdit, className }) {
7
+ return (
8
+ <div className={className}>
9
+ <h3>{user.name}</h3>
10
+ {onEdit && <button onClick={() => onEdit(user)}>Edit</button>}
11
+ </div>
12
+ );
13
+ }
14
+
15
+ // PropTypes for runtime validation
16
+ import PropTypes from 'prop-types';
17
+
18
+ UserCard.propTypes = {
19
+ user: PropTypes.shape({
20
+ id: PropTypes.number.isRequired,
21
+ name: PropTypes.string.isRequired,
22
+ }).isRequired,
23
+ onEdit: PropTypes.func,
24
+ className: PropTypes.string,
25
+ };
26
+
27
+ UserCard.defaultProps = {
28
+ className: '',
29
+ };
30
+ ```
31
+
32
+ ## File Organization
33
+ ```
34
+ src/
35
+ ├── components/
36
+ │ ├── ui/ # Generic reusable (Button, Input, Modal)
37
+ │ ├── features/ # Feature-specific (UserCard, OrderList)
38
+ │ └── layout/ # Layout (Header, Sidebar, Footer)
39
+ ├── hooks/ # Custom hooks
40
+ ├── pages/ # Route pages
41
+ ├── services/ # API calls
42
+ └── utils/ # Helper functions
43
+ ```
44
+
45
+ ## Naming Conventions
46
+ - Components: `PascalCase` - `UserCard.jsx`
47
+ - Hooks: `useCamelCase` - `useAuth.js`
48
+ - Utils: `camelCase` - `formatDate.js`
49
+ - Files: Match component name
50
+
51
+ ## Props Pattern
52
+ ```javascript
53
+ // Destructure props with defaults
54
+ export function Button({
55
+ variant = 'primary',
56
+ loading = false,
57
+ children,
58
+ ...props
59
+ }) {
60
+ return (
61
+ <button disabled={loading} {...props}>
62
+ {loading ? <Spinner /> : children}
63
+ </button>
64
+ );
65
+ }
66
+
67
+ Button.propTypes = {
68
+ variant: PropTypes.oneOf(['primary', 'secondary']),
69
+ loading: PropTypes.bool,
70
+ children: PropTypes.node.isRequired,
71
+ };
72
+ ```
73
+
74
+ ## JSDoc for Type Hints
75
+ ```javascript
76
+ /**
77
+ * @param {Object} props
78
+ * @param {import('../types').User} props.user
79
+ * @param {(user: import('../types').User) => void} [props.onEdit]
80
+ */
81
+ export function UserCard({ user, onEdit }) {
82
+ // IDE gets type hints from JSDoc
83
+ }
84
+ ```
@@ -0,0 +1,98 @@
1
+ # React Hooks Rules (JavaScript)
2
+
3
+ ## Rules of Hooks
4
+ - Only call hooks at the top level
5
+ - Only call hooks from React functions
6
+ - Custom hooks must start with `use`
7
+
8
+ ## useState
9
+ ```javascript
10
+ // Simple state
11
+ const [count, setCount] = useState(0);
12
+ const [user, setUser] = useState(null);
13
+
14
+ // Lazy initialization for expensive values
15
+ const [data, setData] = useState(() => computeExpensiveValue());
16
+
17
+ // Functional updates
18
+ setCount(prev => prev + 1);
19
+ ```
20
+
21
+ ## useEffect
22
+ ```javascript
23
+ // Fetch data
24
+ useEffect(() => {
25
+ let cancelled = false;
26
+
27
+ fetchUser(userId).then(data => {
28
+ if (!cancelled) setUser(data);
29
+ });
30
+
31
+ return () => { cancelled = true; };
32
+ }, [userId]);
33
+
34
+ // Event listener
35
+ useEffect(() => {
36
+ const handler = (e) => setSize({ width: e.target.innerWidth });
37
+ window.addEventListener('resize', handler);
38
+ return () => window.removeEventListener('resize', handler);
39
+ }, []);
40
+ ```
41
+
42
+ ## useMemo & useCallback
43
+ ```javascript
44
+ // Expensive calculation
45
+ const sortedList = useMemo(
46
+ () => items.sort((a, b) => a.name.localeCompare(b.name)),
47
+ [items]
48
+ );
49
+
50
+ // Stable callback reference
51
+ const handleClick = useCallback(
52
+ () => { onClick(id); },
53
+ [onClick, id]
54
+ );
55
+ ```
56
+
57
+ ## Custom Hooks
58
+ ```javascript
59
+ // Reusable data fetching
60
+ function useFetch(url) {
61
+ const [data, setData] = useState(null);
62
+ const [loading, setLoading] = useState(true);
63
+ const [error, setError] = useState(null);
64
+
65
+ useEffect(() => {
66
+ let cancelled = false;
67
+ setLoading(true);
68
+
69
+ fetch(url)
70
+ .then(res => res.json())
71
+ .then(data => {
72
+ if (!cancelled) {
73
+ setData(data);
74
+ setLoading(false);
75
+ }
76
+ })
77
+ .catch(err => {
78
+ if (!cancelled) {
79
+ setError(err);
80
+ setLoading(false);
81
+ }
82
+ });
83
+
84
+ return () => { cancelled = true; };
85
+ }, [url]);
86
+
87
+ return { data, loading, error };
88
+ }
89
+
90
+ // Usage
91
+ function UserProfile({ userId }) {
92
+ const { data: user, loading, error } = useFetch(`/api/users/${userId}`);
93
+
94
+ if (loading) return <Spinner />;
95
+ if (error) return <Error message={error.message} />;
96
+ return <UserCard user={user} />;
97
+ }
98
+ ```
@@ -0,0 +1,6 @@
1
+ {
2
+ "name": "react-js",
3
+ "description": "React with JavaScript framework",
4
+ "includes": ["universal", "languages/javascript"],
5
+ "mcpDefaults": ["github", "filesystem", "puppeteer"]
6
+ }
@@ -0,0 +1,72 @@
1
+ # React TypeScript Component Rules
2
+
3
+ ## Component Structure
4
+ ```typescript
5
+ // Functional components only (no class components)
6
+ interface UserCardProps {
7
+ user: User;
8
+ onEdit?: (user: User) => void;
9
+ className?: string;
10
+ }
11
+
12
+ export function UserCard({ user, onEdit, className }: UserCardProps) {
13
+ return (
14
+ <div className={className}>
15
+ <h3>{user.name}</h3>
16
+ {onEdit && <button onClick={() => onEdit(user)}>Edit</button>}
17
+ </div>
18
+ );
19
+ }
20
+ ```
21
+
22
+ ## File Organization
23
+ ```
24
+ src/
25
+ ├── components/
26
+ │ ├── ui/ # Generic reusable (Button, Input, Modal)
27
+ │ ├── features/ # Feature-specific (UserCard, OrderList)
28
+ │ └── layout/ # Layout (Header, Sidebar, Footer)
29
+ ├── hooks/ # Custom hooks
30
+ ├── pages/ # Route pages
31
+ ├── services/ # API calls
32
+ ├── types/ # TypeScript types
33
+ └── utils/ # Helper functions
34
+ ```
35
+
36
+ ## Naming Conventions
37
+ - Components: `PascalCase` - `UserCard.tsx`
38
+ - Hooks: `useCamelCase` - `useAuth.ts`
39
+ - Utils: `camelCase` - `formatDate.ts`
40
+ - Types: `PascalCase` - `User`, `UserCardProps`
41
+
42
+ ## Props Typing
43
+ ```typescript
44
+ // Define props interface
45
+ interface ButtonProps extends React.ButtonHTMLAttributes<HTMLButtonElement> {
46
+ variant?: 'primary' | 'secondary';
47
+ loading?: boolean;
48
+ children: React.ReactNode;
49
+ }
50
+
51
+ // Use children explicitly
52
+ export function Button({ variant = 'primary', loading, children, ...props }: ButtonProps) {
53
+ return (
54
+ <button disabled={loading} {...props}>
55
+ {loading ? <Spinner /> : children}
56
+ </button>
57
+ );
58
+ }
59
+ ```
60
+
61
+ ## Event Handlers
62
+ ```typescript
63
+ // Type event handlers properly
64
+ function handleChange(e: React.ChangeEvent<HTMLInputElement>) {
65
+ setValue(e.target.value);
66
+ }
67
+
68
+ function handleSubmit(e: React.FormEvent<HTMLFormElement>) {
69
+ e.preventDefault();
70
+ // ...
71
+ }
72
+ ```
@@ -0,0 +1,87 @@
1
+ # React Hooks Rules
2
+
3
+ ## Rules of Hooks
4
+ - Only call hooks at the top level (not in loops, conditions, or nested functions)
5
+ - Only call hooks from React functions (components or custom hooks)
6
+ - Custom hooks must start with `use`
7
+
8
+ ## useState
9
+ ```typescript
10
+ // Type inference works for primitives
11
+ const [count, setCount] = useState(0);
12
+
13
+ // Explicit typing for complex types
14
+ const [user, setUser] = useState<User | null>(null);
15
+
16
+ // Use functional updates for derived state
17
+ setCount(prev => prev + 1);
18
+ ```
19
+
20
+ ## useEffect
21
+ ```typescript
22
+ // Always specify dependencies
23
+ useEffect(() => {
24
+ fetchUser(userId).then(setUser);
25
+ }, [userId]);
26
+
27
+ // Cleanup subscriptions
28
+ useEffect(() => {
29
+ const subscription = api.subscribe(handleUpdate);
30
+ return () => subscription.unsubscribe();
31
+ }, []);
32
+
33
+ // Avoid useEffect for:
34
+ // - Transforming data (use useMemo)
35
+ // - Handling events (use event handlers)
36
+ // - Initializing state (use initializer function)
37
+ ```
38
+
39
+ ## useMemo & useCallback
40
+ ```typescript
41
+ // useMemo for expensive calculations
42
+ const sortedItems = useMemo(
43
+ () => items.sort((a, b) => a.name.localeCompare(b.name)),
44
+ [items]
45
+ );
46
+
47
+ // useCallback for stable function references
48
+ const handleSubmit = useCallback(
49
+ (data: FormData) => {
50
+ submitForm(data);
51
+ },
52
+ [submitForm]
53
+ );
54
+ ```
55
+
56
+ ## Custom Hooks
57
+ ```typescript
58
+ // Extract reusable logic
59
+ function useAsync<T>(asyncFn: () => Promise<T>, deps: unknown[] = []) {
60
+ const [state, setState] = useState<{
61
+ data: T | null;
62
+ loading: boolean;
63
+ error: Error | null;
64
+ }>({ data: null, loading: true, error: null });
65
+
66
+ useEffect(() => {
67
+ setState(s => ({ ...s, loading: true }));
68
+ asyncFn()
69
+ .then(data => setState({ data, loading: false, error: null }))
70
+ .catch(error => setState({ data: null, loading: false, error }));
71
+ }, deps);
72
+
73
+ return state;
74
+ }
75
+
76
+ // Usage
77
+ function UserProfile({ userId }: { userId: string }) {
78
+ const { data: user, loading, error } = useAsync(
79
+ () => fetchUser(userId),
80
+ [userId]
81
+ );
82
+
83
+ if (loading) return <Spinner />;
84
+ if (error) return <Error message={error.message} />;
85
+ return <UserCard user={user!} />;
86
+ }
87
+ ```