@agentuity/frontend 0.0.100 → 0.0.101
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/AGENTS.md +1 -1
- package/README.md +5 -5
- package/dist/client/eventstream.d.ts +12 -0
- package/dist/client/eventstream.d.ts.map +1 -0
- package/dist/client/eventstream.js +39 -0
- package/dist/client/eventstream.js.map +1 -0
- package/dist/client/index.d.ts +33 -0
- package/dist/client/index.d.ts.map +1 -0
- package/dist/client/index.js +149 -0
- package/dist/client/index.js.map +1 -0
- package/dist/client/stream.d.ts +6 -0
- package/dist/client/stream.d.ts.map +1 -0
- package/dist/client/stream.js +49 -0
- package/dist/client/stream.js.map +1 -0
- package/dist/client/types.d.ts +122 -0
- package/dist/client/types.d.ts.map +1 -0
- package/dist/client/types.js +5 -0
- package/dist/client/types.js.map +1 -0
- package/dist/client/websocket.d.ts +6 -0
- package/dist/client/websocket.d.ts.map +1 -0
- package/dist/client/websocket.js +49 -0
- package/dist/client/websocket.js.map +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -1
- package/package.json +5 -3
- package/src/client/eventstream.ts +52 -0
- package/src/client/index.ts +171 -0
- package/src/client/stream.ts +61 -0
- package/src/client/types.ts +150 -0
- package/src/client/websocket.ts +61 -0
- package/src/index.ts +12 -0
package/AGENTS.md
CHANGED
package/README.md
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# @agentuity/
|
|
1
|
+
# @agentuity/frontend
|
|
2
2
|
|
|
3
3
|
Generic web utilities for building Agentuity frontend applications. Provides framework-agnostic utilities for URL building, serialization, reconnection logic, and type definitions.
|
|
4
4
|
|
|
@@ -7,7 +7,7 @@ This package contains reusable JavaScript logic that can be shared across differ
|
|
|
7
7
|
## Installation
|
|
8
8
|
|
|
9
9
|
```bash
|
|
10
|
-
npm install @agentuity/
|
|
10
|
+
npm install @agentuity/frontend
|
|
11
11
|
```
|
|
12
12
|
|
|
13
13
|
## Features
|
|
@@ -24,7 +24,7 @@ npm install @agentuity/web
|
|
|
24
24
|
### URL Building
|
|
25
25
|
|
|
26
26
|
```typescript
|
|
27
|
-
import { buildUrl, defaultBaseUrl } from '@agentuity/
|
|
27
|
+
import { buildUrl, defaultBaseUrl } from '@agentuity/frontend';
|
|
28
28
|
|
|
29
29
|
const url = buildUrl(
|
|
30
30
|
'https://api.example.com',
|
|
@@ -38,7 +38,7 @@ const url = buildUrl(
|
|
|
38
38
|
### Reconnection Manager
|
|
39
39
|
|
|
40
40
|
```typescript
|
|
41
|
-
import { createReconnectManager } from '@agentuity/
|
|
41
|
+
import { createReconnectManager } from '@agentuity/frontend';
|
|
42
42
|
|
|
43
43
|
const reconnect = createReconnectManager({
|
|
44
44
|
onReconnect: () => console.log('Reconnecting...'),
|
|
@@ -55,7 +55,7 @@ reconnect.recordFailure();
|
|
|
55
55
|
### Serialization
|
|
56
56
|
|
|
57
57
|
```typescript
|
|
58
|
-
import { deserializeData } from '@agentuity/
|
|
58
|
+
import { deserializeData } from '@agentuity/frontend';
|
|
59
59
|
|
|
60
60
|
const data = deserializeData<MyType>('{"key":"value"}');
|
|
61
61
|
```
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { EventStreamClient } from './types';
|
|
2
|
+
/**
|
|
3
|
+
* Create an EventSource (SSE) client wrapper with event-based API.
|
|
4
|
+
*
|
|
5
|
+
* Note: Native EventSource has limited authentication support.
|
|
6
|
+
* - withCredentials: true will send cookies and HTTP auth headers
|
|
7
|
+
* - For custom Authorization headers, consider using @microsoft/fetch-event-source
|
|
8
|
+
*/
|
|
9
|
+
export declare function createEventStreamClient(url: string, options?: {
|
|
10
|
+
withCredentials?: boolean;
|
|
11
|
+
}): EventStreamClient;
|
|
12
|
+
//# sourceMappingURL=eventstream.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"eventstream.d.ts","sourceRoot":"","sources":["../../src/client/eventstream.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAgB,iBAAiB,EAAE,MAAM,SAAS,CAAC;AAE/D;;;;;;GAMG;AACH,wBAAgB,uBAAuB,CACtC,GAAG,EAAE,MAAM,EACX,OAAO,CAAC,EAAE;IAAE,eAAe,CAAC,EAAE,OAAO,CAAA;CAAE,GACrC,iBAAiB,CAuCnB"}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Create an EventSource (SSE) client wrapper with event-based API.
|
|
3
|
+
*
|
|
4
|
+
* Note: Native EventSource has limited authentication support.
|
|
5
|
+
* - withCredentials: true will send cookies and HTTP auth headers
|
|
6
|
+
* - For custom Authorization headers, consider using @microsoft/fetch-event-source
|
|
7
|
+
*/
|
|
8
|
+
export function createEventStreamClient(url, options) {
|
|
9
|
+
const eventSource = new EventSource(url, {
|
|
10
|
+
withCredentials: options?.withCredentials ?? false,
|
|
11
|
+
});
|
|
12
|
+
const handlers = {
|
|
13
|
+
message: new Set(),
|
|
14
|
+
open: new Set(),
|
|
15
|
+
error: new Set(),
|
|
16
|
+
};
|
|
17
|
+
// Set up native EventSource event listeners
|
|
18
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
19
|
+
eventSource.onmessage = (event) => {
|
|
20
|
+
handlers.message.forEach((handler) => handler(event));
|
|
21
|
+
};
|
|
22
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
23
|
+
eventSource.onopen = (event) => {
|
|
24
|
+
handlers.open.forEach((handler) => handler(event));
|
|
25
|
+
};
|
|
26
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
27
|
+
eventSource.onerror = (event) => {
|
|
28
|
+
handlers.error.forEach((handler) => handler(event));
|
|
29
|
+
};
|
|
30
|
+
return {
|
|
31
|
+
on: ((event, handler) => {
|
|
32
|
+
handlers[event].add(handler);
|
|
33
|
+
}),
|
|
34
|
+
close() {
|
|
35
|
+
eventSource.close();
|
|
36
|
+
},
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
//# sourceMappingURL=eventstream.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"eventstream.js","sourceRoot":"","sources":["../../src/client/eventstream.ts"],"names":[],"mappings":"AAEA;;;;;;GAMG;AACH,MAAM,UAAU,uBAAuB,CACtC,GAAW,EACX,OAAuC;IAEvC,MAAM,WAAW,GAAG,IAAI,WAAW,CAAC,GAAG,EAAE;QACxC,eAAe,EAAE,OAAO,EAAE,eAAe,IAAI,KAAK;KAClD,CAAC,CAAC;IACH,MAAM,QAAQ,GAIV;QACH,OAAO,EAAE,IAAI,GAAG,EAAE;QAClB,IAAI,EAAE,IAAI,GAAG,EAAE;QACf,KAAK,EAAE,IAAI,GAAG,EAAE;KAChB,CAAC;IAEF,4CAA4C;IAC5C,8DAA8D;IAC9D,WAAW,CAAC,SAAS,GAAG,CAAC,KAAU,EAAE,EAAE;QACtC,QAAQ,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC;IACvD,CAAC,CAAC;IAEF,8DAA8D;IAC9D,WAAW,CAAC,MAAM,GAAG,CAAC,KAAU,EAAE,EAAE;QACnC,QAAQ,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC;IACpD,CAAC,CAAC;IAEF,8DAA8D;IAC9D,WAAW,CAAC,OAAO,GAAG,CAAC,KAAU,EAAE,EAAE;QACpC,QAAQ,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC;IACrD,CAAC,CAAC;IAEF,OAAO;QACN,EAAE,EAAE,CAAC,CAAC,KAAmC,EAAE,OAAqB,EAAE,EAAE;YACnE,QAAQ,CAAC,KAAK,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;QAC9B,CAAC,CAA4B;QAE7B,KAAK;YACJ,WAAW,CAAC,KAAK,EAAE,CAAC;QACrB,CAAC;KACD,CAAC;AACH,CAAC"}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import type { Client, ClientOptions } from './types';
|
|
2
|
+
/**
|
|
3
|
+
* Create a type-safe API client from a RouteRegistry.
|
|
4
|
+
*
|
|
5
|
+
* Uses a Proxy to build up the path as you navigate the object,
|
|
6
|
+
* then executes the request when you call a terminal method (.post(), .get(), .websocket(), etc.).
|
|
7
|
+
*
|
|
8
|
+
* @example
|
|
9
|
+
* ```typescript
|
|
10
|
+
* import { createClient } from '@agentuity/frontend';
|
|
11
|
+
* import type { RPCRouteRegistry } from './generated/routes';
|
|
12
|
+
*
|
|
13
|
+
* const client = createClient<RPCRouteRegistry>();
|
|
14
|
+
*
|
|
15
|
+
* // Type-safe API call
|
|
16
|
+
* const result = await client.hello.post({ name: 'World' });
|
|
17
|
+
*
|
|
18
|
+
* // WebSocket
|
|
19
|
+
* const ws = client.chat.websocket();
|
|
20
|
+
* ws.on('message', (msg) => console.log(msg));
|
|
21
|
+
*
|
|
22
|
+
* // Server-Sent Events
|
|
23
|
+
* const es = client.events.eventstream();
|
|
24
|
+
* es.on('message', (event) => console.log(event.data));
|
|
25
|
+
*
|
|
26
|
+
* // Streaming response
|
|
27
|
+
* const stream = await client.data.stream({ query: 'foo' });
|
|
28
|
+
* stream.on('chunk', (chunk) => console.log(chunk));
|
|
29
|
+
* ```
|
|
30
|
+
*/
|
|
31
|
+
export declare function createClient<R>(options?: ClientOptions, metadata?: unknown): Client<R>;
|
|
32
|
+
export type { ClientOptions, Client, RouteEndpoint, WebSocketClient, EventStreamClient, StreamClient, EventHandler, } from './types';
|
|
33
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/client/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AA0BrD;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AACH,wBAAgB,YAAY,CAAC,CAAC,EAAE,OAAO,GAAE,aAAkB,EAAE,QAAQ,CAAC,EAAE,OAAO,GAAG,MAAM,CAAC,CAAC,CAAC,CAwG1F;AAGD,YAAY,EACX,aAAa,EACb,MAAM,EACN,aAAa,EACb,eAAe,EACf,iBAAiB,EACjB,YAAY,EACZ,YAAY,GACZ,MAAM,SAAS,CAAC"}
|
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
import { createWebSocketClient } from './websocket';
|
|
2
|
+
import { createEventStreamClient } from './eventstream';
|
|
3
|
+
import { createStreamClient } from './stream';
|
|
4
|
+
/**
|
|
5
|
+
* Default base URL (empty = relative URLs).
|
|
6
|
+
*/
|
|
7
|
+
const defaultBaseUrl = '';
|
|
8
|
+
/**
|
|
9
|
+
* Resolve baseUrl - if it's a function, call it; otherwise return the string.
|
|
10
|
+
*/
|
|
11
|
+
function resolveBaseUrl(baseUrl) {
|
|
12
|
+
return typeof baseUrl === 'function' ? baseUrl() : baseUrl;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Resolve headers - if it's a function, call it; otherwise return the object.
|
|
16
|
+
*/
|
|
17
|
+
function resolveHeaders(headers) {
|
|
18
|
+
return typeof headers === 'function' ? headers() : headers;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Create a type-safe API client from a RouteRegistry.
|
|
22
|
+
*
|
|
23
|
+
* Uses a Proxy to build up the path as you navigate the object,
|
|
24
|
+
* then executes the request when you call a terminal method (.post(), .get(), .websocket(), etc.).
|
|
25
|
+
*
|
|
26
|
+
* @example
|
|
27
|
+
* ```typescript
|
|
28
|
+
* import { createClient } from '@agentuity/frontend';
|
|
29
|
+
* import type { RPCRouteRegistry } from './generated/routes';
|
|
30
|
+
*
|
|
31
|
+
* const client = createClient<RPCRouteRegistry>();
|
|
32
|
+
*
|
|
33
|
+
* // Type-safe API call
|
|
34
|
+
* const result = await client.hello.post({ name: 'World' });
|
|
35
|
+
*
|
|
36
|
+
* // WebSocket
|
|
37
|
+
* const ws = client.chat.websocket();
|
|
38
|
+
* ws.on('message', (msg) => console.log(msg));
|
|
39
|
+
*
|
|
40
|
+
* // Server-Sent Events
|
|
41
|
+
* const es = client.events.eventstream();
|
|
42
|
+
* es.on('message', (event) => console.log(event.data));
|
|
43
|
+
*
|
|
44
|
+
* // Streaming response
|
|
45
|
+
* const stream = await client.data.stream({ query: 'foo' });
|
|
46
|
+
* stream.on('chunk', (chunk) => console.log(chunk));
|
|
47
|
+
* ```
|
|
48
|
+
*/
|
|
49
|
+
export function createClient(options = {}, metadata) {
|
|
50
|
+
const { baseUrl = defaultBaseUrl, headers, contentType = 'application/json', signal } = options;
|
|
51
|
+
// Default headers to empty object if not provided
|
|
52
|
+
const defaultHeaders = headers || {};
|
|
53
|
+
const handler = {
|
|
54
|
+
get(target, prop) {
|
|
55
|
+
const currentPath = target.__path;
|
|
56
|
+
const newPath = [...currentPath, prop];
|
|
57
|
+
// Check if this is a terminal method
|
|
58
|
+
const httpMethods = ['get', 'post', 'put', 'delete', 'patch', 'head', 'options'];
|
|
59
|
+
const streamMethods = ['websocket', 'eventstream', 'stream'];
|
|
60
|
+
const isHttpMethod = httpMethods.includes(prop);
|
|
61
|
+
const isStreamMethod = streamMethods.includes(prop);
|
|
62
|
+
const isTerminalMethod = isHttpMethod || isStreamMethod;
|
|
63
|
+
// Terminal: method at the end (minimum 1 path segment required)
|
|
64
|
+
// Examples: client.hello.post(), client.echo.websocket(), client.events.eventstream()
|
|
65
|
+
if (isTerminalMethod && currentPath.length >= 1) {
|
|
66
|
+
const method = prop;
|
|
67
|
+
const pathSegments = currentPath;
|
|
68
|
+
const urlPath = '/api/' + pathSegments.join('/');
|
|
69
|
+
// Determine route type
|
|
70
|
+
let routeType = 'api';
|
|
71
|
+
if (isStreamMethod) {
|
|
72
|
+
// Stream methods directly specify the route type
|
|
73
|
+
if (method === 'websocket')
|
|
74
|
+
routeType = 'websocket';
|
|
75
|
+
else if (method === 'eventstream')
|
|
76
|
+
routeType = 'sse';
|
|
77
|
+
else if (method === 'stream')
|
|
78
|
+
routeType = 'stream';
|
|
79
|
+
}
|
|
80
|
+
else if (metadata) {
|
|
81
|
+
// Look up route type from metadata for HTTP methods
|
|
82
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
83
|
+
let metaNode = metadata;
|
|
84
|
+
for (const segment of pathSegments) {
|
|
85
|
+
if (metaNode && typeof metaNode === 'object') {
|
|
86
|
+
metaNode = metaNode[segment];
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
if (metaNode && typeof metaNode === 'object' && metaNode[method]?.type) {
|
|
90
|
+
routeType = metaNode[method].type;
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
return (input) => {
|
|
94
|
+
const resolvedBaseUrl = resolveBaseUrl(baseUrl);
|
|
95
|
+
const resolvedHeaders = resolveHeaders(defaultHeaders);
|
|
96
|
+
// WebSocket endpoint
|
|
97
|
+
if (routeType === 'websocket') {
|
|
98
|
+
const wsBaseUrl = resolvedBaseUrl.replace(/^http/, 'ws');
|
|
99
|
+
const wsUrl = `${wsBaseUrl}${urlPath}`;
|
|
100
|
+
const ws = createWebSocketClient(wsUrl);
|
|
101
|
+
if (input) {
|
|
102
|
+
ws.on('open', () => ws.send(input));
|
|
103
|
+
}
|
|
104
|
+
return ws;
|
|
105
|
+
}
|
|
106
|
+
// SSE endpoint
|
|
107
|
+
if (routeType === 'sse') {
|
|
108
|
+
const sseUrl = `${resolvedBaseUrl}${urlPath}`;
|
|
109
|
+
// Note: Native EventSource doesn't support custom headers
|
|
110
|
+
// For auth, use withCredentials or consider fetch-based alternatives like @microsoft/fetch-event-source
|
|
111
|
+
return createEventStreamClient(sseUrl, {
|
|
112
|
+
withCredentials: Object.keys(resolvedHeaders).length > 0,
|
|
113
|
+
});
|
|
114
|
+
}
|
|
115
|
+
// Stream endpoint
|
|
116
|
+
if (routeType === 'stream') {
|
|
117
|
+
return fetch(`${resolvedBaseUrl}${urlPath}`, {
|
|
118
|
+
method: method.toUpperCase(),
|
|
119
|
+
headers: { 'Content-Type': contentType, ...resolvedHeaders },
|
|
120
|
+
body: input ? JSON.stringify(input) : undefined,
|
|
121
|
+
signal,
|
|
122
|
+
}).then((res) => {
|
|
123
|
+
if (!res.ok)
|
|
124
|
+
throw new Error(`HTTP ${res.status}: ${res.statusText}`);
|
|
125
|
+
return createStreamClient(res);
|
|
126
|
+
});
|
|
127
|
+
}
|
|
128
|
+
// Regular API endpoint
|
|
129
|
+
return fetch(`${resolvedBaseUrl}${urlPath}`, {
|
|
130
|
+
method: method.toUpperCase(),
|
|
131
|
+
headers: { 'Content-Type': contentType, ...resolvedHeaders },
|
|
132
|
+
body: method.toUpperCase() !== 'GET' && input ? JSON.stringify(input) : undefined,
|
|
133
|
+
signal,
|
|
134
|
+
}).then(async (res) => {
|
|
135
|
+
if (!res.ok)
|
|
136
|
+
throw new Error(`HTTP ${res.status}: ${res.statusText}`);
|
|
137
|
+
if (res.status === 204)
|
|
138
|
+
return undefined;
|
|
139
|
+
return res.json();
|
|
140
|
+
});
|
|
141
|
+
};
|
|
142
|
+
}
|
|
143
|
+
// Otherwise, return a new proxy with extended path
|
|
144
|
+
return new Proxy({ __path: newPath }, handler);
|
|
145
|
+
},
|
|
146
|
+
};
|
|
147
|
+
return new Proxy({ __path: [] }, handler);
|
|
148
|
+
}
|
|
149
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/client/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"stream.d.ts","sourceRoot":"","sources":["../../src/client/stream.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAgB,YAAY,EAAE,MAAM,SAAS,CAAC;AAE1D;;GAEG;AACH,wBAAgB,kBAAkB,CAAC,QAAQ,EAAE,QAAQ,GAAG,YAAY,CAuDnE"}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Create a streaming response reader with event-based API.
|
|
3
|
+
*/
|
|
4
|
+
export function createStreamClient(response) {
|
|
5
|
+
const handlers = {
|
|
6
|
+
chunk: new Set(),
|
|
7
|
+
close: new Set(),
|
|
8
|
+
error: new Set(),
|
|
9
|
+
};
|
|
10
|
+
const reader = response.body?.getReader();
|
|
11
|
+
let cancelled = false;
|
|
12
|
+
// Start reading the stream
|
|
13
|
+
const readStream = async () => {
|
|
14
|
+
if (!reader) {
|
|
15
|
+
const error = new Error('Response body is not readable');
|
|
16
|
+
handlers.error.forEach((handler) => handler(error));
|
|
17
|
+
return;
|
|
18
|
+
}
|
|
19
|
+
try {
|
|
20
|
+
while (!cancelled) {
|
|
21
|
+
const { done, value } = await reader.read();
|
|
22
|
+
if (done) {
|
|
23
|
+
handlers.close.forEach((handler) => handler());
|
|
24
|
+
break;
|
|
25
|
+
}
|
|
26
|
+
if (value) {
|
|
27
|
+
handlers.chunk.forEach((handler) => handler(value));
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
catch (error) {
|
|
32
|
+
const err = error instanceof Error ? error : new Error(String(error));
|
|
33
|
+
handlers.error.forEach((handler) => handler(err));
|
|
34
|
+
}
|
|
35
|
+
};
|
|
36
|
+
// Start reading immediately
|
|
37
|
+
readStream();
|
|
38
|
+
return {
|
|
39
|
+
on: ((event, handler) => {
|
|
40
|
+
handlers[event].add(handler);
|
|
41
|
+
}),
|
|
42
|
+
async cancel() {
|
|
43
|
+
cancelled = true;
|
|
44
|
+
await reader?.cancel();
|
|
45
|
+
handlers.close.forEach((handler) => handler());
|
|
46
|
+
},
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
//# sourceMappingURL=stream.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"stream.js","sourceRoot":"","sources":["../../src/client/stream.ts"],"names":[],"mappings":"AAEA;;GAEG;AACH,MAAM,UAAU,kBAAkB,CAAC,QAAkB;IACpD,MAAM,QAAQ,GAIV;QACH,KAAK,EAAE,IAAI,GAAG,EAAE;QAChB,KAAK,EAAE,IAAI,GAAG,EAAE;QAChB,KAAK,EAAE,IAAI,GAAG,EAAE;KAChB,CAAC;IAEF,MAAM,MAAM,GAAG,QAAQ,CAAC,IAAI,EAAE,SAAS,EAAE,CAAC;IAC1C,IAAI,SAAS,GAAG,KAAK,CAAC;IAEtB,2BAA2B;IAC3B,MAAM,UAAU,GAAG,KAAK,IAAI,EAAE;QAC7B,IAAI,CAAC,MAAM,EAAE,CAAC;YACb,MAAM,KAAK,GAAG,IAAI,KAAK,CAAC,+BAA+B,CAAC,CAAC;YACzD,QAAQ,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC;YACpD,OAAO;QACR,CAAC;QAED,IAAI,CAAC;YACJ,OAAO,CAAC,SAAS,EAAE,CAAC;gBACnB,MAAM,EAAE,IAAI,EAAE,KAAK,EAAE,GAAG,MAAM,MAAM,CAAC,IAAI,EAAE,CAAC;gBAE5C,IAAI,IAAI,EAAE,CAAC;oBACV,QAAQ,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,OAAO,EAAE,CAAC,CAAC;oBAC/C,MAAM;gBACP,CAAC;gBAED,IAAI,KAAK,EAAE,CAAC;oBACX,QAAQ,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC;gBACrD,CAAC;YACF,CAAC;QACF,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YAChB,MAAM,GAAG,GAAG,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,KAAK,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC;YACtE,QAAQ,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC;QACnD,CAAC;IACF,CAAC,CAAC;IAEF,4BAA4B;IAC5B,UAAU,EAAE,CAAC;IAEb,OAAO;QACN,EAAE,EAAE,CAAC,CAAC,KAAkC,EAAE,OAAqB,EAAE,EAAE;YAClE,QAAQ,CAAC,KAAK,CAAC,CAAC,GAAG,CAAC,OAAkD,CAAC,CAAC;QACzE,CAAC,CAAuB;QAExB,KAAK,CAAC,MAAM;YACX,SAAS,GAAG,IAAI,CAAC;YACjB,MAAM,MAAM,EAAE,MAAM,EAAE,CAAC;YACvB,QAAQ,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,OAAO,EAAE,CAAC,CAAC;QAChD,CAAC;KACD,CAAC;AACH,CAAC"}
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Type-safe API client types for RPC-style invocations.
|
|
3
|
+
*/
|
|
4
|
+
/**
|
|
5
|
+
* Options for creating a client.
|
|
6
|
+
*/
|
|
7
|
+
export interface ClientOptions {
|
|
8
|
+
/**
|
|
9
|
+
* Base URL for API requests (e.g., "https://p1234567890.agenuity.run").
|
|
10
|
+
* Defaults to empty string (relative URLs).
|
|
11
|
+
* Can be a string or a function that returns a string for lazy resolution.
|
|
12
|
+
*/
|
|
13
|
+
baseUrl?: string | (() => string);
|
|
14
|
+
/**
|
|
15
|
+
* Default headers to include in all requests.
|
|
16
|
+
* Can be a static object or a function that returns headers for dynamic resolution (e.g., auth tokens).
|
|
17
|
+
*/
|
|
18
|
+
headers?: Record<string, string> | (() => Record<string, string>);
|
|
19
|
+
/**
|
|
20
|
+
* Content-Type header for request bodies.
|
|
21
|
+
* @default "application/json"
|
|
22
|
+
*/
|
|
23
|
+
contentType?: string;
|
|
24
|
+
/**
|
|
25
|
+
* AbortSignal for request cancellation.
|
|
26
|
+
*/
|
|
27
|
+
signal?: AbortSignal;
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Event handler for streaming responses.
|
|
31
|
+
*/
|
|
32
|
+
export type EventHandler<T = unknown> = (data: T) => void;
|
|
33
|
+
/**
|
|
34
|
+
* WebSocket wrapper with event-based API.
|
|
35
|
+
*/
|
|
36
|
+
export interface WebSocketClient {
|
|
37
|
+
/**
|
|
38
|
+
* Register an event handler.
|
|
39
|
+
*/
|
|
40
|
+
on: {
|
|
41
|
+
(event: 'message', handler: EventHandler<unknown>): void;
|
|
42
|
+
(event: 'open', handler: EventHandler<Event>): void;
|
|
43
|
+
(event: 'close', handler: EventHandler<CloseEvent>): void;
|
|
44
|
+
(event: 'error', handler: EventHandler<Event>): void;
|
|
45
|
+
};
|
|
46
|
+
/**
|
|
47
|
+
* Send data through the WebSocket.
|
|
48
|
+
*/
|
|
49
|
+
send(data: unknown): void;
|
|
50
|
+
/**
|
|
51
|
+
* Close the WebSocket connection.
|
|
52
|
+
*/
|
|
53
|
+
close(code?: number, reason?: string): void;
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Server-Sent Events (SSE) client with event-based API.
|
|
57
|
+
*/
|
|
58
|
+
export interface EventStreamClient {
|
|
59
|
+
/**
|
|
60
|
+
* Register an event handler.
|
|
61
|
+
*/
|
|
62
|
+
on: {
|
|
63
|
+
(event: 'message', handler: EventHandler<MessageEvent>): void;
|
|
64
|
+
(event: 'open', handler: EventHandler<Event>): void;
|
|
65
|
+
(event: 'error', handler: EventHandler<Event>): void;
|
|
66
|
+
};
|
|
67
|
+
/**
|
|
68
|
+
* Close the EventSource connection.
|
|
69
|
+
*/
|
|
70
|
+
close(): void;
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* Streaming response reader with event-based API.
|
|
74
|
+
*/
|
|
75
|
+
export interface StreamClient {
|
|
76
|
+
/**
|
|
77
|
+
* Register an event handler.
|
|
78
|
+
*/
|
|
79
|
+
on: {
|
|
80
|
+
(event: 'chunk', handler: EventHandler<Uint8Array>): void;
|
|
81
|
+
(event: 'close', handler: EventHandler<void>): void;
|
|
82
|
+
(event: 'error', handler: EventHandler<Error>): void;
|
|
83
|
+
};
|
|
84
|
+
/**
|
|
85
|
+
* Cancel the stream.
|
|
86
|
+
*/
|
|
87
|
+
cancel(): Promise<void>;
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* API endpoint - callable function for regular HTTP calls.
|
|
91
|
+
*/
|
|
92
|
+
export type APIEndpoint<Input = unknown, Output = unknown> = (input?: Input) => Promise<Output>;
|
|
93
|
+
/**
|
|
94
|
+
* WebSocket endpoint - callable function that returns WebSocket client.
|
|
95
|
+
*/
|
|
96
|
+
export type WebSocketEndpoint<Input = unknown, _Output = unknown> = (input?: Input) => WebSocketClient;
|
|
97
|
+
/**
|
|
98
|
+
* Server-Sent Events endpoint - callable function that returns EventStream client.
|
|
99
|
+
*/
|
|
100
|
+
export type SSEEndpoint<Input = unknown, _Output = unknown> = (input?: Input) => EventStreamClient;
|
|
101
|
+
/**
|
|
102
|
+
* Streaming endpoint - callable function that returns Stream client.
|
|
103
|
+
*/
|
|
104
|
+
export type StreamEndpoint<Input = unknown, _Output = unknown> = (input?: Input) => StreamClient;
|
|
105
|
+
/**
|
|
106
|
+
* Route endpoint - discriminated union based on route type.
|
|
107
|
+
*/
|
|
108
|
+
export type RouteEndpoint<Input = unknown, Output = unknown, Type extends string = 'api'> = Type extends 'websocket' ? WebSocketEndpoint<Input, Output> : Type extends 'sse' ? SSEEndpoint<Input, Output> : Type extends 'stream' ? StreamEndpoint<Input, Output> : APIEndpoint<Input, Output>;
|
|
109
|
+
/**
|
|
110
|
+
* Recursively build the client proxy type from a RouteRegistry.
|
|
111
|
+
*/
|
|
112
|
+
export type Client<R> = {
|
|
113
|
+
[K in keyof R]: R[K] extends {
|
|
114
|
+
input: infer I;
|
|
115
|
+
output: infer O;
|
|
116
|
+
type: infer T;
|
|
117
|
+
} ? RouteEndpoint<I, O, T extends string ? T : 'api'> : R[K] extends {
|
|
118
|
+
input: infer I;
|
|
119
|
+
output: infer O;
|
|
120
|
+
} ? RouteEndpoint<I, O, 'api'> : R[K] extends object ? Client<R[K]> : never;
|
|
121
|
+
};
|
|
122
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/client/types.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/client/types.ts"],"names":[],"mappings":"AAAA;;GAEG"}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { WebSocketClient } from './types';
|
|
2
|
+
/**
|
|
3
|
+
* Create a WebSocket client wrapper with event-based API.
|
|
4
|
+
*/
|
|
5
|
+
export declare function createWebSocketClient(url: string, protocols?: string | string[]): WebSocketClient;
|
|
6
|
+
//# sourceMappingURL=websocket.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"websocket.d.ts","sourceRoot":"","sources":["../../src/client/websocket.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAgB,eAAe,EAAE,MAAM,SAAS,CAAC;AAE7D;;GAEG;AACH,wBAAgB,qBAAqB,CAAC,GAAG,EAAE,MAAM,EAAE,SAAS,CAAC,EAAE,MAAM,GAAG,MAAM,EAAE,GAAG,eAAe,CAuDjG"}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Create a WebSocket client wrapper with event-based API.
|
|
3
|
+
*/
|
|
4
|
+
export function createWebSocketClient(url, protocols) {
|
|
5
|
+
const ws = new WebSocket(url, protocols);
|
|
6
|
+
const handlers = {
|
|
7
|
+
message: new Set(),
|
|
8
|
+
open: new Set(),
|
|
9
|
+
close: new Set(),
|
|
10
|
+
error: new Set(),
|
|
11
|
+
};
|
|
12
|
+
// Set up native WebSocket event listeners
|
|
13
|
+
ws.addEventListener('message', (event) => {
|
|
14
|
+
let data = event.data;
|
|
15
|
+
// Try to parse JSON if data is a string
|
|
16
|
+
if (typeof event.data === 'string') {
|
|
17
|
+
try {
|
|
18
|
+
data = JSON.parse(event.data);
|
|
19
|
+
}
|
|
20
|
+
catch {
|
|
21
|
+
// Keep as string if not valid JSON
|
|
22
|
+
data = event.data;
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
handlers.message.forEach((handler) => handler(data));
|
|
26
|
+
});
|
|
27
|
+
ws.addEventListener('open', (event) => {
|
|
28
|
+
handlers.open.forEach((handler) => handler(event));
|
|
29
|
+
});
|
|
30
|
+
ws.addEventListener('close', (event) => {
|
|
31
|
+
handlers.close.forEach((handler) => handler(event));
|
|
32
|
+
});
|
|
33
|
+
ws.addEventListener('error', (event) => {
|
|
34
|
+
handlers.error.forEach((handler) => handler(event));
|
|
35
|
+
});
|
|
36
|
+
return {
|
|
37
|
+
on: ((event, handler) => {
|
|
38
|
+
handlers[event].add(handler);
|
|
39
|
+
}),
|
|
40
|
+
send(data) {
|
|
41
|
+
const payload = typeof data === 'string' ? data : JSON.stringify(data);
|
|
42
|
+
ws.send(payload);
|
|
43
|
+
},
|
|
44
|
+
close(code, reason) {
|
|
45
|
+
ws.close(code, reason);
|
|
46
|
+
},
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
//# sourceMappingURL=websocket.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"websocket.js","sourceRoot":"","sources":["../../src/client/websocket.ts"],"names":[],"mappings":"AAEA;;GAEG;AACH,MAAM,UAAU,qBAAqB,CAAC,GAAW,EAAE,SAA6B;IAC/E,MAAM,EAAE,GAAG,IAAI,SAAS,CAAC,GAAG,EAAE,SAAS,CAAC,CAAC;IACzC,MAAM,QAAQ,GAKV;QACH,OAAO,EAAE,IAAI,GAAG,EAAE;QAClB,IAAI,EAAE,IAAI,GAAG,EAAE;QACf,KAAK,EAAE,IAAI,GAAG,EAAE;QAChB,KAAK,EAAE,IAAI,GAAG,EAAE;KAChB,CAAC;IAEF,0CAA0C;IAC1C,EAAE,CAAC,gBAAgB,CAAC,SAAS,EAAE,CAAC,KAAmB,EAAE,EAAE;QACtD,IAAI,IAAI,GAAY,KAAK,CAAC,IAAI,CAAC;QAC/B,wCAAwC;QACxC,IAAI,OAAO,KAAK,CAAC,IAAI,KAAK,QAAQ,EAAE,CAAC;YACpC,IAAI,CAAC;gBACJ,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;YAC/B,CAAC;YAAC,MAAM,CAAC;gBACR,mCAAmC;gBACnC,IAAI,GAAG,KAAK,CAAC,IAAI,CAAC;YACnB,CAAC;QACF,CAAC;QACD,QAAQ,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC;IACtD,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,gBAAgB,CAAC,MAAM,EAAE,CAAC,KAAY,EAAE,EAAE;QAC5C,QAAQ,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC;IACpD,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,gBAAgB,CAAC,OAAO,EAAE,CAAC,KAAiB,EAAE,EAAE;QAClD,QAAQ,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC;IACrD,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,gBAAgB,CAAC,OAAO,EAAE,CAAC,KAAY,EAAE,EAAE;QAC7C,QAAQ,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC;IACrD,CAAC,CAAC,CAAC;IAEH,OAAO;QACN,EAAE,EAAE,CAAC,CAAC,KAA6C,EAAE,OAAqB,EAAE,EAAE;YAC7E,QAAQ,CAAC,KAAK,CAAC,CAAC,GAAG,CAAC,OAAqD,CAAC,CAAC;QAC5E,CAAC,CAA0B;QAE3B,IAAI,CAAC,IAAa;YACjB,MAAM,OAAO,GAAG,OAAO,IAAI,KAAK,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC;YACvE,EAAE,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;QAClB,CAAC;QAED,KAAK,CAAC,IAAa,EAAE,MAAe;YACnC,EAAE,CAAC,KAAK,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;QACxB,CAAC;KACD,CAAC;AACH,CAAC"}
|
package/dist/index.d.ts
CHANGED
|
@@ -6,4 +6,6 @@ export { type RouteRegistry, type WebSocketRouteRegistry, type SSERouteRegistry
|
|
|
6
6
|
export { jsonEqual } from './memo';
|
|
7
7
|
export { WebSocketManager, type MessageHandler as WebSocketMessageHandler, type WebSocketCallbacks, type WebSocketManagerOptions, type WebSocketManagerState, } from './websocket-manager';
|
|
8
8
|
export { EventStreamManager, type MessageHandler as EventStreamMessageHandler, type EventStreamCallbacks, type EventStreamManagerOptions, type EventStreamManagerState, } from './eventstream-manager';
|
|
9
|
+
export { createClient } from './client/index';
|
|
10
|
+
export type { Client, ClientOptions, RouteEndpoint, WebSocketClient, EventStreamClient, StreamClient, EventHandler, } from './client/types';
|
|
9
11
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,OAAO,CAAC;AACtC,OAAO,EAAE,QAAQ,EAAE,cAAc,EAAE,MAAM,OAAO,CAAC;AACjD,OAAO,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAC;AAClD,OAAO,EAAE,sBAAsB,EAAE,KAAK,gBAAgB,EAAE,KAAK,gBAAgB,EAAE,MAAM,aAAa,CAAC;AACnG,OAAO,EAAE,KAAK,aAAa,EAAE,KAAK,sBAAsB,EAAE,KAAK,gBAAgB,EAAE,MAAM,SAAS,CAAC;AACjG,OAAO,EAAE,SAAS,EAAE,MAAM,QAAQ,CAAC;AACnC,OAAO,EACN,gBAAgB,EAChB,KAAK,cAAc,IAAI,uBAAuB,EAC9C,KAAK,kBAAkB,EACvB,KAAK,uBAAuB,EAC5B,KAAK,qBAAqB,GAC1B,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EACN,kBAAkB,EAClB,KAAK,cAAc,IAAI,yBAAyB,EAChD,KAAK,oBAAoB,EACzB,KAAK,yBAAyB,EAC9B,KAAK,uBAAuB,GAC5B,MAAM,uBAAuB,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,OAAO,CAAC;AACtC,OAAO,EAAE,QAAQ,EAAE,cAAc,EAAE,MAAM,OAAO,CAAC;AACjD,OAAO,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAC;AAClD,OAAO,EAAE,sBAAsB,EAAE,KAAK,gBAAgB,EAAE,KAAK,gBAAgB,EAAE,MAAM,aAAa,CAAC;AACnG,OAAO,EAAE,KAAK,aAAa,EAAE,KAAK,sBAAsB,EAAE,KAAK,gBAAgB,EAAE,MAAM,SAAS,CAAC;AACjG,OAAO,EAAE,SAAS,EAAE,MAAM,QAAQ,CAAC;AACnC,OAAO,EACN,gBAAgB,EAChB,KAAK,cAAc,IAAI,uBAAuB,EAC9C,KAAK,kBAAkB,EACvB,KAAK,uBAAuB,EAC5B,KAAK,qBAAqB,GAC1B,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EACN,kBAAkB,EAClB,KAAK,cAAc,IAAI,yBAAyB,EAChD,KAAK,oBAAoB,EACzB,KAAK,yBAAyB,EAC9B,KAAK,uBAAuB,GAC5B,MAAM,uBAAuB,CAAC;AAG/B,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAC9C,YAAY,EACX,MAAM,EACN,aAAa,EACb,aAAa,EACb,eAAe,EACf,iBAAiB,EACjB,YAAY,EACZ,YAAY,GACZ,MAAM,gBAAgB,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -5,4 +5,6 @@ export { createReconnectManager } from './reconnect';
|
|
|
5
5
|
export { jsonEqual } from './memo';
|
|
6
6
|
export { WebSocketManager, } from './websocket-manager';
|
|
7
7
|
export { EventStreamManager, } from './eventstream-manager';
|
|
8
|
+
// Export client implementation (local to this package)
|
|
9
|
+
export { createClient } from './client/index';
|
|
8
10
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,OAAO,CAAC;AACtC,OAAO,EAAE,QAAQ,EAAE,cAAc,EAAE,MAAM,OAAO,CAAC;AACjD,OAAO,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAC;AAClD,OAAO,EAAE,sBAAsB,EAAgD,MAAM,aAAa,CAAC;AAEnG,OAAO,EAAE,SAAS,EAAE,MAAM,QAAQ,CAAC;AACnC,OAAO,EACN,gBAAgB,GAKhB,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EACN,kBAAkB,GAKlB,MAAM,uBAAuB,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,OAAO,CAAC;AACtC,OAAO,EAAE,QAAQ,EAAE,cAAc,EAAE,MAAM,OAAO,CAAC;AACjD,OAAO,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAC;AAClD,OAAO,EAAE,sBAAsB,EAAgD,MAAM,aAAa,CAAC;AAEnG,OAAO,EAAE,SAAS,EAAE,MAAM,QAAQ,CAAC;AACnC,OAAO,EACN,gBAAgB,GAKhB,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EACN,kBAAkB,GAKlB,MAAM,uBAAuB,CAAC;AAE/B,uDAAuD;AACvD,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@agentuity/frontend",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.101",
|
|
4
4
|
"license": "Apache-2.0",
|
|
5
5
|
"author": "Agentuity employees and contributors",
|
|
6
6
|
"type": "module",
|
|
@@ -25,9 +25,11 @@
|
|
|
25
25
|
"test": "bun test",
|
|
26
26
|
"prepublishOnly": "bun run clean && bun run build"
|
|
27
27
|
},
|
|
28
|
-
"dependencies": {
|
|
28
|
+
"dependencies": {
|
|
29
|
+
"@agentuity/core": "0.0.101"
|
|
30
|
+
},
|
|
29
31
|
"devDependencies": {
|
|
30
|
-
"@agentuity/test-utils": "
|
|
32
|
+
"@agentuity/test-utils": "0.0.101",
|
|
31
33
|
"@types/bun": "latest",
|
|
32
34
|
"bun-types": "latest",
|
|
33
35
|
"typescript": "^5.9.0"
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import type { EventHandler, EventStreamClient } from './types';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Create an EventSource (SSE) client wrapper with event-based API.
|
|
5
|
+
*
|
|
6
|
+
* Note: Native EventSource has limited authentication support.
|
|
7
|
+
* - withCredentials: true will send cookies and HTTP auth headers
|
|
8
|
+
* - For custom Authorization headers, consider using @microsoft/fetch-event-source
|
|
9
|
+
*/
|
|
10
|
+
export function createEventStreamClient(
|
|
11
|
+
url: string,
|
|
12
|
+
options?: { withCredentials?: boolean }
|
|
13
|
+
): EventStreamClient {
|
|
14
|
+
const eventSource = new EventSource(url, {
|
|
15
|
+
withCredentials: options?.withCredentials ?? false,
|
|
16
|
+
});
|
|
17
|
+
const handlers: {
|
|
18
|
+
message: Set<EventHandler<MessageEvent>>;
|
|
19
|
+
open: Set<EventHandler<Event>>;
|
|
20
|
+
error: Set<EventHandler<Event>>;
|
|
21
|
+
} = {
|
|
22
|
+
message: new Set(),
|
|
23
|
+
open: new Set(),
|
|
24
|
+
error: new Set(),
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
// Set up native EventSource event listeners
|
|
28
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
29
|
+
eventSource.onmessage = (event: any) => {
|
|
30
|
+
handlers.message.forEach((handler) => handler(event));
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
34
|
+
eventSource.onopen = (event: any) => {
|
|
35
|
+
handlers.open.forEach((handler) => handler(event));
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
39
|
+
eventSource.onerror = (event: any) => {
|
|
40
|
+
handlers.error.forEach((handler) => handler(event));
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
return {
|
|
44
|
+
on: ((event: 'message' | 'open' | 'error', handler: EventHandler) => {
|
|
45
|
+
handlers[event].add(handler);
|
|
46
|
+
}) as EventStreamClient['on'],
|
|
47
|
+
|
|
48
|
+
close() {
|
|
49
|
+
eventSource.close();
|
|
50
|
+
},
|
|
51
|
+
};
|
|
52
|
+
}
|
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
import type { Client, ClientOptions } from './types';
|
|
2
|
+
import { createWebSocketClient } from './websocket';
|
|
3
|
+
import { createEventStreamClient } from './eventstream';
|
|
4
|
+
import { createStreamClient } from './stream';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Default base URL (empty = relative URLs).
|
|
8
|
+
*/
|
|
9
|
+
const defaultBaseUrl = '';
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Resolve baseUrl - if it's a function, call it; otherwise return the string.
|
|
13
|
+
*/
|
|
14
|
+
function resolveBaseUrl(baseUrl: string | (() => string)): string {
|
|
15
|
+
return typeof baseUrl === 'function' ? baseUrl() : baseUrl;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Resolve headers - if it's a function, call it; otherwise return the object.
|
|
20
|
+
*/
|
|
21
|
+
function resolveHeaders(
|
|
22
|
+
headers: Record<string, string> | (() => Record<string, string>)
|
|
23
|
+
): Record<string, string> {
|
|
24
|
+
return typeof headers === 'function' ? headers() : headers;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Create a type-safe API client from a RouteRegistry.
|
|
29
|
+
*
|
|
30
|
+
* Uses a Proxy to build up the path as you navigate the object,
|
|
31
|
+
* then executes the request when you call a terminal method (.post(), .get(), .websocket(), etc.).
|
|
32
|
+
*
|
|
33
|
+
* @example
|
|
34
|
+
* ```typescript
|
|
35
|
+
* import { createClient } from '@agentuity/frontend';
|
|
36
|
+
* import type { RPCRouteRegistry } from './generated/routes';
|
|
37
|
+
*
|
|
38
|
+
* const client = createClient<RPCRouteRegistry>();
|
|
39
|
+
*
|
|
40
|
+
* // Type-safe API call
|
|
41
|
+
* const result = await client.hello.post({ name: 'World' });
|
|
42
|
+
*
|
|
43
|
+
* // WebSocket
|
|
44
|
+
* const ws = client.chat.websocket();
|
|
45
|
+
* ws.on('message', (msg) => console.log(msg));
|
|
46
|
+
*
|
|
47
|
+
* // Server-Sent Events
|
|
48
|
+
* const es = client.events.eventstream();
|
|
49
|
+
* es.on('message', (event) => console.log(event.data));
|
|
50
|
+
*
|
|
51
|
+
* // Streaming response
|
|
52
|
+
* const stream = await client.data.stream({ query: 'foo' });
|
|
53
|
+
* stream.on('chunk', (chunk) => console.log(chunk));
|
|
54
|
+
* ```
|
|
55
|
+
*/
|
|
56
|
+
export function createClient<R>(options: ClientOptions = {}, metadata?: unknown): Client<R> {
|
|
57
|
+
const { baseUrl = defaultBaseUrl, headers, contentType = 'application/json', signal } = options;
|
|
58
|
+
|
|
59
|
+
// Default headers to empty object if not provided
|
|
60
|
+
const defaultHeaders = headers || {};
|
|
61
|
+
|
|
62
|
+
const handler: ProxyHandler<{ __path: string[]; __type?: string }> = {
|
|
63
|
+
get(target, prop: string) {
|
|
64
|
+
const currentPath = target.__path;
|
|
65
|
+
const newPath = [...currentPath, prop];
|
|
66
|
+
|
|
67
|
+
// Check if this is a terminal method
|
|
68
|
+
const httpMethods = ['get', 'post', 'put', 'delete', 'patch', 'head', 'options'];
|
|
69
|
+
const streamMethods = ['websocket', 'eventstream', 'stream'];
|
|
70
|
+
const isHttpMethod = httpMethods.includes(prop);
|
|
71
|
+
const isStreamMethod = streamMethods.includes(prop);
|
|
72
|
+
const isTerminalMethod = isHttpMethod || isStreamMethod;
|
|
73
|
+
|
|
74
|
+
// Terminal: method at the end (minimum 1 path segment required)
|
|
75
|
+
// Examples: client.hello.post(), client.echo.websocket(), client.events.eventstream()
|
|
76
|
+
if (isTerminalMethod && currentPath.length >= 1) {
|
|
77
|
+
const method = prop;
|
|
78
|
+
const pathSegments = currentPath;
|
|
79
|
+
const urlPath = '/api/' + pathSegments.join('/');
|
|
80
|
+
|
|
81
|
+
// Determine route type
|
|
82
|
+
let routeType = 'api';
|
|
83
|
+
if (isStreamMethod) {
|
|
84
|
+
// Stream methods directly specify the route type
|
|
85
|
+
if (method === 'websocket') routeType = 'websocket';
|
|
86
|
+
else if (method === 'eventstream') routeType = 'sse';
|
|
87
|
+
else if (method === 'stream') routeType = 'stream';
|
|
88
|
+
} else if (metadata) {
|
|
89
|
+
// Look up route type from metadata for HTTP methods
|
|
90
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
91
|
+
let metaNode: any = metadata;
|
|
92
|
+
for (const segment of pathSegments) {
|
|
93
|
+
if (metaNode && typeof metaNode === 'object') {
|
|
94
|
+
metaNode = metaNode[segment];
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
if (metaNode && typeof metaNode === 'object' && metaNode[method]?.type) {
|
|
98
|
+
routeType = metaNode[method].type;
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
return (input?: unknown) => {
|
|
103
|
+
const resolvedBaseUrl = resolveBaseUrl(baseUrl);
|
|
104
|
+
const resolvedHeaders = resolveHeaders(defaultHeaders);
|
|
105
|
+
|
|
106
|
+
// WebSocket endpoint
|
|
107
|
+
if (routeType === 'websocket') {
|
|
108
|
+
const wsBaseUrl = resolvedBaseUrl.replace(/^http/, 'ws');
|
|
109
|
+
const wsUrl = `${wsBaseUrl}${urlPath}`;
|
|
110
|
+
const ws = createWebSocketClient(wsUrl);
|
|
111
|
+
if (input) {
|
|
112
|
+
ws.on('open', () => ws.send(input));
|
|
113
|
+
}
|
|
114
|
+
return ws;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
// SSE endpoint
|
|
118
|
+
if (routeType === 'sse') {
|
|
119
|
+
const sseUrl = `${resolvedBaseUrl}${urlPath}`;
|
|
120
|
+
// Note: Native EventSource doesn't support custom headers
|
|
121
|
+
// For auth, use withCredentials or consider fetch-based alternatives like @microsoft/fetch-event-source
|
|
122
|
+
return createEventStreamClient(sseUrl, {
|
|
123
|
+
withCredentials: Object.keys(resolvedHeaders).length > 0,
|
|
124
|
+
});
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
// Stream endpoint
|
|
128
|
+
if (routeType === 'stream') {
|
|
129
|
+
return fetch(`${resolvedBaseUrl}${urlPath}`, {
|
|
130
|
+
method: method.toUpperCase(),
|
|
131
|
+
headers: { 'Content-Type': contentType, ...resolvedHeaders },
|
|
132
|
+
body: input ? JSON.stringify(input) : undefined,
|
|
133
|
+
signal,
|
|
134
|
+
}).then((res) => {
|
|
135
|
+
if (!res.ok) throw new Error(`HTTP ${res.status}: ${res.statusText}`);
|
|
136
|
+
return createStreamClient(res);
|
|
137
|
+
});
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
// Regular API endpoint
|
|
141
|
+
return fetch(`${resolvedBaseUrl}${urlPath}`, {
|
|
142
|
+
method: method.toUpperCase(),
|
|
143
|
+
headers: { 'Content-Type': contentType, ...resolvedHeaders },
|
|
144
|
+
body: method.toUpperCase() !== 'GET' && input ? JSON.stringify(input) : undefined,
|
|
145
|
+
signal,
|
|
146
|
+
}).then(async (res) => {
|
|
147
|
+
if (!res.ok) throw new Error(`HTTP ${res.status}: ${res.statusText}`);
|
|
148
|
+
if (res.status === 204) return undefined;
|
|
149
|
+
return res.json();
|
|
150
|
+
});
|
|
151
|
+
};
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
// Otherwise, return a new proxy with extended path
|
|
155
|
+
return new Proxy({ __path: newPath }, handler);
|
|
156
|
+
},
|
|
157
|
+
};
|
|
158
|
+
|
|
159
|
+
return new Proxy({ __path: [] }, handler) as unknown as Client<R>;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
// Re-export types
|
|
163
|
+
export type {
|
|
164
|
+
ClientOptions,
|
|
165
|
+
Client,
|
|
166
|
+
RouteEndpoint,
|
|
167
|
+
WebSocketClient,
|
|
168
|
+
EventStreamClient,
|
|
169
|
+
StreamClient,
|
|
170
|
+
EventHandler,
|
|
171
|
+
} from './types';
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import type { EventHandler, StreamClient } from './types';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Create a streaming response reader with event-based API.
|
|
5
|
+
*/
|
|
6
|
+
export function createStreamClient(response: Response): StreamClient {
|
|
7
|
+
const handlers: {
|
|
8
|
+
chunk: Set<EventHandler<Uint8Array>>;
|
|
9
|
+
close: Set<EventHandler<void>>;
|
|
10
|
+
error: Set<EventHandler<Error>>;
|
|
11
|
+
} = {
|
|
12
|
+
chunk: new Set(),
|
|
13
|
+
close: new Set(),
|
|
14
|
+
error: new Set(),
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
const reader = response.body?.getReader();
|
|
18
|
+
let cancelled = false;
|
|
19
|
+
|
|
20
|
+
// Start reading the stream
|
|
21
|
+
const readStream = async () => {
|
|
22
|
+
if (!reader) {
|
|
23
|
+
const error = new Error('Response body is not readable');
|
|
24
|
+
handlers.error.forEach((handler) => handler(error));
|
|
25
|
+
return;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
try {
|
|
29
|
+
while (!cancelled) {
|
|
30
|
+
const { done, value } = await reader.read();
|
|
31
|
+
|
|
32
|
+
if (done) {
|
|
33
|
+
handlers.close.forEach((handler) => handler());
|
|
34
|
+
break;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
if (value) {
|
|
38
|
+
handlers.chunk.forEach((handler) => handler(value));
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
} catch (error) {
|
|
42
|
+
const err = error instanceof Error ? error : new Error(String(error));
|
|
43
|
+
handlers.error.forEach((handler) => handler(err));
|
|
44
|
+
}
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
// Start reading immediately
|
|
48
|
+
readStream();
|
|
49
|
+
|
|
50
|
+
return {
|
|
51
|
+
on: ((event: 'chunk' | 'close' | 'error', handler: EventHandler) => {
|
|
52
|
+
handlers[event].add(handler as EventHandler<Uint8Array | void | Error>);
|
|
53
|
+
}) as StreamClient['on'],
|
|
54
|
+
|
|
55
|
+
async cancel() {
|
|
56
|
+
cancelled = true;
|
|
57
|
+
await reader?.cancel();
|
|
58
|
+
handlers.close.forEach((handler) => handler());
|
|
59
|
+
},
|
|
60
|
+
};
|
|
61
|
+
}
|
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Type-safe API client types for RPC-style invocations.
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Options for creating a client.
|
|
7
|
+
*/
|
|
8
|
+
export interface ClientOptions {
|
|
9
|
+
/**
|
|
10
|
+
* Base URL for API requests (e.g., "https://p1234567890.agenuity.run").
|
|
11
|
+
* Defaults to empty string (relative URLs).
|
|
12
|
+
* Can be a string or a function that returns a string for lazy resolution.
|
|
13
|
+
*/
|
|
14
|
+
baseUrl?: string | (() => string);
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Default headers to include in all requests.
|
|
18
|
+
* Can be a static object or a function that returns headers for dynamic resolution (e.g., auth tokens).
|
|
19
|
+
*/
|
|
20
|
+
headers?: Record<string, string> | (() => Record<string, string>);
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Content-Type header for request bodies.
|
|
24
|
+
* @default "application/json"
|
|
25
|
+
*/
|
|
26
|
+
contentType?: string;
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* AbortSignal for request cancellation.
|
|
30
|
+
*/
|
|
31
|
+
signal?: AbortSignal;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Event handler for streaming responses.
|
|
36
|
+
*/
|
|
37
|
+
export type EventHandler<T = unknown> = (data: T) => void;
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* WebSocket wrapper with event-based API.
|
|
41
|
+
*/
|
|
42
|
+
export interface WebSocketClient {
|
|
43
|
+
/**
|
|
44
|
+
* Register an event handler.
|
|
45
|
+
*/
|
|
46
|
+
on: {
|
|
47
|
+
(event: 'message', handler: EventHandler<unknown>): void;
|
|
48
|
+
(event: 'open', handler: EventHandler<Event>): void;
|
|
49
|
+
(event: 'close', handler: EventHandler<CloseEvent>): void;
|
|
50
|
+
(event: 'error', handler: EventHandler<Event>): void;
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Send data through the WebSocket.
|
|
55
|
+
*/
|
|
56
|
+
send(data: unknown): void;
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Close the WebSocket connection.
|
|
60
|
+
*/
|
|
61
|
+
close(code?: number, reason?: string): void;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Server-Sent Events (SSE) client with event-based API.
|
|
66
|
+
*/
|
|
67
|
+
export interface EventStreamClient {
|
|
68
|
+
/**
|
|
69
|
+
* Register an event handler.
|
|
70
|
+
*/
|
|
71
|
+
on: {
|
|
72
|
+
(event: 'message', handler: EventHandler<MessageEvent>): void;
|
|
73
|
+
(event: 'open', handler: EventHandler<Event>): void;
|
|
74
|
+
(event: 'error', handler: EventHandler<Event>): void;
|
|
75
|
+
};
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* Close the EventSource connection.
|
|
79
|
+
*/
|
|
80
|
+
close(): void;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* Streaming response reader with event-based API.
|
|
85
|
+
*/
|
|
86
|
+
export interface StreamClient {
|
|
87
|
+
/**
|
|
88
|
+
* Register an event handler.
|
|
89
|
+
*/
|
|
90
|
+
on: {
|
|
91
|
+
(event: 'chunk', handler: EventHandler<Uint8Array>): void;
|
|
92
|
+
(event: 'close', handler: EventHandler<void>): void;
|
|
93
|
+
(event: 'error', handler: EventHandler<Error>): void;
|
|
94
|
+
};
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* Cancel the stream.
|
|
98
|
+
*/
|
|
99
|
+
cancel(): Promise<void>;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* API endpoint - callable function for regular HTTP calls.
|
|
104
|
+
*/
|
|
105
|
+
export type APIEndpoint<Input = unknown, Output = unknown> = (input?: Input) => Promise<Output>;
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* WebSocket endpoint - callable function that returns WebSocket client.
|
|
109
|
+
*/
|
|
110
|
+
export type WebSocketEndpoint<Input = unknown, _Output = unknown> = (
|
|
111
|
+
input?: Input
|
|
112
|
+
) => WebSocketClient;
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* Server-Sent Events endpoint - callable function that returns EventStream client.
|
|
116
|
+
*/
|
|
117
|
+
export type SSEEndpoint<Input = unknown, _Output = unknown> = (input?: Input) => EventStreamClient;
|
|
118
|
+
|
|
119
|
+
/**
|
|
120
|
+
* Streaming endpoint - callable function that returns Stream client.
|
|
121
|
+
*/
|
|
122
|
+
export type StreamEndpoint<Input = unknown, _Output = unknown> = (input?: Input) => StreamClient;
|
|
123
|
+
|
|
124
|
+
/**
|
|
125
|
+
* Route endpoint - discriminated union based on route type.
|
|
126
|
+
*/
|
|
127
|
+
export type RouteEndpoint<
|
|
128
|
+
Input = unknown,
|
|
129
|
+
Output = unknown,
|
|
130
|
+
Type extends string = 'api',
|
|
131
|
+
> = Type extends 'websocket'
|
|
132
|
+
? WebSocketEndpoint<Input, Output>
|
|
133
|
+
: Type extends 'sse'
|
|
134
|
+
? SSEEndpoint<Input, Output>
|
|
135
|
+
: Type extends 'stream'
|
|
136
|
+
? StreamEndpoint<Input, Output>
|
|
137
|
+
: APIEndpoint<Input, Output>;
|
|
138
|
+
|
|
139
|
+
/**
|
|
140
|
+
* Recursively build the client proxy type from a RouteRegistry.
|
|
141
|
+
*/
|
|
142
|
+
export type Client<R> = {
|
|
143
|
+
[K in keyof R]: R[K] extends { input: infer I; output: infer O; type: infer T }
|
|
144
|
+
? RouteEndpoint<I, O, T extends string ? T : 'api'>
|
|
145
|
+
: R[K] extends { input: infer I; output: infer O }
|
|
146
|
+
? RouteEndpoint<I, O, 'api'>
|
|
147
|
+
: R[K] extends object
|
|
148
|
+
? Client<R[K]>
|
|
149
|
+
: never;
|
|
150
|
+
};
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import type { EventHandler, WebSocketClient } from './types';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Create a WebSocket client wrapper with event-based API.
|
|
5
|
+
*/
|
|
6
|
+
export function createWebSocketClient(url: string, protocols?: string | string[]): WebSocketClient {
|
|
7
|
+
const ws = new WebSocket(url, protocols);
|
|
8
|
+
const handlers: {
|
|
9
|
+
message: Set<EventHandler<unknown>>;
|
|
10
|
+
open: Set<EventHandler<Event>>;
|
|
11
|
+
close: Set<EventHandler<CloseEvent>>;
|
|
12
|
+
error: Set<EventHandler<Event>>;
|
|
13
|
+
} = {
|
|
14
|
+
message: new Set(),
|
|
15
|
+
open: new Set(),
|
|
16
|
+
close: new Set(),
|
|
17
|
+
error: new Set(),
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
// Set up native WebSocket event listeners
|
|
21
|
+
ws.addEventListener('message', (event: MessageEvent) => {
|
|
22
|
+
let data: unknown = event.data;
|
|
23
|
+
// Try to parse JSON if data is a string
|
|
24
|
+
if (typeof event.data === 'string') {
|
|
25
|
+
try {
|
|
26
|
+
data = JSON.parse(event.data);
|
|
27
|
+
} catch {
|
|
28
|
+
// Keep as string if not valid JSON
|
|
29
|
+
data = event.data;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
handlers.message.forEach((handler) => handler(data));
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
ws.addEventListener('open', (event: Event) => {
|
|
36
|
+
handlers.open.forEach((handler) => handler(event));
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
ws.addEventListener('close', (event: CloseEvent) => {
|
|
40
|
+
handlers.close.forEach((handler) => handler(event));
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
ws.addEventListener('error', (event: Event) => {
|
|
44
|
+
handlers.error.forEach((handler) => handler(event));
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
return {
|
|
48
|
+
on: ((event: 'message' | 'open' | 'close' | 'error', handler: EventHandler) => {
|
|
49
|
+
handlers[event].add(handler as EventHandler<unknown | Event | CloseEvent>);
|
|
50
|
+
}) as WebSocketClient['on'],
|
|
51
|
+
|
|
52
|
+
send(data: unknown) {
|
|
53
|
+
const payload = typeof data === 'string' ? data : JSON.stringify(data);
|
|
54
|
+
ws.send(payload);
|
|
55
|
+
},
|
|
56
|
+
|
|
57
|
+
close(code?: number, reason?: string) {
|
|
58
|
+
ws.close(code, reason);
|
|
59
|
+
},
|
|
60
|
+
};
|
|
61
|
+
}
|
package/src/index.ts
CHANGED
|
@@ -18,3 +18,15 @@ export {
|
|
|
18
18
|
type EventStreamManagerOptions,
|
|
19
19
|
type EventStreamManagerState,
|
|
20
20
|
} from './eventstream-manager';
|
|
21
|
+
|
|
22
|
+
// Export client implementation (local to this package)
|
|
23
|
+
export { createClient } from './client/index';
|
|
24
|
+
export type {
|
|
25
|
+
Client,
|
|
26
|
+
ClientOptions,
|
|
27
|
+
RouteEndpoint,
|
|
28
|
+
WebSocketClient,
|
|
29
|
+
EventStreamClient,
|
|
30
|
+
StreamClient,
|
|
31
|
+
EventHandler,
|
|
32
|
+
} from './client/types';
|