@atlaskit/collab-provider 39.0.3 → 39.0.4
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/CHANGELOG.md +9 -0
- package/dist/cjs/helpers/utils.js +18 -9
- package/dist/cjs/socket-io-provider.js +13 -11
- package/dist/cjs/version-wrapper.js +1 -1
- package/dist/es2019/helpers/utils.js +14 -9
- package/dist/es2019/socket-io-provider.js +13 -11
- package/dist/es2019/version-wrapper.js +1 -1
- package/dist/esm/helpers/utils.js +18 -9
- package/dist/esm/socket-io-provider.js +13 -11
- package/dist/esm/version-wrapper.js +1 -1
- package/dist/types/helpers/utils.d.ts +5 -4
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,14 @@
|
|
|
1
1
|
# @atlaskit/collab-provider
|
|
2
2
|
|
|
3
|
+
## 39.0.4
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- [`750248fa12247`](https://bitbucket.org/atlassian/atlassian-frontend-monorepo/commits/750248fa12247) -
|
|
8
|
+
GCP collaboration connects with WebSocket first and polling fallback behind
|
|
9
|
+
platform_editor_gcp_collab_ws_first. Detect GCP using the Edge cookie, the shared cloud detector,
|
|
10
|
+
or the staging hostname.
|
|
11
|
+
|
|
3
12
|
## 39.0.3
|
|
4
13
|
|
|
5
14
|
### Patch Changes
|
|
@@ -14,12 +14,12 @@ var _regenerator = _interopRequireDefault(require("@babel/runtime/regenerator"))
|
|
|
14
14
|
var _asyncToGenerator2 = _interopRequireDefault(require("@babel/runtime/helpers/asyncToGenerator"));
|
|
15
15
|
var _defineProperty2 = _interopRequireDefault(require("@babel/runtime/helpers/defineProperty"));
|
|
16
16
|
var _scrubAdf = _interopRequireDefault(require("@atlaskit/adf-utils/scrub-adf"));
|
|
17
|
+
var _cloudProvider = require("@atlaskit/atlassian-context/cloud-provider");
|
|
17
18
|
var _prosemirrorCollab = require("@atlaskit/prosemirror-collab");
|
|
18
19
|
var _customErrors = require("../errors/custom-errors");
|
|
19
20
|
function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; }
|
|
20
21
|
function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { (0, _defineProperty2.default)(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; }
|
|
21
|
-
|
|
22
|
-
var GCP_TENANT_PATTERN = /^.*-cdp-\w+\.jira-dev\.com$/;
|
|
22
|
+
var GCP_STAGING_TENANT_PATTERN = /^(?:[\0-\t\x0B\f\x0E-\u2027\u202A-\uD7FF\uE000-\uFFFF]|[\uD800-\uDBFF][\uDC00-\uDFFF]|[\uD800-\uDBFF](?![\uDC00-\uDFFF])|(?:[^\uD800-\uDBFF]|^)[\uDC00-\uDFFF])*-cdp-[0-9A-Z_a-z]+\.jira-dev\.com$/;
|
|
23
23
|
var createLogger = exports.createLogger = function createLogger(prefix) {
|
|
24
24
|
var color = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 'blue';
|
|
25
25
|
return (
|
|
@@ -308,10 +308,11 @@ function toObfuscatedSteps(_x3) {
|
|
|
308
308
|
return _toObfuscatedSteps.apply(this, arguments);
|
|
309
309
|
}
|
|
310
310
|
/**
|
|
311
|
-
*
|
|
312
|
-
*
|
|
313
|
-
*
|
|
314
|
-
*
|
|
311
|
+
* Keep all three checks: AWS-served Bifrost responses can overwrite the shared
|
|
312
|
+
* detector's cookie to AWS even for a GCP tenant (https://hello.atlassian.net/browse/EDGE-10729).
|
|
313
|
+
* The Edge cookie covers that case; the shared detector also supports SSR, and
|
|
314
|
+
* the hostname check covers missing or unreliable cloud-provider cookies in staging.
|
|
315
|
+
* Any GCP signal is sufficient; an AWS value must not veto the remaining checks.
|
|
315
316
|
*/
|
|
316
317
|
function _toObfuscatedSteps() {
|
|
317
318
|
_toObfuscatedSteps = (0, _asyncToGenerator2.default)( /*#__PURE__*/_regenerator.default.mark(function _callee2(steps) {
|
|
@@ -335,8 +336,16 @@ function _toObfuscatedSteps() {
|
|
|
335
336
|
return _toObfuscatedSteps.apply(this, arguments);
|
|
336
337
|
}
|
|
337
338
|
function isGCPtenant(hostname) {
|
|
338
|
-
|
|
339
|
-
|
|
339
|
+
var edgeProvider;
|
|
340
|
+
try {
|
|
341
|
+
var _document$cookie$spli;
|
|
342
|
+
edgeProvider = typeof document === 'undefined' ? undefined : (_document$cookie$spli = document.cookie.split(';').map(function (cookie) {
|
|
343
|
+
return cookie.trim();
|
|
344
|
+
}).find(function (cookie) {
|
|
345
|
+
return cookie.startsWith('Atl-Ctx-Cloud-Service-Provider=');
|
|
346
|
+
})) === null || _document$cookie$spli === void 0 ? void 0 : _document$cookie$spli.split('=')[1];
|
|
347
|
+
} catch (_unused) {
|
|
348
|
+
// Cookie access can be unavailable; continue with the shared detector and staging fallback.
|
|
340
349
|
}
|
|
341
|
-
return
|
|
350
|
+
return edgeProvider === 'GCP' || (0, _cloudProvider.isGoogleCloudPlatform)() || Boolean(hostname && GCP_STAGING_TENANT_PATTERN.test(hostname));
|
|
342
351
|
}
|
|
@@ -26,7 +26,7 @@ function createSocketIOSocket(url, auth, productInfo, isPresenceOnly, analyticsH
|
|
|
26
26
|
var socketIOOptions = _config.SOCKET_IO_OPTIONS;
|
|
27
27
|
// Default: polling first, with WebSocket upgrade fallback
|
|
28
28
|
var transports = ['polling', 'websocket'];
|
|
29
|
-
var
|
|
29
|
+
var webSocketFirst = false;
|
|
30
30
|
|
|
31
31
|
// Determine transport strategy based on connection type and tenant.
|
|
32
32
|
// https://socket.io/docs/v4/client-options/#transports
|
|
@@ -49,12 +49,14 @@ function createSocketIOSocket(url, auth, productInfo, isPresenceOnly, analyticsH
|
|
|
49
49
|
socketIOOptions = _config.SOCKET_IO_OPTIONS_WITH_HIGH_JITTER;
|
|
50
50
|
if ((0, _fg.fg)('platform_editor_presence_ws_first_with_fallback')) {
|
|
51
51
|
transports = ['websocket', 'polling'];
|
|
52
|
-
|
|
52
|
+
webSocketFirst = true;
|
|
53
53
|
}
|
|
54
54
|
break;
|
|
55
55
|
case 'gcp-collab':
|
|
56
|
-
|
|
57
|
-
|
|
56
|
+
if ((0, _fg.fg)('platform_editor_gcp_collab_ws_first')) {
|
|
57
|
+
transports = ['websocket', 'polling'];
|
|
58
|
+
webSocketFirst = true;
|
|
59
|
+
} else if ((0, _fg.fg)('collab_edit_via_websocket_only_for_gcp')) {
|
|
58
60
|
transports = ['websocket'];
|
|
59
61
|
}
|
|
60
62
|
break;
|
|
@@ -75,16 +77,16 @@ function createSocketIOSocket(url, auth, productInfo, isPresenceOnly, analyticsH
|
|
|
75
77
|
withCredentials: true,
|
|
76
78
|
transports: transports,
|
|
77
79
|
// When WebSocket is rejected while opening, try polling in the same attempt
|
|
78
|
-
tryAllTransports:
|
|
80
|
+
tryAllTransports: webSocketFirst,
|
|
79
81
|
path: path ? "".concat(path, "/socket.io") : "/".concat(pathname.split('/')[1], "/socket.io"),
|
|
80
82
|
auth: auth,
|
|
81
83
|
extraHeaders: extraHeaders,
|
|
82
84
|
query: _objectSpread({
|
|
83
85
|
sourceId: documentAri === null || documentAri === void 0 ? void 0 : documentAri.split('/')[1]
|
|
84
|
-
},
|
|
86
|
+
}, webSocketFirst ? extraHeaders : {})
|
|
85
87
|
});
|
|
86
|
-
if (
|
|
87
|
-
addPollingFallback(client, url, analyticsHelper);
|
|
88
|
+
if (webSocketFirst) {
|
|
89
|
+
addPollingFallback(client, url, isPresenceOnly ? (0, _fg.fg)('platform_editor_presence_ws_restore_after_upgrade') : true, analyticsHelper);
|
|
88
90
|
}
|
|
89
91
|
return client;
|
|
90
92
|
}
|
|
@@ -98,11 +100,11 @@ var WEBSOCKET_CONNECT_TIMEOUT_MS = exports.WEBSOCKET_CONNECT_TIMEOUT_MS = 8000;
|
|
|
98
100
|
* or fails at the transport level. A server rejection (error with `data`) proves WebSocket works, so it never
|
|
99
101
|
* triggers the fallback. Once switched, all reconnects of this socket use polling-first.
|
|
100
102
|
*
|
|
101
|
-
* With `
|
|
103
|
+
* With `restoreAfterUpgrade`, the fallback is no longer sticky and errors no longer
|
|
102
104
|
* trigger it. Errors that reach `connect_error` mean polling failed too (network down), or WebSocket already opened.
|
|
103
105
|
* Once WebSocket carries data, on open or after an upgrade from polling, go back to WebSocket-first.
|
|
104
106
|
*/
|
|
105
|
-
function addPollingFallback(client, url, analyticsHelper) {
|
|
107
|
+
function addPollingFallback(client, url, restoreAfterUpgrade, analyticsHelper) {
|
|
106
108
|
var timer;
|
|
107
109
|
var webSocketWorks = false;
|
|
108
110
|
var isWebSocketFirst = function isWebSocketFirst() {
|
|
@@ -137,7 +139,7 @@ function addPollingFallback(client, url, analyticsHelper) {
|
|
|
137
139
|
};
|
|
138
140
|
startTimer();
|
|
139
141
|
client.io.on('reconnect_attempt', startTimer);
|
|
140
|
-
if (
|
|
142
|
+
if (restoreAfterUpgrade) {
|
|
141
143
|
// The engine opens when the server handshake arrives, so the auth token fetch is not timed
|
|
142
144
|
client.io.on('open', function () {
|
|
143
145
|
clearTimeout(timer);
|
|
@@ -5,7 +5,7 @@ Object.defineProperty(exports, "__esModule", {
|
|
|
5
5
|
});
|
|
6
6
|
exports.version = exports.nextMajorVersion = exports.name = void 0;
|
|
7
7
|
var name = exports.name = "@atlaskit/collab-provider";
|
|
8
|
-
var version = exports.version = "39.0.
|
|
8
|
+
var version = exports.version = "39.0.3";
|
|
9
9
|
var nextMajorVersion = exports.nextMajorVersion = function nextMajorVersion() {
|
|
10
10
|
return [Number(version.split('.')[0]) + 1, 0, 0].join('.');
|
|
11
11
|
};
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import scrubAdf from '@atlaskit/adf-utils/scrub-adf';
|
|
2
|
+
import { isGoogleCloudPlatform } from '@atlaskit/atlassian-context/cloud-provider';
|
|
2
3
|
import { sendableSteps } from '@atlaskit/prosemirror-collab';
|
|
3
4
|
import { CustomError } from '../errors/custom-errors';
|
|
4
|
-
|
|
5
|
-
const GCP_TENANT_PATTERN = /^.*-cdp-\w+\.jira-dev\.com$/;
|
|
5
|
+
const GCP_STAGING_TENANT_PATTERN = /^.*-cdp-\w+\.jira-dev\.com$/u;
|
|
6
6
|
export const createLogger = (prefix, color = 'blue') =>
|
|
7
7
|
// Ignored via go/ees005
|
|
8
8
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
@@ -256,14 +256,19 @@ export async function toObfuscatedSteps(steps) {
|
|
|
256
256
|
}
|
|
257
257
|
|
|
258
258
|
/**
|
|
259
|
-
*
|
|
260
|
-
*
|
|
261
|
-
*
|
|
262
|
-
*
|
|
259
|
+
* Keep all three checks: AWS-served Bifrost responses can overwrite the shared
|
|
260
|
+
* detector's cookie to AWS even for a GCP tenant (https://hello.atlassian.net/browse/EDGE-10729).
|
|
261
|
+
* The Edge cookie covers that case; the shared detector also supports SSR, and
|
|
262
|
+
* the hostname check covers missing or unreliable cloud-provider cookies in staging.
|
|
263
|
+
* Any GCP signal is sufficient; an AWS value must not veto the remaining checks.
|
|
263
264
|
*/
|
|
264
265
|
export function isGCPtenant(hostname) {
|
|
265
|
-
|
|
266
|
-
|
|
266
|
+
let edgeProvider;
|
|
267
|
+
try {
|
|
268
|
+
var _document$cookie$spli;
|
|
269
|
+
edgeProvider = typeof document === 'undefined' ? undefined : (_document$cookie$spli = document.cookie.split(';').map(cookie => cookie.trim()).find(cookie => cookie.startsWith('Atl-Ctx-Cloud-Service-Provider='))) === null || _document$cookie$spli === void 0 ? void 0 : _document$cookie$spli.split('=')[1];
|
|
270
|
+
} catch {
|
|
271
|
+
// Cookie access can be unavailable; continue with the shared detector and staging fallback.
|
|
267
272
|
}
|
|
268
|
-
return
|
|
273
|
+
return edgeProvider === 'GCP' || isGoogleCloudPlatform() || Boolean(hostname && GCP_STAGING_TENANT_PATTERN.test(hostname));
|
|
269
274
|
}
|
|
@@ -15,7 +15,7 @@ export function createSocketIOSocket(url, auth, productInfo, isPresenceOnly, ana
|
|
|
15
15
|
let socketIOOptions = SOCKET_IO_OPTIONS;
|
|
16
16
|
// Default: polling first, with WebSocket upgrade fallback
|
|
17
17
|
let transports = ['polling', 'websocket'];
|
|
18
|
-
let
|
|
18
|
+
let webSocketFirst = false;
|
|
19
19
|
|
|
20
20
|
// Determine transport strategy based on connection type and tenant.
|
|
21
21
|
// https://socket.io/docs/v4/client-options/#transports
|
|
@@ -38,12 +38,14 @@ export function createSocketIOSocket(url, auth, productInfo, isPresenceOnly, ana
|
|
|
38
38
|
socketIOOptions = SOCKET_IO_OPTIONS_WITH_HIGH_JITTER;
|
|
39
39
|
if (fg('platform_editor_presence_ws_first_with_fallback')) {
|
|
40
40
|
transports = ['websocket', 'polling'];
|
|
41
|
-
|
|
41
|
+
webSocketFirst = true;
|
|
42
42
|
}
|
|
43
43
|
break;
|
|
44
44
|
case 'gcp-collab':
|
|
45
|
-
|
|
46
|
-
|
|
45
|
+
if (fg('platform_editor_gcp_collab_ws_first')) {
|
|
46
|
+
transports = ['websocket', 'polling'];
|
|
47
|
+
webSocketFirst = true;
|
|
48
|
+
} else if (fg('collab_edit_via_websocket_only_for_gcp')) {
|
|
47
49
|
transports = ['websocket'];
|
|
48
50
|
}
|
|
49
51
|
break;
|
|
@@ -64,18 +66,18 @@ export function createSocketIOSocket(url, auth, productInfo, isPresenceOnly, ana
|
|
|
64
66
|
withCredentials: true,
|
|
65
67
|
transports,
|
|
66
68
|
// When WebSocket is rejected while opening, try polling in the same attempt
|
|
67
|
-
tryAllTransports:
|
|
69
|
+
tryAllTransports: webSocketFirst,
|
|
68
70
|
path: path ? `${path}/socket.io` : `/${pathname.split('/')[1]}/socket.io`,
|
|
69
71
|
auth,
|
|
70
72
|
extraHeaders,
|
|
71
73
|
query: {
|
|
72
74
|
sourceId: documentAri === null || documentAri === void 0 ? void 0 : documentAri.split('/')[1],
|
|
73
75
|
// Browsers cannot send extraHeaders on a WebSocket handshake, so the BE reads these from the query too
|
|
74
|
-
...(
|
|
76
|
+
...(webSocketFirst ? extraHeaders : {})
|
|
75
77
|
}
|
|
76
78
|
});
|
|
77
|
-
if (
|
|
78
|
-
addPollingFallback(client, url, analyticsHelper);
|
|
79
|
+
if (webSocketFirst) {
|
|
80
|
+
addPollingFallback(client, url, isPresenceOnly ? fg('platform_editor_presence_ws_restore_after_upgrade') : true, analyticsHelper);
|
|
79
81
|
}
|
|
80
82
|
return client;
|
|
81
83
|
}
|
|
@@ -89,11 +91,11 @@ export const WEBSOCKET_CONNECT_TIMEOUT_MS = 8000;
|
|
|
89
91
|
* or fails at the transport level. A server rejection (error with `data`) proves WebSocket works, so it never
|
|
90
92
|
* triggers the fallback. Once switched, all reconnects of this socket use polling-first.
|
|
91
93
|
*
|
|
92
|
-
* With `
|
|
94
|
+
* With `restoreAfterUpgrade`, the fallback is no longer sticky and errors no longer
|
|
93
95
|
* trigger it. Errors that reach `connect_error` mean polling failed too (network down), or WebSocket already opened.
|
|
94
96
|
* Once WebSocket carries data, on open or after an upgrade from polling, go back to WebSocket-first.
|
|
95
97
|
*/
|
|
96
|
-
function addPollingFallback(client, url, analyticsHelper) {
|
|
98
|
+
function addPollingFallback(client, url, restoreAfterUpgrade, analyticsHelper) {
|
|
97
99
|
let timer;
|
|
98
100
|
let webSocketWorks = false;
|
|
99
101
|
const isWebSocketFirst = () => {
|
|
@@ -126,7 +128,7 @@ function addPollingFallback(client, url, analyticsHelper) {
|
|
|
126
128
|
};
|
|
127
129
|
startTimer();
|
|
128
130
|
client.io.on('reconnect_attempt', startTimer);
|
|
129
|
-
if (
|
|
131
|
+
if (restoreAfterUpgrade) {
|
|
130
132
|
// The engine opens when the server handshake arrives, so the auth token fetch is not timed
|
|
131
133
|
client.io.on('open', () => {
|
|
132
134
|
clearTimeout(timer);
|
|
@@ -4,10 +4,10 @@ import _regeneratorRuntime from "@babel/runtime/regenerator";
|
|
|
4
4
|
function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; }
|
|
5
5
|
function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { _defineProperty(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; }
|
|
6
6
|
import scrubAdf from '@atlaskit/adf-utils/scrub-adf';
|
|
7
|
+
import { isGoogleCloudPlatform } from '@atlaskit/atlassian-context/cloud-provider';
|
|
7
8
|
import { sendableSteps } from '@atlaskit/prosemirror-collab';
|
|
8
9
|
import { CustomError } from '../errors/custom-errors';
|
|
9
|
-
|
|
10
|
-
var GCP_TENANT_PATTERN = /^.*-cdp-\w+\.jira-dev\.com$/;
|
|
10
|
+
var GCP_STAGING_TENANT_PATTERN = /^(?:[\0-\t\x0B\f\x0E-\u2027\u202A-\uD7FF\uE000-\uFFFF]|[\uD800-\uDBFF][\uDC00-\uDFFF]|[\uD800-\uDBFF](?![\uDC00-\uDFFF])|(?:[^\uD800-\uDBFF]|^)[\uDC00-\uDFFF])*-cdp-[0-9A-Z_a-z]+\.jira-dev\.com$/;
|
|
11
11
|
export var createLogger = function createLogger(prefix) {
|
|
12
12
|
var color = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 'blue';
|
|
13
13
|
return (
|
|
@@ -297,10 +297,11 @@ export function toObfuscatedSteps(_x3) {
|
|
|
297
297
|
}
|
|
298
298
|
|
|
299
299
|
/**
|
|
300
|
-
*
|
|
301
|
-
*
|
|
302
|
-
*
|
|
303
|
-
*
|
|
300
|
+
* Keep all three checks: AWS-served Bifrost responses can overwrite the shared
|
|
301
|
+
* detector's cookie to AWS even for a GCP tenant (https://hello.atlassian.net/browse/EDGE-10729).
|
|
302
|
+
* The Edge cookie covers that case; the shared detector also supports SSR, and
|
|
303
|
+
* the hostname check covers missing or unreliable cloud-provider cookies in staging.
|
|
304
|
+
* Any GCP signal is sufficient; an AWS value must not veto the remaining checks.
|
|
304
305
|
*/
|
|
305
306
|
function _toObfuscatedSteps() {
|
|
306
307
|
_toObfuscatedSteps = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee2(steps) {
|
|
@@ -324,8 +325,16 @@ function _toObfuscatedSteps() {
|
|
|
324
325
|
return _toObfuscatedSteps.apply(this, arguments);
|
|
325
326
|
}
|
|
326
327
|
export function isGCPtenant(hostname) {
|
|
327
|
-
|
|
328
|
-
|
|
328
|
+
var edgeProvider;
|
|
329
|
+
try {
|
|
330
|
+
var _document$cookie$spli;
|
|
331
|
+
edgeProvider = typeof document === 'undefined' ? undefined : (_document$cookie$spli = document.cookie.split(';').map(function (cookie) {
|
|
332
|
+
return cookie.trim();
|
|
333
|
+
}).find(function (cookie) {
|
|
334
|
+
return cookie.startsWith('Atl-Ctx-Cloud-Service-Provider=');
|
|
335
|
+
})) === null || _document$cookie$spli === void 0 ? void 0 : _document$cookie$spli.split('=')[1];
|
|
336
|
+
} catch (_unused) {
|
|
337
|
+
// Cookie access can be unavailable; continue with the shared detector and staging fallback.
|
|
329
338
|
}
|
|
330
|
-
return
|
|
339
|
+
return edgeProvider === 'GCP' || isGoogleCloudPlatform() || Boolean(hostname && GCP_STAGING_TENANT_PATTERN.test(hostname));
|
|
331
340
|
}
|
|
@@ -17,7 +17,7 @@ export function createSocketIOSocket(url, auth, productInfo, isPresenceOnly, ana
|
|
|
17
17
|
var socketIOOptions = SOCKET_IO_OPTIONS;
|
|
18
18
|
// Default: polling first, with WebSocket upgrade fallback
|
|
19
19
|
var transports = ['polling', 'websocket'];
|
|
20
|
-
var
|
|
20
|
+
var webSocketFirst = false;
|
|
21
21
|
|
|
22
22
|
// Determine transport strategy based on connection type and tenant.
|
|
23
23
|
// https://socket.io/docs/v4/client-options/#transports
|
|
@@ -40,12 +40,14 @@ export function createSocketIOSocket(url, auth, productInfo, isPresenceOnly, ana
|
|
|
40
40
|
socketIOOptions = SOCKET_IO_OPTIONS_WITH_HIGH_JITTER;
|
|
41
41
|
if (fg('platform_editor_presence_ws_first_with_fallback')) {
|
|
42
42
|
transports = ['websocket', 'polling'];
|
|
43
|
-
|
|
43
|
+
webSocketFirst = true;
|
|
44
44
|
}
|
|
45
45
|
break;
|
|
46
46
|
case 'gcp-collab':
|
|
47
|
-
|
|
48
|
-
|
|
47
|
+
if (fg('platform_editor_gcp_collab_ws_first')) {
|
|
48
|
+
transports = ['websocket', 'polling'];
|
|
49
|
+
webSocketFirst = true;
|
|
50
|
+
} else if (fg('collab_edit_via_websocket_only_for_gcp')) {
|
|
49
51
|
transports = ['websocket'];
|
|
50
52
|
}
|
|
51
53
|
break;
|
|
@@ -66,16 +68,16 @@ export function createSocketIOSocket(url, auth, productInfo, isPresenceOnly, ana
|
|
|
66
68
|
withCredentials: true,
|
|
67
69
|
transports: transports,
|
|
68
70
|
// When WebSocket is rejected while opening, try polling in the same attempt
|
|
69
|
-
tryAllTransports:
|
|
71
|
+
tryAllTransports: webSocketFirst,
|
|
70
72
|
path: path ? "".concat(path, "/socket.io") : "/".concat(pathname.split('/')[1], "/socket.io"),
|
|
71
73
|
auth: auth,
|
|
72
74
|
extraHeaders: extraHeaders,
|
|
73
75
|
query: _objectSpread({
|
|
74
76
|
sourceId: documentAri === null || documentAri === void 0 ? void 0 : documentAri.split('/')[1]
|
|
75
|
-
},
|
|
77
|
+
}, webSocketFirst ? extraHeaders : {})
|
|
76
78
|
});
|
|
77
|
-
if (
|
|
78
|
-
addPollingFallback(client, url, analyticsHelper);
|
|
79
|
+
if (webSocketFirst) {
|
|
80
|
+
addPollingFallback(client, url, isPresenceOnly ? fg('platform_editor_presence_ws_restore_after_upgrade') : true, analyticsHelper);
|
|
79
81
|
}
|
|
80
82
|
return client;
|
|
81
83
|
}
|
|
@@ -89,11 +91,11 @@ export var WEBSOCKET_CONNECT_TIMEOUT_MS = 8000;
|
|
|
89
91
|
* or fails at the transport level. A server rejection (error with `data`) proves WebSocket works, so it never
|
|
90
92
|
* triggers the fallback. Once switched, all reconnects of this socket use polling-first.
|
|
91
93
|
*
|
|
92
|
-
* With `
|
|
94
|
+
* With `restoreAfterUpgrade`, the fallback is no longer sticky and errors no longer
|
|
93
95
|
* trigger it. Errors that reach `connect_error` mean polling failed too (network down), or WebSocket already opened.
|
|
94
96
|
* Once WebSocket carries data, on open or after an upgrade from polling, go back to WebSocket-first.
|
|
95
97
|
*/
|
|
96
|
-
function addPollingFallback(client, url, analyticsHelper) {
|
|
98
|
+
function addPollingFallback(client, url, restoreAfterUpgrade, analyticsHelper) {
|
|
97
99
|
var timer;
|
|
98
100
|
var webSocketWorks = false;
|
|
99
101
|
var isWebSocketFirst = function isWebSocketFirst() {
|
|
@@ -128,7 +130,7 @@ function addPollingFallback(client, url, analyticsHelper) {
|
|
|
128
130
|
};
|
|
129
131
|
startTimer();
|
|
130
132
|
client.io.on('reconnect_attempt', startTimer);
|
|
131
|
-
if (
|
|
133
|
+
if (restoreAfterUpgrade) {
|
|
132
134
|
// The engine opens when the server handshake arrives, so the auth token fetch is not timed
|
|
133
135
|
client.io.on('open', function () {
|
|
134
136
|
clearTimeout(timer);
|
|
@@ -91,9 +91,10 @@ export declare function logObfuscatedSteps(oldState: EditorState | null, newStat
|
|
|
91
91
|
}>;
|
|
92
92
|
export declare function toObfuscatedSteps(steps: readonly ProseMirrorStep[]): Promise<string>;
|
|
93
93
|
/**
|
|
94
|
-
*
|
|
95
|
-
*
|
|
96
|
-
*
|
|
97
|
-
*
|
|
94
|
+
* Keep all three checks: AWS-served Bifrost responses can overwrite the shared
|
|
95
|
+
* detector's cookie to AWS even for a GCP tenant (https://hello.atlassian.net/browse/EDGE-10729).
|
|
96
|
+
* The Edge cookie covers that case; the shared detector also supports SSR, and
|
|
97
|
+
* the hostname check covers missing or unreliable cloud-provider cookies in staging.
|
|
98
|
+
* Any GCP signal is sufficient; an AWS value must not veto the remaining checks.
|
|
98
99
|
*/
|
|
99
100
|
export declare function isGCPtenant(hostname?: string): boolean;
|