@spfn/core 0.2.0-beta.11 → 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.
@@ -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 };
@@ -202,6 +202,11 @@ interface ApiConfig {
202
202
  * Per-call options
203
203
  */
204
204
  interface CallOptions {
205
+ /**
206
+ * Request timeout in milliseconds
207
+ * Overrides the global timeout set in ApiConfig
208
+ */
209
+ timeout?: number;
205
210
  /**
206
211
  * Additional headers for this request
207
212
  */