@wix/realtime 1.0.59 → 1.0.61

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.
@@ -8,6 +8,7 @@ export declare function invalidChannel(channel: Channel): RealtimeError;
8
8
  export declare function validateChannel(channel: Channel): void;
9
9
  export declare function disconnectedError(recoverable?: boolean, message?: string): RealtimeError;
10
10
  export declare function connectError(recoverable?: boolean, message?: string): RealtimeError;
11
+ export declare function subscribeTimedOut(channel: SubscribeChannel, timeoutMs: number): RealtimeError;
11
12
  export declare function subscribeFailed(channel: SubscribeChannel, payload?: {
12
13
  status?: number;
13
14
  message?: string;
@@ -9,6 +9,7 @@ exports.invalidChannel = invalidChannel;
9
9
  exports.validateChannel = validateChannel;
10
10
  exports.disconnectedError = disconnectedError;
11
11
  exports.connectError = connectError;
12
+ exports.subscribeTimedOut = subscribeTimedOut;
12
13
  exports.subscribeFailed = subscribeFailed;
13
14
  exports.resubscribeFailed = resubscribeFailed;
14
15
  exports.subscribeError = subscribeError;
@@ -72,6 +73,15 @@ function disconnectedError(recoverable, message) {
72
73
  function connectError(recoverable, message) {
73
74
  return new types_1.RealtimeError(types_1.RealtimeErrorCode.ConnectionError, message || 'connection error', { recoverable });
74
75
  }
76
+ function subscribeTimedOut(channel, timeoutMs) {
77
+ const target = channel.resourceId
78
+ ? `"${channel.name}" / "${channel.resourceId}"`
79
+ : `"${channel.name}"`;
80
+ return new types_1.RealtimeError(types_1.RealtimeErrorCode.ConnectionError, `Subscription to ${target} did not connect within ${timeoutMs}ms. The realtime ` +
81
+ `socket has not been established — commonly a page served over http:// dialing ` +
82
+ `ws:// to a TLS-only host, or a blocked/offline network. The client keeps retrying, ` +
83
+ `so this may still succeed.`, { channel, recoverable: true });
84
+ }
75
85
  function subscriptionFailed(prefix, channel, payload) {
76
86
  let msg = `${prefix} failed`;
77
87
  if (payload?.message) {
@@ -143,6 +143,8 @@ function cleanupConnectionIfIdle(socketAppId, siteId) {
143
143
  closeConnection(socketAppId, siteId);
144
144
  }
145
145
  }
146
+ // Surface a stalled initial subscription after this long, via onSubscriptionError.
147
+ const SUBSCRIBE_TIMEOUT_MS = 10_000;
146
148
  function getOrCreateConnection(httpClient, socketAppId, siteId) {
147
149
  const key = socketKey(socketAppId, siteId);
148
150
  const existing = instanceConnections.get(key);
@@ -206,8 +208,11 @@ async function setupSubscription(httpClient, subscriptionId, channel, messageHan
206
208
  publisher: publisher?.uid ? { id: publisher.uid } : undefined,
207
209
  }, channel);
208
210
  };
211
+ // Cleared on subscribe success/failure; armed just after subscribe below.
212
+ let subscribeTimeout;
209
213
  // --- callback definitions (hoisted above subscribe for #6 race fix) ---
210
214
  function onSubscribeSucceeded(payload) {
215
+ clearTimeout(subscribeTimeout);
211
216
  subscribedChannel.off(realtime_common_1.DUPLEXER_EVENTS.subscriptionFailed, onSubscribeFailed);
212
217
  // If the subscription was removed (via unsubscribe) while waiting for
213
218
  // the duplexer to confirm, clean up the duplexer channel to avoid a
@@ -256,6 +261,7 @@ async function setupSubscription(httpClient, subscriptionId, channel, messageHan
256
261
  });
257
262
  }
258
263
  function onSubscribeFailed(payload) {
264
+ clearTimeout(subscribeTimeout);
259
265
  subscribedChannel.off(realtime_common_1.DUPLEXER_EVENTS.subscriptionSucceeded, onSubscribeSucceeded);
260
266
  const failedEntry = subscriptionManager.get(subscriptionId);
261
267
  subscriptionManager.delete(subscriptionId);
@@ -269,6 +275,14 @@ async function setupSubscription(httpClient, subscriptionId, channel, messageHan
269
275
  });
270
276
  subscribedChannel.once(realtime_common_1.DUPLEXER_EVENTS.subscriptionSucceeded, onSubscribeSucceeded);
271
277
  subscribedChannel.once(realtime_common_1.DUPLEXER_EVENTS.subscriptionFailed, onSubscribeFailed);
278
+ // If neither success nor failure fires in time, report a recoverable timeout
279
+ // but keep the subscription alive so a late connect can still succeed.
280
+ subscribeTimeout = setTimeout(() => {
281
+ const entry = subscriptionManager.get(subscriptionId);
282
+ if (entry && !entry.channelEmitter) {
283
+ entry.onSubscriptionError?.((0, errors_js_1.subscribeTimedOut)(channel, SUBSCRIBE_TIMEOUT_MS), subscriptionId);
284
+ }
285
+ }, SUBSCRIBE_TIMEOUT_MS);
272
286
  }
273
287
  catch (error) {
274
288
  const sub = subscriptionManager.get(subscriptionId);
@@ -8,6 +8,7 @@ export declare function invalidChannel(channel: Channel): RealtimeError;
8
8
  export declare function validateChannel(channel: Channel): void;
9
9
  export declare function disconnectedError(recoverable?: boolean, message?: string): RealtimeError;
10
10
  export declare function connectError(recoverable?: boolean, message?: string): RealtimeError;
11
+ export declare function subscribeTimedOut(channel: SubscribeChannel, timeoutMs: number): RealtimeError;
11
12
  export declare function subscribeFailed(channel: SubscribeChannel, payload?: {
12
13
  status?: number;
13
14
  message?: string;
@@ -58,6 +58,15 @@ export function disconnectedError(recoverable, message) {
58
58
  export function connectError(recoverable, message) {
59
59
  return new RealtimeError(RealtimeErrorCode.ConnectionError, message || 'connection error', { recoverable });
60
60
  }
61
+ export function subscribeTimedOut(channel, timeoutMs) {
62
+ const target = channel.resourceId
63
+ ? `"${channel.name}" / "${channel.resourceId}"`
64
+ : `"${channel.name}"`;
65
+ return new RealtimeError(RealtimeErrorCode.ConnectionError, `Subscription to ${target} did not connect within ${timeoutMs}ms. The realtime ` +
66
+ `socket has not been established — commonly a page served over http:// dialing ` +
67
+ `ws:// to a TLS-only host, or a blocked/offline network. The client keeps retrying, ` +
68
+ `so this may still succeed.`, { channel, recoverable: true });
69
+ }
61
70
  function subscriptionFailed(prefix, channel, payload) {
62
71
  let msg = `${prefix} failed`;
63
72
  if (payload?.message) {
@@ -3,7 +3,7 @@ import { DUPLEXER_EVENTS, MESSAGE_EVENT_NAME, getAppInfoFromAuthToken, tokenToAp
3
3
  import { v4 as uuidv4 } from 'uuid';
4
4
  import { resolveDuplexerChannelName } from '../channel-utils.js';
5
5
  import { DUPLEXER_HOST_URL } from '../constants.js';
6
- import { missingChannelOrSubscriptionId, channelMismatch, channelAppIdMismatch, unsubscribeRequiresAppId, unknownChannelAppId, disconnectedError, connectError, subscribeFailed, resubscribeFailed, subscribeError, validateChannel, } from './errors.js';
6
+ import { missingChannelOrSubscriptionId, channelMismatch, channelAppIdMismatch, unsubscribeRequiresAppId, unknownChannelAppId, disconnectedError, connectError, subscribeFailed, resubscribeFailed, subscribeError, subscribeTimedOut, validateChannel, } from './errors.js';
7
7
  import { SubscriptionManager } from './subscription-manager.js';
8
8
  function socketKey(socketAppId, siteId) {
9
9
  return `${socketAppId}:${siteId}`;
@@ -138,6 +138,8 @@ function cleanupConnectionIfIdle(socketAppId, siteId) {
138
138
  closeConnection(socketAppId, siteId);
139
139
  }
140
140
  }
141
+ // Surface a stalled initial subscription after this long, via onSubscriptionError.
142
+ const SUBSCRIBE_TIMEOUT_MS = 10_000;
141
143
  function getOrCreateConnection(httpClient, socketAppId, siteId) {
142
144
  const key = socketKey(socketAppId, siteId);
143
145
  const existing = instanceConnections.get(key);
@@ -201,8 +203,11 @@ async function setupSubscription(httpClient, subscriptionId, channel, messageHan
201
203
  publisher: publisher?.uid ? { id: publisher.uid } : undefined,
202
204
  }, channel);
203
205
  };
206
+ // Cleared on subscribe success/failure; armed just after subscribe below.
207
+ let subscribeTimeout;
204
208
  // --- callback definitions (hoisted above subscribe for #6 race fix) ---
205
209
  function onSubscribeSucceeded(payload) {
210
+ clearTimeout(subscribeTimeout);
206
211
  subscribedChannel.off(DUPLEXER_EVENTS.subscriptionFailed, onSubscribeFailed);
207
212
  // If the subscription was removed (via unsubscribe) while waiting for
208
213
  // the duplexer to confirm, clean up the duplexer channel to avoid a
@@ -251,6 +256,7 @@ async function setupSubscription(httpClient, subscriptionId, channel, messageHan
251
256
  });
252
257
  }
253
258
  function onSubscribeFailed(payload) {
259
+ clearTimeout(subscribeTimeout);
254
260
  subscribedChannel.off(DUPLEXER_EVENTS.subscriptionSucceeded, onSubscribeSucceeded);
255
261
  const failedEntry = subscriptionManager.get(subscriptionId);
256
262
  subscriptionManager.delete(subscriptionId);
@@ -264,6 +270,14 @@ async function setupSubscription(httpClient, subscriptionId, channel, messageHan
264
270
  });
265
271
  subscribedChannel.once(DUPLEXER_EVENTS.subscriptionSucceeded, onSubscribeSucceeded);
266
272
  subscribedChannel.once(DUPLEXER_EVENTS.subscriptionFailed, onSubscribeFailed);
273
+ // If neither success nor failure fires in time, report a recoverable timeout
274
+ // but keep the subscription alive so a late connect can still succeed.
275
+ subscribeTimeout = setTimeout(() => {
276
+ const entry = subscriptionManager.get(subscriptionId);
277
+ if (entry && !entry.channelEmitter) {
278
+ entry.onSubscriptionError?.(subscribeTimedOut(channel, SUBSCRIBE_TIMEOUT_MS), subscriptionId);
279
+ }
280
+ }, SUBSCRIBE_TIMEOUT_MS);
267
281
  }
268
282
  catch (error) {
269
283
  const sub = subscriptionManager.get(subscriptionId);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@wix/realtime",
3
- "version": "1.0.59",
3
+ "version": "1.0.61",
4
4
  "publishConfig": {
5
5
  "registry": "https://registry.npmjs.org/",
6
6
  "access": "public"
@@ -41,18 +41,18 @@
41
41
  "cjs"
42
42
  ],
43
43
  "dependencies": {
44
- "@wix/auto_sdk_duplexer_publisher": "^1.0.10",
45
- "@wix/auto_sdk_duplexer_realtime-permissions-provider": "^1.0.3",
46
- "@wix/duplexer-js": "^2.0.6",
47
- "@wix/realtime-common": "1.0.21",
48
- "@wix/sdk": "^1.21.4",
49
- "@wix/sdk-runtime": "^1.0.9",
50
- "@wix/sdk-types": "^1.17.5",
51
- "@wix/sdk_realtime_site": "1.0.42",
44
+ "@wix/auto_sdk_duplexer_publisher": "^1.0.12",
45
+ "@wix/auto_sdk_duplexer_realtime-permissions-provider": "^1.0.5",
46
+ "@wix/duplexer-js": "^2.0.11",
47
+ "@wix/realtime-common": "1.0.22",
48
+ "@wix/sdk": "^1.21.12",
49
+ "@wix/sdk-runtime": "^1.0.14",
50
+ "@wix/sdk-types": "^1.17.8",
51
+ "@wix/sdk_realtime_site": "1.0.43",
52
52
  "uuid": "^11.1.0"
53
53
  },
54
54
  "devDependencies": {
55
- "@wix/docs-ts-model": "^1.54.0",
55
+ "@wix/docs-ts-model": "^1.57.0",
56
56
  "typescript": "^5.7.3",
57
57
  "vitest": "^3.0.5"
58
58
  },
@@ -72,5 +72,5 @@
72
72
  "isRootPublicSdk": true
73
73
  }
74
74
  },
75
- "falconPackageHash": "8dcd02a38ba013d9e9178e133fb5311b12a9d7505e159d7db8a5516b"
75
+ "falconPackageHash": "7b67e0cc34259dcbee2e9480d36c7b75741db677d68b8020143e5bb7"
76
76
  }