@agentuity/frontend 0.0.100

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 (48) hide show
  1. package/AGENTS.md +80 -0
  2. package/README.md +65 -0
  3. package/dist/env.d.ts +2 -0
  4. package/dist/env.d.ts.map +1 -0
  5. package/dist/env.js +10 -0
  6. package/dist/env.js.map +1 -0
  7. package/dist/eventstream-manager.d.ts +85 -0
  8. package/dist/eventstream-manager.d.ts.map +1 -0
  9. package/dist/eventstream-manager.js +137 -0
  10. package/dist/eventstream-manager.js.map +1 -0
  11. package/dist/index.d.ts +9 -0
  12. package/dist/index.d.ts.map +1 -0
  13. package/dist/index.js +8 -0
  14. package/dist/index.js.map +1 -0
  15. package/dist/memo.d.ts +6 -0
  16. package/dist/memo.d.ts.map +1 -0
  17. package/dist/memo.js +20 -0
  18. package/dist/memo.js.map +1 -0
  19. package/dist/reconnect.d.ts +22 -0
  20. package/dist/reconnect.d.ts.map +1 -0
  21. package/dist/reconnect.js +47 -0
  22. package/dist/reconnect.js.map +1 -0
  23. package/dist/serialization.d.ts +6 -0
  24. package/dist/serialization.d.ts.map +1 -0
  25. package/dist/serialization.js +16 -0
  26. package/dist/serialization.js.map +1 -0
  27. package/dist/types.d.ts +19 -0
  28. package/dist/types.d.ts.map +1 -0
  29. package/dist/types.js +2 -0
  30. package/dist/types.js.map +1 -0
  31. package/dist/url.d.ts +3 -0
  32. package/dist/url.d.ts.map +1 -0
  33. package/dist/url.js +24 -0
  34. package/dist/url.js.map +1 -0
  35. package/dist/websocket-manager.d.ts +90 -0
  36. package/dist/websocket-manager.d.ts.map +1 -0
  37. package/dist/websocket-manager.js +163 -0
  38. package/dist/websocket-manager.js.map +1 -0
  39. package/package.json +39 -0
  40. package/src/env.ts +9 -0
  41. package/src/eventstream-manager.ts +203 -0
  42. package/src/index.ts +20 -0
  43. package/src/memo.ts +16 -0
  44. package/src/reconnect.ts +73 -0
  45. package/src/serialization.ts +14 -0
  46. package/src/types.ts +29 -0
  47. package/src/url.ts +32 -0
  48. package/src/websocket-manager.ts +234 -0
