realtimehttpauthclient 1.0.0

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/README.md ADDED
@@ -0,0 +1,169 @@
1
+ # realtimehttpauthclient
2
+
3
+ A professional and robust HTTP & Realtime client library with automatic authentication lifecycle management, JWT token rotation, CSRF double-submit protection, and Socket.IO integration.
4
+
5
+ Optimized for **React**, **Bun**, **Node.js**, and **TanStack Start**.
6
+
7
+ ---
8
+
9
+ ## Features
10
+ - **Isomorphic Design**: Safe for Server-Side Rendering (SSR) and Server Functions. No "window is not defined" crashes.
11
+ - **REST HTTP Client**: Typed requests with automatic bearer token attachment and automatic request retry on `401 Unauthorized` using a silent HTTP-only refresh token flow.
12
+ - **CSRF Protection**: Automatically retrieves and attaches CSRF tokens from cookies or body payload to mutating requests (`POST`/`PUT`/`PATCH`/`DELETE`).
13
+ - **Socket.IO Manager**: Reconnect support, automatic channel re-joining, memory/localStorage-backed offline event queue, and Promise-based acknowledgments (`emitAck`).
14
+ - **React Context & Hooks**: Integrated state management for authentication (`useAuth`, `useRequireAuth`, `useRBAC`) and WebSockets (`useSocketClient`, `useSocketEvent`, `useRoomEvent`).
15
+
16
+ ---
17
+
18
+ ## Installation
19
+
20
+ ```bash
21
+ # Using npm
22
+ npm install realtimehttpauthclient
23
+
24
+ # Using bun
25
+ bun add realtimehttpauthclient
26
+ ```
27
+
28
+ ---
29
+
30
+ ## Quick Start (Client Side)
31
+
32
+ Initialize the client singleton (e.g., `realtime.ts`):
33
+
34
+ ```typescript
35
+ import { createRealtimeClient } from 'realtimehttpauthclient';
36
+
37
+ const API_URL = 'http://localhost:3000';
38
+
39
+ export const realtimeClient = createRealtimeClient({
40
+ apiBaseUrl: API_URL,
41
+ socketUrl: API_URL,
42
+ withCredentials: true, // Required to send HTTP-only cookies cross-origin
43
+ auth: {
44
+ persistToken: false, // Recommended: keep access token in memory only
45
+ },
46
+ csrf: {
47
+ enabled: true,
48
+ fetchEndpoint: '/api/auth/csrf',
49
+ },
50
+ refresh: {
51
+ enabled: true,
52
+ endpoint: `${API_URL}/api/auth/refresh`,
53
+ },
54
+ });
55
+ ```
56
+
57
+ Provide the context to your React application:
58
+
59
+ ```tsx
60
+ import { RealtimeProvider } from 'realtimehttpauthclient';
61
+ import { realtimeClient } from './realtime';
62
+
63
+ export default function App() {
64
+ return (
65
+ <RealtimeProvider client={realtimeClient} autoConnect={true}>
66
+ <MainLayout />
67
+ </RealtimeProvider>
68
+ );
69
+ }
70
+ ```
71
+
72
+ ---
73
+
74
+ ## 🔒 SSR & TanStack Start Security Guidelines
75
+
76
+ ### 1. Preventing Session Pollution (Session Leaks)
77
+ > [!WARNING]
78
+ > In an isomorphic application (like TanStack Start), global singletons are shared across all requests executing on the server.
79
+ > Calling `realtimeClient.setAccessToken()` or store operations on a shared global instance will leak state between concurrent users!
80
+
81
+ **Best Practice**:
82
+ - In **browser code** (components, client event handlers): It is safe to use the shared global `realtimeClient` singleton.
83
+ - In **server code** (Server Functions, Route Loaders during SSR):
84
+ - Do **not** use `realtimeClient.setAccessToken()`.
85
+ - Configure the client using a request-bound `getAccessToken` getter, or instantiate a new client instance per request.
86
+
87
+ #### Dynamic Request Context Example (TanStack Start):
88
+ ```typescript
89
+ import { createRealtimeClient } from 'realtimehttpauthclient';
90
+ import { getRequestHeader } from '@tanstack/react-start/server';
91
+
92
+ // Isomorphic client definition
93
+ export const realtimeClient = createRealtimeClient({
94
+ apiBaseUrl: 'http://localhost:3000',
95
+ socketUrl: 'http://localhost:3000',
96
+ // getAccessToken is evaluated on every HTTP request
97
+ getAccessToken: async () => {
98
+ if (typeof window === 'undefined') {
99
+ // Server-side: retrieve the token from request headers or server session
100
+ const authHeader = getRequestHeader('Authorization');
101
+ return authHeader ? authHeader.replace('Bearer ', '') : null;
102
+ }
103
+ // Client-side: falls back to the in-memory TokenManager
104
+ return null;
105
+ }
106
+ });
107
+ ```
108
+
109
+ ---
110
+
111
+ ## API Reference
112
+
113
+ ### React Hooks
114
+
115
+ #### `useAuth()`
116
+ Access the reactive authentication state and handlers.
117
+ ```typescript
118
+ const { user, isAuthenticated, isLoading, error, login, logout, register, refetchUser } = useAuth();
119
+ ```
120
+
121
+ #### `useRequireAuth(onRedirect?)`
122
+ Protects a view by redirecting unauthenticated users to `/login`.
123
+ ```typescript
124
+ const auth = useRequireAuth();
125
+ // If using TanStack Router for programmatic redirection:
126
+ const auth = useRequireAuth((targetPath) => navigate({ to: targetPath }));
127
+ ```
128
+
129
+ #### `useRBAC(requiredRole)`
130
+ Guard components based on role.
131
+ ```typescript
132
+ const { hasAccess, user } = useRBAC('admin');
133
+ ```
134
+
135
+ #### `useHttpClient()`
136
+ Access the typed HTTP client with automatic refresh token fallback and CSRF.
137
+ ```typescript
138
+ const http = useHttpClient();
139
+ const users = await http.get('/api/users');
140
+ const newUser = await http.post('/api/users', { name: 'Alice' });
141
+ ```
142
+
143
+ #### `useSocketClient()`
144
+ Interact with the WebSocket manager.
145
+ ```typescript
146
+ const { connected, emit, emitAck, joinRoom, leaveRoom } = useSocketClient();
147
+ ```
148
+
149
+ #### `useSocketEvent(event, callback)`
150
+ Listen to global socket events with automatic cleanup on unmount.
151
+ ```typescript
152
+ useSocketEvent('notification:received', (data) => {
153
+ console.log('Notification:', data);
154
+ });
155
+ ```
156
+
157
+ #### `useRoomEvent(room, event, callback)`
158
+ Automatically joins a room on mount, listens to event, and leaves room on unmount.
159
+ ```typescript
160
+ useRoomEvent('room-123', 'chat:message', (msg) => {
161
+ setMessages((prev) => [...prev, msg]);
162
+ });
163
+ ```
164
+
165
+ ---
166
+
167
+ ## License
168
+
169
+ MIT
@@ -0,0 +1,284 @@
1
+ import React, { ReactNode } from 'react';
2
+ import { ManagerOptions, SocketOptions, Socket } from 'socket.io-client';
3
+
4
+ type HttpMethod = 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE';
5
+ type JsonValue = string | number | boolean | null | JsonValue[] | {
6
+ [key: string]: JsonValue;
7
+ };
8
+ type HeadersMap = Record<string, string>;
9
+ type QueryParams = Record<string, string | number | boolean | null | undefined>;
10
+ type HttpRequestOptions<TBody = unknown> = {
11
+ path: string;
12
+ method?: HttpMethod;
13
+ body?: TBody;
14
+ headers?: HeadersMap;
15
+ query?: QueryParams;
16
+ signal?: AbortSignal;
17
+ credentials?: RequestCredentials;
18
+ retryOnAuth?: boolean;
19
+ };
20
+ type RefreshConfig = {
21
+ enabled?: boolean;
22
+ endpoint: string;
23
+ method?: 'POST' | 'GET';
24
+ onRefreshSuccess?: (tokens: {
25
+ accessToken?: string | null;
26
+ }) => void;
27
+ onRefreshFailure?: () => void;
28
+ onRefreshStart?: () => void;
29
+ onRefreshEnd?: () => void;
30
+ maxRetries?: number;
31
+ };
32
+ type OfflineQueueItem = {
33
+ id: string;
34
+ event: string;
35
+ payload: unknown;
36
+ namespace?: string;
37
+ createdAt: number;
38
+ attempts: number;
39
+ };
40
+ type ConnectionState = {
41
+ connected: boolean;
42
+ connecting: boolean;
43
+ };
44
+ type User = {
45
+ id: string;
46
+ email: string;
47
+ userName: string;
48
+ role: string;
49
+ };
50
+ type AuthState = {
51
+ user: User | null;
52
+ isAuthenticated: boolean;
53
+ isLoading: boolean;
54
+ error: string | null;
55
+ };
56
+ type CsrfConfig = {
57
+ enabled?: boolean;
58
+ cookieName?: string;
59
+ headerName?: string;
60
+ /** Endpoint to call (GET) to receive the csrf-token cookie when it's missing */
61
+ fetchEndpoint?: string;
62
+ };
63
+ type RealtimeClientConfig = {
64
+ apiBaseUrl: string;
65
+ socketUrl: string;
66
+ withCredentials?: boolean;
67
+ enabled?: boolean;
68
+ autoConnect?: boolean;
69
+ socketOptions?: Partial<ManagerOptions & SocketOptions>;
70
+ getAccessToken?: () => Promise<string | null> | string | null;
71
+ setAccessToken?: (token: string | null) => void;
72
+ refresh?: RefreshConfig;
73
+ auth?: {
74
+ loginEndpoint?: string;
75
+ registerEndpoint?: string;
76
+ logoutEndpoint?: string;
77
+ meEndpoint?: string;
78
+ persistToken?: boolean;
79
+ };
80
+ csrf?: CsrfConfig;
81
+ onUnauthorized?: () => void;
82
+ onSocketConnect?: (socket: Socket) => void;
83
+ onSocketDisconnect?: (reason: Socket.DisconnectReason) => void;
84
+ onSocketError?: (error: Error) => void;
85
+ offlineQueue?: {
86
+ enabled?: boolean;
87
+ storageKey?: string;
88
+ maxItems?: number;
89
+ };
90
+ rooms?: {
91
+ autoRejoin?: boolean;
92
+ };
93
+ ackTimeoutMs?: number;
94
+ namespace?: string;
95
+ debug?: boolean;
96
+ };
97
+ type AckResponse<T = unknown> = {
98
+ ok: boolean;
99
+ data?: T;
100
+ error?: {
101
+ message: string;
102
+ code?: string;
103
+ details?: unknown;
104
+ };
105
+ };
106
+ type AckCallback<T = unknown> = (response: AckResponse<T>) => void;
107
+ type SocketEventHandler<T = unknown> = (payload: T) => void;
108
+ declare class TokenManager {
109
+ private accessToken;
110
+ private getTokenFn?;
111
+ private setTokenFn?;
112
+ private persistToken;
113
+ private refreshTimer;
114
+ private onExpiringCb?;
115
+ private tokenChangeListeners;
116
+ constructor(config?: {
117
+ getAccessToken?: RealtimeClientConfig['getAccessToken'];
118
+ setAccessToken?: RealtimeClientConfig['setAccessToken'];
119
+ persistToken?: boolean;
120
+ });
121
+ setGetter(getter?: RealtimeClientConfig['getAccessToken']): void;
122
+ setSetter(setter?: RealtimeClientConfig['setAccessToken']): void;
123
+ onTokenExpiring(cb: () => void): void;
124
+ onTokenChange(cb: (token: string | null) => void): () => void;
125
+ private emitTokenChange;
126
+ setAccessToken(token: string | null): void;
127
+ getAccessTokenSync(): string | null;
128
+ getAccessToken(): Promise<string | null>;
129
+ isTokenExpired(): boolean;
130
+ clearAll(): void;
131
+ }
132
+ declare class RefreshManager {
133
+ private readonly config;
134
+ private readonly tokenManager;
135
+ private readonly onUnauthorized?;
136
+ private refreshing;
137
+ private refreshAttempts;
138
+ private maxRefreshRetries;
139
+ constructor(config: RefreshConfig | undefined, tokenManager: TokenManager, onUnauthorized?: (() => void) | undefined);
140
+ refreshAccessToken(): Promise<string | null>;
141
+ }
142
+ declare class HttpClient {
143
+ private readonly baseUrl;
144
+ private readonly tokenManager;
145
+ private readonly refreshManager;
146
+ private readonly onUnauthorized?;
147
+ private readonly withCredentials;
148
+ private readonly csrfConfig?;
149
+ private csrfTokenMemory;
150
+ private csrfFetchPromise;
151
+ constructor(baseUrl: string, tokenManager: TokenManager, refreshManager: RefreshManager, onUnauthorized?: (() => void) | undefined, withCredentials?: boolean, csrfConfig?: CsrfConfig | undefined);
152
+ /** Fetch the CSRF token from the server (sets the cookie + stores in memory) */
153
+ private fetchCsrfToken;
154
+ /** Public: eagerly pre-fetch the CSRF token (call on app startup) */
155
+ prefetchCsrf(): Promise<void>;
156
+ request<TResponse = unknown, TBody = unknown>(options: HttpRequestOptions<TBody>): Promise<TResponse>;
157
+ get<TResponse = unknown>(path: string, options?: Omit<HttpRequestOptions, 'path' | 'method'>): Promise<TResponse>;
158
+ post<TResponse = unknown, TBody = unknown>(path: string, body?: TBody, options?: Omit<HttpRequestOptions<TBody>, 'path' | 'method' | 'body'>): Promise<TResponse>;
159
+ put<TResponse = unknown, TBody = unknown>(path: string, body?: TBody, options?: Omit<HttpRequestOptions<TBody>, 'path' | 'method' | 'body'>): Promise<TResponse>;
160
+ patch<TResponse = unknown, TBody = unknown>(path: string, body?: TBody, options?: Omit<HttpRequestOptions<TBody>, 'path' | 'method' | 'body'>): Promise<TResponse>;
161
+ delete<TResponse = unknown>(path: string, options?: Omit<HttpRequestOptions, 'path' | 'method'>): Promise<TResponse>;
162
+ }
163
+ declare class SocketManager {
164
+ private readonly config;
165
+ private readonly tokenManager;
166
+ private readonly refreshManager;
167
+ private readonly onUnauthorized?;
168
+ private socket;
169
+ private connectionPromise;
170
+ private rooms;
171
+ private listeners;
172
+ private pendingEmits;
173
+ private connected;
174
+ private connecting;
175
+ private stateListeners;
176
+ private connectionState;
177
+ constructor(config: RealtimeClientConfig, tokenManager: TokenManager, refreshManager: RefreshManager, onUnauthorized?: (() => void) | undefined);
178
+ private log;
179
+ private emitStateChange;
180
+ getConnectionState(): ConnectionState;
181
+ subscribeToState(listener: (state: ConnectionState) => void): () => void;
182
+ isConnected(): boolean;
183
+ isConnecting(): boolean;
184
+ getSocket(): Socket | null;
185
+ updateAuthToken(token: string | null): void;
186
+ connect(): Promise<Socket | null>;
187
+ private registerCoreEvents;
188
+ disconnect(): Promise<void>;
189
+ joinRoom(room: string): void;
190
+ leaveRoom(room: string): void;
191
+ getRooms(): Set<string>;
192
+ private rejoinRooms;
193
+ on<TPayload = unknown>(event: string, handler: SocketEventHandler<TPayload>): () => void;
194
+ off<TPayload = unknown>(event: string, handler: SocketEventHandler<TPayload>): void;
195
+ once<TPayload = unknown>(event: string, handler: SocketEventHandler<TPayload>): void;
196
+ emit(event: string, payload?: unknown): void;
197
+ emitWithAck<TResponse = unknown>(event: string, payload?: unknown, timeoutMs?: number): Promise<TResponse>;
198
+ flushOfflineQueue(): Promise<void>;
199
+ }
200
+ declare class AuthManager {
201
+ private readonly http;
202
+ private readonly tokenManager;
203
+ private readonly refreshManager;
204
+ private readonly config;
205
+ private readonly onLogout?;
206
+ private state;
207
+ private listeners;
208
+ constructor(http: HttpClient, tokenManager: TokenManager, refreshManager: RefreshManager, config: RealtimeClientConfig['auth'], onLogout?: (() => void) | undefined);
209
+ private emitChange;
210
+ getAuthState(): AuthState;
211
+ subscribe(listener: (state: AuthState) => void): () => void;
212
+ private updateState;
213
+ isAuthenticated(): boolean;
214
+ login(email: string, password: string): Promise<User>;
215
+ register(data: Record<string, unknown>): Promise<User>;
216
+ logout(): Promise<void>;
217
+ getMe(): Promise<User | null>;
218
+ }
219
+ type RealtimeClient = {
220
+ http: HttpClient;
221
+ socket: SocketManager;
222
+ token: TokenManager;
223
+ refresh: RefreshManager;
224
+ auth: AuthManager;
225
+ connect: () => Promise<Socket | null>;
226
+ disconnect: () => Promise<void>;
227
+ getSocket: () => Socket | null;
228
+ on: <T = unknown>(event: string, handler: SocketEventHandler<T>) => () => void;
229
+ once: <T = unknown>(event: string, handler: SocketEventHandler<T>) => void;
230
+ off: <T = unknown>(event: string, handler: SocketEventHandler<T>) => void;
231
+ emit: (event: string, payload?: unknown) => void;
232
+ emitAck: <T = unknown>(event: string, payload?: unknown, timeoutMs?: number) => Promise<T>;
233
+ joinRoom: (room: string) => void;
234
+ leaveRoom: (room: string) => void;
235
+ getRooms: () => Set<string>;
236
+ isConnected: () => boolean;
237
+ isConnecting: () => boolean;
238
+ setAccessToken: (token: string | null) => void;
239
+ getAccessToken: () => Promise<string | null>;
240
+ subscribeToState: (listener: (state: ConnectionState) => void) => () => void;
241
+ getConnectionState: () => ConnectionState;
242
+ };
243
+ declare function createRealtimeClient(config: RealtimeClientConfig): RealtimeClient;
244
+ type RealtimeContextType = ReturnType<typeof createRealtimeClient> | null;
245
+ type RealtimeProviderProps = {
246
+ client: RealtimeClient;
247
+ children: ReactNode;
248
+ autoConnect?: boolean;
249
+ };
250
+ declare function RealtimeProvider({ client, children, autoConnect }: RealtimeProviderProps): React.FunctionComponentElement<React.ProviderProps<RealtimeContextType>>;
251
+ type UseSocketClientResult = {
252
+ socket: Socket | null;
253
+ connected: boolean;
254
+ connecting: boolean;
255
+ emit: (event: string, payload?: unknown) => void;
256
+ emitAck: <T = unknown>(event: string, payload?: unknown, timeoutMs?: number) => Promise<T>;
257
+ on: <T = unknown>(event: string, handler: SocketEventHandler<T>) => () => void;
258
+ once: <T = unknown>(event: string, handler: SocketEventHandler<T>) => void;
259
+ off: <T = unknown>(event: string, handler: SocketEventHandler<T>) => void;
260
+ joinRoom: (room: string) => void;
261
+ leaveRoom: (room: string) => void;
262
+ getRooms: () => Set<string>;
263
+ };
264
+ declare function useRealtimeClient(): RealtimeClient;
265
+ declare function useHttpClient(): HttpClient;
266
+ declare function useConnectionState(): ConnectionState;
267
+ declare function useSocketClient(): UseSocketClientResult;
268
+ declare function useSocketEvent<TPayload = unknown>(event: string, handler: (payload: TPayload) => void): void;
269
+ declare function useRoomEvent<TPayload = unknown>(room: string, event: string, handler: (payload: TPayload) => void): void;
270
+ type UseAuthResult = AuthState & {
271
+ login: (email: string, password: string) => Promise<User>;
272
+ register: (data: Record<string, unknown>) => Promise<User>;
273
+ logout: () => Promise<void>;
274
+ refetchUser: () => Promise<User | null>;
275
+ };
276
+ declare function useAuth(): UseAuthResult;
277
+ declare function useRequireAuth(onRedirect?: (redirectPath: string) => void): UseAuthResult;
278
+ declare function useRBAC(requiredRole: string): {
279
+ hasAccess: boolean;
280
+ user: User | null;
281
+ isLoading: boolean;
282
+ };
283
+
284
+ export { type AckCallback, type AckResponse, AuthManager, type AuthState, type ConnectionState, type CsrfConfig, type HeadersMap, HttpClient, type HttpMethod, type HttpRequestOptions, type JsonValue, type OfflineQueueItem, type QueryParams, type RealtimeClient, type RealtimeClientConfig, RealtimeProvider, type RealtimeProviderProps, type RefreshConfig, RefreshManager, type SocketEventHandler, SocketManager, TokenManager, type UseAuthResult, type UseSocketClientResult, type User, createRealtimeClient, useAuth, useConnectionState, useHttpClient, useRBAC, useRealtimeClient, useRequireAuth, useRoomEvent, useSocketClient, useSocketEvent };
@@ -0,0 +1,284 @@
1
+ import React, { ReactNode } from 'react';
2
+ import { ManagerOptions, SocketOptions, Socket } from 'socket.io-client';
3
+
4
+ type HttpMethod = 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE';
5
+ type JsonValue = string | number | boolean | null | JsonValue[] | {
6
+ [key: string]: JsonValue;
7
+ };
8
+ type HeadersMap = Record<string, string>;
9
+ type QueryParams = Record<string, string | number | boolean | null | undefined>;
10
+ type HttpRequestOptions<TBody = unknown> = {
11
+ path: string;
12
+ method?: HttpMethod;
13
+ body?: TBody;
14
+ headers?: HeadersMap;
15
+ query?: QueryParams;
16
+ signal?: AbortSignal;
17
+ credentials?: RequestCredentials;
18
+ retryOnAuth?: boolean;
19
+ };
20
+ type RefreshConfig = {
21
+ enabled?: boolean;
22
+ endpoint: string;
23
+ method?: 'POST' | 'GET';
24
+ onRefreshSuccess?: (tokens: {
25
+ accessToken?: string | null;
26
+ }) => void;
27
+ onRefreshFailure?: () => void;
28
+ onRefreshStart?: () => void;
29
+ onRefreshEnd?: () => void;
30
+ maxRetries?: number;
31
+ };
32
+ type OfflineQueueItem = {
33
+ id: string;
34
+ event: string;
35
+ payload: unknown;
36
+ namespace?: string;
37
+ createdAt: number;
38
+ attempts: number;
39
+ };
40
+ type ConnectionState = {
41
+ connected: boolean;
42
+ connecting: boolean;
43
+ };
44
+ type User = {
45
+ id: string;
46
+ email: string;
47
+ userName: string;
48
+ role: string;
49
+ };
50
+ type AuthState = {
51
+ user: User | null;
52
+ isAuthenticated: boolean;
53
+ isLoading: boolean;
54
+ error: string | null;
55
+ };
56
+ type CsrfConfig = {
57
+ enabled?: boolean;
58
+ cookieName?: string;
59
+ headerName?: string;
60
+ /** Endpoint to call (GET) to receive the csrf-token cookie when it's missing */
61
+ fetchEndpoint?: string;
62
+ };
63
+ type RealtimeClientConfig = {
64
+ apiBaseUrl: string;
65
+ socketUrl: string;
66
+ withCredentials?: boolean;
67
+ enabled?: boolean;
68
+ autoConnect?: boolean;
69
+ socketOptions?: Partial<ManagerOptions & SocketOptions>;
70
+ getAccessToken?: () => Promise<string | null> | string | null;
71
+ setAccessToken?: (token: string | null) => void;
72
+ refresh?: RefreshConfig;
73
+ auth?: {
74
+ loginEndpoint?: string;
75
+ registerEndpoint?: string;
76
+ logoutEndpoint?: string;
77
+ meEndpoint?: string;
78
+ persistToken?: boolean;
79
+ };
80
+ csrf?: CsrfConfig;
81
+ onUnauthorized?: () => void;
82
+ onSocketConnect?: (socket: Socket) => void;
83
+ onSocketDisconnect?: (reason: Socket.DisconnectReason) => void;
84
+ onSocketError?: (error: Error) => void;
85
+ offlineQueue?: {
86
+ enabled?: boolean;
87
+ storageKey?: string;
88
+ maxItems?: number;
89
+ };
90
+ rooms?: {
91
+ autoRejoin?: boolean;
92
+ };
93
+ ackTimeoutMs?: number;
94
+ namespace?: string;
95
+ debug?: boolean;
96
+ };
97
+ type AckResponse<T = unknown> = {
98
+ ok: boolean;
99
+ data?: T;
100
+ error?: {
101
+ message: string;
102
+ code?: string;
103
+ details?: unknown;
104
+ };
105
+ };
106
+ type AckCallback<T = unknown> = (response: AckResponse<T>) => void;
107
+ type SocketEventHandler<T = unknown> = (payload: T) => void;
108
+ declare class TokenManager {
109
+ private accessToken;
110
+ private getTokenFn?;
111
+ private setTokenFn?;
112
+ private persistToken;
113
+ private refreshTimer;
114
+ private onExpiringCb?;
115
+ private tokenChangeListeners;
116
+ constructor(config?: {
117
+ getAccessToken?: RealtimeClientConfig['getAccessToken'];
118
+ setAccessToken?: RealtimeClientConfig['setAccessToken'];
119
+ persistToken?: boolean;
120
+ });
121
+ setGetter(getter?: RealtimeClientConfig['getAccessToken']): void;
122
+ setSetter(setter?: RealtimeClientConfig['setAccessToken']): void;
123
+ onTokenExpiring(cb: () => void): void;
124
+ onTokenChange(cb: (token: string | null) => void): () => void;
125
+ private emitTokenChange;
126
+ setAccessToken(token: string | null): void;
127
+ getAccessTokenSync(): string | null;
128
+ getAccessToken(): Promise<string | null>;
129
+ isTokenExpired(): boolean;
130
+ clearAll(): void;
131
+ }
132
+ declare class RefreshManager {
133
+ private readonly config;
134
+ private readonly tokenManager;
135
+ private readonly onUnauthorized?;
136
+ private refreshing;
137
+ private refreshAttempts;
138
+ private maxRefreshRetries;
139
+ constructor(config: RefreshConfig | undefined, tokenManager: TokenManager, onUnauthorized?: (() => void) | undefined);
140
+ refreshAccessToken(): Promise<string | null>;
141
+ }
142
+ declare class HttpClient {
143
+ private readonly baseUrl;
144
+ private readonly tokenManager;
145
+ private readonly refreshManager;
146
+ private readonly onUnauthorized?;
147
+ private readonly withCredentials;
148
+ private readonly csrfConfig?;
149
+ private csrfTokenMemory;
150
+ private csrfFetchPromise;
151
+ constructor(baseUrl: string, tokenManager: TokenManager, refreshManager: RefreshManager, onUnauthorized?: (() => void) | undefined, withCredentials?: boolean, csrfConfig?: CsrfConfig | undefined);
152
+ /** Fetch the CSRF token from the server (sets the cookie + stores in memory) */
153
+ private fetchCsrfToken;
154
+ /** Public: eagerly pre-fetch the CSRF token (call on app startup) */
155
+ prefetchCsrf(): Promise<void>;
156
+ request<TResponse = unknown, TBody = unknown>(options: HttpRequestOptions<TBody>): Promise<TResponse>;
157
+ get<TResponse = unknown>(path: string, options?: Omit<HttpRequestOptions, 'path' | 'method'>): Promise<TResponse>;
158
+ post<TResponse = unknown, TBody = unknown>(path: string, body?: TBody, options?: Omit<HttpRequestOptions<TBody>, 'path' | 'method' | 'body'>): Promise<TResponse>;
159
+ put<TResponse = unknown, TBody = unknown>(path: string, body?: TBody, options?: Omit<HttpRequestOptions<TBody>, 'path' | 'method' | 'body'>): Promise<TResponse>;
160
+ patch<TResponse = unknown, TBody = unknown>(path: string, body?: TBody, options?: Omit<HttpRequestOptions<TBody>, 'path' | 'method' | 'body'>): Promise<TResponse>;
161
+ delete<TResponse = unknown>(path: string, options?: Omit<HttpRequestOptions, 'path' | 'method'>): Promise<TResponse>;
162
+ }
163
+ declare class SocketManager {
164
+ private readonly config;
165
+ private readonly tokenManager;
166
+ private readonly refreshManager;
167
+ private readonly onUnauthorized?;
168
+ private socket;
169
+ private connectionPromise;
170
+ private rooms;
171
+ private listeners;
172
+ private pendingEmits;
173
+ private connected;
174
+ private connecting;
175
+ private stateListeners;
176
+ private connectionState;
177
+ constructor(config: RealtimeClientConfig, tokenManager: TokenManager, refreshManager: RefreshManager, onUnauthorized?: (() => void) | undefined);
178
+ private log;
179
+ private emitStateChange;
180
+ getConnectionState(): ConnectionState;
181
+ subscribeToState(listener: (state: ConnectionState) => void): () => void;
182
+ isConnected(): boolean;
183
+ isConnecting(): boolean;
184
+ getSocket(): Socket | null;
185
+ updateAuthToken(token: string | null): void;
186
+ connect(): Promise<Socket | null>;
187
+ private registerCoreEvents;
188
+ disconnect(): Promise<void>;
189
+ joinRoom(room: string): void;
190
+ leaveRoom(room: string): void;
191
+ getRooms(): Set<string>;
192
+ private rejoinRooms;
193
+ on<TPayload = unknown>(event: string, handler: SocketEventHandler<TPayload>): () => void;
194
+ off<TPayload = unknown>(event: string, handler: SocketEventHandler<TPayload>): void;
195
+ once<TPayload = unknown>(event: string, handler: SocketEventHandler<TPayload>): void;
196
+ emit(event: string, payload?: unknown): void;
197
+ emitWithAck<TResponse = unknown>(event: string, payload?: unknown, timeoutMs?: number): Promise<TResponse>;
198
+ flushOfflineQueue(): Promise<void>;
199
+ }
200
+ declare class AuthManager {
201
+ private readonly http;
202
+ private readonly tokenManager;
203
+ private readonly refreshManager;
204
+ private readonly config;
205
+ private readonly onLogout?;
206
+ private state;
207
+ private listeners;
208
+ constructor(http: HttpClient, tokenManager: TokenManager, refreshManager: RefreshManager, config: RealtimeClientConfig['auth'], onLogout?: (() => void) | undefined);
209
+ private emitChange;
210
+ getAuthState(): AuthState;
211
+ subscribe(listener: (state: AuthState) => void): () => void;
212
+ private updateState;
213
+ isAuthenticated(): boolean;
214
+ login(email: string, password: string): Promise<User>;
215
+ register(data: Record<string, unknown>): Promise<User>;
216
+ logout(): Promise<void>;
217
+ getMe(): Promise<User | null>;
218
+ }
219
+ type RealtimeClient = {
220
+ http: HttpClient;
221
+ socket: SocketManager;
222
+ token: TokenManager;
223
+ refresh: RefreshManager;
224
+ auth: AuthManager;
225
+ connect: () => Promise<Socket | null>;
226
+ disconnect: () => Promise<void>;
227
+ getSocket: () => Socket | null;
228
+ on: <T = unknown>(event: string, handler: SocketEventHandler<T>) => () => void;
229
+ once: <T = unknown>(event: string, handler: SocketEventHandler<T>) => void;
230
+ off: <T = unknown>(event: string, handler: SocketEventHandler<T>) => void;
231
+ emit: (event: string, payload?: unknown) => void;
232
+ emitAck: <T = unknown>(event: string, payload?: unknown, timeoutMs?: number) => Promise<T>;
233
+ joinRoom: (room: string) => void;
234
+ leaveRoom: (room: string) => void;
235
+ getRooms: () => Set<string>;
236
+ isConnected: () => boolean;
237
+ isConnecting: () => boolean;
238
+ setAccessToken: (token: string | null) => void;
239
+ getAccessToken: () => Promise<string | null>;
240
+ subscribeToState: (listener: (state: ConnectionState) => void) => () => void;
241
+ getConnectionState: () => ConnectionState;
242
+ };
243
+ declare function createRealtimeClient(config: RealtimeClientConfig): RealtimeClient;
244
+ type RealtimeContextType = ReturnType<typeof createRealtimeClient> | null;
245
+ type RealtimeProviderProps = {
246
+ client: RealtimeClient;
247
+ children: ReactNode;
248
+ autoConnect?: boolean;
249
+ };
250
+ declare function RealtimeProvider({ client, children, autoConnect }: RealtimeProviderProps): React.FunctionComponentElement<React.ProviderProps<RealtimeContextType>>;
251
+ type UseSocketClientResult = {
252
+ socket: Socket | null;
253
+ connected: boolean;
254
+ connecting: boolean;
255
+ emit: (event: string, payload?: unknown) => void;
256
+ emitAck: <T = unknown>(event: string, payload?: unknown, timeoutMs?: number) => Promise<T>;
257
+ on: <T = unknown>(event: string, handler: SocketEventHandler<T>) => () => void;
258
+ once: <T = unknown>(event: string, handler: SocketEventHandler<T>) => void;
259
+ off: <T = unknown>(event: string, handler: SocketEventHandler<T>) => void;
260
+ joinRoom: (room: string) => void;
261
+ leaveRoom: (room: string) => void;
262
+ getRooms: () => Set<string>;
263
+ };
264
+ declare function useRealtimeClient(): RealtimeClient;
265
+ declare function useHttpClient(): HttpClient;
266
+ declare function useConnectionState(): ConnectionState;
267
+ declare function useSocketClient(): UseSocketClientResult;
268
+ declare function useSocketEvent<TPayload = unknown>(event: string, handler: (payload: TPayload) => void): void;
269
+ declare function useRoomEvent<TPayload = unknown>(room: string, event: string, handler: (payload: TPayload) => void): void;
270
+ type UseAuthResult = AuthState & {
271
+ login: (email: string, password: string) => Promise<User>;
272
+ register: (data: Record<string, unknown>) => Promise<User>;
273
+ logout: () => Promise<void>;
274
+ refetchUser: () => Promise<User | null>;
275
+ };
276
+ declare function useAuth(): UseAuthResult;
277
+ declare function useRequireAuth(onRedirect?: (redirectPath: string) => void): UseAuthResult;
278
+ declare function useRBAC(requiredRole: string): {
279
+ hasAccess: boolean;
280
+ user: User | null;
281
+ isLoading: boolean;
282
+ };
283
+
284
+ export { type AckCallback, type AckResponse, AuthManager, type AuthState, type ConnectionState, type CsrfConfig, type HeadersMap, HttpClient, type HttpMethod, type HttpRequestOptions, type JsonValue, type OfflineQueueItem, type QueryParams, type RealtimeClient, type RealtimeClientConfig, RealtimeProvider, type RealtimeProviderProps, type RefreshConfig, RefreshManager, type SocketEventHandler, SocketManager, TokenManager, type UseAuthResult, type UseSocketClientResult, type User, createRealtimeClient, useAuth, useConnectionState, useHttpClient, useRBAC, useRealtimeClient, useRequireAuth, useRoomEvent, useSocketClient, useSocketEvent };
package/dist/index.js ADDED
@@ -0,0 +1,2 @@
1
+ 'use strict';var j=require('react'),socket_ioClient=require('socket.io-client');function _interopDefault(e){return e&&e.__esModule?e:{default:e}}var j__default=/*#__PURE__*/_interopDefault(j);function p(){return typeof window<"u"&&typeof document<"u"}function I(){return typeof crypto<"u"&&"randomUUID"in crypto?crypto.randomUUID():`${Date.now()}-${Math.random().toString(16).slice(2)}-${Math.random().toString(16).slice(2)}`}function E(o,e,t){let n=new URL(e,o);if(t)for(let[s,r]of Object.entries(t))r!=null&&n.searchParams.set(s,String(r));return n.toString()}async function y(o){return o.status===204?void 0:(o.headers.get("content-type")||"").includes("application/json")?await o.json():await o.text()}function $(o,e,t){let n=new Error(e);return n.status=o,n.payload=t,n}function v(o){if(!o)return null;let e=o.trim();return e.length?e:null}function P(){try{return p()&&!!window.localStorage}catch{return false}}function m(o){if(!p())return null;let e=document.cookie.match(new RegExp("(^| )"+o+"=([^;]+)"));return e?decodeURIComponent(e[2]):null}function z(o){if(typeof atob<"u")return atob(o);if(typeof Buffer<"u")return Buffer.from(o,"base64").toString("binary");throw new Error("Neither atob nor Buffer is available")}function x(o){try{let e=o.split(".");if(e.length!==3)return null;let n=e[1].replace(/-/g,"+").replace(/_/g,"/"),s=decodeURIComponent(z(n).split("").map(r=>"%"+("00"+r.charCodeAt(0).toString(16)).slice(-2)).join(""));return JSON.parse(s)}catch{return null}}var S=class{accessToken=null;getTokenFn;setTokenFn;persistToken=false;refreshTimer=null;onExpiringCb;tokenChangeListeners=new Set;constructor(e={}){this.getTokenFn=e.getAccessToken,this.setTokenFn=e.setAccessToken,this.persistToken=e.persistToken??false,this.persistToken&&p()&&(this.accessToken=sessionStorage.getItem("accessToken"));}setGetter(e){this.getTokenFn=e;}setSetter(e){this.setTokenFn=e;}onTokenExpiring(e){this.onExpiringCb=e;}onTokenChange(e){return this.tokenChangeListeners.add(e),()=>{this.tokenChangeListeners.delete(e);}}emitTokenChange(e){for(let t of this.tokenChangeListeners)try{t(e);}catch(n){console.error("Error in token change listener:",n);}}setAccessToken(e){this.refreshTimer&&(clearTimeout(this.refreshTimer),this.refreshTimer=null);let t=this.accessToken;if(this.accessToken=v(e),this.persistToken&&p()&&(this.accessToken?sessionStorage.setItem("accessToken",this.accessToken):sessionStorage.removeItem("accessToken")),this.setTokenFn?.(this.accessToken),this.accessToken!==t&&this.emitTokenChange(this.accessToken),this.accessToken&&p()){let n=x(this.accessToken);if(n&&typeof n.exp=="number"){let s=n.exp*1e3,r=Date.now(),i=s-r;if(i>1e4){let a=Math.max(i-3e4,Math.floor(i*.8));this.refreshTimer=setTimeout(()=>{this.onExpiringCb?.();},a);}else setTimeout(()=>this.onExpiringCb?.(),0);}}}getAccessTokenSync(){return this.accessToken}async getAccessToken(){let e=this.getTokenFn?await this.getTokenFn():null,t=v(e)??this.accessToken;return t&&t!==this.accessToken&&this.setAccessToken(t),t}isTokenExpired(){if(!this.accessToken)return true;let e=x(this.accessToken);return !e||typeof e.exp!="number"?true:Date.now()/1e3+10>e.exp}clearAll(){this.setAccessToken(null);}},C=class{constructor(e,t,n){this.config=e;this.tokenManager=t;this.onUnauthorized=n;this.maxRefreshRetries=e?.maxRetries??3;}config;tokenManager;onUnauthorized;refreshing=null;refreshAttempts=0;maxRefreshRetries=3;async refreshAccessToken(){return this.config?.enabled?this.refreshAttempts>=this.maxRefreshRetries?(this.tokenManager.clearAll(),this.onUnauthorized?.(),null):this.refreshing?this.refreshing:(this.refreshing=(async()=>{this.config?.onRefreshStart?.();try{this.refreshAttempts++;let e=this.config?.method??"POST",t=await fetch(this.config.endpoint,{method:e,credentials:"include",headers:{Accept:"application/json"}});if(!t.ok)return this.config?.onRefreshFailure?.(),this.tokenManager.clearAll(),this.onUnauthorized?.(),null;let n=await y(t)||{},s=v(n.accessToken??null);return s?(this.refreshAttempts=0,this.tokenManager.setAccessToken(s),this.config?.onRefreshSuccess?.({accessToken:s}),s):(this.config?.onRefreshFailure?.(),this.tokenManager.clearAll(),this.onUnauthorized?.(),null)}catch{return this.config?.onRefreshFailure?.(),this.tokenManager.clearAll(),this.onUnauthorized?.(),null}finally{this.refreshing=null,this.config?.onRefreshEnd?.();}})(),this.refreshing):null}},T=class{items=[];storageKey;maxItems;constructor(e){this.storageKey=e?.storageKey??"realtime-offline-queue",this.maxItems=e?.maxItems??200,this.load();}load(){if(P())try{let e=window.localStorage.getItem(this.storageKey);this.items=e?JSON.parse(e):[];}catch{this.items=[];}}save(){if(P())try{window.localStorage.setItem(this.storageKey,JSON.stringify(this.items));}catch{}}enqueue(e){let t={...e,id:I(),createdAt:Date.now(),attempts:0};return this.items=[...this.items,t].slice(-this.maxItems),this.save(),t}all(){return [...this.items]}clear(){this.items=[],this.save();}remove(e){this.items=this.items.filter(t=>t.id!==e),this.save();}},R=class{constructor(e,t,n,s,r=true,i){this.baseUrl=e;this.tokenManager=t;this.refreshManager=n;this.onUnauthorized=s;this.withCredentials=r;this.csrfConfig=i;}baseUrl;tokenManager;refreshManager;onUnauthorized;withCredentials;csrfConfig;csrfTokenMemory=null;csrfFetchPromise=null;async fetchCsrfToken(){if(this.csrfFetchPromise)return this.csrfFetchPromise;let e=this.csrfConfig?.fetchEndpoint;if(e)return this.csrfFetchPromise=(async()=>{try{let t=E(this.baseUrl,e),n=await fetch(t,{method:"GET",credentials:this.withCredentials?"include":"same-origin"});if(n.ok){let s=this.csrfConfig?.cookieName??"csrf-token",r=n.headers.get("X-CSRF-Token");if(r){this.csrfTokenMemory=r;return}try{let i=await n.clone().json();if(i?.csrfToken){this.csrfTokenMemory=i.csrfToken;return}}catch{}await new Promise(i=>setTimeout(i,50)),this.csrfTokenMemory=m(s);}}catch{}finally{this.csrfFetchPromise=null;}})(),this.csrfFetchPromise}async prefetchCsrf(){if(!this.csrfConfig?.enabled||!this.csrfConfig?.fetchEndpoint)return;let e=this.csrfConfig.cookieName??"csrf-token";(m(e)??this.csrfTokenMemory)||await this.fetchCsrfToken();}async request(e){let{path:t,method:n="GET",body:s,headers:r,query:i,signal:a,credentials:k,retryOnAuth:u=true}=e,h=await this.tokenManager.getAccessToken(),L=E(this.baseUrl,t,i),b={};if(this.csrfConfig?.enabled&&["POST","PUT","PATCH","DELETE"].includes(n)){let c=this.csrfConfig.cookieName??"csrf-token",l=m(c)??this.csrfTokenMemory;!l&&this.csrfConfig.fetchEndpoint&&(await this.fetchCsrfToken(),l=m(c)??this.csrfTokenMemory),l&&(b[this.csrfConfig.headerName??"X-CSRF-Token"]=l);}let d=await fetch(L,{method:n,signal:a,credentials:k??(this.withCredentials?"include":"same-origin"),headers:{Accept:"application/json",...s!==void 0?{"Content-Type":"application/json"}:{},...h?{Authorization:`Bearer ${h}`}:{},...b,...r},body:s!==void 0?JSON.stringify(s):void 0});if(d.status===401&&u){if(await this.refreshManager.refreshAccessToken())return this.request({...e,retryOnAuth:false});this.onUnauthorized?.();}if(!d.ok){let c;try{c=await y(d);}catch{c=void 0;}let l=`HTTP ${d.status}`;throw c&&typeof c=="object"&&"message"in c?l=String(c.message):c&&typeof c=="object"&&"error"in c&&(l=String(c.error)),$(d.status,l,c)}return y(d)}get(e,t={}){return this.request({...t,path:e,method:"GET"})}post(e,t,n={}){return this.request({...n,path:e,method:"POST",body:t})}put(e,t,n={}){return this.request({...n,path:e,method:"PUT",body:t})}patch(e,t,n={}){return this.request({...n,path:e,method:"PATCH",body:t})}delete(e,t={}){return this.request({...t,path:e,method:"DELETE"})}},w=class{constructor(e,t,n,s){this.config=e;this.tokenManager=t;this.refreshManager=n;this.onUnauthorized=s;e.offlineQueue?.enabled&&(this.pendingEmits=new T(e.offlineQueue));}config;tokenManager;refreshManager;onUnauthorized;socket=null;connectionPromise=null;rooms=new Map;listeners=new Map;pendingEmits=new T;connected=false;connecting=false;stateListeners=new Set;connectionState={connected:false,connecting:false};log(e,...t){this.config.debug&&console.log(`[RealtimeClient] ${e}`,...t);}emitStateChange(){this.connectionState={connected:this.connected,connecting:this.connecting},this.log(`State changed: connected=${this.connected}, connecting=${this.connecting}`);for(let e of this.stateListeners)e(this.connectionState);}getConnectionState(){return this.connectionState}subscribeToState(e){return this.stateListeners.add(e),()=>{this.stateListeners.delete(e);}}isConnected(){return this.connected}isConnecting(){return this.connecting}getSocket(){return this.socket}updateAuthToken(e){this.socket&&(this.socket.auth=e?{token:e}:{},this.socket.connected&&(this.log("Reconnecting socket due to auth token change..."),this.socket.disconnect().connect()));}async connect(){return this.config.enabled===false?null:await this.tokenManager.getAccessToken()?this.socket?this.socket:this.connectionPromise?this.connectionPromise:(this.connectionPromise=(async()=>{try{if(this.connecting=!0,this.emitStateChange(),this.socket)return this.socket;if(this.log("Connecting socket..."),await this.tokenManager.getAccessToken(),this.socket)return this.socket;let t=socket_ioClient.io(this.config.socketUrl,{autoConnect:!1,transports:["websocket"],withCredentials:this.config.withCredentials??!0,auth:n=>{this.tokenManager.getAccessToken().then(s=>n(s?{token:s}:{})).catch(()=>n({}));},reconnection:!0,reconnectionAttempts:1/0,reconnectionDelay:1e3,reconnectionDelayMax:3e4,timeout:2e4,...this.config.socketOptions});this.socket=t,this.registerCoreEvents(t);for(let[n,s]of this.listeners.entries())for(let r of s)t.on(n,r);return t.connect(),t}finally{this.connectionPromise=null;}})(),this.connectionPromise):(this.log("Skipping socket connection: No access token available"),null)}registerCoreEvents(e){e.on("connect",async()=>{this.log("Socket connected"),this.connected=true,this.connecting=false,this.emitStateChange(),this.config.onSocketConnect?.(e),await this.rejoinRooms(),await this.flushOfflineQueue();}),e.on("disconnect",t=>{this.log("Socket disconnected",t),this.connected=false,this.connecting=false,this.emitStateChange(),this.config.onSocketDisconnect?.(t);}),e.on("connect_error",async t=>{this.log("Socket connection error",t),this.connecting=false,this.emitStateChange(),this.config.onSocketError?.(t);let n=String(t?.message||"").toLowerCase();(n.includes("unauthorized")||n.includes("jwt")||n.includes("token"))&&(await this.refreshManager.refreshAccessToken()?(this.log("Token refreshed successfully, retrying socket connection..."),this.connecting=true,this.emitStateChange(),e.connect()):(this.log("Token refresh failed on connect_error"),this.onUnauthorized?.(),await this.disconnect()));});}async disconnect(){this.log("Disconnecting socket..."),this.connectionPromise=null,this.socket?.removeAllListeners(),this.socket?.disconnect(),this.socket=null,this.connected=false,this.connecting=false,this.emitStateChange();}joinRoom(e){let t=this.rooms.get(e)??0;this.rooms.set(e,t+1),this.log(`Joining room: ${e} (refCount: ${t+1})`),t===0&&this.socket?.connected&&this.socket.emit("room:join",{room:e});}leaveRoom(e){let t=this.rooms.get(e)??0;t<=1?(this.rooms.delete(e),this.log(`Leaving room: ${e} (refCount: 0)`),this.socket?.connected&&this.socket.emit("room:leave",{room:e})):(this.rooms.set(e,t-1),this.log(`Decreasing room refCount: ${e} (refCount: ${t-1})`));}getRooms(){return new Set(this.rooms.keys())}async rejoinRooms(){if(this.config.rooms?.autoRejoin??true){this.log("Rejoining all rooms...");for(let t of this.rooms.keys())this.socket?.emit("room:join",{room:t});}}on(e,t){let n=this.listeners.get(e)??new Set;return n.add(t),this.listeners.set(e,n),this.socket?.on(e,t),()=>this.off(e,t)}off(e,t){this.socket?.off(e,t);let n=this.listeners.get(e);n?.delete(t),n&&n.size===0&&this.listeners.delete(e);}once(e,t){this.socket?.once(e,t);}emit(e,t){if(this.log(`Emitting event ${e}`,t),this.socket?.connected){this.socket.emit(e,t);return}if(this.config.offlineQueue?.enabled){this.log(`Socket not connected, enqueuing event ${e} to offline queue`),this.pendingEmits.enqueue({event:e,payload:t,namespace:this.config.namespace});return}if(this.socket){this.log(`Socket connecting/offline, buffering event ${e} in memory`),this.socket.emit(e,t);return}throw new Error("Socket is not initialized")}emitWithAck(e,t,n){return this.log(`Emitting event with ack ${e}`,t),new Promise((s,r)=>{let i=this.socket,a=n??this.config.ackTimeoutMs??15e3;if(!i){if(this.config.offlineQueue?.enabled){this.log(`Socket not initialized, enqueuing ack event ${e} to offline queue`),this.pendingEmits.enqueue({event:e,payload:t,namespace:this.config.namespace}),r(new Error("Socket offline, event queued instead of ack"));return}r(new Error("Socket not initialized"));return}let k=setTimeout(()=>{this.log(`Ack timeout for event ${e}`),r(new Error(`Ack timeout for event ${e}`));},a);i.emit(e,t,u=>{if(clearTimeout(k),u&&typeof u=="object"&&"ok"in u){let h=u;h.ok?s(h.data):r(new Error(h.error?.message||"Ack error"));}else s(u);});})}async flushOfflineQueue(){if(!this.socket?.connected||!this.config.offlineQueue?.enabled)return;let e=this.pendingEmits.all();if(e.length)for(let t of e)try{this.socket.emit(t.event,t.payload),this.pendingEmits.remove(t.id);}catch{}}},A=class{constructor(e,t,n,s,r){this.http=e;this.tokenManager=t;this.refreshManager=n;this.config=s;this.onLogout=r;this.tokenManager.onTokenChange(async i=>{i?!this.state.user&&!this.state.isLoading&&await this.getMe():this.updateState({user:null,isAuthenticated:false});});}http;tokenManager;refreshManager;config;onLogout;state={user:null,isAuthenticated:false,isLoading:false,error:null};listeners=new Set;emitChange(){for(let e of this.listeners)try{e(this.state);}catch(t){console.error("Error in auth listener:",t);}}getAuthState(){return this.state}subscribe(e){return this.listeners.add(e),()=>{this.listeners.delete(e);}}updateState(e){this.state={...this.state,...e},this.emitChange();}isAuthenticated(){return this.state.isAuthenticated}async login(e,t){this.updateState({isLoading:true,error:null});try{let n=this.config?.loginEndpoint??"/api/auth/login",s=await this.http.post(n,{email:e,password:t});return this.tokenManager.setAccessToken(s.accessToken),this.updateState({user:s.user,isAuthenticated:!0,isLoading:!1}),s.user}catch(n){let s=n.message||"Login failed";throw this.updateState({error:s,isLoading:false}),n}}async register(e){this.updateState({isLoading:true,error:null});try{let t=this.config?.registerEndpoint??"/api/auth/register",n=await this.http.post(t,e);return this.tokenManager.setAccessToken(n.accessToken),this.updateState({user:n.user,isAuthenticated:!0,isLoading:!1}),n.user}catch(t){let n=t.message||"Registration failed";throw this.updateState({error:n,isLoading:false}),t}}async logout(){this.updateState({isLoading:true,error:null});try{let e=this.config?.logoutEndpoint??"/api/auth/logout";await this.http.post(e,{},{retryOnAuth:!1});}catch{}finally{this.tokenManager.clearAll(),this.updateState({user:null,isAuthenticated:false,isLoading:false}),this.onLogout?.();}}async getMe(){let e=await this.tokenManager.getAccessToken();if((!e||this.tokenManager.isTokenExpired())&&(e=await this.refreshManager.refreshAccessToken()),!e)return this.updateState({user:null,isAuthenticated:false,isLoading:false}),null;this.updateState({isLoading:true,error:null});try{let t=this.config?.meEndpoint??"/api/auth/me",n=await this.http.get(t);return this.updateState({user:n,isAuthenticated:!0,isLoading:!1}),n}catch(t){return this.updateState({user:null,isAuthenticated:false,error:t.message||"Failed to fetch user profile",isLoading:false}),null}}};function J(o){let e=new S({getAccessToken:o.getAccessToken,setAccessToken:o.setAccessToken,persistToken:o.auth?.persistToken}),t=new C(o.refresh,e,o.onUnauthorized);e.onTokenExpiring(()=>{t.refreshAccessToken();});let n=new R(o.apiBaseUrl,e,t,o.onUnauthorized,o.withCredentials??true,o.csrf),s=new w(o,e,t,o.onUnauthorized);e.onTokenChange(i=>{s.updateAuthToken(i),i?s.connect():s.disconnect();});let r=new A(n,e,t,o.auth,()=>{s.disconnect();});return {http:n,socket:s,token:e,refresh:t,auth:r,connect:()=>s.connect(),disconnect:()=>s.disconnect(),getSocket:()=>s.getSocket(),on:(i,a)=>s.on(i,a),once:(i,a)=>s.once(i,a),off:(i,a)=>s.off(i,a),emit:(i,a)=>s.emit(i,a),emitAck:(i,a,k)=>s.emitWithAck(i,a,k),joinRoom:i=>s.joinRoom(i),leaveRoom:i=>s.leaveRoom(i),getRooms:()=>s.getRooms(),isConnected:()=>s.isConnected(),isConnecting:()=>s.isConnecting(),setAccessToken:i=>e.setAccessToken(i),getAccessToken:()=>e.getAccessToken(),subscribeToState:i=>s.subscribeToState(i),getConnectionState:()=>s.getConnectionState()}}var O=j.createContext(null);function G({client:o,children:e,autoConnect:t=true}){return j.useEffect(()=>{if(o.http.prefetchCsrf(),!!t)return o.connect(),()=>{o.disconnect();}},[t,o]),j__default.default.createElement(O.Provider,{value:o},e)}function f(){let o=j.useContext(O);if(!o)throw new Error("useRealtimeClient must be used inside RealtimeProvider");return o}function K(){return f().http}function N(){let o=f();return j.useSyncExternalStore(e=>o.subscribeToState(e),()=>o.getConnectionState(),()=>({connected:false,connecting:false}))}function V(){let o=f(),{connected:e,connecting:t}=N();return {socket:o.getSocket(),connected:e,connecting:t,emit:o.emit,emitAck:o.emitAck,on:o.on,once:o.once,off:o.off,joinRoom:o.joinRoom,leaveRoom:o.leaveRoom,getRooms:o.getRooms}}function _(o,e){let t=f(),n=j.useRef(e);j.useEffect(()=>{n.current=e;},[e]),j.useEffect(()=>{let s=t.on(o,r=>n.current(r));return ()=>s()},[t,o]);}function W(o,e,t){let n=f(),s=j.useRef(t);j.useEffect(()=>{s.current=t;},[t]),j.useEffect(()=>{n.joinRoom(o);let r=n.on(e,i=>s.current(i));return ()=>{r(),n.leaveRoom(o);}},[n,e,o]);}function H(){let o=f();return {...j.useSyncExternalStore(t=>o.auth.subscribe(t),()=>o.auth.getAuthState(),()=>({user:null,isAuthenticated:false,isLoading:false,error:null})),login:(t,n)=>o.auth.login(t,n),register:t=>o.auth.register(t),logout:()=>o.auth.logout(),refetchUser:()=>o.auth.getMe()}}function X(o){let e=H();return j.useEffect(()=>{if(!e.isLoading&&!e.isAuthenticated&&typeof window<"u"){let t=window.location.pathname+window.location.search,n=`/login?redirect=${encodeURIComponent(t)}`;o?o(n):window.location.href=n;}},[e.isAuthenticated,e.isLoading,o]),e}function Y(o){let{user:e,isAuthenticated:t,isLoading:n}=H();return {hasAccess:t&&e?.role===o,user:e,isLoading:n}}exports.AuthManager=A;exports.HttpClient=R;exports.RealtimeProvider=G;exports.RefreshManager=C;exports.SocketManager=w;exports.TokenManager=S;exports.createRealtimeClient=J;exports.useAuth=H;exports.useConnectionState=N;exports.useHttpClient=K;exports.useRBAC=Y;exports.useRealtimeClient=f;exports.useRequireAuth=X;exports.useRoomEvent=W;exports.useSocketClient=V;exports.useSocketEvent=_;//# sourceMappingURL=index.js.map
2
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/index.ts"],"names":["isBrowser","uuid","buildUrl","baseUrl","path","query","url","k","v","parseResponse","response","createHttpError","status","message","payload","error","normalizeToken","token","trimmed","storageAvailable","getCookieByName","name","match","safeAtob","str","decodeJwtPayload","parts","base64","jsonPayload","c","TokenManager","config","getter","setter","cb","listener","err","prevToken","expMs","nowMs","ttl","delay","fromFn","RefreshManager","tokenManager","onUnauthorized","method","data","OfflineQueueManager","raw","item","next","id","i","HttpClient","refreshManager","withCredentials","csrfConfig","endpoint","res","cookieName","headerToken","r","options","body","headers","signal","credentials","retryOnAuth","csrfHeader","csrfToken","SocketManager","args","socket","io","event","handlers","handler","reason","msg","room","count","set","timeoutMs","resolve","reject","timer","ack","queue","AuthManager","http","onLogout","updater","email","password","errMsg","user","createRealtimeClient","refresh","newToken","auth","RealtimeContext","createContext","RealtimeProvider","client","children","autoConnect","useEffect","React","useRealtimeClient","ctx","useContext","useHttpClient","useConnectionState","useSyncExternalStore","callback","useSocketClient","connected","connecting","useSocketEvent","handlerRef","useRef","off","useRoomEvent","useAuth","useRequireAuth","onRedirect","currentPath","target","useRBAC","requiredRole","isAuthenticated","isLoading"],"mappings":"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","file":"index.js","sourcesContent":["import React, { createContext, useContext, useEffect, useRef, useSyncExternalStore, type ReactNode } from 'react';\nimport { io, type Socket, type ManagerOptions, type SocketOptions } from 'socket.io-client';\n\n/*\n realtimehttpauthclient\n ======================\n A professional HTTP & Realtime client library.\n Compatible with React, Bun, Node.js, and TanStack Start.\n*/\n\nexport type HttpMethod = 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE';\n\nexport type JsonValue =\n | string\n | number\n | boolean\n | null\n | JsonValue[]\n | { [key: string]: JsonValue };\n\nexport type HeadersMap = Record<string, string>;\n\nexport type QueryParams = Record<string, string | number | boolean | null | undefined>;\n\nexport type HttpRequestOptions<TBody = unknown> = {\n path: string;\n method?: HttpMethod;\n body?: TBody;\n headers?: HeadersMap;\n query?: QueryParams;\n signal?: AbortSignal;\n credentials?: RequestCredentials;\n retryOnAuth?: boolean;\n};\n\nexport type RefreshConfig = {\n enabled?: boolean;\n endpoint: string;\n method?: 'POST' | 'GET';\n onRefreshSuccess?: (tokens: { accessToken?: string | null }) => void;\n onRefreshFailure?: () => void;\n onRefreshStart?: () => void;\n onRefreshEnd?: () => void;\n maxRetries?: number;\n};\n\nexport type OfflineQueueItem = {\n id: string;\n event: string;\n payload: unknown;\n namespace?: string;\n createdAt: number;\n attempts: number;\n};\n\nexport type ConnectionState = {\n connected: boolean;\n connecting: boolean;\n};\n\nexport type User = {\n id: string;\n email: string;\n userName: string;\n role: string;\n};\n\nexport type AuthState = {\n user: User | null;\n isAuthenticated: boolean;\n isLoading: boolean;\n error: string | null;\n};\n\nexport type CsrfConfig = {\n enabled?: boolean;\n cookieName?: string;\n headerName?: string;\n /** Endpoint to call (GET) to receive the csrf-token cookie when it's missing */\n fetchEndpoint?: string;\n};\n\nexport type RealtimeClientConfig = {\n apiBaseUrl: string;\n socketUrl: string;\n withCredentials?: boolean;\n enabled?: boolean;\n autoConnect?: boolean;\n socketOptions?: Partial<ManagerOptions & SocketOptions>;\n getAccessToken?: () => Promise<string | null> | string | null;\n setAccessToken?: (token: string | null) => void;\n refresh?: RefreshConfig;\n auth?: {\n loginEndpoint?: string;\n registerEndpoint?: string;\n logoutEndpoint?: string;\n meEndpoint?: string;\n persistToken?: boolean;\n };\n csrf?: CsrfConfig;\n onUnauthorized?: () => void;\n onSocketConnect?: (socket: Socket) => void;\n onSocketDisconnect?: (reason: Socket.DisconnectReason) => void;\n onSocketError?: (error: Error) => void;\n offlineQueue?: {\n enabled?: boolean;\n storageKey?: string;\n maxItems?: number;\n };\n rooms?: {\n autoRejoin?: boolean;\n };\n ackTimeoutMs?: number;\n namespace?: string;\n debug?: boolean;\n};\n\nexport type AckResponse<T = unknown> = {\n ok: boolean;\n data?: T;\n error?: { message: string; code?: string; details?: unknown };\n};\n\nexport type AckCallback<T = unknown> = (response: AckResponse<T>) => void;\nexport type SocketEventHandler<T = unknown> = (payload: T) => void;\n\nfunction isBrowser() {\n return typeof window !== 'undefined' && typeof document !== 'undefined';\n}\n\nfunction uuid() {\n if (typeof crypto !== 'undefined' && 'randomUUID' in crypto) return crypto.randomUUID();\n return `${Date.now()}-${Math.random().toString(16).slice(2)}-${Math.random().toString(16).slice(2)}`;\n}\n\nfunction buildUrl(baseUrl: string, path: string, query?: QueryParams) {\n const url = new URL(path, baseUrl);\n if (query) {\n for (const [k, v] of Object.entries(query)) {\n if (v === undefined || v === null) continue;\n url.searchParams.set(k, String(v));\n }\n }\n return url.toString();\n}\n\nasync function parseResponse<T>(response: Response): Promise<T> {\n if (response.status === 204) return undefined as T;\n const ct = response.headers.get('content-type') || '';\n if (ct.includes('application/json')) return (await response.json()) as T;\n return (await response.text()) as T;\n}\n\nfunction createHttpError(status: number, message: string, payload?: unknown) {\n const error = new Error(message) as Error & { status?: number; payload?: unknown };\n error.status = status;\n error.payload = payload;\n return error;\n}\n\nfunction normalizeToken(token: string | null | undefined) {\n if (!token) return null;\n const trimmed = token.trim();\n return trimmed.length ? trimmed : null;\n}\n\nfunction storageAvailable() {\n try {\n return isBrowser() && !!window.localStorage;\n } catch {\n return false;\n }\n}\n\nfunction getCookieByName(name: string): string | null {\n if (!isBrowser()) return null;\n const match = document.cookie.match(new RegExp('(^| )' + name + '=([^;]+)'));\n if (match) return decodeURIComponent(match[2]);\n return null;\n}\n\nfunction safeAtob(str: string): string {\n if (typeof atob !== 'undefined') return atob(str);\n if (typeof Buffer !== 'undefined') return Buffer.from(str, 'base64').toString('binary');\n throw new Error('Neither atob nor Buffer is available');\n}\n\nfunction decodeJwtPayload(token: string) {\n try {\n const parts = token.split('.');\n if (parts.length !== 3) return null;\n const base64Url = parts[1];\n const base64 = base64Url.replace(/-/g, '+').replace(/_/g, '/');\n const jsonPayload = decodeURIComponent(\n safeAtob(base64)\n .split('')\n .map((c) => '%' + ('00' + c.charCodeAt(0).toString(16)).slice(-2))\n .join('')\n );\n return JSON.parse(jsonPayload) as { exp?: number; userId?: string; email?: string; role?: string; [key: string]: unknown };\n } catch {\n return null;\n }\n}\n\nexport class TokenManager {\n private accessToken: string | null = null;\n private getTokenFn?: RealtimeClientConfig['getAccessToken'];\n private setTokenFn?: RealtimeClientConfig['setAccessToken'];\n private persistToken: boolean = false;\n private refreshTimer: any = null;\n private onExpiringCb?: () => void;\n private tokenChangeListeners = new Set<(token: string | null) => void>();\n\n constructor(config: {\n getAccessToken?: RealtimeClientConfig['getAccessToken'];\n setAccessToken?: RealtimeClientConfig['setAccessToken'];\n persistToken?: boolean;\n } = {}) {\n this.getTokenFn = config.getAccessToken;\n this.setTokenFn = config.setAccessToken;\n this.persistToken = config.persistToken ?? false;\n\n if (this.persistToken && isBrowser()) {\n this.accessToken = sessionStorage.getItem('accessToken');\n }\n }\n\n setGetter(getter?: RealtimeClientConfig['getAccessToken']) {\n this.getTokenFn = getter;\n }\n\n setSetter(setter?: RealtimeClientConfig['setAccessToken']) {\n this.setTokenFn = setter;\n }\n\n onTokenExpiring(cb: () => void) {\n this.onExpiringCb = cb;\n }\n\n onTokenChange(cb: (token: string | null) => void) {\n this.tokenChangeListeners.add(cb);\n return () => {\n this.tokenChangeListeners.delete(cb);\n };\n }\n\n private emitTokenChange(token: string | null) {\n for (const listener of this.tokenChangeListeners) {\n try {\n listener(token);\n } catch (err) {\n console.error('Error in token change listener:', err);\n }\n }\n }\n\n setAccessToken(token: string | null) {\n if (this.refreshTimer) {\n clearTimeout(this.refreshTimer);\n this.refreshTimer = null;\n }\n\n const prevToken = this.accessToken;\n this.accessToken = normalizeToken(token);\n\n if (this.persistToken && isBrowser()) {\n if (this.accessToken) {\n sessionStorage.setItem('accessToken', this.accessToken);\n } else {\n sessionStorage.removeItem('accessToken');\n }\n }\n\n this.setTokenFn?.(this.accessToken);\n\n if (this.accessToken !== prevToken) {\n this.emitTokenChange(this.accessToken);\n }\n\n // Schedule proactive refresh\n if (this.accessToken && isBrowser()) {\n const payload = decodeJwtPayload(this.accessToken);\n if (payload && typeof payload.exp === 'number') {\n const expMs = payload.exp * 1000;\n const nowMs = Date.now();\n const ttl = expMs - nowMs;\n if (ttl > 10000) {\n const delay = Math.max(ttl - 30000, Math.floor(ttl * 0.8));\n this.refreshTimer = setTimeout(() => {\n this.onExpiringCb?.();\n }, delay);\n } else {\n setTimeout(() => this.onExpiringCb?.(), 0);\n }\n }\n }\n }\n\n getAccessTokenSync() {\n return this.accessToken;\n }\n\n async getAccessToken() {\n const fromFn = this.getTokenFn ? await this.getTokenFn() : null;\n const token = normalizeToken(fromFn) ?? this.accessToken;\n if (token && token !== this.accessToken) {\n this.setAccessToken(token);\n }\n return token;\n }\n\n isTokenExpired() {\n if (!this.accessToken) return true;\n const payload = decodeJwtPayload(this.accessToken);\n if (!payload || typeof payload.exp !== 'number') return true;\n return (Date.now() / 1000) + 10 > payload.exp;\n }\n\n clearAll() {\n this.setAccessToken(null);\n }\n}\n\nexport class RefreshManager {\n private refreshing: Promise<string | null> | null = null;\n private refreshAttempts = 0;\n private maxRefreshRetries = 3;\n\n constructor(\n private readonly config: RefreshConfig | undefined,\n private readonly tokenManager: TokenManager,\n private readonly onUnauthorized?: () => void,\n ) {\n this.maxRefreshRetries = config?.maxRetries ?? 3;\n }\n\n async refreshAccessToken(): Promise<string | null> {\n if (!this.config?.enabled) return null;\n if (this.refreshAttempts >= this.maxRefreshRetries) {\n this.tokenManager.clearAll();\n this.onUnauthorized?.();\n return null;\n }\n if (this.refreshing) return this.refreshing;\n\n this.refreshing = (async () => {\n this.config?.onRefreshStart?.();\n try {\n this.refreshAttempts++;\n const method = this.config?.method ?? 'POST';\n const response = await fetch(this.config!.endpoint, {\n method,\n credentials: 'include',\n headers: { Accept: 'application/json' },\n });\n\n if (!response.ok) {\n this.config?.onRefreshFailure?.();\n this.tokenManager.clearAll();\n this.onUnauthorized?.();\n return null;\n }\n\n const data = (await parseResponse<{ accessToken?: string | null }>(response)) || {};\n const token = normalizeToken(data.accessToken ?? null);\n if (token) {\n this.refreshAttempts = 0;\n this.tokenManager.setAccessToken(token);\n this.config?.onRefreshSuccess?.({ accessToken: token });\n return token;\n }\n\n this.config?.onRefreshFailure?.();\n this.tokenManager.clearAll();\n this.onUnauthorized?.();\n return null;\n } catch (err) {\n this.config?.onRefreshFailure?.();\n this.tokenManager.clearAll();\n this.onUnauthorized?.();\n return null;\n } finally {\n this.refreshing = null;\n this.config?.onRefreshEnd?.();\n }\n })();\n\n return this.refreshing;\n }\n}\n\nclass OfflineQueueManager {\n private items: OfflineQueueItem[] = [];\n private storageKey: string;\n private maxItems: number;\n\n constructor(config?: RealtimeClientConfig['offlineQueue']) {\n this.storageKey = config?.storageKey ?? 'realtime-offline-queue';\n this.maxItems = config?.maxItems ?? 200;\n this.load();\n }\n\n private load() {\n if (!storageAvailable()) return;\n try {\n const raw = window.localStorage.getItem(this.storageKey);\n this.items = raw ? (JSON.parse(raw) as OfflineQueueItem[]) : [];\n } catch {\n this.items = [];\n }\n }\n\n private save() {\n if (!storageAvailable()) return;\n try {\n window.localStorage.setItem(this.storageKey, JSON.stringify(this.items));\n } catch {\n // ignore storage failures\n }\n }\n\n enqueue(item: Omit<OfflineQueueItem, 'id' | 'createdAt' | 'attempts'>) {\n const next: OfflineQueueItem = {\n ...item,\n id: uuid(),\n createdAt: Date.now(),\n attempts: 0,\n };\n this.items = [...this.items, next].slice(-this.maxItems);\n this.save();\n return next;\n }\n\n all() {\n return [...this.items];\n }\n\n clear() {\n this.items = [];\n this.save();\n }\n\n remove(id: string) {\n this.items = this.items.filter((i) => i.id !== id);\n this.save();\n }\n}\n\nexport class HttpClient {\n private csrfTokenMemory: string | null = null;\n private csrfFetchPromise: Promise<void> | null = null;\n\n constructor(\n private readonly baseUrl: string,\n private readonly tokenManager: TokenManager,\n private readonly refreshManager: RefreshManager,\n private readonly onUnauthorized?: () => void,\n private readonly withCredentials = true,\n private readonly csrfConfig?: CsrfConfig,\n ) {}\n\n /** Fetch the CSRF token from the server (sets the cookie + stores in memory) */\n private async fetchCsrfToken(): Promise<void> {\n if (this.csrfFetchPromise) return this.csrfFetchPromise;\n const endpoint = this.csrfConfig?.fetchEndpoint;\n if (!endpoint) return;\n this.csrfFetchPromise = (async () => {\n try {\n const url = buildUrl(this.baseUrl, endpoint);\n const res = await fetch(url, {\n method: 'GET',\n credentials: this.withCredentials ? 'include' : 'same-origin',\n });\n if (res.ok) {\n const cookieName = this.csrfConfig?.cookieName ?? 'csrf-token';\n // Strategy 1: Check X-CSRF-Token response header\n const headerToken = res.headers.get('X-CSRF-Token');\n if (headerToken) {\n this.csrfTokenMemory = headerToken;\n return;\n }\n // Strategy 2: Read from JSON response body\n try {\n const data = await res.clone().json() as { csrfToken?: string };\n if (data?.csrfToken) {\n this.csrfTokenMemory = data.csrfToken;\n return;\n }\n } catch { /* not JSON */ }\n // Strategy 3: Give browser time to register Set-Cookie then read cookie\n await new Promise((r) => setTimeout(r, 50));\n this.csrfTokenMemory = getCookieByName(cookieName);\n }\n } catch {\n // ignore – CSRF will just be missing\n } finally {\n this.csrfFetchPromise = null;\n }\n })();\n return this.csrfFetchPromise;\n }\n\n /** Public: eagerly pre-fetch the CSRF token (call on app startup) */\n async prefetchCsrf(): Promise<void> {\n if (!this.csrfConfig?.enabled || !this.csrfConfig?.fetchEndpoint) return;\n const cookieName = this.csrfConfig.cookieName ?? 'csrf-token';\n const existing = getCookieByName(cookieName) ?? this.csrfTokenMemory;\n if (!existing) {\n await this.fetchCsrfToken();\n }\n }\n\n async request<TResponse = unknown, TBody = unknown>(options: HttpRequestOptions<TBody>): Promise<TResponse> {\n const { path, method = 'GET', body, headers, query, signal, credentials, retryOnAuth = true } = options;\n const token = await this.tokenManager.getAccessToken();\n const url = buildUrl(this.baseUrl, path, query);\n\n let csrfHeader: Record<string, string> = {};\n if (this.csrfConfig?.enabled && ['POST', 'PUT', 'PATCH', 'DELETE'].includes(method)) {\n const cookieName = this.csrfConfig.cookieName ?? 'csrf-token';\n let csrfToken = getCookieByName(cookieName) ?? this.csrfTokenMemory;\n if (!csrfToken && this.csrfConfig.fetchEndpoint) {\n // Auto-fetch the CSRF token when the cookie is absent\n await this.fetchCsrfToken();\n csrfToken = getCookieByName(cookieName) ?? this.csrfTokenMemory;\n }\n if (csrfToken) {\n csrfHeader[this.csrfConfig.headerName ?? 'X-CSRF-Token'] = csrfToken;\n }\n }\n\n const response = await fetch(url, {\n method,\n signal,\n credentials: credentials ?? (this.withCredentials ? 'include' : 'same-origin'),\n headers: {\n Accept: 'application/json',\n ...(body !== undefined ? { 'Content-Type': 'application/json' } : {}),\n ...(token ? { Authorization: `Bearer ${token}` } : {}),\n ...csrfHeader,\n ...headers,\n },\n body: body !== undefined ? JSON.stringify(body) : undefined,\n });\n\n if (response.status === 401 && retryOnAuth) {\n const newToken = await this.refreshManager.refreshAccessToken();\n if (newToken) {\n return this.request<TResponse, TBody>({ ...options, retryOnAuth: false });\n }\n this.onUnauthorized?.();\n }\n\n if (!response.ok) {\n let payload: unknown;\n try {\n payload = await parseResponse(response);\n } catch {\n payload = undefined;\n }\n let message = `HTTP ${response.status}`;\n if (payload && typeof payload === 'object' && 'message' in payload) {\n message = String((payload as any).message);\n } else if (payload && typeof payload === 'object' && 'error' in payload) {\n message = String((payload as any).error);\n }\n throw createHttpError(response.status, message, payload);\n }\n\n return parseResponse<TResponse>(response);\n }\n\n get<TResponse = unknown>(path: string, options: Omit<HttpRequestOptions, 'path' | 'method'> = {}) {\n return this.request<TResponse>({ ...options, path, method: 'GET' });\n }\n\n post<TResponse = unknown, TBody = unknown>(path: string, body?: TBody, options: Omit<HttpRequestOptions<TBody>, 'path' | 'method' | 'body'> = {}) {\n return this.request<TResponse, TBody>({ ...options, path, method: 'POST', body });\n }\n\n put<TResponse = unknown, TBody = unknown>(path: string, body?: TBody, options: Omit<HttpRequestOptions<TBody>, 'path' | 'method' | 'body'> = {}) {\n return this.request<TResponse, TBody>({ ...options, path, method: 'PUT', body });\n }\n\n patch<TResponse = unknown, TBody = unknown>(path: string, body?: TBody, options: Omit<HttpRequestOptions<TBody>, 'path' | 'method' | 'body'> = {}) {\n return this.request<TResponse, TBody>({ ...options, path, method: 'PATCH', body });\n }\n\n delete<TResponse = unknown>(path: string, options: Omit<HttpRequestOptions, 'path' | 'method'> = {}) {\n return this.request<TResponse>({ ...options, path, method: 'DELETE' });\n }\n}\n\nexport class SocketManager {\n private socket: Socket | null = null;\n private connectionPromise: Promise<Socket | null> | null = null;\n private rooms = new Map<string, number>();\n private listeners = new Map<string, Set<SocketEventHandler>>();\n private pendingEmits = new OfflineQueueManager();\n private connected = false;\n private connecting = false;\n private stateListeners = new Set<(state: ConnectionState) => void>();\n private connectionState: ConnectionState = { connected: false, connecting: false };\n\n constructor(\n private readonly config: RealtimeClientConfig,\n private readonly tokenManager: TokenManager,\n private readonly refreshManager: RefreshManager,\n private readonly onUnauthorized?: () => void,\n ) {\n if (config.offlineQueue?.enabled) {\n this.pendingEmits = new OfflineQueueManager(config.offlineQueue);\n }\n }\n\n private log(message: string, ...args: unknown[]) {\n if (this.config.debug) {\n console.log(`[RealtimeClient] ${message}`, ...args);\n }\n }\n\n private emitStateChange() {\n this.connectionState = { connected: this.connected, connecting: this.connecting };\n this.log(`State changed: connected=${this.connected}, connecting=${this.connecting}`);\n for (const listener of this.stateListeners) {\n listener(this.connectionState);\n }\n }\n\n getConnectionState() {\n return this.connectionState;\n }\n\n subscribeToState(listener: (state: ConnectionState) => void) {\n this.stateListeners.add(listener);\n return () => {\n this.stateListeners.delete(listener);\n };\n }\n\n isConnected() {\n return this.connected;\n }\n\n isConnecting() {\n return this.connecting;\n }\n\n getSocket(): Socket | null {\n return this.socket;\n }\n\n updateAuthToken(token: string | null) {\n if (this.socket) {\n this.socket.auth = token ? { token } : {};\n if (this.socket.connected) {\n this.log('Reconnecting socket due to auth token change...');\n this.socket.disconnect().connect();\n }\n }\n }\n\n async connect(): Promise<Socket | null> {\n if (this.config.enabled === false) return null;\n const token = await this.tokenManager.getAccessToken();\n if (!token) {\n this.log('Skipping socket connection: No access token available');\n return null;\n }\n if (this.socket) return this.socket;\n if (this.connectionPromise) return this.connectionPromise;\n\n this.connectionPromise = (async () => {\n try {\n this.connecting = true;\n this.emitStateChange();\n\n if (this.socket) return this.socket;\n\n this.log('Connecting socket...');\n await this.tokenManager.getAccessToken();\n\n if (this.socket) return this.socket;\n\n const socket = io(this.config.socketUrl, {\n autoConnect: false,\n transports: ['websocket'],\n withCredentials: this.config.withCredentials ?? true,\n auth: (cb) => {\n this.tokenManager.getAccessToken()\n .then((token) => cb(token ? { token } : {}))\n .catch(() => cb({}));\n },\n reconnection: true,\n reconnectionAttempts: Infinity,\n reconnectionDelay: 1000,\n reconnectionDelayMax: 30000,\n timeout: 20000,\n ...this.config.socketOptions,\n });\n\n this.socket = socket;\n this.registerCoreEvents(socket);\n\n // Re-register custom event listeners\n for (const [event, handlers] of this.listeners.entries()) {\n for (const handler of handlers) {\n socket.on(event, handler);\n }\n }\n\n socket.connect();\n return socket;\n } finally {\n this.connectionPromise = null;\n }\n })();\n\n return this.connectionPromise;\n }\n\n private registerCoreEvents(socket: Socket) {\n socket.on('connect', async () => {\n this.log('Socket connected');\n this.connected = true;\n this.connecting = false;\n this.emitStateChange();\n this.config.onSocketConnect?.(socket);\n await this.rejoinRooms();\n await this.flushOfflineQueue();\n });\n\n socket.on('disconnect', (reason) => {\n this.log('Socket disconnected', reason);\n this.connected = false;\n this.connecting = false;\n this.emitStateChange();\n this.config.onSocketDisconnect?.(reason);\n });\n\n socket.on('connect_error', async (error: Error & { message?: string }) => {\n this.log('Socket connection error', error);\n this.connecting = false;\n this.emitStateChange();\n this.config.onSocketError?.(error);\n const msg = String(error?.message || '').toLowerCase();\n if (msg.includes('unauthorized') || msg.includes('jwt') || msg.includes('token')) {\n const refreshed = await this.refreshManager.refreshAccessToken();\n if (refreshed) {\n this.log('Token refreshed successfully, retrying socket connection...');\n this.connecting = true;\n this.emitStateChange();\n socket.connect();\n } else {\n this.log('Token refresh failed on connect_error');\n this.onUnauthorized?.();\n await this.disconnect();\n }\n }\n });\n }\n\n async disconnect() {\n this.log('Disconnecting socket...');\n this.connectionPromise = null;\n this.socket?.removeAllListeners();\n this.socket?.disconnect();\n this.socket = null;\n this.connected = false;\n this.connecting = false;\n this.emitStateChange();\n }\n\n joinRoom(room: string) {\n const count = this.rooms.get(room) ?? 0;\n this.rooms.set(room, count + 1);\n this.log(`Joining room: ${room} (refCount: ${count + 1})`);\n if (count === 0 && this.socket?.connected) {\n this.socket.emit('room:join', { room });\n }\n }\n\n leaveRoom(room: string) {\n const count = this.rooms.get(room) ?? 0;\n if (count <= 1) {\n this.rooms.delete(room);\n this.log(`Leaving room: ${room} (refCount: 0)`);\n if (this.socket?.connected) {\n this.socket.emit('room:leave', { room });\n }\n } else {\n this.rooms.set(room, count - 1);\n this.log(`Decreasing room refCount: ${room} (refCount: ${count - 1})`);\n }\n }\n\n getRooms() {\n return new Set(this.rooms.keys());\n }\n\n private async rejoinRooms() {\n const autoRejoin = this.config.rooms?.autoRejoin ?? true;\n if (!autoRejoin) return;\n this.log('Rejoining all rooms...');\n for (const room of this.rooms.keys()) {\n this.socket?.emit('room:join', { room });\n }\n }\n\n on<TPayload = unknown>(event: string, handler: SocketEventHandler<TPayload>) {\n const set = this.listeners.get(event) ?? new Set<SocketEventHandler>();\n set.add(handler as SocketEventHandler);\n this.listeners.set(event, set);\n this.socket?.on(event, handler as SocketEventHandler);\n return () => this.off(event, handler);\n }\n\n off<TPayload = unknown>(event: string, handler: SocketEventHandler<TPayload>) {\n this.socket?.off(event, handler as SocketEventHandler);\n const set = this.listeners.get(event);\n set?.delete(handler as SocketEventHandler);\n if (set && set.size === 0) this.listeners.delete(event);\n }\n\n once<TPayload = unknown>(event: string, handler: SocketEventHandler<TPayload>) {\n this.socket?.once(event, handler as SocketEventHandler);\n }\n\n emit(event: string, payload?: unknown) {\n this.log(`Emitting event ${event}`, payload);\n if (this.socket?.connected) {\n this.socket.emit(event, payload);\n return;\n }\n\n if (this.config.offlineQueue?.enabled) {\n this.log(`Socket not connected, enqueuing event ${event} to offline queue`);\n this.pendingEmits.enqueue({ event, payload, namespace: this.config.namespace });\n return;\n }\n\n if (this.socket) {\n this.log(`Socket connecting/offline, buffering event ${event} in memory`);\n this.socket.emit(event, payload);\n return;\n }\n\n throw new Error('Socket is not initialized');\n }\n\n emitWithAck<TResponse = unknown>(event: string, payload?: unknown, timeoutMs?: number): Promise<TResponse> {\n this.log(`Emitting event with ack ${event}`, payload);\n return new Promise<TResponse>((resolve, reject) => {\n const socket = this.socket;\n const ttl = timeoutMs ?? this.config.ackTimeoutMs ?? 15000;\n\n if (!socket) {\n if (this.config.offlineQueue?.enabled) {\n this.log(`Socket not initialized, enqueuing ack event ${event} to offline queue`);\n this.pendingEmits.enqueue({ event, payload, namespace: this.config.namespace });\n reject(new Error('Socket offline, event queued instead of ack'));\n return;\n }\n reject(new Error('Socket not initialized'));\n return;\n }\n\n const timer = setTimeout(() => {\n this.log(`Ack timeout for event ${event}`);\n reject(new Error(`Ack timeout for event ${event}`));\n }, ttl);\n\n socket.emit(event, payload, (response: any) => {\n clearTimeout(timer);\n if (response && typeof response === 'object' && 'ok' in response) {\n const ack = response as AckResponse<TResponse>;\n if (ack.ok) {\n resolve(ack.data as TResponse);\n } else {\n reject(new Error(ack.error?.message || 'Ack error'));\n }\n } else {\n resolve(response as TResponse);\n }\n });\n });\n }\n\n async flushOfflineQueue() {\n if (!this.socket?.connected) return;\n if (!this.config.offlineQueue?.enabled) return;\n\n const queue = this.pendingEmits.all();\n if (!queue.length) return;\n\n for (const item of queue) {\n try {\n this.socket.emit(item.event, item.payload);\n this.pendingEmits.remove(item.id);\n } catch {\n // keep in queue for next reconnect\n }\n }\n }\n}\n\nexport class AuthManager {\n private state: AuthState = {\n user: null,\n isAuthenticated: false,\n isLoading: false,\n error: null,\n };\n private listeners = new Set<(state: AuthState) => void>();\n\n constructor(\n private readonly http: HttpClient,\n private readonly tokenManager: TokenManager,\n private readonly refreshManager: RefreshManager,\n private readonly config: RealtimeClientConfig['auth'],\n private readonly onLogout?: () => void,\n ) {\n this.tokenManager.onTokenChange(async (token) => {\n if (!token) {\n this.updateState({ user: null, isAuthenticated: false });\n } else {\n if (!this.state.user && !this.state.isLoading) {\n await this.getMe();\n }\n }\n });\n }\n\n private emitChange() {\n for (const listener of this.listeners) {\n try {\n listener(this.state);\n } catch (err) {\n console.error('Error in auth listener:', err);\n }\n }\n }\n\n getAuthState() {\n return this.state;\n }\n\n subscribe(listener: (state: AuthState) => void) {\n this.listeners.add(listener);\n return () => {\n this.listeners.delete(listener);\n };\n }\n\n private updateState(updater: Partial<AuthState>) {\n this.state = { ...this.state, ...updater };\n this.emitChange();\n }\n\n isAuthenticated() {\n return this.state.isAuthenticated;\n }\n\n async login(email: string, password: string): Promise<User> {\n this.updateState({ isLoading: true, error: null });\n try {\n const endpoint = this.config?.loginEndpoint ?? '/api/auth/login';\n const data = await this.http.post<{ accessToken: string; user: User }>(endpoint, { email, password });\n \n this.tokenManager.setAccessToken(data.accessToken);\n this.updateState({ user: data.user, isAuthenticated: true, isLoading: false });\n return data.user;\n } catch (err: any) {\n const errMsg = err.message || 'Login failed';\n this.updateState({ error: errMsg, isLoading: false });\n throw err;\n }\n }\n\n async register(data: Record<string, unknown>): Promise<User> {\n this.updateState({ isLoading: true, error: null });\n try {\n const endpoint = this.config?.registerEndpoint ?? '/api/auth/register';\n const response = await this.http.post<{ accessToken: string; user: User }>(endpoint, data);\n \n this.tokenManager.setAccessToken(response.accessToken);\n this.updateState({ user: response.user, isAuthenticated: true, isLoading: false });\n return response.user;\n } catch (err: any) {\n const errMsg = err.message || 'Registration failed';\n this.updateState({ error: errMsg, isLoading: false });\n throw err;\n }\n }\n\n async logout(): Promise<void> {\n this.updateState({ isLoading: true, error: null });\n try {\n const endpoint = this.config?.logoutEndpoint ?? '/api/auth/logout';\n await this.http.post(endpoint, {}, { retryOnAuth: false });\n } catch (err) {\n // Ignore logout API failures\n } finally {\n this.tokenManager.clearAll();\n this.updateState({ user: null, isAuthenticated: false, isLoading: false });\n this.onLogout?.();\n }\n }\n\n async getMe(): Promise<User | null> {\n let token = await this.tokenManager.getAccessToken();\n if (!token || this.tokenManager.isTokenExpired()) {\n token = await this.refreshManager.refreshAccessToken();\n }\n if (!token) {\n this.updateState({ user: null, isAuthenticated: false, isLoading: false });\n return null;\n }\n\n this.updateState({ isLoading: true, error: null });\n try {\n const endpoint = this.config?.meEndpoint ?? '/api/auth/me';\n const user = await this.http.get<User>(endpoint);\n this.updateState({ user, isAuthenticated: true, isLoading: false });\n return user;\n } catch (err: any) {\n this.updateState({ user: null, isAuthenticated: false, error: err.message || 'Failed to fetch user profile', isLoading: false });\n return null;\n }\n }\n}\n\nexport type RealtimeClient = {\n http: HttpClient;\n socket: SocketManager;\n token: TokenManager;\n refresh: RefreshManager;\n auth: AuthManager;\n connect: () => Promise<Socket | null>;\n disconnect: () => Promise<void>;\n getSocket: () => Socket | null;\n on: <T = unknown>(event: string, handler: SocketEventHandler<T>) => () => void;\n once: <T = unknown>(event: string, handler: SocketEventHandler<T>) => void;\n off: <T = unknown>(event: string, handler: SocketEventHandler<T>) => void;\n emit: (event: string, payload?: unknown) => void;\n emitAck: <T = unknown>(event: string, payload?: unknown, timeoutMs?: number) => Promise<T>;\n joinRoom: (room: string) => void;\n leaveRoom: (room: string) => void;\n getRooms: () => Set<string>;\n isConnected: () => boolean;\n isConnecting: () => boolean;\n setAccessToken: (token: string | null) => void;\n getAccessToken: () => Promise<string | null>;\n subscribeToState: (listener: (state: ConnectionState) => void) => () => void;\n getConnectionState: () => ConnectionState;\n};\n\nexport function createRealtimeClient(config: RealtimeClientConfig): RealtimeClient {\n const token = new TokenManager({\n getAccessToken: config.getAccessToken,\n setAccessToken: config.setAccessToken,\n persistToken: config.auth?.persistToken,\n });\n \n const refresh = new RefreshManager(config.refresh, token, config.onUnauthorized);\n \n token.onTokenExpiring(() => {\n void refresh.refreshAccessToken();\n });\n\n const http = new HttpClient(\n config.apiBaseUrl,\n token,\n refresh,\n config.onUnauthorized,\n config.withCredentials ?? true,\n config.csrf\n );\n \n const socket = new SocketManager(config, token, refresh, config.onUnauthorized);\n \n token.onTokenChange((newToken) => {\n socket.updateAuthToken(newToken);\n if (newToken) {\n void socket.connect();\n } else {\n void socket.disconnect();\n }\n });\n\n const auth = new AuthManager(http, token, refresh, config.auth, () => {\n void socket.disconnect();\n });\n\n return {\n http,\n socket,\n token,\n refresh,\n auth,\n connect: () => socket.connect(),\n disconnect: () => socket.disconnect(),\n getSocket: () => socket.getSocket(),\n on: (event, handler) => socket.on(event, handler),\n once: (event, handler) => socket.once(event, handler),\n off: (event, handler) => socket.off(event, handler),\n emit: (event, payload) => socket.emit(event, payload),\n emitAck: (event, payload, timeoutMs) => socket.emitWithAck(event, payload, timeoutMs),\n joinRoom: (room) => socket.joinRoom(room),\n leaveRoom: (room) => socket.leaveRoom(room),\n getRooms: () => socket.getRooms(),\n isConnected: () => socket.isConnected(),\n isConnecting: () => socket.isConnecting(),\n setAccessToken: (newToken) => token.setAccessToken(newToken),\n getAccessToken: () => token.getAccessToken(),\n subscribeToState: (listener) => socket.subscribeToState(listener),\n getConnectionState: () => socket.getConnectionState(),\n };\n}\n\n/* =========================\n React integration\n ========================= */\n\ntype RealtimeContextType = ReturnType<typeof createRealtimeClient> | null;\nconst RealtimeContext = createContext<RealtimeContextType>(null);\n\nexport type RealtimeProviderProps = {\n client: RealtimeClient;\n children: ReactNode;\n autoConnect?: boolean;\n};\n\nexport function RealtimeProvider({ client, children, autoConnect = true }: RealtimeProviderProps) {\n useEffect(() => {\n // Pre-fetch CSRF token on mount so it's ready before any mutation\n void client.http.prefetchCsrf();\n\n if (!autoConnect) return;\n void client.connect();\n return () => {\n void client.disconnect();\n };\n }, [autoConnect, client]);\n\n return React.createElement(RealtimeContext.Provider, { value: client }, children);\n}\n\nexport type UseSocketClientResult = {\n socket: Socket | null;\n connected: boolean;\n connecting: boolean;\n emit: (event: string, payload?: unknown) => void;\n emitAck: <T = unknown>(event: string, payload?: unknown, timeoutMs?: number) => Promise<T>;\n on: <T = unknown>(event: string, handler: SocketEventHandler<T>) => () => void;\n once: <T = unknown>(event: string, handler: SocketEventHandler<T>) => void;\n off: <T = unknown>(event: string, handler: SocketEventHandler<T>) => void;\n joinRoom: (room: string) => void;\n leaveRoom: (room: string) => void;\n getRooms: () => Set<string>;\n};\n\nexport function useRealtimeClient(): RealtimeClient {\n const ctx = useContext(RealtimeContext);\n if (!ctx) throw new Error('useRealtimeClient must be used inside RealtimeProvider');\n return ctx;\n}\n\nexport function useHttpClient(): HttpClient {\n const client = useRealtimeClient();\n return client.http;\n}\n\nexport function useConnectionState(): ConnectionState {\n const client = useRealtimeClient();\n return useSyncExternalStore(\n (callback) => client.subscribeToState(callback),\n () => client.getConnectionState(),\n () => ({ connected: false, connecting: false })\n );\n}\n\nexport function useSocketClient(): UseSocketClientResult {\n const client = useRealtimeClient();\n const { connected, connecting } = useConnectionState();\n\n return {\n socket: client.getSocket(),\n connected,\n connecting,\n emit: client.emit,\n emitAck: client.emitAck,\n on: client.on,\n once: client.once,\n off: client.off,\n joinRoom: client.joinRoom,\n leaveRoom: client.leaveRoom,\n getRooms: client.getRooms,\n };\n}\n\nexport function useSocketEvent<TPayload = unknown>(event: string, handler: (payload: TPayload) => void) {\n const client = useRealtimeClient();\n const handlerRef = useRef(handler);\n\n useEffect(() => {\n handlerRef.current = handler;\n }, [handler]);\n\n useEffect(() => {\n const off = client.on<TPayload>(event, (payload) => handlerRef.current(payload));\n return () => off();\n }, [client, event]);\n}\n\nexport function useRoomEvent<TPayload = unknown>(room: string, event: string, handler: (payload: TPayload) => void) {\n const client = useRealtimeClient();\n const handlerRef = useRef(handler);\n\n useEffect(() => {\n handlerRef.current = handler;\n }, [handler]);\n\n useEffect(() => {\n client.joinRoom(room);\n const off = client.on<TPayload>(event, (payload) => handlerRef.current(payload));\n return () => {\n off();\n client.leaveRoom(room);\n };\n }, [client, event, room]);\n}\n\nexport type UseAuthResult = AuthState & {\n login: (email: string, password: string) => Promise<User>;\n register: (data: Record<string, unknown>) => Promise<User>;\n logout: () => Promise<void>;\n refetchUser: () => Promise<User | null>;\n};\n\nexport function useAuth(): UseAuthResult {\n const client = useRealtimeClient();\n const state = useSyncExternalStore(\n (cb) => client.auth.subscribe(cb),\n () => client.auth.getAuthState(),\n () => ({ user: null, isAuthenticated: false, isLoading: false, error: null })\n );\n\n return {\n ...state,\n login: (email: string, password: string) => client.auth.login(email, password),\n register: (data: Record<string, unknown>) => client.auth.register(data),\n logout: () => client.auth.logout(),\n refetchUser: () => client.auth.getMe(),\n };\n}\n\nexport function useRequireAuth(onRedirect?: (redirectPath: string) => void) {\n const auth = useAuth();\n \n useEffect(() => {\n if (!auth.isLoading && !auth.isAuthenticated) {\n if (typeof window !== 'undefined') {\n const currentPath = window.location.pathname + window.location.search;\n const target = `/login?redirect=${encodeURIComponent(currentPath)}`;\n if (onRedirect) {\n onRedirect(target);\n } else {\n window.location.href = target;\n }\n }\n }\n }, [auth.isAuthenticated, auth.isLoading, onRedirect]);\n\n return auth;\n}\n\nexport function useRBAC(requiredRole: string) {\n const { user, isAuthenticated, isLoading } = useAuth();\n const hasAccess = isAuthenticated && user?.role === requiredRole;\n return { hasAccess, user, isLoading };\n}\n"]}
package/dist/index.mjs ADDED
@@ -0,0 +1,2 @@
1
+ import j,{createContext,useEffect,useContext,useSyncExternalStore,useRef}from'react';import {io}from'socket.io-client';function p(){return typeof window<"u"&&typeof document<"u"}function I(){return typeof crypto<"u"&&"randomUUID"in crypto?crypto.randomUUID():`${Date.now()}-${Math.random().toString(16).slice(2)}-${Math.random().toString(16).slice(2)}`}function E(o,e,t){let n=new URL(e,o);if(t)for(let[s,r]of Object.entries(t))r!=null&&n.searchParams.set(s,String(r));return n.toString()}async function y(o){return o.status===204?void 0:(o.headers.get("content-type")||"").includes("application/json")?await o.json():await o.text()}function $(o,e,t){let n=new Error(e);return n.status=o,n.payload=t,n}function v(o){if(!o)return null;let e=o.trim();return e.length?e:null}function P(){try{return p()&&!!window.localStorage}catch{return false}}function m(o){if(!p())return null;let e=document.cookie.match(new RegExp("(^| )"+o+"=([^;]+)"));return e?decodeURIComponent(e[2]):null}function z(o){if(typeof atob<"u")return atob(o);if(typeof Buffer<"u")return Buffer.from(o,"base64").toString("binary");throw new Error("Neither atob nor Buffer is available")}function x(o){try{let e=o.split(".");if(e.length!==3)return null;let n=e[1].replace(/-/g,"+").replace(/_/g,"/"),s=decodeURIComponent(z(n).split("").map(r=>"%"+("00"+r.charCodeAt(0).toString(16)).slice(-2)).join(""));return JSON.parse(s)}catch{return null}}var S=class{accessToken=null;getTokenFn;setTokenFn;persistToken=false;refreshTimer=null;onExpiringCb;tokenChangeListeners=new Set;constructor(e={}){this.getTokenFn=e.getAccessToken,this.setTokenFn=e.setAccessToken,this.persistToken=e.persistToken??false,this.persistToken&&p()&&(this.accessToken=sessionStorage.getItem("accessToken"));}setGetter(e){this.getTokenFn=e;}setSetter(e){this.setTokenFn=e;}onTokenExpiring(e){this.onExpiringCb=e;}onTokenChange(e){return this.tokenChangeListeners.add(e),()=>{this.tokenChangeListeners.delete(e);}}emitTokenChange(e){for(let t of this.tokenChangeListeners)try{t(e);}catch(n){console.error("Error in token change listener:",n);}}setAccessToken(e){this.refreshTimer&&(clearTimeout(this.refreshTimer),this.refreshTimer=null);let t=this.accessToken;if(this.accessToken=v(e),this.persistToken&&p()&&(this.accessToken?sessionStorage.setItem("accessToken",this.accessToken):sessionStorage.removeItem("accessToken")),this.setTokenFn?.(this.accessToken),this.accessToken!==t&&this.emitTokenChange(this.accessToken),this.accessToken&&p()){let n=x(this.accessToken);if(n&&typeof n.exp=="number"){let s=n.exp*1e3,r=Date.now(),i=s-r;if(i>1e4){let a=Math.max(i-3e4,Math.floor(i*.8));this.refreshTimer=setTimeout(()=>{this.onExpiringCb?.();},a);}else setTimeout(()=>this.onExpiringCb?.(),0);}}}getAccessTokenSync(){return this.accessToken}async getAccessToken(){let e=this.getTokenFn?await this.getTokenFn():null,t=v(e)??this.accessToken;return t&&t!==this.accessToken&&this.setAccessToken(t),t}isTokenExpired(){if(!this.accessToken)return true;let e=x(this.accessToken);return !e||typeof e.exp!="number"?true:Date.now()/1e3+10>e.exp}clearAll(){this.setAccessToken(null);}},C=class{constructor(e,t,n){this.config=e;this.tokenManager=t;this.onUnauthorized=n;this.maxRefreshRetries=e?.maxRetries??3;}config;tokenManager;onUnauthorized;refreshing=null;refreshAttempts=0;maxRefreshRetries=3;async refreshAccessToken(){return this.config?.enabled?this.refreshAttempts>=this.maxRefreshRetries?(this.tokenManager.clearAll(),this.onUnauthorized?.(),null):this.refreshing?this.refreshing:(this.refreshing=(async()=>{this.config?.onRefreshStart?.();try{this.refreshAttempts++;let e=this.config?.method??"POST",t=await fetch(this.config.endpoint,{method:e,credentials:"include",headers:{Accept:"application/json"}});if(!t.ok)return this.config?.onRefreshFailure?.(),this.tokenManager.clearAll(),this.onUnauthorized?.(),null;let n=await y(t)||{},s=v(n.accessToken??null);return s?(this.refreshAttempts=0,this.tokenManager.setAccessToken(s),this.config?.onRefreshSuccess?.({accessToken:s}),s):(this.config?.onRefreshFailure?.(),this.tokenManager.clearAll(),this.onUnauthorized?.(),null)}catch{return this.config?.onRefreshFailure?.(),this.tokenManager.clearAll(),this.onUnauthorized?.(),null}finally{this.refreshing=null,this.config?.onRefreshEnd?.();}})(),this.refreshing):null}},T=class{items=[];storageKey;maxItems;constructor(e){this.storageKey=e?.storageKey??"realtime-offline-queue",this.maxItems=e?.maxItems??200,this.load();}load(){if(P())try{let e=window.localStorage.getItem(this.storageKey);this.items=e?JSON.parse(e):[];}catch{this.items=[];}}save(){if(P())try{window.localStorage.setItem(this.storageKey,JSON.stringify(this.items));}catch{}}enqueue(e){let t={...e,id:I(),createdAt:Date.now(),attempts:0};return this.items=[...this.items,t].slice(-this.maxItems),this.save(),t}all(){return [...this.items]}clear(){this.items=[],this.save();}remove(e){this.items=this.items.filter(t=>t.id!==e),this.save();}},R=class{constructor(e,t,n,s,r=true,i){this.baseUrl=e;this.tokenManager=t;this.refreshManager=n;this.onUnauthorized=s;this.withCredentials=r;this.csrfConfig=i;}baseUrl;tokenManager;refreshManager;onUnauthorized;withCredentials;csrfConfig;csrfTokenMemory=null;csrfFetchPromise=null;async fetchCsrfToken(){if(this.csrfFetchPromise)return this.csrfFetchPromise;let e=this.csrfConfig?.fetchEndpoint;if(e)return this.csrfFetchPromise=(async()=>{try{let t=E(this.baseUrl,e),n=await fetch(t,{method:"GET",credentials:this.withCredentials?"include":"same-origin"});if(n.ok){let s=this.csrfConfig?.cookieName??"csrf-token",r=n.headers.get("X-CSRF-Token");if(r){this.csrfTokenMemory=r;return}try{let i=await n.clone().json();if(i?.csrfToken){this.csrfTokenMemory=i.csrfToken;return}}catch{}await new Promise(i=>setTimeout(i,50)),this.csrfTokenMemory=m(s);}}catch{}finally{this.csrfFetchPromise=null;}})(),this.csrfFetchPromise}async prefetchCsrf(){if(!this.csrfConfig?.enabled||!this.csrfConfig?.fetchEndpoint)return;let e=this.csrfConfig.cookieName??"csrf-token";(m(e)??this.csrfTokenMemory)||await this.fetchCsrfToken();}async request(e){let{path:t,method:n="GET",body:s,headers:r,query:i,signal:a,credentials:k,retryOnAuth:u=true}=e,h=await this.tokenManager.getAccessToken(),L=E(this.baseUrl,t,i),b={};if(this.csrfConfig?.enabled&&["POST","PUT","PATCH","DELETE"].includes(n)){let c=this.csrfConfig.cookieName??"csrf-token",l=m(c)??this.csrfTokenMemory;!l&&this.csrfConfig.fetchEndpoint&&(await this.fetchCsrfToken(),l=m(c)??this.csrfTokenMemory),l&&(b[this.csrfConfig.headerName??"X-CSRF-Token"]=l);}let d=await fetch(L,{method:n,signal:a,credentials:k??(this.withCredentials?"include":"same-origin"),headers:{Accept:"application/json",...s!==void 0?{"Content-Type":"application/json"}:{},...h?{Authorization:`Bearer ${h}`}:{},...b,...r},body:s!==void 0?JSON.stringify(s):void 0});if(d.status===401&&u){if(await this.refreshManager.refreshAccessToken())return this.request({...e,retryOnAuth:false});this.onUnauthorized?.();}if(!d.ok){let c;try{c=await y(d);}catch{c=void 0;}let l=`HTTP ${d.status}`;throw c&&typeof c=="object"&&"message"in c?l=String(c.message):c&&typeof c=="object"&&"error"in c&&(l=String(c.error)),$(d.status,l,c)}return y(d)}get(e,t={}){return this.request({...t,path:e,method:"GET"})}post(e,t,n={}){return this.request({...n,path:e,method:"POST",body:t})}put(e,t,n={}){return this.request({...n,path:e,method:"PUT",body:t})}patch(e,t,n={}){return this.request({...n,path:e,method:"PATCH",body:t})}delete(e,t={}){return this.request({...t,path:e,method:"DELETE"})}},w=class{constructor(e,t,n,s){this.config=e;this.tokenManager=t;this.refreshManager=n;this.onUnauthorized=s;e.offlineQueue?.enabled&&(this.pendingEmits=new T(e.offlineQueue));}config;tokenManager;refreshManager;onUnauthorized;socket=null;connectionPromise=null;rooms=new Map;listeners=new Map;pendingEmits=new T;connected=false;connecting=false;stateListeners=new Set;connectionState={connected:false,connecting:false};log(e,...t){this.config.debug&&console.log(`[RealtimeClient] ${e}`,...t);}emitStateChange(){this.connectionState={connected:this.connected,connecting:this.connecting},this.log(`State changed: connected=${this.connected}, connecting=${this.connecting}`);for(let e of this.stateListeners)e(this.connectionState);}getConnectionState(){return this.connectionState}subscribeToState(e){return this.stateListeners.add(e),()=>{this.stateListeners.delete(e);}}isConnected(){return this.connected}isConnecting(){return this.connecting}getSocket(){return this.socket}updateAuthToken(e){this.socket&&(this.socket.auth=e?{token:e}:{},this.socket.connected&&(this.log("Reconnecting socket due to auth token change..."),this.socket.disconnect().connect()));}async connect(){return this.config.enabled===false?null:await this.tokenManager.getAccessToken()?this.socket?this.socket:this.connectionPromise?this.connectionPromise:(this.connectionPromise=(async()=>{try{if(this.connecting=!0,this.emitStateChange(),this.socket)return this.socket;if(this.log("Connecting socket..."),await this.tokenManager.getAccessToken(),this.socket)return this.socket;let t=io(this.config.socketUrl,{autoConnect:!1,transports:["websocket"],withCredentials:this.config.withCredentials??!0,auth:n=>{this.tokenManager.getAccessToken().then(s=>n(s?{token:s}:{})).catch(()=>n({}));},reconnection:!0,reconnectionAttempts:1/0,reconnectionDelay:1e3,reconnectionDelayMax:3e4,timeout:2e4,...this.config.socketOptions});this.socket=t,this.registerCoreEvents(t);for(let[n,s]of this.listeners.entries())for(let r of s)t.on(n,r);return t.connect(),t}finally{this.connectionPromise=null;}})(),this.connectionPromise):(this.log("Skipping socket connection: No access token available"),null)}registerCoreEvents(e){e.on("connect",async()=>{this.log("Socket connected"),this.connected=true,this.connecting=false,this.emitStateChange(),this.config.onSocketConnect?.(e),await this.rejoinRooms(),await this.flushOfflineQueue();}),e.on("disconnect",t=>{this.log("Socket disconnected",t),this.connected=false,this.connecting=false,this.emitStateChange(),this.config.onSocketDisconnect?.(t);}),e.on("connect_error",async t=>{this.log("Socket connection error",t),this.connecting=false,this.emitStateChange(),this.config.onSocketError?.(t);let n=String(t?.message||"").toLowerCase();(n.includes("unauthorized")||n.includes("jwt")||n.includes("token"))&&(await this.refreshManager.refreshAccessToken()?(this.log("Token refreshed successfully, retrying socket connection..."),this.connecting=true,this.emitStateChange(),e.connect()):(this.log("Token refresh failed on connect_error"),this.onUnauthorized?.(),await this.disconnect()));});}async disconnect(){this.log("Disconnecting socket..."),this.connectionPromise=null,this.socket?.removeAllListeners(),this.socket?.disconnect(),this.socket=null,this.connected=false,this.connecting=false,this.emitStateChange();}joinRoom(e){let t=this.rooms.get(e)??0;this.rooms.set(e,t+1),this.log(`Joining room: ${e} (refCount: ${t+1})`),t===0&&this.socket?.connected&&this.socket.emit("room:join",{room:e});}leaveRoom(e){let t=this.rooms.get(e)??0;t<=1?(this.rooms.delete(e),this.log(`Leaving room: ${e} (refCount: 0)`),this.socket?.connected&&this.socket.emit("room:leave",{room:e})):(this.rooms.set(e,t-1),this.log(`Decreasing room refCount: ${e} (refCount: ${t-1})`));}getRooms(){return new Set(this.rooms.keys())}async rejoinRooms(){if(this.config.rooms?.autoRejoin??true){this.log("Rejoining all rooms...");for(let t of this.rooms.keys())this.socket?.emit("room:join",{room:t});}}on(e,t){let n=this.listeners.get(e)??new Set;return n.add(t),this.listeners.set(e,n),this.socket?.on(e,t),()=>this.off(e,t)}off(e,t){this.socket?.off(e,t);let n=this.listeners.get(e);n?.delete(t),n&&n.size===0&&this.listeners.delete(e);}once(e,t){this.socket?.once(e,t);}emit(e,t){if(this.log(`Emitting event ${e}`,t),this.socket?.connected){this.socket.emit(e,t);return}if(this.config.offlineQueue?.enabled){this.log(`Socket not connected, enqueuing event ${e} to offline queue`),this.pendingEmits.enqueue({event:e,payload:t,namespace:this.config.namespace});return}if(this.socket){this.log(`Socket connecting/offline, buffering event ${e} in memory`),this.socket.emit(e,t);return}throw new Error("Socket is not initialized")}emitWithAck(e,t,n){return this.log(`Emitting event with ack ${e}`,t),new Promise((s,r)=>{let i=this.socket,a=n??this.config.ackTimeoutMs??15e3;if(!i){if(this.config.offlineQueue?.enabled){this.log(`Socket not initialized, enqueuing ack event ${e} to offline queue`),this.pendingEmits.enqueue({event:e,payload:t,namespace:this.config.namespace}),r(new Error("Socket offline, event queued instead of ack"));return}r(new Error("Socket not initialized"));return}let k=setTimeout(()=>{this.log(`Ack timeout for event ${e}`),r(new Error(`Ack timeout for event ${e}`));},a);i.emit(e,t,u=>{if(clearTimeout(k),u&&typeof u=="object"&&"ok"in u){let h=u;h.ok?s(h.data):r(new Error(h.error?.message||"Ack error"));}else s(u);});})}async flushOfflineQueue(){if(!this.socket?.connected||!this.config.offlineQueue?.enabled)return;let e=this.pendingEmits.all();if(e.length)for(let t of e)try{this.socket.emit(t.event,t.payload),this.pendingEmits.remove(t.id);}catch{}}},A=class{constructor(e,t,n,s,r){this.http=e;this.tokenManager=t;this.refreshManager=n;this.config=s;this.onLogout=r;this.tokenManager.onTokenChange(async i=>{i?!this.state.user&&!this.state.isLoading&&await this.getMe():this.updateState({user:null,isAuthenticated:false});});}http;tokenManager;refreshManager;config;onLogout;state={user:null,isAuthenticated:false,isLoading:false,error:null};listeners=new Set;emitChange(){for(let e of this.listeners)try{e(this.state);}catch(t){console.error("Error in auth listener:",t);}}getAuthState(){return this.state}subscribe(e){return this.listeners.add(e),()=>{this.listeners.delete(e);}}updateState(e){this.state={...this.state,...e},this.emitChange();}isAuthenticated(){return this.state.isAuthenticated}async login(e,t){this.updateState({isLoading:true,error:null});try{let n=this.config?.loginEndpoint??"/api/auth/login",s=await this.http.post(n,{email:e,password:t});return this.tokenManager.setAccessToken(s.accessToken),this.updateState({user:s.user,isAuthenticated:!0,isLoading:!1}),s.user}catch(n){let s=n.message||"Login failed";throw this.updateState({error:s,isLoading:false}),n}}async register(e){this.updateState({isLoading:true,error:null});try{let t=this.config?.registerEndpoint??"/api/auth/register",n=await this.http.post(t,e);return this.tokenManager.setAccessToken(n.accessToken),this.updateState({user:n.user,isAuthenticated:!0,isLoading:!1}),n.user}catch(t){let n=t.message||"Registration failed";throw this.updateState({error:n,isLoading:false}),t}}async logout(){this.updateState({isLoading:true,error:null});try{let e=this.config?.logoutEndpoint??"/api/auth/logout";await this.http.post(e,{},{retryOnAuth:!1});}catch{}finally{this.tokenManager.clearAll(),this.updateState({user:null,isAuthenticated:false,isLoading:false}),this.onLogout?.();}}async getMe(){let e=await this.tokenManager.getAccessToken();if((!e||this.tokenManager.isTokenExpired())&&(e=await this.refreshManager.refreshAccessToken()),!e)return this.updateState({user:null,isAuthenticated:false,isLoading:false}),null;this.updateState({isLoading:true,error:null});try{let t=this.config?.meEndpoint??"/api/auth/me",n=await this.http.get(t);return this.updateState({user:n,isAuthenticated:!0,isLoading:!1}),n}catch(t){return this.updateState({user:null,isAuthenticated:false,error:t.message||"Failed to fetch user profile",isLoading:false}),null}}};function J(o){let e=new S({getAccessToken:o.getAccessToken,setAccessToken:o.setAccessToken,persistToken:o.auth?.persistToken}),t=new C(o.refresh,e,o.onUnauthorized);e.onTokenExpiring(()=>{t.refreshAccessToken();});let n=new R(o.apiBaseUrl,e,t,o.onUnauthorized,o.withCredentials??true,o.csrf),s=new w(o,e,t,o.onUnauthorized);e.onTokenChange(i=>{s.updateAuthToken(i),i?s.connect():s.disconnect();});let r=new A(n,e,t,o.auth,()=>{s.disconnect();});return {http:n,socket:s,token:e,refresh:t,auth:r,connect:()=>s.connect(),disconnect:()=>s.disconnect(),getSocket:()=>s.getSocket(),on:(i,a)=>s.on(i,a),once:(i,a)=>s.once(i,a),off:(i,a)=>s.off(i,a),emit:(i,a)=>s.emit(i,a),emitAck:(i,a,k)=>s.emitWithAck(i,a,k),joinRoom:i=>s.joinRoom(i),leaveRoom:i=>s.leaveRoom(i),getRooms:()=>s.getRooms(),isConnected:()=>s.isConnected(),isConnecting:()=>s.isConnecting(),setAccessToken:i=>e.setAccessToken(i),getAccessToken:()=>e.getAccessToken(),subscribeToState:i=>s.subscribeToState(i),getConnectionState:()=>s.getConnectionState()}}var O=createContext(null);function G({client:o,children:e,autoConnect:t=true}){return useEffect(()=>{if(o.http.prefetchCsrf(),!!t)return o.connect(),()=>{o.disconnect();}},[t,o]),j.createElement(O.Provider,{value:o},e)}function f(){let o=useContext(O);if(!o)throw new Error("useRealtimeClient must be used inside RealtimeProvider");return o}function K(){return f().http}function N(){let o=f();return useSyncExternalStore(e=>o.subscribeToState(e),()=>o.getConnectionState(),()=>({connected:false,connecting:false}))}function V(){let o=f(),{connected:e,connecting:t}=N();return {socket:o.getSocket(),connected:e,connecting:t,emit:o.emit,emitAck:o.emitAck,on:o.on,once:o.once,off:o.off,joinRoom:o.joinRoom,leaveRoom:o.leaveRoom,getRooms:o.getRooms}}function _(o,e){let t=f(),n=useRef(e);useEffect(()=>{n.current=e;},[e]),useEffect(()=>{let s=t.on(o,r=>n.current(r));return ()=>s()},[t,o]);}function W(o,e,t){let n=f(),s=useRef(t);useEffect(()=>{s.current=t;},[t]),useEffect(()=>{n.joinRoom(o);let r=n.on(e,i=>s.current(i));return ()=>{r(),n.leaveRoom(o);}},[n,e,o]);}function H(){let o=f();return {...useSyncExternalStore(t=>o.auth.subscribe(t),()=>o.auth.getAuthState(),()=>({user:null,isAuthenticated:false,isLoading:false,error:null})),login:(t,n)=>o.auth.login(t,n),register:t=>o.auth.register(t),logout:()=>o.auth.logout(),refetchUser:()=>o.auth.getMe()}}function X(o){let e=H();return useEffect(()=>{if(!e.isLoading&&!e.isAuthenticated&&typeof window<"u"){let t=window.location.pathname+window.location.search,n=`/login?redirect=${encodeURIComponent(t)}`;o?o(n):window.location.href=n;}},[e.isAuthenticated,e.isLoading,o]),e}function Y(o){let{user:e,isAuthenticated:t,isLoading:n}=H();return {hasAccess:t&&e?.role===o,user:e,isLoading:n}}export{A as AuthManager,R as HttpClient,G as RealtimeProvider,C as RefreshManager,w as SocketManager,S as TokenManager,J as createRealtimeClient,H as useAuth,N as useConnectionState,K as useHttpClient,Y as useRBAC,f as useRealtimeClient,X as useRequireAuth,W as useRoomEvent,V as useSocketClient,_ as useSocketEvent};//# sourceMappingURL=index.mjs.map
2
+ //# sourceMappingURL=index.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/index.ts"],"names":["isBrowser","uuid","buildUrl","baseUrl","path","query","url","k","v","parseResponse","response","createHttpError","status","message","payload","error","normalizeToken","token","trimmed","storageAvailable","getCookieByName","name","match","safeAtob","str","decodeJwtPayload","parts","base64","jsonPayload","c","TokenManager","config","getter","setter","cb","listener","err","prevToken","expMs","nowMs","ttl","delay","fromFn","RefreshManager","tokenManager","onUnauthorized","method","data","OfflineQueueManager","raw","item","next","id","i","HttpClient","refreshManager","withCredentials","csrfConfig","endpoint","res","cookieName","headerToken","r","options","body","headers","signal","credentials","retryOnAuth","csrfHeader","csrfToken","SocketManager","args","socket","io","event","handlers","handler","reason","msg","room","count","set","timeoutMs","resolve","reject","timer","ack","queue","AuthManager","http","onLogout","updater","email","password","errMsg","user","createRealtimeClient","refresh","newToken","auth","RealtimeContext","createContext","RealtimeProvider","client","children","autoConnect","useEffect","React","useRealtimeClient","ctx","useContext","useHttpClient","useConnectionState","useSyncExternalStore","callback","useSocketClient","connected","connecting","useSocketEvent","handlerRef","useRef","off","useRoomEvent","useAuth","useRequireAuth","onRedirect","currentPath","target","useRBAC","requiredRole","isAuthenticated","isLoading"],"mappings":"uHA8HA,SAASA,CAAAA,EAAY,CACnB,OAAO,OAAO,MAAA,CAAW,GAAA,EAAe,OAAO,QAAA,CAAa,GAC9D,CAEA,SAASC,CAAAA,EAAO,CACd,OAAI,OAAO,OAAW,GAAA,EAAe,YAAA,GAAgB,MAAA,CAAe,MAAA,CAAO,UAAA,EAAW,CAC/E,GAAG,IAAA,CAAK,GAAA,EAAK,CAAA,CAAA,EAAI,IAAA,CAAK,MAAA,GAAS,QAAA,CAAS,EAAE,CAAA,CAAE,KAAA,CAAM,CAAC,CAAC,IAAI,IAAA,CAAK,MAAA,GAAS,QAAA,CAAS,EAAE,EAAE,KAAA,CAAM,CAAC,CAAC,CAAA,CACpG,CAEA,SAASC,EAASC,CAAAA,CAAiBC,CAAAA,CAAcC,CAAAA,CAAqB,CACpE,IAAMC,CAAAA,CAAM,IAAI,GAAA,CAAIF,CAAAA,CAAMD,CAAO,CAAA,CACjC,GAAIE,CAAAA,CACF,OAAW,CAACE,CAAAA,CAAGC,CAAC,CAAA,GAAK,MAAA,CAAO,OAAA,CAAQH,CAAK,CAAA,CAChBG,CAAAA,EAAM,IAAA,EAC7BF,CAAAA,CAAI,YAAA,CAAa,GAAA,CAAIC,EAAG,MAAA,CAAOC,CAAC,CAAC,CAAA,CAGrC,OAAOF,CAAAA,CAAI,UACb,CAEA,eAAeG,CAAAA,CAAiBC,CAAAA,CAAgC,CAC9D,OAAIA,CAAAA,CAAS,MAAA,GAAW,IAAK,MAAA,CAAA,CAClBA,CAAAA,CAAS,QAAQ,GAAA,CAAI,cAAc,CAAA,EAAK,EAAA,EAC5C,QAAA,CAAS,kBAAkB,EAAW,MAAMA,CAAAA,CAAS,IAAA,EAAK,CACzD,MAAMA,CAAAA,CAAS,MACzB,CAEA,SAASC,CAAAA,CAAgBC,CAAAA,CAAgBC,CAAAA,CAAiBC,EAAmB,CAC3E,IAAMC,CAAAA,CAAQ,IAAI,KAAA,CAAMF,CAAO,EAC/B,OAAAE,CAAAA,CAAM,MAAA,CAASH,CAAAA,CACfG,CAAAA,CAAM,OAAA,CAAUD,EACTC,CACT,CAEA,SAASC,CAAAA,CAAeC,CAAAA,CAAkC,CACxD,GAAI,CAACA,CAAAA,CAAO,OAAO,IAAA,CACnB,IAAMC,CAAAA,CAAUD,EAAM,IAAA,EAAK,CAC3B,OAAOC,CAAAA,CAAQ,MAAA,CAASA,EAAU,IACpC,CAEA,SAASC,CAAAA,EAAmB,CAC1B,GAAI,CACF,OAAOnB,CAAAA,EAAU,EAAK,CAAC,CAAC,MAAA,CAAO,YACjC,CAAA,KAAQ,CACN,OAAO,MACT,CACF,CAEA,SAASoB,CAAAA,CAAgBC,CAAAA,CAA6B,CACpD,GAAI,CAACrB,CAAAA,GAAa,OAAO,IAAA,CACzB,IAAMsB,CAAAA,CAAQ,QAAA,CAAS,MAAA,CAAO,MAAM,IAAI,MAAA,CAAO,OAAA,CAAUD,CAAAA,CAAO,UAAU,CAAC,EAC3E,OAAIC,CAAAA,CAAc,kBAAA,CAAmBA,CAAAA,CAAM,CAAC,CAAC,EACtC,IACT,CAEA,SAASC,CAAAA,CAASC,CAAAA,CAAqB,CACrC,GAAI,OAAO,IAAA,CAAS,GAAA,CAAa,OAAO,IAAA,CAAKA,CAAG,CAAA,CAChD,GAAI,OAAO,MAAA,CAAW,GAAA,CAAa,OAAO,OAAO,IAAA,CAAKA,CAAAA,CAAK,QAAQ,CAAA,CAAE,QAAA,CAAS,QAAQ,EACtF,MAAM,IAAI,KAAA,CAAM,sCAAsC,CACxD,CAEA,SAASC,CAAAA,CAAiBR,CAAAA,CAAe,CACvC,GAAI,CACF,IAAMS,EAAQT,CAAAA,CAAM,KAAA,CAAM,GAAG,CAAA,CAC7B,GAAIS,CAAAA,CAAM,SAAW,CAAA,CAAG,OAAO,IAAA,CAE/B,IAAMC,CAAAA,CADYD,CAAAA,CAAM,CAAC,CAAA,CACA,OAAA,CAAQ,KAAM,GAAG,CAAA,CAAE,QAAQ,IAAA,CAAM,GAAG,CAAA,CACvDE,CAAAA,CAAc,kBAAA,CAClBL,CAAAA,CAASI,CAAM,CAAA,CACZ,KAAA,CAAM,EAAE,CAAA,CACR,GAAA,CAAKE,CAAAA,EAAM,KAAO,IAAA,CAAOA,CAAAA,CAAE,UAAA,CAAW,CAAC,CAAA,CAAE,QAAA,CAAS,EAAE,CAAA,EAAG,KAAA,CAAM,CAAA,CAAE,CAAC,CAAA,CAChE,IAAA,CAAK,EAAE,CACZ,CAAA,CACA,OAAO,IAAA,CAAK,KAAA,CAAMD,CAAW,CAC/B,CAAA,KAAQ,CACN,OAAO,IACT,CACF,KAEaE,CAAAA,CAAN,KAAmB,CAChB,WAAA,CAA6B,IAAA,CAC7B,UAAA,CACA,WACA,YAAA,CAAwB,KAAA,CACxB,aAAoB,IAAA,CACpB,YAAA,CACA,qBAAuB,IAAI,GAAA,CAEnC,WAAA,CAAYC,CAAAA,CAIR,EAAC,CAAG,CACN,IAAA,CAAK,UAAA,CAAaA,CAAAA,CAAO,cAAA,CACzB,IAAA,CAAK,UAAA,CAAaA,EAAO,cAAA,CACzB,IAAA,CAAK,YAAA,CAAeA,CAAAA,CAAO,YAAA,EAAgB,KAAA,CAEvC,KAAK,YAAA,EAAgB/B,CAAAA,EAAU,GACjC,IAAA,CAAK,WAAA,CAAc,cAAA,CAAe,QAAQ,aAAa,CAAA,EAE3D,CAEA,SAAA,CAAUgC,CAAAA,CAAiD,CACzD,KAAK,UAAA,CAAaA,EACpB,CAEA,SAAA,CAAUC,CAAAA,CAAiD,CACzD,KAAK,UAAA,CAAaA,EACpB,CAEA,eAAA,CAAgBC,CAAAA,CAAgB,CAC9B,KAAK,YAAA,CAAeA,EACtB,CAEA,aAAA,CAAcA,CAAAA,CAAoC,CAChD,YAAK,oBAAA,CAAqB,GAAA,CAAIA,CAAE,CAAA,CACzB,IAAM,CACX,KAAK,oBAAA,CAAqB,MAAA,CAAOA,CAAE,EACrC,CACF,CAEQ,gBAAgBjB,CAAAA,CAAsB,CAC5C,IAAA,IAAWkB,CAAAA,IAAY,IAAA,CAAK,oBAAA,CAC1B,GAAI,CACFA,CAAAA,CAASlB,CAAK,EAChB,CAAA,MAASmB,CAAAA,CAAK,CACZ,OAAA,CAAQ,KAAA,CAAM,iCAAA,CAAmCA,CAAG,EACtD,CAEJ,CAEA,cAAA,CAAenB,CAAAA,CAAsB,CAC/B,IAAA,CAAK,YAAA,GACP,YAAA,CAAa,KAAK,YAAY,CAAA,CAC9B,IAAA,CAAK,YAAA,CAAe,IAAA,CAAA,CAGtB,IAAMoB,EAAY,IAAA,CAAK,WAAA,CAkBvB,GAjBA,IAAA,CAAK,WAAA,CAAcrB,EAAeC,CAAK,CAAA,CAEnC,IAAA,CAAK,YAAA,EAAgBjB,CAAAA,EAAU,GAC7B,KAAK,WAAA,CACP,cAAA,CAAe,OAAA,CAAQ,aAAA,CAAe,IAAA,CAAK,WAAW,EAEtD,cAAA,CAAe,UAAA,CAAW,aAAa,CAAA,CAAA,CAI3C,IAAA,CAAK,UAAA,GAAa,KAAK,WAAW,CAAA,CAE9B,IAAA,CAAK,WAAA,GAAgBqC,CAAAA,EACvB,IAAA,CAAK,gBAAgB,IAAA,CAAK,WAAW,CAAA,CAInC,IAAA,CAAK,WAAA,EAAerC,CAAAA,GAAa,CACnC,IAAMc,CAAAA,CAAUW,CAAAA,CAAiB,IAAA,CAAK,WAAW,EACjD,GAAIX,CAAAA,EAAW,OAAOA,CAAAA,CAAQ,GAAA,EAAQ,QAAA,CAAU,CAC9C,IAAMwB,CAAAA,CAAQxB,EAAQ,GAAA,CAAM,GAAA,CACtByB,EAAQ,IAAA,CAAK,GAAA,EAAI,CACjBC,CAAAA,CAAMF,CAAAA,CAAQC,CAAAA,CACpB,GAAIC,CAAAA,CAAM,GAAA,CAAO,CACf,IAAMC,CAAAA,CAAQ,IAAA,CAAK,IAAID,CAAAA,CAAM,GAAA,CAAO,IAAA,CAAK,KAAA,CAAMA,CAAAA,CAAM,EAAG,CAAC,CAAA,CACzD,IAAA,CAAK,YAAA,CAAe,UAAA,CAAW,IAAM,CACnC,KAAK,YAAA,KACP,CAAA,CAAGC,CAAK,EACV,CAAA,KACE,WAAW,IAAM,IAAA,CAAK,YAAA,IAAe,CAAG,CAAC,EAE7C,CACF,CACF,CAEA,kBAAA,EAAqB,CACnB,OAAO,IAAA,CAAK,WACd,CAEA,MAAM,gBAAiB,CACrB,IAAMC,EAAS,IAAA,CAAK,UAAA,CAAa,MAAM,IAAA,CAAK,UAAA,EAAW,CAAI,KACrDzB,CAAAA,CAAQD,CAAAA,CAAe0B,CAAM,CAAA,EAAK,IAAA,CAAK,WAAA,CAC7C,OAAIzB,CAAAA,EAASA,CAAAA,GAAU,IAAA,CAAK,WAAA,EAC1B,IAAA,CAAK,cAAA,CAAeA,CAAK,CAAA,CAEpBA,CACT,CAEA,cAAA,EAAiB,CACf,GAAI,CAAC,IAAA,CAAK,WAAA,CAAa,OAAO,KAAA,CAC9B,IAAMH,CAAAA,CAAUW,EAAiB,IAAA,CAAK,WAAW,CAAA,CACjD,OAAI,CAACX,CAAAA,EAAW,OAAOA,CAAAA,CAAQ,GAAA,EAAQ,QAAA,CAAiB,IAAA,CAChD,IAAA,CAAK,GAAA,GAAQ,GAAA,CAAQ,EAAA,CAAKA,EAAQ,GAC5C,CAEA,UAAW,CACT,IAAA,CAAK,cAAA,CAAe,IAAI,EAC1B,CACF,EAEa6B,CAAAA,CAAN,KAAqB,CAK1B,WAAA,CACmBZ,CAAAA,CACAa,CAAAA,CACAC,EACjB,CAHiB,IAAA,CAAA,MAAA,CAAAd,CAAAA,CACA,IAAA,CAAA,YAAA,CAAAa,CAAAA,CACA,IAAA,CAAA,cAAA,CAAAC,EAEjB,IAAA,CAAK,iBAAA,CAAoBd,CAAAA,EAAQ,UAAA,EAAc,EACjD,CALmB,OACA,YAAA,CACA,cAAA,CAPX,UAAA,CAA4C,IAAA,CAC5C,eAAA,CAAkB,CAAA,CAClB,kBAAoB,CAAA,CAU5B,MAAM,kBAAA,EAA6C,CACjD,OAAK,IAAA,CAAK,QAAQ,OAAA,CACd,IAAA,CAAK,eAAA,EAAmB,IAAA,CAAK,iBAAA,EAC/B,IAAA,CAAK,aAAa,QAAA,EAAS,CAC3B,KAAK,cAAA,IAAiB,CACf,MAEL,IAAA,CAAK,UAAA,CAAmB,IAAA,CAAK,UAAA,EAEjC,IAAA,CAAK,UAAA,CAAA,CAAc,SAAY,CAC7B,IAAA,CAAK,MAAA,EAAQ,cAAA,IAAiB,CAC9B,GAAI,CACF,IAAA,CAAK,eAAA,EAAA,CACL,IAAMe,CAAAA,CAAS,IAAA,CAAK,MAAA,EAAQ,QAAU,MAAA,CAChCpC,CAAAA,CAAW,MAAM,KAAA,CAAM,IAAA,CAAK,MAAA,CAAQ,SAAU,CAClD,MAAA,CAAAoC,CAAAA,CACA,WAAA,CAAa,SAAA,CACb,OAAA,CAAS,CAAE,MAAA,CAAQ,kBAAmB,CACxC,CAAC,CAAA,CAED,GAAI,CAACpC,CAAAA,CAAS,EAAA,CACZ,OAAA,IAAA,CAAK,MAAA,EAAQ,gBAAA,IAAmB,CAChC,KAAK,YAAA,CAAa,QAAA,GAClB,IAAA,CAAK,cAAA,KACE,IAAA,CAGT,IAAMqC,CAAAA,CAAQ,MAAMtC,CAAAA,CAA+CC,CAAQ,GAAM,EAAC,CAC5EO,CAAAA,CAAQD,CAAAA,CAAe+B,CAAAA,CAAK,WAAA,EAAe,IAAI,CAAA,CACrD,OAAI9B,CAAAA,EACF,IAAA,CAAK,eAAA,CAAkB,CAAA,CACvB,KAAK,YAAA,CAAa,cAAA,CAAeA,CAAK,CAAA,CACtC,IAAA,CAAK,MAAA,EAAQ,mBAAmB,CAAE,WAAA,CAAaA,CAAM,CAAC,CAAA,CAC/CA,CAAAA,GAGT,KAAK,MAAA,EAAQ,gBAAA,IAAmB,CAChC,IAAA,CAAK,YAAA,CAAa,QAAA,GAClB,IAAA,CAAK,cAAA,IAAiB,CACf,IAAA,CACT,CAAA,KAAc,CACZ,YAAK,MAAA,EAAQ,gBAAA,KACb,IAAA,CAAK,YAAA,CAAa,UAAS,CAC3B,IAAA,CAAK,cAAA,IAAiB,CACf,IACT,CAAA,OAAE,CACA,IAAA,CAAK,UAAA,CAAa,IAAA,CAClB,IAAA,CAAK,MAAA,EAAQ,YAAA,KACf,CACF,CAAA,GAAG,CAEI,IAAA,CAAK,UAAA,CAAA,CAlDsB,IAmDpC,CACF,CAAA,CAEM+B,CAAAA,CAAN,KAA0B,CAChB,KAAA,CAA4B,GAC5B,UAAA,CACA,QAAA,CAER,WAAA,CAAYjB,CAAAA,CAA+C,CACzD,IAAA,CAAK,WAAaA,CAAAA,EAAQ,UAAA,EAAc,wBAAA,CACxC,IAAA,CAAK,QAAA,CAAWA,CAAAA,EAAQ,UAAY,GAAA,CACpC,IAAA,CAAK,IAAA,GACP,CAEQ,IAAA,EAAO,CACb,GAAKZ,CAAAA,EAAiB,CACtB,GAAI,CACF,IAAM8B,EAAM,MAAA,CAAO,YAAA,CAAa,OAAA,CAAQ,IAAA,CAAK,UAAU,CAAA,CACvD,KAAK,KAAA,CAAQA,CAAAA,CAAO,IAAA,CAAK,KAAA,CAAMA,CAAG,CAAA,CAA2B,GAC/D,CAAA,KAAQ,CACN,IAAA,CAAK,KAAA,CAAQ,GACf,CACF,CAEQ,IAAA,EAAO,CACb,GAAK9B,CAAAA,GACL,GAAI,CACF,MAAA,CAAO,YAAA,CAAa,OAAA,CAAQ,IAAA,CAAK,WAAY,IAAA,CAAK,SAAA,CAAU,IAAA,CAAK,KAAK,CAAC,EACzE,MAAQ,CAER,CACF,CAEA,OAAA,CAAQ+B,CAAAA,CAA+D,CACrE,IAAMC,CAAAA,CAAyB,CAC7B,GAAGD,CAAAA,CACH,EAAA,CAAIjD,GAAK,CACT,SAAA,CAAW,IAAA,CAAK,GAAA,EAAI,CACpB,QAAA,CAAU,CACZ,CAAA,CACA,OAAA,IAAA,CAAK,KAAA,CAAQ,CAAC,GAAG,IAAA,CAAK,MAAOkD,CAAI,CAAA,CAAE,KAAA,CAAM,CAAC,IAAA,CAAK,QAAQ,EACvD,IAAA,CAAK,IAAA,EAAK,CACHA,CACT,CAEA,GAAA,EAAM,CACJ,OAAO,CAAC,GAAG,IAAA,CAAK,KAAK,CACvB,CAEA,KAAA,EAAQ,CACN,IAAA,CAAK,KAAA,CAAQ,EAAC,CACd,KAAK,IAAA,GACP,CAEA,MAAA,CAAOC,CAAAA,CAAY,CACjB,KAAK,KAAA,CAAQ,IAAA,CAAK,MAAM,MAAA,CAAQC,CAAAA,EAAMA,EAAE,EAAA,GAAOD,CAAE,CAAA,CACjD,IAAA,CAAK,IAAA,GACP,CACF,CAAA,CAEaE,CAAAA,CAAN,KAAiB,CAItB,WAAA,CACmBnD,CAAAA,CACAyC,EACAW,CAAAA,CACAV,CAAAA,CACAW,CAAAA,CAAkB,IAAA,CAClBC,CAAAA,CACjB,CANiB,aAAAtD,CAAAA,CACA,IAAA,CAAA,YAAA,CAAAyC,CAAAA,CACA,IAAA,CAAA,cAAA,CAAAW,CAAAA,CACA,IAAA,CAAA,cAAA,CAAAV,EACA,IAAA,CAAA,eAAA,CAAAW,CAAAA,CACA,IAAA,CAAA,UAAA,CAAAC,EAChB,CANgB,OAAA,CACA,aACA,cAAA,CACA,cAAA,CACA,eAAA,CACA,UAAA,CATX,eAAA,CAAiC,IAAA,CACjC,iBAAyC,IAAA,CAYjD,MAAc,cAAA,EAAgC,CAC5C,GAAI,IAAA,CAAK,iBAAkB,OAAO,IAAA,CAAK,iBACvC,IAAMC,CAAAA,CAAW,KAAK,UAAA,EAAY,aAAA,CAClC,GAAKA,CAAAA,CACL,OAAA,IAAA,CAAK,gBAAA,CAAA,CAAoB,SAAY,CACnC,GAAI,CACF,IAAMpD,CAAAA,CAAMJ,CAAAA,CAAS,KAAK,OAAA,CAASwD,CAAQ,CAAA,CACrCC,CAAAA,CAAM,MAAM,KAAA,CAAMrD,EAAK,CAC3B,MAAA,CAAQ,KAAA,CACR,WAAA,CAAa,IAAA,CAAK,eAAA,CAAkB,UAAY,aAClD,CAAC,CAAA,CACD,GAAIqD,CAAAA,CAAI,EAAA,CAAI,CACV,IAAMC,CAAAA,CAAa,IAAA,CAAK,UAAA,EAAY,UAAA,EAAc,YAAA,CAE5CC,EAAcF,CAAAA,CAAI,OAAA,CAAQ,GAAA,CAAI,cAAc,CAAA,CAClD,GAAIE,EAAa,CACf,IAAA,CAAK,gBAAkBA,CAAAA,CACvB,MACF,CAEA,GAAI,CACF,IAAMd,CAAAA,CAAO,MAAMY,CAAAA,CAAI,OAAM,CAAE,IAAA,EAAK,CACpC,GAAIZ,CAAAA,EAAM,SAAA,CAAW,CACnB,IAAA,CAAK,eAAA,CAAkBA,CAAAA,CAAK,SAAA,CAC5B,MACF,CACF,MAAQ,CAAiB,CAEzB,MAAM,IAAI,OAAA,CAASe,CAAAA,EAAM,WAAWA,CAAAA,CAAG,EAAE,CAAC,CAAA,CAC1C,IAAA,CAAK,eAAA,CAAkB1C,EAAgBwC,CAAU,EACnD,CACF,CAAA,KAAQ,CAER,CAAA,OAAE,CACA,IAAA,CAAK,gBAAA,CAAmB,KAC1B,CACF,CAAA,GAAG,CACI,KAAK,gBACd,CAGA,MAAM,YAAA,EAA8B,CAClC,GAAI,CAAC,IAAA,CAAK,UAAA,EAAY,OAAA,EAAW,CAAC,IAAA,CAAK,YAAY,aAAA,CAAe,OAClE,IAAMA,CAAAA,CAAa,IAAA,CAAK,UAAA,CAAW,YAAc,YAAA,CAAA,CAChCxC,CAAAA,CAAgBwC,CAAU,CAAA,EAAK,IAAA,CAAK,eAAA,GAEnD,MAAM,IAAA,CAAK,cAAA,GAEf,CAEA,MAAM,OAAA,CAA8CG,EAAwD,CAC1G,GAAM,CAAE,IAAA,CAAA3D,CAAAA,CAAM,MAAA,CAAA0C,EAAS,KAAA,CAAO,IAAA,CAAAkB,CAAAA,CAAM,OAAA,CAAAC,CAAAA,CAAS,KAAA,CAAA5D,EAAO,MAAA,CAAA6D,CAAAA,CAAQ,WAAA,CAAAC,CAAAA,CAAa,WAAA,CAAAC,CAAAA,CAAc,IAAK,CAAA,CAAIL,CAAAA,CAC1F9C,EAAQ,MAAM,IAAA,CAAK,aAAa,cAAA,EAAe,CAC/CX,CAAAA,CAAMJ,CAAAA,CAAS,IAAA,CAAK,OAAA,CAASE,EAAMC,CAAK,CAAA,CAE1CgE,CAAAA,CAAqC,EAAC,CAC1C,GAAI,KAAK,UAAA,EAAY,OAAA,EAAW,CAAC,MAAA,CAAQ,KAAA,CAAO,OAAA,CAAS,QAAQ,CAAA,CAAE,QAAA,CAASvB,CAAM,CAAA,CAAG,CACnF,IAAMc,EAAa,IAAA,CAAK,UAAA,CAAW,UAAA,EAAc,YAAA,CAC7CU,CAAAA,CAAYlD,CAAAA,CAAgBwC,CAAU,CAAA,EAAK,IAAA,CAAK,eAAA,CAChD,CAACU,CAAAA,EAAa,IAAA,CAAK,WAAW,aAAA,GAEhC,MAAM,IAAA,CAAK,cAAA,EAAe,CAC1BA,CAAAA,CAAYlD,EAAgBwC,CAAU,CAAA,EAAK,KAAK,eAAA,CAAA,CAE9CU,CAAAA,GACFD,EAAW,IAAA,CAAK,UAAA,CAAW,UAAA,EAAc,cAAc,CAAA,CAAIC,CAAAA,EAE/D,CAEA,IAAM5D,CAAAA,CAAW,MAAM,KAAA,CAAMJ,CAAAA,CAAK,CAChC,OAAAwC,CAAAA,CACA,MAAA,CAAAoB,CAAAA,CACA,WAAA,CAAaC,CAAAA,GAAgB,IAAA,CAAK,gBAAkB,SAAA,CAAY,aAAA,CAAA,CAChE,OAAA,CAAS,CACP,MAAA,CAAQ,kBAAA,CACR,GAAIH,CAAAA,GAAS,MAAA,CAAY,CAAE,cAAA,CAAgB,kBAAmB,CAAA,CAAI,EAAC,CACnE,GAAI/C,CAAAA,CAAQ,CAAE,aAAA,CAAe,CAAA,OAAA,EAAUA,CAAK,CAAA,CAAG,CAAA,CAAI,EAAC,CACpD,GAAGoD,CAAAA,CACH,GAAGJ,CACL,CAAA,CACA,IAAA,CAAMD,CAAAA,GAAS,MAAA,CAAY,IAAA,CAAK,UAAUA,CAAI,CAAA,CAAI,MACpD,CAAC,CAAA,CAED,GAAItD,EAAS,MAAA,GAAW,GAAA,EAAO0D,CAAAA,CAAa,CAE1C,GADiB,MAAM,KAAK,cAAA,CAAe,kBAAA,EAAmB,CAE5D,OAAO,IAAA,CAAK,OAAA,CAA0B,CAAE,GAAGL,CAAAA,CAAS,WAAA,CAAa,KAAM,CAAC,CAAA,CAE1E,KAAK,cAAA,KACP,CAEA,GAAI,CAACrD,CAAAA,CAAS,GAAI,CAChB,IAAII,CAAAA,CACJ,GAAI,CACFA,CAAAA,CAAU,MAAML,CAAAA,CAAcC,CAAQ,EACxC,CAAA,KAAQ,CACNI,CAAAA,CAAU,OACZ,CACA,IAAID,EAAU,CAAA,KAAA,EAAQH,CAAAA,CAAS,MAAM,CAAA,CAAA,CACrC,MAAII,CAAAA,EAAW,OAAOA,CAAAA,EAAY,QAAA,EAAY,YAAaA,CAAAA,CACzDD,CAAAA,CAAU,MAAA,CAAQC,CAAAA,CAAgB,OAAO,CAAA,CAChCA,GAAW,OAAOA,CAAAA,EAAY,QAAA,EAAY,OAAA,GAAWA,CAAAA,GAC9DD,CAAAA,CAAU,OAAQC,CAAAA,CAAgB,KAAK,CAAA,CAAA,CAEnCH,CAAAA,CAAgBD,CAAAA,CAAS,MAAA,CAAQG,EAASC,CAAO,CACzD,CAEA,OAAOL,CAAAA,CAAyBC,CAAQ,CAC1C,CAEA,GAAA,CAAyBN,CAAAA,CAAc2D,CAAAA,CAAuD,EAAC,CAAG,CAChG,OAAO,IAAA,CAAK,OAAA,CAAmB,CAAE,GAAGA,CAAAA,CAAS,KAAA3D,CAAAA,CAAM,MAAA,CAAQ,KAAM,CAAC,CACpE,CAEA,IAAA,CAA2CA,CAAAA,CAAc4D,CAAAA,CAAcD,CAAAA,CAAuE,EAAC,CAAG,CAChJ,OAAO,IAAA,CAAK,OAAA,CAA0B,CAAE,GAAGA,CAAAA,CAAS,KAAA3D,CAAAA,CAAM,MAAA,CAAQ,MAAA,CAAQ,IAAA,CAAA4D,CAAK,CAAC,CAClF,CAEA,GAAA,CAA0C5D,CAAAA,CAAc4D,CAAAA,CAAcD,CAAAA,CAAuE,GAAI,CAC/I,OAAO,IAAA,CAAK,OAAA,CAA0B,CAAE,GAAGA,EAAS,IAAA,CAAA3D,CAAAA,CAAM,MAAA,CAAQ,KAAA,CAAO,IAAA,CAAA4D,CAAK,CAAC,CACjF,CAEA,KAAA,CAA4C5D,CAAAA,CAAc4D,CAAAA,CAAcD,CAAAA,CAAuE,EAAC,CAAG,CACjJ,OAAO,IAAA,CAAK,OAAA,CAA0B,CAAE,GAAGA,CAAAA,CAAS,IAAA,CAAA3D,CAAAA,CAAM,MAAA,CAAQ,OAAA,CAAS,KAAA4D,CAAK,CAAC,CACnF,CAEA,MAAA,CAA4B5D,CAAAA,CAAc2D,EAAuD,EAAC,CAAG,CACnG,OAAO,IAAA,CAAK,OAAA,CAAmB,CAAE,GAAGA,CAAAA,CAAS,IAAA,CAAA3D,CAAAA,CAAM,MAAA,CAAQ,QAAS,CAAC,CACvE,CACF,CAAA,CAEamE,CAAAA,CAAN,KAAoB,CAWzB,YACmBxC,CAAAA,CACAa,CAAAA,CACAW,CAAAA,CACAV,CAAAA,CACjB,CAJiB,IAAA,CAAA,MAAA,CAAAd,EACA,IAAA,CAAA,YAAA,CAAAa,CAAAA,CACA,IAAA,CAAA,cAAA,CAAAW,CAAAA,CACA,IAAA,CAAA,cAAA,CAAAV,CAAAA,CAEbd,EAAO,YAAA,EAAc,OAAA,GACvB,KAAK,YAAA,CAAe,IAAIiB,EAAoBjB,CAAAA,CAAO,YAAY,CAAA,EAEnE,CARmB,MAAA,CACA,YAAA,CACA,eACA,cAAA,CAdX,MAAA,CAAwB,IAAA,CACxB,iBAAA,CAAmD,IAAA,CACnD,KAAA,CAAQ,IAAI,GAAA,CACZ,SAAA,CAAY,IAAI,GAAA,CAChB,YAAA,CAAe,IAAIiB,EACnB,SAAA,CAAY,KAAA,CACZ,UAAA,CAAa,KAAA,CACb,cAAA,CAAiB,IAAI,IACrB,eAAA,CAAmC,CAAE,SAAA,CAAW,KAAA,CAAO,UAAA,CAAY,KAAM,EAazE,GAAA,CAAInC,CAAAA,CAAAA,GAAoB2D,CAAAA,CAAiB,CAC3C,IAAA,CAAK,MAAA,CAAO,OACd,OAAA,CAAQ,GAAA,CAAI,CAAA,iBAAA,EAAoB3D,CAAO,CAAA,CAAA,CAAI,GAAG2D,CAAI,EAEtD,CAEQ,iBAAkB,CACxB,IAAA,CAAK,gBAAkB,CAAE,SAAA,CAAW,IAAA,CAAK,SAAA,CAAW,UAAA,CAAY,IAAA,CAAK,UAAW,CAAA,CAChF,IAAA,CAAK,GAAA,CAAI,CAAA,yBAAA,EAA4B,IAAA,CAAK,SAAS,gBAAgB,IAAA,CAAK,UAAU,CAAA,CAAE,CAAA,CACpF,IAAA,IAAWrC,CAAAA,IAAY,KAAK,cAAA,CAC1BA,CAAAA,CAAS,IAAA,CAAK,eAAe,EAEjC,CAEA,oBAAqB,CACnB,OAAO,IAAA,CAAK,eACd,CAEA,gBAAA,CAAiBA,EAA4C,CAC3D,OAAA,IAAA,CAAK,cAAA,CAAe,GAAA,CAAIA,CAAQ,CAAA,CACzB,IAAM,CACX,IAAA,CAAK,cAAA,CAAe,MAAA,CAAOA,CAAQ,EACrC,CACF,CAEA,WAAA,EAAc,CACZ,OAAO,IAAA,CAAK,SACd,CAEA,YAAA,EAAe,CACb,OAAO,IAAA,CAAK,UACd,CAEA,SAAA,EAA2B,CACzB,OAAO,IAAA,CAAK,MACd,CAEA,gBAAgBlB,CAAAA,CAAsB,CAChC,IAAA,CAAK,MAAA,GACP,IAAA,CAAK,MAAA,CAAO,KAAOA,CAAAA,CAAQ,CAAE,KAAA,CAAAA,CAAM,CAAA,CAAI,GACnC,IAAA,CAAK,MAAA,CAAO,SAAA,GACd,IAAA,CAAK,GAAA,CAAI,iDAAiD,EAC1D,IAAA,CAAK,MAAA,CAAO,UAAA,EAAW,CAAE,OAAA,EAAQ,CAAA,EAGvC,CAEA,MAAM,OAAA,EAAkC,CACtC,OAAI,IAAA,CAAK,MAAA,CAAO,UAAY,KAAA,CAAc,IAAA,CAC5B,MAAM,IAAA,CAAK,YAAA,CAAa,gBAAe,CAKjD,IAAA,CAAK,MAAA,CAAe,IAAA,CAAK,MAAA,CACzB,IAAA,CAAK,kBAA0B,IAAA,CAAK,iBAAA,EAExC,IAAA,CAAK,iBAAA,CAAA,CAAqB,SAAY,CACpC,GAAI,CAIF,GAHA,IAAA,CAAK,UAAA,CAAa,CAAA,CAAA,CAClB,IAAA,CAAK,iBAAgB,CAEjB,IAAA,CAAK,MAAA,CAAQ,OAAO,IAAA,CAAK,MAAA,CAK7B,GAHA,IAAA,CAAK,GAAA,CAAI,sBAAsB,CAAA,CAC/B,MAAM,IAAA,CAAK,aAAa,cAAA,EAAe,CAEnC,IAAA,CAAK,MAAA,CAAQ,OAAO,IAAA,CAAK,OAE7B,IAAMwD,CAAAA,CAASC,EAAAA,CAAG,IAAA,CAAK,MAAA,CAAO,SAAA,CAAW,CACvC,WAAA,CAAa,CAAA,CAAA,CACb,UAAA,CAAY,CAAC,WAAW,CAAA,CACxB,gBAAiB,IAAA,CAAK,MAAA,CAAO,eAAA,EAAmB,CAAA,CAAA,CAChD,IAAA,CAAOxC,CAAAA,EAAO,CACZ,IAAA,CAAK,YAAA,CAAa,cAAA,EAAe,CAC9B,IAAA,CAAMjB,CAAAA,EAAUiB,EAAGjB,CAAAA,CAAQ,CAAE,KAAA,CAAAA,CAAM,CAAA,CAAI,EAAE,CAAC,CAAA,CAC1C,KAAA,CAAM,IAAMiB,CAAAA,CAAG,EAAE,CAAC,EACvB,CAAA,CACA,YAAA,CAAc,CAAA,CAAA,CACd,oBAAA,CAAsB,IACtB,iBAAA,CAAmB,GAAA,CACnB,oBAAA,CAAsB,GAAA,CACtB,OAAA,CAAS,GAAA,CACT,GAAG,IAAA,CAAK,MAAA,CAAO,aACjB,CAAC,CAAA,CAED,IAAA,CAAK,OAASuC,CAAAA,CACd,IAAA,CAAK,mBAAmBA,CAAM,CAAA,CAG9B,OAAW,CAACE,CAAAA,CAAOC,CAAQ,CAAA,GAAK,IAAA,CAAK,SAAA,CAAU,SAAQ,CACrD,IAAA,IAAWC,CAAAA,IAAWD,CAAAA,CACpBH,CAAAA,CAAO,EAAA,CAAGE,EAAOE,CAAO,CAAA,CAI5B,OAAAJ,CAAAA,CAAO,OAAA,EAAQ,CACRA,CACT,CAAA,OAAE,CACA,IAAA,CAAK,iBAAA,CAAoB,KAC3B,CACF,IAAG,CAEI,IAAA,CAAK,iBAAA,CAAA,EApDV,IAAA,CAAK,GAAA,CAAI,uDAAuD,EACzD,IAAA,CAoDX,CAEQ,kBAAA,CAAmBA,CAAAA,CAAgB,CACzCA,CAAAA,CAAO,GAAG,SAAA,CAAW,SAAY,CAC/B,IAAA,CAAK,GAAA,CAAI,kBAAkB,EAC3B,IAAA,CAAK,SAAA,CAAY,KACjB,IAAA,CAAK,UAAA,CAAa,MAClB,IAAA,CAAK,eAAA,EAAgB,CACrB,IAAA,CAAK,MAAA,CAAO,eAAA,GAAkBA,CAAM,CAAA,CACpC,MAAM,IAAA,CAAK,WAAA,EAAY,CACvB,MAAM,KAAK,iBAAA,GACb,CAAC,CAAA,CAEDA,CAAAA,CAAO,EAAA,CAAG,aAAeK,CAAAA,EAAW,CAClC,IAAA,CAAK,GAAA,CAAI,qBAAA,CAAuBA,CAAM,EACtC,IAAA,CAAK,SAAA,CAAY,KAAA,CACjB,IAAA,CAAK,UAAA,CAAa,KAAA,CAClB,KAAK,eAAA,EAAgB,CACrB,IAAA,CAAK,MAAA,CAAO,kBAAA,GAAqBA,CAAM,EACzC,CAAC,CAAA,CAEDL,CAAAA,CAAO,EAAA,CAAG,eAAA,CAAiB,MAAO1D,GAAwC,CACxE,IAAA,CAAK,IAAI,yBAAA,CAA2BA,CAAK,EACzC,IAAA,CAAK,UAAA,CAAa,KAAA,CAClB,IAAA,CAAK,eAAA,EAAgB,CACrB,KAAK,MAAA,CAAO,aAAA,GAAgBA,CAAK,CAAA,CACjC,IAAMgE,CAAAA,CAAM,OAAOhE,CAAAA,EAAO,OAAA,EAAW,EAAE,CAAA,CAAE,WAAA,EAAY,CAAA,CACjDgE,EAAI,QAAA,CAAS,cAAc,CAAA,EAAKA,CAAAA,CAAI,QAAA,CAAS,KAAK,GAAKA,CAAAA,CAAI,QAAA,CAAS,OAAO,CAAA,IAC3D,MAAM,IAAA,CAAK,eAAe,kBAAA,EAAmB,EAE7D,IAAA,CAAK,GAAA,CAAI,6DAA6D,CAAA,CACtE,KAAK,UAAA,CAAa,IAAA,CAClB,IAAA,CAAK,eAAA,EAAgB,CACrBN,CAAAA,CAAO,SAAQ,GAEf,IAAA,CAAK,IAAI,uCAAuC,CAAA,CAChD,KAAK,cAAA,IAAiB,CACtB,MAAM,IAAA,CAAK,UAAA,EAAW,CAAA,EAG5B,CAAC,EACH,CAEA,MAAM,UAAA,EAAa,CACjB,IAAA,CAAK,IAAI,yBAAyB,CAAA,CAClC,IAAA,CAAK,iBAAA,CAAoB,IAAA,CACzB,IAAA,CAAK,QAAQ,kBAAA,EAAmB,CAChC,IAAA,CAAK,MAAA,EAAQ,UAAA,EAAW,CACxB,KAAK,MAAA,CAAS,IAAA,CACd,IAAA,CAAK,SAAA,CAAY,KAAA,CACjB,IAAA,CAAK,WAAa,KAAA,CAClB,IAAA,CAAK,eAAA,GACP,CAEA,QAAA,CAASO,EAAc,CACrB,IAAMC,CAAAA,CAAQ,IAAA,CAAK,KAAA,CAAM,GAAA,CAAID,CAAI,CAAA,EAAK,CAAA,CACtC,KAAK,KAAA,CAAM,GAAA,CAAIA,EAAMC,CAAAA,CAAQ,CAAC,CAAA,CAC9B,IAAA,CAAK,GAAA,CAAI,CAAA,cAAA,EAAiBD,CAAI,CAAA,YAAA,EAAeC,CAAAA,CAAQ,CAAC,CAAA,CAAA,CAAG,CAAA,CACrDA,CAAAA,GAAU,GAAK,IAAA,CAAK,MAAA,EAAQ,SAAA,EAC9B,IAAA,CAAK,MAAA,CAAO,IAAA,CAAK,YAAa,CAAE,IAAA,CAAAD,CAAK,CAAC,EAE1C,CAEA,UAAUA,CAAAA,CAAc,CACtB,IAAMC,CAAAA,CAAQ,IAAA,CAAK,KAAA,CAAM,IAAID,CAAI,CAAA,EAAK,CAAA,CAClCC,CAAAA,EAAS,CAAA,EACX,IAAA,CAAK,MAAM,MAAA,CAAOD,CAAI,CAAA,CACtB,IAAA,CAAK,GAAA,CAAI,CAAA,cAAA,EAAiBA,CAAI,CAAA,cAAA,CAAgB,CAAA,CAC1C,KAAK,MAAA,EAAQ,SAAA,EACf,KAAK,MAAA,CAAO,IAAA,CAAK,YAAA,CAAc,CAAE,IAAA,CAAAA,CAAK,CAAC,CAAA,GAGzC,IAAA,CAAK,KAAA,CAAM,GAAA,CAAIA,CAAAA,CAAMC,CAAAA,CAAQ,CAAC,CAAA,CAC9B,IAAA,CAAK,GAAA,CAAI,CAAA,0BAAA,EAA6BD,CAAI,CAAA,YAAA,EAAeC,EAAQ,CAAC,CAAA,CAAA,CAAG,CAAA,EAEzE,CAEA,QAAA,EAAW,CACT,OAAO,IAAI,GAAA,CAAI,IAAA,CAAK,KAAA,CAAM,IAAA,EAAM,CAClC,CAEA,MAAc,WAAA,EAAc,CAE1B,GADmB,IAAA,CAAK,OAAO,KAAA,EAAO,UAAA,EAAc,IAAA,CAEpD,CAAA,IAAA,CAAK,GAAA,CAAI,wBAAwB,EACjC,IAAA,IAAWD,CAAAA,IAAQ,KAAK,KAAA,CAAM,IAAA,GAC5B,IAAA,CAAK,MAAA,EAAQ,IAAA,CAAK,WAAA,CAAa,CAAE,IAAA,CAAAA,CAAK,CAAC,EAAA,CAE3C,CAEA,EAAA,CAAuBL,CAAAA,CAAeE,CAAAA,CAAuC,CAC3E,IAAMK,CAAAA,CAAM,IAAA,CAAK,SAAA,CAAU,GAAA,CAAIP,CAAK,GAAK,IAAI,GAAA,CAC7C,OAAAO,CAAAA,CAAI,GAAA,CAAIL,CAA6B,EACrC,IAAA,CAAK,SAAA,CAAU,GAAA,CAAIF,CAAAA,CAAOO,CAAG,CAAA,CAC7B,KAAK,MAAA,EAAQ,EAAA,CAAGP,CAAAA,CAAOE,CAA6B,CAAA,CAC7C,IAAM,KAAK,GAAA,CAAIF,CAAAA,CAAOE,CAAO,CACtC,CAEA,GAAA,CAAwBF,EAAeE,CAAAA,CAAuC,CAC5E,IAAA,CAAK,MAAA,EAAQ,GAAA,CAAIF,CAAAA,CAAOE,CAA6B,CAAA,CACrD,IAAMK,CAAAA,CAAM,IAAA,CAAK,SAAA,CAAU,GAAA,CAAIP,CAAK,CAAA,CACpCO,CAAAA,EAAK,MAAA,CAAOL,CAA6B,CAAA,CACrCK,CAAAA,EAAOA,EAAI,IAAA,GAAS,CAAA,EAAG,IAAA,CAAK,SAAA,CAAU,MAAA,CAAOP,CAAK,EACxD,CAEA,IAAA,CAAyBA,CAAAA,CAAeE,CAAAA,CAAuC,CAC7E,IAAA,CAAK,QAAQ,IAAA,CAAKF,CAAAA,CAAOE,CAA6B,EACxD,CAEA,IAAA,CAAKF,EAAe7D,CAAAA,CAAmB,CAErC,GADA,IAAA,CAAK,GAAA,CAAI,CAAA,eAAA,EAAkB6D,CAAK,CAAA,CAAA,CAAI7D,CAAO,CAAA,CACvC,IAAA,CAAK,MAAA,EAAQ,SAAA,CAAW,CAC1B,IAAA,CAAK,MAAA,CAAO,KAAK6D,CAAAA,CAAO7D,CAAO,EAC/B,MACF,CAEA,GAAI,IAAA,CAAK,MAAA,CAAO,YAAA,EAAc,QAAS,CACrC,IAAA,CAAK,GAAA,CAAI,CAAA,sCAAA,EAAyC6D,CAAK,CAAA,iBAAA,CAAmB,EAC1E,IAAA,CAAK,YAAA,CAAa,OAAA,CAAQ,CAAE,KAAA,CAAAA,CAAAA,CAAO,QAAA7D,CAAAA,CAAS,SAAA,CAAW,IAAA,CAAK,MAAA,CAAO,SAAU,CAAC,EAC9E,MACF,CAEA,GAAI,IAAA,CAAK,MAAA,CAAQ,CACf,KAAK,GAAA,CAAI,CAAA,2CAAA,EAA8C6D,CAAK,CAAA,UAAA,CAAY,CAAA,CACxE,IAAA,CAAK,OAAO,IAAA,CAAKA,CAAAA,CAAO7D,CAAO,CAAA,CAC/B,MACF,CAEA,MAAM,IAAI,KAAA,CAAM,2BAA2B,CAC7C,CAEA,YAAiC6D,CAAAA,CAAe7D,CAAAA,CAAmBqE,CAAAA,CAAwC,CACzG,OAAA,IAAA,CAAK,GAAA,CAAI,2BAA2BR,CAAK,CAAA,CAAA,CAAI7D,CAAO,CAAA,CAC7C,IAAI,OAAA,CAAmB,CAACsE,CAAAA,CAASC,CAAAA,GAAW,CACjD,IAAMZ,CAAAA,CAAS,IAAA,CAAK,OACdjC,CAAAA,CAAM2C,CAAAA,EAAa,IAAA,CAAK,MAAA,CAAO,YAAA,EAAgB,IAAA,CAErD,GAAI,CAACV,CAAAA,CAAQ,CACX,GAAI,IAAA,CAAK,MAAA,CAAO,cAAc,OAAA,CAAS,CACrC,IAAA,CAAK,GAAA,CAAI,CAAA,4CAAA,EAA+CE,CAAK,mBAAmB,CAAA,CAChF,IAAA,CAAK,YAAA,CAAa,OAAA,CAAQ,CAAE,KAAA,CAAAA,EAAO,OAAA,CAAA7D,CAAAA,CAAS,UAAW,IAAA,CAAK,MAAA,CAAO,SAAU,CAAC,CAAA,CAC9EuE,CAAAA,CAAO,IAAI,KAAA,CAAM,6CAA6C,CAAC,CAAA,CAC/D,MACF,CACAA,CAAAA,CAAO,IAAI,KAAA,CAAM,wBAAwB,CAAC,CAAA,CAC1C,MACF,CAEA,IAAMC,CAAAA,CAAQ,WAAW,IAAM,CAC7B,IAAA,CAAK,GAAA,CAAI,CAAA,sBAAA,EAAyBX,CAAK,EAAE,CAAA,CACzCU,CAAAA,CAAO,IAAI,KAAA,CAAM,CAAA,sBAAA,EAAyBV,CAAK,EAAE,CAAC,EACpD,CAAA,CAAGnC,CAAG,CAAA,CAENiC,CAAAA,CAAO,KAAKE,CAAAA,CAAO7D,CAAAA,CAAUJ,CAAAA,EAAkB,CAE7C,GADA,YAAA,CAAa4E,CAAK,CAAA,CACd5E,CAAAA,EAAY,OAAOA,CAAAA,EAAa,QAAA,EAAY,OAAQA,CAAAA,CAAU,CAChE,IAAM6E,CAAAA,CAAM7E,CAAAA,CACR6E,CAAAA,CAAI,GACNH,CAAAA,CAAQG,CAAAA,CAAI,IAAiB,CAAA,CAE7BF,CAAAA,CAAO,IAAI,MAAME,CAAAA,CAAI,KAAA,EAAO,OAAA,EAAW,WAAW,CAAC,EAEvD,MACEH,CAAAA,CAAQ1E,CAAqB,EAEjC,CAAC,EACH,CAAC,CACH,CAEA,MAAM,iBAAA,EAAoB,CAExB,GADI,CAAC,KAAK,MAAA,EAAQ,SAAA,EACd,CAAC,IAAA,CAAK,MAAA,CAAO,YAAA,EAAc,QAAS,OAExC,IAAM8E,CAAAA,CAAQ,IAAA,CAAK,YAAA,CAAa,GAAA,GAChC,GAAKA,CAAAA,CAAM,OAEX,IAAA,IAAWtC,CAAAA,IAAQsC,EACjB,GAAI,CACF,IAAA,CAAK,MAAA,CAAO,IAAA,CAAKtC,CAAAA,CAAK,MAAOA,CAAAA,CAAK,OAAO,CAAA,CACzC,IAAA,CAAK,YAAA,CAAa,MAAA,CAAOA,EAAK,EAAE,EAClC,CAAA,KAAQ,CAER,CAEJ,CACF,EAEauC,CAAAA,CAAN,KAAkB,CASvB,WAAA,CACmBC,CAAAA,CACA9C,CAAAA,CACAW,EACAxB,CAAAA,CACA4D,CAAAA,CACjB,CALiB,IAAA,CAAA,IAAA,CAAAD,CAAAA,CACA,IAAA,CAAA,YAAA,CAAA9C,EACA,IAAA,CAAA,cAAA,CAAAW,CAAAA,CACA,IAAA,CAAA,MAAA,CAAAxB,CAAAA,CACA,IAAA,CAAA,QAAA,CAAA4D,CAAAA,CAEjB,KAAK,YAAA,CAAa,aAAA,CAAc,MAAO1E,CAAAA,EAAU,CAC1CA,CAAAA,CAGC,CAAC,IAAA,CAAK,KAAA,CAAM,MAAQ,CAAC,IAAA,CAAK,MAAM,SAAA,EAClC,MAAM,IAAA,CAAK,KAAA,EAAM,CAHnB,IAAA,CAAK,YAAY,CAAE,IAAA,CAAM,IAAA,CAAM,eAAA,CAAiB,KAAM,CAAC,EAM3D,CAAC,EACH,CAfmB,IAAA,CACA,YAAA,CACA,cAAA,CACA,OACA,QAAA,CAbX,KAAA,CAAmB,CACzB,IAAA,CAAM,IAAA,CACN,eAAA,CAAiB,MACjB,SAAA,CAAW,KAAA,CACX,KAAA,CAAO,IACT,CAAA,CACQ,SAAA,CAAY,IAAI,GAAA,CAoBhB,UAAA,EAAa,CACnB,IAAA,IAAWkB,CAAAA,IAAY,IAAA,CAAK,UAC1B,GAAI,CACFA,CAAAA,CAAS,IAAA,CAAK,KAAK,EACrB,OAASC,CAAAA,CAAK,CACZ,QAAQ,KAAA,CAAM,yBAAA,CAA2BA,CAAG,EAC9C,CAEJ,CAEA,YAAA,EAAe,CACb,OAAO,KAAK,KACd,CAEA,SAAA,CAAUD,CAAAA,CAAsC,CAC9C,OAAA,IAAA,CAAK,UAAU,GAAA,CAAIA,CAAQ,CAAA,CACpB,IAAM,CACX,IAAA,CAAK,UAAU,MAAA,CAAOA,CAAQ,EAChC,CACF,CAEQ,WAAA,CAAYyD,EAA6B,CAC/C,IAAA,CAAK,KAAA,CAAQ,CAAE,GAAG,IAAA,CAAK,MAAO,GAAGA,CAAQ,CAAA,CACzC,IAAA,CAAK,UAAA,GACP,CAEA,eAAA,EAAkB,CAChB,OAAO,IAAA,CAAK,KAAA,CAAM,eACpB,CAEA,MAAM,KAAA,CAAMC,CAAAA,CAAeC,CAAAA,CAAiC,CAC1D,IAAA,CAAK,YAAY,CAAE,SAAA,CAAW,IAAA,CAAM,KAAA,CAAO,IAAK,CAAC,EACjD,GAAI,CACF,IAAMpC,CAAAA,CAAW,IAAA,CAAK,MAAA,EAAQ,eAAiB,iBAAA,CACzCX,CAAAA,CAAO,MAAM,IAAA,CAAK,IAAA,CAAK,IAAA,CAA0CW,EAAU,CAAE,KAAA,CAAAmC,CAAAA,CAAO,QAAA,CAAAC,CAAS,CAAC,EAEpG,OAAA,IAAA,CAAK,YAAA,CAAa,cAAA,CAAe/C,CAAAA,CAAK,WAAW,CAAA,CACjD,KAAK,WAAA,CAAY,CAAE,IAAA,CAAMA,CAAAA,CAAK,IAAA,CAAM,eAAA,CAAiB,GAAM,SAAA,CAAW,CAAA,CAAM,CAAC,CAAA,CACtEA,CAAAA,CAAK,IACd,OAASX,CAAAA,CAAU,CACjB,IAAM2D,CAAAA,CAAS3D,CAAAA,CAAI,SAAW,cAAA,CAC9B,MAAA,IAAA,CAAK,WAAA,CAAY,CAAE,KAAA,CAAO2D,CAAAA,CAAQ,UAAW,KAAM,CAAC,CAAA,CAC9C3D,CACR,CACF,CAEA,MAAM,QAAA,CAASW,CAAAA,CAA8C,CAC3D,IAAA,CAAK,WAAA,CAAY,CAAE,UAAW,IAAA,CAAM,KAAA,CAAO,IAAK,CAAC,CAAA,CACjD,GAAI,CACF,IAAMW,CAAAA,CAAW,IAAA,CAAK,MAAA,EAAQ,gBAAA,EAAoB,oBAAA,CAC5ChD,EAAW,MAAM,IAAA,CAAK,IAAA,CAAK,IAAA,CAA0CgD,CAAAA,CAAUX,CAAI,EAEzF,OAAA,IAAA,CAAK,YAAA,CAAa,cAAA,CAAerC,CAAAA,CAAS,WAAW,CAAA,CACrD,KAAK,WAAA,CAAY,CAAE,KAAMA,CAAAA,CAAS,IAAA,CAAM,gBAAiB,CAAA,CAAA,CAAM,SAAA,CAAW,CAAA,CAAM,CAAC,CAAA,CAC1EA,CAAAA,CAAS,IAClB,CAAA,MAAS0B,CAAAA,CAAU,CACjB,IAAM2D,CAAAA,CAAS3D,CAAAA,CAAI,SAAW,qBAAA,CAC9B,MAAA,IAAA,CAAK,WAAA,CAAY,CAAE,KAAA,CAAO2D,CAAAA,CAAQ,UAAW,KAAM,CAAC,CAAA,CAC9C3D,CACR,CACF,CAEA,MAAM,MAAA,EAAwB,CAC5B,IAAA,CAAK,WAAA,CAAY,CAAE,SAAA,CAAW,KAAM,KAAA,CAAO,IAAK,CAAC,CAAA,CACjD,GAAI,CACF,IAAMsB,CAAAA,CAAW,IAAA,CAAK,MAAA,EAAQ,cAAA,EAAkB,kBAAA,CAChD,MAAM,KAAK,IAAA,CAAK,IAAA,CAAKA,EAAU,EAAC,CAAG,CAAE,WAAA,CAAa,CAAA,CAAM,CAAC,EAC3D,CAAA,KAAc,CAEd,QAAE,CACA,IAAA,CAAK,YAAA,CAAa,QAAA,EAAS,CAC3B,IAAA,CAAK,YAAY,CAAE,IAAA,CAAM,IAAA,CAAM,eAAA,CAAiB,KAAA,CAAO,SAAA,CAAW,KAAM,CAAC,CAAA,CACzE,IAAA,CAAK,QAAA,KACP,CACF,CAEA,MAAM,KAAA,EAA8B,CAClC,IAAIzC,CAAAA,CAAQ,MAAM,KAAK,YAAA,CAAa,cAAA,EAAe,CAInD,GAAA,CAHI,CAACA,CAAAA,EAAS,KAAK,YAAA,CAAa,cAAA,EAAe,IAC7CA,CAAAA,CAAQ,MAAM,IAAA,CAAK,eAAe,kBAAA,EAAmB,CAAA,CAEnD,CAACA,CAAAA,CACH,OAAA,IAAA,CAAK,YAAY,CAAE,IAAA,CAAM,IAAA,CAAM,eAAA,CAAiB,KAAA,CAAO,SAAA,CAAW,KAAM,CAAC,CAAA,CAClE,IAAA,CAGT,IAAA,CAAK,WAAA,CAAY,CAAE,UAAW,IAAA,CAAM,KAAA,CAAO,IAAK,CAAC,CAAA,CACjD,GAAI,CACF,IAAMyC,CAAAA,CAAW,IAAA,CAAK,MAAA,EAAQ,UAAA,EAAc,cAAA,CACtCsC,EAAO,MAAM,IAAA,CAAK,IAAA,CAAK,GAAA,CAAUtC,CAAQ,CAAA,CAC/C,YAAK,WAAA,CAAY,CAAE,IAAA,CAAAsC,CAAAA,CAAM,eAAA,CAAiB,CAAA,CAAA,CAAM,UAAW,CAAA,CAAM,CAAC,CAAA,CAC3DA,CACT,CAAA,MAAS5D,CAAAA,CAAU,CACjB,OAAA,IAAA,CAAK,WAAA,CAAY,CAAE,IAAA,CAAM,IAAA,CAAM,gBAAiB,KAAA,CAAO,KAAA,CAAOA,CAAAA,CAAI,OAAA,EAAW,8BAAA,CAAgC,SAAA,CAAW,KAAM,CAAC,CAAA,CACxH,IACT,CACF,CACF,EA2BO,SAAS6D,CAAAA,CAAqBlE,CAAAA,CAA8C,CACjF,IAAMd,CAAAA,CAAQ,IAAIa,EAAa,CAC7B,cAAA,CAAgBC,CAAAA,CAAO,cAAA,CACvB,cAAA,CAAgBA,CAAAA,CAAO,eACvB,YAAA,CAAcA,CAAAA,CAAO,IAAA,EAAM,YAC7B,CAAC,CAAA,CAEKmE,EAAU,IAAIvD,CAAAA,CAAeZ,CAAAA,CAAO,OAAA,CAASd,CAAAA,CAAOc,CAAAA,CAAO,cAAc,CAAA,CAE/Ed,CAAAA,CAAM,eAAA,CAAgB,IAAM,CACrBiF,CAAAA,CAAQ,qBACf,CAAC,EAED,IAAMR,CAAAA,CAAO,IAAIpC,CAAAA,CACfvB,CAAAA,CAAO,UAAA,CACPd,CAAAA,CACAiF,CAAAA,CACAnE,CAAAA,CAAO,eACPA,CAAAA,CAAO,eAAA,EAAmB,IAAA,CAC1BA,CAAAA,CAAO,IACT,CAAA,CAEM0C,EAAS,IAAIF,CAAAA,CAAcxC,CAAAA,CAAQd,CAAAA,CAAOiF,CAAAA,CAASnE,CAAAA,CAAO,cAAc,CAAA,CAE9Ed,CAAAA,CAAM,aAAA,CAAekF,CAAAA,EAAa,CAChC1B,CAAAA,CAAO,gBAAgB0B,CAAQ,CAAA,CAC3BA,CAAAA,CACG1B,CAAAA,CAAO,OAAA,EAAQ,CAEfA,EAAO,UAAA,GAEhB,CAAC,CAAA,CAED,IAAM2B,CAAAA,CAAO,IAAIX,CAAAA,CAAYC,CAAAA,CAAMzE,CAAAA,CAAOiF,CAAAA,CAASnE,CAAAA,CAAO,IAAA,CAAM,IAAM,CAC/D0C,CAAAA,CAAO,aACd,CAAC,EAED,OAAO,CACL,IAAA,CAAAiB,CAAAA,CACA,MAAA,CAAAjB,CAAAA,CACA,MAAAxD,CAAAA,CACA,OAAA,CAAAiF,CAAAA,CACA,IAAA,CAAAE,CAAAA,CACA,OAAA,CAAS,IAAM3B,CAAAA,CAAO,OAAA,EAAQ,CAC9B,UAAA,CAAY,IAAMA,CAAAA,CAAO,YAAW,CACpC,SAAA,CAAW,IAAMA,CAAAA,CAAO,SAAA,EAAU,CAClC,GAAI,CAACE,CAAAA,CAAOE,CAAAA,GAAYJ,CAAAA,CAAO,EAAA,CAAGE,CAAAA,CAAOE,CAAO,CAAA,CAChD,IAAA,CAAM,CAACF,CAAAA,CAAOE,CAAAA,GAAYJ,CAAAA,CAAO,KAAKE,CAAAA,CAAOE,CAAO,CAAA,CACpD,GAAA,CAAK,CAACF,CAAAA,CAAOE,IAAYJ,CAAAA,CAAO,GAAA,CAAIE,CAAAA,CAAOE,CAAO,CAAA,CAClD,IAAA,CAAM,CAACF,CAAAA,CAAO7D,CAAAA,GAAY2D,CAAAA,CAAO,IAAA,CAAKE,CAAAA,CAAO7D,CAAO,EACpD,OAAA,CAAS,CAAC6D,CAAAA,CAAO7D,CAAAA,CAASqE,CAAAA,GAAcV,CAAAA,CAAO,YAAYE,CAAAA,CAAO7D,CAAAA,CAASqE,CAAS,CAAA,CACpF,QAAA,CAAWH,CAAAA,EAASP,EAAO,QAAA,CAASO,CAAI,CAAA,CACxC,SAAA,CAAYA,CAAAA,EAASP,CAAAA,CAAO,UAAUO,CAAI,CAAA,CAC1C,QAAA,CAAU,IAAMP,CAAAA,CAAO,QAAA,GACvB,WAAA,CAAa,IAAMA,CAAAA,CAAO,WAAA,EAAY,CACtC,YAAA,CAAc,IAAMA,CAAAA,CAAO,YAAA,EAAa,CACxC,cAAA,CAAiB0B,CAAAA,EAAalF,CAAAA,CAAM,eAAekF,CAAQ,CAAA,CAC3D,eAAgB,IAAMlF,CAAAA,CAAM,gBAAe,CAC3C,gBAAA,CAAmBkB,CAAAA,EAAasC,CAAAA,CAAO,gBAAA,CAAiBtC,CAAQ,EAChE,kBAAA,CAAoB,IAAMsC,CAAAA,CAAO,kBAAA,EACnC,CACF,CAOA,IAAM4B,CAAAA,CAAkBC,aAAAA,CAAmC,IAAI,CAAA,CAQxD,SAASC,EAAiB,CAAE,MAAA,CAAAC,CAAAA,CAAQ,QAAA,CAAAC,CAAAA,CAAU,WAAA,CAAAC,EAAc,IAAK,CAAA,CAA0B,CAChG,OAAAC,SAAAA,CAAU,IAAM,CAId,GAFKH,CAAAA,CAAO,IAAA,CAAK,YAAA,EAAa,CAE1B,CAAA,CAACE,EACL,OAAKF,CAAAA,CAAO,OAAA,EAAQ,CACb,IAAM,CACNA,EAAO,UAAA,GACd,CACF,CAAA,CAAG,CAACE,EAAaF,CAAM,CAAC,CAAA,CAEjBI,CAAAA,CAAM,aAAA,CAAcP,CAAAA,CAAgB,SAAU,CAAE,KAAA,CAAOG,CAAO,CAAA,CAAGC,CAAQ,CAClF,CAgBO,SAASI,CAAAA,EAAoC,CAClD,IAAMC,CAAAA,CAAMC,UAAAA,CAAWV,CAAe,CAAA,CACtC,GAAI,CAACS,CAAAA,CAAK,MAAM,IAAI,MAAM,wDAAwD,CAAA,CAClF,OAAOA,CACT,CAEO,SAASE,GAA4B,CAE1C,OADeH,CAAAA,EAAkB,CACnB,IAChB,CAEO,SAASI,CAAAA,EAAsC,CACpD,IAAMT,CAAAA,CAASK,CAAAA,EAAkB,CACjC,OAAOK,oBAAAA,CACJC,CAAAA,EAAaX,EAAO,gBAAA,CAAiBW,CAAQ,EAC9C,IAAMX,CAAAA,CAAO,kBAAA,EAAmB,CAChC,KAAO,CAAE,UAAW,KAAA,CAAO,UAAA,CAAY,KAAM,CAAA,CAC/C,CACF,CAEO,SAASY,CAAAA,EAAyC,CACvD,IAAMZ,CAAAA,CAASK,CAAAA,EAAkB,CAC3B,CAAE,SAAA,CAAAQ,CAAAA,CAAW,UAAA,CAAAC,CAAW,CAAA,CAAIL,CAAAA,GAElC,OAAO,CACL,MAAA,CAAQT,CAAAA,CAAO,SAAA,EAAU,CACzB,UAAAa,CAAAA,CACA,UAAA,CAAAC,CAAAA,CACA,IAAA,CAAMd,CAAAA,CAAO,IAAA,CACb,QAASA,CAAAA,CAAO,OAAA,CAChB,EAAA,CAAIA,CAAAA,CAAO,EAAA,CACX,IAAA,CAAMA,EAAO,IAAA,CACb,GAAA,CAAKA,EAAO,GAAA,CACZ,QAAA,CAAUA,EAAO,QAAA,CACjB,SAAA,CAAWA,CAAAA,CAAO,SAAA,CAClB,QAAA,CAAUA,CAAAA,CAAO,QACnB,CACF,CAEO,SAASe,CAAAA,CAAmC5C,CAAAA,CAAeE,CAAAA,CAAsC,CACtG,IAAM2B,CAAAA,CAASK,CAAAA,EAAkB,CAC3BW,CAAAA,CAAaC,MAAAA,CAAO5C,CAAO,CAAA,CAEjC8B,SAAAA,CAAU,IAAM,CACda,CAAAA,CAAW,OAAA,CAAU3C,EACvB,CAAA,CAAG,CAACA,CAAO,CAAC,CAAA,CAEZ8B,SAAAA,CAAU,IAAM,CACd,IAAMe,CAAAA,CAAMlB,CAAAA,CAAO,EAAA,CAAa7B,CAAAA,CAAQ7D,GAAY0G,CAAAA,CAAW,OAAA,CAAQ1G,CAAO,CAAC,CAAA,CAC/E,OAAO,IAAM4G,CAAAA,EACf,EAAG,CAAClB,CAAAA,CAAQ7B,CAAK,CAAC,EACpB,CAEO,SAASgD,CAAAA,CAAiC3C,CAAAA,CAAcL,EAAeE,CAAAA,CAAsC,CAClH,IAAM2B,CAAAA,CAASK,CAAAA,EAAkB,CAC3BW,EAAaC,MAAAA,CAAO5C,CAAO,CAAA,CAEjC8B,SAAAA,CAAU,IAAM,CACda,EAAW,OAAA,CAAU3C,EACvB,CAAA,CAAG,CAACA,CAAO,CAAC,EAEZ8B,SAAAA,CAAU,IAAM,CACdH,CAAAA,CAAO,QAAA,CAASxB,CAAI,EACpB,IAAM0C,CAAAA,CAAMlB,CAAAA,CAAO,EAAA,CAAa7B,CAAAA,CAAQ7D,CAAAA,EAAY0G,EAAW,OAAA,CAAQ1G,CAAO,CAAC,CAAA,CAC/E,OAAO,IAAM,CACX4G,CAAAA,EAAI,CACJlB,EAAO,SAAA,CAAUxB,CAAI,EACvB,CACF,CAAA,CAAG,CAACwB,CAAAA,CAAQ7B,CAAAA,CAAOK,CAAI,CAAC,EAC1B,CASO,SAAS4C,CAAAA,EAAyB,CACvC,IAAMpB,EAASK,CAAAA,EAAkB,CAOjC,OAAO,CACL,GAPYK,oBAAAA,CACXhF,GAAOsE,CAAAA,CAAO,IAAA,CAAK,SAAA,CAAUtE,CAAE,CAAA,CAChC,IAAMsE,EAAO,IAAA,CAAK,YAAA,EAAa,CAC/B,KAAO,CAAE,IAAA,CAAM,KAAM,eAAA,CAAiB,KAAA,CAAO,SAAA,CAAW,KAAA,CAAO,KAAA,CAAO,IAAK,EAC7E,CAAA,CAIE,KAAA,CAAO,CAACX,CAAAA,CAAeC,CAAAA,GAAqBU,CAAAA,CAAO,KAAK,KAAA,CAAMX,CAAAA,CAAOC,CAAQ,CAAA,CAC7E,QAAA,CAAW/C,GAAkCyD,CAAAA,CAAO,IAAA,CAAK,QAAA,CAASzD,CAAI,CAAA,CACtE,MAAA,CAAQ,IAAMyD,CAAAA,CAAO,IAAA,CAAK,MAAA,EAAO,CACjC,WAAA,CAAa,IAAMA,EAAO,IAAA,CAAK,KAAA,EACjC,CACF,CAEO,SAASqB,EAAeC,CAAAA,CAA6C,CAC1E,IAAM1B,CAAAA,CAAOwB,CAAAA,EAAQ,CAErB,OAAAjB,SAAAA,CAAU,IAAM,CACd,GAAI,CAACP,CAAAA,CAAK,WAAa,CAACA,CAAAA,CAAK,eAAA,EACvB,OAAO,MAAA,CAAW,GAAA,CAAa,CACjC,IAAM2B,CAAAA,CAAc,MAAA,CAAO,QAAA,CAAS,QAAA,CAAW,MAAA,CAAO,SAAS,MAAA,CACzDC,CAAAA,CAAS,CAAA,gBAAA,EAAmB,kBAAA,CAAmBD,CAAW,CAAC,GAC7DD,CAAAA,CACFA,CAAAA,CAAWE,CAAM,CAAA,CAEjB,MAAA,CAAO,QAAA,CAAS,KAAOA,EAE3B,CAEJ,CAAA,CAAG,CAAC5B,CAAAA,CAAK,eAAA,CAAiBA,EAAK,SAAA,CAAW0B,CAAU,CAAC,CAAA,CAE9C1B,CACT,CAEO,SAAS6B,CAAAA,CAAQC,CAAAA,CAAsB,CAC5C,GAAM,CAAE,IAAA,CAAAlC,EAAM,eAAA,CAAAmC,CAAAA,CAAiB,SAAA,CAAAC,CAAU,CAAA,CAAIR,CAAAA,GAE7C,OAAO,CAAE,SAAA,CADSO,CAAAA,EAAmBnC,CAAAA,EAAM,IAAA,GAASkC,EAChC,IAAA,CAAAlC,CAAAA,CAAM,SAAA,CAAAoC,CAAU,CACtC","file":"index.mjs","sourcesContent":["import React, { createContext, useContext, useEffect, useRef, useSyncExternalStore, type ReactNode } from 'react';\nimport { io, type Socket, type ManagerOptions, type SocketOptions } from 'socket.io-client';\n\n/*\n realtimehttpauthclient\n ======================\n A professional HTTP & Realtime client library.\n Compatible with React, Bun, Node.js, and TanStack Start.\n*/\n\nexport type HttpMethod = 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE';\n\nexport type JsonValue =\n | string\n | number\n | boolean\n | null\n | JsonValue[]\n | { [key: string]: JsonValue };\n\nexport type HeadersMap = Record<string, string>;\n\nexport type QueryParams = Record<string, string | number | boolean | null | undefined>;\n\nexport type HttpRequestOptions<TBody = unknown> = {\n path: string;\n method?: HttpMethod;\n body?: TBody;\n headers?: HeadersMap;\n query?: QueryParams;\n signal?: AbortSignal;\n credentials?: RequestCredentials;\n retryOnAuth?: boolean;\n};\n\nexport type RefreshConfig = {\n enabled?: boolean;\n endpoint: string;\n method?: 'POST' | 'GET';\n onRefreshSuccess?: (tokens: { accessToken?: string | null }) => void;\n onRefreshFailure?: () => void;\n onRefreshStart?: () => void;\n onRefreshEnd?: () => void;\n maxRetries?: number;\n};\n\nexport type OfflineQueueItem = {\n id: string;\n event: string;\n payload: unknown;\n namespace?: string;\n createdAt: number;\n attempts: number;\n};\n\nexport type ConnectionState = {\n connected: boolean;\n connecting: boolean;\n};\n\nexport type User = {\n id: string;\n email: string;\n userName: string;\n role: string;\n};\n\nexport type AuthState = {\n user: User | null;\n isAuthenticated: boolean;\n isLoading: boolean;\n error: string | null;\n};\n\nexport type CsrfConfig = {\n enabled?: boolean;\n cookieName?: string;\n headerName?: string;\n /** Endpoint to call (GET) to receive the csrf-token cookie when it's missing */\n fetchEndpoint?: string;\n};\n\nexport type RealtimeClientConfig = {\n apiBaseUrl: string;\n socketUrl: string;\n withCredentials?: boolean;\n enabled?: boolean;\n autoConnect?: boolean;\n socketOptions?: Partial<ManagerOptions & SocketOptions>;\n getAccessToken?: () => Promise<string | null> | string | null;\n setAccessToken?: (token: string | null) => void;\n refresh?: RefreshConfig;\n auth?: {\n loginEndpoint?: string;\n registerEndpoint?: string;\n logoutEndpoint?: string;\n meEndpoint?: string;\n persistToken?: boolean;\n };\n csrf?: CsrfConfig;\n onUnauthorized?: () => void;\n onSocketConnect?: (socket: Socket) => void;\n onSocketDisconnect?: (reason: Socket.DisconnectReason) => void;\n onSocketError?: (error: Error) => void;\n offlineQueue?: {\n enabled?: boolean;\n storageKey?: string;\n maxItems?: number;\n };\n rooms?: {\n autoRejoin?: boolean;\n };\n ackTimeoutMs?: number;\n namespace?: string;\n debug?: boolean;\n};\n\nexport type AckResponse<T = unknown> = {\n ok: boolean;\n data?: T;\n error?: { message: string; code?: string; details?: unknown };\n};\n\nexport type AckCallback<T = unknown> = (response: AckResponse<T>) => void;\nexport type SocketEventHandler<T = unknown> = (payload: T) => void;\n\nfunction isBrowser() {\n return typeof window !== 'undefined' && typeof document !== 'undefined';\n}\n\nfunction uuid() {\n if (typeof crypto !== 'undefined' && 'randomUUID' in crypto) return crypto.randomUUID();\n return `${Date.now()}-${Math.random().toString(16).slice(2)}-${Math.random().toString(16).slice(2)}`;\n}\n\nfunction buildUrl(baseUrl: string, path: string, query?: QueryParams) {\n const url = new URL(path, baseUrl);\n if (query) {\n for (const [k, v] of Object.entries(query)) {\n if (v === undefined || v === null) continue;\n url.searchParams.set(k, String(v));\n }\n }\n return url.toString();\n}\n\nasync function parseResponse<T>(response: Response): Promise<T> {\n if (response.status === 204) return undefined as T;\n const ct = response.headers.get('content-type') || '';\n if (ct.includes('application/json')) return (await response.json()) as T;\n return (await response.text()) as T;\n}\n\nfunction createHttpError(status: number, message: string, payload?: unknown) {\n const error = new Error(message) as Error & { status?: number; payload?: unknown };\n error.status = status;\n error.payload = payload;\n return error;\n}\n\nfunction normalizeToken(token: string | null | undefined) {\n if (!token) return null;\n const trimmed = token.trim();\n return trimmed.length ? trimmed : null;\n}\n\nfunction storageAvailable() {\n try {\n return isBrowser() && !!window.localStorage;\n } catch {\n return false;\n }\n}\n\nfunction getCookieByName(name: string): string | null {\n if (!isBrowser()) return null;\n const match = document.cookie.match(new RegExp('(^| )' + name + '=([^;]+)'));\n if (match) return decodeURIComponent(match[2]);\n return null;\n}\n\nfunction safeAtob(str: string): string {\n if (typeof atob !== 'undefined') return atob(str);\n if (typeof Buffer !== 'undefined') return Buffer.from(str, 'base64').toString('binary');\n throw new Error('Neither atob nor Buffer is available');\n}\n\nfunction decodeJwtPayload(token: string) {\n try {\n const parts = token.split('.');\n if (parts.length !== 3) return null;\n const base64Url = parts[1];\n const base64 = base64Url.replace(/-/g, '+').replace(/_/g, '/');\n const jsonPayload = decodeURIComponent(\n safeAtob(base64)\n .split('')\n .map((c) => '%' + ('00' + c.charCodeAt(0).toString(16)).slice(-2))\n .join('')\n );\n return JSON.parse(jsonPayload) as { exp?: number; userId?: string; email?: string; role?: string; [key: string]: unknown };\n } catch {\n return null;\n }\n}\n\nexport class TokenManager {\n private accessToken: string | null = null;\n private getTokenFn?: RealtimeClientConfig['getAccessToken'];\n private setTokenFn?: RealtimeClientConfig['setAccessToken'];\n private persistToken: boolean = false;\n private refreshTimer: any = null;\n private onExpiringCb?: () => void;\n private tokenChangeListeners = new Set<(token: string | null) => void>();\n\n constructor(config: {\n getAccessToken?: RealtimeClientConfig['getAccessToken'];\n setAccessToken?: RealtimeClientConfig['setAccessToken'];\n persistToken?: boolean;\n } = {}) {\n this.getTokenFn = config.getAccessToken;\n this.setTokenFn = config.setAccessToken;\n this.persistToken = config.persistToken ?? false;\n\n if (this.persistToken && isBrowser()) {\n this.accessToken = sessionStorage.getItem('accessToken');\n }\n }\n\n setGetter(getter?: RealtimeClientConfig['getAccessToken']) {\n this.getTokenFn = getter;\n }\n\n setSetter(setter?: RealtimeClientConfig['setAccessToken']) {\n this.setTokenFn = setter;\n }\n\n onTokenExpiring(cb: () => void) {\n this.onExpiringCb = cb;\n }\n\n onTokenChange(cb: (token: string | null) => void) {\n this.tokenChangeListeners.add(cb);\n return () => {\n this.tokenChangeListeners.delete(cb);\n };\n }\n\n private emitTokenChange(token: string | null) {\n for (const listener of this.tokenChangeListeners) {\n try {\n listener(token);\n } catch (err) {\n console.error('Error in token change listener:', err);\n }\n }\n }\n\n setAccessToken(token: string | null) {\n if (this.refreshTimer) {\n clearTimeout(this.refreshTimer);\n this.refreshTimer = null;\n }\n\n const prevToken = this.accessToken;\n this.accessToken = normalizeToken(token);\n\n if (this.persistToken && isBrowser()) {\n if (this.accessToken) {\n sessionStorage.setItem('accessToken', this.accessToken);\n } else {\n sessionStorage.removeItem('accessToken');\n }\n }\n\n this.setTokenFn?.(this.accessToken);\n\n if (this.accessToken !== prevToken) {\n this.emitTokenChange(this.accessToken);\n }\n\n // Schedule proactive refresh\n if (this.accessToken && isBrowser()) {\n const payload = decodeJwtPayload(this.accessToken);\n if (payload && typeof payload.exp === 'number') {\n const expMs = payload.exp * 1000;\n const nowMs = Date.now();\n const ttl = expMs - nowMs;\n if (ttl > 10000) {\n const delay = Math.max(ttl - 30000, Math.floor(ttl * 0.8));\n this.refreshTimer = setTimeout(() => {\n this.onExpiringCb?.();\n }, delay);\n } else {\n setTimeout(() => this.onExpiringCb?.(), 0);\n }\n }\n }\n }\n\n getAccessTokenSync() {\n return this.accessToken;\n }\n\n async getAccessToken() {\n const fromFn = this.getTokenFn ? await this.getTokenFn() : null;\n const token = normalizeToken(fromFn) ?? this.accessToken;\n if (token && token !== this.accessToken) {\n this.setAccessToken(token);\n }\n return token;\n }\n\n isTokenExpired() {\n if (!this.accessToken) return true;\n const payload = decodeJwtPayload(this.accessToken);\n if (!payload || typeof payload.exp !== 'number') return true;\n return (Date.now() / 1000) + 10 > payload.exp;\n }\n\n clearAll() {\n this.setAccessToken(null);\n }\n}\n\nexport class RefreshManager {\n private refreshing: Promise<string | null> | null = null;\n private refreshAttempts = 0;\n private maxRefreshRetries = 3;\n\n constructor(\n private readonly config: RefreshConfig | undefined,\n private readonly tokenManager: TokenManager,\n private readonly onUnauthorized?: () => void,\n ) {\n this.maxRefreshRetries = config?.maxRetries ?? 3;\n }\n\n async refreshAccessToken(): Promise<string | null> {\n if (!this.config?.enabled) return null;\n if (this.refreshAttempts >= this.maxRefreshRetries) {\n this.tokenManager.clearAll();\n this.onUnauthorized?.();\n return null;\n }\n if (this.refreshing) return this.refreshing;\n\n this.refreshing = (async () => {\n this.config?.onRefreshStart?.();\n try {\n this.refreshAttempts++;\n const method = this.config?.method ?? 'POST';\n const response = await fetch(this.config!.endpoint, {\n method,\n credentials: 'include',\n headers: { Accept: 'application/json' },\n });\n\n if (!response.ok) {\n this.config?.onRefreshFailure?.();\n this.tokenManager.clearAll();\n this.onUnauthorized?.();\n return null;\n }\n\n const data = (await parseResponse<{ accessToken?: string | null }>(response)) || {};\n const token = normalizeToken(data.accessToken ?? null);\n if (token) {\n this.refreshAttempts = 0;\n this.tokenManager.setAccessToken(token);\n this.config?.onRefreshSuccess?.({ accessToken: token });\n return token;\n }\n\n this.config?.onRefreshFailure?.();\n this.tokenManager.clearAll();\n this.onUnauthorized?.();\n return null;\n } catch (err) {\n this.config?.onRefreshFailure?.();\n this.tokenManager.clearAll();\n this.onUnauthorized?.();\n return null;\n } finally {\n this.refreshing = null;\n this.config?.onRefreshEnd?.();\n }\n })();\n\n return this.refreshing;\n }\n}\n\nclass OfflineQueueManager {\n private items: OfflineQueueItem[] = [];\n private storageKey: string;\n private maxItems: number;\n\n constructor(config?: RealtimeClientConfig['offlineQueue']) {\n this.storageKey = config?.storageKey ?? 'realtime-offline-queue';\n this.maxItems = config?.maxItems ?? 200;\n this.load();\n }\n\n private load() {\n if (!storageAvailable()) return;\n try {\n const raw = window.localStorage.getItem(this.storageKey);\n this.items = raw ? (JSON.parse(raw) as OfflineQueueItem[]) : [];\n } catch {\n this.items = [];\n }\n }\n\n private save() {\n if (!storageAvailable()) return;\n try {\n window.localStorage.setItem(this.storageKey, JSON.stringify(this.items));\n } catch {\n // ignore storage failures\n }\n }\n\n enqueue(item: Omit<OfflineQueueItem, 'id' | 'createdAt' | 'attempts'>) {\n const next: OfflineQueueItem = {\n ...item,\n id: uuid(),\n createdAt: Date.now(),\n attempts: 0,\n };\n this.items = [...this.items, next].slice(-this.maxItems);\n this.save();\n return next;\n }\n\n all() {\n return [...this.items];\n }\n\n clear() {\n this.items = [];\n this.save();\n }\n\n remove(id: string) {\n this.items = this.items.filter((i) => i.id !== id);\n this.save();\n }\n}\n\nexport class HttpClient {\n private csrfTokenMemory: string | null = null;\n private csrfFetchPromise: Promise<void> | null = null;\n\n constructor(\n private readonly baseUrl: string,\n private readonly tokenManager: TokenManager,\n private readonly refreshManager: RefreshManager,\n private readonly onUnauthorized?: () => void,\n private readonly withCredentials = true,\n private readonly csrfConfig?: CsrfConfig,\n ) {}\n\n /** Fetch the CSRF token from the server (sets the cookie + stores in memory) */\n private async fetchCsrfToken(): Promise<void> {\n if (this.csrfFetchPromise) return this.csrfFetchPromise;\n const endpoint = this.csrfConfig?.fetchEndpoint;\n if (!endpoint) return;\n this.csrfFetchPromise = (async () => {\n try {\n const url = buildUrl(this.baseUrl, endpoint);\n const res = await fetch(url, {\n method: 'GET',\n credentials: this.withCredentials ? 'include' : 'same-origin',\n });\n if (res.ok) {\n const cookieName = this.csrfConfig?.cookieName ?? 'csrf-token';\n // Strategy 1: Check X-CSRF-Token response header\n const headerToken = res.headers.get('X-CSRF-Token');\n if (headerToken) {\n this.csrfTokenMemory = headerToken;\n return;\n }\n // Strategy 2: Read from JSON response body\n try {\n const data = await res.clone().json() as { csrfToken?: string };\n if (data?.csrfToken) {\n this.csrfTokenMemory = data.csrfToken;\n return;\n }\n } catch { /* not JSON */ }\n // Strategy 3: Give browser time to register Set-Cookie then read cookie\n await new Promise((r) => setTimeout(r, 50));\n this.csrfTokenMemory = getCookieByName(cookieName);\n }\n } catch {\n // ignore – CSRF will just be missing\n } finally {\n this.csrfFetchPromise = null;\n }\n })();\n return this.csrfFetchPromise;\n }\n\n /** Public: eagerly pre-fetch the CSRF token (call on app startup) */\n async prefetchCsrf(): Promise<void> {\n if (!this.csrfConfig?.enabled || !this.csrfConfig?.fetchEndpoint) return;\n const cookieName = this.csrfConfig.cookieName ?? 'csrf-token';\n const existing = getCookieByName(cookieName) ?? this.csrfTokenMemory;\n if (!existing) {\n await this.fetchCsrfToken();\n }\n }\n\n async request<TResponse = unknown, TBody = unknown>(options: HttpRequestOptions<TBody>): Promise<TResponse> {\n const { path, method = 'GET', body, headers, query, signal, credentials, retryOnAuth = true } = options;\n const token = await this.tokenManager.getAccessToken();\n const url = buildUrl(this.baseUrl, path, query);\n\n let csrfHeader: Record<string, string> = {};\n if (this.csrfConfig?.enabled && ['POST', 'PUT', 'PATCH', 'DELETE'].includes(method)) {\n const cookieName = this.csrfConfig.cookieName ?? 'csrf-token';\n let csrfToken = getCookieByName(cookieName) ?? this.csrfTokenMemory;\n if (!csrfToken && this.csrfConfig.fetchEndpoint) {\n // Auto-fetch the CSRF token when the cookie is absent\n await this.fetchCsrfToken();\n csrfToken = getCookieByName(cookieName) ?? this.csrfTokenMemory;\n }\n if (csrfToken) {\n csrfHeader[this.csrfConfig.headerName ?? 'X-CSRF-Token'] = csrfToken;\n }\n }\n\n const response = await fetch(url, {\n method,\n signal,\n credentials: credentials ?? (this.withCredentials ? 'include' : 'same-origin'),\n headers: {\n Accept: 'application/json',\n ...(body !== undefined ? { 'Content-Type': 'application/json' } : {}),\n ...(token ? { Authorization: `Bearer ${token}` } : {}),\n ...csrfHeader,\n ...headers,\n },\n body: body !== undefined ? JSON.stringify(body) : undefined,\n });\n\n if (response.status === 401 && retryOnAuth) {\n const newToken = await this.refreshManager.refreshAccessToken();\n if (newToken) {\n return this.request<TResponse, TBody>({ ...options, retryOnAuth: false });\n }\n this.onUnauthorized?.();\n }\n\n if (!response.ok) {\n let payload: unknown;\n try {\n payload = await parseResponse(response);\n } catch {\n payload = undefined;\n }\n let message = `HTTP ${response.status}`;\n if (payload && typeof payload === 'object' && 'message' in payload) {\n message = String((payload as any).message);\n } else if (payload && typeof payload === 'object' && 'error' in payload) {\n message = String((payload as any).error);\n }\n throw createHttpError(response.status, message, payload);\n }\n\n return parseResponse<TResponse>(response);\n }\n\n get<TResponse = unknown>(path: string, options: Omit<HttpRequestOptions, 'path' | 'method'> = {}) {\n return this.request<TResponse>({ ...options, path, method: 'GET' });\n }\n\n post<TResponse = unknown, TBody = unknown>(path: string, body?: TBody, options: Omit<HttpRequestOptions<TBody>, 'path' | 'method' | 'body'> = {}) {\n return this.request<TResponse, TBody>({ ...options, path, method: 'POST', body });\n }\n\n put<TResponse = unknown, TBody = unknown>(path: string, body?: TBody, options: Omit<HttpRequestOptions<TBody>, 'path' | 'method' | 'body'> = {}) {\n return this.request<TResponse, TBody>({ ...options, path, method: 'PUT', body });\n }\n\n patch<TResponse = unknown, TBody = unknown>(path: string, body?: TBody, options: Omit<HttpRequestOptions<TBody>, 'path' | 'method' | 'body'> = {}) {\n return this.request<TResponse, TBody>({ ...options, path, method: 'PATCH', body });\n }\n\n delete<TResponse = unknown>(path: string, options: Omit<HttpRequestOptions, 'path' | 'method'> = {}) {\n return this.request<TResponse>({ ...options, path, method: 'DELETE' });\n }\n}\n\nexport class SocketManager {\n private socket: Socket | null = null;\n private connectionPromise: Promise<Socket | null> | null = null;\n private rooms = new Map<string, number>();\n private listeners = new Map<string, Set<SocketEventHandler>>();\n private pendingEmits = new OfflineQueueManager();\n private connected = false;\n private connecting = false;\n private stateListeners = new Set<(state: ConnectionState) => void>();\n private connectionState: ConnectionState = { connected: false, connecting: false };\n\n constructor(\n private readonly config: RealtimeClientConfig,\n private readonly tokenManager: TokenManager,\n private readonly refreshManager: RefreshManager,\n private readonly onUnauthorized?: () => void,\n ) {\n if (config.offlineQueue?.enabled) {\n this.pendingEmits = new OfflineQueueManager(config.offlineQueue);\n }\n }\n\n private log(message: string, ...args: unknown[]) {\n if (this.config.debug) {\n console.log(`[RealtimeClient] ${message}`, ...args);\n }\n }\n\n private emitStateChange() {\n this.connectionState = { connected: this.connected, connecting: this.connecting };\n this.log(`State changed: connected=${this.connected}, connecting=${this.connecting}`);\n for (const listener of this.stateListeners) {\n listener(this.connectionState);\n }\n }\n\n getConnectionState() {\n return this.connectionState;\n }\n\n subscribeToState(listener: (state: ConnectionState) => void) {\n this.stateListeners.add(listener);\n return () => {\n this.stateListeners.delete(listener);\n };\n }\n\n isConnected() {\n return this.connected;\n }\n\n isConnecting() {\n return this.connecting;\n }\n\n getSocket(): Socket | null {\n return this.socket;\n }\n\n updateAuthToken(token: string | null) {\n if (this.socket) {\n this.socket.auth = token ? { token } : {};\n if (this.socket.connected) {\n this.log('Reconnecting socket due to auth token change...');\n this.socket.disconnect().connect();\n }\n }\n }\n\n async connect(): Promise<Socket | null> {\n if (this.config.enabled === false) return null;\n const token = await this.tokenManager.getAccessToken();\n if (!token) {\n this.log('Skipping socket connection: No access token available');\n return null;\n }\n if (this.socket) return this.socket;\n if (this.connectionPromise) return this.connectionPromise;\n\n this.connectionPromise = (async () => {\n try {\n this.connecting = true;\n this.emitStateChange();\n\n if (this.socket) return this.socket;\n\n this.log('Connecting socket...');\n await this.tokenManager.getAccessToken();\n\n if (this.socket) return this.socket;\n\n const socket = io(this.config.socketUrl, {\n autoConnect: false,\n transports: ['websocket'],\n withCredentials: this.config.withCredentials ?? true,\n auth: (cb) => {\n this.tokenManager.getAccessToken()\n .then((token) => cb(token ? { token } : {}))\n .catch(() => cb({}));\n },\n reconnection: true,\n reconnectionAttempts: Infinity,\n reconnectionDelay: 1000,\n reconnectionDelayMax: 30000,\n timeout: 20000,\n ...this.config.socketOptions,\n });\n\n this.socket = socket;\n this.registerCoreEvents(socket);\n\n // Re-register custom event listeners\n for (const [event, handlers] of this.listeners.entries()) {\n for (const handler of handlers) {\n socket.on(event, handler);\n }\n }\n\n socket.connect();\n return socket;\n } finally {\n this.connectionPromise = null;\n }\n })();\n\n return this.connectionPromise;\n }\n\n private registerCoreEvents(socket: Socket) {\n socket.on('connect', async () => {\n this.log('Socket connected');\n this.connected = true;\n this.connecting = false;\n this.emitStateChange();\n this.config.onSocketConnect?.(socket);\n await this.rejoinRooms();\n await this.flushOfflineQueue();\n });\n\n socket.on('disconnect', (reason) => {\n this.log('Socket disconnected', reason);\n this.connected = false;\n this.connecting = false;\n this.emitStateChange();\n this.config.onSocketDisconnect?.(reason);\n });\n\n socket.on('connect_error', async (error: Error & { message?: string }) => {\n this.log('Socket connection error', error);\n this.connecting = false;\n this.emitStateChange();\n this.config.onSocketError?.(error);\n const msg = String(error?.message || '').toLowerCase();\n if (msg.includes('unauthorized') || msg.includes('jwt') || msg.includes('token')) {\n const refreshed = await this.refreshManager.refreshAccessToken();\n if (refreshed) {\n this.log('Token refreshed successfully, retrying socket connection...');\n this.connecting = true;\n this.emitStateChange();\n socket.connect();\n } else {\n this.log('Token refresh failed on connect_error');\n this.onUnauthorized?.();\n await this.disconnect();\n }\n }\n });\n }\n\n async disconnect() {\n this.log('Disconnecting socket...');\n this.connectionPromise = null;\n this.socket?.removeAllListeners();\n this.socket?.disconnect();\n this.socket = null;\n this.connected = false;\n this.connecting = false;\n this.emitStateChange();\n }\n\n joinRoom(room: string) {\n const count = this.rooms.get(room) ?? 0;\n this.rooms.set(room, count + 1);\n this.log(`Joining room: ${room} (refCount: ${count + 1})`);\n if (count === 0 && this.socket?.connected) {\n this.socket.emit('room:join', { room });\n }\n }\n\n leaveRoom(room: string) {\n const count = this.rooms.get(room) ?? 0;\n if (count <= 1) {\n this.rooms.delete(room);\n this.log(`Leaving room: ${room} (refCount: 0)`);\n if (this.socket?.connected) {\n this.socket.emit('room:leave', { room });\n }\n } else {\n this.rooms.set(room, count - 1);\n this.log(`Decreasing room refCount: ${room} (refCount: ${count - 1})`);\n }\n }\n\n getRooms() {\n return new Set(this.rooms.keys());\n }\n\n private async rejoinRooms() {\n const autoRejoin = this.config.rooms?.autoRejoin ?? true;\n if (!autoRejoin) return;\n this.log('Rejoining all rooms...');\n for (const room of this.rooms.keys()) {\n this.socket?.emit('room:join', { room });\n }\n }\n\n on<TPayload = unknown>(event: string, handler: SocketEventHandler<TPayload>) {\n const set = this.listeners.get(event) ?? new Set<SocketEventHandler>();\n set.add(handler as SocketEventHandler);\n this.listeners.set(event, set);\n this.socket?.on(event, handler as SocketEventHandler);\n return () => this.off(event, handler);\n }\n\n off<TPayload = unknown>(event: string, handler: SocketEventHandler<TPayload>) {\n this.socket?.off(event, handler as SocketEventHandler);\n const set = this.listeners.get(event);\n set?.delete(handler as SocketEventHandler);\n if (set && set.size === 0) this.listeners.delete(event);\n }\n\n once<TPayload = unknown>(event: string, handler: SocketEventHandler<TPayload>) {\n this.socket?.once(event, handler as SocketEventHandler);\n }\n\n emit(event: string, payload?: unknown) {\n this.log(`Emitting event ${event}`, payload);\n if (this.socket?.connected) {\n this.socket.emit(event, payload);\n return;\n }\n\n if (this.config.offlineQueue?.enabled) {\n this.log(`Socket not connected, enqueuing event ${event} to offline queue`);\n this.pendingEmits.enqueue({ event, payload, namespace: this.config.namespace });\n return;\n }\n\n if (this.socket) {\n this.log(`Socket connecting/offline, buffering event ${event} in memory`);\n this.socket.emit(event, payload);\n return;\n }\n\n throw new Error('Socket is not initialized');\n }\n\n emitWithAck<TResponse = unknown>(event: string, payload?: unknown, timeoutMs?: number): Promise<TResponse> {\n this.log(`Emitting event with ack ${event}`, payload);\n return new Promise<TResponse>((resolve, reject) => {\n const socket = this.socket;\n const ttl = timeoutMs ?? this.config.ackTimeoutMs ?? 15000;\n\n if (!socket) {\n if (this.config.offlineQueue?.enabled) {\n this.log(`Socket not initialized, enqueuing ack event ${event} to offline queue`);\n this.pendingEmits.enqueue({ event, payload, namespace: this.config.namespace });\n reject(new Error('Socket offline, event queued instead of ack'));\n return;\n }\n reject(new Error('Socket not initialized'));\n return;\n }\n\n const timer = setTimeout(() => {\n this.log(`Ack timeout for event ${event}`);\n reject(new Error(`Ack timeout for event ${event}`));\n }, ttl);\n\n socket.emit(event, payload, (response: any) => {\n clearTimeout(timer);\n if (response && typeof response === 'object' && 'ok' in response) {\n const ack = response as AckResponse<TResponse>;\n if (ack.ok) {\n resolve(ack.data as TResponse);\n } else {\n reject(new Error(ack.error?.message || 'Ack error'));\n }\n } else {\n resolve(response as TResponse);\n }\n });\n });\n }\n\n async flushOfflineQueue() {\n if (!this.socket?.connected) return;\n if (!this.config.offlineQueue?.enabled) return;\n\n const queue = this.pendingEmits.all();\n if (!queue.length) return;\n\n for (const item of queue) {\n try {\n this.socket.emit(item.event, item.payload);\n this.pendingEmits.remove(item.id);\n } catch {\n // keep in queue for next reconnect\n }\n }\n }\n}\n\nexport class AuthManager {\n private state: AuthState = {\n user: null,\n isAuthenticated: false,\n isLoading: false,\n error: null,\n };\n private listeners = new Set<(state: AuthState) => void>();\n\n constructor(\n private readonly http: HttpClient,\n private readonly tokenManager: TokenManager,\n private readonly refreshManager: RefreshManager,\n private readonly config: RealtimeClientConfig['auth'],\n private readonly onLogout?: () => void,\n ) {\n this.tokenManager.onTokenChange(async (token) => {\n if (!token) {\n this.updateState({ user: null, isAuthenticated: false });\n } else {\n if (!this.state.user && !this.state.isLoading) {\n await this.getMe();\n }\n }\n });\n }\n\n private emitChange() {\n for (const listener of this.listeners) {\n try {\n listener(this.state);\n } catch (err) {\n console.error('Error in auth listener:', err);\n }\n }\n }\n\n getAuthState() {\n return this.state;\n }\n\n subscribe(listener: (state: AuthState) => void) {\n this.listeners.add(listener);\n return () => {\n this.listeners.delete(listener);\n };\n }\n\n private updateState(updater: Partial<AuthState>) {\n this.state = { ...this.state, ...updater };\n this.emitChange();\n }\n\n isAuthenticated() {\n return this.state.isAuthenticated;\n }\n\n async login(email: string, password: string): Promise<User> {\n this.updateState({ isLoading: true, error: null });\n try {\n const endpoint = this.config?.loginEndpoint ?? '/api/auth/login';\n const data = await this.http.post<{ accessToken: string; user: User }>(endpoint, { email, password });\n \n this.tokenManager.setAccessToken(data.accessToken);\n this.updateState({ user: data.user, isAuthenticated: true, isLoading: false });\n return data.user;\n } catch (err: any) {\n const errMsg = err.message || 'Login failed';\n this.updateState({ error: errMsg, isLoading: false });\n throw err;\n }\n }\n\n async register(data: Record<string, unknown>): Promise<User> {\n this.updateState({ isLoading: true, error: null });\n try {\n const endpoint = this.config?.registerEndpoint ?? '/api/auth/register';\n const response = await this.http.post<{ accessToken: string; user: User }>(endpoint, data);\n \n this.tokenManager.setAccessToken(response.accessToken);\n this.updateState({ user: response.user, isAuthenticated: true, isLoading: false });\n return response.user;\n } catch (err: any) {\n const errMsg = err.message || 'Registration failed';\n this.updateState({ error: errMsg, isLoading: false });\n throw err;\n }\n }\n\n async logout(): Promise<void> {\n this.updateState({ isLoading: true, error: null });\n try {\n const endpoint = this.config?.logoutEndpoint ?? '/api/auth/logout';\n await this.http.post(endpoint, {}, { retryOnAuth: false });\n } catch (err) {\n // Ignore logout API failures\n } finally {\n this.tokenManager.clearAll();\n this.updateState({ user: null, isAuthenticated: false, isLoading: false });\n this.onLogout?.();\n }\n }\n\n async getMe(): Promise<User | null> {\n let token = await this.tokenManager.getAccessToken();\n if (!token || this.tokenManager.isTokenExpired()) {\n token = await this.refreshManager.refreshAccessToken();\n }\n if (!token) {\n this.updateState({ user: null, isAuthenticated: false, isLoading: false });\n return null;\n }\n\n this.updateState({ isLoading: true, error: null });\n try {\n const endpoint = this.config?.meEndpoint ?? '/api/auth/me';\n const user = await this.http.get<User>(endpoint);\n this.updateState({ user, isAuthenticated: true, isLoading: false });\n return user;\n } catch (err: any) {\n this.updateState({ user: null, isAuthenticated: false, error: err.message || 'Failed to fetch user profile', isLoading: false });\n return null;\n }\n }\n}\n\nexport type RealtimeClient = {\n http: HttpClient;\n socket: SocketManager;\n token: TokenManager;\n refresh: RefreshManager;\n auth: AuthManager;\n connect: () => Promise<Socket | null>;\n disconnect: () => Promise<void>;\n getSocket: () => Socket | null;\n on: <T = unknown>(event: string, handler: SocketEventHandler<T>) => () => void;\n once: <T = unknown>(event: string, handler: SocketEventHandler<T>) => void;\n off: <T = unknown>(event: string, handler: SocketEventHandler<T>) => void;\n emit: (event: string, payload?: unknown) => void;\n emitAck: <T = unknown>(event: string, payload?: unknown, timeoutMs?: number) => Promise<T>;\n joinRoom: (room: string) => void;\n leaveRoom: (room: string) => void;\n getRooms: () => Set<string>;\n isConnected: () => boolean;\n isConnecting: () => boolean;\n setAccessToken: (token: string | null) => void;\n getAccessToken: () => Promise<string | null>;\n subscribeToState: (listener: (state: ConnectionState) => void) => () => void;\n getConnectionState: () => ConnectionState;\n};\n\nexport function createRealtimeClient(config: RealtimeClientConfig): RealtimeClient {\n const token = new TokenManager({\n getAccessToken: config.getAccessToken,\n setAccessToken: config.setAccessToken,\n persistToken: config.auth?.persistToken,\n });\n \n const refresh = new RefreshManager(config.refresh, token, config.onUnauthorized);\n \n token.onTokenExpiring(() => {\n void refresh.refreshAccessToken();\n });\n\n const http = new HttpClient(\n config.apiBaseUrl,\n token,\n refresh,\n config.onUnauthorized,\n config.withCredentials ?? true,\n config.csrf\n );\n \n const socket = new SocketManager(config, token, refresh, config.onUnauthorized);\n \n token.onTokenChange((newToken) => {\n socket.updateAuthToken(newToken);\n if (newToken) {\n void socket.connect();\n } else {\n void socket.disconnect();\n }\n });\n\n const auth = new AuthManager(http, token, refresh, config.auth, () => {\n void socket.disconnect();\n });\n\n return {\n http,\n socket,\n token,\n refresh,\n auth,\n connect: () => socket.connect(),\n disconnect: () => socket.disconnect(),\n getSocket: () => socket.getSocket(),\n on: (event, handler) => socket.on(event, handler),\n once: (event, handler) => socket.once(event, handler),\n off: (event, handler) => socket.off(event, handler),\n emit: (event, payload) => socket.emit(event, payload),\n emitAck: (event, payload, timeoutMs) => socket.emitWithAck(event, payload, timeoutMs),\n joinRoom: (room) => socket.joinRoom(room),\n leaveRoom: (room) => socket.leaveRoom(room),\n getRooms: () => socket.getRooms(),\n isConnected: () => socket.isConnected(),\n isConnecting: () => socket.isConnecting(),\n setAccessToken: (newToken) => token.setAccessToken(newToken),\n getAccessToken: () => token.getAccessToken(),\n subscribeToState: (listener) => socket.subscribeToState(listener),\n getConnectionState: () => socket.getConnectionState(),\n };\n}\n\n/* =========================\n React integration\n ========================= */\n\ntype RealtimeContextType = ReturnType<typeof createRealtimeClient> | null;\nconst RealtimeContext = createContext<RealtimeContextType>(null);\n\nexport type RealtimeProviderProps = {\n client: RealtimeClient;\n children: ReactNode;\n autoConnect?: boolean;\n};\n\nexport function RealtimeProvider({ client, children, autoConnect = true }: RealtimeProviderProps) {\n useEffect(() => {\n // Pre-fetch CSRF token on mount so it's ready before any mutation\n void client.http.prefetchCsrf();\n\n if (!autoConnect) return;\n void client.connect();\n return () => {\n void client.disconnect();\n };\n }, [autoConnect, client]);\n\n return React.createElement(RealtimeContext.Provider, { value: client }, children);\n}\n\nexport type UseSocketClientResult = {\n socket: Socket | null;\n connected: boolean;\n connecting: boolean;\n emit: (event: string, payload?: unknown) => void;\n emitAck: <T = unknown>(event: string, payload?: unknown, timeoutMs?: number) => Promise<T>;\n on: <T = unknown>(event: string, handler: SocketEventHandler<T>) => () => void;\n once: <T = unknown>(event: string, handler: SocketEventHandler<T>) => void;\n off: <T = unknown>(event: string, handler: SocketEventHandler<T>) => void;\n joinRoom: (room: string) => void;\n leaveRoom: (room: string) => void;\n getRooms: () => Set<string>;\n};\n\nexport function useRealtimeClient(): RealtimeClient {\n const ctx = useContext(RealtimeContext);\n if (!ctx) throw new Error('useRealtimeClient must be used inside RealtimeProvider');\n return ctx;\n}\n\nexport function useHttpClient(): HttpClient {\n const client = useRealtimeClient();\n return client.http;\n}\n\nexport function useConnectionState(): ConnectionState {\n const client = useRealtimeClient();\n return useSyncExternalStore(\n (callback) => client.subscribeToState(callback),\n () => client.getConnectionState(),\n () => ({ connected: false, connecting: false })\n );\n}\n\nexport function useSocketClient(): UseSocketClientResult {\n const client = useRealtimeClient();\n const { connected, connecting } = useConnectionState();\n\n return {\n socket: client.getSocket(),\n connected,\n connecting,\n emit: client.emit,\n emitAck: client.emitAck,\n on: client.on,\n once: client.once,\n off: client.off,\n joinRoom: client.joinRoom,\n leaveRoom: client.leaveRoom,\n getRooms: client.getRooms,\n };\n}\n\nexport function useSocketEvent<TPayload = unknown>(event: string, handler: (payload: TPayload) => void) {\n const client = useRealtimeClient();\n const handlerRef = useRef(handler);\n\n useEffect(() => {\n handlerRef.current = handler;\n }, [handler]);\n\n useEffect(() => {\n const off = client.on<TPayload>(event, (payload) => handlerRef.current(payload));\n return () => off();\n }, [client, event]);\n}\n\nexport function useRoomEvent<TPayload = unknown>(room: string, event: string, handler: (payload: TPayload) => void) {\n const client = useRealtimeClient();\n const handlerRef = useRef(handler);\n\n useEffect(() => {\n handlerRef.current = handler;\n }, [handler]);\n\n useEffect(() => {\n client.joinRoom(room);\n const off = client.on<TPayload>(event, (payload) => handlerRef.current(payload));\n return () => {\n off();\n client.leaveRoom(room);\n };\n }, [client, event, room]);\n}\n\nexport type UseAuthResult = AuthState & {\n login: (email: string, password: string) => Promise<User>;\n register: (data: Record<string, unknown>) => Promise<User>;\n logout: () => Promise<void>;\n refetchUser: () => Promise<User | null>;\n};\n\nexport function useAuth(): UseAuthResult {\n const client = useRealtimeClient();\n const state = useSyncExternalStore(\n (cb) => client.auth.subscribe(cb),\n () => client.auth.getAuthState(),\n () => ({ user: null, isAuthenticated: false, isLoading: false, error: null })\n );\n\n return {\n ...state,\n login: (email: string, password: string) => client.auth.login(email, password),\n register: (data: Record<string, unknown>) => client.auth.register(data),\n logout: () => client.auth.logout(),\n refetchUser: () => client.auth.getMe(),\n };\n}\n\nexport function useRequireAuth(onRedirect?: (redirectPath: string) => void) {\n const auth = useAuth();\n \n useEffect(() => {\n if (!auth.isLoading && !auth.isAuthenticated) {\n if (typeof window !== 'undefined') {\n const currentPath = window.location.pathname + window.location.search;\n const target = `/login?redirect=${encodeURIComponent(currentPath)}`;\n if (onRedirect) {\n onRedirect(target);\n } else {\n window.location.href = target;\n }\n }\n }\n }, [auth.isAuthenticated, auth.isLoading, onRedirect]);\n\n return auth;\n}\n\nexport function useRBAC(requiredRole: string) {\n const { user, isAuthenticated, isLoading } = useAuth();\n const hasAccess = isAuthenticated && user?.role === requiredRole;\n return { hasAccess, user, isLoading };\n}\n"]}
package/package.json ADDED
@@ -0,0 +1,50 @@
1
+ {
2
+ "name": "realtimehttpauthclient",
3
+ "version": "1.0.0",
4
+ "description": "A professional real-time HTTP client with automatic authentication lifecycle, JWT token rotation, CSRF protection, and Socket.IO integration for React, Bun, Node.js, and TanStack Start.",
5
+ "main": "dist/index.js",
6
+ "module": "dist/index.mjs",
7
+ "types": "dist/index.d.ts",
8
+ "exports": {
9
+ ".": {
10
+ "types": "./dist/index.d.ts",
11
+ "import": "./dist/index.mjs",
12
+ "require": "./dist/index.js",
13
+ "default": "./dist/index.mjs"
14
+ }
15
+ },
16
+ "files": [
17
+ "dist"
18
+ ],
19
+ "scripts": {
20
+ "build": "tsup",
21
+ "dev": "tsup --watch",
22
+ "prepublishOnly": "npm run build"
23
+ },
24
+ "keywords": [
25
+ "realtime",
26
+ "socket.io",
27
+ "auth",
28
+ "jwt",
29
+ "csrf",
30
+ "tanstack-start",
31
+ "react",
32
+ "http-client"
33
+ ],
34
+ "author": "AGBO Martin",
35
+ "license": "MIT",
36
+ "dependencies": {
37
+ "socket.io-client": "^4.8.3"
38
+ },
39
+ "peerDependencies": {
40
+ "react": ">=18.0.0",
41
+ "react-dom": ">=18.0.0"
42
+ },
43
+ "devDependencies": {
44
+ "@types/node": "^20.11.24",
45
+ "@types/react": "^18.3.12",
46
+ "@types/react-dom": "^18.3.1",
47
+ "tsup": "^8.3.5",
48
+ "typescript": "5.9.2"
49
+ }
50
+ }