@ragbits/api-client-react 0.0.3
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 +253 -0
- package/dist/RagbitsProvider.d.ts +7 -0
- package/dist/hooks.d.ts +17 -0
- package/dist/index.cjs +203 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.js +173 -0
- package/dist/types.d.ts +18 -0
- package/package.json +62 -0
package/README.md
ADDED
|
@@ -0,0 +1,253 @@
|
|
|
1
|
+
# @ragbits/api-client-react
|
|
2
|
+
|
|
3
|
+
React hooks for the Ragbits API client. This package provides React-friendly hooks built on top of the core `@ragbits/api-client` package with full TypeScript support and automatic state management.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
- **React hooks** - Type-safe hooks for API calls and streaming
|
|
8
|
+
- **Automatic state management** - Loading states, error handling, and data caching
|
|
9
|
+
- **Request cancellation** - Built-in abort functionality for ongoing requests
|
|
10
|
+
- **Streaming support** - Real-time streaming with React state integration
|
|
11
|
+
- **TypeScript support** - Full type safety with predefined endpoints
|
|
12
|
+
- **Context provider** - Easy setup with React Context
|
|
13
|
+
|
|
14
|
+
## Installation
|
|
15
|
+
|
|
16
|
+
```bash
|
|
17
|
+
npm install @ragbits/api-client-react
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
**Note:** This package depends on `@ragbits/api-client` which will be installed automatically.
|
|
21
|
+
|
|
22
|
+
## Quick Start
|
|
23
|
+
|
|
24
|
+
### 1. Setup Provider
|
|
25
|
+
|
|
26
|
+
First, wrap your app with the `RagbitsProvider`:
|
|
27
|
+
|
|
28
|
+
```tsx
|
|
29
|
+
import { RagbitsProvider } from '@ragbits/api-client-react'
|
|
30
|
+
|
|
31
|
+
function App() {
|
|
32
|
+
return (
|
|
33
|
+
<RagbitsProvider baseUrl="http://localhost:8000">
|
|
34
|
+
<YourApp />
|
|
35
|
+
</RagbitsProvider>
|
|
36
|
+
)
|
|
37
|
+
}
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
### 2. Hooks Usage Examples
|
|
41
|
+
|
|
42
|
+
#### API Call Example
|
|
43
|
+
|
|
44
|
+
```tsx
|
|
45
|
+
import { useRagbitsCall } from '@ragbits/api-client-react'
|
|
46
|
+
|
|
47
|
+
function ConfigComponent() {
|
|
48
|
+
const config = useRagbitsCall('/api/config')
|
|
49
|
+
|
|
50
|
+
const handleLoadConfig = async () => {
|
|
51
|
+
try {
|
|
52
|
+
await config.call()
|
|
53
|
+
console.log('Config loaded:', config.data)
|
|
54
|
+
} catch (error) {
|
|
55
|
+
console.error('Failed to load config:', error)
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
return (
|
|
60
|
+
<div>
|
|
61
|
+
<button onClick={handleLoadConfig} disabled={config.isLoading}>
|
|
62
|
+
{config.isLoading
|
|
63
|
+
? 'Loading...'
|
|
64
|
+
: config.data
|
|
65
|
+
? 'Reload Config'
|
|
66
|
+
: 'Load Config'}
|
|
67
|
+
</button>
|
|
68
|
+
|
|
69
|
+
{config.data && (
|
|
70
|
+
<div>
|
|
71
|
+
<h3>Config loaded successfully</h3>
|
|
72
|
+
</div>
|
|
73
|
+
)}
|
|
74
|
+
|
|
75
|
+
{config.error && <p>Error: {config.error.message}</p>}
|
|
76
|
+
</div>
|
|
77
|
+
)
|
|
78
|
+
}
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
#### Streaming Example
|
|
82
|
+
|
|
83
|
+
```tsx
|
|
84
|
+
import { useRagbitsStream } from '@ragbits/api-client-react'
|
|
85
|
+
|
|
86
|
+
function ChatComponent() {
|
|
87
|
+
const chatStream = useRagbitsStream('/api/chat')
|
|
88
|
+
|
|
89
|
+
const handleSendMessage = () => {
|
|
90
|
+
const chatRequest = {
|
|
91
|
+
message: 'Hello!',
|
|
92
|
+
history: [],
|
|
93
|
+
context: {},
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
chatStream.stream(chatRequest, {
|
|
97
|
+
onMessage: (chunk) => {
|
|
98
|
+
console.log('chunk:', chunk)
|
|
99
|
+
},
|
|
100
|
+
onError: (error) => {
|
|
101
|
+
console.error('Stream error:', error)
|
|
102
|
+
},
|
|
103
|
+
onClose: () => {
|
|
104
|
+
console.log('Stream completed')
|
|
105
|
+
},
|
|
106
|
+
})
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
return (
|
|
110
|
+
<div>
|
|
111
|
+
<button
|
|
112
|
+
onClick={handleSendMessage}
|
|
113
|
+
disabled={chatStream.isStreaming}
|
|
114
|
+
>
|
|
115
|
+
{chatStream.isStreaming ? 'Streaming...' : 'Send Message'}
|
|
116
|
+
</button>
|
|
117
|
+
|
|
118
|
+
<button
|
|
119
|
+
onClick={chatStream.cancel}
|
|
120
|
+
disabled={!chatStream.isStreaming}
|
|
121
|
+
>
|
|
122
|
+
Cancel
|
|
123
|
+
</button>
|
|
124
|
+
|
|
125
|
+
{chatStream.error && <p>Error: {chatStream.error.message}</p>}
|
|
126
|
+
</div>
|
|
127
|
+
)
|
|
128
|
+
}
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
## API Reference
|
|
132
|
+
|
|
133
|
+
### `RagbitsProvider`
|
|
134
|
+
|
|
135
|
+
Provider component that sets up the `RagbitsClient` instance and provides it to the React context.
|
|
136
|
+
|
|
137
|
+
**Props:**
|
|
138
|
+
|
|
139
|
+
```typescript
|
|
140
|
+
interface RagbitsProviderProps {
|
|
141
|
+
baseUrl?: string // Base URL for the API (default: "http://127.0.0.1:8000")
|
|
142
|
+
children: ReactNode
|
|
143
|
+
}
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
### `useRagbitsCall<T>(endpoint, defaultOptions?)`
|
|
147
|
+
|
|
148
|
+
React hook for making type-safe API calls with automatic state management.
|
|
149
|
+
|
|
150
|
+
**Parameters:**
|
|
151
|
+
|
|
152
|
+
- `endpoint`: Predefined API endpoint path (e.g., '/api/config', '/api/feedback')
|
|
153
|
+
- `defaultOptions` (optional): Default request options
|
|
154
|
+
|
|
155
|
+
**Returns:**
|
|
156
|
+
|
|
157
|
+
```typescript
|
|
158
|
+
interface RagbitsCallResult<T, E = Error> {
|
|
159
|
+
data: T | null // Response data
|
|
160
|
+
error: E | null // Error if request failed
|
|
161
|
+
isLoading: boolean // Loading state
|
|
162
|
+
call: (options?) => Promise<T> // Function to make the API call
|
|
163
|
+
reset: () => void // Reset state to initial values
|
|
164
|
+
abort: () => void // Abort current request
|
|
165
|
+
}
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
### `useRagbitsStream<T>(endpoint)`
|
|
169
|
+
|
|
170
|
+
React hook for handling streaming responses with automatic state management.
|
|
171
|
+
|
|
172
|
+
**Parameters:**
|
|
173
|
+
|
|
174
|
+
- `endpoint`: Predefined streaming endpoint path (e.g., '/api/chat')
|
|
175
|
+
|
|
176
|
+
**Returns:**
|
|
177
|
+
|
|
178
|
+
```typescript
|
|
179
|
+
interface RagbitsStreamResult<E = Error> {
|
|
180
|
+
isStreaming: boolean // Whether a stream is active
|
|
181
|
+
error: E | null // Stream error
|
|
182
|
+
stream: (data, callbacks) => () => void // Start streaming
|
|
183
|
+
cancel: () => void // Cancel current stream
|
|
184
|
+
}
|
|
185
|
+
```
|
|
186
|
+
|
|
187
|
+
### `useRagbitsContext()`
|
|
188
|
+
|
|
189
|
+
Access the underlying `RagbitsClient` instance directly.
|
|
190
|
+
|
|
191
|
+
**Returns:**
|
|
192
|
+
|
|
193
|
+
```typescript
|
|
194
|
+
interface RagbitsContextValue {
|
|
195
|
+
client: RagbitsClient
|
|
196
|
+
}
|
|
197
|
+
```
|
|
198
|
+
|
|
199
|
+
## Types
|
|
200
|
+
|
|
201
|
+
All types from `@ragbits/api-client` are re-exported:
|
|
202
|
+
|
|
203
|
+
```typescript
|
|
204
|
+
import type {
|
|
205
|
+
// Core types
|
|
206
|
+
RagbitsClient,
|
|
207
|
+
ClientConfig,
|
|
208
|
+
|
|
209
|
+
// Request/Response types
|
|
210
|
+
ChatRequest,
|
|
211
|
+
FeedbackRequest,
|
|
212
|
+
ConfigResponse,
|
|
213
|
+
FeedbackResponse,
|
|
214
|
+
|
|
215
|
+
// Message types
|
|
216
|
+
Message,
|
|
217
|
+
MessageRole,
|
|
218
|
+
TypedChatResponse,
|
|
219
|
+
ChatResponseType,
|
|
220
|
+
|
|
221
|
+
// Feedback types
|
|
222
|
+
FeedbackType,
|
|
223
|
+
|
|
224
|
+
// Stream types
|
|
225
|
+
StreamCallbacks,
|
|
226
|
+
|
|
227
|
+
// Endpoint types
|
|
228
|
+
ApiEndpointPath,
|
|
229
|
+
StreamingEndpointPath,
|
|
230
|
+
ApiEndpointResponse,
|
|
231
|
+
StreamingEndpointStream,
|
|
232
|
+
TypedApiRequestOptions,
|
|
233
|
+
} from '@ragbits/api-client-react'
|
|
234
|
+
```
|
|
235
|
+
|
|
236
|
+
## Browser Support
|
|
237
|
+
|
|
238
|
+
This package supports all modern browsers with React 16.8+ (for hooks):
|
|
239
|
+
|
|
240
|
+
- Chrome 42+
|
|
241
|
+
- Firefox 39+
|
|
242
|
+
- Safari 10.1+
|
|
243
|
+
- Edge 14+
|
|
244
|
+
|
|
245
|
+
## Node.js Support
|
|
246
|
+
|
|
247
|
+
For Node.js environments, you'll need:
|
|
248
|
+
|
|
249
|
+
- Node.js 18+
|
|
250
|
+
|
|
251
|
+
## License
|
|
252
|
+
|
|
253
|
+
MIT
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { type ReactNode } from 'react';
|
|
2
|
+
import type { ClientConfig, RagbitsContextValue } from './index';
|
|
3
|
+
export interface RagbitsProviderProps extends ClientConfig {
|
|
4
|
+
children: ReactNode;
|
|
5
|
+
}
|
|
6
|
+
export declare function RagbitsProvider({ children, ...config }: RagbitsProviderProps): import("react/jsx-runtime").JSX.Element;
|
|
7
|
+
export declare function useRagbitsContext(): RagbitsContextValue;
|
package/dist/hooks.d.ts
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { ApiEndpointPath, ApiEndpointResponse, TypedApiRequestOptions, StreamingEndpointPath } from '@ragbits/api-client';
|
|
2
|
+
import type { RagbitsCallResult, RagbitsStreamResult } from './types';
|
|
3
|
+
/**
|
|
4
|
+
* Hook for making API calls to Ragbits endpoints
|
|
5
|
+
* - Only predefined routes are allowed
|
|
6
|
+
* - Response type can be overridden with explicit type parameter
|
|
7
|
+
* @param endpoint - The predefined API endpoint
|
|
8
|
+
* @param defaultOptions - Default options for the API call
|
|
9
|
+
*/
|
|
10
|
+
export declare function useRagbitsCall<TEndpoint extends ApiEndpointPath, TResponse = ApiEndpointResponse<TEndpoint>>(endpoint: TEndpoint, defaultOptions?: TypedApiRequestOptions<TEndpoint>): RagbitsCallResult<TResponse, Error, TEndpoint>;
|
|
11
|
+
/**
|
|
12
|
+
* Hook for handling streaming responses from Ragbits endpoints
|
|
13
|
+
* - Only predefined streaming routes are allowed
|
|
14
|
+
* - Response type can be overridden with explicit type parameter
|
|
15
|
+
* @param endpoint - The predefined streaming endpoint
|
|
16
|
+
*/
|
|
17
|
+
export declare function useRagbitsStream<TEndpoint extends StreamingEndpointPath>(endpoint: TEndpoint): RagbitsStreamResult<Error, TEndpoint>;
|
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,203 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __reExport = (target, mod, secondTarget) => (__copyProps(target, mod, "default"), secondTarget && __copyProps(secondTarget, mod, "default"));
|
|
19
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
20
|
+
|
|
21
|
+
// src/index.ts
|
|
22
|
+
var index_exports = {};
|
|
23
|
+
__export(index_exports, {
|
|
24
|
+
RagbitsProvider: () => RagbitsProvider,
|
|
25
|
+
useRagbitsCall: () => useRagbitsCall,
|
|
26
|
+
useRagbitsContext: () => useRagbitsContext,
|
|
27
|
+
useRagbitsStream: () => useRagbitsStream
|
|
28
|
+
});
|
|
29
|
+
module.exports = __toCommonJS(index_exports);
|
|
30
|
+
__reExport(index_exports, require("@ragbits/api-client"), module.exports);
|
|
31
|
+
|
|
32
|
+
// src/hooks.ts
|
|
33
|
+
var import_react2 = require("react");
|
|
34
|
+
|
|
35
|
+
// src/RagbitsProvider.tsx
|
|
36
|
+
var import_react = require("react");
|
|
37
|
+
var import_api_client = require("@ragbits/api-client");
|
|
38
|
+
var import_jsx_runtime = require("react/jsx-runtime");
|
|
39
|
+
var RagbitsContext = (0, import_react.createContext)(null);
|
|
40
|
+
function RagbitsProvider({ children, ...config }) {
|
|
41
|
+
const client = (0, import_react.useMemo)(() => new import_api_client.RagbitsClient(config), [config]);
|
|
42
|
+
const contextValue = (0, import_react.useMemo)(
|
|
43
|
+
() => ({
|
|
44
|
+
client
|
|
45
|
+
}),
|
|
46
|
+
[client]
|
|
47
|
+
);
|
|
48
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(RagbitsContext.Provider, { value: contextValue, children });
|
|
49
|
+
}
|
|
50
|
+
function useRagbitsContext() {
|
|
51
|
+
const context = (0, import_react.useContext)(RagbitsContext);
|
|
52
|
+
if (!context) {
|
|
53
|
+
throw new Error(
|
|
54
|
+
"useRagbitsContext must be used within a RagbitsProvider"
|
|
55
|
+
);
|
|
56
|
+
}
|
|
57
|
+
return context;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
// src/hooks.ts
|
|
61
|
+
function useRagbitsCall(endpoint, defaultOptions) {
|
|
62
|
+
const { client } = useRagbitsContext();
|
|
63
|
+
const [data, setData] = (0, import_react2.useState)(null);
|
|
64
|
+
const [error, setError] = (0, import_react2.useState)(null);
|
|
65
|
+
const [isLoading, setIsLoading] = (0, import_react2.useState)(false);
|
|
66
|
+
const abortControllerRef = (0, import_react2.useRef)(null);
|
|
67
|
+
const abort = (0, import_react2.useCallback)(() => {
|
|
68
|
+
if (!abortControllerRef.current) {
|
|
69
|
+
return null;
|
|
70
|
+
}
|
|
71
|
+
abortControllerRef.current.abort();
|
|
72
|
+
abortControllerRef.current = null;
|
|
73
|
+
setIsLoading(false);
|
|
74
|
+
}, []);
|
|
75
|
+
const call = (0, import_react2.useCallback)(
|
|
76
|
+
async (options = {}) => {
|
|
77
|
+
if (abortControllerRef.current && isLoading) {
|
|
78
|
+
abortControllerRef.current.abort();
|
|
79
|
+
}
|
|
80
|
+
const abortController = new AbortController();
|
|
81
|
+
abortControllerRef.current = abortController;
|
|
82
|
+
setIsLoading(true);
|
|
83
|
+
setError(null);
|
|
84
|
+
try {
|
|
85
|
+
const mergedOptions = {
|
|
86
|
+
...defaultOptions,
|
|
87
|
+
...options,
|
|
88
|
+
headers: {
|
|
89
|
+
...defaultOptions?.headers,
|
|
90
|
+
...options.headers
|
|
91
|
+
}
|
|
92
|
+
};
|
|
93
|
+
const requestOptions = {
|
|
94
|
+
...mergedOptions,
|
|
95
|
+
signal: abortController.signal
|
|
96
|
+
};
|
|
97
|
+
const result = await client.makeRequest(
|
|
98
|
+
endpoint,
|
|
99
|
+
requestOptions
|
|
100
|
+
);
|
|
101
|
+
if (!abortController.signal.aborted) {
|
|
102
|
+
setData(result);
|
|
103
|
+
abortControllerRef.current = null;
|
|
104
|
+
}
|
|
105
|
+
return result;
|
|
106
|
+
} catch (err) {
|
|
107
|
+
if (!abortController.signal.aborted) {
|
|
108
|
+
const error2 = err instanceof Error ? err : new Error("API call failed");
|
|
109
|
+
setError(error2);
|
|
110
|
+
abortControllerRef.current = null;
|
|
111
|
+
throw error2;
|
|
112
|
+
}
|
|
113
|
+
throw err;
|
|
114
|
+
} finally {
|
|
115
|
+
if (!abortController.signal.aborted) {
|
|
116
|
+
setIsLoading(false);
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
},
|
|
120
|
+
[client, endpoint, defaultOptions, isLoading]
|
|
121
|
+
);
|
|
122
|
+
const reset = (0, import_react2.useCallback)(() => {
|
|
123
|
+
abort();
|
|
124
|
+
setData(null);
|
|
125
|
+
setError(null);
|
|
126
|
+
setIsLoading(false);
|
|
127
|
+
}, [abort]);
|
|
128
|
+
return {
|
|
129
|
+
data,
|
|
130
|
+
error,
|
|
131
|
+
isLoading,
|
|
132
|
+
call,
|
|
133
|
+
reset,
|
|
134
|
+
abort
|
|
135
|
+
};
|
|
136
|
+
}
|
|
137
|
+
function useRagbitsStream(endpoint) {
|
|
138
|
+
const { client } = useRagbitsContext();
|
|
139
|
+
const [isStreaming, setIsStreaming] = (0, import_react2.useState)(false);
|
|
140
|
+
const [error, setError] = (0, import_react2.useState)(null);
|
|
141
|
+
const abortControllerRef = (0, import_react2.useRef)(null);
|
|
142
|
+
const cancel = (0, import_react2.useCallback)(() => {
|
|
143
|
+
if (!abortControllerRef.current) {
|
|
144
|
+
return null;
|
|
145
|
+
}
|
|
146
|
+
abortControllerRef.current.abort();
|
|
147
|
+
abortControllerRef.current = null;
|
|
148
|
+
setIsStreaming(false);
|
|
149
|
+
}, []);
|
|
150
|
+
const stream = (0, import_react2.useCallback)(
|
|
151
|
+
(data, callbacks) => {
|
|
152
|
+
if (abortControllerRef.current && isStreaming) {
|
|
153
|
+
abortControllerRef.current.abort();
|
|
154
|
+
}
|
|
155
|
+
const abortController = new AbortController();
|
|
156
|
+
abortControllerRef.current = abortController;
|
|
157
|
+
setError(null);
|
|
158
|
+
setIsStreaming(true);
|
|
159
|
+
const cancelFn = client.makeStreamRequest(
|
|
160
|
+
endpoint,
|
|
161
|
+
data,
|
|
162
|
+
{
|
|
163
|
+
onMessage: callbacks.onMessage,
|
|
164
|
+
onError: (err) => {
|
|
165
|
+
if (!abortController.signal.aborted) {
|
|
166
|
+
setError(err);
|
|
167
|
+
setIsStreaming(false);
|
|
168
|
+
callbacks.onError(err.message);
|
|
169
|
+
}
|
|
170
|
+
},
|
|
171
|
+
onClose: () => {
|
|
172
|
+
if (!abortController.signal.aborted) {
|
|
173
|
+
setIsStreaming(false);
|
|
174
|
+
if (callbacks.onClose) {
|
|
175
|
+
callbacks.onClose();
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
},
|
|
180
|
+
abortController.signal
|
|
181
|
+
);
|
|
182
|
+
return () => {
|
|
183
|
+
cancel();
|
|
184
|
+
cancelFn();
|
|
185
|
+
};
|
|
186
|
+
},
|
|
187
|
+
[client, cancel, endpoint, isStreaming]
|
|
188
|
+
);
|
|
189
|
+
return {
|
|
190
|
+
isStreaming,
|
|
191
|
+
error,
|
|
192
|
+
stream,
|
|
193
|
+
cancel
|
|
194
|
+
};
|
|
195
|
+
}
|
|
196
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
197
|
+
0 && (module.exports = {
|
|
198
|
+
RagbitsProvider,
|
|
199
|
+
useRagbitsCall,
|
|
200
|
+
useRagbitsContext,
|
|
201
|
+
useRagbitsStream,
|
|
202
|
+
...require("@ragbits/api-client")
|
|
203
|
+
});
|
package/dist/index.d.ts
ADDED
package/dist/index.js
ADDED
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
// src/index.ts
|
|
2
|
+
export * from "@ragbits/api-client";
|
|
3
|
+
|
|
4
|
+
// src/hooks.ts
|
|
5
|
+
import { useState, useCallback, useRef } from "react";
|
|
6
|
+
|
|
7
|
+
// src/RagbitsProvider.tsx
|
|
8
|
+
import { createContext, useContext, useMemo } from "react";
|
|
9
|
+
import { RagbitsClient } from "@ragbits/api-client";
|
|
10
|
+
import { jsx } from "react/jsx-runtime";
|
|
11
|
+
var RagbitsContext = createContext(null);
|
|
12
|
+
function RagbitsProvider({ children, ...config }) {
|
|
13
|
+
const client = useMemo(() => new RagbitsClient(config), [config]);
|
|
14
|
+
const contextValue = useMemo(
|
|
15
|
+
() => ({
|
|
16
|
+
client
|
|
17
|
+
}),
|
|
18
|
+
[client]
|
|
19
|
+
);
|
|
20
|
+
return /* @__PURE__ */ jsx(RagbitsContext.Provider, { value: contextValue, children });
|
|
21
|
+
}
|
|
22
|
+
function useRagbitsContext() {
|
|
23
|
+
const context = useContext(RagbitsContext);
|
|
24
|
+
if (!context) {
|
|
25
|
+
throw new Error(
|
|
26
|
+
"useRagbitsContext must be used within a RagbitsProvider"
|
|
27
|
+
);
|
|
28
|
+
}
|
|
29
|
+
return context;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
// src/hooks.ts
|
|
33
|
+
function useRagbitsCall(endpoint, defaultOptions) {
|
|
34
|
+
const { client } = useRagbitsContext();
|
|
35
|
+
const [data, setData] = useState(null);
|
|
36
|
+
const [error, setError] = useState(null);
|
|
37
|
+
const [isLoading, setIsLoading] = useState(false);
|
|
38
|
+
const abortControllerRef = useRef(null);
|
|
39
|
+
const abort = useCallback(() => {
|
|
40
|
+
if (!abortControllerRef.current) {
|
|
41
|
+
return null;
|
|
42
|
+
}
|
|
43
|
+
abortControllerRef.current.abort();
|
|
44
|
+
abortControllerRef.current = null;
|
|
45
|
+
setIsLoading(false);
|
|
46
|
+
}, []);
|
|
47
|
+
const call = useCallback(
|
|
48
|
+
async (options = {}) => {
|
|
49
|
+
if (abortControllerRef.current && isLoading) {
|
|
50
|
+
abortControllerRef.current.abort();
|
|
51
|
+
}
|
|
52
|
+
const abortController = new AbortController();
|
|
53
|
+
abortControllerRef.current = abortController;
|
|
54
|
+
setIsLoading(true);
|
|
55
|
+
setError(null);
|
|
56
|
+
try {
|
|
57
|
+
const mergedOptions = {
|
|
58
|
+
...defaultOptions,
|
|
59
|
+
...options,
|
|
60
|
+
headers: {
|
|
61
|
+
...defaultOptions?.headers,
|
|
62
|
+
...options.headers
|
|
63
|
+
}
|
|
64
|
+
};
|
|
65
|
+
const requestOptions = {
|
|
66
|
+
...mergedOptions,
|
|
67
|
+
signal: abortController.signal
|
|
68
|
+
};
|
|
69
|
+
const result = await client.makeRequest(
|
|
70
|
+
endpoint,
|
|
71
|
+
requestOptions
|
|
72
|
+
);
|
|
73
|
+
if (!abortController.signal.aborted) {
|
|
74
|
+
setData(result);
|
|
75
|
+
abortControllerRef.current = null;
|
|
76
|
+
}
|
|
77
|
+
return result;
|
|
78
|
+
} catch (err) {
|
|
79
|
+
if (!abortController.signal.aborted) {
|
|
80
|
+
const error2 = err instanceof Error ? err : new Error("API call failed");
|
|
81
|
+
setError(error2);
|
|
82
|
+
abortControllerRef.current = null;
|
|
83
|
+
throw error2;
|
|
84
|
+
}
|
|
85
|
+
throw err;
|
|
86
|
+
} finally {
|
|
87
|
+
if (!abortController.signal.aborted) {
|
|
88
|
+
setIsLoading(false);
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
},
|
|
92
|
+
[client, endpoint, defaultOptions, isLoading]
|
|
93
|
+
);
|
|
94
|
+
const reset = useCallback(() => {
|
|
95
|
+
abort();
|
|
96
|
+
setData(null);
|
|
97
|
+
setError(null);
|
|
98
|
+
setIsLoading(false);
|
|
99
|
+
}, [abort]);
|
|
100
|
+
return {
|
|
101
|
+
data,
|
|
102
|
+
error,
|
|
103
|
+
isLoading,
|
|
104
|
+
call,
|
|
105
|
+
reset,
|
|
106
|
+
abort
|
|
107
|
+
};
|
|
108
|
+
}
|
|
109
|
+
function useRagbitsStream(endpoint) {
|
|
110
|
+
const { client } = useRagbitsContext();
|
|
111
|
+
const [isStreaming, setIsStreaming] = useState(false);
|
|
112
|
+
const [error, setError] = useState(null);
|
|
113
|
+
const abortControllerRef = useRef(null);
|
|
114
|
+
const cancel = useCallback(() => {
|
|
115
|
+
if (!abortControllerRef.current) {
|
|
116
|
+
return null;
|
|
117
|
+
}
|
|
118
|
+
abortControllerRef.current.abort();
|
|
119
|
+
abortControllerRef.current = null;
|
|
120
|
+
setIsStreaming(false);
|
|
121
|
+
}, []);
|
|
122
|
+
const stream = useCallback(
|
|
123
|
+
(data, callbacks) => {
|
|
124
|
+
if (abortControllerRef.current && isStreaming) {
|
|
125
|
+
abortControllerRef.current.abort();
|
|
126
|
+
}
|
|
127
|
+
const abortController = new AbortController();
|
|
128
|
+
abortControllerRef.current = abortController;
|
|
129
|
+
setError(null);
|
|
130
|
+
setIsStreaming(true);
|
|
131
|
+
const cancelFn = client.makeStreamRequest(
|
|
132
|
+
endpoint,
|
|
133
|
+
data,
|
|
134
|
+
{
|
|
135
|
+
onMessage: callbacks.onMessage,
|
|
136
|
+
onError: (err) => {
|
|
137
|
+
if (!abortController.signal.aborted) {
|
|
138
|
+
setError(err);
|
|
139
|
+
setIsStreaming(false);
|
|
140
|
+
callbacks.onError(err.message);
|
|
141
|
+
}
|
|
142
|
+
},
|
|
143
|
+
onClose: () => {
|
|
144
|
+
if (!abortController.signal.aborted) {
|
|
145
|
+
setIsStreaming(false);
|
|
146
|
+
if (callbacks.onClose) {
|
|
147
|
+
callbacks.onClose();
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
},
|
|
152
|
+
abortController.signal
|
|
153
|
+
);
|
|
154
|
+
return () => {
|
|
155
|
+
cancel();
|
|
156
|
+
cancelFn();
|
|
157
|
+
};
|
|
158
|
+
},
|
|
159
|
+
[client, cancel, endpoint, isStreaming]
|
|
160
|
+
);
|
|
161
|
+
return {
|
|
162
|
+
isStreaming,
|
|
163
|
+
error,
|
|
164
|
+
stream,
|
|
165
|
+
cancel
|
|
166
|
+
};
|
|
167
|
+
}
|
|
168
|
+
export {
|
|
169
|
+
RagbitsProvider,
|
|
170
|
+
useRagbitsCall,
|
|
171
|
+
useRagbitsContext,
|
|
172
|
+
useRagbitsStream
|
|
173
|
+
};
|
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import type { StreamCallbacks, RagbitsClient, StreamingEndpointPath, StreamingEndpointRequest, TypedApiRequestOptions, ApiEndpointPath, StreamingEndpointStream } from '@ragbits/api-client';
|
|
2
|
+
export interface RagbitsCallResult<T, E = Error, TEndpoint extends ApiEndpointPath = ApiEndpointPath> {
|
|
3
|
+
data: T | null;
|
|
4
|
+
error: E | null;
|
|
5
|
+
isLoading: boolean;
|
|
6
|
+
call: (options?: TypedApiRequestOptions<TEndpoint>) => Promise<T>;
|
|
7
|
+
reset: () => void;
|
|
8
|
+
abort: () => void;
|
|
9
|
+
}
|
|
10
|
+
export interface RagbitsStreamResult<E = Error, TEndpoint extends StreamingEndpointPath = StreamingEndpointPath> {
|
|
11
|
+
isStreaming: boolean;
|
|
12
|
+
error: E | null;
|
|
13
|
+
stream: (data: StreamingEndpointRequest<TEndpoint>, callbacks: StreamCallbacks<StreamingEndpointStream<TEndpoint>, string>) => () => void;
|
|
14
|
+
cancel: () => void;
|
|
15
|
+
}
|
|
16
|
+
export interface RagbitsContextValue {
|
|
17
|
+
client: RagbitsClient;
|
|
18
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@ragbits/api-client-react",
|
|
3
|
+
"version": "0.0.3",
|
|
4
|
+
"description": "React hooks for the Ragbits API client",
|
|
5
|
+
"main": "dist/index.cjs",
|
|
6
|
+
"module": "dist/index.js",
|
|
7
|
+
"types": "dist/index.d.ts",
|
|
8
|
+
"type": "module",
|
|
9
|
+
"exports": {
|
|
10
|
+
".": {
|
|
11
|
+
"types": "./dist/index.d.ts",
|
|
12
|
+
"import": "./dist/index.js",
|
|
13
|
+
"require": "./dist/index.cjs"
|
|
14
|
+
}
|
|
15
|
+
},
|
|
16
|
+
"scripts": {
|
|
17
|
+
"build": "npm run clean && tsup src/index.ts --format cjs,esm && tsc --emitDeclarationOnly --declaration --declarationDir dist --project tsconfig.json",
|
|
18
|
+
"dev": "tsc -b . --watch",
|
|
19
|
+
"test": "vitest",
|
|
20
|
+
"test:run": "vitest run",
|
|
21
|
+
"test:coverage": "vitest run --coverage",
|
|
22
|
+
"lint": "eslint .",
|
|
23
|
+
"format": "prettier --write .",
|
|
24
|
+
"format:check": "prettier --check .",
|
|
25
|
+
"clean": "rm -rf ./dist"
|
|
26
|
+
},
|
|
27
|
+
"peerDependencies": {
|
|
28
|
+
"react": ">=16.8.0",
|
|
29
|
+
"react-dom": ">=16.8.0"
|
|
30
|
+
},
|
|
31
|
+
"dependencies": {
|
|
32
|
+
"@ragbits/api-client": "*"
|
|
33
|
+
},
|
|
34
|
+
"devDependencies": {
|
|
35
|
+
"@eslint/js": "^9.17.0",
|
|
36
|
+
"@testing-library/jest-dom": "^6.4.0",
|
|
37
|
+
"@testing-library/react": "^14.0.0",
|
|
38
|
+
"@testing-library/user-event": "^14.5.0",
|
|
39
|
+
"@types/react": "^18.2.0",
|
|
40
|
+
"@types/react-dom": "^18.2.0",
|
|
41
|
+
"@vitest/coverage-v8": "^1.6.0",
|
|
42
|
+
"eslint": "^9.17.0",
|
|
43
|
+
"eslint-plugin-react-hooks": "^5.0.0",
|
|
44
|
+
"eslint-plugin-react-refresh": "^0.4.16",
|
|
45
|
+
"globals": "^15.14.0",
|
|
46
|
+
"jsdom": "^23.0.0",
|
|
47
|
+
"msw": "^2.0.0",
|
|
48
|
+
"prettier": "^3.4.2",
|
|
49
|
+
"tsup": "^8.0.0",
|
|
50
|
+
"typescript": "^5.0.0",
|
|
51
|
+
"typescript-eslint": "^8.18.2",
|
|
52
|
+
"vitest": "^1.6.0"
|
|
53
|
+
},
|
|
54
|
+
"keywords": [
|
|
55
|
+
"ragbits",
|
|
56
|
+
"api",
|
|
57
|
+
"client",
|
|
58
|
+
"react"
|
|
59
|
+
],
|
|
60
|
+
"author": "deepsense.ai",
|
|
61
|
+
"license": "MIT"
|
|
62
|
+
}
|