@pooflabs/core 0.0.34 → 0.0.36

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
@@ -4037,6 +4037,7 @@ const WS_CONFIG = {
4037
4037
  const WS_V2_PATH = '/ws/v2';
4038
4038
  let browserReconnectHooksAttached = false;
4039
4039
  let lastBrowserTriggeredReconnectAt = 0;
4040
+ let reconnectInProgress = null;
4040
4041
  // ============ Helper Functions ============
4041
4042
  function generateSubscriptionId() {
4042
4043
  return `sub_${Date.now()}_${Math.random().toString(36).substring(2, 11)}`;
@@ -4095,10 +4096,11 @@ function scheduleTokenRefresh(connection, isServer) {
4095
4096
  return;
4096
4097
  const timeUntilExpiry = expirationTime - Date.now();
4097
4098
  if (timeUntilExpiry <= TOKEN_REFRESH_BUFFER) {
4098
- console.info('[WS v2] Token expiring soon, proactively refreshing and reconnecting');
4099
- // Refresh the token directly rather than going through getFreshAuthToken(),
4100
- // which only refreshes when the token is within 60s of expiry. We want to
4101
- // refresh as soon as we enter the buffer window to avoid unnecessary reconnects.
4099
+ console.info('[WS v2] Token expiring soon, proactively refreshing');
4100
+ // Refresh the token and store it. Do NOT reconnect — the server only
4101
+ // verifies JWT at $connect time, so the existing connection continues
4102
+ // working. The fresh token is picked up by urlProvider the next time
4103
+ // a reconnect naturally happens (network drop, keepalive timeout, etc.).
4102
4104
  try {
4103
4105
  const currentRefreshToken = await getRefreshToken(isServer);
4104
4106
  if (!currentRefreshToken) {
@@ -4108,7 +4110,7 @@ function scheduleTokenRefresh(connection, isServer) {
4108
4110
  const refreshData = await refreshSession(currentRefreshToken);
4109
4111
  if (refreshData && refreshData.idToken && refreshData.accessToken) {
4110
4112
  await updateIdTokenAndAccessToken(refreshData.idToken, refreshData.accessToken, isServer);
4111
- reconnectWithNewAuthV2();
4113
+ console.info('[WS v2] Token refreshed successfully, stored for next connection');
4112
4114
  }
4113
4115
  else {
4114
4116
  console.warn('[WS v2] Proactive token refresh returned incomplete data');
@@ -4216,8 +4218,6 @@ async function getOrCreateConnection(appId, isServer) {
4216
4218
  isConnected: false,
4217
4219
  appId,
4218
4220
  tokenRefreshTimer: null,
4219
- lastMessageAt: Date.now(),
4220
- keepaliveTimer: null,
4221
4221
  consecutiveAuthFailures: 0,
4222
4222
  };
4223
4223
  connections.set(appId, connection);
@@ -4238,8 +4238,6 @@ async function getOrCreateConnection(appId, isServer) {
4238
4238
  else {
4239
4239
  wsUrl.searchParams.append('appId', config.appId);
4240
4240
  }
4241
- // Add timestamp to prevent connection reuse issues
4242
- wsUrl.searchParams.append('_t', Date.now().toString());
4243
4241
  // Add auth token if available
4244
4242
  const authToken = await getFreshAuthToken(isServer);
4245
4243
  if (authToken) {
@@ -4271,7 +4269,6 @@ async function getOrCreateConnection(appId, isServer) {
4271
4269
  ws.addEventListener('open', () => {
4272
4270
  connection.isConnecting = false;
4273
4271
  connection.isConnected = true;
4274
- connection.lastMessageAt = Date.now();
4275
4272
  connection.consecutiveAuthFailures = 0;
4276
4273
  // Schedule periodic token freshness checks
4277
4274
  scheduleTokenRefresh(connection, isServer);
@@ -4280,25 +4277,9 @@ async function getOrCreateConnection(appId, isServer) {
4280
4277
  sub.lastData = undefined;
4281
4278
  sendSubscribe(connection, sub);
4282
4279
  }
4283
- // Start keepalive detection — if no messages for 90s, force reconnect
4284
- if (connection.keepaliveTimer) {
4285
- clearInterval(connection.keepaliveTimer);
4286
- }
4287
- connection.keepaliveTimer = setInterval(() => {
4288
- var _a;
4289
- if (Date.now() - connection.lastMessageAt > 90000) {
4290
- console.warn('[WS v2] No messages received for 90s, forcing reconnect');
4291
- if (connection.keepaliveTimer) {
4292
- clearInterval(connection.keepaliveTimer);
4293
- connection.keepaliveTimer = null;
4294
- }
4295
- (_a = connection.ws) === null || _a === void 0 ? void 0 : _a.reconnect();
4296
- }
4297
- }, 30000);
4298
4280
  });
4299
4281
  // Handle incoming messages
4300
4282
  ws.addEventListener('message', (event) => {
4301
- connection.lastMessageAt = Date.now();
4302
4283
  try {
4303
4284
  const message = JSON.parse(event.data);
4304
4285
  handleServerMessage(connection, message);
@@ -4322,10 +4303,6 @@ async function getOrCreateConnection(appId, isServer) {
4322
4303
  clearInterval(connection.tokenRefreshTimer);
4323
4304
  connection.tokenRefreshTimer = null;
4324
4305
  }
4325
- if (connection.keepaliveTimer) {
4326
- clearInterval(connection.keepaliveTimer);
4327
- connection.keepaliveTimer = null;
4328
- }
4329
4306
  });
4330
4307
  return connection;
4331
4308
  }
@@ -4579,17 +4556,19 @@ async function removeCallbackFromSubscription(connection, subscriptionId, callba
4579
4556
  async function closeAllSubscriptionsV2() {
4580
4557
  const closePromises = [];
4581
4558
  for (const [appId, connection] of connections) {
4582
- // Clear token refresh timer
4583
4559
  if (connection.tokenRefreshTimer) {
4584
4560
  clearInterval(connection.tokenRefreshTimer);
4585
4561
  connection.tokenRefreshTimer = null;
4586
4562
  }
4587
4563
  if (connection.ws) {
4564
+ const ws = connection.ws;
4565
+ connection.ws = null;
4566
+ connection.isConnected = false;
4588
4567
  closePromises.push(new Promise((resolve) => {
4589
- connection.ws.addEventListener('close', () => {
4568
+ ws.addEventListener('close', () => {
4590
4569
  resolve();
4591
4570
  });
4592
- connection.ws.close();
4571
+ ws.close();
4593
4572
  }));
4594
4573
  }
4595
4574
  connections.delete(appId);
@@ -4636,7 +4615,18 @@ function getCachedDataV2(path, prompt) {
4636
4615
  * Note: Existing subscriptions will receive new initial data after reconnection.
4637
4616
  */
4638
4617
  async function reconnectWithNewAuthV2() {
4639
- // For each active connection
4618
+ if (reconnectInProgress) {
4619
+ return reconnectInProgress;
4620
+ }
4621
+ reconnectInProgress = doReconnectWithNewAuth();
4622
+ try {
4623
+ await reconnectInProgress;
4624
+ }
4625
+ finally {
4626
+ reconnectInProgress = null;
4627
+ }
4628
+ }
4629
+ async function doReconnectWithNewAuth() {
4640
4630
  for (const [appId, connection] of connections) {
4641
4631
  if (!connection.ws) {
4642
4632
  continue;
@@ -4645,7 +4635,6 @@ async function reconnectWithNewAuthV2() {
4645
4635
  pending.reject(new Error('Connection reconnecting due to auth change'));
4646
4636
  }
4647
4637
  connection.pendingSubscriptions.clear();
4648
- // Resolve any pending unsubscriptions - connection is closing anyway
4649
4638
  for (const [, pending] of connection.pendingUnsubscriptions) {
4650
4639
  pending.resolve();
4651
4640
  }
@@ -4655,8 +4644,6 @@ async function reconnectWithNewAuthV2() {
4655
4644
  // Close the WebSocket (this triggers reconnection in ReconnectingWebSocket)
4656
4645
  // We use reconnect() which will close and re-open with fresh URL (including new token)
4657
4646
  try {
4658
- // ReconnectingWebSocket.reconnect() closes current connection and opens a new one
4659
- // The urlProvider will be called again, getting a fresh auth token
4660
4647
  connection.ws.reconnect();
4661
4648
  }
4662
4649
  catch (error) {