@outfoxx/sunday 1.0.9 → 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.
Files changed (234) hide show
  1. package/LICENSE.txt +203 -0
  2. package/README.md +36 -2
  3. package/build/date-time-types.d.ts +35 -0
  4. package/build/date-time-types.js +699 -0
  5. package/build/date-time-types.js.map +1 -0
  6. package/build/event-parser.d.ts +17 -0
  7. package/build/event-parser.js +151 -0
  8. package/build/event-parser.js.map +1 -0
  9. package/build/fetch-event-source.d.ts +66 -0
  10. package/build/fetch-event-source.js +365 -0
  11. package/build/fetch-event-source.js.map +1 -0
  12. package/build/fetch-request-factory.d.ts +32 -0
  13. package/build/fetch-request-factory.js +316 -0
  14. package/build/fetch-request-factory.js.map +1 -0
  15. package/build/fetch.d.ts +9 -0
  16. package/build/fetch.js +140 -0
  17. package/build/fetch.js.map +1 -0
  18. package/build/header-parameters.d.ts +3 -0
  19. package/build/header-parameters.js +82 -0
  20. package/build/header-parameters.js.map +1 -0
  21. package/build/index.d.ts +26 -0
  22. package/build/index.js +41 -0
  23. package/build/index.js.map +1 -0
  24. package/{dist → build}/logger.d.ts +9 -0
  25. package/build/logger.js +36 -0
  26. package/build/logger.js.map +1 -0
  27. package/build/media-type-codecs/any-text-decoder.d.ts +7 -0
  28. package/build/media-type-codecs/any-text-decoder.js +29 -0
  29. package/build/media-type-codecs/any-text-decoder.js.map +1 -0
  30. package/build/media-type-codecs/any-text-encoder.d.ts +6 -0
  31. package/build/media-type-codecs/any-text-encoder.js +27 -0
  32. package/build/media-type-codecs/any-text-encoder.js.map +1 -0
  33. package/build/media-type-codecs/binary-decoder.d.ts +7 -0
  34. package/build/media-type-codecs/binary-decoder.js +33 -0
  35. package/build/media-type-codecs/binary-decoder.js.map +1 -0
  36. package/build/media-type-codecs/binary-encoder.d.ts +6 -0
  37. package/build/media-type-codecs/binary-encoder.js +30 -0
  38. package/build/media-type-codecs/binary-encoder.js.map +1 -0
  39. package/build/media-type-codecs/cbor-decoder.d.ts +13 -0
  40. package/build/media-type-codecs/cbor-decoder.js +38 -0
  41. package/build/media-type-codecs/cbor-decoder.js.map +1 -0
  42. package/build/media-type-codecs/cbor-encoder.d.ts +12 -0
  43. package/build/media-type-codecs/cbor-encoder.js +36 -0
  44. package/build/media-type-codecs/cbor-encoder.js.map +1 -0
  45. package/build/media-type-codecs/cbor-tags.d.ts +6 -0
  46. package/build/media-type-codecs/cbor-tags.js +20 -0
  47. package/build/media-type-codecs/cbor-tags.js.map +1 -0
  48. package/build/media-type-codecs/default-policies.d.ts +3 -0
  49. package/build/media-type-codecs/default-policies.js +33 -0
  50. package/build/media-type-codecs/default-policies.js.map +1 -0
  51. package/build/media-type-codecs/json-decoder.d.ts +14 -0
  52. package/build/media-type-codecs/json-decoder.js +40 -0
  53. package/build/media-type-codecs/json-decoder.js.map +1 -0
  54. package/build/media-type-codecs/json-encoder.d.ts +13 -0
  55. package/build/media-type-codecs/json-encoder.js +43 -0
  56. package/build/media-type-codecs/json-encoder.js.map +1 -0
  57. package/build/media-type-codecs/media-type-decoder.d.ts +14 -0
  58. package/build/media-type-codecs/media-type-decoder.js +18 -0
  59. package/build/media-type-codecs/media-type-decoder.js.map +1 -0
  60. package/build/media-type-codecs/media-type-decoders.d.ts +16 -0
  61. package/build/media-type-codecs/media-type-decoders.js +63 -0
  62. package/build/media-type-codecs/media-type-decoders.js.map +1 -0
  63. package/build/media-type-codecs/media-type-encoder.d.ts +12 -0
  64. package/build/media-type-codecs/media-type-encoder.js +22 -0
  65. package/build/media-type-codecs/media-type-encoder.js.map +1 -0
  66. package/build/media-type-codecs/media-type-encoders.d.ts +16 -0
  67. package/build/media-type-codecs/media-type-encoders.js +65 -0
  68. package/build/media-type-codecs/media-type-encoders.js.map +1 -0
  69. package/build/media-type-codecs/www-form-url-encoder.d.ts +31 -0
  70. package/build/media-type-codecs/www-form-url-encoder.js +281 -0
  71. package/build/media-type-codecs/www-form-url-encoder.js.map +1 -0
  72. package/build/media-type.d.ts +94 -0
  73. package/build/media-type.js +305 -0
  74. package/build/media-type.js.map +1 -0
  75. package/build/problem.d.ts +35 -0
  76. package/build/problem.js +122 -0
  77. package/build/problem.js.map +1 -0
  78. package/build/request-adapters.d.ts +23 -0
  79. package/build/request-adapters.js +53 -0
  80. package/build/request-adapters.js.map +1 -0
  81. package/build/request-factory.d.ts +39 -0
  82. package/build/request-factory.js +15 -0
  83. package/build/request-factory.js.map +1 -0
  84. package/build/result-response.d.ts +4 -0
  85. package/build/result-response.js +15 -0
  86. package/build/result-response.js.map +1 -0
  87. package/build/schema-builtins.d.ts +21 -0
  88. package/build/schema-builtins.js +201 -0
  89. package/build/schema-builtins.js.map +1 -0
  90. package/build/schema-policy.d.ts +31 -0
  91. package/build/schema-policy.js +41 -0
  92. package/build/schema-policy.js.map +1 -0
  93. package/build/schema-runtime.d.ts +25 -0
  94. package/build/schema-runtime.js +77 -0
  95. package/build/schema-runtime.js.map +1 -0
  96. package/build/sunday-error.d.ts +10 -0
  97. package/build/sunday-error.js +36 -0
  98. package/build/sunday-error.js.map +1 -0
  99. package/build/url-template.js +33 -0
  100. package/build/url-template.js.map +1 -0
  101. package/build/util/async-iterables.d.ts +15 -0
  102. package/build/util/async-iterables.js +179 -0
  103. package/build/util/async-iterables.js.map +1 -0
  104. package/build/util/errors.d.ts +2 -0
  105. package/build/util/errors.js +65 -0
  106. package/build/util/errors.js.map +1 -0
  107. package/build/util/nullify.d.ts +5 -0
  108. package/build/util/nullify.js +37 -0
  109. package/build/util/nullify.js.map +1 -0
  110. package/build/util/numbers.d.ts +11 -0
  111. package/build/util/numbers.js +41 -0
  112. package/build/util/numbers.js.map +1 -0
  113. package/build/util/subscriptions.d.ts +16 -0
  114. package/build/util/subscriptions.js +78 -0
  115. package/build/util/subscriptions.js.map +1 -0
  116. package/build/util/unknowns.d.ts +2 -0
  117. package/build/util/unknowns.js +23 -0
  118. package/build/util/unknowns.js.map +1 -0
  119. package/package.json +72 -49
  120. package/dist/any-type.d.ts +0 -2
  121. package/dist/any-type.js +0 -2
  122. package/dist/any-type.js.map +0 -1
  123. package/dist/binary-decoder.d.ts +0 -6
  124. package/dist/binary-decoder.js +0 -16
  125. package/dist/binary-decoder.js.map +0 -1
  126. package/dist/binary-encoder.d.ts +0 -5
  127. package/dist/binary-encoder.js +0 -13
  128. package/dist/binary-encoder.js.map +0 -1
  129. package/dist/cbor-decoder.d.ts +0 -15
  130. package/dist/cbor-decoder.js +0 -126
  131. package/dist/cbor-decoder.js.map +0 -1
  132. package/dist/cbor-encoder.d.ts +0 -29
  133. package/dist/cbor-encoder.js +0 -81
  134. package/dist/cbor-encoder.js.map +0 -1
  135. package/dist/cbor-tags.d.ts +0 -3
  136. package/dist/cbor-tags.js +0 -4
  137. package/dist/cbor-tags.js.map +0 -1
  138. package/dist/class-type.d.ts +0 -2
  139. package/dist/class-type.js +0 -2
  140. package/dist/class-type.js.map +0 -1
  141. package/dist/date-time-types.d.ts +0 -7
  142. package/dist/date-time-types.js +0 -5
  143. package/dist/date-time-types.js.map +0 -1
  144. package/dist/fetch-event-source.d.ts +0 -52
  145. package/dist/fetch-event-source.js +0 -271
  146. package/dist/fetch-event-source.js.map +0 -1
  147. package/dist/fetch-request-factory.d.ts +0 -26
  148. package/dist/fetch-request-factory.js +0 -125
  149. package/dist/fetch-request-factory.js.map +0 -1
  150. package/dist/fetch.d.ts +0 -1
  151. package/dist/fetch.js +0 -19
  152. package/dist/fetch.js.map +0 -1
  153. package/dist/http-error.d.ts +0 -10
  154. package/dist/http-error.js +0 -45
  155. package/dist/http-error.js.map +0 -1
  156. package/dist/index.d.ts +0 -25
  157. package/dist/index.js +0 -26
  158. package/dist/index.js.map +0 -1
  159. package/dist/json-decoder.d.ts +0 -31
  160. package/dist/json-decoder.js +0 -139
  161. package/dist/json-decoder.js.map +0 -1
  162. package/dist/json-encoder.d.ts +0 -35
  163. package/dist/json-encoder.js +0 -134
  164. package/dist/json-encoder.js.map +0 -1
  165. package/dist/logger.js +0 -2
  166. package/dist/logger.js.map +0 -1
  167. package/dist/media-type-decoder.d.ts +0 -4
  168. package/dist/media-type-decoder.js +0 -2
  169. package/dist/media-type-decoder.js.map +0 -1
  170. package/dist/media-type-decoders.d.ts +0 -17
  171. package/dist/media-type-decoders.js +0 -40
  172. package/dist/media-type-decoders.js.map +0 -1
  173. package/dist/media-type-encoder.d.ts +0 -8
  174. package/dist/media-type-encoder.js +0 -6
  175. package/dist/media-type-encoder.js.map +0 -1
  176. package/dist/media-type-encoders.d.ts +0 -17
  177. package/dist/media-type-encoders.js +0 -42
  178. package/dist/media-type-encoders.js.map +0 -1
  179. package/dist/media-type.d.ts +0 -14
  180. package/dist/media-type.js +0 -18
  181. package/dist/media-type.js.map +0 -1
  182. package/dist/problem.d.ts +0 -16
  183. package/dist/problem.js +0 -11
  184. package/dist/problem.js.map +0 -1
  185. package/dist/request-factory.d.ts +0 -38
  186. package/dist/request-factory.js +0 -2
  187. package/dist/request-factory.js.map +0 -1
  188. package/dist/url-encoder.d.ts +0 -59
  189. package/dist/url-encoder.js +0 -141
  190. package/dist/url-encoder.js.map +0 -1
  191. package/dist/url-template.js +0 -18
  192. package/dist/url-template.js.map +0 -1
  193. package/dist/util/base64.d.ts +0 -5
  194. package/dist/util/base64.js +0 -64
  195. package/dist/util/base64.js.map +0 -1
  196. package/dist/util/hex.d.ts +0 -4
  197. package/dist/util/hex.js +0 -18
  198. package/dist/util/hex.js.map +0 -1
  199. package/dist/util/rxjs.d.ts +0 -2
  200. package/dist/util/rxjs.js +0 -14
  201. package/dist/util/rxjs.js.map +0 -1
  202. package/dist/util/stream-rxjs.d.ts +0 -2
  203. package/dist/util/stream-rxjs.js +0 -26
  204. package/dist/util/stream-rxjs.js.map +0 -1
  205. package/src/any-type.ts +0 -4
  206. package/src/binary-decoder.ts +0 -24
  207. package/src/binary-encoder.ts +0 -19
  208. package/src/cbor-decoder.ts +0 -148
  209. package/src/cbor-encoder.ts +0 -95
  210. package/src/cbor-tags.ts +0 -3
  211. package/src/class-type.ts +0 -3
  212. package/src/date-time-types.ts +0 -10
  213. package/src/fetch-event-source.ts +0 -387
  214. package/src/fetch-request-factory.ts +0 -225
  215. package/src/fetch.ts +0 -30
  216. package/src/http-error.ts +0 -55
  217. package/src/index.ts +0 -26
  218. package/src/json-decoder.ts +0 -164
  219. package/src/json-encoder.ts +0 -162
  220. package/src/logger.ts +0 -8
  221. package/src/media-type-decoder.ts +0 -5
  222. package/src/media-type-decoders.ts +0 -59
  223. package/src/media-type-encoder.ts +0 -16
  224. package/src/media-type-encoders.ts +0 -61
  225. package/src/media-type.ts +0 -25
  226. package/src/problem.ts +0 -25
  227. package/src/request-factory.ts +0 -76
  228. package/src/url-encoder.ts +0 -173
  229. package/src/url-template.ts +0 -21
  230. package/src/util/base64.ts +0 -77
  231. package/src/util/hex.ts +0 -15
  232. package/src/util/rxjs.ts +0 -18
  233. package/src/util/stream-rxjs.ts +0 -28
  234. /package/{dist → build}/url-template.d.ts +0 -0
@@ -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
@@ -1,3 +0,0 @@
1
- export const isoDateTag = 0;
2
- export const epochDateTag = 1;
3
- export const uriTag = 32;
package/src/class-type.ts DELETED
@@ -1,3 +0,0 @@
1
- import { ClassType as CT } from '@outfoxx/jackson-js/dist/@types';
2
-
3
- export type ClassType<T> = CT<T>;
@@ -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
- }