@spfn/core 0.2.0-beta.12 → 0.2.0-beta.13
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/event/sse/client.d.ts +50 -1
- package/dist/event/sse/client.js +57 -22
- package/dist/event/sse/client.js.map +1 -1
- package/dist/event/sse/index.d.ts +10 -4
- package/dist/event/sse/index.js +125 -12
- package/dist/event/sse/index.js.map +1 -1
- package/dist/server/index.d.ts +3 -2
- package/dist/server/index.js +151 -15
- package/dist/server/index.js.map +1 -1
- package/dist/types-B-lVqv6b.d.ts +298 -0
- package/docs/event.md +88 -0
- package/package.json +1 -1
- package/dist/types-B-e_f2dQ.d.ts +0 -121
|
@@ -0,0 +1,298 @@
|
|
|
1
|
+
import { Context } from 'hono';
|
|
2
|
+
import { E as EventRouterDef, I as InferEventNames, b as InferEventPayload } from './router-Di7ENoah.js';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* SSE Token Manager
|
|
6
|
+
*
|
|
7
|
+
* Auth-agnostic token issuance and verification for SSE connections.
|
|
8
|
+
* Issues one-time-use tokens with TTL for Token Exchange pattern.
|
|
9
|
+
*
|
|
10
|
+
* @example
|
|
11
|
+
* ```typescript
|
|
12
|
+
* const manager = new SSETokenManager({ ttl: 30000 });
|
|
13
|
+
*
|
|
14
|
+
* // Issue token for authenticated user
|
|
15
|
+
* const token = await manager.issue('user-123');
|
|
16
|
+
*
|
|
17
|
+
* // Verify and consume token (one-time use)
|
|
18
|
+
* const subject = await manager.verify(token); // 'user-123'
|
|
19
|
+
* const again = await manager.verify(token); // null (already consumed)
|
|
20
|
+
*
|
|
21
|
+
* // Cleanup on shutdown
|
|
22
|
+
* manager.destroy();
|
|
23
|
+
* ```
|
|
24
|
+
*/
|
|
25
|
+
/**
|
|
26
|
+
* Stored SSE token data
|
|
27
|
+
*/
|
|
28
|
+
interface SSEToken {
|
|
29
|
+
token: string;
|
|
30
|
+
subject: string;
|
|
31
|
+
expiresAt: number;
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Token storage interface
|
|
35
|
+
*
|
|
36
|
+
* Implement this for custom storage backends (e.g., Redis for multi-instance).
|
|
37
|
+
*/
|
|
38
|
+
interface SSETokenStore {
|
|
39
|
+
/** Store a token */
|
|
40
|
+
set(token: string, data: SSEToken): Promise<void>;
|
|
41
|
+
/** Get and delete a token (one-time use) */
|
|
42
|
+
consume(token: string): Promise<SSEToken | null>;
|
|
43
|
+
/** Remove expired tokens */
|
|
44
|
+
cleanup(): Promise<void>;
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* SSETokenManager configuration
|
|
48
|
+
*/
|
|
49
|
+
interface SSETokenManagerConfig {
|
|
50
|
+
/**
|
|
51
|
+
* Token time-to-live in milliseconds
|
|
52
|
+
* @default 30000
|
|
53
|
+
*/
|
|
54
|
+
ttl?: number;
|
|
55
|
+
/**
|
|
56
|
+
* Custom token store (default: in-memory Map)
|
|
57
|
+
*/
|
|
58
|
+
store?: SSETokenStore;
|
|
59
|
+
/**
|
|
60
|
+
* Cleanup interval in milliseconds
|
|
61
|
+
* @default 60000
|
|
62
|
+
*/
|
|
63
|
+
cleanupInterval?: number;
|
|
64
|
+
}
|
|
65
|
+
declare class SSETokenManager {
|
|
66
|
+
private store;
|
|
67
|
+
private ttl;
|
|
68
|
+
private cleanupTimer;
|
|
69
|
+
constructor(config?: SSETokenManagerConfig);
|
|
70
|
+
/**
|
|
71
|
+
* Issue a new one-time-use token for the given subject
|
|
72
|
+
*/
|
|
73
|
+
issue(subject: string): Promise<string>;
|
|
74
|
+
/**
|
|
75
|
+
* Verify and consume a token
|
|
76
|
+
* @returns subject string if valid, null if invalid/expired/already consumed
|
|
77
|
+
*/
|
|
78
|
+
verify(token: string): Promise<string | null>;
|
|
79
|
+
/**
|
|
80
|
+
* Cleanup timer and resources
|
|
81
|
+
*/
|
|
82
|
+
destroy(): void;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* SSE Types
|
|
87
|
+
*
|
|
88
|
+
* Type definitions for Server-Sent Events
|
|
89
|
+
*/
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* SSE message sent from server
|
|
93
|
+
*/
|
|
94
|
+
interface SSEMessage<TEvent extends string = string, TPayload = unknown> {
|
|
95
|
+
/** Event name */
|
|
96
|
+
event: TEvent;
|
|
97
|
+
/** Event payload */
|
|
98
|
+
data: TPayload;
|
|
99
|
+
/** Optional message ID for reconnection */
|
|
100
|
+
id?: string;
|
|
101
|
+
}
|
|
102
|
+
/**
|
|
103
|
+
* SSE auth configuration (internal, non-generic)
|
|
104
|
+
*
|
|
105
|
+
* Stored in SSEHandlerConfig. Generic user-facing version is SSEAuthConfig.
|
|
106
|
+
*/
|
|
107
|
+
interface SSEHandlerAuthConfig {
|
|
108
|
+
/**
|
|
109
|
+
* Enable SSE token authentication
|
|
110
|
+
* @default false
|
|
111
|
+
*/
|
|
112
|
+
enabled?: boolean;
|
|
113
|
+
/**
|
|
114
|
+
* Token TTL in milliseconds
|
|
115
|
+
* @default 30000
|
|
116
|
+
*/
|
|
117
|
+
tokenTtl?: number;
|
|
118
|
+
/**
|
|
119
|
+
* Custom token store (e.g., Redis for multi-instance)
|
|
120
|
+
*/
|
|
121
|
+
store?: SSETokenStore;
|
|
122
|
+
/**
|
|
123
|
+
* Extract subject (user ID) from Hono context
|
|
124
|
+
* @default (c) => c.get('auth')?.userId ?? null
|
|
125
|
+
*/
|
|
126
|
+
getSubject?: (c: Context) => string | null;
|
|
127
|
+
/**
|
|
128
|
+
* Subscription authorization hook (called once on connect)
|
|
129
|
+
*
|
|
130
|
+
* Return allowed events subset. Empty array = 403 rejection.
|
|
131
|
+
*/
|
|
132
|
+
authorize?: (subject: string, events: string[]) => Promise<string[]> | string[];
|
|
133
|
+
/**
|
|
134
|
+
* Per-event payload filter map (called on every event emission)
|
|
135
|
+
*
|
|
136
|
+
* Return false to skip sending the event to this user.
|
|
137
|
+
*/
|
|
138
|
+
filter?: Record<string, (subject: string, payload: unknown) => boolean>;
|
|
139
|
+
}
|
|
140
|
+
/**
|
|
141
|
+
* SSE auth configuration (user-facing, generic)
|
|
142
|
+
*
|
|
143
|
+
* Provides type-safe event names and payload inference from EventRouter.
|
|
144
|
+
*
|
|
145
|
+
* @example
|
|
146
|
+
* ```typescript
|
|
147
|
+
* .events(eventRouter, {
|
|
148
|
+
* auth: {
|
|
149
|
+
* enabled: true,
|
|
150
|
+
* authorize: async (subject, events) => {
|
|
151
|
+
* // events: ('userCreated' | 'orderUpdated')[]
|
|
152
|
+
* return events.filter(e => hasPermission(subject, e));
|
|
153
|
+
* },
|
|
154
|
+
* filter: {
|
|
155
|
+
* orderUpdated: (subject, payload) => {
|
|
156
|
+
* // payload: { orderId: string; userId: string }
|
|
157
|
+
* return payload.userId === subject;
|
|
158
|
+
* },
|
|
159
|
+
* },
|
|
160
|
+
* },
|
|
161
|
+
* })
|
|
162
|
+
* ```
|
|
163
|
+
*/
|
|
164
|
+
interface SSEAuthConfig<TRouter extends EventRouterDef<any>> {
|
|
165
|
+
enabled?: boolean;
|
|
166
|
+
tokenTtl?: number;
|
|
167
|
+
store?: SSETokenStore;
|
|
168
|
+
getSubject?: (c: Context) => string | null;
|
|
169
|
+
authorize?: (subject: string, events: InferEventNames<TRouter>[]) => Promise<InferEventNames<TRouter>[]> | InferEventNames<TRouter>[];
|
|
170
|
+
filter?: {
|
|
171
|
+
[K in InferEventNames<TRouter>]?: (subject: string, payload: InferEventPayload<TRouter, K>) => boolean;
|
|
172
|
+
};
|
|
173
|
+
}
|
|
174
|
+
/**
|
|
175
|
+
* SSE Handler configuration
|
|
176
|
+
*/
|
|
177
|
+
interface SSEHandlerConfig {
|
|
178
|
+
/**
|
|
179
|
+
* Keep-alive ping interval in milliseconds
|
|
180
|
+
* @default 30000
|
|
181
|
+
*/
|
|
182
|
+
pingInterval?: number;
|
|
183
|
+
/**
|
|
184
|
+
* Custom headers for SSE response
|
|
185
|
+
*/
|
|
186
|
+
headers?: Record<string, string>;
|
|
187
|
+
/**
|
|
188
|
+
* Authentication and authorization configuration
|
|
189
|
+
*/
|
|
190
|
+
auth?: SSEHandlerAuthConfig;
|
|
191
|
+
}
|
|
192
|
+
/**
|
|
193
|
+
* SSE Client configuration
|
|
194
|
+
*/
|
|
195
|
+
interface SSEClientConfig {
|
|
196
|
+
/**
|
|
197
|
+
* Backend API host URL
|
|
198
|
+
* @default NEXT_PUBLIC_SPFN_API_URL || 'http://localhost:8790'
|
|
199
|
+
* @example 'http://localhost:8790'
|
|
200
|
+
* @example 'https://api.example.com'
|
|
201
|
+
*/
|
|
202
|
+
host?: string;
|
|
203
|
+
/**
|
|
204
|
+
* SSE endpoint pathname
|
|
205
|
+
* @default '/events/stream'
|
|
206
|
+
*/
|
|
207
|
+
pathname?: string;
|
|
208
|
+
/**
|
|
209
|
+
* Full URL (overrides host + pathname)
|
|
210
|
+
* @deprecated Use host and pathname instead
|
|
211
|
+
* @example 'http://localhost:8790/events/stream'
|
|
212
|
+
*/
|
|
213
|
+
url?: string;
|
|
214
|
+
/**
|
|
215
|
+
* Auto reconnect on disconnect
|
|
216
|
+
* @default true
|
|
217
|
+
*/
|
|
218
|
+
reconnect?: boolean;
|
|
219
|
+
/**
|
|
220
|
+
* Reconnect delay in milliseconds
|
|
221
|
+
* @default 3000
|
|
222
|
+
*/
|
|
223
|
+
reconnectDelay?: number;
|
|
224
|
+
/**
|
|
225
|
+
* Maximum reconnect attempts (0 = infinite)
|
|
226
|
+
* @default 0
|
|
227
|
+
*/
|
|
228
|
+
maxReconnectAttempts?: number;
|
|
229
|
+
/**
|
|
230
|
+
* Include credentials (cookies) in request
|
|
231
|
+
* @default false
|
|
232
|
+
*/
|
|
233
|
+
withCredentials?: boolean;
|
|
234
|
+
/**
|
|
235
|
+
* Acquire a one-time SSE token before connecting.
|
|
236
|
+
*
|
|
237
|
+
* Called on every (re)connect. The returned token is appended
|
|
238
|
+
* to the SSE URL as `?token=...`.
|
|
239
|
+
*
|
|
240
|
+
* @example
|
|
241
|
+
* ```typescript
|
|
242
|
+
* acquireToken: async () => {
|
|
243
|
+
* const res = await fetch('/api/events/token', {
|
|
244
|
+
* method: 'POST',
|
|
245
|
+
* credentials: 'include',
|
|
246
|
+
* });
|
|
247
|
+
* const data = await res.json();
|
|
248
|
+
* return data.token;
|
|
249
|
+
* }
|
|
250
|
+
* ```
|
|
251
|
+
*/
|
|
252
|
+
acquireToken?: () => Promise<string>;
|
|
253
|
+
}
|
|
254
|
+
/**
|
|
255
|
+
* Event handler function
|
|
256
|
+
*/
|
|
257
|
+
type SSEEventHandler<TPayload> = (payload: TPayload) => void;
|
|
258
|
+
/**
|
|
259
|
+
* Event handlers map for EventRouter
|
|
260
|
+
*/
|
|
261
|
+
type SSEEventHandlers<TRouter extends EventRouterDef<any>> = {
|
|
262
|
+
[K in InferEventNames<TRouter>]?: SSEEventHandler<InferEventPayload<TRouter, K>>;
|
|
263
|
+
};
|
|
264
|
+
/**
|
|
265
|
+
* Subscription options
|
|
266
|
+
*/
|
|
267
|
+
interface SSESubscribeOptions<TRouter extends EventRouterDef<any>> {
|
|
268
|
+
/**
|
|
269
|
+
* Events to subscribe
|
|
270
|
+
*/
|
|
271
|
+
events: InferEventNames<TRouter>[];
|
|
272
|
+
/**
|
|
273
|
+
* Event handlers
|
|
274
|
+
*/
|
|
275
|
+
handlers: SSEEventHandlers<TRouter>;
|
|
276
|
+
/**
|
|
277
|
+
* Called when connection opens
|
|
278
|
+
*/
|
|
279
|
+
onOpen?: () => void;
|
|
280
|
+
/**
|
|
281
|
+
* Called on connection error
|
|
282
|
+
*/
|
|
283
|
+
onError?: (error: Event) => void;
|
|
284
|
+
/**
|
|
285
|
+
* Called when reconnecting
|
|
286
|
+
*/
|
|
287
|
+
onReconnect?: (attempt: number) => void;
|
|
288
|
+
}
|
|
289
|
+
/**
|
|
290
|
+
* SSE connection state
|
|
291
|
+
*/
|
|
292
|
+
type SSEConnectionState = 'connecting' | 'open' | 'closed' | 'error';
|
|
293
|
+
/**
|
|
294
|
+
* Unsubscribe function
|
|
295
|
+
*/
|
|
296
|
+
type SSEUnsubscribe = () => void;
|
|
297
|
+
|
|
298
|
+
export { type SSEHandlerConfig as S, type SSEAuthConfig as a, SSETokenManager as b, type SSEToken as c, type SSETokenStore as d, type SSETokenManagerConfig as e, type SSEMessage as f, type SSEHandlerAuthConfig as g, type SSEClientConfig as h, type SSEEventHandler as i, type SSEEventHandlers as j, type SSESubscribeOptions as k, type SSEConnectionState as l, type SSEUnsubscribe as m };
|
package/docs/event.md
CHANGED
|
@@ -99,6 +99,64 @@ export default defineServerConfig()
|
|
|
99
99
|
})
|
|
100
100
|
```
|
|
101
101
|
|
|
102
|
+
## SSE Authentication
|
|
103
|
+
|
|
104
|
+
Browser `EventSource` API does not support custom headers. SPFN uses a **Token Exchange** pattern:
|
|
105
|
+
|
|
106
|
+
1. Client sends `POST /events/token` with Bearer JWT
|
|
107
|
+
2. Server returns a one-time token (30s TTL)
|
|
108
|
+
3. Client connects to `GET /events/stream?token=...&events=...`
|
|
109
|
+
|
|
110
|
+
```typescript
|
|
111
|
+
// server.config.ts
|
|
112
|
+
import { defineServerConfig } from '@spfn/core/server';
|
|
113
|
+
import { authenticate } from '@spfn/auth/server';
|
|
114
|
+
|
|
115
|
+
export default defineServerConfig()
|
|
116
|
+
.middlewares([authenticate])
|
|
117
|
+
.routes(appRouter)
|
|
118
|
+
.events(eventRouter, {
|
|
119
|
+
auth: { enabled: true }, // This is all you need
|
|
120
|
+
})
|
|
121
|
+
.build();
|
|
122
|
+
// → POST /events/token (protected by authenticate middleware)
|
|
123
|
+
// → GET /events/stream?token=...&events=... (token verified)
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
### Authorization Hooks
|
|
127
|
+
|
|
128
|
+
#### `authorize` — Subscription Authorization (once on connect)
|
|
129
|
+
|
|
130
|
+
```typescript
|
|
131
|
+
.events(eventRouter, {
|
|
132
|
+
auth: {
|
|
133
|
+
enabled: true,
|
|
134
|
+
authorize: async (subject, events) =>
|
|
135
|
+
{
|
|
136
|
+
// events: ('userCreated' | 'orderPlaced')[] — type inferred
|
|
137
|
+
const user = await usersRepository.findById(subject);
|
|
138
|
+
if (user.role === 'admin') return events;
|
|
139
|
+
return events.filter(e => !e.startsWith('admin.'));
|
|
140
|
+
},
|
|
141
|
+
},
|
|
142
|
+
})
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
#### `filter` — Payload Filtering (on every event emission)
|
|
146
|
+
|
|
147
|
+
```typescript
|
|
148
|
+
.events(eventRouter, {
|
|
149
|
+
auth: {
|
|
150
|
+
enabled: true,
|
|
151
|
+
filter: {
|
|
152
|
+
// payload type inferred per-event — no casting needed
|
|
153
|
+
orderPlaced: (subject, payload) => payload.userId === subject,
|
|
154
|
+
// userCreated: no filter → sent to all authenticated users
|
|
155
|
+
},
|
|
156
|
+
},
|
|
157
|
+
})
|
|
158
|
+
```
|
|
159
|
+
|
|
102
160
|
## Browser Client
|
|
103
161
|
|
|
104
162
|
```typescript
|
|
@@ -135,6 +193,24 @@ const unsubscribe = client.subscribe({
|
|
|
135
193
|
unsubscribe();
|
|
136
194
|
```
|
|
137
195
|
|
|
196
|
+
### With Authentication
|
|
197
|
+
|
|
198
|
+
```typescript
|
|
199
|
+
const client = createSSEClient<EventRouter>({
|
|
200
|
+
acquireToken: async () =>
|
|
201
|
+
{
|
|
202
|
+
const res = await fetch('/api/events/token', {
|
|
203
|
+
method: 'POST',
|
|
204
|
+
credentials: 'include',
|
|
205
|
+
});
|
|
206
|
+
const data = await res.json();
|
|
207
|
+
return data.token;
|
|
208
|
+
},
|
|
209
|
+
});
|
|
210
|
+
```
|
|
211
|
+
|
|
212
|
+
`acquireToken` is called on every (re)connect — one-time tokens are handled automatically.
|
|
213
|
+
|
|
138
214
|
## Simple Subscribe Helper
|
|
139
215
|
|
|
140
216
|
```typescript
|
|
@@ -267,6 +343,18 @@ await userCreated.emit({ userId: '123' });
|
|
|
267
343
|
| `reconnectDelay` | number | `3000` | Reconnect delay (ms) |
|
|
268
344
|
| `maxReconnectAttempts` | number | `0` | Max attempts (0 = infinite) |
|
|
269
345
|
| `withCredentials` | boolean | `false` | Include cookies |
|
|
346
|
+
| `acquireToken` | () => Promise\<string\> | - | Acquire one-time SSE token before connecting |
|
|
347
|
+
|
|
348
|
+
### SSE Auth Options
|
|
349
|
+
|
|
350
|
+
| Option | Type | Default | Description |
|
|
351
|
+
|--------|------|---------|-------------|
|
|
352
|
+
| `enabled` | boolean | `false` | Enable token authentication |
|
|
353
|
+
| `tokenTtl` | number | `30000` | Token TTL in milliseconds |
|
|
354
|
+
| `store` | SSETokenStore | InMemory | Custom token store (e.g., Redis) |
|
|
355
|
+
| `getSubject` | (c) => string \| null | `c.get('auth')?.userId` | Extract subject from context |
|
|
356
|
+
| `authorize` | (subject, events) => events[] | - | Subscription authorization hook |
|
|
357
|
+
| `filter` | { [event]: (subject, payload) => boolean } | - | Per-event payload filter |
|
|
270
358
|
|
|
271
359
|
## Event Flow Architecture
|
|
272
360
|
|
package/package.json
CHANGED
package/dist/types-B-e_f2dQ.d.ts
DELETED
|
@@ -1,121 +0,0 @@
|
|
|
1
|
-
import { E as EventRouterDef, I as InferEventNames, b as InferEventPayload } from './router-Di7ENoah.js';
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* SSE Types
|
|
5
|
-
*
|
|
6
|
-
* Type definitions for Server-Sent Events
|
|
7
|
-
*/
|
|
8
|
-
|
|
9
|
-
/**
|
|
10
|
-
* SSE message sent from server
|
|
11
|
-
*/
|
|
12
|
-
interface SSEMessage<TEvent extends string = string, TPayload = unknown> {
|
|
13
|
-
/** Event name */
|
|
14
|
-
event: TEvent;
|
|
15
|
-
/** Event payload */
|
|
16
|
-
data: TPayload;
|
|
17
|
-
/** Optional message ID for reconnection */
|
|
18
|
-
id?: string;
|
|
19
|
-
}
|
|
20
|
-
/**
|
|
21
|
-
* SSE Handler configuration
|
|
22
|
-
*/
|
|
23
|
-
interface SSEHandlerConfig {
|
|
24
|
-
/**
|
|
25
|
-
* Keep-alive ping interval in milliseconds
|
|
26
|
-
* @default 30000
|
|
27
|
-
*/
|
|
28
|
-
pingInterval?: number;
|
|
29
|
-
/**
|
|
30
|
-
* Custom headers for SSE response
|
|
31
|
-
*/
|
|
32
|
-
headers?: Record<string, string>;
|
|
33
|
-
}
|
|
34
|
-
/**
|
|
35
|
-
* SSE Client configuration
|
|
36
|
-
*/
|
|
37
|
-
interface SSEClientConfig {
|
|
38
|
-
/**
|
|
39
|
-
* Backend API host URL
|
|
40
|
-
* @default NEXT_PUBLIC_SPFN_API_URL || 'http://localhost:8790'
|
|
41
|
-
* @example 'http://localhost:8790'
|
|
42
|
-
* @example 'https://api.example.com'
|
|
43
|
-
*/
|
|
44
|
-
host?: string;
|
|
45
|
-
/**
|
|
46
|
-
* SSE endpoint pathname
|
|
47
|
-
* @default '/events/stream'
|
|
48
|
-
*/
|
|
49
|
-
pathname?: string;
|
|
50
|
-
/**
|
|
51
|
-
* Full URL (overrides host + pathname)
|
|
52
|
-
* @deprecated Use host and pathname instead
|
|
53
|
-
* @example 'http://localhost:8790/events/stream'
|
|
54
|
-
*/
|
|
55
|
-
url?: string;
|
|
56
|
-
/**
|
|
57
|
-
* Auto reconnect on disconnect
|
|
58
|
-
* @default true
|
|
59
|
-
*/
|
|
60
|
-
reconnect?: boolean;
|
|
61
|
-
/**
|
|
62
|
-
* Reconnect delay in milliseconds
|
|
63
|
-
* @default 3000
|
|
64
|
-
*/
|
|
65
|
-
reconnectDelay?: number;
|
|
66
|
-
/**
|
|
67
|
-
* Maximum reconnect attempts (0 = infinite)
|
|
68
|
-
* @default 0
|
|
69
|
-
*/
|
|
70
|
-
maxReconnectAttempts?: number;
|
|
71
|
-
/**
|
|
72
|
-
* Include credentials (cookies) in request
|
|
73
|
-
* @default false
|
|
74
|
-
*/
|
|
75
|
-
withCredentials?: boolean;
|
|
76
|
-
}
|
|
77
|
-
/**
|
|
78
|
-
* Event handler function
|
|
79
|
-
*/
|
|
80
|
-
type SSEEventHandler<TPayload> = (payload: TPayload) => void;
|
|
81
|
-
/**
|
|
82
|
-
* Event handlers map for EventRouter
|
|
83
|
-
*/
|
|
84
|
-
type SSEEventHandlers<TRouter extends EventRouterDef<any>> = {
|
|
85
|
-
[K in InferEventNames<TRouter>]?: SSEEventHandler<InferEventPayload<TRouter, K>>;
|
|
86
|
-
};
|
|
87
|
-
/**
|
|
88
|
-
* Subscription options
|
|
89
|
-
*/
|
|
90
|
-
interface SSESubscribeOptions<TRouter extends EventRouterDef<any>> {
|
|
91
|
-
/**
|
|
92
|
-
* Events to subscribe
|
|
93
|
-
*/
|
|
94
|
-
events: InferEventNames<TRouter>[];
|
|
95
|
-
/**
|
|
96
|
-
* Event handlers
|
|
97
|
-
*/
|
|
98
|
-
handlers: SSEEventHandlers<TRouter>;
|
|
99
|
-
/**
|
|
100
|
-
* Called when connection opens
|
|
101
|
-
*/
|
|
102
|
-
onOpen?: () => void;
|
|
103
|
-
/**
|
|
104
|
-
* Called on connection error
|
|
105
|
-
*/
|
|
106
|
-
onError?: (error: Event) => void;
|
|
107
|
-
/**
|
|
108
|
-
* Called when reconnecting
|
|
109
|
-
*/
|
|
110
|
-
onReconnect?: (attempt: number) => void;
|
|
111
|
-
}
|
|
112
|
-
/**
|
|
113
|
-
* SSE connection state
|
|
114
|
-
*/
|
|
115
|
-
type SSEConnectionState = 'connecting' | 'open' | 'closed' | 'error';
|
|
116
|
-
/**
|
|
117
|
-
* Unsubscribe function
|
|
118
|
-
*/
|
|
119
|
-
type SSEUnsubscribe = () => void;
|
|
120
|
-
|
|
121
|
-
export type { SSEHandlerConfig as S, SSEMessage as a, SSEClientConfig as b, SSEEventHandler as c, SSEEventHandlers as d, SSESubscribeOptions as e, SSEConnectionState as f, SSEUnsubscribe as g };
|