@outfoxx/sunday 1.0.8 → 1.0.10
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 +36 -2
- package/build/date-time-types.d.ts +35 -0
- package/build/date-time-types.js +699 -0
- package/build/date-time-types.js.map +1 -0
- package/build/event-parser.d.ts +17 -0
- package/build/event-parser.js +151 -0
- package/build/event-parser.js.map +1 -0
- package/build/fetch-event-source.d.ts +66 -0
- package/build/fetch-event-source.js +365 -0
- package/build/fetch-event-source.js.map +1 -0
- package/build/fetch-request-factory.d.ts +32 -0
- package/build/fetch-request-factory.js +316 -0
- package/build/fetch-request-factory.js.map +1 -0
- package/build/fetch.d.ts +9 -0
- package/build/fetch.js +140 -0
- package/build/fetch.js.map +1 -0
- package/build/header-parameters.d.ts +3 -0
- package/build/header-parameters.js +82 -0
- package/build/header-parameters.js.map +1 -0
- package/build/index.d.ts +26 -0
- package/build/index.js +41 -0
- package/build/index.js.map +1 -0
- package/{dist → build}/logger.d.ts +9 -0
- package/build/logger.js +36 -0
- package/build/logger.js.map +1 -0
- package/build/media-type-codecs/any-text-decoder.d.ts +7 -0
- package/build/media-type-codecs/any-text-decoder.js +29 -0
- package/build/media-type-codecs/any-text-decoder.js.map +1 -0
- package/build/media-type-codecs/any-text-encoder.d.ts +6 -0
- package/build/media-type-codecs/any-text-encoder.js +27 -0
- package/build/media-type-codecs/any-text-encoder.js.map +1 -0
- package/build/media-type-codecs/binary-decoder.d.ts +7 -0
- package/build/media-type-codecs/binary-decoder.js +33 -0
- package/build/media-type-codecs/binary-decoder.js.map +1 -0
- package/build/media-type-codecs/binary-encoder.d.ts +6 -0
- package/build/media-type-codecs/binary-encoder.js +30 -0
- package/build/media-type-codecs/binary-encoder.js.map +1 -0
- package/build/media-type-codecs/cbor-decoder.d.ts +13 -0
- package/build/media-type-codecs/cbor-decoder.js +38 -0
- package/build/media-type-codecs/cbor-decoder.js.map +1 -0
- package/build/media-type-codecs/cbor-encoder.d.ts +12 -0
- package/build/media-type-codecs/cbor-encoder.js +36 -0
- package/build/media-type-codecs/cbor-encoder.js.map +1 -0
- package/build/media-type-codecs/cbor-tags.d.ts +6 -0
- package/build/media-type-codecs/cbor-tags.js +20 -0
- package/build/media-type-codecs/cbor-tags.js.map +1 -0
- package/build/media-type-codecs/default-policies.d.ts +3 -0
- package/build/media-type-codecs/default-policies.js +33 -0
- package/build/media-type-codecs/default-policies.js.map +1 -0
- package/build/media-type-codecs/json-decoder.d.ts +14 -0
- package/build/media-type-codecs/json-decoder.js +40 -0
- package/build/media-type-codecs/json-decoder.js.map +1 -0
- package/build/media-type-codecs/json-encoder.d.ts +13 -0
- package/build/media-type-codecs/json-encoder.js +43 -0
- package/build/media-type-codecs/json-encoder.js.map +1 -0
- package/build/media-type-codecs/media-type-decoder.d.ts +14 -0
- package/build/media-type-codecs/media-type-decoder.js +18 -0
- package/build/media-type-codecs/media-type-decoder.js.map +1 -0
- package/build/media-type-codecs/media-type-decoders.d.ts +16 -0
- package/build/media-type-codecs/media-type-decoders.js +63 -0
- package/build/media-type-codecs/media-type-decoders.js.map +1 -0
- package/build/media-type-codecs/media-type-encoder.d.ts +12 -0
- package/build/media-type-codecs/media-type-encoder.js +22 -0
- package/build/media-type-codecs/media-type-encoder.js.map +1 -0
- package/build/media-type-codecs/media-type-encoders.d.ts +16 -0
- package/build/media-type-codecs/media-type-encoders.js +65 -0
- package/build/media-type-codecs/media-type-encoders.js.map +1 -0
- package/build/media-type-codecs/www-form-url-encoder.d.ts +31 -0
- package/build/media-type-codecs/www-form-url-encoder.js +281 -0
- package/build/media-type-codecs/www-form-url-encoder.js.map +1 -0
- package/build/media-type.d.ts +94 -0
- package/build/media-type.js +305 -0
- package/build/media-type.js.map +1 -0
- package/build/problem.d.ts +35 -0
- package/build/problem.js +122 -0
- package/build/problem.js.map +1 -0
- package/build/request-adapters.d.ts +23 -0
- package/build/request-adapters.js +53 -0
- package/build/request-adapters.js.map +1 -0
- package/build/request-factory.d.ts +39 -0
- package/build/request-factory.js +15 -0
- package/build/request-factory.js.map +1 -0
- package/build/result-response.d.ts +4 -0
- package/build/result-response.js +15 -0
- package/build/result-response.js.map +1 -0
- package/build/schema-builtins.d.ts +21 -0
- package/build/schema-builtins.js +201 -0
- package/build/schema-builtins.js.map +1 -0
- package/build/schema-policy.d.ts +31 -0
- package/build/schema-policy.js +41 -0
- package/build/schema-policy.js.map +1 -0
- package/build/schema-runtime.d.ts +25 -0
- package/build/schema-runtime.js +77 -0
- package/build/schema-runtime.js.map +1 -0
- package/build/sunday-error.d.ts +10 -0
- package/build/sunday-error.js +36 -0
- package/build/sunday-error.js.map +1 -0
- package/build/url-template.js +33 -0
- package/build/url-template.js.map +1 -0
- package/build/util/async-iterables.d.ts +15 -0
- package/build/util/async-iterables.js +179 -0
- package/build/util/async-iterables.js.map +1 -0
- package/build/util/errors.d.ts +2 -0
- package/build/util/errors.js +65 -0
- package/build/util/errors.js.map +1 -0
- package/build/util/nullify.d.ts +5 -0
- package/build/util/nullify.js +37 -0
- package/build/util/nullify.js.map +1 -0
- package/build/util/numbers.d.ts +11 -0
- package/build/util/numbers.js +41 -0
- package/build/util/numbers.js.map +1 -0
- package/build/util/subscriptions.d.ts +16 -0
- package/build/util/subscriptions.js +78 -0
- package/build/util/subscriptions.js.map +1 -0
- package/build/util/unknowns.d.ts +2 -0
- package/build/util/unknowns.js +23 -0
- package/build/util/unknowns.js.map +1 -0
- package/package.json +72 -49
- package/dist/any-type.d.ts +0 -2
- package/dist/any-type.js +0 -2
- package/dist/any-type.js.map +0 -1
- package/dist/binary-decoder.d.ts +0 -6
- package/dist/binary-decoder.js +0 -16
- package/dist/binary-decoder.js.map +0 -1
- package/dist/binary-encoder.d.ts +0 -5
- 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/class-type.d.ts +0 -2
- package/dist/class-type.js +0 -2
- package/dist/class-type.js.map +0 -1
- package/dist/date-time-types.d.ts +0 -7
- package/dist/date-time-types.js +0 -5
- package/dist/date-time-types.js.map +0 -1
- package/dist/fetch-event-source.d.ts +0 -52
- package/dist/fetch-event-source.js +0 -271
- package/dist/fetch-event-source.js.map +0 -1
- package/dist/fetch-request-factory.d.ts +0 -26
- package/dist/fetch-request-factory.js +0 -125
- package/dist/fetch-request-factory.js.map +0 -1
- package/dist/fetch.d.ts +0 -1
- package/dist/fetch.js +0 -19
- package/dist/fetch.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/index.d.ts +0 -25
- package/dist/index.js +0 -26
- package/dist/index.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 -116
- package/dist/json-encoder.js.map +0 -1
- package/dist/logger.js +0 -2
- package/dist/logger.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.d.ts +0 -17
- package/dist/media-type-decoders.js +0 -40
- package/dist/media-type-decoders.js.map +0 -1
- package/dist/media-type-encoder.d.ts +0 -8
- package/dist/media-type-encoder.js +0 -6
- package/dist/media-type-encoder.js.map +0 -1
- package/dist/media-type-encoders.d.ts +0 -17
- package/dist/media-type-encoders.js +0 -42
- package/dist/media-type-encoders.js.map +0 -1
- package/dist/media-type.d.ts +0 -14
- package/dist/media-type.js +0 -18
- package/dist/media-type.js.map +0 -1
- package/dist/problem.d.ts +0 -16
- package/dist/problem.js +0 -11
- package/dist/problem.js.map +0 -1
- package/dist/request-factory.d.ts +0 -38
- package/dist/request-factory.js +0 -2
- package/dist/request-factory.js.map +0 -1
- package/dist/url-encoder.d.ts +0 -59
- package/dist/url-encoder.js +0 -141
- package/dist/url-encoder.js.map +0 -1
- package/dist/url-template.js +0 -18
- package/dist/url-template.js.map +0 -1
- package/dist/util/base64.d.ts +0 -5
- package/dist/util/base64.js +0 -64
- package/dist/util/base64.js.map +0 -1
- package/dist/util/hex.d.ts +0 -4
- package/dist/util/hex.js +0 -18
- package/dist/util/hex.js.map +0 -1
- package/dist/util/rxjs.d.ts +0 -2
- package/dist/util/rxjs.js +0 -14
- package/dist/util/rxjs.js.map +0 -1
- package/dist/util/stream-rxjs.d.ts +0 -2
- package/dist/util/stream-rxjs.js +0 -26
- package/dist/util/stream-rxjs.js.map +0 -1
- package/src/any-type.ts +0 -4
- 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/class-type.ts +0 -3
- package/src/date-time-types.ts +0 -10
- package/src/fetch-event-source.ts +0 -387
- package/src/fetch-request-factory.ts +0 -225
- package/src/fetch.ts +0 -30
- package/src/http-error.ts +0 -55
- package/src/index.ts +0 -26
- package/src/json-decoder.ts +0 -164
- package/src/json-encoder.ts +0 -144
- package/src/logger.ts +0 -8
- 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/media-type.ts +0 -25
- package/src/problem.ts +0 -25
- package/src/request-factory.ts +0 -76
- package/src/url-encoder.ts +0 -173
- package/src/url-template.ts +0 -21
- package/src/util/base64.ts +0 -77
- package/src/util/hex.ts +0 -15
- package/src/util/rxjs.ts +0 -18
- package/src/util/stream-rxjs.ts +0 -28
- /package/{dist → build}/url-template.d.ts +0 -0
package/src/cbor-encoder.ts
DELETED
|
@@ -1,95 +0,0 @@
|
|
|
1
|
-
import { CBOR, TaggedValue } from '@outfoxx/cbor-redux';
|
|
2
|
-
import { JsonStringifier } from '@outfoxx/jackson-js';
|
|
3
|
-
import { CustomMapper, Serializer } from '@outfoxx/jackson-js/dist/@types';
|
|
4
|
-
import { DateTime } from 'luxon';
|
|
5
|
-
import { AnyType } from './any-type';
|
|
6
|
-
import { epochDateTag, isoDateTag, uriTag } from './cbor-tags';
|
|
7
|
-
import { MediaTypeEncoder } from './media-type-encoder';
|
|
8
|
-
|
|
9
|
-
export class CBOREncoder implements MediaTypeEncoder {
|
|
10
|
-
static get default(): CBOREncoder {
|
|
11
|
-
return new CBOREncoder(CBOREncoder.DateEncoding.SECONDS_SINCE_EPOCH);
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
private readonly customSerializers: CustomMapper<Serializer>[];
|
|
15
|
-
private stringifier = new JsonStringifier();
|
|
16
|
-
|
|
17
|
-
constructor(readonly dateEncoding: CBOREncoder.DateEncoding) {
|
|
18
|
-
this.customSerializers = [
|
|
19
|
-
{
|
|
20
|
-
type: () => Date,
|
|
21
|
-
mapper: this.dateSerializer,
|
|
22
|
-
},
|
|
23
|
-
{
|
|
24
|
-
type: () => DateTime,
|
|
25
|
-
mapper: this.dateTimeSerializer,
|
|
26
|
-
},
|
|
27
|
-
{
|
|
28
|
-
type: () => URL,
|
|
29
|
-
mapper: this.urlSerializer,
|
|
30
|
-
},
|
|
31
|
-
];
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
encode<T>(value: T, type?: AnyType): ArrayBuffer {
|
|
35
|
-
return CBOR.encode(this.encodeJSON(value, type));
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
private encodeJSON<T>(value: T, type?: AnyType): unknown {
|
|
39
|
-
return this.stringifier.transform(value, {
|
|
40
|
-
serializers: this.customSerializers,
|
|
41
|
-
mainCreator: () => type ?? [Object],
|
|
42
|
-
});
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
private dateTimeSerializer: Serializer = (key: string, value: DateTime) => {
|
|
46
|
-
if (value == null) {
|
|
47
|
-
return null;
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
switch (this.dateEncoding) {
|
|
51
|
-
case CBOREncoder.DateEncoding.ISO8601:
|
|
52
|
-
return new TaggedValue(value.toISO(), isoDateTag);
|
|
53
|
-
case CBOREncoder.DateEncoding.SECONDS_SINCE_EPOCH:
|
|
54
|
-
return new TaggedValue(value.toSeconds(), epochDateTag);
|
|
55
|
-
}
|
|
56
|
-
};
|
|
57
|
-
|
|
58
|
-
private dateSerializer: Serializer = (key: string, value: Date) => {
|
|
59
|
-
if (value == null) {
|
|
60
|
-
return null;
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
switch (this.dateEncoding) {
|
|
64
|
-
case CBOREncoder.DateEncoding.ISO8601:
|
|
65
|
-
return new TaggedValue(value.toISOString(), isoDateTag);
|
|
66
|
-
case CBOREncoder.DateEncoding.SECONDS_SINCE_EPOCH:
|
|
67
|
-
return new TaggedValue(value.getTime() / 1000.0, epochDateTag);
|
|
68
|
-
}
|
|
69
|
-
};
|
|
70
|
-
|
|
71
|
-
private urlSerializer: Serializer = (key: string, value: URL) => {
|
|
72
|
-
if (value == null) {
|
|
73
|
-
return null;
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
return new TaggedValue(value.toString(), uriTag);
|
|
77
|
-
};
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
export namespace CBOREncoder {
|
|
81
|
-
/**
|
|
82
|
-
* Configures how `Date` & `DateTime` parameters are encoded.
|
|
83
|
-
*/
|
|
84
|
-
export enum DateEncoding {
|
|
85
|
-
/**
|
|
86
|
-
* Encode `Date`/`DateTime` values as a UNIX timestamp (floating point seconds since epoch).
|
|
87
|
-
*/
|
|
88
|
-
SECONDS_SINCE_EPOCH = 'float',
|
|
89
|
-
|
|
90
|
-
/**
|
|
91
|
-
* Encode `Date`/`DateTime` values as an ISO-8601-formatted string (in RFC 3339 format). This is the default behavior.
|
|
92
|
-
*/
|
|
93
|
-
ISO8601 = 'string',
|
|
94
|
-
}
|
|
95
|
-
}
|
package/src/cbor-tags.ts
DELETED
package/src/class-type.ts
DELETED
package/src/date-time-types.ts
DELETED
|
@@ -1,10 +0,0 @@
|
|
|
1
|
-
import { DateTime as luxonDateTime } from 'luxon';
|
|
2
|
-
|
|
3
|
-
export type Date = luxonDateTime;
|
|
4
|
-
export const Date = luxonDateTime;
|
|
5
|
-
|
|
6
|
-
export type Time = luxonDateTime;
|
|
7
|
-
export const Time = luxonDateTime;
|
|
8
|
-
|
|
9
|
-
export type DateTime = luxonDateTime;
|
|
10
|
-
export const DateTime = luxonDateTime;
|
|
@@ -1,387 +0,0 @@
|
|
|
1
|
-
import { EMPTY, Observable, of, Subscription } from 'rxjs';
|
|
2
|
-
import { map, switchMap, tap } from 'rxjs/operators';
|
|
3
|
-
import { validate } from './fetch';
|
|
4
|
-
import { Logger } from './logger';
|
|
5
|
-
import { MediaType } from './media-type';
|
|
6
|
-
import { ExtEventSource } from './request-factory';
|
|
7
|
-
import { fromStream } from './util/stream-rxjs';
|
|
8
|
-
|
|
9
|
-
export class FetchEventSource extends EventTarget implements ExtEventSource {
|
|
10
|
-
private static LAST_EVENT_ID_HEADER = 'Last-Event-ID';
|
|
11
|
-
private static MAX_RETRY_TIME_MULTIPLE = 30;
|
|
12
|
-
private static EVENT_TIMEOUT_DEFAULT = 75;
|
|
13
|
-
private static EVENT_TIMEOUT_CHECK_INTERVAL = 2;
|
|
14
|
-
|
|
15
|
-
CONNECTING = 0;
|
|
16
|
-
OPEN = 1;
|
|
17
|
-
CLOSED = 2;
|
|
18
|
-
|
|
19
|
-
readyState: number = this.CLOSED;
|
|
20
|
-
url: string;
|
|
21
|
-
withCredentials = false;
|
|
22
|
-
onerror: ((this: EventSource, ev: Event) => unknown) | null = null;
|
|
23
|
-
onmessage: ((this: EventSource, ev: MessageEvent) => unknown) | null = null;
|
|
24
|
-
onopen: ((this: EventSource, ev: Event) => unknown) | null = null;
|
|
25
|
-
|
|
26
|
-
private adapter: (
|
|
27
|
-
url: string,
|
|
28
|
-
requestInit: RequestInit
|
|
29
|
-
) => Observable<Request>;
|
|
30
|
-
private connectionSubscription?: Subscription;
|
|
31
|
-
private decoder: TextDecoder = new TextDecoder('utf-8');
|
|
32
|
-
private retryTime = 100;
|
|
33
|
-
private retryAttempt = 0;
|
|
34
|
-
private connectionAttemptTime = 0;
|
|
35
|
-
private lastEventId?: string;
|
|
36
|
-
private logger?: Logger;
|
|
37
|
-
private unprocessedBuffers: ArrayBuffer[] = [];
|
|
38
|
-
private unprocessedText = '';
|
|
39
|
-
private eventTimeout?: number;
|
|
40
|
-
private eventTimeoutCheckHandle?: number;
|
|
41
|
-
private lastEventTime?: number;
|
|
42
|
-
|
|
43
|
-
constructor(
|
|
44
|
-
url: string,
|
|
45
|
-
eventSourceInit?: EventSourceInit & {
|
|
46
|
-
adapter?: (url: string, requestInit: RequestInit) => Observable<Request>;
|
|
47
|
-
eventTimeout?: number;
|
|
48
|
-
logger?: Logger;
|
|
49
|
-
}
|
|
50
|
-
) {
|
|
51
|
-
super();
|
|
52
|
-
this.url = url;
|
|
53
|
-
this.adapter =
|
|
54
|
-
eventSourceInit?.adapter ??
|
|
55
|
-
((_url, requestInit) => of(new Request(_url, requestInit)));
|
|
56
|
-
this.eventTimeout =
|
|
57
|
-
eventSourceInit?.eventTimeout ??
|
|
58
|
-
FetchEventSource.EVENT_TIMEOUT_DEFAULT * 1000;
|
|
59
|
-
this.logger = eventSourceInit?.logger;
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
connect(): void {
|
|
63
|
-
if (this.readyState === this.CONNECTING || this.readyState === this.OPEN) {
|
|
64
|
-
// this.logger?.debug?.('skipping connect', { state: this.readyState });
|
|
65
|
-
return;
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
this.internalConnect();
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
private internalConnect() {
|
|
72
|
-
this.logger?.debug?.('connecting');
|
|
73
|
-
|
|
74
|
-
this.readyState = this.CONNECTING;
|
|
75
|
-
|
|
76
|
-
const headers = new Headers({
|
|
77
|
-
accept: MediaType.EVENT_STREAM,
|
|
78
|
-
});
|
|
79
|
-
if (this.lastEventId) {
|
|
80
|
-
headers.append(FetchEventSource.LAST_EVENT_ID_HEADER, this.lastEventId);
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
const abort = new AbortController();
|
|
84
|
-
|
|
85
|
-
const requestInit: RequestInit = {
|
|
86
|
-
headers,
|
|
87
|
-
cache: 'no-store',
|
|
88
|
-
redirect: 'follow',
|
|
89
|
-
signal: abort.signal,
|
|
90
|
-
};
|
|
91
|
-
|
|
92
|
-
this.connectionAttemptTime = Date.now();
|
|
93
|
-
|
|
94
|
-
this.connectionSubscription = this.adapter(this.url, requestInit)
|
|
95
|
-
.pipe(
|
|
96
|
-
switchMap((request) => fetch(request)),
|
|
97
|
-
switchMap((response) => validate(response, true)),
|
|
98
|
-
tap(() => this.receivedHeaders()),
|
|
99
|
-
switchMap((response) => {
|
|
100
|
-
const body = response.body;
|
|
101
|
-
if (!body) {
|
|
102
|
-
return EMPTY;
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
return fromStream(body);
|
|
106
|
-
}),
|
|
107
|
-
map((value) => this.receivedData(value))
|
|
108
|
-
)
|
|
109
|
-
.subscribe({
|
|
110
|
-
error: (error: unknown) => {
|
|
111
|
-
abort.abort();
|
|
112
|
-
this.receivedError(error);
|
|
113
|
-
},
|
|
114
|
-
complete: () => {
|
|
115
|
-
abort.abort();
|
|
116
|
-
this.receivedComplete();
|
|
117
|
-
},
|
|
118
|
-
});
|
|
119
|
-
this.connectionSubscription.add(() => abort.abort());
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
close(): void {
|
|
123
|
-
this.logger?.debug?.('closed');
|
|
124
|
-
|
|
125
|
-
this.readyState = this.CLOSED;
|
|
126
|
-
|
|
127
|
-
this.internalClose();
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
private internalClose() {
|
|
131
|
-
this.connectionSubscription?.unsubscribe();
|
|
132
|
-
this.connectionSubscription = undefined;
|
|
133
|
-
|
|
134
|
-
this.stopEventTimeoutCheck();
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
private startEventTimeoutCheck() {
|
|
138
|
-
this.stopEventTimeoutCheck();
|
|
139
|
-
|
|
140
|
-
if (!this.eventTimeout) {
|
|
141
|
-
return;
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
// this.logger?.debug?.('starting event timeout checks');
|
|
145
|
-
|
|
146
|
-
this.eventTimeoutCheckHandle = setInterval(
|
|
147
|
-
() => this.checkEventTimeout(),
|
|
148
|
-
FetchEventSource.EVENT_TIMEOUT_CHECK_INTERVAL * 1000
|
|
149
|
-
);
|
|
150
|
-
}
|
|
151
|
-
|
|
152
|
-
private stopEventTimeoutCheck() {
|
|
153
|
-
// this.logger?.debug?.('stopping event timeout checks');
|
|
154
|
-
|
|
155
|
-
if (this.eventTimeoutCheckHandle) {
|
|
156
|
-
clearInterval(this.eventTimeoutCheckHandle);
|
|
157
|
-
}
|
|
158
|
-
this.eventTimeoutCheckHandle = undefined;
|
|
159
|
-
}
|
|
160
|
-
|
|
161
|
-
private checkEventTimeout() {
|
|
162
|
-
if (!this.eventTimeout) {
|
|
163
|
-
this.stopEventTimeoutCheck();
|
|
164
|
-
return;
|
|
165
|
-
}
|
|
166
|
-
|
|
167
|
-
// this.logger?.debug?.('checking event timeout');
|
|
168
|
-
|
|
169
|
-
// Check elapsed time since last received event
|
|
170
|
-
const elapsed = Date.now() - (this.lastEventTime ?? 0);
|
|
171
|
-
if (elapsed > this.eventTimeout) {
|
|
172
|
-
this.logger?.debug?.('event timeout reached', {
|
|
173
|
-
elapsed,
|
|
174
|
-
});
|
|
175
|
-
|
|
176
|
-
this.internalClose();
|
|
177
|
-
|
|
178
|
-
this.scheduleReconnect();
|
|
179
|
-
}
|
|
180
|
-
}
|
|
181
|
-
|
|
182
|
-
private receivedHeaders() {
|
|
183
|
-
if (this.readyState !== this.CONNECTING) {
|
|
184
|
-
this.close();
|
|
185
|
-
throw Error('Invalid readyState');
|
|
186
|
-
}
|
|
187
|
-
|
|
188
|
-
this.logger?.debug?.('connected');
|
|
189
|
-
|
|
190
|
-
this.retryAttempt = 0;
|
|
191
|
-
this.readyState = this.OPEN;
|
|
192
|
-
|
|
193
|
-
this.startEventTimeoutCheck();
|
|
194
|
-
|
|
195
|
-
const event = new Event('open');
|
|
196
|
-
this.onopen?.(event);
|
|
197
|
-
this.dispatchEvent(event);
|
|
198
|
-
}
|
|
199
|
-
|
|
200
|
-
private receivedData(value: ArrayBuffer) {
|
|
201
|
-
this.unprocessedBuffers.push(value);
|
|
202
|
-
|
|
203
|
-
while (this.unprocessedBuffers.length) {
|
|
204
|
-
const latest = this.unprocessedBuffers[
|
|
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] : [];
|
|
219
|
-
|
|
220
|
-
let text = '';
|
|
221
|
-
for (const buffer of readyToProcess) {
|
|
222
|
-
text += this.decoder.decode(buffer, { stream: true });
|
|
223
|
-
}
|
|
224
|
-
|
|
225
|
-
this.receivedText(text);
|
|
226
|
-
}
|
|
227
|
-
}
|
|
228
|
-
|
|
229
|
-
private receivedText(text: string) {
|
|
230
|
-
// Clear out carriage returns
|
|
231
|
-
text = text.replace('\r\n', '\n');
|
|
232
|
-
|
|
233
|
-
this.unprocessedText += text;
|
|
234
|
-
|
|
235
|
-
const eventStrings = this.extractEventStrings();
|
|
236
|
-
|
|
237
|
-
this.parseEvents(eventStrings);
|
|
238
|
-
}
|
|
239
|
-
|
|
240
|
-
private receivedError(error: unknown) {
|
|
241
|
-
if (
|
|
242
|
-
error instanceof DOMException &&
|
|
243
|
-
error.code === DOMException.ABORT_ERR
|
|
244
|
-
) {
|
|
245
|
-
// this.logger?.debug?.('aborted');
|
|
246
|
-
|
|
247
|
-
return;
|
|
248
|
-
}
|
|
249
|
-
|
|
250
|
-
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');
|
|
262
|
-
|
|
263
|
-
if (this.readyState !== this.CLOSED) {
|
|
264
|
-
this.scheduleReconnect();
|
|
265
|
-
|
|
266
|
-
return;
|
|
267
|
-
}
|
|
268
|
-
}
|
|
269
|
-
|
|
270
|
-
private scheduleReconnect() {
|
|
271
|
-
// calculate total delay
|
|
272
|
-
const backOffDelay = Math.pow(this.retryAttempt, 2) * this.retryTime;
|
|
273
|
-
let retryDelay = Math.min(
|
|
274
|
-
this.retryTime + backOffDelay,
|
|
275
|
-
this.retryTime * FetchEventSource.MAX_RETRY_TIME_MULTIPLE
|
|
276
|
-
);
|
|
277
|
-
|
|
278
|
-
// Adjust delay by amount of time last reconnect cycle took, except
|
|
279
|
-
// on the first attempt
|
|
280
|
-
if (this.retryAttempt > 0) {
|
|
281
|
-
const connectionTime = Date.now() - this.connectionAttemptTime;
|
|
282
|
-
retryDelay = Math.max(retryDelay - connectionTime, 0);
|
|
283
|
-
}
|
|
284
|
-
|
|
285
|
-
this.retryAttempt++;
|
|
286
|
-
|
|
287
|
-
this.logger?.debug?.('scheduling reconnect', { retryDelay });
|
|
288
|
-
|
|
289
|
-
setTimeout(() => this.internalConnect(), retryDelay);
|
|
290
|
-
}
|
|
291
|
-
|
|
292
|
-
private extractEventStrings(): string[] {
|
|
293
|
-
const received = this.unprocessedText;
|
|
294
|
-
if (!received.length) {
|
|
295
|
-
return [];
|
|
296
|
-
}
|
|
297
|
-
|
|
298
|
-
const eventStrings = received.split(EVENT_SEPARATOR);
|
|
299
|
-
|
|
300
|
-
const last = eventStrings.pop();
|
|
301
|
-
this.unprocessedText = last?.length ? last : '';
|
|
302
|
-
|
|
303
|
-
return eventStrings;
|
|
304
|
-
}
|
|
305
|
-
|
|
306
|
-
private parseEvents(eventStrings: string[]) {
|
|
307
|
-
for (const eventString of eventStrings) {
|
|
308
|
-
const line = eventString.trim();
|
|
309
|
-
if (!line.length) {
|
|
310
|
-
continue;
|
|
311
|
-
}
|
|
312
|
-
|
|
313
|
-
const parsedEvent = FetchEventSource.parseEvent(eventString);
|
|
314
|
-
|
|
315
|
-
if (parsedEvent.retry) {
|
|
316
|
-
const retryTime = Number.parseInt(parsedEvent.retry, 10);
|
|
317
|
-
|
|
318
|
-
if (
|
|
319
|
-
Number.isSafeInteger(retryTime) &&
|
|
320
|
-
parsedEvent.id == null &&
|
|
321
|
-
parsedEvent.event == null &&
|
|
322
|
-
parsedEvent.data == null
|
|
323
|
-
) {
|
|
324
|
-
this.logger?.debug?.('updating retry timeout', { retryTime });
|
|
325
|
-
|
|
326
|
-
this.retryTime = retryTime;
|
|
327
|
-
} else {
|
|
328
|
-
this.logger?.debug?.('ignoring invalid retry timeout event', {
|
|
329
|
-
parsedEvent,
|
|
330
|
-
});
|
|
331
|
-
}
|
|
332
|
-
|
|
333
|
-
continue;
|
|
334
|
-
}
|
|
335
|
-
|
|
336
|
-
this.lastEventTime = Date.now();
|
|
337
|
-
this.lastEventId = parsedEvent.id ?? this.lastEventId;
|
|
338
|
-
|
|
339
|
-
// Skip empty or comment only events
|
|
340
|
-
if (!parsedEvent.id && !parsedEvent.event && !parsedEvent.data) {
|
|
341
|
-
// this.logger?.debug?.('skipping empty event');
|
|
342
|
-
continue;
|
|
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
|
-
}
|
|
354
|
-
}
|
|
355
|
-
|
|
356
|
-
private static parseEvent(eventString: string): EventInfo {
|
|
357
|
-
const event: EventInfo = {};
|
|
358
|
-
|
|
359
|
-
for (const line of eventString.split('\n')) {
|
|
360
|
-
const fields = line.split(':');
|
|
361
|
-
const key = fields[0].trim();
|
|
362
|
-
const value = fields.splice(1).join(':');
|
|
363
|
-
|
|
364
|
-
switch (key) {
|
|
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();
|
|
373
|
-
}
|
|
374
|
-
}
|
|
375
|
-
|
|
376
|
-
return event;
|
|
377
|
-
}
|
|
378
|
-
}
|
|
379
|
-
|
|
380
|
-
const EVENT_SEPARATOR = /\n\n/;
|
|
381
|
-
|
|
382
|
-
interface EventInfo {
|
|
383
|
-
id?: string;
|
|
384
|
-
event?: string;
|
|
385
|
-
data?: string;
|
|
386
|
-
retry?: string;
|
|
387
|
-
}
|