starknet 10.6.6 → 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/CHANGELOG.md +12 -0
- package/dist/index.d.ts +39 -0
- package/dist/index.global.js +154 -46
- package/dist/index.global.js.map +1 -1
- package/dist/index.js +154 -46
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +154 -46
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
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
|
-
|
|
7544
|
-
|
|
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
|
-
|
|
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
|
|
|
@@ -7578,6 +7604,17 @@ var WebSocketChannel = class {
|
|
|
7578
7604
|
// resetting the reconnection attempt counter.
|
|
7579
7605
|
reconnectStabilityTimeoutId = null;
|
|
7580
7606
|
requestQueue = [];
|
|
7607
|
+
/** Abort handles for the requests currently on the wire, one per pending `sendReceive`. */
|
|
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();
|
|
7581
7618
|
events = new EventEmitter();
|
|
7582
7619
|
openListener = (ev) => {
|
|
7583
7620
|
this.scheduleReconnectAttemptsReset();
|
|
@@ -7661,12 +7698,19 @@ var WebSocketChannel = class {
|
|
|
7661
7698
|
});
|
|
7662
7699
|
}
|
|
7663
7700
|
const sendId = this.send(method, params);
|
|
7701
|
+
const socket = this.websocket;
|
|
7664
7702
|
return new Promise((resolve, reject) => {
|
|
7665
|
-
|
|
7666
|
-
if (!this.websocket || this.websocket.readyState !== ws_default.OPEN) {
|
|
7703
|
+
if (socket.readyState !== ws_default.OPEN) {
|
|
7667
7704
|
reject(new WebSocketNotConnectedError("WebSocket not available or not connected."));
|
|
7668
7705
|
return;
|
|
7669
7706
|
}
|
|
7707
|
+
let timeoutId;
|
|
7708
|
+
const settle = () => {
|
|
7709
|
+
clearTimeout(timeoutId);
|
|
7710
|
+
socket.removeEventListener("message", messageHandler);
|
|
7711
|
+
socket.removeEventListener("error", errorHandler);
|
|
7712
|
+
this.inFlight.delete(abort);
|
|
7713
|
+
};
|
|
7670
7714
|
const messageHandler = (event) => {
|
|
7671
7715
|
if (!isString(event.data)) {
|
|
7672
7716
|
logger.warn("WebSocket received non-string message data:", event.data);
|
|
@@ -7681,34 +7725,35 @@ var WebSocketChannel = class {
|
|
|
7681
7725
|
);
|
|
7682
7726
|
return;
|
|
7683
7727
|
}
|
|
7684
|
-
if (message.id
|
|
7685
|
-
|
|
7686
|
-
|
|
7687
|
-
|
|
7688
|
-
|
|
7689
|
-
|
|
7690
|
-
} else {
|
|
7691
|
-
reject(
|
|
7692
|
-
new Error(`Error on ${method} (id: ${sendId}): ${JSON.stringify(message.error)}`)
|
|
7693
|
-
);
|
|
7694
|
-
}
|
|
7728
|
+
if (message.id !== sendId) return;
|
|
7729
|
+
settle();
|
|
7730
|
+
if ("result" in message) {
|
|
7731
|
+
resolve(message.result);
|
|
7732
|
+
} else {
|
|
7733
|
+
reject(new Error(`Error on ${method} (id: ${sendId}): ${JSON.stringify(message.error)}`));
|
|
7695
7734
|
}
|
|
7696
7735
|
};
|
|
7697
7736
|
const errorHandler = (event) => {
|
|
7698
|
-
|
|
7699
|
-
this.websocket.removeEventListener("message", messageHandler);
|
|
7700
|
-
this.websocket.removeEventListener("error", errorHandler);
|
|
7737
|
+
settle();
|
|
7701
7738
|
reject(
|
|
7702
7739
|
new Error(
|
|
7703
7740
|
`WebSocket error during ${method} (id: ${sendId}): ${event.type || "Unknown error"}`
|
|
7704
7741
|
)
|
|
7705
7742
|
);
|
|
7706
7743
|
};
|
|
7707
|
-
|
|
7708
|
-
|
|
7744
|
+
const abort = (reason) => {
|
|
7745
|
+
settle();
|
|
7746
|
+
reject(
|
|
7747
|
+
new WebSocketNotConnectedError(
|
|
7748
|
+
`Request ${method} (id: ${sendId}) went unanswered: ${reason}`
|
|
7749
|
+
)
|
|
7750
|
+
);
|
|
7751
|
+
};
|
|
7752
|
+
socket.addEventListener("message", messageHandler);
|
|
7753
|
+
socket.addEventListener("error", errorHandler);
|
|
7754
|
+
this.inFlight.add(abort);
|
|
7709
7755
|
timeoutId = setTimeout(() => {
|
|
7710
|
-
|
|
7711
|
-
this.websocket.removeEventListener("error", errorHandler);
|
|
7756
|
+
settle();
|
|
7712
7757
|
reject(
|
|
7713
7758
|
new TimeoutError(
|
|
7714
7759
|
`Request ${method} (id: ${sendId}) timed out after ${this.requestTimeout}ms`
|
|
@@ -7765,6 +7810,8 @@ var WebSocketChannel = class {
|
|
|
7765
7810
|
this.userInitiatedClose = true;
|
|
7766
7811
|
this.isReconnecting = false;
|
|
7767
7812
|
this._rejectRequestQueue("the connection was closed by the user");
|
|
7813
|
+
this._rejectInFlight("the connection was closed by the user");
|
|
7814
|
+
this._rejectUnsubscribeWaiters("the connection was closed by the user");
|
|
7768
7815
|
this.websocket.close(code, reason);
|
|
7769
7816
|
}
|
|
7770
7817
|
/**
|
|
@@ -7789,14 +7836,48 @@ var WebSocketChannel = class {
|
|
|
7789
7836
|
* @returns {Promise<boolean>} A Promise that resolves with `true` if the unsubscription was successful.
|
|
7790
7837
|
*/
|
|
7791
7838
|
async unsubscribe(subscriptionId) {
|
|
7792
|
-
|
|
7793
|
-
|
|
7794
|
-
|
|
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
|
+
}
|
|
7795
7848
|
if (status) {
|
|
7796
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
|
+
);
|
|
7797
7856
|
}
|
|
7798
7857
|
return status;
|
|
7799
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
|
+
}
|
|
7800
7881
|
/**
|
|
7801
7882
|
* Returns a Promise that resolves when a specific subscription is successfully unsubscribed.
|
|
7802
7883
|
* @param {SUBSCRIPTION_ID} targetId - The ID of the subscription to wait for.
|
|
@@ -7808,14 +7889,10 @@ var WebSocketChannel = class {
|
|
|
7808
7889
|
* ```
|
|
7809
7890
|
*/
|
|
7810
7891
|
waitForUnsubscription(targetId) {
|
|
7811
|
-
return new Promise((resolve) => {
|
|
7812
|
-
const
|
|
7813
|
-
|
|
7814
|
-
|
|
7815
|
-
resolve();
|
|
7816
|
-
}
|
|
7817
|
-
};
|
|
7818
|
-
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);
|
|
7819
7896
|
});
|
|
7820
7897
|
}
|
|
7821
7898
|
/**
|
|
@@ -7856,6 +7933,21 @@ var WebSocketChannel = class {
|
|
|
7856
7933
|
reject(new WebSocketNotConnectedError(`Request ${method} was never sent: ${reason}`));
|
|
7857
7934
|
});
|
|
7858
7935
|
}
|
|
7936
|
+
/**
|
|
7937
|
+
* Settle every request already on the wire.
|
|
7938
|
+
*
|
|
7939
|
+
* The counterpart of `_rejectRequestQueue`, for requests past the queue. Their only other
|
|
7940
|
+
* exit is the `requestTimeout` timer, so without this the caller waits the whole timeout —
|
|
7941
|
+
* 60s by default — for a reply that can no longer arrive, and that pending timer keeps the
|
|
7942
|
+
* Node event loop alive for just as long.
|
|
7943
|
+
*/
|
|
7944
|
+
_rejectInFlight(reason) {
|
|
7945
|
+
if (this.inFlight.size === 0) return;
|
|
7946
|
+
const pending = Array.from(this.inFlight);
|
|
7947
|
+
this.inFlight.clear();
|
|
7948
|
+
logger.info(`WebSocket: Rejecting ${pending.length} in-flight request(s). Reason: ${reason}.`);
|
|
7949
|
+
pending.forEach((abort) => abort(reason));
|
|
7950
|
+
}
|
|
7859
7951
|
async _restoreSubscriptions() {
|
|
7860
7952
|
const oldSubscriptions = Array.from(this.activeSubscriptions.values());
|
|
7861
7953
|
this.activeSubscriptions.clear();
|
|
@@ -7867,6 +7959,7 @@ var WebSocketChannel = class {
|
|
|
7867
7959
|
logger.info(`Subscription ${sub.method} restored with new ID: ${newSubId}`);
|
|
7868
7960
|
} catch (error) {
|
|
7869
7961
|
logger.error(`Failed to restore subscription ${sub.method}:`, error);
|
|
7962
|
+
sub._markClosed();
|
|
7870
7963
|
}
|
|
7871
7964
|
});
|
|
7872
7965
|
await Promise.all(restorePromises);
|
|
@@ -7909,18 +8002,24 @@ var WebSocketChannel = class {
|
|
|
7909
8002
|
`WebSocket: Connection lost. Attempting to reconnect... (${this.reconnectAttempts}/${this.reconnectOptions.retries})`
|
|
7910
8003
|
);
|
|
7911
8004
|
this.reconnect();
|
|
8005
|
+
let attemptSettled = false;
|
|
8006
|
+
const scheduleRetry = () => {
|
|
8007
|
+
if (attemptSettled || !this.isReconnecting) return;
|
|
8008
|
+
attemptSettled = true;
|
|
8009
|
+
const delay = this.reconnectOptions.exponential ? this.reconnectOptions.delay * 2 ** (this.reconnectAttempts - 1) : this.reconnectOptions.delay;
|
|
8010
|
+
logger.info(`WebSocket: Reconnect attempt failed. Retrying in ${delay}ms.`);
|
|
8011
|
+
this.reconnectTimeoutId = setTimeout(tryReconnect, delay);
|
|
8012
|
+
};
|
|
7912
8013
|
this.websocket.onopen = async () => {
|
|
7913
8014
|
logger.info("WebSocket: Reconnection successful.");
|
|
8015
|
+
attemptSettled = true;
|
|
7914
8016
|
this.isReconnecting = false;
|
|
7915
8017
|
await this._restoreSubscriptions();
|
|
7916
8018
|
this._processRequestQueue();
|
|
7917
8019
|
this.events.emit("open", new Event("open"));
|
|
7918
8020
|
};
|
|
7919
|
-
this.websocket.onerror =
|
|
7920
|
-
|
|
7921
|
-
logger.info(`WebSocket: Reconnect attempt failed. Retrying in ${delay}ms.`);
|
|
7922
|
-
this.reconnectTimeoutId = setTimeout(tryReconnect, delay);
|
|
7923
|
-
};
|
|
8021
|
+
this.websocket.onerror = scheduleRetry;
|
|
8022
|
+
this.websocket.addEventListener("close", scheduleRetry);
|
|
7924
8023
|
};
|
|
7925
8024
|
tryReconnect();
|
|
7926
8025
|
}
|
|
@@ -7929,6 +8028,8 @@ var WebSocketChannel = class {
|
|
|
7929
8028
|
this.websocket.removeEventListener("close", this.closeListener);
|
|
7930
8029
|
this.websocket.removeEventListener("message", this.messageListener);
|
|
7931
8030
|
this.websocket.removeEventListener("error", this.errorListener);
|
|
8031
|
+
this._rejectInFlight("the connection was closed");
|
|
8032
|
+
this._rejectUnsubscribeWaiters("the connection was closed");
|
|
7932
8033
|
this.events.emit("close", ev);
|
|
7933
8034
|
if (!this.userInitiatedClose) {
|
|
7934
8035
|
this._startReconnect();
|
|
@@ -7950,9 +8051,16 @@ var WebSocketChannel = class {
|
|
|
7950
8051
|
if (subscription) {
|
|
7951
8052
|
subscription._handleEvent(result);
|
|
7952
8053
|
} else {
|
|
7953
|
-
|
|
7954
|
-
|
|
7955
|
-
|
|
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
|
+
});
|
|
7956
8064
|
}
|
|
7957
8065
|
}
|
|
7958
8066
|
logger.debug("onMessageProxy:", event.data);
|