@wix/realtime 1.0.58 → 1.0.60

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.
@@ -17,7 +17,8 @@ export type { PublishChannel } from '@wix/realtime-common/types';
17
17
  * import { publisher } from '@wix/realtime';
18
18
  * import { elevate } from '@wix/sdk/context';
19
19
  *
20
- * await elevate(publisher.publish)({ name: 'my-channel' }, { message: 'hello' });
20
+ * const elevatedPublish = elevate(publisher.publish);
21
+ * await elevatedPublish({ name: 'my-channel' }, { text: 'hello' });
21
22
  * ```
22
23
  *
23
24
  * @example Publish a message to the specified channel: Self-hosted
@@ -33,7 +34,7 @@ export type { PublishChannel } from '@wix/realtime-common/types';
33
34
  * // Include the auth strategy and host as relevant
34
35
  * });
35
36
  *
36
- * await client.publisher.publish({ name: 'my-channel' }, { message: 'hello' });
37
+ * await client.publisher.publish({ name: 'my-channel' }, { text: 'hello' });
37
38
  * ```
38
39
  *
39
40
  * @param channel - Channel and resource to publish to.
@@ -19,7 +19,8 @@ const rest_modules_1 = require("@wix/sdk-runtime/rest-modules");
19
19
  * import { publisher } from '@wix/realtime';
20
20
  * import { elevate } from '@wix/sdk/context';
21
21
  *
22
- * await elevate(publisher.publish)({ name: 'my-channel' }, { message: 'hello' });
22
+ * const elevatedPublish = elevate(publisher.publish);
23
+ * await elevatedPublish({ name: 'my-channel' }, { text: 'hello' });
23
24
  * ```
24
25
  *
25
26
  * @example Publish a message to the specified channel: Self-hosted
@@ -35,7 +36,7 @@ const rest_modules_1 = require("@wix/sdk-runtime/rest-modules");
35
36
  * // Include the auth strategy and host as relevant
36
37
  * });
37
38
  *
38
- * await client.publisher.publish({ name: 'my-channel' }, { message: 'hello' });
39
+ * await client.publisher.publish({ name: 'my-channel' }, { text: 'hello' });
39
40
  * ```
40
41
  *
41
42
  * @param channel - Channel and resource to publish to.
@@ -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);
@@ -125,7 +125,7 @@
125
125
  },
126
126
  {
127
127
  "kind": "code",
128
- "text": "```ts\nimport { publisher } from '@wix/realtime';\nimport { elevate } from '@wix/sdk/context';\n\nawait elevate(publisher.publish)({ name: 'my-channel' }, { message: 'hello' });\n```"
128
+ "text": "```ts\nimport { publisher } from '@wix/realtime';\nimport { elevate } from '@wix/sdk/context';\n\nconst elevatedPublish = elevate(publisher.publish);\nawait elevatedPublish({ name: 'my-channel' }, { text: 'hello' });\n```"
129
129
  }
130
130
  ]
131
131
  },
@@ -138,7 +138,7 @@
138
138
  },
139
139
  {
140
140
  "kind": "code",
141
- "text": "```ts\nimport { publisher } from '@wix/realtime';\nimport { createClient } from '@wix/sdk';\n\n// Import the auth strategy for the relevant access type\n// Import the relevant host module if needed\n\nconst client = createClient({\n modules: { publisher },\n // Include the auth strategy and host as relevant\n});\n\nawait client.publisher.publish({ name: 'my-channel' }, { message: 'hello' });\n```"
141
+ "text": "```ts\nimport { publisher } from '@wix/realtime';\nimport { createClient } from '@wix/sdk';\n\n// Import the auth strategy for the relevant access type\n// Import the relevant host module if needed\n\nconst client = createClient({\n modules: { publisher },\n // Include the auth strategy and host as relevant\n});\n\nawait client.publisher.publish({ name: 'my-channel' }, { text: 'hello' });\n```"
142
142
  }
143
143
  ]
144
144
  },
@@ -285,7 +285,7 @@
285
285
  },
286
286
  {
287
287
  "kind": "code",
288
- "text": "```ts\nimport { publisher } from '@wix/realtime';\nimport { elevate } from '@wix/sdk/context';\n\nawait elevate(publisher.publish)({ name: 'my-channel' }, { message: 'hello' });\n```"
288
+ "text": "```ts\nimport { publisher } from '@wix/realtime';\nimport { elevate } from '@wix/sdk/context';\n\nconst elevatedPublish = elevate(publisher.publish);\nawait elevatedPublish({ name: 'my-channel' }, { text: 'hello' });\n```"
289
289
  }