package/src/url.ts ADDED
@@ -0,0 +1,32 @@
1
+ import { getProcessEnv } from './env';
2
+
3
+ export const buildUrl = (
4
+ base: string,
5
+ path: string,
6
+ subpath?: string,
7
+ query?: URLSearchParams
8
+ ): string => {
9
+ path = path.startsWith('/') ? path : `/${path}`;
10
+ let url = base.replace(/\/$/, '') + path;
11
+ if (subpath) {
12
+ subpath = subpath.startsWith('/') ? subpath : `/${subpath}`;
13
+ url += `/${subpath}`;
14
+ }
15
+ if (query) {
16
+ url += `?${query.toString()}`;
17
+ }
18
+ return url;
19
+ };
20
+
21
+ const tryOrigin = () => {
22
+ if (typeof window !== 'undefined') {
23
+ return window.location.origin;
24
+ }
25
+ };
26
+
27
+ export const defaultBaseUrl: string =
28
+ getProcessEnv('NEXT_PUBLIC_AGENTUITY_URL') ||
29
+ getProcessEnv('VITE_AGENTUITY_URL') ||
30
+ getProcessEnv('AGENTUITY_URL') ||
31
+ tryOrigin() ||
32
+ 'http://localhost:3500';
@@ -0,0 +1,234 @@
1
+ import { createReconnectManager, type ReconnectManager } from './reconnect';
2
+ import { deserializeData } from './serialization';
3
+
4
+ /**
5
+ * Serialize data for WebSocket transmission
6
+ */
7
+ const serializeWSData = (
8
+ data: unknown
9
+ ): string | ArrayBufferLike | Blob | ArrayBufferView<ArrayBufferLike> => {
10
+ if (typeof data === 'string') {
11
+ return data;
12
+ }
13
+ if (typeof data === 'object') {
14
+ if (data instanceof ArrayBuffer || ArrayBuffer.isView(data) || data instanceof Blob) {
15
+ return data;
16
+ }
17
+ return JSON.stringify(data);
18
+ }
19
+ throw new Error('unsupported data type for websocket: ' + typeof data);
20
+ };
21
+
22
+ /**
23
+ * Message handler callback type
24
+ */
25
+ export type MessageHandler<T = unknown> = (data: T) => void;
26
+
27
+ /**
28
+ * WebSocket state change callback types
29
+ */
30
+ export interface WebSocketCallbacks<TOutput = unknown> {
31
+ /** Called when connection is established */
32
+ onConnect?: () => void;
33
+ /** Called when connection is closed */
34
+ onDisconnect?: () => void;
35
+ /** Called when an error occurs */
36
+ onError?: (error: Error) => void;
37
+ /** Called when a message is received */
38
+ onMessage?: MessageHandler<TOutput>;
39
+ }
40
+
41
+ /**
42
+ * Options for WebSocketManager
43
+ */
44
+ export interface WebSocketManagerOptions<TOutput = unknown> {
45
+ /** WebSocket URL */
46
+ url: string;
47
+ /** Callbacks for state changes */
48
+ callbacks?: WebSocketCallbacks<TOutput>;
49
+ /** Reconnection configuration */
50
+ reconnect?: {
51
+ threshold?: number;
52
+ baseDelay?: number;
53
+ factor?: number;
54
+ maxDelay?: number;
55
+ jitter?: number;
56
+ };
57
+ }
58
+
59
+ /**
60
+ * WebSocket manager state
61
+ */
62
+ export interface WebSocketManagerState {
63
+ /** Whether WebSocket is currently connected */
64
+ isConnected: boolean;
65
+ /** Current error, if any */
66
+ error: Error | null;
67
+ /** WebSocket ready state */
68
+ readyState: number;
69
+ }
70
+
71
+ /**
72
+ * Generic WebSocket connection manager with automatic reconnection,
73
+ * message queuing, and handler management.
74
+ *
75
+ * Framework-agnostic - can be used with React, Svelte, Vue, or vanilla JS.
76
+ */
77
+ export class WebSocketManager<TInput = unknown, TOutput = unknown> {
78
+ private ws: WebSocket | undefined;
79
+ private manualClose = false;
80
+ private pendingMessages: TOutput[] = [];
81
+ private queuedMessages: TInput[] = [];
82
+ private messageHandler: MessageHandler<TOutput> | undefined;
83
+ private reconnectManager: ReconnectManager | undefined;
84
+ private callbacks: WebSocketCallbacks<TOutput>;
85
+ private url: string;
86
+ private reconnectConfig: Required<NonNullable<WebSocketManagerOptions<TOutput>['reconnect']>>;
87
+
88
+ constructor(options: WebSocketManagerOptions<TOutput>) {
89
+ this.url = options.url;
90
+ this.callbacks = options.callbacks || {};
91
+ this.reconnectConfig = {
92
+ threshold: options.reconnect?.threshold ?? 0,
93
+ baseDelay: options.reconnect?.baseDelay ?? 500,
94
+ factor: options.reconnect?.factor ?? 2,
95
+ maxDelay: options.reconnect?.maxDelay ?? 30000,
96
+ jitter: options.reconnect?.jitter ?? 500,
97
+ };
98
+ }
99
+
100
+ /**
101
+ * Connect to the WebSocket server
102
+ */
103
+ connect(): void {
104
+ if (this.manualClose) return;
105
+
106
+ this.ws = new WebSocket(this.url);
107
+
108
+ this.ws.onopen = () => {
109
+ this.reconnectManager?.recordSuccess();
110
+ this.callbacks.onConnect?.();
111
+
112
+ // Flush queued messages
113
+ if (this.queuedMessages.length > 0) {
114
+ this.queuedMessages.forEach((msg) => this.ws!.send(serializeWSData(msg)));
115
+ this.queuedMessages = [];
116
+ }
117
+ };
118
+
119
+ this.ws.onerror = () => {
120
+ const error = new Error('WebSocket error');
121
+ this.callbacks.onError?.(error);
122
+ };
123
+
124
+ this.ws.onclose = (evt) => {
125
+ this.ws = undefined;
126
+ this.callbacks.onDisconnect?.();
127
+
128
+ if (this.manualClose) {
129
+ this.queuedMessages = [];
130
+ return;
131
+ }
132
+
133
+ if (evt.code !== 1000) {
134
+ const error = new Error(`WebSocket closed: ${evt.code} ${evt.reason || ''}`);
135
+ this.callbacks.onError?.(error);
136
+ }
137
+
138
+ this.reconnectManager?.recordFailure();
139
+ };
140
+
141
+ this.ws.onmessage = (event: MessageEvent) => {
142
+ const payload = deserializeData<TOutput>(event.data);
143
+
144
+ // Call the registered message handler
145
+ if (this.messageHandler) {
146
+ this.messageHandler(payload);
147
+ } else {
148
+ // Buffer messages until a handler is set
149
+ this.pendingMessages.push(payload);
150
+ }
151
+
152
+ // Also call the callback if provided
153
+ this.callbacks.onMessage?.(payload);
154
+ };
155
+
156
+ // Setup reconnect manager
157
+ if (!this.reconnectManager) {
158
+ this.reconnectManager = createReconnectManager({
159
+ onReconnect: () => this.connect(),
160
+ threshold: this.reconnectConfig.threshold,
161
+ baseDelay: this.reconnectConfig.baseDelay,
162
+ factor: this.reconnectConfig.factor,
163
+ maxDelay: this.reconnectConfig.maxDelay,
164
+ jitter: this.reconnectConfig.jitter,
165
+ enabled: () => !this.manualClose,
166
+ });
167
+ }
168
+ }
169
+
170
+ /**
171
+ * Send data through the WebSocket.
172
+ * Messages are queued if not currently connected.
173
+ */
174
+ send(data: TInput): void {
175
+ if (this.ws?.readyState === WebSocket.OPEN) {
176
+ this.ws.send(serializeWSData(data));
177
+ } else {
178
+ this.queuedMessages.push(data);
179
+ }
180
+ }
181
+
182
+ /**
183
+ * Set the message handler.
184
+ * Any buffered messages will be delivered immediately.
185
+ */
186
+ setMessageHandler(handler: MessageHandler<TOutput>): void {
187
+ this.messageHandler = handler;
188
+ // Flush pending messages
189
+ this.pendingMessages.forEach(handler);
190
+ this.pendingMessages = [];
191
+ }
192
+
193
+ /**
194
+ * Get current state
195
+ */
196
+ getState(): WebSocketManagerState {
197
+ return {
198
+ isConnected: this.ws?.readyState === WebSocket.OPEN,
199
+ error: null, // Error state managed externally via callbacks
200
+ readyState: this.ws?.readyState ?? WebSocket.CLOSED,
201
+ };
202
+ }
203
+
204
+ /**
205
+ * Close the WebSocket connection and cleanup
206
+ */
207
+ close(): void {
208
+ this.manualClose = true;
209
+ this.reconnectManager?.dispose();
210
+
211
+ if (this.ws) {
212
+ this.ws.onopen = null;
213
+ this.ws.onerror = null;
214
+ this.ws.onclose = null;
215
+ this.ws.onmessage = null;
216
+ this.ws.close();
217
+ }
218
+
219
+ this.ws = undefined;
220
+ this.messageHandler = undefined;
221
+ this.pendingMessages = [];
222
+ this.queuedMessages = [];
223
+
224
+ // Notify disconnect callback
225
+ this.callbacks.onDisconnect?.();
226
+ }
227
+
228
+ /**
229
+ * Dispose of the manager (alias for close)
230
+ */
231
+ dispose(): void {
232
+ this.close();
233
+ }
234
+ }