@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.mjs CHANGED
@@ -4017,6 +4017,7 @@ const WS_CONFIG = {
4017
4017
  const WS_V2_PATH = '/ws/v2';
4018
4018
  let browserReconnectHooksAttached = false;
4019
4019
  let lastBrowserTriggeredReconnectAt = 0;
4020
+ let reconnectInProgress = null;
4020
4021
  // ============ Helper Functions ============
4021
4022
  function generateSubscriptionId() {
4022
4023
  return `sub_${Date.now()}_${Math.random().toString(36).substring(2, 11)}`;
@@ -4075,10 +4076,11 @@ function scheduleTokenRefresh(connection, isServer) {
4075
4076
  return;
4076
4077
  const timeUntilExpiry = expirationTime - Date.now();
4077
4078
  if (timeUntilExpiry <= TOKEN_REFRESH_BUFFER) {
4078
- console.info('[WS v2] Token expiring soon, proactively refreshing and reconnecting');
4079
- // Refresh the token directly rather than going through getFreshAuthToken(),
4080
- // which only refreshes when the token is within 60s of expiry. We want to
4081
- // refresh as soon as we enter the buffer window to avoid unnecessary reconnects.
4079
+ console.info('[WS v2] Token expiring soon, proactively refreshing');
4080
+ // Refresh the token and store it. Do NOT reconnect — the server only
4081
+ // verifies JWT at $connect time, so the existing connection continues
4082
+ // working. The fresh token is picked up by urlProvider the next time
4083
+ // a reconnect naturally happens (network drop, keepalive timeout, etc.).
4082
4084
  try {
4083
4085
  const currentRefreshToken = await getRefreshToken(isServer);
4084
4086
  if (!currentRefreshToken) {
@@ -4088,7 +4090,7 @@ function scheduleTokenRefresh(connection, isServer) {
4088
4090
  const refreshData = await refreshSession(currentRefreshToken);
4089
4091
  if (refreshData && refreshData.idToken && refreshData.accessToken) {
4090
4092
  await updateIdTokenAndAccessToken(refreshData.idToken, refreshData.accessToken, isServer);
4091
- reconnectWithNewAuthV2();
4093
+ console.info('[WS v2] Token refreshed successfully, stored for next connection');
4092
4094
  }
4093
4095
  else {
4094
4096
  console.warn('[WS v2] Proactive token refresh returned incomplete data');
@@ -4196,8 +4198,6 @@ async function getOrCreateConnection(appId, isServer) {
4196
4198
  isConnected: false,
4197
4199
  appId,
4198
4200
  tokenRefreshTimer: null,
4199
- lastMessageAt: Date.now(),
4200
- keepaliveTimer: null,
4201
4201
  consecutiveAuthFailures: 0,
4202
4202
  };
4203
4203
  connections.set(appId, connection);
@@ -4218,8 +4218,6 @@ async function getOrCreateConnection(appId, isServer) {
4218
4218
  else {
4219
4219
  wsUrl.searchParams.append('appId', config.appId);
4220
4220
  }
4221
- // Add timestamp to prevent connection reuse issues
4222
- wsUrl.searchParams.append('_t', Date.now().toString());
4223
4221
  // Add auth token if available
4224
4222
  const authToken = await getFreshAuthToken(isServer);
4225
4223
  if (authToken) {
@@ -4251,7 +4249,6 @@ async function getOrCreateConnection(appId, isServer) {
4251
4249
  ws.addEventListener('open', () => {
4252
4250
  connection.isConnecting = false;
4253
4251
  connection.isConnected = true;
4254
- connection.lastMessageAt = Date.now();
4255
4252
  connection.consecutiveAuthFailures = 0;
4256
4253
  // Schedule periodic token freshness checks
4257
4254
  scheduleTokenRefresh(connection, isServer);
@@ -4260,25 +4257,9 @@ async function getOrCreateConnection(appId, isServer) {
4260
4257
  sub.lastData = undefined;
4261
4258
  sendSubscribe(connection, sub);
4262
4259
  }
4263
- // Start keepalive detection — if no messages for 90s, force reconnect
4264
- if (connection.keepaliveTimer) {
4265
- clearInterval(connection.keepaliveTimer);
4266
- }
4267
- connection.keepaliveTimer = setInterval(() => {
4268
- var _a;
4269
- if (Date.now() - connection.lastMessageAt > 90000) {
4270
- console.warn('[WS v2] No messages received for 90s, forcing reconnect');
4271
- if (connection.keepaliveTimer) {
4272
- clearInterval(connection.keepaliveTimer);
4273
- connection.keepaliveTimer = null;
4274
- }
4275
- (_a = connection.ws) === null || _a === void 0 ? void 0 : _a.reconnect();
4276
- }
4277
- }, 30000);
4278
4260
  });
4279
4261
  // Handle incoming messages
4280
4262
  ws.addEventListener('message', (event) => {
4281
- connection.lastMessageAt = Date.now();
4282
4263
  try {
4283
4264
  const message = JSON.parse(event.data);
4284
4265
  handleServerMessage(connection, message);
@@ -4302,10 +4283,6 @@ async function getOrCreateConnection(appId, isServer) {
4302
4283
  clearInterval(connection.tokenRefreshTimer);
4303
4284
  connection.tokenRefreshTimer = null;
4304
4285
  }
4305
- if (connection.keepaliveTimer) {
4306
- clearInterval(connection.keepaliveTimer);
4307
- connection.keepaliveTimer = null;
4308
- }
4309
4286
  });
4310
4287
  return connection;
4311
4288
  }
@@ -4559,17 +4536,19 @@ async function removeCallbackFromSubscription(connection, subscriptionId, callba
4559
4536
  async function closeAllSubscriptionsV2() {
4560
4537
  const closePromises = [];
4561
4538
  for (const [appId, connection] of connections) {
4562
- // Clear token refresh timer
4563
4539
  if (connection.tokenRefreshTimer) {
4564
4540
  clearInterval(connection.tokenRefreshTimer);
4565
4541
  connection.tokenRefreshTimer = null;
4566
4542
  }
4567
4543
  if (connection.ws) {
4544
+ const ws = connection.ws;
4545
+ connection.ws = null;
4546
+ connection.isConnected = false;
4568
4547
  closePromises.push(new Promise((resolve) => {
4569
- connection.ws.addEventListener('close', () => {
4548
+ ws.addEventListener('close', () => {
4570
4549
  resolve();
4571
4550
  });
4572
- connection.ws.close();
4551
+ ws.close();
4573
4552
  }));
4574
4553
  }
4575
4554
  connections.delete(appId);
@@ -4616,7 +4595,18 @@ function getCachedDataV2(path, prompt) {
4616
4595
  * Note: Existing subscriptions will receive new initial data after reconnection.
4617
4596
  */
4618
4597
  async function reconnectWithNewAuthV2() {
4619
- // For each active connection
4598
+ if (reconnectInProgress) {
4599
+ return reconnectInProgress;
4600
+ }
4601
+ reconnectInProgress = doReconnectWithNewAuth();
4602
+ try {
4603
+ await reconnectInProgress;
4604
+ }
4605
+ finally {
4606
+ reconnectInProgress = null;
4607
+ }
4608
+ }
4609
+ async function doReconnectWithNewAuth() {
4620
4610
  for (const [appId, connection] of connections) {
4621
4611
  if (!connection.ws) {
4622
4612
  continue;
@@ -4625,7 +4615,6 @@ async function reconnectWithNewAuthV2() {
4625
4615
  pending.reject(new Error('Connection reconnecting due to auth change'));
4626
4616
  }
4627
4617
  connection.pendingSubscriptions.clear();
4628
- // Resolve any pending unsubscriptions - connection is closing anyway
4629
4618
  for (const [, pending] of connection.pendingUnsubscriptions) {
4630
4619
  pending.resolve();
4631
4620
  }
@@ -4635,8 +4624,6 @@ async function reconnectWithNewAuthV2() {
4635
4624
  // Close the WebSocket (this triggers reconnection in ReconnectingWebSocket)
4636
4625
  // We use reconnect() which will close and re-open with fresh URL (including new token)
4637
4626
  try {
4638
- // ReconnectingWebSocket.reconnect() closes current connection and opens a new one
4639
- // The urlProvider will be called again, getting a fresh auth token
4640
4627
  connection.ws.reconnect();
4641
4628
  }
4642
4629
  catch (error) {