@bounded-sh/core 0.0.102 → 0.0.103
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/client/realtime-store.d.ts +2 -0
- package/dist/client/reconnect-backoff.d.ts +16 -0
- package/dist/client/socket-heartbeat.d.ts +47 -0
- package/dist/client/subscription-v2.d.ts +6 -0
- package/dist/{index-CMT0saxy.mjs → index-CC2WVN43.mjs} +170 -5
- package/dist/index-CC2WVN43.mjs.map +1 -0
- package/dist/{index-BEGeDFQI.js → index-D7dRe0PL.js} +170 -5
- package/dist/index-D7dRe0PL.js.map +1 -0
- package/dist/index.js +1 -1
- package/dist/index.mjs +1 -1
- package/dist/{realtime-store-DqAAqfo9.mjs → realtime-store-2iWofj5n.mjs} +2 -2
- package/dist/realtime-store-2iWofj5n.mjs.map +1 -0
- package/dist/{realtime-store-DrtEj-bE.js → realtime-store-CuFYPLgk.js} +2 -2
- package/dist/realtime-store-CuFYPLgk.js.map +1 -0
- package/dist/{solana-message-C_717vAx.mjs → solana-message-Bx04VMG1.mjs} +2 -2
- package/dist/{solana-message-C_717vAx.mjs.map → solana-message-Bx04VMG1.mjs.map} +1 -1
- package/dist/{solana-message-CJTnYiJu.js → solana-message-DLBiBfnG.js} +2 -2
- package/dist/{solana-message-CJTnYiJu.js.map → solana-message-DLBiBfnG.js.map} +1 -1
- package/dist/{solana-operations-CU0YbiMu.js → solana-operations-Be0dWtVZ.js} +3 -3
- package/dist/{solana-operations-CU0YbiMu.js.map → solana-operations-Be0dWtVZ.js.map} +1 -1
- package/dist/{solana-operations-DGNA2nGZ.mjs → solana-operations-CCFNQIgd.mjs} +4 -4
- package/dist/{solana-operations-DGNA2nGZ.mjs.map → solana-operations-CCFNQIgd.mjs.map} +1 -1
- package/dist/{solana-v1-operations-yQII8r1s.js → solana-v1-operations-Bbj0F4cp.js} +3 -3
- package/dist/{solana-v1-operations-yQII8r1s.js.map → solana-v1-operations-Bbj0F4cp.js.map} +1 -1
- package/dist/{solana-v1-operations-Bpd4TASG.mjs → solana-v1-operations-C6Qev1fZ.mjs} +3 -3
- package/dist/{solana-v1-operations-Bpd4TASG.mjs.map → solana-v1-operations-C6Qev1fZ.mjs.map} +1 -1
- package/package.json +2 -2
- package/dist/index-BEGeDFQI.js.map +0 -1
- package/dist/index-CMT0saxy.mjs.map +0 -1
- package/dist/realtime-store-DqAAqfo9.mjs.map +0 -1
- package/dist/realtime-store-DrtEj-bE.js.map +0 -1
|
@@ -4190,7 +4190,7 @@ class ServerSessionManager {
|
|
|
4190
4190
|
const sessionConfig = (_a = this.sessionConfig) !== null && _a !== void 0 ? _a : undefined;
|
|
4191
4191
|
/* fetch nonce from auth API (bound to the snapshotted app+issuer) */
|
|
4192
4192
|
const nonce = await genAuthNonce(sessionConfig);
|
|
4193
|
-
const { genSolanaMessage } = await Promise.resolve().then(function () { return require('./solana-message-
|
|
4193
|
+
const { genSolanaMessage } = await Promise.resolve().then(function () { return require('./solana-message-DLBiBfnG.js'); });
|
|
4194
4194
|
const message = await genSolanaMessage(address, nonce, sessionConfig === null || sessionConfig === void 0 ? void 0 : sessionConfig.appId);
|
|
4195
4195
|
/* sign the message */
|
|
4196
4196
|
const sigBytes = nacl.sign.detached(new TextEncoder().encode(message), kp.secretKey);
|
|
@@ -5231,6 +5231,83 @@ async function resolveClientTransactionVersions(authProvider) {
|
|
|
5231
5231
|
return typeof authProvider.signTransactionV1 === 'function' ? ['legacy', 0, 1] : ['legacy', 0];
|
|
5232
5232
|
}
|
|
5233
5233
|
|
|
5234
|
+
/**
|
|
5235
|
+
* Idle heartbeat for the realtime WebSocket connection (subscription-v2.ts).
|
|
5236
|
+
*
|
|
5237
|
+
* A socket whose server has stopped delivering frames looks exactly like a
|
|
5238
|
+
* quiet one: the browser reports it open and nothing arrives. The client only
|
|
5239
|
+
* learns the difference by asking. Once nothing has been received for
|
|
5240
|
+
* HEARTBEAT_IDLE_MS the heartbeat asks the connection to send a ping; if
|
|
5241
|
+
* nothing at all arrives within HEARTBEAT_TIMEOUT_MS after that, the socket is
|
|
5242
|
+
* declared dead and the connection is reconnected. Any inbound frame counts as
|
|
5243
|
+
* proof of life, so a busy socket is never pinged and a pong is just another
|
|
5244
|
+
* frame. The connection may decline to ping (it already has a request in
|
|
5245
|
+
* flight with its own timeout); the idle wait then simply starts over.
|
|
5246
|
+
*
|
|
5247
|
+
* The timeout sits above both budgets a pong can wait behind: the connection's
|
|
5248
|
+
* own 30 s request budget (the server handles one frame per socket at a time)
|
|
5249
|
+
* and the object's 30 s CPU slice, which another socket's handler can hold
|
|
5250
|
+
* while every pong waits. A healthy socket must never be declared dead
|
|
5251
|
+
* within either.
|
|
5252
|
+
*
|
|
5253
|
+
* Pure: timers come from the clock passed in, so tests can drive it.
|
|
5254
|
+
*/
|
|
5255
|
+
const HEARTBEAT_IDLE_MS = 25000;
|
|
5256
|
+
const HEARTBEAT_TIMEOUT_MS = 45000;
|
|
5257
|
+
class SocketHeartbeat {
|
|
5258
|
+
constructor(
|
|
5259
|
+
/** Send a ping now; return false to skip this round (the idle wait restarts). */
|
|
5260
|
+
sendPing, onDead, clock = globalThis, idleMs = HEARTBEAT_IDLE_MS, timeoutMs = HEARTBEAT_TIMEOUT_MS) {
|
|
5261
|
+
this.sendPing = sendPing;
|
|
5262
|
+
this.onDead = onDead;
|
|
5263
|
+
this.clock = clock;
|
|
5264
|
+
this.idleMs = idleMs;
|
|
5265
|
+
this.timeoutMs = timeoutMs;
|
|
5266
|
+
this.idleTimer = null;
|
|
5267
|
+
this.timeoutTimer = null;
|
|
5268
|
+
this.stopped = false;
|
|
5269
|
+
}
|
|
5270
|
+
start() {
|
|
5271
|
+
if (this.stopped)
|
|
5272
|
+
return;
|
|
5273
|
+
this.armIdle();
|
|
5274
|
+
}
|
|
5275
|
+
/** An inbound frame arrived: the socket is alive, so the idle wait starts over. */
|
|
5276
|
+
noteActivity() {
|
|
5277
|
+
if (this.stopped)
|
|
5278
|
+
return;
|
|
5279
|
+
this.clearTimers();
|
|
5280
|
+
this.armIdle();
|
|
5281
|
+
}
|
|
5282
|
+
stop() {
|
|
5283
|
+
this.stopped = true;
|
|
5284
|
+
this.clearTimers();
|
|
5285
|
+
}
|
|
5286
|
+
armIdle() {
|
|
5287
|
+
this.idleTimer = this.clock.setTimeout(() => {
|
|
5288
|
+
this.idleTimer = null;
|
|
5289
|
+
if (!this.sendPing()) {
|
|
5290
|
+
this.armIdle();
|
|
5291
|
+
return;
|
|
5292
|
+
}
|
|
5293
|
+
this.timeoutTimer = this.clock.setTimeout(() => {
|
|
5294
|
+
this.timeoutTimer = null;
|
|
5295
|
+
this.onDead();
|
|
5296
|
+
}, this.timeoutMs);
|
|
5297
|
+
}, this.idleMs);
|
|
5298
|
+
}
|
|
5299
|
+
clearTimers() {
|
|
5300
|
+
if (this.idleTimer !== null) {
|
|
5301
|
+
this.clock.clearTimeout(this.idleTimer);
|
|
5302
|
+
this.idleTimer = null;
|
|
5303
|
+
}
|
|
5304
|
+
if (this.timeoutTimer !== null) {
|
|
5305
|
+
this.clock.clearTimeout(this.timeoutTimer);
|
|
5306
|
+
this.timeoutTimer = null;
|
|
5307
|
+
}
|
|
5308
|
+
}
|
|
5309
|
+
}
|
|
5310
|
+
|
|
5234
5311
|
/**
|
|
5235
5312
|
* WebSocket v2 Subscription Manager
|
|
5236
5313
|
*
|
|
@@ -5325,6 +5402,81 @@ const WS_CONFIG = {
|
|
|
5325
5402
|
// Give handshakes more time before considering the attempt failed.
|
|
5326
5403
|
connectionTimeout: 10000,
|
|
5327
5404
|
};
|
|
5405
|
+
/** Client-chosen close code for a socket the heartbeat declared dead (4000-4999 are application codes). */
|
|
5406
|
+
const HEARTBEAT_CLOSE_CODE = 4000;
|
|
5407
|
+
function heartbeatReconnectDelayMs() {
|
|
5408
|
+
return BASE_MIN_RECONNECT_DELAY_MS + Math.floor(Math.random() * MIN_RECONNECT_DELAY_JITTER_MS);
|
|
5409
|
+
}
|
|
5410
|
+
/**
|
|
5411
|
+
* Start the idle heartbeat for the native socket that just opened on `ws`.
|
|
5412
|
+
* A ping goes out after HEARTBEAT_IDLE_MS of silence, and a socket that
|
|
5413
|
+
* answers nothing for HEARTBEAT_TIMEOUT_MS more is reconnected through the
|
|
5414
|
+
* wrapper after a jittered delay, the same path an auth change takes. A
|
|
5415
|
+
* server that has stopped dispatching frames leaves the socket open and
|
|
5416
|
+
* silent, so nothing else on the client would ever notice.
|
|
5417
|
+
*/
|
|
5418
|
+
function startHeartbeat(connection, ws) {
|
|
5419
|
+
stopHeartbeat(connection);
|
|
5420
|
+
const heartbeat = new SocketHeartbeat(() => {
|
|
5421
|
+
// An outbound wait already has its own timeout, and the server
|
|
5422
|
+
// answers one frame per socket at a time, so a ping behind it would
|
|
5423
|
+
// only measure that wait. Let the idle wait start over instead.
|
|
5424
|
+
if (hasOutboundWait(connection))
|
|
5425
|
+
return false;
|
|
5426
|
+
// The wrapper drops a send on a socket that is not open, and a send
|
|
5427
|
+
// that throws is a socket that cannot carry a ping either way; the
|
|
5428
|
+
// dead timer then reports it.
|
|
5429
|
+
try {
|
|
5430
|
+
ws.send(JSON.stringify({ type: 'ping' }));
|
|
5431
|
+
}
|
|
5432
|
+
catch (error) {
|
|
5433
|
+
console.warn('[WS v2] Heartbeat ping could not be sent:', error);
|
|
5434
|
+
}
|
|
5435
|
+
return true;
|
|
5436
|
+
}, () => {
|
|
5437
|
+
// Only the socket this heartbeat was started for. A replaced or
|
|
5438
|
+
// torn-down connection must never be resurrected by a late timer.
|
|
5439
|
+
if (connection.heartbeat !== heartbeat || connection.ws !== ws)
|
|
5440
|
+
return;
|
|
5441
|
+
console.warn('[WS v2] No frames within the heartbeat window; reconnecting');
|
|
5442
|
+
// The heartbeat stays installed through the delay: a frame that
|
|
5443
|
+
// arrives meanwhile cancels the replacement (see the message
|
|
5444
|
+
// listener) and starts a fresh idle window on the same socket.
|
|
5445
|
+
connection.heartbeatReconnectTimer = setTimeout(() => {
|
|
5446
|
+
connection.heartbeatReconnectTimer = null;
|
|
5447
|
+
if (connection.heartbeat !== heartbeat || connection.ws !== ws)
|
|
5448
|
+
return;
|
|
5449
|
+
connection.heartbeat = null;
|
|
5450
|
+
try {
|
|
5451
|
+
ws.reconnect(HEARTBEAT_CLOSE_CODE, 'Heartbeat timeout');
|
|
5452
|
+
}
|
|
5453
|
+
catch (error) {
|
|
5454
|
+
console.error('[WS v2] Error reconnecting after heartbeat timeout:', error);
|
|
5455
|
+
}
|
|
5456
|
+
}, heartbeatReconnectDelayMs());
|
|
5457
|
+
});
|
|
5458
|
+
connection.heartbeat = heartbeat;
|
|
5459
|
+
heartbeat.start();
|
|
5460
|
+
}
|
|
5461
|
+
function stopHeartbeat(connection) {
|
|
5462
|
+
var _a;
|
|
5463
|
+
(_a = connection.heartbeat) === null || _a === void 0 ? void 0 : _a.stop();
|
|
5464
|
+
connection.heartbeat = null;
|
|
5465
|
+
if (connection.heartbeatReconnectTimer) {
|
|
5466
|
+
clearTimeout(connection.heartbeatReconnectTimer);
|
|
5467
|
+
connection.heartbeatReconnectTimer = null;
|
|
5468
|
+
}
|
|
5469
|
+
}
|
|
5470
|
+
/** A frame of ours the server has not answered yet: a request, a subscription confirmation, or a snapshot refresh. */
|
|
5471
|
+
function hasOutboundWait(connection) {
|
|
5472
|
+
if (connection.pendingRequests.size > 0 || connection.pendingSubscriptions.size > 0)
|
|
5473
|
+
return true;
|
|
5474
|
+
for (const subscription of connection.subscriptions.values()) {
|
|
5475
|
+
if (subscription.refresh)
|
|
5476
|
+
return true;
|
|
5477
|
+
}
|
|
5478
|
+
return false;
|
|
5479
|
+
}
|
|
5328
5480
|
const WS_V2_PATH = '/ws/v2';
|
|
5329
5481
|
let browserReconnectHooksAttached = false;
|
|
5330
5482
|
let lastBrowserTriggeredReconnectAt = 0;
|
|
@@ -5793,6 +5945,7 @@ function closeConnectionNow(connection) {
|
|
|
5793
5945
|
clearInterval(connection.tokenRefreshTimer);
|
|
5794
5946
|
connection.tokenRefreshTimer = null;
|
|
5795
5947
|
}
|
|
5948
|
+
stopHeartbeat(connection);
|
|
5796
5949
|
const ws = connection.ws;
|
|
5797
5950
|
connection.ws = null;
|
|
5798
5951
|
connection.isConnecting = false;
|
|
@@ -6077,6 +6230,8 @@ async function getOrCreateConnectionOnce(appId, isServer, routePath, authTokenPr
|
|
|
6077
6230
|
authTokenValue: null,
|
|
6078
6231
|
authPrincipal: null,
|
|
6079
6232
|
socketGeneration: 0,
|
|
6233
|
+
heartbeat: null,
|
|
6234
|
+
heartbeatReconnectTimer: null,
|
|
6080
6235
|
tokenRefreshTimer: null,
|
|
6081
6236
|
idleCloseTimer: null,
|
|
6082
6237
|
consecutiveAuthFailures: 0,
|
|
@@ -6151,6 +6306,7 @@ async function getOrCreateConnectionOnce(appId, isServer, routePath, authTokenPr
|
|
|
6151
6306
|
connection.socketGeneration += 1;
|
|
6152
6307
|
connection.isConnecting = false;
|
|
6153
6308
|
connection.isConnected = true;
|
|
6309
|
+
startHeartbeat(connection, ws);
|
|
6154
6310
|
// NOTE: Do NOT reset consecutiveAuthFailures here. It is reset when a
|
|
6155
6311
|
// fresh auth token is actually obtained or on an explicit auth change.
|
|
6156
6312
|
// Schedule periodic token freshness checks. An anonymous (read-only
|
|
@@ -6185,6 +6341,13 @@ async function getOrCreateConnectionOnce(appId, isServer, routePath, authTokenPr
|
|
|
6185
6341
|
});
|
|
6186
6342
|
// Handle incoming messages
|
|
6187
6343
|
ws.addEventListener('message', (event) => {
|
|
6344
|
+
var _a;
|
|
6345
|
+
if (connection.heartbeatReconnectTimer) {
|
|
6346
|
+
// A frame inside the replacement delay: the socket is alive after all.
|
|
6347
|
+
clearTimeout(connection.heartbeatReconnectTimer);
|
|
6348
|
+
connection.heartbeatReconnectTimer = null;
|
|
6349
|
+
}
|
|
6350
|
+
(_a = connection.heartbeat) === null || _a === void 0 ? void 0 : _a.noteActivity();
|
|
6188
6351
|
try {
|
|
6189
6352
|
const message = JSON.parse(event.data);
|
|
6190
6353
|
handleServerMessage(connection, message);
|
|
@@ -6206,6 +6369,7 @@ async function getOrCreateConnectionOnce(appId, isServer, routePath, authTokenPr
|
|
|
6206
6369
|
});
|
|
6207
6370
|
// Handle close
|
|
6208
6371
|
ws.addEventListener('close', () => {
|
|
6372
|
+
stopHeartbeat(connection);
|
|
6209
6373
|
connection.isConnected = false;
|
|
6210
6374
|
connection.isAuthenticating = false;
|
|
6211
6375
|
const disconnectError = new Error('WebSocket disconnected');
|
|
@@ -7185,6 +7349,7 @@ async function closeAllSubscriptionsV2() {
|
|
|
7185
7349
|
connection.tokenRefreshTimer = null;
|
|
7186
7350
|
}
|
|
7187
7351
|
if (connection.ws) {
|
|
7352
|
+
stopHeartbeat(connection);
|
|
7188
7353
|
const ws = connection.ws;
|
|
7189
7354
|
connection.ws = null;
|
|
7190
7355
|
connection.isConnected = false;
|
|
@@ -7304,7 +7469,7 @@ async function doReconnectWithNewAuth() {
|
|
|
7304
7469
|
console.warn('[WS v2] Failed to clear HTTP read cache on auth change:', error);
|
|
7305
7470
|
}
|
|
7306
7471
|
try {
|
|
7307
|
-
const { reconnectRealtimeStoreWithNewAuth } = await Promise.resolve().then(function () { return require('./realtime-store-
|
|
7472
|
+
const { reconnectRealtimeStoreWithNewAuth } = await Promise.resolve().then(function () { return require('./realtime-store-CuFYPLgk.js'); });
|
|
7308
7473
|
await reconnectRealtimeStoreWithNewAuth();
|
|
7309
7474
|
}
|
|
7310
7475
|
catch (error) {
|
|
@@ -8758,7 +8923,7 @@ async function setMany(many, options) {
|
|
|
8758
8923
|
'Nothing was signed or submitted.');
|
|
8759
8924
|
}
|
|
8760
8925
|
const solanaChain = workerChain;
|
|
8761
|
-
const { handlePreBuiltTransaction, buildExpectedWriteIntent } = await Promise.resolve().then(function () { return require('./solana-operations-
|
|
8926
|
+
const { handlePreBuiltTransaction, buildExpectedWriteIntent } = await Promise.resolve().then(function () { return require('./solana-operations-Be0dWtVZ.js'); });
|
|
8762
8927
|
// OA-0001: derive the expected intent from OUR OWN request (the one
|
|
8763
8928
|
// ground truth a tampered builder response cannot change) so the
|
|
8764
8929
|
// validator can reject any transaction whose operation semantics
|
|
@@ -10533,7 +10698,7 @@ function toMillis(seconds) {
|
|
|
10533
10698
|
|
|
10534
10699
|
/** SIWS message creation is async already, so its onchain module can be lazy. */
|
|
10535
10700
|
async function genSolanaMessage(address, nonce) {
|
|
10536
|
-
const implementation = await Promise.resolve().then(function () { return require('./solana-message-
|
|
10701
|
+
const implementation = await Promise.resolve().then(function () { return require('./solana-message-DLBiBfnG.js'); });
|
|
10537
10702
|
return implementation.genSolanaMessage(address, nonce);
|
|
10538
10703
|
}
|
|
10539
10704
|
|
|
@@ -11180,4 +11345,4 @@ exports.toMillis = toMillis;
|
|
|
11180
11345
|
exports.toSeconds = toSeconds;
|
|
11181
11346
|
exports.waitForSetResult = waitForSetResult;
|
|
11182
11347
|
exports.withEffects = withEffects;
|
|
11183
|
-
//# sourceMappingURL=index-
|
|
11348
|
+
//# sourceMappingURL=index-D7dRe0PL.js.map
|