290
290
  ]
291
291
  },
@@ -298,7 +298,7 @@
298
298
  },
299
299
  {
300
300
  "kind": "code",
301
- "text": "```ts\nimport { publisher } from '@wix/realtime';\nimport { createClient } from '@wix/sdk';\n\n// Import the auth strategy for the relevant access type\n// Import the relevant host module if needed\n\nconst client = createClient({\n modules: { publisher },\n // Include the auth strategy and host as relevant\n});\n\nawait client.publisher.publish({ name: 'my-channel' }, { message: 'hello' });\n```"
301
+ "text": "```ts\nimport { publisher } from '@wix/realtime';\nimport { createClient } from '@wix/sdk';\n\n// Import the auth strategy for the relevant access type\n// Import the relevant host module if needed\n\nconst client = createClient({\n modules: { publisher },\n // Include the auth strategy and host as relevant\n});\n\nawait client.publisher.publish({ name: 'my-channel' }, { text: 'hello' });\n```"
302
302
  }
303
303
  ]
304
304
  },
@@ -17,7 +17,8 @@ export type { PublishChannel } from '@wix/realtime-common/types';
17
17
  * import { publisher } from '@wix/realtime';
18
18
  * import { elevate } from '@wix/sdk/context';
19
19
  *
20
- * await elevate(publisher.publish)({ name: 'my-channel' }, { message: 'hello' });
20
+ * const elevatedPublish = elevate(publisher.publish);
21
+ * await elevatedPublish({ name: 'my-channel' }, { text: 'hello' });
21
22
  * ```
22
23
  *
23
24
  * @example Publish a message to the specified channel: Self-hosted
@@ -33,7 +34,7 @@ export type { PublishChannel } from '@wix/realtime-common/types';
33
34
  * // Include the auth strategy and host as relevant
34
35
  * });
35
36
  *
36
- * await client.publisher.publish({ name: 'my-channel' }, { message: 'hello' });
37
+ * await client.publisher.publish({ name: 'my-channel' }, { text: 'hello' });
37
38
  * ```
38
39
  *
39
40
  * @param channel - Channel and resource to publish to.
@@ -16,7 +16,8 @@ import { createRESTModule } from '@wix/sdk-runtime/rest-modules';
16
16
  * import { publisher } from '@wix/realtime';
17
17
  * import { elevate } from '@wix/sdk/context';
18
18
  *
19
- * await elevate(publisher.publish)({ name: 'my-channel' }, { message: 'hello' });
19
+ * const elevatedPublish = elevate(publisher.publish);
20
+ * await elevatedPublish({ name: 'my-channel' }, { text: 'hello' });
20
21
  * ```
21
22
  *
22
23
  * @example Publish a message to the specified channel: Self-hosted
@@ -32,7 +33,7 @@ import { createRESTModule } from '@wix/sdk-runtime/rest-modules';
32
33
  * // Include the auth strategy and host as relevant
33
34
  * });
34
35
  *
35
- * await client.publisher.publish({ name: 'my-channel' }, { message: 'hello' });
36
+ * await client.publisher.publish({ name: 'my-channel' }, { text: 'hello' });
36
37
  * ```
37
38
  *
38
39
  * @param channel - Channel and resource to publish to.
@@ -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.58",
3
+ "version": "1.0.60",
4
4
  "publishConfig": {
5
5
  "registry": "https://registry.npmjs.org/",
6
6
  "access": "public"
@@ -43,7 +43,7 @@
43
43
  "dependencies": {
44
44
  "@wix/auto_sdk_duplexer_publisher": "^1.0.10",
45
45
  "@wix/auto_sdk_duplexer_realtime-permissions-provider": "^1.0.3",
46
- "@wix/duplexer-js": "^2.0.6",
46
+ "@wix/duplexer-js": "^2.0.11",
47
47
  "@wix/realtime-common": "1.0.21",
48
48
  "@wix/sdk": "^1.21.4",
49
49
  "@wix/sdk-runtime": "^1.0.9",
@@ -72,5 +72,5 @@
72
72
  "isRootPublicSdk": true
73
73
  }
74
74
  },
75
- "falconPackageHash": "c267e028556d885cfdf1aaf4198bd6fa301f290af81fbbb5b9575dbc"
75
+ "falconPackageHash": "f348b697ba9d7cf1e635ee58ddc37776af77932d9faee97e4b637d5d"
76
76
  }