starknet 10.6.7 → 10.6.8

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/dist/index.js CHANGED
@@ -7476,6 +7476,8 @@ var Subscription = class {
7476
7476
  maxBufferSize;
7477
7477
  handler = null;
7478
7478
  _isClosed = false;
7479
+ // The unsubscribe request currently on the wire, shared by concurrent callers.
7480
+ pendingUnsubscribe = null;
7479
7481
  /**
7480
7482
  * @internal
7481
7483
  * @param options - Subscription configuration options
@@ -7494,6 +7496,20 @@ var Subscription = class {
7494
7496
  get isClosed() {
7495
7497
  return this._isClosed;
7496
7498
  }
7499
+ /**
7500
+ * Closes the subscription locally, without contacting the node.
7501
+ *
7502
+ * Used when the channel knows the subscription is gone and cannot be recovered — a
7503
+ * re-subscribe refused after a reconnection, for instance. Without it the object would keep
7504
+ * reporting itself as open while no event could ever reach its handler again.
7505
+ * @internal
7506
+ */
7507
+ _markClosed() {
7508
+ if (this._isClosed) return;
7509
+ this._isClosed = true;
7510
+ this.events.emit("unsubscribe", void 0);
7511
+ this.events.clear();
7512
+ }
7497
7513
  /**
7498
7514
  * Internal method to handle incoming events from the WebSocket channel.
7499
7515
  * If a handler is attached, it's invoked immediately. Otherwise, the event is buffered.
@@ -7540,14 +7556,24 @@ var Subscription = class {
7540
7556
  if (this._isClosed) {
7541
7557
  return true;
7542
7558
  }
7543
- const success = await this.channel.unsubscribe(this.id);
7544
- if (success) {
7545
- this._isClosed = true;
7546
- this.channel.removeSubscription(this.id);
7547
- this.events.emit("unsubscribe", void 0);
7548
- this.events.clear();
7559
+ if (this.pendingUnsubscribe) {
7560
+ return this.pendingUnsubscribe;
7549
7561
  }
7550
- return success;
7562
+ this.pendingUnsubscribe = (async () => {
7563
+ try {
7564
+ const success = await this.channel.unsubscribe(this.id);
7565
+ if (success) {
7566
+ this._isClosed = true;
7567
+ this.channel.removeSubscription(this.id);
7568
+ this.events.emit("unsubscribe", void 0);
7569
+ this.events.clear();
7570
+ }
7571
+ return success;
7572
+ } finally {
7573
+ this.pendingUnsubscribe = null;
7574
+ }
7575
+ })();
7576
+ return this.pendingUnsubscribe;
7551
7577
  }
7552
7578
  };
7553
7579
 
@@ -7580,6 +7606,15 @@ var WebSocketChannel = class {
7580
7606
  requestQueue = [];
7581
7607
  /** Abort handles for the requests currently on the wire, one per pending `sendReceive`. */
7582
7608
  inFlight = /* @__PURE__ */ new Set();
7609
+ /**
7610
+ * Callers blocked in `waitForUnsubscription`, keyed by subscription id.
7611
+ *
7612
+ * Held here rather than as `unsubscribe` event listeners because that event only ever
7613
+ * announces success: a waiter attached to it cannot learn that the node refused the
7614
+ * unsubscribe or that the connection went away, and would wait forever with no timeout of
7615
+ * its own to fall back on.
7616
+ */
7617
+ unsubscribeWaiters = /* @__PURE__ */ new Map();
7583
7618
  events = new EventEmitter();
7584
7619
  openListener = (ev) => {
7585
7620
  this.scheduleReconnectAttemptsReset();
@@ -7776,6 +7811,7 @@ var WebSocketChannel = class {
7776
7811
  this.isReconnecting = false;
7777
7812
  this._rejectRequestQueue("the connection was closed by the user");
7778
7813
  this._rejectInFlight("the connection was closed by the user");
7814
+ this._rejectUnsubscribeWaiters("the connection was closed by the user");
7779
7815
  this.websocket.close(code, reason);
7780
7816
  }
7781
7817
  /**
@@ -7800,14 +7836,48 @@ var WebSocketChannel = class {
7800
7836
  * @returns {Promise<boolean>} A Promise that resolves with `true` if the unsubscription was successful.
7801
7837
  */
7802
7838
  async unsubscribe(subscriptionId) {
7803
- const status = await this.sendReceive("starknet_unsubscribe", {
7804
- subscription_id: subscriptionId
7805
- });
7839
+ let status;
7840
+ try {
7841
+ status = await this.sendReceive("starknet_unsubscribe", {
7842
+ subscription_id: subscriptionId
7843
+ });
7844
+ } catch (error) {
7845
+ this._settleUnsubscribeWaiters(subscriptionId, error);
7846
+ throw error;
7847
+ }
7806
7848
  if (status) {
7807
7849
  this.events.emit("unsubscribe", subscriptionId);
7850
+ this._settleUnsubscribeWaiters(subscriptionId);
7851
+ } else {
7852
+ this._settleUnsubscribeWaiters(
7853
+ subscriptionId,
7854
+ new Error(`Node refused to unsubscribe subscription ${subscriptionId}`)
7855
+ );
7808
7856
  }
7809
7857
  return status;
7810
7858
  }
7859
+ /** Settles every caller waiting on one subscription id: resolved, or rejected with `error`. */
7860
+ _settleUnsubscribeWaiters(subscriptionId, error) {
7861
+ const waiters = this.unsubscribeWaiters.get(subscriptionId);
7862
+ if (!waiters) return;
7863
+ this.unsubscribeWaiters.delete(subscriptionId);
7864
+ waiters.forEach((waiter) => error ? waiter.reject(error) : waiter.resolve());
7865
+ }
7866
+ /**
7867
+ * Rejects every caller still waiting on any subscription.
7868
+ *
7869
+ * Once the connection is gone, no unsubscribe can be observed on it: a reconnection restores
7870
+ * each subscription under a fresh id, so the id being waited on will never be announced.
7871
+ */
7872
+ _rejectUnsubscribeWaiters(reason) {
7873
+ if (this.unsubscribeWaiters.size === 0) return;
7874
+ Array.from(this.unsubscribeWaiters.keys()).forEach(
7875
+ (id) => this._settleUnsubscribeWaiters(
7876
+ id,
7877
+ new WebSocketNotConnectedError(`Subscription ${id} was never unsubscribed: ${reason}`)
7878
+ )
7879
+ );
7880
+ }
7811
7881
  /**
7812
7882
  * Returns a Promise that resolves when a specific subscription is successfully unsubscribed.
7813
7883
  * @param {SUBSCRIPTION_ID} targetId - The ID of the subscription to wait for.
@@ -7819,14 +7889,10 @@ var WebSocketChannel = class {
7819
7889
  * ```
7820
7890
  */
7821
7891
  waitForUnsubscription(targetId) {
7822
- return new Promise((resolve) => {
7823
- const listener = (unsubId) => {
7824
- if (unsubId === targetId) {
7825
- this.events.off("unsubscribe", listener);
7826
- resolve();
7827
- }
7828
- };
7829
- this.events.on("unsubscribe", listener);
7892
+ return new Promise((resolve, reject) => {
7893
+ const waiters = this.unsubscribeWaiters.get(targetId) ?? /* @__PURE__ */ new Set();
7894
+ waiters.add({ resolve, reject });
7895
+ this.unsubscribeWaiters.set(targetId, waiters);
7830
7896
  });
7831
7897
  }
7832
7898
  /**
@@ -7893,6 +7959,7 @@ var WebSocketChannel = class {
7893
7959
  logger.info(`Subscription ${sub.method} restored with new ID: ${newSubId}`);
7894
7960
  } catch (error) {
7895
7961
  logger.error(`Failed to restore subscription ${sub.method}:`, error);
7962
+ sub._markClosed();
7896
7963
  }
7897
7964
  });
7898
7965
  await Promise.all(restorePromises);
@@ -7962,6 +8029,7 @@ var WebSocketChannel = class {
7962
8029
  this.websocket.removeEventListener("message", this.messageListener);
7963
8030
  this.websocket.removeEventListener("error", this.errorListener);
7964
8031
  this._rejectInFlight("the connection was closed");
8032
+ this._rejectUnsubscribeWaiters("the connection was closed");
7965
8033
  this.events.emit("close", ev);
7966
8034
  if (!this.userInitiatedClose) {
7967
8035
  this._startReconnect();
@@ -7983,9 +8051,16 @@ var WebSocketChannel = class {
7983
8051
  if (subscription) {
7984
8052
  subscription._handleEvent(result);
7985
8053
  } else {
7986
- logger.warn(
7987
- `WebSocketChannel: Received event for untracked subscription ID: ${subscription_id}.`
7988
- );
8054
+ queueMicrotask(() => {
8055
+ const registered = this.activeSubscriptions.get(subscription_id);
8056
+ if (registered) {
8057
+ registered._handleEvent(result);
8058
+ } else {
8059
+ logger.warn(
8060
+ `WebSocketChannel: Received event for untracked subscription ID: ${subscription_id}.`
8061
+ );
8062
+ }
8063
+ });
7989
8064
  }
7990
8065
  }
7991
8066
  logger.debug("onMessageProxy:", event.data);