@relax.js/core 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/LICENSE +21 -0
- package/README.md +188 -0
- package/dist/DataLoader.d.ts +51 -0
- package/dist/DependencyInjection.d.ts +271 -0
- package/dist/DependencyInjectionOld.d.ts +35 -0
- package/dist/Metadata.d.ts +8 -0
- package/dist/SequentialId.d.ts +47 -0
- package/dist/_alt/src/MustardEngine.d.ts +30 -0
- package/dist/_alt/src/MustardParser.d.ts +63 -0
- package/dist/_alt/src/MustardParser2.d.ts +35 -0
- package/dist/_alt/src/pipes.d.ts +93 -0
- package/dist/_alt/src/template.d.ts +166 -0
- package/dist/_alt/src/tools.d.ts +4 -0
- package/dist/_alt/tests/pipes.tests.d.ts +1 -0
- package/dist/_alt/tests/template.tests.d.ts +1 -0
- package/dist/_alt/vitest.config.d.ts +2 -0
- package/dist/collections/Index.d.ts +1 -0
- package/dist/collections/LinkedList.d.ts +75 -0
- package/dist/collections/Pager.d.ts +15 -0
- package/dist/collections/index.js +2 -0
- package/dist/collections/index.js.map +7 -0
- package/dist/collections/index.mjs +2 -0
- package/dist/collections/index.mjs.map +7 -0
- package/dist/components/Table.d.ts +13 -0
- package/dist/components/index.d.ts +4 -0
- package/dist/components/index.js +128 -0
- package/dist/components/index.js.map +7 -0
- package/dist/components/index.mjs +128 -0
- package/dist/components/index.mjs.map +7 -0
- package/dist/components/lists/Table.d.ts +59 -0
- package/dist/components/lists/TreeView.d.ts +67 -0
- package/dist/components/lists/index.d.ts +2 -0
- package/dist/components/loader.d.ts +60 -0
- package/dist/components/menus/MenuItem.d.ts +30 -0
- package/dist/components/menus/TopMenu.d.ts +16 -0
- package/dist/components/menus/index.d.ts +2 -0
- package/dist/components/panels/tabs.d.ts +15 -0
- package/dist/di/index.d.ts +1 -0
- package/dist/di/index.js +2 -0
- package/dist/di/index.js.map +7 -0
- package/dist/di/index.mjs +2 -0
- package/dist/di/index.mjs.map +7 -0
- package/dist/elements/CopyAttributes.d.ts +2 -0
- package/dist/elements/dom.d.ts +18 -0
- package/dist/elements/index.d.ts +2 -0
- package/dist/elements/index.js +2 -0
- package/dist/elements/index.js.map +7 -0
- package/dist/elements/index.mjs +2 -0
- package/dist/elements/index.mjs.map +7 -0
- package/dist/errors.d.ts +71 -0
- package/dist/forms/FormReader.d.ts +182 -0
- package/dist/forms/FormValidator.d.ts +114 -0
- package/dist/forms/ValidationRules.d.ts +103 -0
- package/dist/forms/index.d.ts +4 -0
- package/dist/forms/index.js +2 -0
- package/dist/forms/index.js.map +7 -0
- package/dist/forms/index.mjs +2 -0
- package/dist/forms/index.mjs.map +7 -0
- package/dist/forms/setFormData.d.ts +49 -0
- package/dist/getParentComponent.d.ts +43 -0
- package/dist/html/TableRenderer.d.ts +44 -0
- package/dist/html/TreeBinder.d.ts +9 -0
- package/dist/html/html.d.ts +55 -0
- package/dist/html/index.d.ts +5 -0
- package/dist/html/index.js +2 -0
- package/dist/html/index.js.map +7 -0
- package/dist/html/index.mjs +2 -0
- package/dist/html/index.mjs.map +7 -0
- package/dist/html/template.d.ts +167 -0
- package/dist/http/ServerSentEvents.d.ts +116 -0
- package/dist/http/SimpleWebSocket.d.ts +153 -0
- package/dist/http/http.d.ts +177 -0
- package/dist/http/index.d.ts +3 -0
- package/dist/http/index.js +2 -0
- package/dist/http/index.js.map +7 -0
- package/dist/http/index.mjs +2 -0
- package/dist/http/index.mjs.map +7 -0
- package/dist/i18n/i18n.d.ts +105 -0
- package/dist/i18n/icu.d.ts +64 -0
- package/dist/i18n/index.d.ts +2 -0
- package/dist/i18n/index.js +2 -0
- package/dist/i18n/index.js.map +7 -0
- package/dist/i18n/index.mjs +2 -0
- package/dist/i18n/index.mjs.map +7 -0
- package/dist/index.d.ts +16 -0
- package/dist/index.js +5 -0
- package/dist/index.js.map +7 -0
- package/dist/index.mjs +5 -0
- package/dist/index.mjs.map +7 -0
- package/dist/lib/DataLoader.d.ts +51 -0
- package/dist/lib/DependencyInjection.d.ts +271 -0
- package/dist/lib/InvokeParent.d.ts +10 -0
- package/dist/lib/Pipes.d.ts +236 -0
- package/dist/lib/SequentialId.d.ts +47 -0
- package/dist/lib/collections/Index.d.ts +1 -0
- package/dist/lib/collections/LinkedList.d.ts +75 -0
- package/dist/lib/collections/Pager.d.ts +15 -0
- package/dist/lib/collections/TableRenderer.d.ts +44 -0
- package/dist/lib/di/index.d.ts +1 -0
- package/dist/lib/elements/CopyAttributes.d.ts +2 -0
- package/dist/lib/elements/dom.d.ts +18 -0
- package/dist/lib/elements/index.d.ts +2 -0
- package/dist/lib/errors.d.ts +71 -0
- package/dist/lib/forms/FormReader.d.ts +182 -0
- package/dist/lib/forms/FormValidator.d.ts +114 -0
- package/dist/lib/forms/ValidationRules.d.ts +103 -0
- package/dist/lib/forms/index.d.ts +4 -0
- package/dist/lib/forms/setFormData.d.ts +49 -0
- package/dist/lib/getParentComponent.d.ts +43 -0
- package/dist/lib/html/TableRenderer.d.ts +44 -0
- package/dist/lib/html/TreeBinder.d.ts +9 -0
- package/dist/lib/html/html.d.ts +55 -0
- package/dist/lib/html/html2.d.ts +55 -0
- package/dist/lib/html/index.d.ts +5 -0
- package/dist/lib/html/m.d.ts +167 -0
- package/dist/lib/html/m2.d.ts +8 -0
- package/dist/lib/html/m3.d.ts +0 -0
- package/dist/lib/html/template.d.ts +167 -0
- package/dist/lib/http/HttpClient.d.ts +153 -0
- package/dist/lib/http/ServerSentEvents.d.ts +116 -0
- package/dist/lib/http/SimpleWebSocket.d.ts +153 -0
- package/dist/lib/http/http.d.ts +177 -0
- package/dist/lib/http/index.d.ts +3 -0
- package/dist/lib/i18n/i18n.d.ts +105 -0
- package/dist/lib/i18n/icu.d.ts +64 -0
- package/dist/lib/i18n/index.d.ts +2 -0
- package/dist/lib/index.d.ts +16 -0
- package/dist/lib/routing/NavigateRouteEvent.d.ts +52 -0
- package/dist/lib/routing/RouteLink.d.ts +7 -0
- package/dist/lib/routing/Routing.d.ts +270 -0
- package/dist/lib/routing/RoutingTarget.d.ts +22 -0
- package/dist/lib/routing/index.d.ts +7 -0
- package/dist/lib/routing/navigation.d.ts +70 -0
- package/dist/lib/routing/routeMatching.d.ts +21 -0
- package/dist/lib/routing/routeTargetRegistry.d.ts +23 -0
- package/dist/lib/routing/types.d.ts +130 -0
- package/dist/lib/templates/NodeTemplate.d.ts +38 -0
- package/dist/lib/templates/accessorParser.d.ts +87 -0
- package/dist/lib/templates/parseTemplate.d.ts +6 -0
- package/dist/lib/templates/tokenizer.d.ts +76 -0
- package/dist/lib/tools.d.ts +30 -0
- package/dist/lib/utils/index.d.ts +4 -0
- package/dist/pipes.d.ts +236 -0
- package/dist/routing/NavigateRouteEvent.d.ts +52 -0
- package/dist/routing/RouteLink.d.ts +7 -0
- package/dist/routing/RoutingTarget.d.ts +22 -0
- package/dist/routing/index.d.ts +7 -0
- package/dist/routing/index.js +5 -0
- package/dist/routing/index.js.map +7 -0
- package/dist/routing/index.mjs +5 -0
- package/dist/routing/index.mjs.map +7 -0
- package/dist/routing/navigation.d.ts +70 -0
- package/dist/routing/routeMatching.d.ts +21 -0
- package/dist/routing/routeTargetRegistry.d.ts +23 -0
- package/dist/routing/types.d.ts +130 -0
- package/dist/templates/NodeTemplate.d.ts +38 -0
- package/dist/templates/accessorParser.d.ts +87 -0
- package/dist/templates/parseTemplate.d.ts +6 -0
- package/dist/templates/tokenizer.d.ts +76 -0
- package/dist/tools.d.ts +30 -0
- package/dist/utils/index.d.ts +4 -0
- package/dist/utils/index.js +2 -0
- package/dist/utils/index.js.map +7 -0
- package/dist/utils/index.mjs +2 -0
- package/dist/utils/index.mjs.map +7 -0
- package/docs/Architecture.md +333 -0
- package/docs/DependencyInjection.md +237 -0
- package/docs/Errors.md +87 -0
- package/docs/GettingStarted.md +231 -0
- package/docs/Pipes.md +211 -0
- package/docs/Translations.md +312 -0
- package/docs/WhyRelaxjs.md +336 -0
- package/docs/elements/dom.md +102 -0
- package/docs/forms/creating-form-components.md +924 -0
- package/docs/forms/form-api.md +94 -0
- package/docs/forms/forms.md +99 -0
- package/docs/forms/patterns.md +311 -0
- package/docs/forms/reading-writing.md +365 -0
- package/docs/forms/validation.md +351 -0
- package/docs/html/TableRenderer.md +292 -0
- package/docs/html/html.md +175 -0
- package/docs/html/index.md +54 -0
- package/docs/html/template.md +422 -0
- package/docs/http/HttpClient.md +459 -0
- package/docs/http/ServerSentEvents.md +184 -0
- package/docs/http/index.md +109 -0
- package/docs/i18n/i18n.md +309 -0
- package/docs/i18n/intl-standard.md +178 -0
- package/docs/routing/RouteLink.md +98 -0
- package/docs/routing/Routing.md +332 -0
- package/docs/routing/RoutingTarget.md +136 -0
- package/docs/routing/layouts.md +207 -0
- package/docs/utilities.md +143 -0
- package/package.json +93 -0
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @module SimpleWebSocket
|
|
3
|
+
* WebSocket client with automatic reconnection and message queuing.
|
|
4
|
+
* Provides a reliable messaging layer over WebSocket connections.
|
|
5
|
+
*
|
|
6
|
+
* @example
|
|
7
|
+
* // Create and connect
|
|
8
|
+
* const ws = new WebSocketClient<ChatMessage>('wss://chat.example.com');
|
|
9
|
+
* ws.connect();
|
|
10
|
+
*
|
|
11
|
+
* // Send and receive
|
|
12
|
+
* await ws.send({ text: 'Hello' });
|
|
13
|
+
* const msg = await ws.receive();
|
|
14
|
+
*/
|
|
15
|
+
/**
|
|
16
|
+
* Simplified message event for WebSocket data.
|
|
17
|
+
*/
|
|
18
|
+
export interface SimpleDataEvent {
|
|
19
|
+
data: string | ArrayBufferLike | Blob | ArrayBufferView;
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Abstraction interface for WebSocket to enable unit testing.
|
|
23
|
+
* Implement this for custom WebSocket instances or mocks.
|
|
24
|
+
*/
|
|
25
|
+
export interface WebSocketAbstraction {
|
|
26
|
+
onopen: ((event: Event) => void) | null;
|
|
27
|
+
onerror: ((event: ErrorEvent) => void) | null;
|
|
28
|
+
onclose: ((event: CloseEvent) => void) | null;
|
|
29
|
+
onmessage: ((event: SimpleDataEvent) => void) | null;
|
|
30
|
+
send(data: string | ArrayBufferLike | Blob | ArrayBufferView): void;
|
|
31
|
+
close(): void;
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Factory function type for creating WebSocket instances.
|
|
35
|
+
*/
|
|
36
|
+
export type WebSocketFactory = () => WebSocketAbstraction;
|
|
37
|
+
/**
|
|
38
|
+
* Managed WebSocket client with automatic reconnection and message queuing.
|
|
39
|
+
*
|
|
40
|
+
* Features:
|
|
41
|
+
* - Automatic reconnection on disconnect
|
|
42
|
+
* - Message queuing when disconnected
|
|
43
|
+
* - Type-safe message handling with optional codecs
|
|
44
|
+
* - Promise-based receive API
|
|
45
|
+
*
|
|
46
|
+
* @template TMessage - The type of messages sent and received
|
|
47
|
+
*
|
|
48
|
+
* @example
|
|
49
|
+
* // Basic usage
|
|
50
|
+
* interface ChatMessage { user: string; text: string; }
|
|
51
|
+
*
|
|
52
|
+
* const client = new WebSocketClient<ChatMessage>('wss://chat.example.com');
|
|
53
|
+
* client.connect();
|
|
54
|
+
*
|
|
55
|
+
* // Send messages
|
|
56
|
+
* await client.send({ user: 'John', text: 'Hello!' });
|
|
57
|
+
*
|
|
58
|
+
* // Receive messages
|
|
59
|
+
* while (true) {
|
|
60
|
+
* const message = await client.receive();
|
|
61
|
+
* console.log(`${message.user}: ${message.text}`);
|
|
62
|
+
* }
|
|
63
|
+
*
|
|
64
|
+
* @example
|
|
65
|
+
* // With options
|
|
66
|
+
* const client = new WebSocketClient<Message>('wss://api.example.com', {
|
|
67
|
+
* autoReconnect: true,
|
|
68
|
+
* onConnect: (socket) => console.log('Connected'),
|
|
69
|
+
* onClose: () => console.log('Disconnected')
|
|
70
|
+
* });
|
|
71
|
+
*/
|
|
72
|
+
export declare class WebSocketClient<TMessage> {
|
|
73
|
+
private options?;
|
|
74
|
+
private ws?;
|
|
75
|
+
private receiveQueue;
|
|
76
|
+
private receivePromiseWrapper?;
|
|
77
|
+
private sendQueue;
|
|
78
|
+
private _isConnected;
|
|
79
|
+
private isSendingQueue;
|
|
80
|
+
private url?;
|
|
81
|
+
private wsFactory?;
|
|
82
|
+
private reconnectAttempts;
|
|
83
|
+
private shouldReconnect;
|
|
84
|
+
get connected(): boolean;
|
|
85
|
+
constructor(urlOrWebSocketFactory: string | WebSocketFactory, options?: WebSocketOptions<TMessage>);
|
|
86
|
+
connect(): void;
|
|
87
|
+
disconnect(): void;
|
|
88
|
+
send(data: TMessage): void;
|
|
89
|
+
/**
|
|
90
|
+
* Receive a new message.
|
|
91
|
+
*
|
|
92
|
+
* @throws Error if called while another receive() is pending.
|
|
93
|
+
* @throws Error if connection closes while waiting.
|
|
94
|
+
*/
|
|
95
|
+
receive(): Promise<TMessage>;
|
|
96
|
+
private onMessage;
|
|
97
|
+
private reConnect;
|
|
98
|
+
private sendInternal;
|
|
99
|
+
private sendQueueItems;
|
|
100
|
+
}
|
|
101
|
+
/**
|
|
102
|
+
* CODEC used for messages.
|
|
103
|
+
*/
|
|
104
|
+
export interface WebSocketCodec<TMessage> {
|
|
105
|
+
/**
|
|
106
|
+
*
|
|
107
|
+
* @param data
|
|
108
|
+
*/
|
|
109
|
+
encode(data: TMessage): string | ArrayBufferLike | Blob | ArrayBufferView;
|
|
110
|
+
/**
|
|
111
|
+
*
|
|
112
|
+
* @param data
|
|
113
|
+
*/
|
|
114
|
+
decode(data: string | ArrayBufferLike | Blob | ArrayBufferView): TMessage;
|
|
115
|
+
}
|
|
116
|
+
/**
|
|
117
|
+
* Configuration options for @see WebSocketClient.
|
|
118
|
+
*/
|
|
119
|
+
export interface WebSocketOptions<TMessage> {
|
|
120
|
+
/**
|
|
121
|
+
* CODEC to use for inbound and outbound messages (if something else that JSON should be used).
|
|
122
|
+
*/
|
|
123
|
+
codec?: WebSocketCodec<TMessage>;
|
|
124
|
+
/**
|
|
125
|
+
* Automatically reconnect when getting disconnected (default: true).
|
|
126
|
+
*/
|
|
127
|
+
autoReconnect?: boolean;
|
|
128
|
+
/**
|
|
129
|
+
* Initial delay in milliseconds before reconnecting (default: 1000).
|
|
130
|
+
* Uses exponential backoff on subsequent attempts.
|
|
131
|
+
*/
|
|
132
|
+
reconnectDelay?: number;
|
|
133
|
+
/**
|
|
134
|
+
* Maximum delay in milliseconds between reconnect attempts (default: 30000).
|
|
135
|
+
*/
|
|
136
|
+
maxReconnectDelay?: number;
|
|
137
|
+
/**
|
|
138
|
+
* Callback when the WS is connected.
|
|
139
|
+
*
|
|
140
|
+
* Can be used for authentication messages etc.
|
|
141
|
+
*
|
|
142
|
+
* @param socket Socket
|
|
143
|
+
*/
|
|
144
|
+
onConnect?: (socket: WebSocketClient<TMessage>) => void;
|
|
145
|
+
/**
|
|
146
|
+
* Invoked when the connection is closed.
|
|
147
|
+
*
|
|
148
|
+
* The connection will be automatically reconnected if configured (on by default).
|
|
149
|
+
*
|
|
150
|
+
* @param socket Socket.
|
|
151
|
+
*/
|
|
152
|
+
onClose?: (socket: WebSocketClient<TMessage>) => void;
|
|
153
|
+
}
|
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @module http
|
|
3
|
+
* Type-safe HTTP module built on fetch() with automatic JWT handling.
|
|
4
|
+
*
|
|
5
|
+
* @example
|
|
6
|
+
* import { configure, get, post } from './http';
|
|
7
|
+
*
|
|
8
|
+
* configure({ baseUrl: '/api' });
|
|
9
|
+
* const response = await get('/users');
|
|
10
|
+
* const users = response.as<User[]>();
|
|
11
|
+
*/
|
|
12
|
+
/**
|
|
13
|
+
* Configuration options for the http module.
|
|
14
|
+
*/
|
|
15
|
+
export interface HttpOptions {
|
|
16
|
+
/**
|
|
17
|
+
* Root URL to remote endpoint. Used so that each method only has to specify path in requests.
|
|
18
|
+
*/
|
|
19
|
+
baseUrl?: string;
|
|
20
|
+
/**
|
|
21
|
+
* Default content type to use if none is specified in the request method.
|
|
22
|
+
*/
|
|
23
|
+
contentType?: string;
|
|
24
|
+
/**
|
|
25
|
+
* Checks for a JWT token in localStorage to automatically include it in requests.
|
|
26
|
+
*
|
|
27
|
+
* Undefined = use "jwt", null = disable.
|
|
28
|
+
*/
|
|
29
|
+
bearerTokenName?: string | null;
|
|
30
|
+
/**
|
|
31
|
+
* Default request timeout in milliseconds.
|
|
32
|
+
* Uses `AbortSignal.timeout()` to automatically abort requests that take too long.
|
|
33
|
+
* Can be overridden per-request by passing a `signal` in `RequestInit`.
|
|
34
|
+
*
|
|
35
|
+
* @example
|
|
36
|
+
* configure({ baseUrl: '/api', timeout: 10000 }); // 10 second timeout
|
|
37
|
+
*/
|
|
38
|
+
timeout?: number;
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Response for request methods.
|
|
42
|
+
*/
|
|
43
|
+
export interface HttpResponse {
|
|
44
|
+
/**
|
|
45
|
+
* Http status code.
|
|
46
|
+
*/
|
|
47
|
+
statusCode: number;
|
|
48
|
+
/**
|
|
49
|
+
* Reason to why the status code was used.
|
|
50
|
+
*/
|
|
51
|
+
statusReason: string;
|
|
52
|
+
/**
|
|
53
|
+
* True if this is a 2xx response.
|
|
54
|
+
*/
|
|
55
|
+
success: boolean;
|
|
56
|
+
/**
|
|
57
|
+
* Content type of response body.
|
|
58
|
+
*/
|
|
59
|
+
contentType: string | null;
|
|
60
|
+
/**
|
|
61
|
+
* Body returned.
|
|
62
|
+
*
|
|
63
|
+
* Body has been read and deserialized from json (if the request content type was 'application/json' which is the default).
|
|
64
|
+
*/
|
|
65
|
+
body: unknown;
|
|
66
|
+
/**
|
|
67
|
+
* Charset used in body.
|
|
68
|
+
*/
|
|
69
|
+
charset: string | null;
|
|
70
|
+
/**
|
|
71
|
+
* Cast body to a type.
|
|
72
|
+
*/
|
|
73
|
+
as<T>(): T;
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* Error thrown when a request fails.
|
|
77
|
+
*/
|
|
78
|
+
export declare class HttpError extends Error {
|
|
79
|
+
message: string;
|
|
80
|
+
response: HttpResponse;
|
|
81
|
+
constructor(response: HttpResponse);
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* HTTP request options.
|
|
85
|
+
*/
|
|
86
|
+
export interface RequestOptions {
|
|
87
|
+
method?: 'GET' | 'POST' | 'PUT' | 'DELETE';
|
|
88
|
+
mode?: 'cors' | 'no-cors' | '*cors' | 'same-origin';
|
|
89
|
+
cache: 'default' | 'no-store' | 'reload' | 'no-cache' | 'force-cache' | 'only-if-cached';
|
|
90
|
+
credentials: 'omit' | 'same-origin' | 'include';
|
|
91
|
+
headers: Map<string, string>;
|
|
92
|
+
redirect: 'follow' | 'manual' | '*follow' | 'error';
|
|
93
|
+
referrerPolicy: 'no-referrer' | '*no-referrer-when-downgrade' | 'origin' | 'origin-when-cross-origin' | 'same-origin' | 'strict-origin' | 'strict-origin-when-cross-origin' | 'unsafe-url';
|
|
94
|
+
/**
|
|
95
|
+
* Will be serialized if the content type is json (and the body is an object).
|
|
96
|
+
*/
|
|
97
|
+
body: unknown;
|
|
98
|
+
}
|
|
99
|
+
declare type FetchFn = (input: RequestInfo | URL, init?: RequestInit) => Promise<Response>;
|
|
100
|
+
/**
|
|
101
|
+
* Replace the fetch implementation for testing purposes.
|
|
102
|
+
*
|
|
103
|
+
* @param fn - Custom fetch function, or undefined to restore the default.
|
|
104
|
+
*
|
|
105
|
+
* @example
|
|
106
|
+
* setFetch(async (url, options) => {
|
|
107
|
+
* return new Response(JSON.stringify({ id: 1 }), { status: 200 });
|
|
108
|
+
* });
|
|
109
|
+
*/
|
|
110
|
+
export declare function setFetch(fn?: FetchFn): void;
|
|
111
|
+
/**
|
|
112
|
+
* Configure the http module.
|
|
113
|
+
*
|
|
114
|
+
* @example
|
|
115
|
+
* configure({ baseUrl: '/api/v1', bearerTokenName: 'auth_token' });
|
|
116
|
+
*/
|
|
117
|
+
export declare function configure(options: HttpOptions): void;
|
|
118
|
+
/**
|
|
119
|
+
* Make an HTTP request.
|
|
120
|
+
*
|
|
121
|
+
* @param url - URL to make the request against.
|
|
122
|
+
* @param options - Request options.
|
|
123
|
+
* @returns Response from server.
|
|
124
|
+
*
|
|
125
|
+
* @example
|
|
126
|
+
* const response = await request('/users', { method: 'GET' });
|
|
127
|
+
*/
|
|
128
|
+
export declare function request(url: string, options?: RequestInit): Promise<HttpResponse>;
|
|
129
|
+
/**
|
|
130
|
+
* GET a resource.
|
|
131
|
+
*
|
|
132
|
+
* @param url - URL to get resource from.
|
|
133
|
+
* @param queryString - Optional query string parameters.
|
|
134
|
+
* @param options - Request options.
|
|
135
|
+
* @returns HTTP response.
|
|
136
|
+
*
|
|
137
|
+
* @example
|
|
138
|
+
* const response = await get('/users', { page: '1', limit: '10' });
|
|
139
|
+
* const users = response.as<User[]>();
|
|
140
|
+
*/
|
|
141
|
+
export declare function get(url: string, queryString?: Record<string, string>, options?: RequestInit): Promise<HttpResponse>;
|
|
142
|
+
/**
|
|
143
|
+
* POST a resource.
|
|
144
|
+
*
|
|
145
|
+
* @param url - URL to post to.
|
|
146
|
+
* @param data - Data to post.
|
|
147
|
+
* @param options - Request options.
|
|
148
|
+
* @returns HTTP response.
|
|
149
|
+
*
|
|
150
|
+
* @example
|
|
151
|
+
* const response = await post('/users', JSON.stringify({ name: 'John' }));
|
|
152
|
+
*/
|
|
153
|
+
export declare function post(url: string, data: BodyInit, options?: RequestInit): Promise<HttpResponse>;
|
|
154
|
+
/**
|
|
155
|
+
* PUT a resource.
|
|
156
|
+
*
|
|
157
|
+
* @param url - URL to resource.
|
|
158
|
+
* @param data - Data to put.
|
|
159
|
+
* @param options - Request options.
|
|
160
|
+
* @returns HTTP response.
|
|
161
|
+
*
|
|
162
|
+
* @example
|
|
163
|
+
* const response = await put('/users/1', JSON.stringify({ name: 'Jane' }));
|
|
164
|
+
*/
|
|
165
|
+
export declare function put(url: string, data: BodyInit, options?: RequestInit): Promise<HttpResponse>;
|
|
166
|
+
/**
|
|
167
|
+
* DELETE a resource.
|
|
168
|
+
*
|
|
169
|
+
* @param url - URL to resource.
|
|
170
|
+
* @param options - Request options.
|
|
171
|
+
* @returns HTTP response.
|
|
172
|
+
*
|
|
173
|
+
* @example
|
|
174
|
+
* const response = await del('/users/1');
|
|
175
|
+
*/
|
|
176
|
+
export declare function del(url: string, options?: RequestInit): Promise<HttpResponse>;
|
|
177
|
+
export {};
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
export { HttpOptions, HttpResponse, HttpError, RequestOptions, configure, setFetch, request, get, post, put, del } from './http';
|
|
2
|
+
export { SSEDataEvent, SSEOptions, SSEEventFactory, SSEClient } from './ServerSentEvents';
|
|
3
|
+
export { SimpleDataEvent, WebSocketAbstraction, WebSocketFactory, WebSocketClient, WebSocketCodec, WebSocketOptions } from './SimpleWebSocket';
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
var f=Object.defineProperty;var w=Object.getOwnPropertyDescriptor;var k=Object.getOwnPropertyNames;var x=Object.prototype.hasOwnProperty;var _=(s,e)=>{for(var t in e)f(s,t,{get:e[t],enumerable:!0})},R=(s,e,t,n)=>{if(e&&typeof e=="object"||typeof e=="function")for(let r of k(e))!x.call(s,r)&&r!==t&&f(s,r,{get:()=>e[r],enumerable:!(n=w(e,r))||n.enumerable});return s};var C=s=>R(f({},"__esModule",{value:!0}),s);var P={};_(P,{HttpError:()=>u,SSEClient:()=>h,SSEDataEvent:()=>c,WebSocketClient:()=>d,configure:()=>T,del:()=>E,get:()=>y,post:()=>b,put:()=>S,request:()=>o,setFetch:()=>m});module.exports=C(P);var u=class extends Error{constructor(e){super(e.statusReason),this.message=e.statusReason,this.response=e}},i={bearerTokenName:"jwt"},g=fetch;function m(s){g=s??fetch}function T(s){i={...i,...s},s.bearerTokenName===void 0&&(i.bearerTokenName="jwt")}async function o(s,e){if(i.bearerTokenName){let r=localStorage.getItem(i.bearerTokenName);if(r&&e){let a=e?.headers?new Headers(e.headers):new Headers;a.get("Authorization")||a.set("Authorization","Bearer "+r),e.headers=a}}i.timeout&&!e?.signal&&(e??={},e.signal=AbortSignal.timeout(i.timeout)),i.baseUrl&&(s[0]!=="/"&&i.baseUrl[i.baseUrl.length-1]!=="/"?s=`${i.baseUrl}/${s}`:s=i.baseUrl+s);let t=await g(s,e);if(!t.ok)return{statusCode:t.status,statusReason:t.statusText,success:!1,contentType:t.headers.get("content-type"),body:await t.text(),charset:t.headers.get("charset"),as(){throw new Error("No response received")}};let n=null;return t.status!==204&&(n=await t.json()),{success:!0,statusCode:t.status,statusReason:t.statusText,contentType:t.headers.get("content-type"),body:n,charset:t.headers.get("charset"),as(){return n}}}async function y(s,e,t){if(t?t.method="GET":t={method:"GET",headers:{"content-type":i.contentType??"application/json"}},e){let n="&";s.indexOf("?")===-1&&(n="?");for(let r in e){let a=e[r];s+=`${n}${r}=${a}`,n="&"}}return o(s,t)}async function b(s,e,t){return t?(t.method="POST",t.body=e):t={method:"POST",body:e,headers:{"content-type":i.contentType??"application/json"}},o(s,t)}async function S(s,e,t){return t?(t.method="PUT",t.body=e):t={method:"PUT",body:e,headers:{"content-type":i.contentType??"application/json"}},o(s,t)}async function E(s,e){return e?e.method="DELETE":e={method:"DELETE",headers:{"content-type":i.contentType??"application/json"}},o(s,e)}var c=class extends Event{constructor(t,n,r){super(t,{bubbles:!0,...r});this.data=n}},h=class{constructor(e,t){this.url=e;this.options=t;this.target=this.resolveTarget(t?.target)}get connected(){return this.eventSource?.readyState===EventSource.OPEN}connect(){if(this.eventSource)return;let e=new EventSource(this.url,{withCredentials:this.options?.withCredentials??!1});if(this.eventSource=e,e.onopen=()=>{this.options?.onConnect?.(this)},e.onerror=t=>{this.options?.onError?.(this,t)},this.options?.eventTypes&&this.options.eventTypes.length>0)for(let t of this.options.eventTypes)e.addEventListener(t,n=>{this.dispatchEvent(t,n.data)});else e.onmessage=t=>{this.dispatchEvent("message",t.data)}}disconnect(){this.eventSource?.close(),this.eventSource=void 0}resolveTarget(e){if(!e)return document;if(typeof e=="string"){let t=document.querySelector(e);if(!t)throw new Error(`SSEClient: Target element not found: ${e}`);return t}return e}dispatchEvent(e,t){let n;if(t.length>0&&(t[0]==="{"||t[0]==="["||t[0]==='"'))try{n=JSON.parse(t)}catch{n=t}else n=t;let r=this.options?.eventFactory?this.options.eventFactory(e,n):new c(e,n);this.target.dispatchEvent(r)}};var p=class{constructor(e,t){this.value=e;this.removeCallback=t;this.next=null;this.prev=null}remove(){this.prev.next=this.next,this.next.prev=this.prev,this.removeCallback()}},l=class{constructor(){this._length=0}addFirst(e){let t=new p(e,()=>this._length--);this._first?(t.next=this._first,this._first.prev=t,this._first=t):(this._first=t,this._last=this._first),this._length++}addLast(e){let t=new p(e,()=>this._length--);this._first?(t.prev=this._last,this._last.next=t,this._last=t):(this._first=t,this._last=this._first),this._length++}removeFirst(){if(!this.first)throw new Error("The list is empty.");let e=this._first.value;return this._first=this._first.next,this._length--,e}removeLast(){if(!this.last)throw new Error("The list is empty.");let e=this._last.value;return this._last=this._last.prev,this._length--,e}get length(){return this._length}get first(){return this._first}get firstValue(){return this._first?.value}get last(){return this._last}get lastValue(){return this._last?.value}};var d=class{constructor(e,t){this.options=t;this.receiveQueue=new l;this.sendQueue=new l;this._isConnected=!1;this.isSendingQueue=!1;this.reconnectAttempts=0;this.shouldReconnect=!0;typeof e=="string"?this.url=e:this.wsFactory=e}get connected(){return this._isConnected}connect(){this.shouldReconnect=!0,this.reconnectAttempts=0,this.reConnect()}disconnect(){this.shouldReconnect=!1,this.ws?.close()}send(e){if(!this._isConnected||this.isSendingQueue){this.sendQueue.addLast(e);return}this.sendQueue.length>0&&this.sendQueueItems(),this.sendInternal(e)}receive(){if(this.receivePromiseWrapper)throw new Error("You can only invoke receive() once at a time.");return this.receiveQueue.firstValue?Promise.resolve(this.receiveQueue.removeFirst()):(this.receivePromiseWrapper=new v,new Promise((e,t)=>{this.receivePromiseWrapper.resolve=e,this.receivePromiseWrapper.reject=t}))}onMessage(e){let t;if(this.options?.codec?t=this.options.codec.decode(e.data):typeof e.data=="string"&&e.data.length>0&&(e.data[0]==='"'||e.data[0]==="["||e.data[0]==="{")?t=JSON.parse(e.data):t=e.data,this.receivePromiseWrapper){this.receivePromiseWrapper.resolve(t),this.receivePromiseWrapper=void 0;return}this.receiveQueue.addLast(t)}reConnect(){let e=this.url?new WebSocket(this.url):this.wsFactory();this.ws=e,e.onmessage=t=>this.onMessage(t),e.onerror=()=>e.close(),e.onopen=()=>{this._isConnected=!0,this.reconnectAttempts=0,this.options?.onConnect?.(this),this.sendQueueItems()},e.onclose=()=>{if(this._isConnected=!1,this.receivePromiseWrapper&&(this.receivePromiseWrapper.reject(new Error("WebSocket connection closed")),this.receivePromiseWrapper=void 0),this.options?.onClose?.(this),this.shouldReconnect&&this.options?.autoReconnect!==!1){let t=this.options?.reconnectDelay??1e3,n=this.options?.maxReconnectDelay??3e4,r=Math.min(t*Math.pow(2,this.reconnectAttempts),n);this.reconnectAttempts++,setTimeout(()=>this.reConnect(),r)}}}sendInternal(e){let t;typeof e!="string"?this.options?.codec?t=this.options.codec.encode(e):t=JSON.stringify(e):t=e,this.ws.send(t)}sendQueueItems(){if(!this.isSendingQueue){for(this.isSendingQueue=!0;this.sendQueue.length>0;){let e=this.sendQueue.removeFirst();if(e==null)break;this.sendInternal(e)}this.isSendingQueue=!1}}},v=class{};
|
|
2
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../src/http/index.ts", "../../src/http/http.ts", "../../src/http/ServerSentEvents.ts", "../../src/collections/LinkedList.ts", "../../src/http/SimpleWebSocket.ts"],
|
|
4
|
+
"sourcesContent": ["export {\r\n HttpOptions,\r\n HttpResponse,\r\n HttpError,\r\n RequestOptions,\r\n configure,\r\n setFetch,\r\n request,\r\n get,\r\n post,\r\n put,\r\n del\r\n} from './http';\r\n\r\nexport {\r\n SSEDataEvent,\r\n SSEOptions,\r\n SSEEventFactory,\r\n SSEClient\r\n} from './ServerSentEvents';\r\n\r\nexport {\r\n SimpleDataEvent,\r\n WebSocketAbstraction,\r\n WebSocketFactory,\r\n WebSocketClient,\r\n WebSocketCodec,\r\n WebSocketOptions\r\n} from './SimpleWebSocket';\r\n", "/**\r\n * @module http\r\n * Type-safe HTTP module built on fetch() with automatic JWT handling.\r\n *\r\n * @example\r\n * import { configure, get, post } from './http';\r\n *\r\n * configure({ baseUrl: '/api' });\r\n * const response = await get('/users');\r\n * const users = response.as<User[]>();\r\n */\r\n\r\n/**\r\n * Configuration options for the http module.\r\n */\r\nexport interface HttpOptions {\r\n /**\r\n * Root URL to remote endpoint. Used so that each method only has to specify path in requests.\r\n */\r\n baseUrl?: string;\r\n\r\n /**\r\n * Default content type to use if none is specified in the request method.\r\n */\r\n contentType?: string;\r\n\r\n /**\r\n * Checks for a JWT token in localStorage to automatically include it in requests.\r\n *\r\n * Undefined = use \"jwt\", null = disable.\r\n */\r\n bearerTokenName?: string | null;\r\n\r\n /**\r\n * Default request timeout in milliseconds.\r\n * Uses `AbortSignal.timeout()` to automatically abort requests that take too long.\r\n * Can be overridden per-request by passing a `signal` in `RequestInit`.\r\n *\r\n * @example\r\n * configure({ baseUrl: '/api', timeout: 10000 }); // 10 second timeout\r\n */\r\n timeout?: number;\r\n}\r\n\r\n/**\r\n * Response for request methods.\r\n */\r\nexport interface HttpResponse {\r\n /**\r\n * Http status code.\r\n */\r\n statusCode: number;\r\n\r\n /**\r\n * Reason to why the status code was used.\r\n */\r\n statusReason: string;\r\n\r\n /**\r\n * True if this is a 2xx response.\r\n */\r\n success: boolean;\r\n\r\n /**\r\n * Content type of response body.\r\n */\r\n contentType: string | null;\r\n\r\n /**\r\n * Body returned.\r\n *\r\n * Body has been read and deserialized from json (if the request content type was 'application/json' which is the default).\r\n */\r\n body: unknown;\r\n\r\n /**\r\n * Charset used in body.\r\n */\r\n charset: string | null;\r\n\r\n /**\r\n * Cast body to a type.\r\n */\r\n as<T>(): T;\r\n}\r\n\r\n/**\r\n * Error thrown when a request fails.\r\n */\r\nexport class HttpError extends Error {\r\n message: string;\r\n response: HttpResponse;\r\n\r\n constructor(response: HttpResponse) {\r\n super(response.statusReason);\r\n this.message = response.statusReason;\r\n this.response = response;\r\n }\r\n}\r\n\r\n/**\r\n * HTTP request options.\r\n */\r\nexport interface RequestOptions {\r\n method?: 'GET' | 'POST' | 'PUT' | 'DELETE';\r\n mode?: 'cors' | 'no-cors' | '*cors' | 'same-origin';\r\n cache:\r\n | 'default'\r\n | 'no-store'\r\n | 'reload'\r\n | 'no-cache'\r\n | 'force-cache'\r\n | 'only-if-cached';\r\n credentials: 'omit' | 'same-origin' | 'include';\r\n headers: Map<string, string>;\r\n redirect: 'follow' | 'manual' | '*follow' | 'error';\r\n referrerPolicy:\r\n | 'no-referrer'\r\n | '*no-referrer-when-downgrade'\r\n | 'origin'\r\n | 'origin-when-cross-origin'\r\n | 'same-origin'\r\n | 'strict-origin'\r\n | 'strict-origin-when-cross-origin'\r\n | 'unsafe-url';\r\n\r\n /**\r\n * Will be serialized if the content type is json (and the body is an object).\r\n */\r\n body: unknown;\r\n}\r\n\r\ndeclare type FetchFn = (input: RequestInfo | URL, init?: RequestInit) => Promise<Response>;\r\n\r\nlet config: HttpOptions = {\r\n bearerTokenName: 'jwt'\r\n};\r\n\r\nlet fetchImpl: FetchFn = fetch;\r\n\r\n/**\r\n * Replace the fetch implementation for testing purposes.\r\n *\r\n * @param fn - Custom fetch function, or undefined to restore the default.\r\n *\r\n * @example\r\n * setFetch(async (url, options) => {\r\n * return new Response(JSON.stringify({ id: 1 }), { status: 200 });\r\n * });\r\n */\r\nexport function setFetch(fn?: FetchFn): void {\r\n fetchImpl = fn ?? fetch;\r\n}\r\n\r\n/**\r\n * Configure the http module.\r\n *\r\n * @example\r\n * configure({ baseUrl: '/api/v1', bearerTokenName: 'auth_token' });\r\n */\r\nexport function configure(options: HttpOptions): void {\r\n config = {\r\n ...config,\r\n ...options\r\n };\r\n if (options.bearerTokenName === undefined) {\r\n config.bearerTokenName = 'jwt';\r\n }\r\n}\r\n\r\n/**\r\n * Make an HTTP request.\r\n *\r\n * @param url - URL to make the request against.\r\n * @param options - Request options.\r\n * @returns Response from server.\r\n *\r\n * @example\r\n * const response = await request('/users', { method: 'GET' });\r\n */\r\nexport async function request(url: string, options?: RequestInit): Promise<HttpResponse> {\r\n if (config.bearerTokenName) {\r\n const token = localStorage.getItem(config.bearerTokenName);\r\n if (token && options) {\r\n const headers = options?.headers\r\n ? new Headers(options.headers)\r\n : new Headers();\r\n\r\n if (!headers.get('Authorization')) {\r\n headers.set('Authorization', 'Bearer ' + token);\r\n }\r\n\r\n options.headers = headers;\r\n }\r\n }\r\n\r\n if (config.timeout && !options?.signal) {\r\n options ??= {};\r\n options.signal = AbortSignal.timeout(config.timeout);\r\n }\r\n\r\n if (config.baseUrl) {\r\n if (url[0] !== '/' && config.baseUrl[config.baseUrl.length - 1] !== '/') {\r\n url = `${config.baseUrl}/${url}`;\r\n } else {\r\n url = config.baseUrl + url;\r\n }\r\n }\r\n\r\n const response = await fetchImpl(url, options);\r\n\r\n if (!response.ok) {\r\n return {\r\n statusCode: response.status,\r\n statusReason: response.statusText,\r\n success: false,\r\n contentType: response.headers.get('content-type'),\r\n body: await response.text(),\r\n charset: response.headers.get('charset'),\r\n\r\n as() {\r\n throw new Error('No response received');\r\n }\r\n };\r\n }\r\n\r\n let body: unknown | null = null;\r\n if (response.status !== 204) {\r\n body = await response.json();\r\n }\r\n\r\n return {\r\n success: true,\r\n statusCode: response.status,\r\n statusReason: response.statusText,\r\n contentType: response.headers.get('content-type'),\r\n body: body,\r\n charset: response.headers.get('charset'),\r\n as<T>() {\r\n return <T>body;\r\n }\r\n };\r\n}\r\n\r\n/**\r\n * GET a resource.\r\n *\r\n * @param url - URL to get resource from.\r\n * @param queryString - Optional query string parameters.\r\n * @param options - Request options.\r\n * @returns HTTP response.\r\n *\r\n * @example\r\n * const response = await get('/users', { page: '1', limit: '10' });\r\n * const users = response.as<User[]>();\r\n */\r\nexport async function get(\r\n url: string,\r\n queryString?: Record<string, string>,\r\n options?: RequestInit\r\n): Promise<HttpResponse> {\r\n if (!options) {\r\n options = {\r\n method: 'GET',\r\n headers: {\r\n 'content-type': config.contentType ?? 'application/json'\r\n }\r\n };\r\n } else {\r\n options.method = 'GET';\r\n }\r\n\r\n if (queryString) {\r\n let prefix = '&';\r\n if (url.indexOf('?') === -1) {\r\n prefix = '?';\r\n }\r\n\r\n for (const key in queryString) {\r\n const value = queryString[key];\r\n url += `${prefix}${key}=${value}`;\r\n prefix = '&';\r\n }\r\n }\r\n\r\n return request(url, options);\r\n}\r\n\r\n/**\r\n * POST a resource.\r\n *\r\n * @param url - URL to post to.\r\n * @param data - Data to post.\r\n * @param options - Request options.\r\n * @returns HTTP response.\r\n *\r\n * @example\r\n * const response = await post('/users', JSON.stringify({ name: 'John' }));\r\n */\r\nexport async function post(\r\n url: string,\r\n data: BodyInit,\r\n options?: RequestInit\r\n): Promise<HttpResponse> {\r\n if (!options) {\r\n options = {\r\n method: 'POST',\r\n body: data,\r\n headers: {\r\n 'content-type': config.contentType ?? 'application/json'\r\n }\r\n };\r\n } else {\r\n options.method = 'POST';\r\n options.body = data;\r\n }\r\n\r\n return request(url, options);\r\n}\r\n\r\n/**\r\n * PUT a resource.\r\n *\r\n * @param url - URL to resource.\r\n * @param data - Data to put.\r\n * @param options - Request options.\r\n * @returns HTTP response.\r\n *\r\n * @example\r\n * const response = await put('/users/1', JSON.stringify({ name: 'Jane' }));\r\n */\r\nexport async function put(\r\n url: string,\r\n data: BodyInit,\r\n options?: RequestInit\r\n): Promise<HttpResponse> {\r\n if (!options) {\r\n options = {\r\n method: 'PUT',\r\n body: data,\r\n headers: {\r\n 'content-type': config.contentType ?? 'application/json'\r\n }\r\n };\r\n } else {\r\n options.method = 'PUT';\r\n options.body = data;\r\n }\r\n\r\n return request(url, options);\r\n}\r\n\r\n/**\r\n * DELETE a resource.\r\n *\r\n * @param url - URL to resource.\r\n * @param options - Request options.\r\n * @returns HTTP response.\r\n *\r\n * @example\r\n * const response = await del('/users/1');\r\n */\r\nexport async function del(url: string, options?: RequestInit): Promise<HttpResponse> {\r\n if (!options) {\r\n options = {\r\n method: 'DELETE',\r\n headers: {\r\n 'content-type': config.contentType ?? 'application/json'\r\n }\r\n };\r\n } else {\r\n options.method = 'DELETE';\r\n }\r\n\r\n return request(url, options);\r\n}\r\n", "/**\r\n * @module ServerSentEvents\r\n * SSE client that dispatches received events as DOM events.\r\n * Uses the browser's built-in EventSource reconnection.\r\n *\r\n * @example\r\n * const sse = new SSEClient('/api/events', {\r\n * eventTypes: ['user-updated', 'order-created']\r\n * });\r\n * sse.connect();\r\n *\r\n * document.addEventListener('user-updated', (e: SSEDataEvent) => {\r\n * console.log('User updated:', e.data);\r\n * });\r\n */\r\n\r\n/**\r\n * Event dispatched when an SSE message is received.\r\n * The event name matches the SSE event type.\r\n */\r\nexport class SSEDataEvent extends Event {\r\n constructor(\r\n eventName: string,\r\n public data: unknown,\r\n eventInit?: EventInit\r\n ) {\r\n super(eventName, { bubbles: true, ...eventInit });\r\n }\r\n}\r\n\r\n/**\r\n * Factory function for creating custom event instances.\r\n *\r\n * @example\r\n * const factory: SSEEventFactory = (eventName, data) => {\r\n * switch (eventName) {\r\n * case 'user-updated':\r\n * return new UserUpdatedEvent(data as User);\r\n * default:\r\n * return new SSEDataEvent(eventName, data);\r\n * }\r\n * };\r\n */\r\nexport type SSEEventFactory = (eventName: string, data: unknown) => Event;\r\n\r\n/**\r\n * Configuration options for SSEClient.\r\n */\r\nexport interface SSEOptions {\r\n /**\r\n * Target element or CSS selector for event dispatching.\r\n * Defaults to document.\r\n */\r\n target?: string | Element;\r\n\r\n /**\r\n * Whether to send credentials with the request (default: false).\r\n */\r\n withCredentials?: boolean;\r\n\r\n /**\r\n * Specific SSE event types to listen for.\r\n * If not specified, listens to the default 'message' event.\r\n *\r\n * @example\r\n * eventTypes: ['user-updated', 'order-created']\r\n */\r\n eventTypes?: string[];\r\n\r\n /**\r\n * Factory function for creating custom event instances.\r\n * If not provided, SSEDataEvent is used.\r\n *\r\n * @example\r\n * eventFactory: (name, data) => new MyCustomEvent(name, data)\r\n */\r\n eventFactory?: SSEEventFactory;\r\n\r\n /**\r\n * Callback when connection is established.\r\n */\r\n onConnect?: (client: SSEClient) => void;\r\n\r\n /**\r\n * Callback when an error occurs.\r\n * Note: EventSource automatically reconnects on errors.\r\n */\r\n onError?: (client: SSEClient, error: Event) => void;\r\n}\r\n\r\n/**\r\n * Server-Sent Events client that dispatches received events as DOM events.\r\n * Uses the browser's built-in EventSource with automatic reconnection.\r\n *\r\n * @example\r\n * const sse = new SSEClient('/api/events', {\r\n * target: '#notifications',\r\n * eventTypes: ['notification', 'alert']\r\n * });\r\n *\r\n * sse.connect();\r\n *\r\n * document.querySelector('#notifications')\r\n * .addEventListener('notification', (e: SSEDataEvent) => {\r\n * showNotification(e.data);\r\n * });\r\n *\r\n * sse.disconnect();\r\n */\r\nexport class SSEClient {\r\n private eventSource?: EventSource;\r\n private target: Element | Document;\r\n\r\n /**\r\n * Whether the client is currently connected.\r\n */\r\n get connected(): boolean {\r\n return this.eventSource?.readyState === EventSource.OPEN;\r\n }\r\n\r\n constructor(\r\n private url: string,\r\n private options?: SSEOptions\r\n ) {\r\n this.target = this.resolveTarget(options?.target);\r\n }\r\n\r\n /**\r\n * Establish connection to the SSE endpoint.\r\n */\r\n connect(): void {\r\n if (this.eventSource) {\r\n return;\r\n }\r\n\r\n const eventSource = new EventSource(this.url, {\r\n withCredentials: this.options?.withCredentials ?? false\r\n });\r\n\r\n this.eventSource = eventSource;\r\n\r\n eventSource.onopen = () => {\r\n this.options?.onConnect?.(this);\r\n };\r\n\r\n eventSource.onerror = (error) => {\r\n this.options?.onError?.(this, error);\r\n };\r\n\r\n if (this.options?.eventTypes && this.options.eventTypes.length > 0) {\r\n for (const eventType of this.options.eventTypes) {\r\n eventSource.addEventListener(eventType, (e: MessageEvent) => {\r\n this.dispatchEvent(eventType, e.data);\r\n });\r\n }\r\n } else {\r\n eventSource.onmessage = (e: MessageEvent) => {\r\n this.dispatchEvent('message', e.data);\r\n };\r\n }\r\n }\r\n\r\n /**\r\n * Close the connection.\r\n */\r\n disconnect(): void {\r\n this.eventSource?.close();\r\n this.eventSource = undefined;\r\n }\r\n\r\n private resolveTarget(target?: string | Element): Element | Document {\r\n if (!target) {\r\n return document;\r\n }\r\n if (typeof target === 'string') {\r\n const element = document.querySelector(target);\r\n if (!element) {\r\n throw new Error(`SSEClient: Target element not found: ${target}`);\r\n }\r\n return element;\r\n }\r\n return target;\r\n }\r\n\r\n private dispatchEvent(eventName: string, rawData: string): void {\r\n let data: unknown;\r\n\r\n if (rawData.length > 0 && (rawData[0] === '{' || rawData[0] === '[' || rawData[0] === '\"')) {\r\n try {\r\n data = JSON.parse(rawData);\r\n } catch {\r\n data = rawData;\r\n }\r\n } else {\r\n data = rawData;\r\n }\r\n\r\n const event = this.options?.eventFactory\r\n ? this.options.eventFactory(eventName, data)\r\n : new SSEDataEvent(eventName, data);\r\n\r\n this.target.dispatchEvent(event);\r\n }\r\n}\r\n", "/**\r\n * A node in the @see LinkedList.\r\n */\r\nexport class Node<T> {\r\n /**\r\n * Next node unless last one.\r\n */\r\n public next: Node<T> | null = null;\r\n /**\r\n * Previous node unless first one.\r\n */\r\n public prev: Node<T> | null = null;\r\n\r\n /**\r\n * Constructor.\r\n * @param value Value contained in the node.\r\n */\r\n constructor(public value: T, private removeCallback: () => void) {}\r\n\r\n /**\r\n * Remove this node.\r\n * Will notify the list of the update to ensure correct element count.\r\n */\r\n remove() {\r\n this.prev.next = this.next;\r\n this.next.prev = this.prev;\r\n this.removeCallback();\r\n }\r\n}\r\n\r\n/**\r\n * A trivial linked list implementation.\r\n */\r\nexport class LinkedList<T> {\r\n private _first?: Node<T>;\r\n private _last?: Node<T>;\r\n private _length = 0;\r\n\r\n /**\r\n * Add a value to the beginning of the list.\r\n * @param value Value that should be contained in the node.\r\n */\r\n addFirst(value: T) {\r\n const newNode = new Node(value, () => this._length--);\r\n if (!this._first) {\r\n this._first = newNode;\r\n this._last = this._first;\r\n } else {\r\n newNode.next = this._first;\r\n this._first.prev = newNode;\r\n this._first = newNode;\r\n }\r\n\r\n this._length++;\r\n }\r\n\r\n /**\r\n * Add a value to the end of the list.\r\n * @param value Value that should be contained in a node.\r\n */\r\n addLast(value: T) {\r\n const newNode = new Node(value, () => this._length--);\r\n if (!this._first) {\r\n this._first = newNode;\r\n this._last = this._first;\r\n } else {\r\n newNode.prev = this._last;\r\n this._last.next = newNode;\r\n this._last = newNode;\r\n }\r\n\r\n this._length++;\r\n }\r\n\r\n /**\r\n * Remove a node from the beginning of the list.\r\n * @returns Value contained in the first node.\r\n */\r\n removeFirst(): T {\r\n if (!this.first) {\r\n throw new Error('The list is empty.');\r\n }\r\n\r\n const value = this._first.value;\r\n this._first = this._first.next;\r\n this._length--;\r\n return value;\r\n }\r\n\r\n /**\r\n * Remove a node from the end of the list.\r\n * @returns Value contained in the last node.\r\n */\r\n removeLast(): T {\r\n if (!this.last) {\r\n throw new Error('The list is empty.');\r\n }\r\n\r\n const value = this._last.value;\r\n this._last = this._last.prev;\r\n this._length--;\r\n return value;\r\n }\r\n\r\n /**\r\n * Number of nodes in the list.\r\n *\r\n * The count works as long as you do not manually remove nodes (by assigning next/prev to the neighbors).\r\n */\r\n get length(): number {\r\n return this._length;\r\n }\r\n\r\n /**\r\n * First ndoe.\r\n */\r\n get first(): Node<T> | undefined {\r\n return this._first;\r\n }\r\n\r\n /**\r\n * Contained value of the first node.\r\n */\r\n get firstValue(): T | undefined {\r\n return this._first?.value;\r\n }\r\n\r\n /**\r\n * Last node.\r\n */\r\n get last(): Node<T> | undefined {\r\n return this._last;\r\n }\r\n\r\n /**\r\n * Contained value of the last node.\r\n */\r\n get lastValue(): T | undefined {\r\n return this._last?.value;\r\n }\r\n}\r\n", "/**\r\n * @module SimpleWebSocket\r\n * WebSocket client with automatic reconnection and message queuing.\r\n * Provides a reliable messaging layer over WebSocket connections.\r\n *\r\n * @example\r\n * // Create and connect\r\n * const ws = new WebSocketClient<ChatMessage>('wss://chat.example.com');\r\n * ws.connect();\r\n *\r\n * // Send and receive\r\n * await ws.send({ text: 'Hello' });\r\n * const msg = await ws.receive();\r\n */\r\n\r\nimport { LinkedList } from '../collections/LinkedList';\r\n\r\n/**\r\n * Simplified message event for WebSocket data.\r\n */\r\nexport interface SimpleDataEvent {\r\n data: string | ArrayBufferLike | Blob | ArrayBufferView;\r\n}\r\n\r\n/**\r\n * Abstraction interface for WebSocket to enable unit testing.\r\n * Implement this for custom WebSocket instances or mocks.\r\n */\r\nexport interface WebSocketAbstraction {\r\n onopen: ((event: Event) => void) | null;\r\n onerror: ((event: ErrorEvent) => void) | null;\r\n onclose: ((event: CloseEvent) => void) | null;\r\n onmessage: ((event: SimpleDataEvent) => void) | null;\r\n send(data: string | ArrayBufferLike | Blob | ArrayBufferView): void;\r\n close(): void;\r\n}\r\n\r\n/**\r\n * Factory function type for creating WebSocket instances.\r\n */\r\nexport type WebSocketFactory = () => WebSocketAbstraction;\r\n\r\n/**\r\n * Managed WebSocket client with automatic reconnection and message queuing.\r\n *\r\n * Features:\r\n * - Automatic reconnection on disconnect\r\n * - Message queuing when disconnected\r\n * - Type-safe message handling with optional codecs\r\n * - Promise-based receive API\r\n *\r\n * @template TMessage - The type of messages sent and received\r\n *\r\n * @example\r\n * // Basic usage\r\n * interface ChatMessage { user: string; text: string; }\r\n *\r\n * const client = new WebSocketClient<ChatMessage>('wss://chat.example.com');\r\n * client.connect();\r\n *\r\n * // Send messages\r\n * await client.send({ user: 'John', text: 'Hello!' });\r\n *\r\n * // Receive messages\r\n * while (true) {\r\n * const message = await client.receive();\r\n * console.log(`${message.user}: ${message.text}`);\r\n * }\r\n *\r\n * @example\r\n * // With options\r\n * const client = new WebSocketClient<Message>('wss://api.example.com', {\r\n * autoReconnect: true,\r\n * onConnect: (socket) => console.log('Connected'),\r\n * onClose: () => console.log('Disconnected')\r\n * });\r\n */\r\nexport class WebSocketClient<TMessage> {\r\n private ws?: WebSocketAbstraction;\r\n private receiveQueue = new LinkedList<TMessage>();\r\n private receivePromiseWrapper?: PromiseWrapper<TMessage>;\r\n private sendQueue = new LinkedList<TMessage>();\r\n private _isConnected = false;\r\n private isSendingQueue = false;\r\n private url?: string;\r\n private wsFactory?: WebSocketFactory;\r\n private reconnectAttempts = 0;\r\n private shouldReconnect = true;\r\n\r\n get connected(): boolean {\r\n return this._isConnected;\r\n }\r\n\r\n constructor(\r\n urlOrWebSocketFactory: string | WebSocketFactory,\r\n private options?: WebSocketOptions<TMessage>\r\n ) {\r\n if (typeof urlOrWebSocketFactory === 'string') {\r\n this.url = urlOrWebSocketFactory;\r\n } else {\r\n this.wsFactory = urlOrWebSocketFactory;\r\n }\r\n }\r\n\r\n connect() {\r\n this.shouldReconnect = true;\r\n this.reconnectAttempts = 0;\r\n this.reConnect();\r\n }\r\n\r\n disconnect() {\r\n this.shouldReconnect = false;\r\n this.ws?.close();\r\n }\r\n\r\n send(data: TMessage): void {\r\n if (!this._isConnected || this.isSendingQueue) {\r\n this.sendQueue.addLast(data);\r\n return;\r\n }\r\n\r\n if (this.sendQueue.length > 0) {\r\n this.sendQueueItems();\r\n }\r\n\r\n this.sendInternal(data);\r\n }\r\n\r\n /**\r\n * Receive a new message.\r\n *\r\n * @throws Error if called while another receive() is pending.\r\n * @throws Error if connection closes while waiting.\r\n */\r\n receive(): Promise<TMessage> {\r\n if (this.receivePromiseWrapper) {\r\n throw new Error('You can only invoke receive() once at a time.');\r\n }\r\n\r\n if (this.receiveQueue.firstValue) {\r\n return Promise.resolve(this.receiveQueue.removeFirst());\r\n }\r\n\r\n this.receivePromiseWrapper = new PromiseWrapper();\r\n return new Promise((resolve, reject) => {\r\n this.receivePromiseWrapper.resolve = resolve;\r\n this.receivePromiseWrapper.reject = reject;\r\n });\r\n }\r\n\r\n private onMessage(ev: SimpleDataEvent) {\r\n let msg: TMessage;\r\n if (this.options?.codec) {\r\n msg = this.options.codec.decode(ev.data);\r\n } else if (typeof ev.data === 'string') {\r\n if (\r\n ev.data.length > 0 &&\r\n (ev.data[0] === '\"' || ev.data[0] === '[' || ev.data[0] === '{')\r\n ) {\r\n msg = JSON.parse(ev.data);\r\n } else {\r\n msg = ev.data as TMessage;\r\n }\r\n } else {\r\n msg = ev.data as TMessage;\r\n }\r\n\r\n if (this.receivePromiseWrapper) {\r\n this.receivePromiseWrapper.resolve(msg);\r\n this.receivePromiseWrapper = undefined;\r\n return;\r\n }\r\n\r\n this.receiveQueue.addLast(msg);\r\n }\r\n\r\n private reConnect() {\r\n const ws = this.url ? new WebSocket(this.url) : this.wsFactory();\r\n this.ws = ws;\r\n ws.onmessage = (evt: SimpleDataEvent) => this.onMessage(evt);\r\n ws.onerror = () => ws.close();\r\n ws.onopen = () => {\r\n this._isConnected = true;\r\n this.reconnectAttempts = 0;\r\n this.options?.onConnect?.(this);\r\n this.sendQueueItems();\r\n };\r\n ws.onclose = () => {\r\n this._isConnected = false;\r\n\r\n if (this.receivePromiseWrapper) {\r\n this.receivePromiseWrapper.reject(new Error('WebSocket connection closed'));\r\n this.receivePromiseWrapper = undefined;\r\n }\r\n\r\n this.options?.onClose?.(this);\r\n\r\n if (this.shouldReconnect && this.options?.autoReconnect !== false) {\r\n const baseDelay = this.options?.reconnectDelay ?? 1000;\r\n const maxDelay = this.options?.maxReconnectDelay ?? 30000;\r\n const delay = Math.min(baseDelay * Math.pow(2, this.reconnectAttempts), maxDelay);\r\n this.reconnectAttempts++;\r\n setTimeout(() => this.reConnect(), delay);\r\n }\r\n };\r\n }\r\n\r\n private sendInternal(data: TMessage) {\r\n let dataToSend: string | ArrayBufferLike | Blob | ArrayBufferView;\r\n if (typeof data !== 'string') {\r\n if (this.options?.codec) {\r\n dataToSend = this.options.codec.encode(data);\r\n } else {\r\n dataToSend = JSON.stringify(data);\r\n }\r\n } else {\r\n dataToSend = data;\r\n }\r\n\r\n this.ws.send(dataToSend);\r\n }\r\n\r\n private sendQueueItems(): void {\r\n if (this.isSendingQueue) {\r\n return;\r\n }\r\n\r\n this.isSendingQueue = true;\r\n while (this.sendQueue.length > 0) {\r\n const item = this.sendQueue.removeFirst();\r\n if (item == null) {\r\n break;\r\n }\r\n\r\n this.sendInternal(item);\r\n }\r\n\r\n this.isSendingQueue = false;\r\n }\r\n}\r\n\r\n/**\r\n * CODEC used for messages.\r\n */\r\nexport interface WebSocketCodec<TMessage> {\r\n /**\r\n *\r\n * @param data\r\n */\r\n encode(data: TMessage): string | ArrayBufferLike | Blob | ArrayBufferView;\r\n\r\n /**\r\n *\r\n * @param data\r\n */\r\n decode(data: string | ArrayBufferLike | Blob | ArrayBufferView): TMessage;\r\n}\r\n\r\n/**\r\n * Configuration options for @see WebSocketClient.\r\n */\r\nexport interface WebSocketOptions<TMessage> {\r\n /**\r\n * CODEC to use for inbound and outbound messages (if something else that JSON should be used).\r\n */\r\n codec?: WebSocketCodec<TMessage>;\r\n\r\n /**\r\n * Automatically reconnect when getting disconnected (default: true).\r\n */\r\n autoReconnect?: boolean;\r\n\r\n /**\r\n * Initial delay in milliseconds before reconnecting (default: 1000).\r\n * Uses exponential backoff on subsequent attempts.\r\n */\r\n reconnectDelay?: number;\r\n\r\n /**\r\n * Maximum delay in milliseconds between reconnect attempts (default: 30000).\r\n */\r\n maxReconnectDelay?: number;\r\n\r\n /**\r\n * Callback when the WS is connected.\r\n *\r\n * Can be used for authentication messages etc.\r\n *\r\n * @param socket Socket\r\n */\r\n onConnect?: (socket: WebSocketClient<TMessage>) => void;\r\n\r\n /**\r\n * Invoked when the connection is closed.\r\n *\r\n * The connection will be automatically reconnected if configured (on by default).\r\n *\r\n * @param socket Socket.\r\n */\r\n onClose?: (socket: WebSocketClient<TMessage>) => void;\r\n}\r\n\r\nclass PromiseWrapper<TMessage> {\r\n resolve!: (value: TMessage | PromiseLike<TMessage>) => void;\r\n reject!: (reason?: unknown) => void;\r\n}\r\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": ["index_exports", "__export", "HttpError", "SSEClient", "SSEDataEvent", "WebSocketClient", "configure", "del", "get", "post", "put", "request", "setFetch", "__toCommonJS", "HttpError", "response", "config", "fetchImpl", "setFetch", "fn", "configure", "options", "request", "url", "token", "headers", "body", "get", "queryString", "prefix", "key", "value", "post", "data", "put", "del", "SSEDataEvent", "eventName", "data", "eventInit", "SSEClient", "url", "options", "eventSource", "error", "eventType", "e", "target", "element", "rawData", "event", "Node", "value", "removeCallback", "LinkedList", "newNode", "WebSocketClient", "urlOrWebSocketFactory", "options", "LinkedList", "data", "PromiseWrapper", "resolve", "reject", "ev", "msg", "ws", "evt", "baseDelay", "maxDelay", "delay", "dataToSend", "item"]
|
|
7
|
+
}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
var h=class extends Error{constructor(e){super(e.statusReason),this.message=e.statusReason,this.response=e}},r={bearerTokenName:"jwt"},v=fetch;function g(s){v=s??fetch}function m(s){r={...r,...s},s.bearerTokenName===void 0&&(r.bearerTokenName="jwt")}async function a(s,e){if(r.bearerTokenName){let i=localStorage.getItem(r.bearerTokenName);if(i&&e){let o=e?.headers?new Headers(e.headers):new Headers;o.get("Authorization")||o.set("Authorization","Bearer "+i),e.headers=o}}r.timeout&&!e?.signal&&(e??={},e.signal=AbortSignal.timeout(r.timeout)),r.baseUrl&&(s[0]!=="/"&&r.baseUrl[r.baseUrl.length-1]!=="/"?s=`${r.baseUrl}/${s}`:s=r.baseUrl+s);let t=await v(s,e);if(!t.ok)return{statusCode:t.status,statusReason:t.statusText,success:!1,contentType:t.headers.get("content-type"),body:await t.text(),charset:t.headers.get("charset"),as(){throw new Error("No response received")}};let n=null;return t.status!==204&&(n=await t.json()),{success:!0,statusCode:t.status,statusReason:t.statusText,contentType:t.headers.get("content-type"),body:n,charset:t.headers.get("charset"),as(){return n}}}async function T(s,e,t){if(t?t.method="GET":t={method:"GET",headers:{"content-type":r.contentType??"application/json"}},e){let n="&";s.indexOf("?")===-1&&(n="?");for(let i in e){let o=e[i];s+=`${n}${i}=${o}`,n="&"}}return a(s,t)}async function y(s,e,t){return t?(t.method="POST",t.body=e):t={method:"POST",body:e,headers:{"content-type":r.contentType??"application/json"}},a(s,t)}async function b(s,e,t){return t?(t.method="PUT",t.body=e):t={method:"PUT",body:e,headers:{"content-type":r.contentType??"application/json"}},a(s,t)}async function S(s,e){return e?e.method="DELETE":e={method:"DELETE",headers:{"content-type":r.contentType??"application/json"}},a(s,e)}var l=class extends Event{constructor(t,n,i){super(t,{bubbles:!0,...i});this.data=n}},p=class{constructor(e,t){this.url=e;this.options=t;this.target=this.resolveTarget(t?.target)}get connected(){return this.eventSource?.readyState===EventSource.OPEN}connect(){if(this.eventSource)return;let e=new EventSource(this.url,{withCredentials:this.options?.withCredentials??!1});if(this.eventSource=e,e.onopen=()=>{this.options?.onConnect?.(this)},e.onerror=t=>{this.options?.onError?.(this,t)},this.options?.eventTypes&&this.options.eventTypes.length>0)for(let t of this.options.eventTypes)e.addEventListener(t,n=>{this.dispatchEvent(t,n.data)});else e.onmessage=t=>{this.dispatchEvent("message",t.data)}}disconnect(){this.eventSource?.close(),this.eventSource=void 0}resolveTarget(e){if(!e)return document;if(typeof e=="string"){let t=document.querySelector(e);if(!t)throw new Error(`SSEClient: Target element not found: ${e}`);return t}return e}dispatchEvent(e,t){let n;if(t.length>0&&(t[0]==="{"||t[0]==="["||t[0]==='"'))try{n=JSON.parse(t)}catch{n=t}else n=t;let i=this.options?.eventFactory?this.options.eventFactory(e,n):new l(e,n);this.target.dispatchEvent(i)}};var u=class{constructor(e,t){this.value=e;this.removeCallback=t;this.next=null;this.prev=null}remove(){this.prev.next=this.next,this.next.prev=this.prev,this.removeCallback()}},c=class{constructor(){this._length=0}addFirst(e){let t=new u(e,()=>this._length--);this._first?(t.next=this._first,this._first.prev=t,this._first=t):(this._first=t,this._last=this._first),this._length++}addLast(e){let t=new u(e,()=>this._length--);this._first?(t.prev=this._last,this._last.next=t,this._last=t):(this._first=t,this._last=this._first),this._length++}removeFirst(){if(!this.first)throw new Error("The list is empty.");let e=this._first.value;return this._first=this._first.next,this._length--,e}removeLast(){if(!this.last)throw new Error("The list is empty.");let e=this._last.value;return this._last=this._last.prev,this._length--,e}get length(){return this._length}get first(){return this._first}get firstValue(){return this._first?.value}get last(){return this._last}get lastValue(){return this._last?.value}};var d=class{constructor(e,t){this.options=t;this.receiveQueue=new c;this.sendQueue=new c;this._isConnected=!1;this.isSendingQueue=!1;this.reconnectAttempts=0;this.shouldReconnect=!0;typeof e=="string"?this.url=e:this.wsFactory=e}get connected(){return this._isConnected}connect(){this.shouldReconnect=!0,this.reconnectAttempts=0,this.reConnect()}disconnect(){this.shouldReconnect=!1,this.ws?.close()}send(e){if(!this._isConnected||this.isSendingQueue){this.sendQueue.addLast(e);return}this.sendQueue.length>0&&this.sendQueueItems(),this.sendInternal(e)}receive(){if(this.receivePromiseWrapper)throw new Error("You can only invoke receive() once at a time.");return this.receiveQueue.firstValue?Promise.resolve(this.receiveQueue.removeFirst()):(this.receivePromiseWrapper=new f,new Promise((e,t)=>{this.receivePromiseWrapper.resolve=e,this.receivePromiseWrapper.reject=t}))}onMessage(e){let t;if(this.options?.codec?t=this.options.codec.decode(e.data):typeof e.data=="string"&&e.data.length>0&&(e.data[0]==='"'||e.data[0]==="["||e.data[0]==="{")?t=JSON.parse(e.data):t=e.data,this.receivePromiseWrapper){this.receivePromiseWrapper.resolve(t),this.receivePromiseWrapper=void 0;return}this.receiveQueue.addLast(t)}reConnect(){let e=this.url?new WebSocket(this.url):this.wsFactory();this.ws=e,e.onmessage=t=>this.onMessage(t),e.onerror=()=>e.close(),e.onopen=()=>{this._isConnected=!0,this.reconnectAttempts=0,this.options?.onConnect?.(this),this.sendQueueItems()},e.onclose=()=>{if(this._isConnected=!1,this.receivePromiseWrapper&&(this.receivePromiseWrapper.reject(new Error("WebSocket connection closed")),this.receivePromiseWrapper=void 0),this.options?.onClose?.(this),this.shouldReconnect&&this.options?.autoReconnect!==!1){let t=this.options?.reconnectDelay??1e3,n=this.options?.maxReconnectDelay??3e4,i=Math.min(t*Math.pow(2,this.reconnectAttempts),n);this.reconnectAttempts++,setTimeout(()=>this.reConnect(),i)}}}sendInternal(e){let t;typeof e!="string"?this.options?.codec?t=this.options.codec.encode(e):t=JSON.stringify(e):t=e,this.ws.send(t)}sendQueueItems(){if(!this.isSendingQueue){for(this.isSendingQueue=!0;this.sendQueue.length>0;){let e=this.sendQueue.removeFirst();if(e==null)break;this.sendInternal(e)}this.isSendingQueue=!1}}},f=class{};export{h as HttpError,p as SSEClient,l as SSEDataEvent,d as WebSocketClient,m as configure,S as del,T as get,y as post,b as put,a as request,g as setFetch};
|
|
2
|
+
//# sourceMappingURL=index.mjs.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../src/http/http.ts", "../../src/http/ServerSentEvents.ts", "../../src/collections/LinkedList.ts", "../../src/http/SimpleWebSocket.ts"],
|
|
4
|
+
"sourcesContent": ["/**\r\n * @module http\r\n * Type-safe HTTP module built on fetch() with automatic JWT handling.\r\n *\r\n * @example\r\n * import { configure, get, post } from './http';\r\n *\r\n * configure({ baseUrl: '/api' });\r\n * const response = await get('/users');\r\n * const users = response.as<User[]>();\r\n */\r\n\r\n/**\r\n * Configuration options for the http module.\r\n */\r\nexport interface HttpOptions {\r\n /**\r\n * Root URL to remote endpoint. Used so that each method only has to specify path in requests.\r\n */\r\n baseUrl?: string;\r\n\r\n /**\r\n * Default content type to use if none is specified in the request method.\r\n */\r\n contentType?: string;\r\n\r\n /**\r\n * Checks for a JWT token in localStorage to automatically include it in requests.\r\n *\r\n * Undefined = use \"jwt\", null = disable.\r\n */\r\n bearerTokenName?: string | null;\r\n\r\n /**\r\n * Default request timeout in milliseconds.\r\n * Uses `AbortSignal.timeout()` to automatically abort requests that take too long.\r\n * Can be overridden per-request by passing a `signal` in `RequestInit`.\r\n *\r\n * @example\r\n * configure({ baseUrl: '/api', timeout: 10000 }); // 10 second timeout\r\n */\r\n timeout?: number;\r\n}\r\n\r\n/**\r\n * Response for request methods.\r\n */\r\nexport interface HttpResponse {\r\n /**\r\n * Http status code.\r\n */\r\n statusCode: number;\r\n\r\n /**\r\n * Reason to why the status code was used.\r\n */\r\n statusReason: string;\r\n\r\n /**\r\n * True if this is a 2xx response.\r\n */\r\n success: boolean;\r\n\r\n /**\r\n * Content type of response body.\r\n */\r\n contentType: string | null;\r\n\r\n /**\r\n * Body returned.\r\n *\r\n * Body has been read and deserialized from json (if the request content type was 'application/json' which is the default).\r\n */\r\n body: unknown;\r\n\r\n /**\r\n * Charset used in body.\r\n */\r\n charset: string | null;\r\n\r\n /**\r\n * Cast body to a type.\r\n */\r\n as<T>(): T;\r\n}\r\n\r\n/**\r\n * Error thrown when a request fails.\r\n */\r\nexport class HttpError extends Error {\r\n message: string;\r\n response: HttpResponse;\r\n\r\n constructor(response: HttpResponse) {\r\n super(response.statusReason);\r\n this.message = response.statusReason;\r\n this.response = response;\r\n }\r\n}\r\n\r\n/**\r\n * HTTP request options.\r\n */\r\nexport interface RequestOptions {\r\n method?: 'GET' | 'POST' | 'PUT' | 'DELETE';\r\n mode?: 'cors' | 'no-cors' | '*cors' | 'same-origin';\r\n cache:\r\n | 'default'\r\n | 'no-store'\r\n | 'reload'\r\n | 'no-cache'\r\n | 'force-cache'\r\n | 'only-if-cached';\r\n credentials: 'omit' | 'same-origin' | 'include';\r\n headers: Map<string, string>;\r\n redirect: 'follow' | 'manual' | '*follow' | 'error';\r\n referrerPolicy:\r\n | 'no-referrer'\r\n | '*no-referrer-when-downgrade'\r\n | 'origin'\r\n | 'origin-when-cross-origin'\r\n | 'same-origin'\r\n | 'strict-origin'\r\n | 'strict-origin-when-cross-origin'\r\n | 'unsafe-url';\r\n\r\n /**\r\n * Will be serialized if the content type is json (and the body is an object).\r\n */\r\n body: unknown;\r\n}\r\n\r\ndeclare type FetchFn = (input: RequestInfo | URL, init?: RequestInit) => Promise<Response>;\r\n\r\nlet config: HttpOptions = {\r\n bearerTokenName: 'jwt'\r\n};\r\n\r\nlet fetchImpl: FetchFn = fetch;\r\n\r\n/**\r\n * Replace the fetch implementation for testing purposes.\r\n *\r\n * @param fn - Custom fetch function, or undefined to restore the default.\r\n *\r\n * @example\r\n * setFetch(async (url, options) => {\r\n * return new Response(JSON.stringify({ id: 1 }), { status: 200 });\r\n * });\r\n */\r\nexport function setFetch(fn?: FetchFn): void {\r\n fetchImpl = fn ?? fetch;\r\n}\r\n\r\n/**\r\n * Configure the http module.\r\n *\r\n * @example\r\n * configure({ baseUrl: '/api/v1', bearerTokenName: 'auth_token' });\r\n */\r\nexport function configure(options: HttpOptions): void {\r\n config = {\r\n ...config,\r\n ...options\r\n };\r\n if (options.bearerTokenName === undefined) {\r\n config.bearerTokenName = 'jwt';\r\n }\r\n}\r\n\r\n/**\r\n * Make an HTTP request.\r\n *\r\n * @param url - URL to make the request against.\r\n * @param options - Request options.\r\n * @returns Response from server.\r\n *\r\n * @example\r\n * const response = await request('/users', { method: 'GET' });\r\n */\r\nexport async function request(url: string, options?: RequestInit): Promise<HttpResponse> {\r\n if (config.bearerTokenName) {\r\n const token = localStorage.getItem(config.bearerTokenName);\r\n if (token && options) {\r\n const headers = options?.headers\r\n ? new Headers(options.headers)\r\n : new Headers();\r\n\r\n if (!headers.get('Authorization')) {\r\n headers.set('Authorization', 'Bearer ' + token);\r\n }\r\n\r\n options.headers = headers;\r\n }\r\n }\r\n\r\n if (config.timeout && !options?.signal) {\r\n options ??= {};\r\n options.signal = AbortSignal.timeout(config.timeout);\r\n }\r\n\r\n if (config.baseUrl) {\r\n if (url[0] !== '/' && config.baseUrl[config.baseUrl.length - 1] !== '/') {\r\n url = `${config.baseUrl}/${url}`;\r\n } else {\r\n url = config.baseUrl + url;\r\n }\r\n }\r\n\r\n const response = await fetchImpl(url, options);\r\n\r\n if (!response.ok) {\r\n return {\r\n statusCode: response.status,\r\n statusReason: response.statusText,\r\n success: false,\r\n contentType: response.headers.get('content-type'),\r\n body: await response.text(),\r\n charset: response.headers.get('charset'),\r\n\r\n as() {\r\n throw new Error('No response received');\r\n }\r\n };\r\n }\r\n\r\n let body: unknown | null = null;\r\n if (response.status !== 204) {\r\n body = await response.json();\r\n }\r\n\r\n return {\r\n success: true,\r\n statusCode: response.status,\r\n statusReason: response.statusText,\r\n contentType: response.headers.get('content-type'),\r\n body: body,\r\n charset: response.headers.get('charset'),\r\n as<T>() {\r\n return <T>body;\r\n }\r\n };\r\n}\r\n\r\n/**\r\n * GET a resource.\r\n *\r\n * @param url - URL to get resource from.\r\n * @param queryString - Optional query string parameters.\r\n * @param options - Request options.\r\n * @returns HTTP response.\r\n *\r\n * @example\r\n * const response = await get('/users', { page: '1', limit: '10' });\r\n * const users = response.as<User[]>();\r\n */\r\nexport async function get(\r\n url: string,\r\n queryString?: Record<string, string>,\r\n options?: RequestInit\r\n): Promise<HttpResponse> {\r\n if (!options) {\r\n options = {\r\n method: 'GET',\r\n headers: {\r\n 'content-type': config.contentType ?? 'application/json'\r\n }\r\n };\r\n } else {\r\n options.method = 'GET';\r\n }\r\n\r\n if (queryString) {\r\n let prefix = '&';\r\n if (url.indexOf('?') === -1) {\r\n prefix = '?';\r\n }\r\n\r\n for (const key in queryString) {\r\n const value = queryString[key];\r\n url += `${prefix}${key}=${value}`;\r\n prefix = '&';\r\n }\r\n }\r\n\r\n return request(url, options);\r\n}\r\n\r\n/**\r\n * POST a resource.\r\n *\r\n * @param url - URL to post to.\r\n * @param data - Data to post.\r\n * @param options - Request options.\r\n * @returns HTTP response.\r\n *\r\n * @example\r\n * const response = await post('/users', JSON.stringify({ name: 'John' }));\r\n */\r\nexport async function post(\r\n url: string,\r\n data: BodyInit,\r\n options?: RequestInit\r\n): Promise<HttpResponse> {\r\n if (!options) {\r\n options = {\r\n method: 'POST',\r\n body: data,\r\n headers: {\r\n 'content-type': config.contentType ?? 'application/json'\r\n }\r\n };\r\n } else {\r\n options.method = 'POST';\r\n options.body = data;\r\n }\r\n\r\n return request(url, options);\r\n}\r\n\r\n/**\r\n * PUT a resource.\r\n *\r\n * @param url - URL to resource.\r\n * @param data - Data to put.\r\n * @param options - Request options.\r\n * @returns HTTP response.\r\n *\r\n * @example\r\n * const response = await put('/users/1', JSON.stringify({ name: 'Jane' }));\r\n */\r\nexport async function put(\r\n url: string,\r\n data: BodyInit,\r\n options?: RequestInit\r\n): Promise<HttpResponse> {\r\n if (!options) {\r\n options = {\r\n method: 'PUT',\r\n body: data,\r\n headers: {\r\n 'content-type': config.contentType ?? 'application/json'\r\n }\r\n };\r\n } else {\r\n options.method = 'PUT';\r\n options.body = data;\r\n }\r\n\r\n return request(url, options);\r\n}\r\n\r\n/**\r\n * DELETE a resource.\r\n *\r\n * @param url - URL to resource.\r\n * @param options - Request options.\r\n * @returns HTTP response.\r\n *\r\n * @example\r\n * const response = await del('/users/1');\r\n */\r\nexport async function del(url: string, options?: RequestInit): Promise<HttpResponse> {\r\n if (!options) {\r\n options = {\r\n method: 'DELETE',\r\n headers: {\r\n 'content-type': config.contentType ?? 'application/json'\r\n }\r\n };\r\n } else {\r\n options.method = 'DELETE';\r\n }\r\n\r\n return request(url, options);\r\n}\r\n", "/**\r\n * @module ServerSentEvents\r\n * SSE client that dispatches received events as DOM events.\r\n * Uses the browser's built-in EventSource reconnection.\r\n *\r\n * @example\r\n * const sse = new SSEClient('/api/events', {\r\n * eventTypes: ['user-updated', 'order-created']\r\n * });\r\n * sse.connect();\r\n *\r\n * document.addEventListener('user-updated', (e: SSEDataEvent) => {\r\n * console.log('User updated:', e.data);\r\n * });\r\n */\r\n\r\n/**\r\n * Event dispatched when an SSE message is received.\r\n * The event name matches the SSE event type.\r\n */\r\nexport class SSEDataEvent extends Event {\r\n constructor(\r\n eventName: string,\r\n public data: unknown,\r\n eventInit?: EventInit\r\n ) {\r\n super(eventName, { bubbles: true, ...eventInit });\r\n }\r\n}\r\n\r\n/**\r\n * Factory function for creating custom event instances.\r\n *\r\n * @example\r\n * const factory: SSEEventFactory = (eventName, data) => {\r\n * switch (eventName) {\r\n * case 'user-updated':\r\n * return new UserUpdatedEvent(data as User);\r\n * default:\r\n * return new SSEDataEvent(eventName, data);\r\n * }\r\n * };\r\n */\r\nexport type SSEEventFactory = (eventName: string, data: unknown) => Event;\r\n\r\n/**\r\n * Configuration options for SSEClient.\r\n */\r\nexport interface SSEOptions {\r\n /**\r\n * Target element or CSS selector for event dispatching.\r\n * Defaults to document.\r\n */\r\n target?: string | Element;\r\n\r\n /**\r\n * Whether to send credentials with the request (default: false).\r\n */\r\n withCredentials?: boolean;\r\n\r\n /**\r\n * Specific SSE event types to listen for.\r\n * If not specified, listens to the default 'message' event.\r\n *\r\n * @example\r\n * eventTypes: ['user-updated', 'order-created']\r\n */\r\n eventTypes?: string[];\r\n\r\n /**\r\n * Factory function for creating custom event instances.\r\n * If not provided, SSEDataEvent is used.\r\n *\r\n * @example\r\n * eventFactory: (name, data) => new MyCustomEvent(name, data)\r\n */\r\n eventFactory?: SSEEventFactory;\r\n\r\n /**\r\n * Callback when connection is established.\r\n */\r\n onConnect?: (client: SSEClient) => void;\r\n\r\n /**\r\n * Callback when an error occurs.\r\n * Note: EventSource automatically reconnects on errors.\r\n */\r\n onError?: (client: SSEClient, error: Event) => void;\r\n}\r\n\r\n/**\r\n * Server-Sent Events client that dispatches received events as DOM events.\r\n * Uses the browser's built-in EventSource with automatic reconnection.\r\n *\r\n * @example\r\n * const sse = new SSEClient('/api/events', {\r\n * target: '#notifications',\r\n * eventTypes: ['notification', 'alert']\r\n * });\r\n *\r\n * sse.connect();\r\n *\r\n * document.querySelector('#notifications')\r\n * .addEventListener('notification', (e: SSEDataEvent) => {\r\n * showNotification(e.data);\r\n * });\r\n *\r\n * sse.disconnect();\r\n */\r\nexport class SSEClient {\r\n private eventSource?: EventSource;\r\n private target: Element | Document;\r\n\r\n /**\r\n * Whether the client is currently connected.\r\n */\r\n get connected(): boolean {\r\n return this.eventSource?.readyState === EventSource.OPEN;\r\n }\r\n\r\n constructor(\r\n private url: string,\r\n private options?: SSEOptions\r\n ) {\r\n this.target = this.resolveTarget(options?.target);\r\n }\r\n\r\n /**\r\n * Establish connection to the SSE endpoint.\r\n */\r\n connect(): void {\r\n if (this.eventSource) {\r\n return;\r\n }\r\n\r\n const eventSource = new EventSource(this.url, {\r\n withCredentials: this.options?.withCredentials ?? false\r\n });\r\n\r\n this.eventSource = eventSource;\r\n\r\n eventSource.onopen = () => {\r\n this.options?.onConnect?.(this);\r\n };\r\n\r\n eventSource.onerror = (error) => {\r\n this.options?.onError?.(this, error);\r\n };\r\n\r\n if (this.options?.eventTypes && this.options.eventTypes.length > 0) {\r\n for (const eventType of this.options.eventTypes) {\r\n eventSource.addEventListener(eventType, (e: MessageEvent) => {\r\n this.dispatchEvent(eventType, e.data);\r\n });\r\n }\r\n } else {\r\n eventSource.onmessage = (e: MessageEvent) => {\r\n this.dispatchEvent('message', e.data);\r\n };\r\n }\r\n }\r\n\r\n /**\r\n * Close the connection.\r\n */\r\n disconnect(): void {\r\n this.eventSource?.close();\r\n this.eventSource = undefined;\r\n }\r\n\r\n private resolveTarget(target?: string | Element): Element | Document {\r\n if (!target) {\r\n return document;\r\n }\r\n if (typeof target === 'string') {\r\n const element = document.querySelector(target);\r\n if (!element) {\r\n throw new Error(`SSEClient: Target element not found: ${target}`);\r\n }\r\n return element;\r\n }\r\n return target;\r\n }\r\n\r\n private dispatchEvent(eventName: string, rawData: string): void {\r\n let data: unknown;\r\n\r\n if (rawData.length > 0 && (rawData[0] === '{' || rawData[0] === '[' || rawData[0] === '\"')) {\r\n try {\r\n data = JSON.parse(rawData);\r\n } catch {\r\n data = rawData;\r\n }\r\n } else {\r\n data = rawData;\r\n }\r\n\r\n const event = this.options?.eventFactory\r\n ? this.options.eventFactory(eventName, data)\r\n : new SSEDataEvent(eventName, data);\r\n\r\n this.target.dispatchEvent(event);\r\n }\r\n}\r\n", "/**\r\n * A node in the @see LinkedList.\r\n */\r\nexport class Node<T> {\r\n /**\r\n * Next node unless last one.\r\n */\r\n public next: Node<T> | null = null;\r\n /**\r\n * Previous node unless first one.\r\n */\r\n public prev: Node<T> | null = null;\r\n\r\n /**\r\n * Constructor.\r\n * @param value Value contained in the node.\r\n */\r\n constructor(public value: T, private removeCallback: () => void) {}\r\n\r\n /**\r\n * Remove this node.\r\n * Will notify the list of the update to ensure correct element count.\r\n */\r\n remove() {\r\n this.prev.next = this.next;\r\n this.next.prev = this.prev;\r\n this.removeCallback();\r\n }\r\n}\r\n\r\n/**\r\n * A trivial linked list implementation.\r\n */\r\nexport class LinkedList<T> {\r\n private _first?: Node<T>;\r\n private _last?: Node<T>;\r\n private _length = 0;\r\n\r\n /**\r\n * Add a value to the beginning of the list.\r\n * @param value Value that should be contained in the node.\r\n */\r\n addFirst(value: T) {\r\n const newNode = new Node(value, () => this._length--);\r\n if (!this._first) {\r\n this._first = newNode;\r\n this._last = this._first;\r\n } else {\r\n newNode.next = this._first;\r\n this._first.prev = newNode;\r\n this._first = newNode;\r\n }\r\n\r\n this._length++;\r\n }\r\n\r\n /**\r\n * Add a value to the end of the list.\r\n * @param value Value that should be contained in a node.\r\n */\r\n addLast(value: T) {\r\n const newNode = new Node(value, () => this._length--);\r\n if (!this._first) {\r\n this._first = newNode;\r\n this._last = this._first;\r\n } else {\r\n newNode.prev = this._last;\r\n this._last.next = newNode;\r\n this._last = newNode;\r\n }\r\n\r\n this._length++;\r\n }\r\n\r\n /**\r\n * Remove a node from the beginning of the list.\r\n * @returns Value contained in the first node.\r\n */\r\n removeFirst(): T {\r\n if (!this.first) {\r\n throw new Error('The list is empty.');\r\n }\r\n\r\n const value = this._first.value;\r\n this._first = this._first.next;\r\n this._length--;\r\n return value;\r\n }\r\n\r\n /**\r\n * Remove a node from the end of the list.\r\n * @returns Value contained in the last node.\r\n */\r\n removeLast(): T {\r\n if (!this.last) {\r\n throw new Error('The list is empty.');\r\n }\r\n\r\n const value = this._last.value;\r\n this._last = this._last.prev;\r\n this._length--;\r\n return value;\r\n }\r\n\r\n /**\r\n * Number of nodes in the list.\r\n *\r\n * The count works as long as you do not manually remove nodes (by assigning next/prev to the neighbors).\r\n */\r\n get length(): number {\r\n return this._length;\r\n }\r\n\r\n /**\r\n * First ndoe.\r\n */\r\n get first(): Node<T> | undefined {\r\n return this._first;\r\n }\r\n\r\n /**\r\n * Contained value of the first node.\r\n */\r\n get firstValue(): T | undefined {\r\n return this._first?.value;\r\n }\r\n\r\n /**\r\n * Last node.\r\n */\r\n get last(): Node<T> | undefined {\r\n return this._last;\r\n }\r\n\r\n /**\r\n * Contained value of the last node.\r\n */\r\n get lastValue(): T | undefined {\r\n return this._last?.value;\r\n }\r\n}\r\n", "/**\r\n * @module SimpleWebSocket\r\n * WebSocket client with automatic reconnection and message queuing.\r\n * Provides a reliable messaging layer over WebSocket connections.\r\n *\r\n * @example\r\n * // Create and connect\r\n * const ws = new WebSocketClient<ChatMessage>('wss://chat.example.com');\r\n * ws.connect();\r\n *\r\n * // Send and receive\r\n * await ws.send({ text: 'Hello' });\r\n * const msg = await ws.receive();\r\n */\r\n\r\nimport { LinkedList } from '../collections/LinkedList';\r\n\r\n/**\r\n * Simplified message event for WebSocket data.\r\n */\r\nexport interface SimpleDataEvent {\r\n data: string | ArrayBufferLike | Blob | ArrayBufferView;\r\n}\r\n\r\n/**\r\n * Abstraction interface for WebSocket to enable unit testing.\r\n * Implement this for custom WebSocket instances or mocks.\r\n */\r\nexport interface WebSocketAbstraction {\r\n onopen: ((event: Event) => void) | null;\r\n onerror: ((event: ErrorEvent) => void) | null;\r\n onclose: ((event: CloseEvent) => void) | null;\r\n onmessage: ((event: SimpleDataEvent) => void) | null;\r\n send(data: string | ArrayBufferLike | Blob | ArrayBufferView): void;\r\n close(): void;\r\n}\r\n\r\n/**\r\n * Factory function type for creating WebSocket instances.\r\n */\r\nexport type WebSocketFactory = () => WebSocketAbstraction;\r\n\r\n/**\r\n * Managed WebSocket client with automatic reconnection and message queuing.\r\n *\r\n * Features:\r\n * - Automatic reconnection on disconnect\r\n * - Message queuing when disconnected\r\n * - Type-safe message handling with optional codecs\r\n * - Promise-based receive API\r\n *\r\n * @template TMessage - The type of messages sent and received\r\n *\r\n * @example\r\n * // Basic usage\r\n * interface ChatMessage { user: string; text: string; }\r\n *\r\n * const client = new WebSocketClient<ChatMessage>('wss://chat.example.com');\r\n * client.connect();\r\n *\r\n * // Send messages\r\n * await client.send({ user: 'John', text: 'Hello!' });\r\n *\r\n * // Receive messages\r\n * while (true) {\r\n * const message = await client.receive();\r\n * console.log(`${message.user}: ${message.text}`);\r\n * }\r\n *\r\n * @example\r\n * // With options\r\n * const client = new WebSocketClient<Message>('wss://api.example.com', {\r\n * autoReconnect: true,\r\n * onConnect: (socket) => console.log('Connected'),\r\n * onClose: () => console.log('Disconnected')\r\n * });\r\n */\r\nexport class WebSocketClient<TMessage> {\r\n private ws?: WebSocketAbstraction;\r\n private receiveQueue = new LinkedList<TMessage>();\r\n private receivePromiseWrapper?: PromiseWrapper<TMessage>;\r\n private sendQueue = new LinkedList<TMessage>();\r\n private _isConnected = false;\r\n private isSendingQueue = false;\r\n private url?: string;\r\n private wsFactory?: WebSocketFactory;\r\n private reconnectAttempts = 0;\r\n private shouldReconnect = true;\r\n\r\n get connected(): boolean {\r\n return this._isConnected;\r\n }\r\n\r\n constructor(\r\n urlOrWebSocketFactory: string | WebSocketFactory,\r\n private options?: WebSocketOptions<TMessage>\r\n ) {\r\n if (typeof urlOrWebSocketFactory === 'string') {\r\n this.url = urlOrWebSocketFactory;\r\n } else {\r\n this.wsFactory = urlOrWebSocketFactory;\r\n }\r\n }\r\n\r\n connect() {\r\n this.shouldReconnect = true;\r\n this.reconnectAttempts = 0;\r\n this.reConnect();\r\n }\r\n\r\n disconnect() {\r\n this.shouldReconnect = false;\r\n this.ws?.close();\r\n }\r\n\r\n send(data: TMessage): void {\r\n if (!this._isConnected || this.isSendingQueue) {\r\n this.sendQueue.addLast(data);\r\n return;\r\n }\r\n\r\n if (this.sendQueue.length > 0) {\r\n this.sendQueueItems();\r\n }\r\n\r\n this.sendInternal(data);\r\n }\r\n\r\n /**\r\n * Receive a new message.\r\n *\r\n * @throws Error if called while another receive() is pending.\r\n * @throws Error if connection closes while waiting.\r\n */\r\n receive(): Promise<TMessage> {\r\n if (this.receivePromiseWrapper) {\r\n throw new Error('You can only invoke receive() once at a time.');\r\n }\r\n\r\n if (this.receiveQueue.firstValue) {\r\n return Promise.resolve(this.receiveQueue.removeFirst());\r\n }\r\n\r\n this.receivePromiseWrapper = new PromiseWrapper();\r\n return new Promise((resolve, reject) => {\r\n this.receivePromiseWrapper.resolve = resolve;\r\n this.receivePromiseWrapper.reject = reject;\r\n });\r\n }\r\n\r\n private onMessage(ev: SimpleDataEvent) {\r\n let msg: TMessage;\r\n if (this.options?.codec) {\r\n msg = this.options.codec.decode(ev.data);\r\n } else if (typeof ev.data === 'string') {\r\n if (\r\n ev.data.length > 0 &&\r\n (ev.data[0] === '\"' || ev.data[0] === '[' || ev.data[0] === '{')\r\n ) {\r\n msg = JSON.parse(ev.data);\r\n } else {\r\n msg = ev.data as TMessage;\r\n }\r\n } else {\r\n msg = ev.data as TMessage;\r\n }\r\n\r\n if (this.receivePromiseWrapper) {\r\n this.receivePromiseWrapper.resolve(msg);\r\n this.receivePromiseWrapper = undefined;\r\n return;\r\n }\r\n\r\n this.receiveQueue.addLast(msg);\r\n }\r\n\r\n private reConnect() {\r\n const ws = this.url ? new WebSocket(this.url) : this.wsFactory();\r\n this.ws = ws;\r\n ws.onmessage = (evt: SimpleDataEvent) => this.onMessage(evt);\r\n ws.onerror = () => ws.close();\r\n ws.onopen = () => {\r\n this._isConnected = true;\r\n this.reconnectAttempts = 0;\r\n this.options?.onConnect?.(this);\r\n this.sendQueueItems();\r\n };\r\n ws.onclose = () => {\r\n this._isConnected = false;\r\n\r\n if (this.receivePromiseWrapper) {\r\n this.receivePromiseWrapper.reject(new Error('WebSocket connection closed'));\r\n this.receivePromiseWrapper = undefined;\r\n }\r\n\r\n this.options?.onClose?.(this);\r\n\r\n if (this.shouldReconnect && this.options?.autoReconnect !== false) {\r\n const baseDelay = this.options?.reconnectDelay ?? 1000;\r\n const maxDelay = this.options?.maxReconnectDelay ?? 30000;\r\n const delay = Math.min(baseDelay * Math.pow(2, this.reconnectAttempts), maxDelay);\r\n this.reconnectAttempts++;\r\n setTimeout(() => this.reConnect(), delay);\r\n }\r\n };\r\n }\r\n\r\n private sendInternal(data: TMessage) {\r\n let dataToSend: string | ArrayBufferLike | Blob | ArrayBufferView;\r\n if (typeof data !== 'string') {\r\n if (this.options?.codec) {\r\n dataToSend = this.options.codec.encode(data);\r\n } else {\r\n dataToSend = JSON.stringify(data);\r\n }\r\n } else {\r\n dataToSend = data;\r\n }\r\n\r\n this.ws.send(dataToSend);\r\n }\r\n\r\n private sendQueueItems(): void {\r\n if (this.isSendingQueue) {\r\n return;\r\n }\r\n\r\n this.isSendingQueue = true;\r\n while (this.sendQueue.length > 0) {\r\n const item = this.sendQueue.removeFirst();\r\n if (item == null) {\r\n break;\r\n }\r\n\r\n this.sendInternal(item);\r\n }\r\n\r\n this.isSendingQueue = false;\r\n }\r\n}\r\n\r\n/**\r\n * CODEC used for messages.\r\n */\r\nexport interface WebSocketCodec<TMessage> {\r\n /**\r\n *\r\n * @param data\r\n */\r\n encode(data: TMessage): string | ArrayBufferLike | Blob | ArrayBufferView;\r\n\r\n /**\r\n *\r\n * @param data\r\n */\r\n decode(data: string | ArrayBufferLike | Blob | ArrayBufferView): TMessage;\r\n}\r\n\r\n/**\r\n * Configuration options for @see WebSocketClient.\r\n */\r\nexport interface WebSocketOptions<TMessage> {\r\n /**\r\n * CODEC to use for inbound and outbound messages (if something else that JSON should be used).\r\n */\r\n codec?: WebSocketCodec<TMessage>;\r\n\r\n /**\r\n * Automatically reconnect when getting disconnected (default: true).\r\n */\r\n autoReconnect?: boolean;\r\n\r\n /**\r\n * Initial delay in milliseconds before reconnecting (default: 1000).\r\n * Uses exponential backoff on subsequent attempts.\r\n */\r\n reconnectDelay?: number;\r\n\r\n /**\r\n * Maximum delay in milliseconds between reconnect attempts (default: 30000).\r\n */\r\n maxReconnectDelay?: number;\r\n\r\n /**\r\n * Callback when the WS is connected.\r\n *\r\n * Can be used for authentication messages etc.\r\n *\r\n * @param socket Socket\r\n */\r\n onConnect?: (socket: WebSocketClient<TMessage>) => void;\r\n\r\n /**\r\n * Invoked when the connection is closed.\r\n *\r\n * The connection will be automatically reconnected if configured (on by default).\r\n *\r\n * @param socket Socket.\r\n */\r\n onClose?: (socket: WebSocketClient<TMessage>) => void;\r\n}\r\n\r\nclass PromiseWrapper<TMessage> {\r\n resolve!: (value: TMessage | PromiseLike<TMessage>) => void;\r\n reject!: (reason?: unknown) => void;\r\n}\r\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": ["HttpError", "response", "config", "fetchImpl", "setFetch", "fn", "configure", "options", "request", "url", "token", "headers", "body", "get", "queryString", "prefix", "key", "value", "post", "data", "put", "del", "SSEDataEvent", "eventName", "data", "eventInit", "SSEClient", "url", "options", "eventSource", "error", "eventType", "e", "target", "element", "rawData", "event", "Node", "value", "removeCallback", "LinkedList", "newNode", "WebSocketClient", "urlOrWebSocketFactory", "options", "LinkedList", "data", "PromiseWrapper", "resolve", "reject", "ev", "msg", "ws", "evt", "baseDelay", "maxDelay", "delay", "dataToSend", "item"]
|
|
7
|
+
}
|