@nymphjs/client 1.0.0-beta.13 → 1.0.0-beta.131

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 (73) hide show
  1. package/CHANGELOG.md +552 -0
  2. package/README.md +12 -8
  3. package/asyncitertest.js +53 -0
  4. package/dist/Entity.d.ts +158 -0
  5. package/dist/Entity.js +643 -0
  6. package/dist/Entity.js.map +1 -0
  7. package/dist/Entity.types.d.ts +216 -0
  8. package/dist/Entity.types.js +2 -0
  9. package/{lib → dist}/EntityWeakCache.d.ts +1 -1
  10. package/{lib → dist}/EntityWeakCache.js +5 -9
  11. package/dist/EntityWeakCache.js.map +1 -0
  12. package/{lib → dist}/HttpRequester.d.ts +18 -4
  13. package/dist/HttpRequester.js +365 -0
  14. package/dist/HttpRequester.js.map +1 -0
  15. package/{lib → dist}/Nymph.d.ts +48 -11
  16. package/{lib → dist}/Nymph.js +172 -56
  17. package/dist/Nymph.js.map +1 -0
  18. package/dist/Nymph.types.d.ts +227 -0
  19. package/dist/Nymph.types.js +45 -0
  20. package/dist/Nymph.types.js.map +1 -0
  21. package/{lib → dist}/PubSub.d.ts +16 -10
  22. package/{lib → dist}/PubSub.js +207 -135
  23. package/dist/PubSub.js.map +1 -0
  24. package/{lib → dist}/PubSub.types.d.ts +19 -6
  25. package/dist/PubSub.types.js +2 -0
  26. package/{lib → dist}/entityRefresh.d.ts +1 -1
  27. package/{lib → dist}/entityRefresh.js +21 -13
  28. package/dist/entityRefresh.js.map +1 -0
  29. package/dist/index.d.ts +13 -0
  30. package/dist/index.js +13 -2
  31. package/dist/index.js.map +1 -1
  32. package/{lib → dist}/utils.d.ts +2 -1
  33. package/dist/utils.js +145 -0
  34. package/dist/utils.js.map +1 -0
  35. package/jest.config.js +31 -5
  36. package/package.json +36 -32
  37. package/src/Entity.ts +361 -247
  38. package/src/Entity.types.ts +37 -57
  39. package/src/EntityWeakCache.ts +8 -6
  40. package/src/HttpRequester.ts +268 -31
  41. package/src/Nymph.ts +203 -96
  42. package/src/Nymph.types.ts +98 -2
  43. package/src/PubSub.ts +264 -181
  44. package/src/PubSub.types.ts +33 -19
  45. package/src/entityRefresh.ts +6 -6
  46. package/src/index.ts +10 -10
  47. package/src/utils.ts +67 -14
  48. package/tsconfig.json +7 -4
  49. package/typedoc.json +4 -0
  50. package/dist/index.js.LICENSE.txt +0 -8
  51. package/lib/Entity.d.ts +0 -51
  52. package/lib/Entity.js +0 -500
  53. package/lib/Entity.js.map +0 -1
  54. package/lib/Entity.types.d.ts +0 -65
  55. package/lib/Entity.types.js +0 -3
  56. package/lib/EntityWeakCache.js.map +0 -1
  57. package/lib/HttpRequester.js +0 -190
  58. package/lib/HttpRequester.js.map +0 -1
  59. package/lib/Nymph.js.map +0 -1
  60. package/lib/Nymph.types.d.ts +0 -98
  61. package/lib/Nymph.types.js +0 -3
  62. package/lib/Nymph.types.js.map +0 -1
  63. package/lib/PubSub.js.map +0 -1
  64. package/lib/PubSub.types.js +0 -3
  65. package/lib/entityRefresh.js.map +0 -1
  66. package/lib/index.d.ts +0 -13
  67. package/lib/index.js +0 -34
  68. package/lib/index.js.map +0 -1
  69. package/lib/utils.js +0 -86
  70. package/lib/utils.js.map +0 -1
  71. package/webpack.config.js +0 -28
  72. /package/{lib → dist}/Entity.types.js.map +0 -0
  73. /package/{lib → dist}/PubSub.types.js.map +0 -0
@@ -1,4 +1,5 @@
1
- import type Nymph from './Nymph';
1
+ import type Nymph from './Nymph.js';
2
+ import type Entity from './Entity.js';
2
3
 
