@agentuity/react 0.0.69 → 0.0.70
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/dist/api.d.ts +196 -0
- package/dist/api.d.ts.map +1 -0
- package/dist/api.js +325 -0
- package/dist/api.js.map +1 -0
- package/dist/env.d.ts.map +1 -1
- package/dist/env.js +3 -0
- package/dist/env.js.map +1 -1
- package/dist/eventstream.d.ts +52 -13
- package/dist/eventstream.d.ts.map +1 -1
- package/dist/eventstream.js +45 -13
- package/dist/eventstream.js.map +1 -1
- package/dist/index.d.ts +2 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -1
- package/dist/index.js.map +1 -1
- package/dist/memo.d.ts +7 -0
- package/dist/memo.d.ts.map +1 -0
- package/dist/memo.js +32 -0
- package/dist/memo.js.map +1 -0
- package/dist/serialization.d.ts.map +1 -1
- package/dist/serialization.js +5 -7
- package/dist/serialization.js.map +1 -1
- package/dist/types.d.ts +10 -25
- package/dist/types.d.ts.map +1 -1
- package/dist/url.d.ts.map +1 -1
- package/dist/url.js +6 -0
- package/dist/url.js.map +1 -1
- package/dist/websocket.d.ts +60 -13
- package/dist/websocket.d.ts.map +1 -1
- package/dist/websocket.js +47 -13
- package/dist/websocket.js.map +1 -1
- package/package.json +3 -2
- package/src/api.ts +529 -0
- package/src/env.ts +3 -0
- package/src/eventstream.ts +87 -40
- package/src/index.ts +2 -1
- package/src/memo.ts +32 -0
- package/src/serialization.ts +4 -6
- package/src/types.ts +19 -29
- package/src/url.ts +7 -0
- package/src/websocket.ts +109 -43
- package/dist/run.d.ts +0 -34
- package/dist/run.d.ts.map +0 -1
- package/dist/run.js +0 -68
- package/dist/run.js.map +0 -1
- package/src/run.ts +0 -119
package/src/eventstream.ts
CHANGED
|
@@ -2,19 +2,39 @@ import { useCallback, useContext, useEffect, useMemo, useRef, useState } from 'r
|
|
|
2
2
|
import type { InferOutput } from '@agentuity/core';
|
|
3
3
|
import { AgentuityContext } from './context';
|
|
4
4
|
import { buildUrl } from './url';
|
|
5
|
-
import type { AgentName, AgentRegistry } from './types';
|
|
6
5
|
import { deserializeData } from './serialization';
|
|
7
6
|
import { createReconnectManager } from './reconnect';
|
|
7
|
+
import { jsonEqual } from './memo';
|
|
8
|
+
import type { SSERouteRegistry } from './types';
|
|
8
9
|
|
|
9
10
|
type onMessageHandler<T = unknown> = (data: T) => void;
|
|
10
11
|
|
|
11
|
-
|
|
12
|
+
/**
|
|
13
|
+
* Extract SSE route keys (e.g., '/events', '/notifications')
|
|
14
|
+
*/
|
|
15
|
+
export type SSERouteKey = keyof SSERouteRegistry;
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Extract output type for an SSE route (SSE is typically one-way server->client)
|
|
19
|
+
*/
|
|
20
|
+
export type SSERouteOutput<TRoute extends SSERouteKey> = TRoute extends keyof SSERouteRegistry
|
|
21
|
+
? SSERouteRegistry[TRoute] extends { outputSchema: infer TSchema }
|
|
22
|
+
? TSchema extends undefined | never
|
|
23
|
+
? void
|
|
24
|
+
: InferOutput<TSchema>
|
|
25
|
+
: void
|
|
26
|
+
: void;
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Options for EventStream hooks
|
|
30
|
+
*/
|
|
31
|
+
export interface EventStreamOptions {
|
|
12
32
|
/**
|
|
13
33
|
* Optional query parameters to append to the EventStream URL
|
|
14
34
|
*/
|
|
15
35
|
query?: URLSearchParams;
|
|
16
36
|
/**
|
|
17
|
-
* Optional subpath to append to the
|
|
37
|
+
* Optional subpath to append to the EventStream path
|
|
18
38
|
*/
|
|
19
39
|
subpath?: string;
|
|
20
40
|
/**
|
|
@@ -23,20 +43,29 @@ interface EventStreamArgs {
|
|
|
23
43
|
signal?: AbortSignal;
|
|
24
44
|
}
|
|
25
45
|
|
|
26
|
-
interface
|
|
27
|
-
connected
|
|
46
|
+
interface EventStreamResponseInternal<TOutput> {
|
|
47
|
+
/** Whether EventStream is currently connected */
|
|
48
|
+
isConnected: boolean;
|
|
49
|
+
/** Most recent data received from EventStream */
|
|
28
50
|
data?: TOutput;
|
|
51
|
+
/** Error if connection or message failed */
|
|
29
52
|
error: Error | null;
|
|
53
|
+
/** Whether an error has occurred */
|
|
54
|
+
isError: boolean;
|
|
55
|
+
/** Set handler for incoming messages (use data property instead) */
|
|
30
56
|
setHandler: (handler: onMessageHandler<TOutput>) => void;
|
|
57
|
+
/** EventStream connection state (CONNECTING=0, OPEN=1, CLOSED=2) */
|
|
31
58
|
readyState: number;
|
|
59
|
+
/** Close the EventStream connection */
|
|
32
60
|
close: () => void;
|
|
61
|
+
/** Reset state to initial values */
|
|
33
62
|
reset: () => void;
|
|
34
63
|
}
|
|
35
64
|
|
|
36
|
-
|
|
65
|
+
const useEventStreamInternal = <TOutput>(
|
|
37
66
|
path: string,
|
|
38
|
-
options?:
|
|
39
|
-
):
|
|
67
|
+
options?: EventStreamOptions
|
|
68
|
+
): EventStreamResponseInternal<TOutput> => {
|
|
40
69
|
const context = useContext(AgentuityContext);
|
|
41
70
|
|
|
42
71
|
if (!context) {
|
|
@@ -53,7 +82,8 @@ export const useEventStream = <TOutput>(
|
|
|
53
82
|
|
|
54
83
|
const [data, setData] = useState<TOutput>();
|
|
55
84
|
const [error, setError] = useState<Error | null>(null);
|
|
56
|
-
const [
|
|
85
|
+
const [isError, setIsError] = useState(false);
|
|
86
|
+
const [isConnected, setIsConnected] = useState(false);
|
|
57
87
|
|
|
58
88
|
const esUrl = useMemo(
|
|
59
89
|
() => buildUrl(context.baseUrl!, path, options?.subpath, options?.query),
|
|
@@ -68,13 +98,15 @@ export const useEventStream = <TOutput>(
|
|
|
68
98
|
|
|
69
99
|
esRef.current.onopen = () => {
|
|
70
100
|
reconnectManagerRef.current?.recordSuccess();
|
|
71
|
-
|
|
101
|
+
setIsConnected(true);
|
|
72
102
|
setError(null);
|
|
103
|
+
setIsError(false);
|
|
73
104
|
};
|
|
74
105
|
|
|
75
106
|
esRef.current.onerror = () => {
|
|
76
107
|
setError(new Error('EventStream error'));
|
|
77
|
-
|
|
108
|
+
setIsError(true);
|
|
109
|
+
setIsConnected(false);
|
|
78
110
|
|
|
79
111
|
if (manualClose.current) {
|
|
80
112
|
return;
|
|
@@ -99,7 +131,8 @@ export const useEventStream = <TOutput>(
|
|
|
99
131
|
firstMessageReceived = true;
|
|
100
132
|
}
|
|
101
133
|
const payload = deserializeData<TOutput>(event.data);
|
|
102
|
-
|
|
134
|
+
// Use JSON memoization to prevent re-renders when data hasn't changed
|
|
135
|
+
setData((prev) => (prev !== undefined && jsonEqual(prev, payload) ? prev : payload));
|
|
103
136
|
if (handler.current) {
|
|
104
137
|
handler.current(payload);
|
|
105
138
|
} else {
|
|
@@ -134,7 +167,7 @@ export const useEventStream = <TOutput>(
|
|
|
134
167
|
esRef.current = undefined;
|
|
135
168
|
handler.current = undefined;
|
|
136
169
|
pending.current = [];
|
|
137
|
-
|
|
170
|
+
setIsConnected(false);
|
|
138
171
|
}, []);
|
|
139
172
|
|
|
140
173
|
useEffect(() => {
|
|
@@ -158,7 +191,10 @@ export const useEventStream = <TOutput>(
|
|
|
158
191
|
}
|
|
159
192
|
}, [options?.signal, cleanup]);
|
|
160
193
|
|
|
161
|
-
const reset = () =>
|
|
194
|
+
const reset = () => {
|
|
195
|
+
setError(null);
|
|
196
|
+
setIsError(false);
|
|
197
|
+
};
|
|
162
198
|
|
|
163
199
|
const setHandler = useCallback((h: onMessageHandler<TOutput>) => {
|
|
164
200
|
handler.current = h;
|
|
@@ -171,49 +207,60 @@ export const useEventStream = <TOutput>(
|
|
|
171
207
|
};
|
|
172
208
|
|
|
173
209
|
return {
|
|
174
|
-
|
|
210
|
+
isConnected,
|
|
175
211
|
close,
|
|
176
212
|
data,
|
|
177
213
|
error,
|
|
214
|
+
isError,
|
|
178
215
|
setHandler,
|
|
179
216
|
reset,
|
|
180
217
|
readyState: esRef.current?.readyState ?? EventSource.CLOSED,
|
|
181
218
|
};
|
|
182
219
|
};
|
|
183
220
|
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
221
|
+
/**
|
|
222
|
+
* Type-safe EventStream (SSE) hook for connecting to SSE routes.
|
|
223
|
+
*
|
|
224
|
+
* Provides automatic type inference for route outputs based on
|
|
225
|
+
* the SSERouteRegistry generated from your routes.
|
|
226
|
+
*
|
|
227
|
+
* @template TRoute - SSE route key from SSERouteRegistry (e.g., '/events', '/notifications')
|
|
228
|
+
*
|
|
229
|
+
* @example Simple SSE connection
|
|
230
|
+
* ```typescript
|
|
231
|
+
* const { isConnected, data } = useEventStream('/events');
|
|
232
|
+
*
|
|
233
|
+
* // data is fully typed based on route output schema!
|
|
234
|
+
* ```
|
|
235
|
+
*
|
|
236
|
+
* @example SSE with query parameters
|
|
237
|
+
* ```typescript
|
|
238
|
+
* const { isConnected, data } = useEventStream('/notifications', {
|
|
239
|
+
* query: new URLSearchParams({ userId: '123' })
|
|
240
|
+
* });
|
|
241
|
+
* ```
|
|
242
|
+
*/
|
|
243
|
+
export function useEventStream<TRoute extends SSERouteKey>(
|
|
244
|
+
route: TRoute,
|
|
245
|
+
options?: EventStreamOptions
|
|
246
|
+
): Omit<EventStreamResponseInternal<SSERouteOutput<TRoute>>, 'setHandler'> & {
|
|
247
|
+
data?: SSERouteOutput<TRoute>;
|
|
248
|
+
} {
|
|
249
|
+
const [data, setData] = useState<SSERouteOutput<TRoute>>();
|
|
250
|
+
const { isConnected, close, setHandler, readyState, error, isError, reset } =
|
|
251
|
+
useEventStreamInternal<SSERouteOutput<TRoute>>(route as string, options);
|
|
206
252
|
|
|
207
253
|
useEffect(() => {
|
|
208
254
|
setHandler(setData);
|
|
209
|
-
}, [
|
|
255
|
+
}, [route, setHandler]);
|
|
210
256
|
|
|
211
257
|
return {
|
|
212
|
-
|
|
258
|
+
isConnected,
|
|
213
259
|
close,
|
|
214
260
|
data,
|
|
215
261
|
error,
|
|
262
|
+
isError,
|
|
216
263
|
reset,
|
|
217
264
|
readyState,
|
|
218
265
|
};
|
|
219
|
-
}
|
|
266
|
+
}
|
package/src/index.ts
CHANGED
package/src/memo.ts
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Simple JSON-based equality check for memoization.
|
|
3
|
+
* Compares stringified JSON to avoid deep equality overhead.
|
|
4
|
+
*/
|
|
5
|
+
export function jsonEqual<T>(a: T, b: T): boolean {
|
|
6
|
+
if (a === b) return true;
|
|
7
|
+
if (a === undefined || b === undefined) return false;
|
|
8
|
+
if (a === null || b === null) return a === b;
|
|
9
|
+
|
|
10
|
+
try {
|
|
11
|
+
return JSON.stringify(a) === JSON.stringify(b);
|
|
12
|
+
} catch {
|
|
13
|
+
// Fallback for non-serializable values
|
|
14
|
+
return false;
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Hook to memoize a value based on JSON equality instead of reference equality.
|
|
20
|
+
* Prevents unnecessary re-renders when data hasn't actually changed.
|
|
21
|
+
*/
|
|
22
|
+
import { useRef } from 'react';
|
|
23
|
+
|
|
24
|
+
export function useJsonMemo<T>(value: T): T {
|
|
25
|
+
const ref = useRef<T>(value);
|
|
26
|
+
|
|
27
|
+
if (!jsonEqual(ref.current, value)) {
|
|
28
|
+
ref.current = value;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
return ref.current;
|
|
32
|
+
}
|
package/src/serialization.ts
CHANGED
|
@@ -4,12 +4,10 @@
|
|
|
4
4
|
*/
|
|
5
5
|
export const deserializeData = <T>(data: string): T => {
|
|
6
6
|
if (data) {
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
console.error('error parsing data as JSON', ex, data);
|
|
12
|
-
}
|
|
7
|
+
try {
|
|
8
|
+
return JSON.parse(data) as T;
|
|
9
|
+
} catch {
|
|
10
|
+
/* */
|
|
13
11
|
}
|
|
14
12
|
}
|
|
15
13
|
return data as T;
|
package/src/types.ts
CHANGED
|
@@ -1,39 +1,29 @@
|
|
|
1
|
-
import type { StandardSchemaV1 } from '@agentuity/core';
|
|
2
|
-
|
|
3
1
|
/**
|
|
4
|
-
*
|
|
2
|
+
* Route registry containing all typed API routes in the application.
|
|
3
|
+
* Auto-generated by the build tool from routes that use validator() middleware.
|
|
5
4
|
*/
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
inputSchema: TInput;
|
|
11
|
-
outputSchema: TOutput;
|
|
5
|
+
// eslint-disable-next-line @typescript-eslint/no-empty-object-type
|
|
6
|
+
export interface RouteRegistry {
|
|
7
|
+
// Will be augmented by generated code
|
|
8
|
+
// Format: 'METHOD /path': { inputSchema: ..., outputSchema: ... }
|
|
12
9
|
}
|
|
13
10
|
|
|
14
11
|
/**
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
-
* Example usage in generated code (.agentuity/types.d.ts):
|
|
19
|
-
* ```typescript
|
|
20
|
-
* import type { Agent } from '@agentuity/react';
|
|
21
|
-
* import type { MyInputSchema, MyOutputSchema } from './schemas';
|
|
22
|
-
*
|
|
23
|
-
* declare module '@agentuity/react' {
|
|
24
|
-
* interface AgentRegistry {
|
|
25
|
-
* 'my-agent': Agent<MyInputSchema, MyOutputSchema>;
|
|
26
|
-
* 'another-agent': Agent<AnotherInput, AnotherOutput>;
|
|
27
|
-
* }
|
|
28
|
-
* }
|
|
29
|
-
* ```
|
|
12
|
+
* WebSocket route registry containing all typed WebSocket routes in the application.
|
|
13
|
+
* Auto-generated by the build tool from routes that use validator() middleware.
|
|
30
14
|
*/
|
|
31
15
|
// eslint-disable-next-line @typescript-eslint/no-empty-object-type
|
|
32
|
-
export interface
|
|
16
|
+
export interface WebSocketRouteRegistry {
|
|
17
|
+
// Will be augmented by generated code
|
|
18
|
+
// Format: '/path': { inputSchema: ..., outputSchema: ... }
|
|
19
|
+
}
|
|
33
20
|
|
|
34
21
|
/**
|
|
35
|
-
*
|
|
36
|
-
*
|
|
37
|
-
* After augmentation, this becomes a strict union of agent names for full type safety.
|
|
22
|
+
* SSE route registry containing all typed SSE routes in the application.
|
|
23
|
+
* Auto-generated by the build tool from routes that use validator() middleware.
|
|
38
24
|
*/
|
|
39
|
-
|
|
25
|
+
// eslint-disable-next-line @typescript-eslint/no-empty-object-type
|
|
26
|
+
export interface SSERouteRegistry {
|
|
27
|
+
// Will be augmented by generated code
|
|
28
|
+
// Format: '/path': { inputSchema: ..., outputSchema: ... }
|
|
29
|
+
}
|
package/src/url.ts
CHANGED
|
@@ -18,8 +18,15 @@ export const buildUrl = (
|
|
|
18
18
|
return url;
|
|
19
19
|
};
|
|
20
20
|
|
|
21
|
+
const tryOrigin = () => {
|
|
22
|
+
if (typeof window !== 'undefined') {
|
|
23
|
+
return window.location.origin;
|
|
24
|
+
}
|
|
25
|
+
};
|
|
26
|
+
|
|
21
27
|
export const defaultBaseUrl: string =
|
|
22
28
|
getProcessEnv('NEXT_PUBLIC_AGENTUITY_URL') ||
|
|
23
29
|
getProcessEnv('VITE_AGENTUITY_URL') ||
|
|
24
30
|
getProcessEnv('AGENTUITY_URL') ||
|
|
31
|
+
tryOrigin() ||
|
|
25
32
|
'http://localhost:3500';
|
package/src/websocket.ts
CHANGED
|
@@ -2,19 +2,52 @@ import { useCallback, useContext, useEffect, useMemo, useRef, useState } from 'r
|
|
|
2
2
|
import type { InferInput, InferOutput } from '@agentuity/core';
|
|
3
3
|
import { AgentuityContext } from './context';
|
|
4
4
|
import { buildUrl } from './url';
|
|
5
|
-
import type { AgentName, AgentRegistry } from './types';
|
|
6
5
|
import { deserializeData } from './serialization';
|
|
7
6
|
import { createReconnectManager } from './reconnect';
|
|
7
|
+
import { jsonEqual } from './memo';
|
|
8
|
+
import type { WebSocketRouteRegistry } from './types';
|
|
8
9
|
|
|
9
10
|
type onMessageHandler<T = unknown> = (data: T) => void;
|
|
10
11
|
|
|
11
|
-
|
|
12
|
+
/**
|
|
13
|
+
* Extract WebSocket route keys (e.g., '/ws', '/chat')
|
|
14
|
+
*/
|
|
15
|
+
export type WebSocketRouteKey = keyof WebSocketRouteRegistry;
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Extract input type for a WebSocket route
|
|
19
|
+
*/
|
|
20
|
+
export type WebSocketRouteInput<TRoute extends WebSocketRouteKey> =
|
|
21
|
+
TRoute extends keyof WebSocketRouteRegistry
|
|
22
|
+
? WebSocketRouteRegistry[TRoute] extends { inputSchema: infer TSchema }
|
|
23
|
+
? TSchema extends undefined | never
|
|
24
|
+
? never
|
|
25
|
+
: InferInput<TSchema>
|
|
26
|
+
: never
|
|
27
|
+
: never;
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Extract output type for a WebSocket route
|
|
31
|
+
*/
|
|
32
|
+
export type WebSocketRouteOutput<TRoute extends WebSocketRouteKey> =
|
|
33
|
+
TRoute extends keyof WebSocketRouteRegistry
|
|
34
|
+
? WebSocketRouteRegistry[TRoute] extends { outputSchema: infer TSchema }
|
|
35
|
+
? TSchema extends undefined | never
|
|
36
|
+
? void
|
|
37
|
+
: InferOutput<TSchema>
|
|
38
|
+
: void
|
|
39
|
+
: void;
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Options for WebSocket hooks
|
|
43
|
+
*/
|
|
44
|
+
export interface WebsocketOptions {
|
|
12
45
|
/**
|
|
13
46
|
* Optional query parameters to append to the websocket URL
|
|
14
47
|
*/
|
|
15
48
|
query?: URLSearchParams;
|
|
16
49
|
/**
|
|
17
|
-
* Optional subpath to append to the
|
|
50
|
+
* Optional subpath to append to the websocket path
|
|
18
51
|
*/
|
|
19
52
|
subpath?: string;
|
|
20
53
|
/**
|
|
@@ -38,21 +71,31 @@ const serializeWSData = (
|
|
|
38
71
|
throw new Error('unsupported data type for websocket: ' + typeof data);
|
|
39
72
|
};
|
|
40
73
|
|
|
41
|
-
interface
|
|
42
|
-
connected
|
|
74
|
+
interface WebsocketResponseInternal<TInput, TOutput> {
|
|
75
|
+
/** Whether WebSocket is currently connected */
|
|
76
|
+
isConnected: boolean;
|
|
77
|
+
/** Most recent data received from WebSocket */
|
|
43
78
|
data?: TOutput;
|
|
79
|
+
/** Error if connection or message failed */
|
|
44
80
|
error: Error | null;
|
|
81
|
+
/** Whether an error has occurred */
|
|
82
|
+
isError: boolean;
|
|
83
|
+
/** Send data through the WebSocket */
|
|
45
84
|
send: (data: TInput) => void;
|
|
85
|
+
/** Set handler for incoming messages (use data property instead) */
|
|
46
86
|
setHandler: (handler: onMessageHandler<TOutput>) => void;
|
|
87
|
+
/** WebSocket connection state (CONNECTING=0, OPEN=1, CLOSING=2, CLOSED=3) */
|
|
47
88
|
readyState: WebSocket['readyState'];
|
|
89
|
+
/** Close the WebSocket connection */
|
|
48
90
|
close: () => void;
|
|
91
|
+
/** Reset state to initial values */
|
|
49
92
|
reset: () => void;
|
|
50
93
|
}
|
|
51
94
|
|
|
52
|
-
|
|
95
|
+
const useWebsocketInternal = <TInput, TOutput>(
|
|
53
96
|
path: string,
|
|
54
|
-
options?:
|
|
55
|
-
):
|
|
97
|
+
options?: WebsocketOptions
|
|
98
|
+
): WebsocketResponseInternal<TInput, TOutput> => {
|
|
56
99
|
const context = useContext(AgentuityContext);
|
|
57
100
|
|
|
58
101
|
if (!context) {
|
|
@@ -70,7 +113,8 @@ export const useWebsocket = <TInput, TOutput>(
|
|
|
70
113
|
|
|
71
114
|
const [data, setData] = useState<TOutput>();
|
|
72
115
|
const [error, setError] = useState<Error | null>(null);
|
|
73
|
-
const [
|
|
116
|
+
const [isError, setIsError] = useState(false);
|
|
117
|
+
const [isConnected, setIsConnected] = useState(false);
|
|
74
118
|
|
|
75
119
|
const wsUrl = useMemo(() => {
|
|
76
120
|
const base = context.baseUrl!;
|
|
@@ -85,8 +129,9 @@ export const useWebsocket = <TInput, TOutput>(
|
|
|
85
129
|
|
|
86
130
|
wsRef.current.onopen = () => {
|
|
87
131
|
reconnectManagerRef.current?.recordSuccess();
|
|
88
|
-
|
|
132
|
+
setIsConnected(true);
|
|
89
133
|
setError(null);
|
|
134
|
+
setIsError(false);
|
|
90
135
|
if (queued.current.length > 0) {
|
|
91
136
|
queued.current.forEach((msg: unknown) => wsRef.current!.send(serializeWSData(msg)));
|
|
92
137
|
queued.current = [];
|
|
@@ -95,24 +140,27 @@ export const useWebsocket = <TInput, TOutput>(
|
|
|
95
140
|
|
|
96
141
|
wsRef.current.onerror = () => {
|
|
97
142
|
setError(new Error('WebSocket error'));
|
|
143
|
+
setIsError(true);
|
|
98
144
|
};
|
|
99
145
|
|
|
100
146
|
wsRef.current.onclose = (evt) => {
|
|
101
147
|
wsRef.current = undefined;
|
|
102
|
-
|
|
148
|
+
setIsConnected(false);
|
|
103
149
|
if (manualClose.current) {
|
|
104
150
|
queued.current = [];
|
|
105
151
|
return;
|
|
106
152
|
}
|
|
107
153
|
if (evt.code !== 1000) {
|
|
108
154
|
setError(new Error(`WebSocket closed: ${evt.code} ${evt.reason || ''}`));
|
|
155
|
+
setIsError(true);
|
|
109
156
|
}
|
|
110
157
|
reconnectManagerRef.current?.recordFailure();
|
|
111
158
|
};
|
|
112
159
|
|
|
113
160
|
wsRef.current.onmessage = (event: { data: string }) => {
|
|
114
161
|
const payload = deserializeData<TOutput>(event.data);
|
|
115
|
-
|
|
162
|
+
// Use JSON memoization to prevent re-renders when data hasn't changed
|
|
163
|
+
setData((prev) => (prev !== undefined && jsonEqual(prev, payload) ? prev : payload));
|
|
116
164
|
if (handler.current) {
|
|
117
165
|
handler.current(payload);
|
|
118
166
|
} else {
|
|
@@ -149,7 +197,7 @@ export const useWebsocket = <TInput, TOutput>(
|
|
|
149
197
|
handler.current = undefined;
|
|
150
198
|
pending.current = [];
|
|
151
199
|
queued.current = [];
|
|
152
|
-
|
|
200
|
+
setIsConnected(false);
|
|
153
201
|
}, []);
|
|
154
202
|
|
|
155
203
|
useEffect(() => {
|
|
@@ -173,7 +221,10 @@ export const useWebsocket = <TInput, TOutput>(
|
|
|
173
221
|
}
|
|
174
222
|
}, [options?.signal, cleanup]);
|
|
175
223
|
|
|
176
|
-
const reset = () =>
|
|
224
|
+
const reset = () => {
|
|
225
|
+
setError(null);
|
|
226
|
+
setIsError(false);
|
|
227
|
+
};
|
|
177
228
|
|
|
178
229
|
const send = (data: TInput) => {
|
|
179
230
|
if (wsRef.current?.readyState === WebSocket.OPEN) {
|
|
@@ -194,10 +245,11 @@ export const useWebsocket = <TInput, TOutput>(
|
|
|
194
245
|
};
|
|
195
246
|
|
|
196
247
|
return {
|
|
197
|
-
|
|
248
|
+
isConnected,
|
|
198
249
|
close,
|
|
199
250
|
data,
|
|
200
251
|
error,
|
|
252
|
+
isError,
|
|
201
253
|
send,
|
|
202
254
|
setHandler,
|
|
203
255
|
reset,
|
|
@@ -205,43 +257,57 @@ export const useWebsocket = <TInput, TOutput>(
|
|
|
205
257
|
};
|
|
206
258
|
};
|
|
207
259
|
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
260
|
+
/**
|
|
261
|
+
* Type-safe WebSocket hook for connecting to WebSocket routes.
|
|
262
|
+
*
|
|
263
|
+
* Provides automatic type inference for route inputs and outputs based on
|
|
264
|
+
* the WebSocketRouteRegistry generated from your routes.
|
|
265
|
+
*
|
|
266
|
+
* @template TRoute - WebSocket route key from WebSocketRouteRegistry (e.g., '/ws', '/chat')
|
|
267
|
+
*
|
|
268
|
+
* @example Simple WebSocket connection
|
|
269
|
+
* ```typescript
|
|
270
|
+
* const { isConnected, data, send } = useWebsocket('/ws');
|
|
271
|
+
*
|
|
272
|
+
* // Send typed data
|
|
273
|
+
* send({ message: 'Hello' }); // Fully typed based on route schema!
|
|
274
|
+
* ```
|
|
275
|
+
*
|
|
276
|
+
* @example WebSocket with query parameters
|
|
277
|
+
* ```typescript
|
|
278
|
+
* const { isConnected, data, send } = useWebsocket('/chat', {
|
|
279
|
+
* query: new URLSearchParams({ room: 'general' })
|
|
280
|
+
* });
|
|
281
|
+
* ```
|
|
282
|
+
*/
|
|
283
|
+
export function useWebsocket<TRoute extends WebSocketRouteKey>(
|
|
284
|
+
route: TRoute,
|
|
285
|
+
options?: WebsocketOptions
|
|
286
|
+
): Omit<
|
|
287
|
+
WebsocketResponseInternal<WebSocketRouteInput<TRoute>, WebSocketRouteOutput<TRoute>>,
|
|
288
|
+
'setHandler'
|
|
289
|
+
> & {
|
|
290
|
+
data?: WebSocketRouteOutput<TRoute>;
|
|
291
|
+
} {
|
|
292
|
+
const [data, setData] = useState<WebSocketRouteOutput<TRoute>>();
|
|
293
|
+
const { isConnected, close, send, setHandler, readyState, error, isError, reset } =
|
|
294
|
+
useWebsocketInternal<WebSocketRouteInput<TRoute>, WebSocketRouteOutput<TRoute>>(
|
|
295
|
+
route as string,
|
|
296
|
+
options
|
|
297
|
+
);
|
|
233
298
|
|
|
234
299
|
useEffect(() => {
|
|
235
300
|
setHandler(setData);
|
|
236
|
-
}, [
|
|
301
|
+
}, [route, setHandler]);
|
|
237
302
|
|
|
238
303
|
return {
|
|
239
|
-
|
|
304
|
+
isConnected,
|
|
240
305
|
close,
|
|
241
306
|
data,
|
|
242
307
|
error,
|
|
308
|
+
isError,
|
|
243
309
|
reset,
|
|
244
310
|
send,
|
|
245
311
|
readyState,
|
|
246
312
|
};
|
|
247
|
-
}
|
|
313
|
+
}
|
package/dist/run.d.ts
DELETED
|
@@ -1,34 +0,0 @@
|
|
|
1
|
-
import type { InferInput, InferOutput } from '@agentuity/core';
|
|
2
|
-
import type { AgentName, AgentRegistry } from './types';
|
|
3
|
-
interface RunArgs {
|
|
4
|
-
/**
|
|
5
|
-
* Optional query parameters to append to the URL
|
|
6
|
-
*/
|
|
7
|
-
query?: URLSearchParams;
|
|
8
|
-
/**
|
|
9
|
-
* Optional headers to send with the request
|
|
10
|
-
*/
|
|
11
|
-
headers?: Record<string, string>;
|
|
12
|
-
/**
|
|
13
|
-
* Optional subpath to append to the agent path (such as /agent/:agent_name/:subpath)
|
|
14
|
-
*/
|
|
15
|
-
subpath?: string;
|
|
16
|
-
/**
|
|
17
|
-
* HTTP method to use (default: POST)
|
|
18
|
-
*/
|
|
19
|
-
method?: string;
|
|
20
|
-
/**
|
|
21
|
-
* Optional AbortSignal to cancel the request
|
|
22
|
-
*/
|
|
23
|
-
signal?: AbortSignal;
|
|
24
|
-
}
|
|
25
|
-
interface UseAgentResponse<TInput, TOutput> {
|
|
26
|
-
data?: TOutput;
|
|
27
|
-
error: Error | null;
|
|
28
|
-
run: (input: TInput, options?: RunArgs) => Promise<TOutput>;
|
|
29
|
-
reset: () => void;
|
|
30
|
-
running: boolean;
|
|
31
|
-
}
|
|
32
|
-
export declare const useAgent: <TName extends AgentName, TInput = TName extends keyof AgentRegistry ? InferInput<AgentRegistry[TName]["inputSchema"]> : never, TOutput = TName extends keyof AgentRegistry ? InferOutput<AgentRegistry[TName]["outputSchema"]> : never>(name: TName) => UseAgentResponse<TInput, TOutput>;
|
|
33
|
-
export {};
|
|
34
|
-
//# sourceMappingURL=run.d.ts.map
|
package/dist/run.d.ts.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"run.d.ts","sourceRoot":"","sources":["../src/run.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAC;AAG/D,OAAO,KAAK,EAAE,SAAS,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AAExD,UAAU,OAAO;IAChB;;OAEG;IACH,KAAK,CAAC,EAAE,eAAe,CAAC;IACxB;;OAEG;IACH,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACjC;;OAEG;IACH,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB;;OAEG;IACH,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB;;OAEG;IACH,MAAM,CAAC,EAAE,WAAW,CAAC;CACrB;AAED,UAAU,gBAAgB,CAAC,MAAM,EAAE,OAAO;IACzC,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,KAAK,EAAE,KAAK,GAAG,IAAI,CAAC;IACpB,GAAG,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,OAAO,KAAK,OAAO,CAAC,OAAO,CAAC,CAAC;IAC5D,KAAK,EAAE,MAAM,IAAI,CAAC;IAClB,OAAO,EAAE,OAAO,CAAC;CACjB;AAED,eAAO,MAAM,QAAQ,GACpB,KAAK,SAAS,SAAS,EACvB,MAAM,GAAG,KAAK,SAAS,MAAM,aAAa,GACvC,UAAU,CAAC,aAAa,CAAC,KAAK,CAAC,CAAC,aAAa,CAAC,CAAC,GAC/C,KAAK,EACR,OAAO,GAAG,KAAK,SAAS,MAAM,aAAa,GACxC,WAAW,CAAC,aAAa,CAAC,KAAK,CAAC,CAAC,cAAc,CAAC,CAAC,GACjD,KAAK,EAER,MAAM,KAAK,KACT,gBAAgB,CAAC,MAAM,EAAE,OAAO,CAuElC,CAAC"}
|