@bounded-sh/core 0.0.102 → 0.0.104
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/solana-operations.d.ts +15 -0
- package/dist/client/subscription-v2.d.ts +6 -0
- package/dist/{index-BEGeDFQI.js → index-62gjQH30.js} +220 -26
- package/dist/index-62gjQH30.js.map +1 -0
- package/dist/{index-CMT0saxy.mjs → index-CLst0IiM.mjs} +220 -26
- package/dist/index-CLst0IiM.mjs.map +1 -0
- package/dist/index.js +1 -1
- package/dist/index.mjs +1 -1
- package/dist/{realtime-store-DrtEj-bE.js → realtime-store-9SCt1RPf.js} +2 -2
- package/dist/realtime-store-9SCt1RPf.js.map +1 -0
- package/dist/{realtime-store-DqAAqfo9.mjs → realtime-store-C9AgPg7U.mjs} +2 -2
- package/dist/realtime-store-C9AgPg7U.mjs.map +1 -0
- package/dist/{solana-message-CJTnYiJu.js → solana-message-Bf2q_JO1.js} +2 -2
- package/dist/{solana-message-CJTnYiJu.js.map → solana-message-Bf2q_JO1.js.map} +1 -1
- package/dist/{solana-message-C_717vAx.mjs → solana-message-Deru1tF1.mjs} +2 -2
- package/dist/{solana-message-C_717vAx.mjs.map → solana-message-Deru1tF1.mjs.map} +1 -1
- package/dist/{solana-operations-DGNA2nGZ.mjs → solana-operations-Cm19J14R.mjs} +34 -5
- package/dist/{solana-operations-DGNA2nGZ.mjs.map → solana-operations-Cm19J14R.mjs.map} +1 -1
- package/dist/{solana-operations-CU0YbiMu.js → solana-operations-eTiLgak2.js} +34 -3
- package/dist/{solana-operations-CU0YbiMu.js.map → solana-operations-eTiLgak2.js.map} +1 -1
- package/dist/{solana-v1-operations-yQII8r1s.js → solana-v1-operations-CnmiKiyj.js} +3 -3
- package/dist/{solana-v1-operations-yQII8r1s.js.map → solana-v1-operations-CnmiKiyj.js.map} +1 -1
- package/dist/{solana-v1-operations-Bpd4TASG.mjs → solana-v1-operations-Dxs4XM_F.mjs} +3 -3
- package/dist/{solana-v1-operations-Bpd4TASG.mjs.map → solana-v1-operations-Dxs4XM_F.mjs.map} +1 -1
- package/dist/types.d.ts +16 -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
|
@@ -33,6 +33,8 @@ export declare class RealtimeStore {
|
|
|
33
33
|
private reconnectTimer;
|
|
34
34
|
private reconnectDelay;
|
|
35
35
|
private maxReconnectDelay;
|
|
36
|
+
/** When the current socket finished connecting; decides whether a close resets the backoff. */
|
|
37
|
+
private connectedAtMs;
|
|
36
38
|
private idbFlushTimer;
|
|
37
39
|
private idbDirtyKeys;
|
|
38
40
|
private closed;
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Reconnect backoff for RealtimeStore's WebSocket.
|
|
3
|
+
*
|
|
4
|
+
* A server that sheds load closes many sockets at once (1013). Without jitter
|
|
5
|
+
* every client comes back at the same instant; without growth on quick closes a
|
|
6
|
+
* socket that is accepted and then immediately dropped retries at the floor
|
|
7
|
+
* forever. So: a connection that stayed up for STABLE_CONNECTION_MS resets the
|
|
8
|
+
* delay, a shorter one doubles it, and every scheduled wait is jittered.
|
|
9
|
+
*/
|
|
10
|
+
export declare const RECONNECT_BASE_DELAY_MS = 1000;
|
|
11
|
+
export declare const RECONNECT_MAX_DELAY_MS = 30000;
|
|
12
|
+
export declare const STABLE_CONNECTION_MS = 10000;
|
|
13
|
+
/** Delay to use after a close, given how long the closed connection had been up (null: never connected). */
|
|
14
|
+
export declare function reconnectDelayAfterClose(currentDelayMs: number, connectedForMs: number | null, maxDelayMs?: number): number;
|
|
15
|
+
/** The wait actually scheduled: the delay scaled by a random factor in [0.5, 1.5). */
|
|
16
|
+
export declare function jitteredReconnectDelayMs(delayMs: number, random?: () => number): number;
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Idle heartbeat for the realtime WebSocket connection (subscription-v2.ts).
|
|
3
|
+
*
|
|
4
|
+
* A socket whose server has stopped delivering frames looks exactly like a
|
|
5
|
+
* quiet one: the browser reports it open and nothing arrives. The client only
|
|
6
|
+
* learns the difference by asking. Once nothing has been received for
|
|
7
|
+
* HEARTBEAT_IDLE_MS the heartbeat asks the connection to send a ping; if
|
|
8
|
+
* nothing at all arrives within HEARTBEAT_TIMEOUT_MS after that, the socket is
|
|
9
|
+
* declared dead and the connection is reconnected. Any inbound frame counts as
|
|
10
|
+
* proof of life, so a busy socket is never pinged and a pong is just another
|
|
11
|
+
* frame. The connection may decline to ping (it already has a request in
|
|
12
|
+
* flight with its own timeout); the idle wait then simply starts over.
|
|
13
|
+
*
|
|
14
|
+
* The timeout sits above both budgets a pong can wait behind: the connection's
|
|
15
|
+
* own 30 s request budget (the server handles one frame per socket at a time)
|
|
16
|
+
* and the object's 30 s CPU slice, which another socket's handler can hold
|
|
17
|
+
* while every pong waits. A healthy socket must never be declared dead
|
|
18
|
+
* within either.
|
|
19
|
+
*
|
|
20
|
+
* Pure: timers come from the clock passed in, so tests can drive it.
|
|
21
|
+
*/
|
|
22
|
+
export declare const HEARTBEAT_IDLE_MS = 25000;
|
|
23
|
+
export declare const HEARTBEAT_TIMEOUT_MS = 45000;
|
|
24
|
+
export interface HeartbeatClock {
|
|
25
|
+
setTimeout(callback: () => void, ms: number): unknown;
|
|
26
|
+
clearTimeout(handle: unknown): void;
|
|
27
|
+
}
|
|
28
|
+
export declare class SocketHeartbeat {
|
|
29
|
+
/** Send a ping now; return false to skip this round (the idle wait restarts). */
|
|
30
|
+
private readonly sendPing;
|
|
31
|
+
private readonly onDead;
|
|
32
|
+
private readonly clock;
|
|
33
|
+
private readonly idleMs;
|
|
34
|
+
private readonly timeoutMs;
|
|
35
|
+
private idleTimer;
|
|
36
|
+
private timeoutTimer;
|
|
37
|
+
private stopped;
|
|
38
|
+
constructor(
|
|
39
|
+
/** Send a ping now; return false to skip this round (the idle wait restarts). */
|
|
40
|
+
sendPing: () => boolean, onDead: () => void, clock?: HeartbeatClock, idleMs?: number, timeoutMs?: number);
|
|
41
|
+
start(): void;
|
|
42
|
+
/** An inbound frame arrived: the socket is alive, so the idle wait starts over. */
|
|
43
|
+
noteActivity(): void;
|
|
44
|
+
stop(): void;
|
|
45
|
+
private armIdle;
|
|
46
|
+
private clearTimers;
|
|
47
|
+
}
|
|
@@ -121,6 +121,21 @@ export declare function validateServerTransactionSemantics(input: {
|
|
|
121
121
|
instructions: ServerTransactionInstructionView[];
|
|
122
122
|
PublicKeyCtor: PublicKeyClass;
|
|
123
123
|
}): void;
|
|
124
|
+
/**
|
|
125
|
+
* Poofnet lane of the onchain 202. Poofnet has no chain, so there is nothing to
|
|
126
|
+
* submit and no blockhash to refresh: the worker issued an unsendable message
|
|
127
|
+
* (off-curve fee payer) that only proves this wallet approved this write. It is
|
|
128
|
+
* validated exactly like a mainnet transaction before the wallet sees it, signed
|
|
129
|
+
* by the same provider method, and returned for the worker to verify and commit.
|
|
130
|
+
*/
|
|
131
|
+
/**
|
|
132
|
+
* The fee payer of every poofnet approval message (realtime's
|
|
133
|
+
* POOFNET_VOID_FEE_PAYER): an off-curve address with no private key, so a message
|
|
134
|
+
* it pays for can never land on any cluster. Pinned here so a 202 from anything
|
|
135
|
+
* else can never get a live, broadcastable transaction signed.
|
|
136
|
+
*/
|
|
137
|
+
export declare const POOFNET_VOID_FEE_PAYER = "CAXES7Tt3KGsSw4A5knQXKKUJZWsrguu6PC7SDBL4oq2";
|
|
138
|
+
export declare function signPoofnetTransaction(serializedTransaction: string, authProvider: AuthProvider, options: SetOptions | undefined, expectedIntent: ExpectedWriteIntent): Promise<string>;
|
|
124
139
|
export declare function handlePreBuiltTransaction(tx: {
|
|
125
140
|
serializedTransaction: string;
|
|
126
141
|
blockhash: string;
|
|
@@ -28,6 +28,12 @@ export declare const SHARD_CAP = 45;
|
|
|
28
28
|
* target, far beyond any legitimate UI.
|
|
29
29
|
*/
|
|
30
30
|
export declare const MAX_SHARDS_PER_KEY = 8;
|
|
31
|
+
/**
|
|
32
|
+
* Every idle client on a stalled object hits its dead timer at about the same
|
|
33
|
+
* moment, so the replacement waits out the wrapper's own first-attempt delay
|
|
34
|
+
* with the same jitter, instead of every client reopening in one instant.
|
|
35
|
+
*/
|
|
36
|
+
export declare const HEARTBEAT_RECONNECT_DELAY_MAX_MS: number;
|
|
31
37
|
/**
|
|
32
38
|
* Subscribe to data at a path using WebSocket v2.
|
|
33
39
|
* Returns an unsubscribe function.
|
|
@@ -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-Bf2q_JO1.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-9SCt1RPf.js'); });
|
|
7308
7473
|
await reconnectRealtimeStoreWithNewAuth();
|
|
7309
7474
|
}
|
|
7310
7475
|
catch (error) {
|
|
@@ -8647,7 +8812,7 @@ async function set(path, document, options) {
|
|
|
8647
8812
|
* state appears only under `observation`/`mirrorSync` once actually observed.
|
|
8648
8813
|
*/
|
|
8649
8814
|
async function setMany(many, options) {
|
|
8650
|
-
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k
|
|
8815
|
+
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k;
|
|
8651
8816
|
// Returns the data that was set, or undefined if the document was already set.
|
|
8652
8817
|
try {
|
|
8653
8818
|
// A server WalletClient threads its immutable config snapshot; the default
|
|
@@ -8752,13 +8917,35 @@ async function setMany(many, options) {
|
|
|
8752
8917
|
throw new Error('The write response carried no execution-chain tag, so this receipt could not state where the write ran. ' +
|
|
8753
8918
|
'Nothing was signed or submitted.');
|
|
8754
8919
|
}
|
|
8920
|
+
if (workerChain === 'realtime_offchain') {
|
|
8921
|
+
// Poofnet: the wallet signs the worker's unsendable approval message and
|
|
8922
|
+
// the worker verifies and commits it. Nothing is submitted to any chain.
|
|
8923
|
+
if (typeof curTx.serializedTransaction !== 'string' || typeof curTx.poofnetIntentId !== 'string') {
|
|
8924
|
+
throw new Error('Unsupported 202 transaction shape: a poofnet write needs serializedTransaction and poofnetIntentId');
|
|
8925
|
+
}
|
|
8926
|
+
const { signPoofnetTransaction, buildExpectedWriteIntent } = await Promise.resolve().then(function () { return require('./solana-operations-eTiLgak2.js'); });
|
|
8927
|
+
const signedTransaction = await signPoofnetTransaction(curTx.serializedTransaction, authProvider, options, buildExpectedWriteIntent(documents));
|
|
8928
|
+
// Signing-only, exactly as on a real chain: signed and NOT committed.
|
|
8929
|
+
if ((options === null || options === void 0 ? void 0 : options.shouldSubmitTx) === false) {
|
|
8930
|
+
return {
|
|
8931
|
+
status: 'signed', appId, requestedDocuments: buildRequestedDocuments(documents),
|
|
8932
|
+
chain: 'realtime_offchain', transactionId: null, signedTransaction,
|
|
8933
|
+
poofnetIntentId: curTx.poofnetIntentId,
|
|
8934
|
+
};
|
|
8935
|
+
}
|
|
8936
|
+
const commitResponse = await makeApiRequest('PUT', 'items/signed', { intentId: curTx.poofnetIntentId, signedTransaction }, options === null || options === void 0 ? void 0 : options._overrides);
|
|
8937
|
+
if (commitResponse.status !== 200) {
|
|
8938
|
+
throw new Error(`Poofnet signed write was not committed (status ${commitResponse.status})`);
|
|
8939
|
+
}
|
|
8940
|
+
return directCommitResult(commitResponse, documents);
|
|
8941
|
+
}
|
|
8755
8942
|
if (curTx.serializedTransaction) {
|
|
8756
8943
|
if (!SOLANA_CHAIN_TAGS.has(workerChain)) {
|
|
8757
8944
|
throw new Error(`The write response reported execution chain "${workerChain}" for a Solana transaction. ` +
|
|
8758
8945
|
'Nothing was signed or submitted.');
|
|
8759
8946
|
}
|
|
8760
8947
|
const solanaChain = workerChain;
|
|
8761
|
-
const { handlePreBuiltTransaction, buildExpectedWriteIntent } = await Promise.resolve().then(function () { return require('./solana-operations-
|
|
8948
|
+
const { handlePreBuiltTransaction, buildExpectedWriteIntent } = await Promise.resolve().then(function () { return require('./solana-operations-eTiLgak2.js'); });
|
|
8762
8949
|
// OA-0001: derive the expected intent from OUR OWN request (the one
|
|
8763
8950
|
// ground truth a tampered builder response cannot change) so the
|
|
8764
8951
|
// validator can reject any transaction whose operation semantics
|
|
@@ -8801,26 +8988,7 @@ async function setMany(many, options) {
|
|
|
8801
8988
|
}
|
|
8802
8989
|
}
|
|
8803
8990
|
else if (setResponse.status === 200) {
|
|
8804
|
-
|
|
8805
|
-
// batch-level — `transactionId` comes from the worker's own receipt and is
|
|
8806
|
-
// NEVER lifted out of a result document (a delete returns the PREVIOUS
|
|
8807
|
-
// document, so its stale `_transaction_hash` would misreport the delete).
|
|
8808
|
-
const body = (setResponse.data && typeof setResponse.data === 'object' && !Array.isArray(setResponse.data))
|
|
8809
|
-
? setResponse.data
|
|
8810
|
-
: null;
|
|
8811
|
-
const receipt = ((body === null || body === void 0 ? void 0 : body.receipt) && typeof body.receipt === 'object' && !Array.isArray(body.receipt))
|
|
8812
|
-
? body.receipt
|
|
8813
|
-
: null;
|
|
8814
|
-
const rawTransactionId = receipt === null || receipt === void 0 ? void 0 : receipt.transactionId;
|
|
8815
|
-
return {
|
|
8816
|
-
status: 'committed',
|
|
8817
|
-
requestedDocuments: buildRequestedDocuments(documents),
|
|
8818
|
-
observation: observationFromDirectCommit(documents.map(d => d.destinationPath), receipt === null || receipt === void 0 ? void 0 : receipt.observation),
|
|
8819
|
-
// A worker predating the batch receipt reports no lane. Say so rather
|
|
8820
|
-
// than inventing one — and never discard a committed write's receipt.
|
|
8821
|
-
chain: ((_l = readWorkerChainTag(receipt)) !== null && _l !== void 0 ? _l : 'unknown'),
|
|
8822
|
-
transactionId: typeof rawTransactionId === 'string' && rawTransactionId.length > 0 ? rawTransactionId : null,
|
|
8823
|
-
};
|
|
8991
|
+
return directCommitResult(setResponse, documents);
|
|
8824
8992
|
}
|
|
8825
8993
|
else {
|
|
8826
8994
|
throw new Error(`Failed to set document: ${setResponse}`);
|
|
@@ -8830,6 +8998,32 @@ async function setMany(many, options) {
|
|
|
8830
8998
|
throw error;
|
|
8831
8999
|
}
|
|
8832
9000
|
}
|
|
9001
|
+
/**
|
|
9002
|
+
* Direct commit: the worker applied the batch itself (an offchain write, or a
|
|
9003
|
+
* poofnet onchain write after its signature verified). The receipt is batch-level
|
|
9004
|
+
* - `transactionId` comes from the worker's own receipt and is NEVER lifted out
|
|
9005
|
+
* of a result document (a delete returns the PREVIOUS document, so its stale
|
|
9006
|
+
* `_transaction_hash` would misreport the delete).
|
|
9007
|
+
*/
|
|
9008
|
+
function directCommitResult(response, documents) {
|
|
9009
|
+
var _a;
|
|
9010
|
+
const body = (response.data && typeof response.data === 'object' && !Array.isArray(response.data))
|
|
9011
|
+
? response.data
|
|
9012
|
+
: null;
|
|
9013
|
+
const receipt = ((body === null || body === void 0 ? void 0 : body.receipt) && typeof body.receipt === 'object' && !Array.isArray(body.receipt))
|
|
9014
|
+
? body.receipt
|
|
9015
|
+
: null;
|
|
9016
|
+
const rawTransactionId = receipt === null || receipt === void 0 ? void 0 : receipt.transactionId;
|
|
9017
|
+
return {
|
|
9018
|
+
status: 'committed',
|
|
9019
|
+
requestedDocuments: buildRequestedDocuments(documents),
|
|
9020
|
+
observation: observationFromDirectCommit(documents.map(d => d.destinationPath), receipt === null || receipt === void 0 ? void 0 : receipt.observation),
|
|
9021
|
+
// A worker predating the batch receipt reports no lane. Say so rather
|
|
9022
|
+
// than inventing one - and never discard a committed write's receipt.
|
|
9023
|
+
chain: ((_a = readWorkerChainTag(receipt)) !== null && _a !== void 0 ? _a : 'unknown'),
|
|
9024
|
+
transactionId: typeof rawTransactionId === 'string' && rawTransactionId.length > 0 ? rawTransactionId : null,
|
|
9025
|
+
};
|
|
9026
|
+
}
|
|
8833
9027
|
/**
|
|
8834
9028
|
* Extract the document/collection path from a cache key. Keys are
|
|
8835
9029
|
* `<appId:principal>|<path>:<...>` (H1 principal-scoped) — strip the principal
|
|
@@ -10533,7 +10727,7 @@ function toMillis(seconds) {
|
|
|
10533
10727
|
|
|
10534
10728
|
/** SIWS message creation is async already, so its onchain module can be lazy. */
|
|
10535
10729
|
async function genSolanaMessage(address, nonce) {
|
|
10536
|
-
const implementation = await Promise.resolve().then(function () { return require('./solana-message-
|
|
10730
|
+
const implementation = await Promise.resolve().then(function () { return require('./solana-message-Bf2q_JO1.js'); });
|
|
10537
10731
|
return implementation.genSolanaMessage(address, nonce);
|
|
10538
10732
|
}
|
|
10539
10733
|
|
|
@@ -11180,4 +11374,4 @@ exports.toMillis = toMillis;
|
|
|
11180
11374
|
exports.toSeconds = toSeconds;
|
|
11181
11375
|
exports.waitForSetResult = waitForSetResult;
|
|
11182
11376
|
exports.withEffects = withEffects;
|
|
11183
|
-
//# sourceMappingURL=index-
|
|
11377
|
+
//# sourceMappingURL=index-62gjQH30.js.map
|