3
4
  export type ServerCallResponse = {
4
5
  return: any;
@@ -56,9 +57,9 @@ export interface DataObjectInterface {
56
57
  /**
57
58
  * Delete the object from storage.
58
59
  *
59
- * @returns True on success, false on failure.
60
+ * An error is thrown on failure.
60
61
  */
61
- $delete(): Promise<boolean>;
62
+ $delete(): Promise<void>;
62
63
  /**
63
64
  * Perform a more strict comparison of this object to another.
64
65
  *
@@ -87,24 +88,22 @@ export interface DataObjectInterface {
87
88
  /**
88
89
  * Save the object's dirty data to storage.
89
90
  *
90
- * @returns True on success, false on failure.
91
+ * An error is thrown on failure.
91
92
  */
92
- $patch(): Promise<boolean>;
93
+ $patch(): Promise<void>;
93
94
  /**
94
95
  * Refresh the object from storage. (Bypasses Nymph's cache.)
95
96
  *
96
- * If the object has been deleted from storage, the database cannot be
97
- * reached, or a database error occurs, `refresh()` will return 0.
98
- *
99
- * @returns False if the data has not been saved, 0 if it can't be refreshed, true on success.
97
+ * @returns False if the data has not been saved, true on success. An error is
98
+ * thrown on failure.
100
99
  */
101
100
  $refresh(): Promise<boolean | 0>;
102
101
  /**
103
102
  * Save the object to storage.
104
103
  *
105
- * @returns True on success, false on failure.
104
+ * An error is thrown on failure.
106
105
  */
107
- $save(): Promise<boolean>;
106
+ $save(): Promise<void>;
108
107
  /**
109
108
  * The object's data.
110
109
  */
@@ -155,6 +154,24 @@ export interface EntityInterface extends DataObjectInterface {
155
154
  * @returns True or false.
156
155
  */
157
156
  $hasTag(...tags: string[]): boolean;
157
+ /**
158
+ * Check whether a property is dirty.
159
+ *
160
+ * To be a dirty property, it must have been set or deleted since the entity
161
+ * was initialized. A clean property existed on initialization and hasn't been
162
+ * set or deleted. An untracked property didn't exist on initialization and
163
+ * hasn't been set or deleted.
164
+ *
165
+ * Note that this doesn't necessarily mean the property has changed. It could
166
+ * have been set to the same value, or created and then deleted.
167
+ *
168
+ * Entities are initialized when they are pulled from the server or saved.
169
+ * This is done with the `$init` method.
170
+ *
171
+ * @param property The name of a property.
172
+ * @returns True if it's dirty, false if not, and null if it's not tracked.
173
+ */
174
+ $isDirty(property: string): boolean | null;
158
175
  /**
159
176
  * Initialize this entity from a JSON representation.
160
177
  *
@@ -166,14 +183,14 @@ export interface EntityInterface extends DataObjectInterface {
166
183
  *
167
184
  * @returns The entity.
168
185
  */
169
- $ready(): Promise<EntityInterface>;
186
+ $wake(): Promise<EntityInterface>;
170
187
  /**
171
188
  * Ready this entity's data, and the data of entity's within this one's.
172
189
  *
173
- * @param level The number of levels deep to ready. If undefined, it will keep going until there are no more entities. (Careful of infinite loops.)
190
+ * @param level The number of levels deep to wake. If undefined, it will keep going until there are no more entities. (Careful of infinite loops.)
174
191
  * @returns The entity.
175
192
  */
176
- $readyAll(level?: number): Promise<EntityInterface>;
193
+ $wakeAll(level?: number): Promise<EntityInterface>;
177
194
  /**
178
195
  * Remove one or more tags.
179
196
  *
@@ -195,7 +212,7 @@ export interface EntityInterface extends DataObjectInterface {
195
212
  $serverCall(
196
213
  method: string,
197
214
  params: Iterable<any>,
198
- stateless: boolean
215
+ stateless: boolean,
199
216
  ): Promise<any>;
200
217
  /**
201
218
  * Return a Nymph Entity Reference for this entity.
@@ -208,46 +225,9 @@ export interface EntityInterface extends DataObjectInterface {
208
225
  $toReference(): EntityReference | EntityInterface;
209
226
  }
210
227
 
211
- export type EntityConstructor = (new (...args: any[]) => EntityInterface) & {
212
- /**
213
- * The instance of Nymph to use for queries.
214
- */
215
- nymph: Nymph;
216
- /**
217
- * The lookup name for this entity.
218
- *
219
- * This is used for reference arrays (and sleeping references) and client
220
- * requests.
221
- */
222
- class: string;
223
- /**
224
- * Create a new entity instance.
225
- *
226
- * @param guid An optional GUID to retrieve.
227
- */
228
- factory(guid?: string): Promise<EntityInterface>;
229
- /**
230
- * Create a new entity instance.
231
- *
232
- * @param guid An optional GUID to retrieve.
233
- */
234
- factorySync(guid?: string): EntityInterface;
235
- /**
236
- * Create a new sleeping reference instance.
237
- *
238
- * Sleeping references won't retrieve their data from the server until they
239
- * are readied with `ready()` or a parent's `readyAll()`.
240
- *
241
- * @param reference The Nymph Entity Reference to use to wake.
242
- * @returns The new instance.
243
- */
244
- factoryReference(reference: EntityReference): EntityInterface;
245
- /**
246
- * Call a static method on the server version of this entity.
247
- *
248
- * @param method The name of the method.
249
- * @param params The parameters to call the method with.
250
- * @returns The value that the method on the server returned.
251
- */
252
- serverCallStatic(method: string, params: Iterable<any>): Promise<any>;
228
+ export type EntityConstructor<
229
+ D extends EntityData = EntityData,
230
+ E extends Entity<D> = Entity<D>,
231
+ > = (new (...args: any[]) => E) & {
232
+ [k in keyof typeof Entity]: (typeof Entity)[k];
253
233
  };
@@ -1,15 +1,18 @@
1
- import { EntityConstructor, EntityInterface } from './Entity.types';
1
+ import { EntityConstructor, EntityInterface } from './Entity.types.js';
2
2
 
3
3
  export default class EntityWeakCache {
4
- private references: WeakMap<EntityConstructor, { [k: string]: any }> =
5
- new WeakMap();
4
+ private references: WeakMap<
5
+ EntityConstructor,
6
+ { [k: string]: WeakRef<EntityInterface> }
7
+ > = new WeakMap();
6
8
 
7
9
  get(EntityClass: EntityConstructor, guid: string): EntityInterface | null {
8
10
  const classMap = this.references.get(EntityClass);
9
11
  if (classMap && guid in classMap) {
10
12
  const weakRef = classMap[guid];
11
- if (weakRef && weakRef.deref() != null) {
12
- return weakRef.deref();
13
+ const deref = weakRef && weakRef.deref();
14
+ if (deref != null) {
15
+ return deref;
13
16
  } else {
14
17
  delete classMap[guid];
15
18
  }
@@ -22,7 +25,6 @@ export default class EntityWeakCache {
22
25
  return;
23
26
  }
24
27
 
25
- // @ts-ignore TS doesn't know about WeakRef.
26
28
  const weakRef = new WeakRef(entity);
27
29
 
28
30
  const classMap = this.references.get(EntityClass) || {};
@@ -1,25 +1,42 @@
1
+ import {
2
+ fetchEventSource,
3
+ EventStreamContentType,
4
+ } from 'fetch-event-source-hperrin';
5
+
1
6
  export type HttpRequesterEventType = 'request' | 'response';
2
7
  export type HttpRequesterRequestCallback = (
3
8
  requester: HttpRequester,
4
9
  url: string,
5
- options: RequestInit
10
+ options: RequestInit,
6
11
  ) => void;
7
12
  export type HttpRequesterResponseCallback = (
8
13
  requester: HttpRequester,
9
14
  response: Response,
10
- text: string
15
+ text: string,
16
+ ) => void;
17
+ export type HttpRequesterIteratorCallback = (
18
+ requester: HttpRequester,
19
+ url: string,
20
+ headers: Record<string, string>,
11
21
  ) => void;
12
22
  export type HttpRequesterRequestOptions = {
13
23
  url: string;
24
+ headers?: { [k: string]: any };
14
25
  data: { [k: string]: any };
15
26
  dataType: string;
16
27
  };
17
28
 
29
+ export interface AbortableAsyncIterator<
30
+ T extends any = any,
31
+ > extends AsyncIterable<T> {
32
+ abortController: AbortController;
33
+ }
34
+
18
35
  export default class HttpRequester {
19
36
  private fetch: WindowOrWorkerGlobalScope['fetch'];
20
- private xsrfToken: string | null = null;
21
37
  private requestCallbacks: HttpRequesterRequestCallback[] = [];
22
38
  private responseCallbacks: HttpRequesterResponseCallback[] = [];
39
+ private iteratorCallbacks: HttpRequesterIteratorCallback[] = [];
23
40
 
24
41
  static makeUrl(url: string, data: { [k: string]: any }) {
25
42
  if (!data) {
@@ -45,14 +62,18 @@ export default class HttpRequester {
45
62
  callback: T extends 'request'
46
63
  ? HttpRequesterRequestCallback
47
64
  : T extends 'response'
48
- ? HttpRequesterResponseCallback
49
- : never
65
+ ? HttpRequesterResponseCallback
66
+ : T extends 'iterator'
67
+ ? HttpRequesterIteratorCallback
68
+ : never,
50
69
  ) {
51
70
  const prop = (event + 'Callbacks') as T extends 'request'
52
71
  ? 'requestCallbacks'
53
72
  : T extends 'response'
54
- ? 'responseCallbacks'
55
- : never;
73
+ ? 'responseCallbacks'
74
+ : T extends 'iterator'
75
+ ? 'iteratorCallbacks'
76
+ : never;
56
77
  if (!(prop in this)) {
57
78
  throw new Error('Invalid event type.');
58
79
  }
@@ -66,14 +87,18 @@ export default class HttpRequester {
66
87
  callback: T extends 'request'
67
88
  ? HttpRequesterRequestCallback
68
89
  : T extends 'response'
69
- ? HttpRequesterResponseCallback
70
- : never
90
+ ? HttpRequesterResponseCallback
91
+ : T extends 'iterator'
92
+ ? HttpRequesterIteratorCallback
93
+ : never,
71
94
  ) {
72
95
  const prop = (event + 'Callbacks') as T extends 'request'
73
96
  ? 'requestCallbacks'
74
97
  : T extends 'response'
75
- ? 'responseCallbacks'
76
- : never;
98
+ ? 'responseCallbacks'
99
+ : T extends 'iterator'
100
+ ? 'iteratorCallbacks'
101
+ : never;
77
102
  if (!(prop in this)) {
78
103
  return false;
79
104
  }
@@ -86,10 +111,6 @@ export default class HttpRequester {
86
111
  return true;
87
112
  }
88
113
 
89
- setXsrfToken(xsrfToken: string | null) {
90
- this.xsrfToken = xsrfToken;
91
- }
92
-
93
114
  async GET(opt: HttpRequesterRequestOptions) {
94
115
  return await this._httpRequest('GET', opt);
95
116
  }
@@ -98,6 +119,10 @@ export default class HttpRequester {
98
119
  return await this._httpRequest('POST', opt);
99
120
  }
100
121
 
122
+ async POST_ITERATOR(opt: HttpRequesterRequestOptions) {
123
+ return await this._iteratorRequest('POST', opt);
124
+ }
125
+
101
126
  async PUT(opt: HttpRequesterRequestOptions) {
102
127
  return await this._httpRequest('PUT', opt);
103
128
  }
@@ -112,7 +137,7 @@ export default class HttpRequester {
112
137
 
113
138
  async _httpRequest(
114
139
  method: 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE',
115
- opt: HttpRequesterRequestOptions
140
+ opt: HttpRequesterRequestOptions,
116
141
  ) {
117
142
  const dataString = JSON.stringify(opt.data);
118
143
  let url = opt.url;
@@ -123,7 +148,7 @@ export default class HttpRequester {
123
148
  }
124
149
  const options: RequestInit = {
125
150
  method,
126
- headers: {},
151
+ headers: opt.headers ?? {},
127
152
  credentials: 'include',
128
153
  };
129
154
 
@@ -137,18 +162,13 @@ export default class HttpRequester {
137
162
  this.requestCallbacks[i] && this.requestCallbacks[i](this, url, options);
138
163
  }
139
164
 
140
- if (this.xsrfToken !== null) {
141
- (options.headers as Record<string, string>)['X-Xsrf-Token'] =
142
- this.xsrfToken;
143
- }
144
-
145
165
  const response = await this.fetch(url, options);
146
166
  let text: string;
147
167
  try {
148
168
  text = await response.text();
149
169
  } catch (e: any) {
150
170
  throw new InvalidResponseError(
151
- 'Server response did not contain valid text body.'
171
+ 'Server response did not contain valid text body.',
152
172
  );
153
173
  }
154
174
  if (!response.ok) {
@@ -170,12 +190,12 @@ export default class HttpRequester {
170
190
  throw response.status < 200
171
191
  ? new InformationalError(response, errObj)
172
192
  : response.status < 300
173
- ? new SuccessError(response, errObj)
174
- : response.status < 400
175
- ? new RedirectError(response, errObj)
176
- : response.status < 500
177
- ? new ClientError(response, errObj)
178
- : new ServerError(response, errObj);
193
+ ? new SuccessError(response, errObj)
194
+ : response.status < 400
195
+ ? new RedirectError(response, errObj)
196
+ : response.status < 500
197
+ ? new ClientError(response, errObj)
198
+ : new ServerError(response, errObj);
179
199
  }
180
200
  for (let i = 0; i < this.responseCallbacks.length; i++) {
181
201
  this.responseCallbacks[i] &&
@@ -192,13 +212,216 @@ export default class HttpRequester {
192
212
  throw e;
193
213
  }
194
214
  throw new InvalidResponseError(
195
- 'Server response was invalid: ' + JSON.stringify(text)
215
+ 'Server response was invalid: ' + JSON.stringify(text),
196
216
  );
197
217
  }
198
218
  } else {
199
219
  return text;
200
220
  }
201
221
  }
222
+
223
+ async _iteratorRequest(
224
+ method: 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE',
225
+ opt: HttpRequesterRequestOptions,
226
+ ): Promise<AbortableAsyncIterator> {
227
+ const dataString = JSON.stringify(opt.data);
228
+ let url = opt.url;
229
+ if (method === 'GET') {
230
+ // TODO: what should this size be?
231
+ // && dataString.length < 1) {
232
+ url = HttpRequester.makeUrl(opt.url, opt.data);
233
+ }
234
+ const hasBody = method !== 'GET' && opt.data;
235
+ const headers: Record<string, string> = opt.headers ?? {};
236
+
237
+ if (hasBody) {
238
+ headers['Content-Type'] = 'application/json';
239
+ }
240
+
241
+ for (let i = 0; i < this.iteratorCallbacks.length; i++) {
242
+ this.iteratorCallbacks[i] &&
243
+ this.iteratorCallbacks[i](this, url, headers);
244
+ }
245
+
246
+ const responses: any[] = [];
247
+ let nextResponseResolve: (value: void) => void;
248
+ let nextResponseReadyPromise = new Promise<void>((res) => {
249
+ nextResponseResolve = res;
250
+ });
251
+ let responsesDone = false;
252
+ let serverResponse: Response;
253
+
254
+ const ctrl = new AbortController();
255
+
256
+ fetchEventSource(url, {
257
+ openWhenHidden: true,
258
+ fetch: this.fetch,
259
+
260
+ method,
261
+ headers,
262
+ credentials: 'include',
263
+ body: hasBody ? dataString : undefined,
264
+ signal: ctrl.signal,
265
+
266
+ async onopen(response) {
267
+ serverResponse = response;
268
+ if (response.ok) {
269
+ if (response.headers.get('content-type') === EventStreamContentType) {
270
+ throw new InvalidResponseError(
271
+ 'Server response is not an event stream.',
272
+ );
273
+ }
274
+
275
+ // Response is ok, wait for messages.
276
+ return;
277
+ }
278
+
279
+ let text: string = '';
280
+ try {
281
+ text = await response.text();
282
+ } catch (e: any) {
283
+ // Ignore error here.
284
+ }
285
+
286
+ let errObj;
287
+ try {
288
+ errObj = JSON.parse(text);
289
+ } catch (e: any) {
290
+ if (!(e instanceof SyntaxError)) {
291
+ throw e;
292
+ }
293
+ }
294
+
295
+ if (typeof errObj !== 'object') {
296
+ errObj = {
297
+ textStatus: response.statusText,
298
+ };
299
+ }
300
+ errObj.status = response.status;
301
+ throw response.status < 200
302
+ ? new InformationalError(response, errObj)
303
+ : response.status < 300
304
+ ? new SuccessError(response, errObj)
305
+ : response.status < 400
306
+ ? new RedirectError(response, errObj)
307
+ : response.status < 500
308
+ ? new ClientError(response, errObj)
309
+ : new ServerError(response, errObj);
310
+ },
311
+
312
+ onmessage(event) {
313
+ if (event.event === 'next') {
314
+ let text = event.data;
315
+
316
+ if (opt.dataType === 'json') {
317
+ if (!text.length) {
318
+ responses.push(
319
+ new InvalidResponseError('Server response was empty.'),
320
+ );
321
+ } else {
322
+ try {
323
+ responses.push(JSON.parse(text));
324
+ } catch (e: any) {
325
+ if (!(e instanceof SyntaxError)) {
326
+ responses.push(e);
327
+ } else {
328
+ responses.push(
329
+ new InvalidResponseError(
330
+ 'Server response was invalid: ' + JSON.stringify(text),
331
+ ),
332
+ );
333
+ }
334
+ }
335
+ }
336
+ } else {
337
+ responses.push(text);
338
+ }
339
+ } else if (event.event === 'error') {
340
+ let text = event.data;
341
+
342
+ let errObj;
343
+ try {
344
+ errObj = JSON.parse(text);
345
+ } catch (e: any) {
346
+ if (!(e instanceof SyntaxError)) {
347
+ throw e;
348
+ }
349
+ }
350
+
351
+ if (typeof errObj !== 'object') {
352
+ errObj = {
353
+ status: 500,
354
+ textStatus: 'Iterator Error',
355
+ };
356
+ }
357
+ responses.push(
358
+ errObj.status < 200
359
+ ? new InformationalError(serverResponse, errObj)
360
+ : errObj.status < 300
361
+ ? new SuccessError(serverResponse, errObj)
362
+ : errObj.status < 400
363
+ ? new RedirectError(serverResponse, errObj)
364
+ : errObj.status < 500
365
+ ? new ClientError(serverResponse, errObj)
366
+ : new ServerError(serverResponse, errObj),
367
+ );
368
+ } else if (event.event === 'finished') {
369
+ responsesDone = true;
370
+ } else if (event.event === 'ping') {
371
+ // Ignore keep-alive pings.
372
+ return;
373
+ }
374
+
375
+ const resolve = nextResponseResolve;
376
+ if (!responsesDone) {
377
+ nextResponseReadyPromise = new Promise<void>((res) => {
378
+ nextResponseResolve = res;
379
+ });
380
+ }
381
+
382
+ // Resolve the promise to continue any waiting iterator.
383
+ resolve();
384
+ },
385
+
386
+ onclose() {
387
+ responses.push(
388
+ new ConnectionClosedUnexpectedlyError(
389
+ 'The connection to the server was closed unexpectedly.',
390
+ ),
391
+ );
392
+
393
+ responsesDone = true;
394
+ nextResponseResolve();
395
+ },
396
+
397
+ onerror(err) {
398
+ // Rethrow to stop the operation.
399
+ throw err;
400
+ },
401
+ }).catch((err) => {
402
+ responses.push(
403
+ new ConnectionError('The connection could not be established: ' + err),
404
+ );
405
+
406
+ responsesDone = true;
407
+ nextResponseResolve();
408
+ });
409
+
410
+ const iterator: AbortableAsyncIterator = {
411
+ abortController: ctrl,
412
+ async *[Symbol.asyncIterator]() {
413
+ do {
414
+ await nextResponseReadyPromise;
415
+
416
+ while (responses.length) {
417
+ yield responses.shift();
418
+ }
419
+ } while (!responsesDone);
420
+ },
421
+ };
422
+
423
+ return iterator;
424
+ }
202
425
  }
203
426
 
204
427
  export class InvalidResponseError extends Error {
@@ -208,6 +431,20 @@ export class InvalidResponseError extends Error {
208
431
  }
209
432
  }
210
433
 
434
+ export class ConnectionClosedUnexpectedlyError extends Error {
435
+ constructor(message: string) {
436
+ super(message);
437
+ this.name = 'ConnectionClosedUnexpectedlyError';
438
+ }
439
+ }
440
+
441
+ export class ConnectionError extends Error {
442
+ constructor(message: string) {
443
+ super(message);
444
+ this.name = 'ConnectionError';
445
+ }
446
+ }
447
+
211
448
  export class HttpError extends Error {
212
449
  status: number;
213
450
  statusText: string;
@@ -215,7 +452,7 @@ export class HttpError extends Error {
215
452
  constructor(
216
453
  name: string,
217
454
  response: Response,
218
- errObj: { textStatus: string }
455
+ errObj: { textStatus: string },
219
456
  ) {
220
457
  super(errObj.textStatus);
221
458
  this.name = name;