@mfxhub/sdk 0.2.1 → 0.2.2

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/CHANGELOG.md CHANGED
@@ -4,6 +4,14 @@ Every release of `@mfxhub/sdk`, newest first. The format follows
4
4
  [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and versions follow
5
5
  [Semantic Versioning](https://semver.org/).
6
6
 
7
+ ## 0.2.2
8
+
9
+ ### Fixed
10
+
11
+ - `stream.close()` ends the stream for good. A `next()` or a `for await` loop waiting for a message
12
+ ends with nothing, and a reconnect under way is given up. Before, closing while a read waited
13
+ made the stream reconnect and keep delivering, so a script that closed its stream did not end.
14
+
7
15
  ## 0.2.1
8
16
 
9
17
  ### Added
package/dist/stream.d.ts CHANGED
@@ -127,7 +127,10 @@ export declare class MfxStream implements AsyncIterable<StreamEvent> {
127
127
  resume(channel: string): Promise<void>;
128
128
  /** Stop watching a channel, and stop subscribing to it again after a reconnect. */
129
129
  unsubscribe(channel: string): Promise<void>;
130
- /** Close the connection, if one is open. */
130
+ /**
131
+ * Close the stream for good. A read waiting on it ends with null, a reconnect under way is
132
+ * given up, and the stream is never opened again.
133
+ */
131
134
  close(): Promise<void>;
132
135
  [Symbol.asyncIterator](): AsyncGenerator<StreamEvent, void, undefined>;
133
136
  /**
package/dist/stream.js CHANGED
@@ -208,6 +208,8 @@ export class MfxStream {
208
208
  refused: 0,
209
209
  };
210
210
  #connection = null;
211
+ /** The caller closed the stream: nothing reconnects it, and every read is null from here on. */
212
+ #closed = false;
211
213
  /** Use {@link MfxClient.stream}. */
212
214
  constructor(client, options = {}) {
213
215
  this.#client = client;
@@ -258,8 +260,12 @@ export class MfxStream {
258
260
  this.#channels.delete(channel);
259
261
  await this.#send({ op: "unsubscribe", ch: channel });
260
262
  }
261
- /** Close the connection, if one is open. */
263
+ /**
264
+ * Close the stream for good. A read waiting on it ends with null, a reconnect under way is
265
+ * given up, and the stream is never opened again.
266
+ */
262
267
  async close() {
268
+ this.#closed = true;
263
269
  const connection = this.#connection;
264
270
  this.#connection = null;
265
271
  if (connection !== null)
@@ -287,6 +293,9 @@ export class MfxStream {
287
293
  raw = await connection.recv();
288
294
  }
289
295
  catch (error) {
296
+ // The caller's own close ends the read; it is not a drop to come back from.
297
+ if (this.#closed)
298
+ return null;
290
299
  if (!(error instanceof StreamClosed))
291
300
  throw error;
292
301
  if (!(await this.#reopen(error)))
@@ -362,7 +371,14 @@ export class MfxStream {
362
371
  this.#bump("reconnects");
363
372
  // The client's own backoff, so the stream and the reads lean on this tenant's quota alike.
364
373
  await this.#client.pause(this.#client.backoffMs(this.#stats.reconnects));
374
+ if (this.#closedNow())
375
+ return false; // closed while waiting out the backoff
365
376
  await this.open();
377
+ if (this.#closedNow()) {
378
+ // closed while the connection was being opened: that one is closed too
379
+ await this.close();
380
+ return false;
381
+ }
366
382
  for (const [channel, last] of [...this.#channels]) {
367
383
  if (replayable(channel) && last !== null) {
368
384
  await this.#send({ op: "resume", ch: channel, after_seq: last });
@@ -376,6 +392,10 @@ export class MfxStream {
376
392
  }
377
393
  return true;
378
394
  }
395
+ /** Read afresh after an await: close() may have run while this was waiting. */
396
+ #closedNow() {
397
+ return this.#closed;
398
+ }
379
399
  async #send(message) {
380
400
  const connection = this.#connection;
381
401
  if (connection === null)
package/dist/version.d.ts CHANGED
@@ -1,4 +1,4 @@
1
1
  /** This package's version. */
2
- export declare const VERSION = "0.2.1";
2
+ export declare const VERSION = "0.2.2";
3
3
  /** What every request says it is, as a product token: the SDK's language and its version. */
4
- export declare const USER_AGENT = "mfx-sdk-typescript/0.2.1";
4
+ export declare const USER_AGENT = "mfx-sdk-typescript/0.2.2";
package/dist/version.js CHANGED
@@ -1,5 +1,5 @@
1
1
  // This package's version, in one place: exported by the package, and named by every request.
2
2
  /** This package's version. */
3
- export const VERSION = "0.2.1";
3
+ export const VERSION = "0.2.2";
4
4
  /** What every request says it is, as a product token: the SDK's language and its version. */
5
5
  export const USER_AGENT = `mfx-sdk-typescript/${VERSION}`;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mfxhub/sdk",
3
- "version": "0.2.1",
3
+ "version": "0.2.2",
4
4
  "description": "Typed TypeScript client for the MFX Hub API: hosted trading terminals, market data, execution and a live event stream",
5
5
  "license": "Apache-2.0",
6
6
  "type": "module",