@ereo/client 0.1.6
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.
- package/README.md +93 -0
- package/dist/error-boundary.d.ts +215 -0
- package/dist/error-boundary.d.ts.map +1 -0
- package/dist/form.d.ts +436 -0
- package/dist/form.d.ts.map +1 -0
- package/dist/hooks.d.ts +261 -0
- package/dist/hooks.d.ts.map +1 -0
- package/dist/hydration.d.ts +67 -0
- package/dist/hydration.d.ts.map +1 -0
- package/dist/index.d.ts +32 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +2081 -0
- package/dist/islands.d.ts +103 -0
- package/dist/islands.d.ts.map +1 -0
- package/dist/link.d.ts +91 -0
- package/dist/link.d.ts.map +1 -0
- package/dist/navigation.d.ts +121 -0
- package/dist/navigation.d.ts.map +1 -0
- package/dist/prefetch.d.ts +57 -0
- package/dist/prefetch.d.ts.map +1 -0
- package/dist/typed-link.d.ts +189 -0
- package/dist/typed-link.d.ts.map +1 -0
- package/dist/typed-navigate.d.ts +238 -0
- package/dist/typed-navigate.d.ts.map +1 -0
- package/package.json +46 -0
package/README.md
ADDED
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
# @ereo/client
|
|
2
|
+
|
|
3
|
+
Client-side runtime for the EreoJS framework. Includes islands architecture, client-side navigation, prefetching, forms, and error boundaries.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
bun add @ereo/client
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Quick Start
|
|
12
|
+
|
|
13
|
+
```typescript
|
|
14
|
+
import { initClient, Link, useLoaderData } from '@ereo/client';
|
|
15
|
+
|
|
16
|
+
// Initialize the client runtime
|
|
17
|
+
initClient();
|
|
18
|
+
|
|
19
|
+
// Use loader data in components
|
|
20
|
+
function UserProfile() {
|
|
21
|
+
const { user } = useLoaderData<{ user: { name: string } }>();
|
|
22
|
+
return <h1>{user.name}</h1>;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
// Client-side navigation with prefetching
|
|
26
|
+
function Nav() {
|
|
27
|
+
return <Link to="/dashboard" prefetch="intent">Dashboard</Link>;
|
|
28
|
+
}
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
## Key Features
|
|
32
|
+
|
|
33
|
+
- **Islands Architecture** - Partial hydration with `createIsland` and `hydrateIslands`
|
|
34
|
+
- **Client Navigation** - SPA-like navigation with `navigate`, `goBack`, `goForward`
|
|
35
|
+
- **Prefetching** - Smart prefetching with `prefetch`, hover and viewport strategies
|
|
36
|
+
- **Data Hooks** - `useLoaderData`, `useActionData`, `useNavigation`, `useError`
|
|
37
|
+
- **Link Components** - `Link` and `NavLink` with active state detection
|
|
38
|
+
- **Forms** - Enhanced forms with `Form`, `useSubmit`, `useFetcher`
|
|
39
|
+
- **Error Boundaries** - Graceful error handling with `ErrorBoundary` and `RouteErrorBoundary`
|
|
40
|
+
- **Scroll Restoration** - Automatic scroll position management
|
|
41
|
+
|
|
42
|
+
## Forms Example
|
|
43
|
+
|
|
44
|
+
```tsx
|
|
45
|
+
import { Form, useSubmit, useNavigation } from '@ereo/client';
|
|
46
|
+
|
|
47
|
+
function ContactForm() {
|
|
48
|
+
const submit = useSubmit();
|
|
49
|
+
const navigation = useNavigation();
|
|
50
|
+
const isSubmitting = navigation.state === 'submitting';
|
|
51
|
+
|
|
52
|
+
return (
|
|
53
|
+
<Form method="post" action="/contact">
|
|
54
|
+
<input name="email" type="email" required />
|
|
55
|
+
<button disabled={isSubmitting}>
|
|
56
|
+
{isSubmitting ? 'Sending...' : 'Send'}
|
|
57
|
+
</button>
|
|
58
|
+
</Form>
|
|
59
|
+
);
|
|
60
|
+
}
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
## Islands
|
|
64
|
+
|
|
65
|
+
```tsx
|
|
66
|
+
import { createIsland } from '@ereo/client';
|
|
67
|
+
import Chart from './Chart';
|
|
68
|
+
|
|
69
|
+
// Create an island wrapper for selective hydration
|
|
70
|
+
const InteractiveChart = createIsland(Chart, 'Chart');
|
|
71
|
+
|
|
72
|
+
// Use with hydration directives
|
|
73
|
+
function Page() {
|
|
74
|
+
return (
|
|
75
|
+
<div>
|
|
76
|
+
<h1>Dashboard</h1>
|
|
77
|
+
<InteractiveChart client:visible data={chartData} />
|
|
78
|
+
</div>
|
|
79
|
+
);
|
|
80
|
+
}
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
## Documentation
|
|
84
|
+
|
|
85
|
+
For full documentation, visit [https://ereojs.dev/docs/client](https://ereojs.dev/docs/client)
|
|
86
|
+
|
|
87
|
+
## Part of EreoJS
|
|
88
|
+
|
|
89
|
+
This package is part of the [EreoJS](https://github.com/ereojs/ereo) monorepo - a modern full-stack framework built for Bun.
|
|
90
|
+
|
|
91
|
+
## License
|
|
92
|
+
|
|
93
|
+
MIT
|
|
@@ -0,0 +1,215 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @ereo/client - Error Boundary Context and Recovery System
|
|
3
|
+
*
|
|
4
|
+
* Provides error boundaries, context, and recovery utilities for the EreoJS framework.
|
|
5
|
+
*/
|
|
6
|
+
import { Component, type ReactNode, type ErrorInfo, type ComponentType } from 'react';
|
|
7
|
+
import type { RouteParams, ErrorConfig } from '@ereo/core';
|
|
8
|
+
import { ErrorContext, type ErrorContextValue } from './hooks';
|
|
9
|
+
/**
|
|
10
|
+
* Props for the ErrorBoundary component.
|
|
11
|
+
*/
|
|
12
|
+
export interface ErrorBoundaryProps {
|
|
13
|
+
/** Fallback UI when error occurs */
|
|
14
|
+
fallback?: ReactNode | ((error: Error, reset: () => void) => ReactNode);
|
|
15
|
+
/** Called when error is caught */
|
|
16
|
+
onError?: (error: Error, errorInfo: ErrorInfo) => void;
|
|
17
|
+
/** Children to render */
|
|
18
|
+
children: ReactNode;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* State for the ErrorBoundary component.
|
|
22
|
+
*/
|
|
23
|
+
interface ErrorBoundaryState {
|
|
24
|
+
error: Error | null;
|
|
25
|
+
errorInfo: ErrorInfo | null;
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Route error response structure.
|
|
29
|
+
*/
|
|
30
|
+
export interface RouteErrorResponse {
|
|
31
|
+
status: number;
|
|
32
|
+
statusText: string;
|
|
33
|
+
data: unknown;
|
|
34
|
+
internal?: boolean;
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Props for RouteErrorBoundary component.
|
|
38
|
+
*/
|
|
39
|
+
export interface RouteErrorBoundaryProps {
|
|
40
|
+
/** Route identifier */
|
|
41
|
+
routeId: string;
|
|
42
|
+
/** Error configuration from route */
|
|
43
|
+
errorConfig?: ErrorConfig;
|
|
44
|
+
/** Children to render */
|
|
45
|
+
children: ReactNode;
|
|
46
|
+
/** Fallback component from route module */
|
|
47
|
+
fallbackComponent?: ComponentType<{
|
|
48
|
+
error: Error;
|
|
49
|
+
params: RouteParams;
|
|
50
|
+
}>;
|
|
51
|
+
/** Route params */
|
|
52
|
+
params?: RouteParams;
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Return type for useErrorBoundary hook.
|
|
56
|
+
*/
|
|
57
|
+
export interface UseErrorBoundaryReturn {
|
|
58
|
+
error: Error | undefined;
|
|
59
|
+
reset: () => void;
|
|
60
|
+
showBoundary: (error: Error) => void;
|
|
61
|
+
}
|
|
62
|
+
export { ErrorContext, type ErrorContextValue };
|
|
63
|
+
/**
|
|
64
|
+
* Error boundary component that catches JavaScript errors in child components.
|
|
65
|
+
*/
|
|
66
|
+
export declare class ErrorBoundary extends Component<ErrorBoundaryProps, ErrorBoundaryState> {
|
|
67
|
+
constructor(props: ErrorBoundaryProps);
|
|
68
|
+
/**
|
|
69
|
+
* Update state when an error is caught.
|
|
70
|
+
*/
|
|
71
|
+
static getDerivedStateFromError(error: Error): Partial<ErrorBoundaryState>;
|
|
72
|
+
/**
|
|
73
|
+
* Log error information and call onError callback.
|
|
74
|
+
*/
|
|
75
|
+
componentDidCatch(error: Error, errorInfo: ErrorInfo): void;
|
|
76
|
+
/**
|
|
77
|
+
* Reset the error state.
|
|
78
|
+
*/
|
|
79
|
+
reset: () => void;
|
|
80
|
+
render(): ReactNode;
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* Hook to access error boundary functionality.
|
|
84
|
+
* Provides error state, reset function, and ability to programmatically trigger errors.
|
|
85
|
+
*
|
|
86
|
+
* @returns Object with error state and control functions
|
|
87
|
+
* @throws Error if used outside EreoProvider or ErrorProvider
|
|
88
|
+
*
|
|
89
|
+
* @example
|
|
90
|
+
* ```tsx
|
|
91
|
+
* function MyComponent() {
|
|
92
|
+
* const { error, reset, showBoundary } = useErrorBoundary();
|
|
93
|
+
*
|
|
94
|
+
* const handleAsyncError = async () => {
|
|
95
|
+
* try {
|
|
96
|
+
* await fetchData();
|
|
97
|
+
* } catch (e) {
|
|
98
|
+
* showBoundary(e as Error);
|
|
99
|
+
* }
|
|
100
|
+
* };
|
|
101
|
+
*
|
|
102
|
+
* return <button onClick={handleAsyncError}>Fetch</button>;
|
|
103
|
+
* }
|
|
104
|
+
* ```
|
|
105
|
+
*/
|
|
106
|
+
export declare function useErrorBoundary(): UseErrorBoundaryReturn;
|
|
107
|
+
/**
|
|
108
|
+
* Hook to get the current route error.
|
|
109
|
+
* This is a simple implementation that accesses the error from context.
|
|
110
|
+
*
|
|
111
|
+
* @returns The current error or undefined
|
|
112
|
+
*
|
|
113
|
+
* @example
|
|
114
|
+
* ```tsx
|
|
115
|
+
* function ErrorPage() {
|
|
116
|
+
* const error = useRouteError();
|
|
117
|
+
*
|
|
118
|
+
* if (isRouteErrorResponse(error)) {
|
|
119
|
+
* return <div>HTTP Error: {error.status}</div>;
|
|
120
|
+
* }
|
|
121
|
+
*
|
|
122
|
+
* return <div>Unknown error occurred</div>;
|
|
123
|
+
* }
|
|
124
|
+
* ```
|
|
125
|
+
*/
|
|
126
|
+
export declare function useRouteError(): unknown;
|
|
127
|
+
/**
|
|
128
|
+
* Error boundary specifically for route-level error handling.
|
|
129
|
+
* Integrates with route configuration and supports error recovery strategies.
|
|
130
|
+
*/
|
|
131
|
+
export declare class RouteErrorBoundary extends Component<RouteErrorBoundaryProps, ErrorBoundaryState & {
|
|
132
|
+
retryCount: number;
|
|
133
|
+
}> {
|
|
134
|
+
constructor(props: RouteErrorBoundaryProps);
|
|
135
|
+
static getDerivedStateFromError(error: Error): Partial<ErrorBoundaryState>;
|
|
136
|
+
componentDidCatch(error: Error, errorInfo: ErrorInfo): void;
|
|
137
|
+
/**
|
|
138
|
+
* Reset the error state and optionally retry.
|
|
139
|
+
*/
|
|
140
|
+
reset: () => void;
|
|
141
|
+
render(): ReactNode;
|
|
142
|
+
}
|
|
143
|
+
/**
|
|
144
|
+
* Type guard to check if an error is a RouteErrorResponse.
|
|
145
|
+
*
|
|
146
|
+
* @param error - The error to check
|
|
147
|
+
* @returns True if the error is a RouteErrorResponse
|
|
148
|
+
*
|
|
149
|
+
* @example
|
|
150
|
+
* ```tsx
|
|
151
|
+
* const error = useRouteError();
|
|
152
|
+
*
|
|
153
|
+
* if (isRouteErrorResponse(error)) {
|
|
154
|
+
* console.log(error.status); // e.g., 404
|
|
155
|
+
* console.log(error.statusText); // e.g., "Not Found"
|
|
156
|
+
* }
|
|
157
|
+
* ```
|
|
158
|
+
*/
|
|
159
|
+
export declare function isRouteErrorResponse(error: unknown): error is RouteErrorResponse;
|
|
160
|
+
/**
|
|
161
|
+
* Create a RouteErrorResponse.
|
|
162
|
+
*
|
|
163
|
+
* @param status - HTTP status code
|
|
164
|
+
* @param statusText - HTTP status text
|
|
165
|
+
* @param data - Additional error data
|
|
166
|
+
* @returns A RouteErrorResponse object
|
|
167
|
+
*
|
|
168
|
+
* @example
|
|
169
|
+
* ```tsx
|
|
170
|
+
* throw createRouteErrorResponse(404, 'Not Found', { message: 'Page not found' });
|
|
171
|
+
* ```
|
|
172
|
+
*/
|
|
173
|
+
export declare function createRouteErrorResponse(status: number, statusText: string, data?: unknown): RouteErrorResponse;
|
|
174
|
+
/**
|
|
175
|
+
* Wrap a component with an error boundary.
|
|
176
|
+
*
|
|
177
|
+
* @param WrappedComponent - The component to wrap
|
|
178
|
+
* @param errorBoundaryProps - Props to pass to the error boundary
|
|
179
|
+
* @returns A new component wrapped with an error boundary
|
|
180
|
+
*
|
|
181
|
+
* @example
|
|
182
|
+
* ```tsx
|
|
183
|
+
* const SafeComponent = withErrorBoundary(MyComponent, {
|
|
184
|
+
* fallback: <div>Something went wrong</div>,
|
|
185
|
+
* onError: (error) => console.error(error),
|
|
186
|
+
* });
|
|
187
|
+
* ```
|
|
188
|
+
*/
|
|
189
|
+
export declare function withErrorBoundary<P extends object>(WrappedComponent: ComponentType<P>, errorBoundaryProps?: Omit<ErrorBoundaryProps, 'children'>): ComponentType<P>;
|
|
190
|
+
/**
|
|
191
|
+
* Create a custom error for route responses.
|
|
192
|
+
*
|
|
193
|
+
* @example
|
|
194
|
+
* ```tsx
|
|
195
|
+
* // In a loader function
|
|
196
|
+
* export async function loader({ params }: LoaderArgs) {
|
|
197
|
+
* const post = await getPost(params.id);
|
|
198
|
+
* if (!post) {
|
|
199
|
+
* throw new RouteError(404, 'Not Found', { message: 'Post not found' });
|
|
200
|
+
* }
|
|
201
|
+
* return { post };
|
|
202
|
+
* }
|
|
203
|
+
* ```
|
|
204
|
+
*/
|
|
205
|
+
export declare class RouteError extends Error {
|
|
206
|
+
status: number;
|
|
207
|
+
statusText: string;
|
|
208
|
+
data: unknown;
|
|
209
|
+
constructor(status: number, statusText: string, data?: unknown);
|
|
210
|
+
/**
|
|
211
|
+
* Convert to RouteErrorResponse.
|
|
212
|
+
*/
|
|
213
|
+
toResponse(): RouteErrorResponse;
|
|
214
|
+
}
|
|
215
|
+
//# sourceMappingURL=error-boundary.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"error-boundary.d.ts","sourceRoot":"","sources":["../src/error-boundary.tsx"],"names":[],"mappings":"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"}
|