@atlaskit/collab-provider 39.0.2 → 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 CHANGED
@@ -1,5 +1,20 @@
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
+
12
+ ## 39.0.3
13
+
14
+ ### Patch Changes
15
+
16
+ - Updated dependencies
17
+
3
18
  ## 39.0.2
4
19
 
5
20
  ### 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
- // eslint-disable-next-line require-unicode-regexp
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
- * Copied from confluence/next/packages/graphql/src/utils/gcpDomainUtils.ts
312
- * Checks if the current domain is a GCP tenant matching the pattern: .*-cdp-{cdp-id}\.jira-dev\.com
313
- * @param {string | undefined} hostname optional hostname to evaluate
314
- * @returns {boolean} true if the domain matches the GCP tenant pattern
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
- if (!hostname) {
339
- return false;
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 GCP_TENANT_PATTERN.test(hostname);
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 presenceWebSocketFirst = false;
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
- presenceWebSocketFirst = true;
52
+ webSocketFirst = true;
53
53
  }
54
54
  break;
55
55
  case 'gcp-collab':
56
- // GCP: use WebSocket for all collab editing as well
57
- if ((0, _fg.fg)('collab_edit_via_websocket_only_for_gcp')) {
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: presenceWebSocketFirst,
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
- }, presenceWebSocketFirst ? extraHeaders : {})
86
+ }, webSocketFirst ? extraHeaders : {})
85
87
  });
86
- if (presenceWebSocketFirst) {
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 `platform_editor_presence_ws_restore_after_upgrade`, the fallback is no longer sticky and errors no longer
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 ((0, _fg.fg)('platform_editor_presence_ws_restore_after_upgrade')) {
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.1";
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
- // eslint-disable-next-line require-unicode-regexp
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
- * Copied from confluence/next/packages/graphql/src/utils/gcpDomainUtils.ts
260
- * Checks if the current domain is a GCP tenant matching the pattern: .*-cdp-{cdp-id}\.jira-dev\.com
261
- * @param {string | undefined} hostname optional hostname to evaluate
262
- * @returns {boolean} true if the domain matches the GCP tenant pattern
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
- if (!hostname) {
266
- return false;
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 GCP_TENANT_PATTERN.test(hostname);
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 presenceWebSocketFirst = false;
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
- presenceWebSocketFirst = true;
41
+ webSocketFirst = true;
42
42
  }
43
43
  break;
44
44
  case 'gcp-collab':
45
- // GCP: use WebSocket for all collab editing as well
46
- if (fg('collab_edit_via_websocket_only_for_gcp')) {
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: presenceWebSocketFirst,
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
- ...(presenceWebSocketFirst ? extraHeaders : {})
76
+ ...(webSocketFirst ? extraHeaders : {})
75
77
  }
76
78
  });
77
- if (presenceWebSocketFirst) {
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 `platform_editor_presence_ws_restore_after_upgrade`, the fallback is no longer sticky and errors no longer
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 (fg('platform_editor_presence_ws_restore_after_upgrade')) {
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);
@@ -1,5 +1,5 @@
1
1
  export const name = "@atlaskit/collab-provider";
2
- export const version = "39.0.1";
2
+ export const version = "39.0.3";
3
3
  export const nextMajorVersion = () => {
4
4
  return [Number(version.split('.')[0]) + 1, 0, 0].join('.');
5
5
  };
@@ -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
- // eslint-disable-next-line require-unicode-regexp
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
- * Copied from confluence/next/packages/graphql/src/utils/gcpDomainUtils.ts
301
- * Checks if the current domain is a GCP tenant matching the pattern: .*-cdp-{cdp-id}\.jira-dev\.com
302
- * @param {string | undefined} hostname optional hostname to evaluate
303
- * @returns {boolean} true if the domain matches the GCP tenant pattern
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
- if (!hostname) {
328
- return false;
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 GCP_TENANT_PATTERN.test(hostname);
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 presenceWebSocketFirst = false;
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
- presenceWebSocketFirst = true;
43
+ webSocketFirst = true;
44
44
  }
45
45
  break;
46
46
  case 'gcp-collab':
47
- // GCP: use WebSocket for all collab editing as well
48
- if (fg('collab_edit_via_websocket_only_for_gcp')) {
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: presenceWebSocketFirst,
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
- }, presenceWebSocketFirst ? extraHeaders : {})
77
+ }, webSocketFirst ? extraHeaders : {})
76
78
  });
77
- if (presenceWebSocketFirst) {
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 `platform_editor_presence_ws_restore_after_upgrade`, the fallback is no longer sticky and errors no longer
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 (fg('platform_editor_presence_ws_restore_after_upgrade')) {
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);
@@ -1,5 +1,5 @@
1
1
  export var name = "@atlaskit/collab-provider";
2
- export var version = "39.0.1";
2
+ export var version = "39.0.3";
3
3
  export var nextMajorVersion = function nextMajorVersion() {
4
4
  return [Number(version.split('.')[0]) + 1, 0, 0].join('.');
5
5
  };
@@ -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
- * Copied from confluence/next/packages/graphql/src/utils/gcpDomainUtils.ts
95
- * Checks if the current domain is a GCP tenant matching the pattern: .*-cdp-{cdp-id}\.jira-dev\.com
96
- * @param {string | undefined} hostname optional hostname to evaluate
97
- * @returns {boolean} true if the domain matches the GCP tenant pattern
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;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@atlaskit/collab-provider",
3
- "version": "39.0.2",
3
+ "version": "39.0.4",
4
4
  "description": "A provider for collaborative editing.",
5
5
  "publishConfig": {
6
6
  "registry": "https://registry.npmjs.org/"
@@ -30,7 +30,7 @@
30
30
  "@atlaskit/platform-feature-flags": "^2.2.0",
31
31
  "@atlaskit/prosemirror-collab": "^1.1.0",
32
32
  "@atlaskit/react-ufo": "^8.3.0",
33
- "@atlaskit/tmp-editor-statsig": "^247.0.0",
33
+ "@atlaskit/tmp-editor-statsig": "^248.0.0",
34
34
  "@atlaskit/ufo": "^2.0.0",
35
35
  "@atlaskit/util-service-support": "^7.4.0",
36
36
  "@babel/runtime": "^7.0.0",
@@ -75,6 +75,6 @@
75
75
  }
76
76
  },
77
77
  "peerDependencies": {
78
- "@atlaskit/editor-common": "^134.0.0"
78
+ "@atlaskit/editor-common": "^134.1.0"
79
79
  }
80
80
  }