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/CHANGELOG.md +6 -0
- package/dist/index.d.ts +28 -0
- package/dist/index.global.js +96 -21
- package/dist/index.global.js.map +1 -1
- package/dist/index.js +96 -21
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +96 -21
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,9 @@
|
|
|
1
|
+
## [10.6.8](https://github.com/starknet-io/starknet.js/compare/v10.6.7...v10.6.8) (2026-08-07)
|
|
2
|
+
|
|
3
|
+
### Bug Fixes
|
|
4
|
+
|
|
5
|
+
- **ws:** close the subscription lifecycle races ([c242c76](https://github.com/starknet-io/starknet.js/commit/c242c76ab73f508ecd454d1d7eb2fde8728c4f9d))
|
|
6
|
+
|
|
1
7
|
## [10.6.7](https://github.com/starknet-io/starknet.js/compare/v10.6.6...v10.6.7) (2026-08-06)
|
|
2
8
|
|
|
3
9
|
### Bug Fixes
|
package/dist/index.d.ts
CHANGED
|
@@ -4962,6 +4962,7 @@ declare class Subscription<T = any> {
|
|
|
4962
4962
|
private maxBufferSize;
|
|
4963
4963
|
private handler;
|
|
4964
4964
|
private _isClosed;
|
|
4965
|
+
private pendingUnsubscribe;
|
|
4965
4966
|
/**
|
|
4966
4967
|
* @internal
|
|
4967
4968
|
* @param options - Subscription configuration options
|
|
@@ -4972,6 +4973,15 @@ declare class Subscription<T = any> {
|
|
|
4972
4973
|
* @returns {boolean} `true` if unsubscribed, `false` otherwise.
|
|
4973
4974
|
*/
|
|
4974
4975
|
get isClosed(): boolean;
|
|
4976
|
+
/**
|
|
4977
|
+
* Closes the subscription locally, without contacting the node.
|
|
4978
|
+
*
|
|
4979
|
+
* Used when the channel knows the subscription is gone and cannot be recovered — a
|
|
4980
|
+
* re-subscribe refused after a reconnection, for instance. Without it the object would keep
|
|
4981
|
+
* reporting itself as open while no event could ever reach its handler again.
|
|
4982
|
+
* @internal
|
|
4983
|
+
*/
|
|
4984
|
+
_markClosed(): void;
|
|
4975
4985
|
/**
|
|
4976
4986
|
* Internal method to handle incoming events from the WebSocket channel.
|
|
4977
4987
|
* If a handler is attached, it's invoked immediately. Otherwise, the event is buffered.
|
|
@@ -5153,6 +5163,15 @@ declare class WebSocketChannel {
|
|
|
5153
5163
|
private requestQueue;
|
|
5154
5164
|
/** Abort handles for the requests currently on the wire, one per pending `sendReceive`. */
|
|
5155
5165
|
private inFlight;
|
|
5166
|
+
/**
|
|
5167
|
+
* Callers blocked in `waitForUnsubscription`, keyed by subscription id.
|
|
5168
|
+
*
|
|
5169
|
+
* Held here rather than as `unsubscribe` event listeners because that event only ever
|
|
5170
|
+
* announces success: a waiter attached to it cannot learn that the node refused the
|
|
5171
|
+
* unsubscribe or that the connection went away, and would wait forever with no timeout of
|
|
5172
|
+
* its own to fall back on.
|
|
5173
|
+
*/
|
|
5174
|
+
private unsubscribeWaiters;
|
|
5156
5175
|
private events;
|
|
5157
5176
|
private openListener;
|
|
5158
5177
|
private closeListener;
|
|
@@ -5228,6 +5247,15 @@ declare class WebSocketChannel {
|
|
|
5228
5247
|
* @returns {Promise<boolean>} A Promise that resolves with `true` if the unsubscription was successful.
|
|
5229
5248
|
*/
|
|
5230
5249
|
unsubscribe(subscriptionId: SUBSCRIPTION_ID): Promise<boolean>;
|
|
5250
|
+
/** Settles every caller waiting on one subscription id: resolved, or rejected with `error`. */
|
|
5251
|
+
private _settleUnsubscribeWaiters;
|
|
5252
|
+
/**
|
|
5253
|
+
* Rejects every caller still waiting on any subscription.
|
|
5254
|
+
*
|
|
5255
|
+
* Once the connection is gone, no unsubscribe can be observed on it: a reconnection restores
|
|
5256
|
+
* each subscription under a fresh id, so the id being waited on will never be announced.
|
|
5257
|
+
*/
|
|
5258
|
+
private _rejectUnsubscribeWaiters;
|
|
5231
5259
|
/**
|
|
5232
5260
|
* Returns a Promise that resolves when a specific subscription is successfully unsubscribed.
|
|
5233
5261
|
* @param {SUBSCRIPTION_ID} targetId - The ID of the subscription to wait for.
|
package/dist/index.global.js
CHANGED
|
@@ -12516,6 +12516,8 @@ ${indent}}` : "}";
|
|
|
12516
12516
|
maxBufferSize;
|
|
12517
12517
|
handler = null;
|
|
12518
12518
|
_isClosed = false;
|
|
12519
|
+
// The unsubscribe request currently on the wire, shared by concurrent callers.
|
|
12520
|
+
pendingUnsubscribe = null;
|
|
12519
12521
|
/**
|
|
12520
12522
|
* @internal
|
|
12521
12523
|
* @param options - Subscription configuration options
|
|
@@ -12534,6 +12536,20 @@ ${indent}}` : "}";
|
|
|
12534
12536
|
get isClosed() {
|
|
12535
12537
|
return this._isClosed;
|
|
12536
12538
|
}
|
|
12539
|
+
/**
|
|
12540
|
+
* Closes the subscription locally, without contacting the node.
|
|
12541
|
+
*
|
|
12542
|
+
* Used when the channel knows the subscription is gone and cannot be recovered — a
|
|
12543
|
+
* re-subscribe refused after a reconnection, for instance. Without it the object would keep
|
|
12544
|
+
* reporting itself as open while no event could ever reach its handler again.
|
|
12545
|
+
* @internal
|
|
12546
|
+
*/
|
|
12547
|
+
_markClosed() {
|
|
12548
|
+
if (this._isClosed) return;
|
|
12549
|
+
this._isClosed = true;
|
|
12550
|
+
this.events.emit("unsubscribe", void 0);
|
|
12551
|
+
this.events.clear();
|
|
12552
|
+
}
|
|
12537
12553
|
/**
|
|
12538
12554
|
* Internal method to handle incoming events from the WebSocket channel.
|
|
12539
12555
|
* If a handler is attached, it's invoked immediately. Otherwise, the event is buffered.
|
|
@@ -12580,14 +12596,24 @@ ${indent}}` : "}";
|
|
|
12580
12596
|
if (this._isClosed) {
|
|
12581
12597
|
return true;
|
|
12582
12598
|
}
|
|
12583
|
-
|
|
12584
|
-
|
|
12585
|
-
this._isClosed = true;
|
|
12586
|
-
this.channel.removeSubscription(this.id);
|
|
12587
|
-
this.events.emit("unsubscribe", void 0);
|
|
12588
|
-
this.events.clear();
|
|
12599
|
+
if (this.pendingUnsubscribe) {
|
|
12600
|
+
return this.pendingUnsubscribe;
|
|
12589
12601
|
}
|
|
12590
|
-
|
|
12602
|
+
this.pendingUnsubscribe = (async () => {
|
|
12603
|
+
try {
|
|
12604
|
+
const success = await this.channel.unsubscribe(this.id);
|
|
12605
|
+
if (success) {
|
|
12606
|
+
this._isClosed = true;
|
|
12607
|
+
this.channel.removeSubscription(this.id);
|
|
12608
|
+
this.events.emit("unsubscribe", void 0);
|
|
12609
|
+
this.events.clear();
|
|
12610
|
+
}
|
|
12611
|
+
return success;
|
|
12612
|
+
} finally {
|
|
12613
|
+
this.pendingUnsubscribe = null;
|
|
12614
|
+
}
|
|
12615
|
+
})();
|
|
12616
|
+
return this.pendingUnsubscribe;
|
|
12591
12617
|
}
|
|
12592
12618
|
};
|
|
12593
12619
|
|
|
@@ -12620,6 +12646,15 @@ ${indent}}` : "}";
|
|
|
12620
12646
|
requestQueue = [];
|
|
12621
12647
|
/** Abort handles for the requests currently on the wire, one per pending `sendReceive`. */
|
|
12622
12648
|
inFlight = /* @__PURE__ */ new Set();
|
|
12649
|
+
/**
|
|
12650
|
+
* Callers blocked in `waitForUnsubscription`, keyed by subscription id.
|
|
12651
|
+
*
|
|
12652
|
+
* Held here rather than as `unsubscribe` event listeners because that event only ever
|
|
12653
|
+
* announces success: a waiter attached to it cannot learn that the node refused the
|
|
12654
|
+
* unsubscribe or that the connection went away, and would wait forever with no timeout of
|
|
12655
|
+
* its own to fall back on.
|
|
12656
|
+
*/
|
|
12657
|
+
unsubscribeWaiters = /* @__PURE__ */ new Map();
|
|
12623
12658
|
events = new EventEmitter();
|
|
12624
12659
|
openListener = (ev) => {
|
|
12625
12660
|
this.scheduleReconnectAttemptsReset();
|
|
@@ -12816,6 +12851,7 @@ ${indent}}` : "}";
|
|
|
12816
12851
|
this.isReconnecting = false;
|
|
12817
12852
|
this._rejectRequestQueue("the connection was closed by the user");
|
|
12818
12853
|
this._rejectInFlight("the connection was closed by the user");
|
|
12854
|
+
this._rejectUnsubscribeWaiters("the connection was closed by the user");
|
|
12819
12855
|
this.websocket.close(code, reason);
|
|
12820
12856
|
}
|
|
12821
12857
|
/**
|
|
@@ -12840,14 +12876,48 @@ ${indent}}` : "}";
|
|
|
12840
12876
|
* @returns {Promise<boolean>} A Promise that resolves with `true` if the unsubscription was successful.
|
|
12841
12877
|
*/
|
|
12842
12878
|
async unsubscribe(subscriptionId) {
|
|
12843
|
-
|
|
12844
|
-
|
|
12845
|
-
|
|
12879
|
+
let status;
|
|
12880
|
+
try {
|
|
12881
|
+
status = await this.sendReceive("starknet_unsubscribe", {
|
|
12882
|
+
subscription_id: subscriptionId
|
|
12883
|
+
});
|
|
12884
|
+
} catch (error) {
|
|
12885
|
+
this._settleUnsubscribeWaiters(subscriptionId, error);
|
|
12886
|
+
throw error;
|
|
12887
|
+
}
|
|
12846
12888
|
if (status) {
|
|
12847
12889
|
this.events.emit("unsubscribe", subscriptionId);
|
|
12890
|
+
this._settleUnsubscribeWaiters(subscriptionId);
|
|
12891
|
+
} else {
|
|
12892
|
+
this._settleUnsubscribeWaiters(
|
|
12893
|
+
subscriptionId,
|
|
12894
|
+
new Error(`Node refused to unsubscribe subscription ${subscriptionId}`)
|
|
12895
|
+
);
|
|
12848
12896
|
}
|
|
12849
12897
|
return status;
|
|
12850
12898
|
}
|
|
12899
|
+
/** Settles every caller waiting on one subscription id: resolved, or rejected with `error`. */
|
|
12900
|
+
_settleUnsubscribeWaiters(subscriptionId, error) {
|
|
12901
|
+
const waiters = this.unsubscribeWaiters.get(subscriptionId);
|
|
12902
|
+
if (!waiters) return;
|
|
12903
|
+
this.unsubscribeWaiters.delete(subscriptionId);
|
|
12904
|
+
waiters.forEach((waiter) => error ? waiter.reject(error) : waiter.resolve());
|
|
12905
|
+
}
|
|
12906
|
+
/**
|
|
12907
|
+
* Rejects every caller still waiting on any subscription.
|
|
12908
|
+
*
|
|
12909
|
+
* Once the connection is gone, no unsubscribe can be observed on it: a reconnection restores
|
|
12910
|
+
* each subscription under a fresh id, so the id being waited on will never be announced.
|
|
12911
|
+
*/
|
|
12912
|
+
_rejectUnsubscribeWaiters(reason) {
|
|
12913
|
+
if (this.unsubscribeWaiters.size === 0) return;
|
|
12914
|
+
Array.from(this.unsubscribeWaiters.keys()).forEach(
|
|
12915
|
+
(id) => this._settleUnsubscribeWaiters(
|
|
12916
|
+
id,
|
|
12917
|
+
new WebSocketNotConnectedError(`Subscription ${id} was never unsubscribed: ${reason}`)
|
|
12918
|
+
)
|
|
12919
|
+
);
|
|
12920
|
+
}
|
|
12851
12921
|
/**
|
|
12852
12922
|
* Returns a Promise that resolves when a specific subscription is successfully unsubscribed.
|
|
12853
12923
|
* @param {SUBSCRIPTION_ID} targetId - The ID of the subscription to wait for.
|
|
@@ -12859,14 +12929,10 @@ ${indent}}` : "}";
|
|
|
12859
12929
|
* ```
|
|
12860
12930
|
*/
|
|
12861
12931
|
waitForUnsubscription(targetId) {
|
|
12862
|
-
return new Promise((resolve) => {
|
|
12863
|
-
const
|
|
12864
|
-
|
|
12865
|
-
|
|
12866
|
-
resolve();
|
|
12867
|
-
}
|
|
12868
|
-
};
|
|
12869
|
-
this.events.on("unsubscribe", listener);
|
|
12932
|
+
return new Promise((resolve, reject) => {
|
|
12933
|
+
const waiters = this.unsubscribeWaiters.get(targetId) ?? /* @__PURE__ */ new Set();
|
|
12934
|
+
waiters.add({ resolve, reject });
|
|
12935
|
+
this.unsubscribeWaiters.set(targetId, waiters);
|
|
12870
12936
|
});
|
|
12871
12937
|
}
|
|
12872
12938
|
/**
|
|
@@ -12933,6 +12999,7 @@ ${indent}}` : "}";
|
|
|
12933
12999
|
logger.info(`Subscription ${sub.method} restored with new ID: ${newSubId}`);
|
|
12934
13000
|
} catch (error) {
|
|
12935
13001
|
logger.error(`Failed to restore subscription ${sub.method}:`, error);
|
|
13002
|
+
sub._markClosed();
|
|
12936
13003
|
}
|
|
12937
13004
|
});
|
|
12938
13005
|
await Promise.all(restorePromises);
|
|
@@ -13002,6 +13069,7 @@ ${indent}}` : "}";
|
|
|
13002
13069
|
this.websocket.removeEventListener("message", this.messageListener);
|
|
13003
13070
|
this.websocket.removeEventListener("error", this.errorListener);
|
|
13004
13071
|
this._rejectInFlight("the connection was closed");
|
|
13072
|
+
this._rejectUnsubscribeWaiters("the connection was closed");
|
|
13005
13073
|
this.events.emit("close", ev);
|
|
13006
13074
|
if (!this.userInitiatedClose) {
|
|
13007
13075
|
this._startReconnect();
|
|
@@ -13023,9 +13091,16 @@ ${indent}}` : "}";
|
|
|
13023
13091
|
if (subscription) {
|
|
13024
13092
|
subscription._handleEvent(result);
|
|
13025
13093
|
} else {
|
|
13026
|
-
|
|
13027
|
-
|
|
13028
|
-
|
|
13094
|
+
queueMicrotask(() => {
|
|
13095
|
+
const registered = this.activeSubscriptions.get(subscription_id);
|
|
13096
|
+
if (registered) {
|
|
13097
|
+
registered._handleEvent(result);
|
|
13098
|
+
} else {
|
|
13099
|
+
logger.warn(
|
|
13100
|
+
`WebSocketChannel: Received event for untracked subscription ID: ${subscription_id}.`
|
|
13101
|
+
);
|
|
13102
|
+
}
|
|
13103
|
+
});
|
|
13029
13104
|
}
|
|
13030
13105
|
}
|
|
13031
13106
|
logger.debug("onMessageProxy:", event.data);
|