@outfoxx/sunday 1.0.9 → 1.1.0-alpha.11
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.txt +203 -0
- package/README.md +35 -2
- package/dist/any-type.d.ts +2 -0
- package/dist/any-type.js +13 -0
- package/dist/any-type.js.map +1 -1
- package/dist/class-type.d.ts +2 -2
- package/dist/class-type.js +13 -0
- package/dist/class-type.js.map +1 -1
- package/dist/date-time-types.d.ts +2 -7
- package/dist/date-time-types.js +15 -4
- package/dist/date-time-types.js.map +1 -1
- package/dist/event-parser.d.ts +17 -0
- package/dist/event-parser.js +153 -0
- package/dist/event-parser.js.map +1 -0
- package/dist/fetch-event-source.d.ts +12 -10
- package/dist/fetch-event-source.js +154 -138
- package/dist/fetch-event-source.js.map +1 -1
- package/dist/fetch-request-factory.d.ts +10 -5
- package/dist/fetch-request-factory.js +100 -71
- package/dist/fetch-request-factory.js.map +1 -1
- package/dist/fetch.d.ts +7 -1
- package/dist/fetch.js +74 -8
- package/dist/fetch.js.map +1 -1
- package/dist/header-parameters.d.ts +3 -0
- package/dist/header-parameters.js +53 -0
- package/dist/header-parameters.js.map +1 -0
- package/dist/index.d.ts +12 -12
- package/dist/index.js +25 -12
- package/dist/index.js.map +1 -1
- package/dist/logger.js +13 -0
- package/dist/logger.js.map +1 -1
- package/dist/{binary-decoder.d.ts → media-type-codecs/binary-decoder.d.ts} +2 -2
- package/dist/media-type-codecs/binary-decoder.js +29 -0
- package/dist/media-type-codecs/binary-decoder.js.map +1 -0
- package/dist/{binary-encoder.d.ts → media-type-codecs/binary-encoder.d.ts} +0 -0
- package/dist/media-type-codecs/binary-encoder.js +26 -0
- package/dist/media-type-codecs/binary-encoder.js.map +1 -0
- package/dist/media-type-codecs/cbor-decoder.d.ts +39 -0
- package/dist/media-type-codecs/cbor-decoder.js +426 -0
- package/dist/media-type-codecs/cbor-decoder.js.map +1 -0
- package/dist/media-type-codecs/cbor-encoder.d.ts +43 -0
- package/dist/media-type-codecs/cbor-encoder.js +239 -0
- package/dist/media-type-codecs/cbor-encoder.js.map +1 -0
- package/dist/media-type-codecs/cbor-tags.d.ts +4 -0
- package/dist/media-type-codecs/cbor-tags.js +18 -0
- package/dist/media-type-codecs/cbor-tags.js.map +1 -0
- package/dist/media-type-codecs/json-decoder.d.ts +39 -0
- package/dist/media-type-codecs/json-decoder.js +380 -0
- package/dist/media-type-codecs/json-decoder.js.map +1 -0
- package/dist/media-type-codecs/json-encoder.d.ts +44 -0
- package/dist/media-type-codecs/json-encoder.js +276 -0
- package/dist/media-type-codecs/json-encoder.js.map +1 -0
- package/dist/media-type-codecs/media-type-decoder.d.ts +11 -0
- package/dist/media-type-codecs/media-type-decoder.js +19 -0
- package/dist/media-type-codecs/media-type-decoder.js.map +1 -0
- package/dist/{media-type-decoders.d.ts → media-type-codecs/media-type-decoders.d.ts} +5 -4
- package/dist/media-type-codecs/media-type-decoders.js +53 -0
- package/dist/media-type-codecs/media-type-decoders.js.map +1 -0
- package/dist/{media-type-encoder.d.ts → media-type-codecs/media-type-encoder.d.ts} +5 -1
- package/dist/media-type-codecs/media-type-encoder.js +24 -0
- package/dist/media-type-codecs/media-type-encoder.js.map +1 -0
- package/dist/{media-type-encoders.d.ts → media-type-codecs/media-type-encoders.d.ts} +5 -4
- package/dist/media-type-codecs/media-type-encoders.js +55 -0
- package/dist/media-type-codecs/media-type-encoders.js.map +1 -0
- package/dist/{url-encoder.d.ts → media-type-codecs/www-form-url-encoder.d.ts} +8 -8
- package/dist/{url-encoder.js → media-type-codecs/www-form-url-encoder.js} +51 -37
- package/dist/media-type-codecs/www-form-url-encoder.js.map +1 -0
- package/dist/media-type.d.ts +91 -13
- package/dist/media-type.js +286 -15
- package/dist/media-type.js.map +1 -1
- package/dist/problem.d.ts +24 -9
- package/dist/problem.js +153 -6
- package/dist/problem.js.map +1 -1
- package/dist/request-factory.d.ts +10 -10
- package/dist/request-factory.js +13 -0
- package/dist/request-factory.js.map +1 -1
- package/dist/sunday-error.d.ts +11 -0
- package/dist/sunday-error.js +31 -0
- package/dist/sunday-error.js.map +1 -0
- package/dist/url-template.js +13 -0
- package/dist/url-template.js.map +1 -1
- package/dist/util/any.d.ts +2 -0
- package/dist/util/any.js +24 -0
- package/dist/util/any.js.map +1 -0
- package/dist/util/base64.js +13 -0
- package/dist/util/base64.js.map +1 -1
- package/dist/util/error.d.ts +2 -0
- package/dist/util/error.js +26 -0
- package/dist/util/error.js.map +1 -0
- package/dist/util/hex.js +16 -2
- package/dist/util/hex.js.map +1 -1
- package/dist/util/rxjs.d.ts +3 -0
- package/dist/util/rxjs.js +22 -5
- package/dist/util/rxjs.js.map +1 -1
- package/dist/util/stream-rxjs.js +13 -0
- package/dist/util/stream-rxjs.js.map +1 -1
- package/dist/util/temporal.d.ts +2 -0
- package/dist/util/temporal.js +31 -0
- package/dist/util/temporal.js.map +1 -0
- package/package.json +29 -22
- package/src/any-type.ts +18 -0
- package/src/class-type.ts +20 -2
- package/src/date-time-types.ts +35 -8
- package/src/event-parser.ts +204 -0
- package/src/fetch-event-source.ts +163 -159
- package/src/fetch-request-factory.ts +142 -100
- package/src/fetch.ts +79 -14
- package/src/header-parameters.ts +66 -0
- package/src/index.ts +26 -12
- package/src/logger.ts +14 -0
- package/src/media-type-codecs/binary-decoder.ts +38 -0
- package/src/media-type-codecs/binary-encoder.ts +33 -0
- package/src/media-type-codecs/cbor-decoder.ts +529 -0
- package/src/media-type-codecs/cbor-encoder.ts +321 -0
- package/src/media-type-codecs/cbor-tags.ts +18 -0
- package/src/media-type-codecs/json-decoder.ts +484 -0
- package/src/media-type-codecs/json-encoder.ts +342 -0
- package/src/media-type-codecs/media-type-decoder.ts +34 -0
- package/src/media-type-codecs/media-type-decoders.ts +81 -0
- package/src/media-type-codecs/media-type-encoder.ts +45 -0
- package/src/media-type-codecs/media-type-encoders.ts +83 -0
- package/src/media-type-codecs/www-form-url-encoder.ts +193 -0
- package/src/media-type.ts +340 -22
- package/src/problem.ts +158 -12
- package/src/request-factory.ts +35 -12
- package/src/sunday-error.ts +51 -0
- package/src/url-template.ts +14 -0
- package/src/util/any.ts +24 -0
- package/src/util/base64.ts +14 -0
- package/src/util/error.ts +28 -0
- package/src/util/hex.ts +17 -2
- package/src/util/rxjs.ts +30 -5
- package/src/util/stream-rxjs.ts +14 -0
- package/src/util/temporal.ts +32 -0
- package/dist/binary-decoder.js +0 -16
- package/dist/binary-decoder.js.map +0 -1
- package/dist/binary-encoder.js +0 -13
- package/dist/binary-encoder.js.map +0 -1
- package/dist/cbor-decoder.d.ts +0 -15
- package/dist/cbor-decoder.js +0 -126
- package/dist/cbor-decoder.js.map +0 -1
- package/dist/cbor-encoder.d.ts +0 -29
- package/dist/cbor-encoder.js +0 -81
- package/dist/cbor-encoder.js.map +0 -1
- package/dist/cbor-tags.d.ts +0 -3
- package/dist/cbor-tags.js +0 -4
- package/dist/cbor-tags.js.map +0 -1
- package/dist/http-error.d.ts +0 -10
- package/dist/http-error.js +0 -45
- package/dist/http-error.js.map +0 -1
- package/dist/json-decoder.d.ts +0 -31
- package/dist/json-decoder.js +0 -139
- package/dist/json-decoder.js.map +0 -1
- package/dist/json-encoder.d.ts +0 -35
- package/dist/json-encoder.js +0 -134
- package/dist/json-encoder.js.map +0 -1
- package/dist/media-type-decoder.d.ts +0 -4
- package/dist/media-type-decoder.js +0 -2
- package/dist/media-type-decoder.js.map +0 -1
- package/dist/media-type-decoders.js +0 -40
- package/dist/media-type-decoders.js.map +0 -1
- package/dist/media-type-encoder.js +0 -6
- package/dist/media-type-encoder.js.map +0 -1
- package/dist/media-type-encoders.js +0 -42
- package/dist/media-type-encoders.js.map +0 -1
- package/dist/url-encoder.js.map +0 -1
- package/src/binary-decoder.ts +0 -24
- package/src/binary-encoder.ts +0 -19
- package/src/cbor-decoder.ts +0 -148
- package/src/cbor-encoder.ts +0 -95
- package/src/cbor-tags.ts +0 -3
- package/src/http-error.ts +0 -55
- package/src/json-decoder.ts +0 -164
- package/src/json-encoder.ts +0 -162
- package/src/media-type-decoder.ts +0 -5
- package/src/media-type-decoders.ts +0 -59
- package/src/media-type-encoder.ts +0 -16
- package/src/media-type-encoders.ts +0 -61
- package/src/url-encoder.ts +0 -173
|
@@ -1,9 +1,24 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
// Copyright 2020 Outfox, Inc.
|
|
2
|
+
//
|
|
3
|
+
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
4
|
+
// you may not use this file except in compliance with the License.
|
|
5
|
+
// You may obtain a copy of the License at
|
|
6
|
+
//
|
|
7
|
+
// http://www.apache.org/licenses/LICENSE-2.0
|
|
8
|
+
//
|
|
9
|
+
// Unless required by applicable law or agreed to in writing, software
|
|
10
|
+
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
11
|
+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
12
|
+
// See the License for the specific language governing permissions and
|
|
13
|
+
// limitations under the License.
|
|
14
|
+
|
|
15
|
+
import { EMPTY, map, Observable, of, Subscription, switchMap, tap } from 'rxjs';
|
|
16
|
+
import { EventInfo, EventParser } from './event-parser';
|
|
3
17
|
import { validate } from './fetch';
|
|
4
18
|
import { Logger } from './logger';
|
|
5
19
|
import { MediaType } from './media-type';
|
|
6
20
|
import { ExtEventSource } from './request-factory';
|
|
21
|
+
import { unknownSet } from './util/any';
|
|
7
22
|
import { fromStream } from './util/stream-rxjs';
|
|
8
23
|
|
|
9
24
|
export class FetchEventSource extends EventTarget implements ExtEventSource {
|
|
@@ -23,22 +38,27 @@ export class FetchEventSource extends EventTarget implements ExtEventSource {
|
|
|
23
38
|
onmessage: ((this: EventSource, ev: MessageEvent) => unknown) | null = null;
|
|
24
39
|
onopen: ((this: EventSource, ev: Event) => unknown) | null = null;
|
|
25
40
|
|
|
41
|
+
get retryTime(): number {
|
|
42
|
+
return this.internalRetryTime;
|
|
43
|
+
}
|
|
44
|
+
|
|
26
45
|
private adapter: (
|
|
27
46
|
url: string,
|
|
28
47
|
requestInit: RequestInit
|
|
29
48
|
) => Observable<Request>;
|
|
30
49
|
private connectionSubscription?: Subscription;
|
|
31
|
-
private
|
|
32
|
-
private retryTime = 100;
|
|
50
|
+
private internalRetryTime = 100;
|
|
33
51
|
private retryAttempt = 0;
|
|
34
52
|
private connectionAttemptTime = 0;
|
|
53
|
+
private connectionOrigin?: string;
|
|
54
|
+
private reconnectTimeoutHandle?: number;
|
|
35
55
|
private lastEventId?: string;
|
|
36
56
|
private logger?: Logger;
|
|
37
|
-
private
|
|
38
|
-
private unprocessedText = '';
|
|
39
|
-
private eventTimeout?: number;
|
|
57
|
+
private readonly eventTimeout?: number;
|
|
40
58
|
private eventTimeoutCheckHandle?: number;
|
|
41
|
-
private
|
|
59
|
+
private lastEventReceivedTime = 0;
|
|
60
|
+
private eventParser = new EventParser();
|
|
61
|
+
private readonly externalAbortController?: AbortController;
|
|
42
62
|
|
|
43
63
|
constructor(
|
|
44
64
|
url: string,
|
|
@@ -46,6 +66,7 @@ export class FetchEventSource extends EventTarget implements ExtEventSource {
|
|
|
46
66
|
adapter?: (url: string, requestInit: RequestInit) => Observable<Request>;
|
|
47
67
|
eventTimeout?: number;
|
|
48
68
|
logger?: Logger;
|
|
69
|
+
abortController?: AbortController;
|
|
49
70
|
}
|
|
50
71
|
) {
|
|
51
72
|
super();
|
|
@@ -57,8 +78,13 @@ export class FetchEventSource extends EventTarget implements ExtEventSource {
|
|
|
57
78
|
eventSourceInit?.eventTimeout ??
|
|
58
79
|
FetchEventSource.EVENT_TIMEOUT_DEFAULT * 1000;
|
|
59
80
|
this.logger = eventSourceInit?.logger;
|
|
81
|
+
this.externalAbortController = eventSourceInit?.abortController;
|
|
60
82
|
}
|
|
61
83
|
|
|
84
|
+
//
|
|
85
|
+
// Connect
|
|
86
|
+
//
|
|
87
|
+
|
|
62
88
|
connect(): void {
|
|
63
89
|
if (this.readyState === this.CONNECTING || this.readyState === this.OPEN) {
|
|
64
90
|
// this.logger?.debug?.('skipping connect', { state: this.readyState });
|
|
@@ -74,19 +100,20 @@ export class FetchEventSource extends EventTarget implements ExtEventSource {
|
|
|
74
100
|
this.readyState = this.CONNECTING;
|
|
75
101
|
|
|
76
102
|
const headers = new Headers({
|
|
77
|
-
accept: MediaType.
|
|
103
|
+
accept: MediaType.EventStream.toString(),
|
|
78
104
|
});
|
|
79
105
|
if (this.lastEventId) {
|
|
80
106
|
headers.append(FetchEventSource.LAST_EVENT_ID_HEADER, this.lastEventId);
|
|
81
107
|
}
|
|
82
108
|
|
|
83
|
-
const
|
|
109
|
+
const abortController =
|
|
110
|
+
this.externalAbortController ?? new AbortController();
|
|
84
111
|
|
|
85
112
|
const requestInit: RequestInit = {
|
|
86
113
|
headers,
|
|
87
114
|
cache: 'no-store',
|
|
88
115
|
redirect: 'follow',
|
|
89
|
-
signal:
|
|
116
|
+
signal: abortController.signal,
|
|
90
117
|
};
|
|
91
118
|
|
|
92
119
|
this.connectionAttemptTime = Date.now();
|
|
@@ -95,7 +122,7 @@ export class FetchEventSource extends EventTarget implements ExtEventSource {
|
|
|
95
122
|
.pipe(
|
|
96
123
|
switchMap((request) => fetch(request)),
|
|
97
124
|
switchMap((response) => validate(response, true)),
|
|
98
|
-
tap(() => this.receivedHeaders()),
|
|
125
|
+
tap((response) => this.receivedHeaders(response)),
|
|
99
126
|
switchMap((response) => {
|
|
100
127
|
const body = response.body;
|
|
101
128
|
if (!body) {
|
|
@@ -108,19 +135,23 @@ export class FetchEventSource extends EventTarget implements ExtEventSource {
|
|
|
108
135
|
)
|
|
109
136
|
.subscribe({
|
|
110
137
|
error: (error: unknown) => {
|
|
111
|
-
abort.abort();
|
|
112
138
|
this.receivedError(error);
|
|
113
139
|
},
|
|
114
140
|
complete: () => {
|
|
115
|
-
abort.abort();
|
|
116
141
|
this.receivedComplete();
|
|
117
142
|
},
|
|
118
143
|
});
|
|
119
|
-
this.connectionSubscription.add(() =>
|
|
144
|
+
this.connectionSubscription.add(() => {
|
|
145
|
+
abortController.abort();
|
|
146
|
+
});
|
|
120
147
|
}
|
|
121
148
|
|
|
149
|
+
//
|
|
150
|
+
// Close
|
|
151
|
+
//
|
|
152
|
+
|
|
122
153
|
close(): void {
|
|
123
|
-
this.logger?.debug?.('
|
|
154
|
+
this.logger?.debug?.('close requested');
|
|
124
155
|
|
|
125
156
|
this.readyState = this.CLOSED;
|
|
126
157
|
|
|
@@ -131,19 +162,27 @@ export class FetchEventSource extends EventTarget implements ExtEventSource {
|
|
|
131
162
|
this.connectionSubscription?.unsubscribe();
|
|
132
163
|
this.connectionSubscription = undefined;
|
|
133
164
|
|
|
165
|
+
this.clearReconnect();
|
|
166
|
+
|
|
134
167
|
this.stopEventTimeoutCheck();
|
|
135
168
|
}
|
|
136
169
|
|
|
137
|
-
|
|
170
|
+
//
|
|
171
|
+
// Event Timeout
|
|
172
|
+
//
|
|
173
|
+
|
|
174
|
+
private startEventTimeoutCheck(lastEventReceivedTime: number) {
|
|
138
175
|
this.stopEventTimeoutCheck();
|
|
139
176
|
|
|
140
177
|
if (!this.eventTimeout) {
|
|
141
178
|
return;
|
|
142
179
|
}
|
|
143
180
|
|
|
181
|
+
this.lastEventReceivedTime = lastEventReceivedTime;
|
|
182
|
+
|
|
144
183
|
// this.logger?.debug?.('starting event timeout checks');
|
|
145
184
|
|
|
146
|
-
this.eventTimeoutCheckHandle = setInterval(
|
|
185
|
+
this.eventTimeoutCheckHandle = window.setInterval(
|
|
147
186
|
() => this.checkEventTimeout(),
|
|
148
187
|
FetchEventSource.EVENT_TIMEOUT_CHECK_INTERVAL * 1000
|
|
149
188
|
);
|
|
@@ -167,107 +206,95 @@ export class FetchEventSource extends EventTarget implements ExtEventSource {
|
|
|
167
206
|
// this.logger?.debug?.('checking event timeout');
|
|
168
207
|
|
|
169
208
|
// Check elapsed time since last received event
|
|
170
|
-
const elapsed = Date.now() -
|
|
209
|
+
const elapsed = Date.now() - this.lastEventReceivedTime;
|
|
171
210
|
if (elapsed > this.eventTimeout) {
|
|
172
211
|
this.logger?.debug?.('event timeout reached', {
|
|
173
212
|
elapsed,
|
|
174
213
|
});
|
|
175
|
-
|
|
176
|
-
this.internalClose();
|
|
214
|
+
this.fireErrorEvent(Error('EventTimeout'));
|
|
177
215
|
|
|
178
216
|
this.scheduleReconnect();
|
|
179
217
|
}
|
|
180
218
|
}
|
|
181
219
|
|
|
182
|
-
|
|
220
|
+
//
|
|
221
|
+
// Connection Handlers
|
|
222
|
+
//
|
|
223
|
+
|
|
224
|
+
private receivedHeaders(response: Response) {
|
|
183
225
|
if (this.readyState !== this.CONNECTING) {
|
|
184
|
-
this.
|
|
185
|
-
|
|
226
|
+
this.logger?.error?.('Invalid readyState for receiveHaders', {
|
|
227
|
+
readyState: this.readyState,
|
|
228
|
+
});
|
|
229
|
+
|
|
230
|
+
this.fireErrorEvent(Error('InvalidState'));
|
|
231
|
+
|
|
232
|
+
this.scheduleReconnect();
|
|
233
|
+
return;
|
|
186
234
|
}
|
|
187
235
|
|
|
188
|
-
this.logger?.debug?.('
|
|
236
|
+
this.logger?.debug?.('opened');
|
|
189
237
|
|
|
238
|
+
this.connectionOrigin = response.url;
|
|
190
239
|
this.retryAttempt = 0;
|
|
191
240
|
this.readyState = this.OPEN;
|
|
192
241
|
|
|
193
|
-
this
|
|
242
|
+
// Start event timeout check, treating this
|
|
243
|
+
// connect as last time we received an event
|
|
244
|
+
this.startEventTimeoutCheck(Date.now());
|
|
194
245
|
|
|
195
246
|
const event = new Event('open');
|
|
196
247
|
this.onopen?.(event);
|
|
197
248
|
this.dispatchEvent(event);
|
|
198
249
|
}
|
|
199
250
|
|
|
200
|
-
private receivedData(
|
|
201
|
-
this.
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
this.unprocessedBuffers.length - 1
|
|
206
|
-
];
|
|
207
|
-
const latestBytes = new Uint8Array(latest);
|
|
208
|
-
const latestNewLine = latestBytes.indexOf(0xa);
|
|
209
|
-
if (latestNewLine == -1) {
|
|
210
|
-
return;
|
|
211
|
-
}
|
|
212
|
-
const nextLine = latestNewLine + 1;
|
|
213
|
-
|
|
214
|
-
const readyToProcess = this.unprocessedBuffers.slice(0, -1);
|
|
215
|
-
readyToProcess.push(latest.slice(0, nextLine));
|
|
216
|
-
|
|
217
|
-
const leftOver = latest.slice(nextLine);
|
|
218
|
-
this.unprocessedBuffers = leftOver.byteLength ? [leftOver] : [];
|
|
251
|
+
private receivedData(buffer: ArrayBuffer) {
|
|
252
|
+
if (this.readyState !== this.OPEN) {
|
|
253
|
+
this.logger?.error?.('Invalid readyState for receiveData', {
|
|
254
|
+
readyState: this.readyState,
|
|
255
|
+
});
|
|
219
256
|
|
|
220
|
-
|
|
221
|
-
for (const buffer of readyToProcess) {
|
|
222
|
-
text += this.decoder.decode(buffer, { stream: true });
|
|
223
|
-
}
|
|
257
|
+
this.fireErrorEvent(Error('InvalidState'));
|
|
224
258
|
|
|
225
|
-
this.
|
|
259
|
+
this.scheduleReconnect();
|
|
260
|
+
return;
|
|
226
261
|
}
|
|
227
|
-
}
|
|
228
262
|
|
|
229
|
-
|
|
230
|
-
// Clear out carriage returns
|
|
231
|
-
text = text.replace('\r\n', '\n');
|
|
263
|
+
this.logger?.debug?.('received data', { length: validate.length });
|
|
232
264
|
|
|
233
|
-
this.
|
|
234
|
-
|
|
235
|
-
const eventStrings = this.extractEventStrings();
|
|
236
|
-
|
|
237
|
-
this.parseEvents(eventStrings);
|
|
265
|
+
this.eventParser.process(buffer, this.dispatchParsedEvent);
|
|
238
266
|
}
|
|
239
267
|
|
|
240
268
|
private receivedError(error: unknown) {
|
|
241
|
-
if (
|
|
242
|
-
error instanceof DOMException &&
|
|
243
|
-
error.code === DOMException.ABORT_ERR
|
|
244
|
-
) {
|
|
245
|
-
// this.logger?.debug?.('aborted');
|
|
246
|
-
|
|
269
|
+
if (this.readyState === this.CLOSED) {
|
|
247
270
|
return;
|
|
248
271
|
}
|
|
249
272
|
|
|
250
273
|
this.logger?.debug?.('received error', { error });
|
|
251
|
-
|
|
252
|
-
this.scheduleReconnect();
|
|
253
|
-
|
|
254
|
-
const event = new Event('error');
|
|
255
|
-
((event as unknown) as Record<string, unknown>).error = error;
|
|
256
|
-
|
|
257
|
-
this.onerror?.(event);
|
|
258
|
-
}
|
|
259
|
-
|
|
260
|
-
private receivedComplete() {
|
|
261
|
-
this.logger?.debug?.('received complete');
|
|
274
|
+
this.fireErrorEvent(error);
|
|
262
275
|
|
|
263
276
|
if (this.readyState !== this.CLOSED) {
|
|
264
277
|
this.scheduleReconnect();
|
|
278
|
+
}
|
|
279
|
+
}
|
|
265
280
|
|
|
281
|
+
private receivedComplete() {
|
|
282
|
+
if (this.readyState == this.CLOSED) {
|
|
266
283
|
return;
|
|
267
284
|
}
|
|
285
|
+
|
|
286
|
+
this.logger?.debug?.('received complete');
|
|
287
|
+
|
|
288
|
+
this.scheduleReconnect();
|
|
268
289
|
}
|
|
269
290
|
|
|
291
|
+
//
|
|
292
|
+
// Reconnection
|
|
293
|
+
//
|
|
294
|
+
|
|
270
295
|
private scheduleReconnect() {
|
|
296
|
+
this.internalClose();
|
|
297
|
+
|
|
271
298
|
// calculate total delay
|
|
272
299
|
const backOffDelay = Math.pow(this.retryAttempt, 2) * this.retryTime;
|
|
273
300
|
let retryDelay = Math.min(
|
|
@@ -275,113 +302,90 @@ export class FetchEventSource extends EventTarget implements ExtEventSource {
|
|
|
275
302
|
this.retryTime * FetchEventSource.MAX_RETRY_TIME_MULTIPLE
|
|
276
303
|
);
|
|
277
304
|
|
|
278
|
-
// Adjust delay by amount of time last
|
|
279
|
-
// on the first attempt
|
|
305
|
+
// Adjust delay by amount of time last connect
|
|
306
|
+
// cycle took, except on the first attempt
|
|
280
307
|
if (this.retryAttempt > 0) {
|
|
281
308
|
const connectionTime = Date.now() - this.connectionAttemptTime;
|
|
282
|
-
|
|
309
|
+
// Ensure delay is at least as large as
|
|
310
|
+
// minimum retry time interval
|
|
311
|
+
retryDelay = Math.max(retryDelay - connectionTime, this.retryTime);
|
|
283
312
|
}
|
|
284
313
|
|
|
285
314
|
this.retryAttempt++;
|
|
315
|
+
this.readyState = this.CONNECTING;
|
|
286
316
|
|
|
287
317
|
this.logger?.debug?.('scheduling reconnect', { retryDelay });
|
|
288
318
|
|
|
289
|
-
|
|
319
|
+
this.reconnectTimeoutHandle = window.setTimeout(
|
|
320
|
+
() => this.internalConnect(),
|
|
321
|
+
retryDelay
|
|
322
|
+
);
|
|
290
323
|
}
|
|
291
324
|
|
|
292
|
-
private
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
return [];
|
|
325
|
+
private clearReconnect() {
|
|
326
|
+
if (this.reconnectTimeoutHandle) {
|
|
327
|
+
clearTimeout(this.reconnectTimeoutHandle);
|
|
296
328
|
}
|
|
297
|
-
|
|
298
|
-
const eventStrings = received.split(EVENT_SEPARATOR);
|
|
299
|
-
|
|
300
|
-
const last = eventStrings.pop();
|
|
301
|
-
this.unprocessedText = last?.length ? last : '';
|
|
302
|
-
|
|
303
|
-
return eventStrings;
|
|
329
|
+
this.reconnectTimeoutHandle = undefined;
|
|
304
330
|
}
|
|
305
331
|
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
if (!line.length) {
|
|
310
|
-
continue;
|
|
311
|
-
}
|
|
312
|
-
|
|
313
|
-
const parsedEvent = FetchEventSource.parseEvent(eventString);
|
|
332
|
+
//
|
|
333
|
+
// Event Dispatch
|
|
334
|
+
//
|
|
314
335
|
|
|
315
|
-
|
|
316
|
-
|
|
336
|
+
private dispatchParsedEvent = (eventInfo: EventInfo) => {
|
|
337
|
+
this.lastEventReceivedTime = Date.now();
|
|
317
338
|
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
parsedEvent.id == null &&
|
|
321
|
-
parsedEvent.event == null &&
|
|
322
|
-
parsedEvent.data == null
|
|
323
|
-
) {
|
|
324
|
-
this.logger?.debug?.('updating retry timeout', { retryTime });
|
|
339
|
+
if (eventInfo.retry) {
|
|
340
|
+
const retryTime = Number.parseInt(eventInfo.retry, 10);
|
|
325
341
|
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
this.logger?.debug?.('ignoring invalid retry timeout event', {
|
|
329
|
-
parsedEvent,
|
|
330
|
-
});
|
|
331
|
-
}
|
|
342
|
+
if (Number.isSafeInteger(retryTime)) {
|
|
343
|
+
this.logger?.debug?.('updating retry timeout', { retryTime });
|
|
332
344
|
|
|
333
|
-
|
|
345
|
+
this.internalRetryTime = retryTime;
|
|
346
|
+
} else {
|
|
347
|
+
this.logger?.debug?.('ignoring invalid retry timeout event', {
|
|
348
|
+
eventInfo,
|
|
349
|
+
});
|
|
334
350
|
}
|
|
351
|
+
}
|
|
335
352
|
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
// Dispatch event
|
|
346
|
-
const event = new MessageEvent(parsedEvent.event ?? 'message', {
|
|
347
|
-
data: parsedEvent.data,
|
|
348
|
-
lastEventId: this.lastEventId,
|
|
349
|
-
});
|
|
350
|
-
|
|
351
|
-
this.onmessage?.(event);
|
|
352
|
-
this.dispatchEvent(event);
|
|
353
|
+
// skip empty events
|
|
354
|
+
if (
|
|
355
|
+
eventInfo.id == null &&
|
|
356
|
+
eventInfo.event == null &&
|
|
357
|
+
eventInfo.data == null
|
|
358
|
+
) {
|
|
359
|
+
// skip empty event
|
|
360
|
+
return;
|
|
353
361
|
}
|
|
354
|
-
}
|
|
355
362
|
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
case 'retry':
|
|
366
|
-
event.retry = value;
|
|
367
|
-
break;
|
|
368
|
-
case '':
|
|
369
|
-
// comment do nothing
|
|
370
|
-
break;
|
|
371
|
-
default:
|
|
372
|
-
(event as Record<string, string>)[key] = value.trim();
|
|
363
|
+
// Save last-event-id if the new id is valid
|
|
364
|
+
if (eventInfo.id != null) {
|
|
365
|
+
// Check for NULL as it is not allowed
|
|
366
|
+
if (eventInfo.id.indexOf('\0') == -1) {
|
|
367
|
+
this.lastEventId = eventInfo.id;
|
|
368
|
+
} else {
|
|
369
|
+
this.logger?.debug?.(
|
|
370
|
+
'event id contains null, unable to use for last-event-id'
|
|
371
|
+
);
|
|
373
372
|
}
|
|
374
373
|
}
|
|
375
374
|
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
375
|
+
// Dispatch event
|
|
376
|
+
const event = new MessageEvent(eventInfo.event ?? 'message', {
|
|
377
|
+
data: eventInfo.data,
|
|
378
|
+
lastEventId: this.lastEventId,
|
|
379
|
+
origin: this.connectionOrigin,
|
|
380
|
+
});
|
|
379
381
|
|
|
380
|
-
|
|
382
|
+
this.onmessage?.(event);
|
|
383
|
+
this.dispatchEvent(event);
|
|
384
|
+
};
|
|
381
385
|
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
386
|
+
fireErrorEvent(error: unknown): void {
|
|
387
|
+
const event = new Event('error');
|
|
388
|
+
unknownSet(event, 'error', error);
|
|
389
|
+
this.onerror?.(event);
|
|
390
|
+
}
|
|
387
391
|
}
|