@atlaskit/editor-synced-block-provider 8.1.9 → 8.2.0
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 +27 -0
- package/dist/cjs/common/types.js +63 -2
- package/dist/cjs/hooks/useFetchSyncBlockData.js +48 -14
- package/dist/cjs/store-manager/referenceSyncBlockStoreManager.js +33 -4
- package/dist/cjs/store-manager/syncBlockBatchFetcher.js +27 -0
- package/dist/cjs/store-manager/syncBlockSubscriptionManager.js +46 -11
- package/dist/es2019/common/types.js +47 -1
- package/dist/es2019/hooks/useFetchSyncBlockData.js +36 -3
- package/dist/es2019/store-manager/referenceSyncBlockStoreManager.js +26 -2
- package/dist/es2019/store-manager/syncBlockBatchFetcher.js +25 -0
- package/dist/es2019/store-manager/syncBlockSubscriptionManager.js +41 -11
- package/dist/esm/common/types.js +62 -1
- package/dist/esm/hooks/useFetchSyncBlockData.js +49 -15
- package/dist/esm/store-manager/referenceSyncBlockStoreManager.js +34 -5
- package/dist/esm/store-manager/syncBlockBatchFetcher.js +27 -0
- package/dist/esm/store-manager/syncBlockSubscriptionManager.js +46 -11
- package/dist/types/common/types.d.ts +29 -0
- package/dist/types/store-manager/referenceSyncBlockStoreManager.d.ts +8 -0
- package/dist/types/store-manager/syncBlockBatchFetcher.d.ts +7 -0
- package/dist/types/store-manager/syncBlockSubscriptionManager.d.ts +4 -5
- package/package.json +2 -2
|
@@ -2,7 +2,7 @@ import _defineProperty from "@babel/runtime/helpers/defineProperty";
|
|
|
2
2
|
import isEqual from 'lodash/isEqual';
|
|
3
3
|
import { logException } from '@atlaskit/editor-common/monitoring';
|
|
4
4
|
import { fg } from '@atlaskit/platform-feature-flags';
|
|
5
|
-
import { SyncBlockError } from '../common/types';
|
|
5
|
+
import { isProviderNotReadyError, ProviderNotReadyError, SyncBlockError } from '../common/types';
|
|
6
6
|
import { buildErrorAttribution, cacheDeletionForcedPayload, fetchErrorPayload, fetchSuccessPayload, getSourceInfoErrorPayload, sourceInfoOrphanedPayload, updateReferenceErrorPayload } from '../utils/errorHandling';
|
|
7
7
|
import { getFetchExperience, getFetchSourceInfoExperience, getSaveReferenceExperience } from '../utils/experienceTracking';
|
|
8
8
|
import { resolveSyncBlockInstance } from '../utils/resolveSyncBlockInstance';
|
|
@@ -79,7 +79,9 @@ export class ReferenceSyncBlockStoreManager {
|
|
|
79
79
|
this._batchFetcher = new SyncBlockBatchFetcher({
|
|
80
80
|
getSubscriptions: () => this._subscriptionManager.getSubscriptions(),
|
|
81
81
|
fetchSyncBlocksData: nodes => this.fetchSyncBlocksData(nodes),
|
|
82
|
-
getFireAnalyticsEvent: () => this.fireAnalyticsEvent
|
|
82
|
+
getFireAnalyticsEvent: () => this.fireAnalyticsEvent,
|
|
83
|
+
// EDITOR-7860: skip + re-queue fetches while not ready / torn down.
|
|
84
|
+
isProviderReady: () => this.hasDataProvider()
|
|
83
85
|
});
|
|
84
86
|
|
|
85
87
|
// The provider might have SSR data cache already set, so we need to update the cache in session memory storage
|
|
@@ -89,6 +91,17 @@ export class ReferenceSyncBlockStoreManager {
|
|
|
89
91
|
return node.type.name === 'syncBlock';
|
|
90
92
|
}
|
|
91
93
|
|
|
94
|
+
/**
|
|
95
|
+
* Whether the async data provider is wired and the manager is not torn down.
|
|
96
|
+
* Consumers gate fetch/subscribe on this to avoid a false `Data provider not
|
|
97
|
+
* set` fetch error (EDITOR-7860). The `isDestroyed` check covers managers
|
|
98
|
+
* orphaned mid-flight during an async provider swap, where `dataProvider`
|
|
99
|
+
* alone is insufficient.
|
|
100
|
+
*/
|
|
101
|
+
hasDataProvider() {
|
|
102
|
+
return !this.isDestroyed && !!this.dataProvider;
|
|
103
|
+
}
|
|
104
|
+
|
|
92
105
|
/**
|
|
93
106
|
* Enables or disables real-time GraphQL subscriptions for block updates.
|
|
94
107
|
* When enabled, the store manager will subscribe to real-time updates
|
|
@@ -412,6 +425,11 @@ export class ReferenceSyncBlockStoreManager {
|
|
|
412
425
|
return;
|
|
413
426
|
}
|
|
414
427
|
if (!this.dataProvider) {
|
|
428
|
+
// EDITOR-7860: tag the throw so catch sites can suppress the benign
|
|
429
|
+
// not-ready/torn-down case. Gate-off keeps the legacy throw + message.
|
|
430
|
+
if (fg('platform_editor_blocks_patch_3')) {
|
|
431
|
+
throw new ProviderNotReadyError();
|
|
432
|
+
}
|
|
415
433
|
throw new Error('Data provider not set');
|
|
416
434
|
}
|
|
417
435
|
nodesToFetch.forEach(node => {
|
|
@@ -779,6 +797,12 @@ export class ReferenceSyncBlockStoreManager {
|
|
|
779
797
|
return this._subscriptionManager.subscribeToSyncBlock(resourceId, localId, callback);
|
|
780
798
|
} catch (error) {
|
|
781
799
|
var _this$fireAnalyticsEv11;
|
|
800
|
+
// EDITOR-7860: benign not-ready/torn-down case — suppress both the
|
|
801
|
+
// exception-tracker log and the analytics event (checked first so the
|
|
802
|
+
// benign case stays fully silent). Gate-off behaviour is unchanged.
|
|
803
|
+
if (isProviderNotReadyError(error) && fg('platform_editor_blocks_patch_3')) {
|
|
804
|
+
return () => {};
|
|
805
|
+
}
|
|
782
806
|
logException(error, {
|
|
783
807
|
location: 'editor-synced-block-provider/referenceSyncBlockStoreManager'
|
|
784
808
|
});
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import _defineProperty from "@babel/runtime/helpers/defineProperty";
|
|
2
2
|
import rafSchedule from 'raf-schd';
|
|
3
3
|
import { logException } from '@atlaskit/editor-common/monitoring';
|
|
4
|
+
import { fg } from '@atlaskit/platform-feature-flags';
|
|
5
|
+
import { isProviderNotReadyError } from '../common/types';
|
|
4
6
|
import { fetchErrorPayload } from '../utils/errorHandling';
|
|
5
7
|
import { createSyncBlockNode, getSourceProductFromResourceIdSafe } from '../utils/utils';
|
|
6
8
|
/**
|
|
@@ -21,6 +23,17 @@ export class SyncBlockBatchFetcher {
|
|
|
21
23
|
if (this.pendingFetchRequests.size === 0) {
|
|
22
24
|
return;
|
|
23
25
|
}
|
|
26
|
+
|
|
27
|
+
// EDITOR-7860: not ready — skip and leave resourceIds queued for the
|
|
28
|
+
// next batch once the provider resolves. Gate-off is unchanged (the
|
|
29
|
+
// readiness check is nested under the gate so it is not consulted when
|
|
30
|
+
// the gate is off, and `fg()` stays a standalone condition so gate
|
|
31
|
+
// exposure is still tracked — satisfies @atlaskit/platform/no-preconditioning).
|
|
32
|
+
if (fg('platform_editor_blocks_patch_3')) {
|
|
33
|
+
if (this.deps.isProviderReady && !this.deps.isProviderReady()) {
|
|
34
|
+
return;
|
|
35
|
+
}
|
|
36
|
+
}
|
|
24
37
|
const resourceIds = Array.from(this.pendingFetchRequests);
|
|
25
38
|
const syncBlockNodes = resourceIds.map(resId => {
|
|
26
39
|
const subscriptions = this.deps.getSubscriptions().get(resId) || {};
|
|
@@ -32,6 +45,18 @@ export class SyncBlockBatchFetcher {
|
|
|
32
45
|
// Track in-flight before the fetch so guards remain positive.
|
|
33
46
|
resourceIds.forEach(resId => this.inFlightFetches.add(resId));
|
|
34
47
|
this.deps.fetchSyncBlocksData(syncBlockNodes).catch(error => {
|
|
48
|
+
// EDITOR-7860: benign not-ready throw — re-queue for retry and emit
|
|
49
|
+
// nothing (no analytics, no exception-tracker noise). Checked before
|
|
50
|
+
// `logException` so the benign case stays silent. Re-schedule so the
|
|
51
|
+
// re-queued IDs are retried on the next frame even if no further
|
|
52
|
+
// `queueFetch()` arrives. Gate-off behaviour is unchanged.
|
|
53
|
+
if (isProviderNotReadyError(error) && fg('platform_editor_blocks_patch_3')) {
|
|
54
|
+
resourceIds.forEach(resId => this.pendingFetchRequests.add(resId));
|
|
55
|
+
if (!this.isDestroyed) {
|
|
56
|
+
this.scheduledBatchFetch();
|
|
57
|
+
}
|
|
58
|
+
return;
|
|
59
|
+
}
|
|
35
60
|
logException(error, {
|
|
36
61
|
location: 'editor-synced-block-provider/syncBlockBatchFetcher/batchedFetchSyncBlocks'
|
|
37
62
|
});
|
|
@@ -12,6 +12,25 @@ import { getSourceProductFromResourceIdSafe } from '../utils/utils';
|
|
|
12
12
|
*/
|
|
13
13
|
|
|
14
14
|
export class SyncBlockSubscriptionManager {
|
|
15
|
+
// EDITOR-7861: higher ceiling lets transient WS-gateway drops self-heal
|
|
16
|
+
// before a terminal failure is surfaced.
|
|
17
|
+
getMaxRetryAttempts() {
|
|
18
|
+
return fg('platform_editor_blocks_patch_3') ? SyncBlockSubscriptionManager.MAX_RETRY_ATTEMPTS_HARDENED : SyncBlockSubscriptionManager.MAX_RETRY_ATTEMPTS;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
// Backoff delay for the given attempt.
|
|
22
|
+
// Gate OFF: pure exponential (1s, 2s, 4s, 8s, 16s).
|
|
23
|
+
// Gate ON (EDITOR-7861): exponential capped at MAX_RETRY_DELAY_MS with equal
|
|
24
|
+
// jitter (capped/2 + random*capped/2) — de-synchronises simultaneous
|
|
25
|
+
// reconnects while guaranteeing a non-zero delay (full jitter could hit 0).
|
|
26
|
+
getReconnectionDelay(attempts) {
|
|
27
|
+
const exponential = SyncBlockSubscriptionManager.INITIAL_RETRY_DELAY_MS * Math.pow(SyncBlockSubscriptionManager.RETRY_BACKOFF_MULTIPLIER, attempts);
|
|
28
|
+
if (!fg('platform_editor_blocks_patch_3')) {
|
|
29
|
+
return exponential;
|
|
30
|
+
}
|
|
31
|
+
const half = Math.min(exponential, SyncBlockSubscriptionManager.MAX_RETRY_DELAY_MS) / 2;
|
|
32
|
+
return Math.round(half + Math.random() * half);
|
|
33
|
+
}
|
|
15
34
|
constructor(deps) {
|
|
16
35
|
_defineProperty(this, "subscriptions", new Map());
|
|
17
36
|
_defineProperty(this, "titleSubscriptions", new Map());
|
|
@@ -23,6 +42,7 @@ export class SyncBlockSubscriptionManager {
|
|
|
23
42
|
// causing the cache to be deleted prematurely. We delay deletion to allow
|
|
24
43
|
// the new component to subscribe and cancel the pending deletion.
|
|
25
44
|
_defineProperty(this, "pendingCacheDeletions", new Map());
|
|
45
|
+
// backoff cap (gate ON)
|
|
26
46
|
_defineProperty(this, "retryAttempts", new Map());
|
|
27
47
|
_defineProperty(this, "pendingRetries", new Map());
|
|
28
48
|
this.deps = deps;
|
|
@@ -237,11 +257,17 @@ export class SyncBlockSubscriptionManager {
|
|
|
237
257
|
const unsubscribe = dataProvider.subscribeToBlockUpdates(resourceId, syncBlockInstance => {
|
|
238
258
|
this.handleGraphQLUpdate(syncBlockInstance);
|
|
239
259
|
}, error => {
|
|
240
|
-
var _this$deps$getFireAna2;
|
|
241
260
|
logException(error, {
|
|
242
261
|
location: 'editor-synced-block-provider/syncBlockSubscriptionManager/graphql-subscription'
|
|
243
262
|
});
|
|
244
|
-
|
|
263
|
+
// EDITOR-7861: a single socket drop is usually transient and
|
|
264
|
+
// recovers on reconnect, so under the gate we don't fire a
|
|
265
|
+
// user-facing error here — it's only surfaced on exhaustion (see
|
|
266
|
+
// scheduleReconnection). Gate OFF keeps the legacy fire-on-drop.
|
|
267
|
+
if (!fg('platform_editor_blocks_patch_3')) {
|
|
268
|
+
var _this$deps$getFireAna2;
|
|
269
|
+
(_this$deps$getFireAna2 = this.deps.getFireAnalyticsEvent()) === null || _this$deps$getFireAna2 === void 0 ? void 0 : _this$deps$getFireAna2(fetchErrorPayload(error.message, resourceId, getSourceProductFromResourceIdSafe(resourceId)));
|
|
270
|
+
}
|
|
245
271
|
this.handleSubscriptionTerminated(resourceId);
|
|
246
272
|
}, () => {
|
|
247
273
|
this.handleSubscriptionTerminated(resourceId);
|
|
@@ -270,16 +296,16 @@ export class SyncBlockSubscriptionManager {
|
|
|
270
296
|
}
|
|
271
297
|
}
|
|
272
298
|
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
* Delay = INITIAL_RETRY_DELAY_MS * (RETRY_BACKOFF_MULTIPLIER ^ attempts)
|
|
276
|
-
* e.g. 1s, 2s, 4s, 8s, 16s
|
|
277
|
-
*/
|
|
299
|
+
// Schedules a reconnection with backoff (see getMaxRetryAttempts /
|
|
300
|
+
// getReconnectionDelay for the gated behaviour).
|
|
278
301
|
scheduleReconnection(resourceId) {
|
|
279
302
|
var _this$retryAttempts$g;
|
|
280
303
|
const attempts = (_this$retryAttempts$g = this.retryAttempts.get(resourceId)) !== null && _this$retryAttempts$g !== void 0 ? _this$retryAttempts$g : 0;
|
|
281
|
-
|
|
304
|
+
const maxAttempts = this.getMaxRetryAttempts();
|
|
305
|
+
if (attempts >= maxAttempts) {
|
|
282
306
|
var _this$deps$getFireAna3;
|
|
307
|
+
// Exhausted all attempts — the only place a WS drop surfaces as a
|
|
308
|
+
// fetch error under the gate (EDITOR-7861).
|
|
283
309
|
const errorMessage = `Subscription reconnection failed after ${attempts} attempts`;
|
|
284
310
|
logException(new Error(errorMessage), {
|
|
285
311
|
location: 'editor-synced-block-provider/syncBlockSubscriptionManager/max-retries-exhausted'
|
|
@@ -287,7 +313,7 @@ export class SyncBlockSubscriptionManager {
|
|
|
287
313
|
(_this$deps$getFireAna3 = this.deps.getFireAnalyticsEvent()) === null || _this$deps$getFireAna3 === void 0 ? void 0 : _this$deps$getFireAna3(fetchErrorPayload(errorMessage, resourceId, getSourceProductFromResourceIdSafe(resourceId)));
|
|
288
314
|
return;
|
|
289
315
|
}
|
|
290
|
-
const delay =
|
|
316
|
+
const delay = this.getReconnectionDelay(attempts);
|
|
291
317
|
const timer = setTimeout(() => {
|
|
292
318
|
this.pendingRetries.delete(resourceId);
|
|
293
319
|
|
|
@@ -389,7 +415,11 @@ export class SyncBlockSubscriptionManager {
|
|
|
389
415
|
}
|
|
390
416
|
}
|
|
391
417
|
}
|
|
392
|
-
// Reconnection with exponential backoff
|
|
418
|
+
// Reconnection with exponential backoff.
|
|
393
419
|
_defineProperty(SyncBlockSubscriptionManager, "INITIAL_RETRY_DELAY_MS", 1000);
|
|
394
420
|
_defineProperty(SyncBlockSubscriptionManager, "RETRY_BACKOFF_MULTIPLIER", 2);
|
|
395
|
-
_defineProperty(SyncBlockSubscriptionManager, "MAX_RETRY_ATTEMPTS", 5);
|
|
421
|
+
_defineProperty(SyncBlockSubscriptionManager, "MAX_RETRY_ATTEMPTS", 5);
|
|
422
|
+
// legacy (gate OFF)
|
|
423
|
+
_defineProperty(SyncBlockSubscriptionManager, "MAX_RETRY_ATTEMPTS_HARDENED", 8);
|
|
424
|
+
// gate ON (EDITOR-7861)
|
|
425
|
+
_defineProperty(SyncBlockSubscriptionManager, "MAX_RETRY_DELAY_MS", 30000);
|
package/dist/esm/common/types.js
CHANGED
|
@@ -1,3 +1,13 @@
|
|
|
1
|
+
import _typeof from "@babel/runtime/helpers/typeof";
|
|
2
|
+
import _createClass from "@babel/runtime/helpers/createClass";
|
|
3
|
+
import _classCallCheck from "@babel/runtime/helpers/classCallCheck";
|
|
4
|
+
import _possibleConstructorReturn from "@babel/runtime/helpers/possibleConstructorReturn";
|
|
5
|
+
import _getPrototypeOf from "@babel/runtime/helpers/getPrototypeOf";
|
|
6
|
+
import _inherits from "@babel/runtime/helpers/inherits";
|
|
7
|
+
import _wrapNativeSuper from "@babel/runtime/helpers/wrapNativeSuper";
|
|
8
|
+
import _defineProperty from "@babel/runtime/helpers/defineProperty";
|
|
9
|
+
function _callSuper(t, o, e) { return o = _getPrototypeOf(o), _possibleConstructorReturn(t, _isNativeReflectConstruct() ? Reflect.construct(o, e || [], _getPrototypeOf(t).constructor) : o.apply(t, e)); }
|
|
10
|
+
function _isNativeReflectConstruct() { try { var t = !Boolean.prototype.valueOf.call(Reflect.construct(Boolean, [], function () {})); } catch (t) {} return (_isNativeReflectConstruct = function _isNativeReflectConstruct() { return !!t; })(); }
|
|
1
11
|
export var SyncBlockError = /*#__PURE__*/function (SyncBlockError) {
|
|
2
12
|
SyncBlockError["Errored"] = "errored";
|
|
3
13
|
SyncBlockError["NotFound"] = "not_found";
|
|
@@ -16,4 +26,55 @@ export var SyncBlockError = /*#__PURE__*/function (SyncBlockError) {
|
|
|
16
26
|
// block does not exist on this site (e.g. cross-site reference or hard deleted)
|
|
17
27
|
SyncBlockError["EntityNotFound"] = "entity_not_found";
|
|
18
28
|
return SyncBlockError;
|
|
19
|
-
}({});
|
|
29
|
+
}({});
|
|
30
|
+
/**
|
|
31
|
+
* Helpers to distinguish "data provider not ready / torn down" from a genuine
|
|
32
|
+
* fetch/subscribe failure (EDITOR-7860). On Jira the provider is wired
|
|
33
|
+
* asynchronously and `destroy()` nulls it on orphaned managers, so queued/
|
|
34
|
+
* in-flight ops throw `Data provider not set` — previously mis-logged as a real
|
|
35
|
+
* error. These let throw and catch sites agree on one non-string-matched signal
|
|
36
|
+
* so the residual false errors are suppressed. Gated by
|
|
37
|
+
* `platform_editor_blocks_patch_3`.
|
|
38
|
+
*
|
|
39
|
+
* NB: these intentionally live here rather than in a dedicated module to avoid
|
|
40
|
+
* adding a downstream file to consuming Jira packages' Thunderstone complexity.
|
|
41
|
+
*/
|
|
42
|
+
|
|
43
|
+
/** Legacy message — kept identical for gate-off and historical events. */
|
|
44
|
+
export var PROVIDER_NOT_READY_MESSAGE = 'Data provider not set';
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Thrown when a fetch/subscribe runs against a manager whose provider is not
|
|
48
|
+
* (yet) available or torn down. Keeps the legacy message + name so string
|
|
49
|
+
* matchers still work, while new code uses {@link isProviderNotReadyError}.
|
|
50
|
+
*/
|
|
51
|
+
export var ProviderNotReadyError = /*#__PURE__*/function (_Error) {
|
|
52
|
+
function ProviderNotReadyError() {
|
|
53
|
+
var _this;
|
|
54
|
+
var message = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : PROVIDER_NOT_READY_MESSAGE;
|
|
55
|
+
_classCallCheck(this, ProviderNotReadyError);
|
|
56
|
+
_this = _callSuper(this, ProviderNotReadyError, [message]);
|
|
57
|
+
_defineProperty(_this, "isProviderNotReadyError", true);
|
|
58
|
+
_this.name = 'ProviderNotReadyError';
|
|
59
|
+
// Restore prototype chain so instanceof works after transpilation.
|
|
60
|
+
Object.setPrototypeOf(_this, ProviderNotReadyError.prototype);
|
|
61
|
+
return _this;
|
|
62
|
+
}
|
|
63
|
+
_inherits(ProviderNotReadyError, _Error);
|
|
64
|
+
return _createClass(ProviderNotReadyError);
|
|
65
|
+
}( /*#__PURE__*/_wrapNativeSuper(Error));
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* True when the value is a "provider not ready / torn down" condition, not a
|
|
69
|
+
* genuine failure. Recognises the tagged {@link ProviderNotReadyError} and the
|
|
70
|
+
* legacy message (for errors thrown before rollout).
|
|
71
|
+
*/
|
|
72
|
+
export var isProviderNotReadyError = function isProviderNotReadyError(error) {
|
|
73
|
+
if (error instanceof ProviderNotReadyError) {
|
|
74
|
+
return true;
|
|
75
|
+
}
|
|
76
|
+
if (_typeof(error) === 'object' && error !== null && error.isProviderNotReadyError === true) {
|
|
77
|
+
return true;
|
|
78
|
+
}
|
|
79
|
+
return error instanceof Error && error.message === PROVIDER_NOT_READY_MESSAGE;
|
|
80
|
+
};
|
|
@@ -7,10 +7,12 @@ function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t =
|
|
|
7
7
|
import { useCallback, useEffect, useMemo, useState } from 'react';
|
|
8
8
|
import { isSSR } from '@atlaskit/editor-common/core-utils';
|
|
9
9
|
import { logException } from '@atlaskit/editor-common/monitoring';
|
|
10
|
-
import {
|
|
10
|
+
import { fg } from '@atlaskit/platform-feature-flags';
|
|
11
|
+
import { isProviderNotReadyError, SyncBlockError } from '../common/types';
|
|
11
12
|
import { fetchErrorPayload } from '../utils/errorHandling';
|
|
12
13
|
import { createSyncBlockNode, getSourceProductFromResourceIdSafe } from '../utils/utils';
|
|
13
14
|
export var useFetchSyncBlockData = function useFetchSyncBlockData(manager, resourceId, localId, fireAnalyticsEvent) {
|
|
15
|
+
var _manager$referenceMan2, _manager$referenceMan3, _manager$referenceMan4;
|
|
14
16
|
// Initialize both states from a single cache lookup to avoid race conditions.
|
|
15
17
|
// When a block is moved/remounted, the old component's cleanup may clear the cache
|
|
16
18
|
// before or after the new component mounts. By doing a single lookup, we ensure
|
|
@@ -34,6 +36,14 @@ export var useFetchSyncBlockData = function useFetchSyncBlockData(manager, resou
|
|
|
34
36
|
syncBlockInstance = _useState2$.syncBlockInstance,
|
|
35
37
|
isLoading = _useState2$.isLoading,
|
|
36
38
|
setFetchState = _useState2[1];
|
|
39
|
+
|
|
40
|
+
// On Jira the data provider is wired asynchronously, so the manager can be
|
|
41
|
+
// constructed with `dataProvider === undefined`. Fetching/subscribing in that
|
|
42
|
+
// window throws `Data provider not set`, logged as a false fetch error
|
|
43
|
+
// (EDITOR-7860). Gate on readiness; once the provider resolves a new manager
|
|
44
|
+
// instance is created so `referenceManager` changes identity and the effect
|
|
45
|
+
// below re-runs, subscribing exactly once.
|
|
46
|
+
var isDataProviderReady = fg('platform_editor_blocks_patch_3') ? (_manager$referenceMan2 = (_manager$referenceMan3 = (_manager$referenceMan4 = manager.referenceManager).hasDataProvider) === null || _manager$referenceMan3 === void 0 ? void 0 : _manager$referenceMan3.call(_manager$referenceMan4)) !== null && _manager$referenceMan2 !== void 0 ? _manager$referenceMan2 : false : true;
|
|
37
47
|
var reloadData = useCallback( /*#__PURE__*/_asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee() {
|
|
38
48
|
var syncBlockNode, _t;
|
|
39
49
|
return _regeneratorRuntime.wrap(function (_context) {
|
|
@@ -45,14 +55,20 @@ export var useFetchSyncBlockData = function useFetchSyncBlockData(manager, resou
|
|
|
45
55
|
}
|
|
46
56
|
return _context.abrupt("return");
|
|
47
57
|
case 1:
|
|
48
|
-
|
|
58
|
+
if (isDataProviderReady) {
|
|
59
|
+
_context.next = 2;
|
|
60
|
+
break;
|
|
61
|
+
}
|
|
62
|
+
return _context.abrupt("return");
|
|
63
|
+
case 2:
|
|
64
|
+
_context.prev = 2;
|
|
49
65
|
syncBlockNode = resourceId && localId ? createSyncBlockNode(localId, resourceId) : null;
|
|
50
66
|
if (syncBlockNode) {
|
|
51
|
-
_context.next =
|
|
67
|
+
_context.next = 3;
|
|
52
68
|
break;
|
|
53
69
|
}
|
|
54
70
|
throw new Error('Failed to create sync block node from resourceid and localid');
|
|
55
|
-
case
|
|
71
|
+
case 3:
|
|
56
72
|
setFetchState(function (prev) {
|
|
57
73
|
return _objectSpread(_objectSpread({}, prev), {}, {
|
|
58
74
|
isLoading: true
|
|
@@ -60,14 +76,25 @@ export var useFetchSyncBlockData = function useFetchSyncBlockData(manager, resou
|
|
|
60
76
|
});
|
|
61
77
|
|
|
62
78
|
// Fetch sync block data, the `subscribeToSyncBlock` will update the state once data is fetched
|
|
63
|
-
_context.next =
|
|
79
|
+
_context.next = 4;
|
|
64
80
|
return manager.referenceManager.fetchSyncBlocksData([syncBlockNode]);
|
|
65
|
-
case 3:
|
|
66
|
-
_context.next = 5;
|
|
67
|
-
break;
|
|
68
81
|
case 4:
|
|
69
|
-
_context.
|
|
70
|
-
|
|
82
|
+
_context.next = 7;
|
|
83
|
+
break;
|
|
84
|
+
case 5:
|
|
85
|
+
_context.prev = 5;
|
|
86
|
+
_t = _context["catch"](2);
|
|
87
|
+
if (!(isProviderNotReadyError(_t) && fg('platform_editor_blocks_patch_3'))) {
|
|
88
|
+
_context.next = 6;
|
|
89
|
+
break;
|
|
90
|
+
}
|
|
91
|
+
setFetchState(function (prev) {
|
|
92
|
+
return _objectSpread(_objectSpread({}, prev), {}, {
|
|
93
|
+
isLoading: true
|
|
94
|
+
});
|
|
95
|
+
});
|
|
96
|
+
return _context.abrupt("return");
|
|
97
|
+
case 6:
|
|
71
98
|
logException(_t, {
|
|
72
99
|
location: 'editor-synced-block-provider/useFetchSyncBlockData'
|
|
73
100
|
});
|
|
@@ -84,24 +111,31 @@ export var useFetchSyncBlockData = function useFetchSyncBlockData(manager, resou
|
|
|
84
111
|
isLoading: false
|
|
85
112
|
});
|
|
86
113
|
return _context.abrupt("return");
|
|
87
|
-
case
|
|
114
|
+
case 7:
|
|
88
115
|
setFetchState(function (prev) {
|
|
89
116
|
return _objectSpread(_objectSpread({}, prev), {}, {
|
|
90
117
|
isLoading: false
|
|
91
118
|
});
|
|
92
119
|
});
|
|
93
|
-
case
|
|
120
|
+
case 8:
|
|
94
121
|
case "end":
|
|
95
122
|
return _context.stop();
|
|
96
123
|
}
|
|
97
|
-
}, _callee, null, [[
|
|
98
|
-
})), [isLoading, localId, manager.referenceManager, resourceId, fireAnalyticsEvent]);
|
|
124
|
+
}, _callee, null, [[2, 5]]);
|
|
125
|
+
})), [isLoading, isDataProviderReady, localId, manager.referenceManager, resourceId, fireAnalyticsEvent]);
|
|
99
126
|
useEffect(function () {
|
|
100
127
|
if (isSSR()) {
|
|
101
128
|
// in SSR, we don't need to subscribe to updates,
|
|
102
129
|
// instead we rely on pre-fetched data ONLY, see initialization of syncBlockInstance above
|
|
103
130
|
return;
|
|
104
131
|
}
|
|
132
|
+
|
|
133
|
+
// Not ready: skip subscribe (it would trigger a batched fetch and a false
|
|
134
|
+
// error) and keep `isLoading: true`. `isDataProviderReady` is in the deps,
|
|
135
|
+
// so the effect re-runs and subscribes once the provider resolves.
|
|
136
|
+
if (!isDataProviderReady) {
|
|
137
|
+
return;
|
|
138
|
+
}
|
|
105
139
|
var unsubscribe = manager.referenceManager.subscribeToSyncBlock(resourceId || '', localId || '', function (data) {
|
|
106
140
|
setFetchState({
|
|
107
141
|
syncBlockInstance: data,
|
|
@@ -111,7 +145,7 @@ export var useFetchSyncBlockData = function useFetchSyncBlockData(manager, resou
|
|
|
111
145
|
return function () {
|
|
112
146
|
unsubscribe();
|
|
113
147
|
};
|
|
114
|
-
}, [localId, manager.referenceManager, resourceId]);
|
|
148
|
+
}, [isDataProviderReady, localId, manager.referenceManager, resourceId]);
|
|
115
149
|
var ssrProviders = useMemo(function () {
|
|
116
150
|
return resourceId ? manager.referenceManager.getSSRProviders(resourceId) : null;
|
|
117
151
|
}, [resourceId, manager.referenceManager]);
|
|
@@ -12,7 +12,7 @@ function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t =
|
|
|
12
12
|
import isEqual from 'lodash/isEqual';
|
|
13
13
|
import { logException } from '@atlaskit/editor-common/monitoring';
|
|
14
14
|
import { fg } from '@atlaskit/platform-feature-flags';
|
|
15
|
-
import { SyncBlockError } from '../common/types';
|
|
15
|
+
import { isProviderNotReadyError, ProviderNotReadyError, SyncBlockError } from '../common/types';
|
|
16
16
|
import { buildErrorAttribution, cacheDeletionForcedPayload, fetchErrorPayload, fetchSuccessPayload, getSourceInfoErrorPayload, sourceInfoOrphanedPayload, updateReferenceErrorPayload } from '../utils/errorHandling';
|
|
17
17
|
import { getFetchExperience, getFetchSourceInfoExperience, getSaveReferenceExperience } from '../utils/experienceTracking';
|
|
18
18
|
import { resolveSyncBlockInstance } from '../utils/resolveSyncBlockInstance';
|
|
@@ -121,6 +121,10 @@ export var ReferenceSyncBlockStoreManager = /*#__PURE__*/function () {
|
|
|
121
121
|
},
|
|
122
122
|
getFireAnalyticsEvent: function getFireAnalyticsEvent() {
|
|
123
123
|
return _this.fireAnalyticsEvent;
|
|
124
|
+
},
|
|
125
|
+
// EDITOR-7860: skip + re-queue fetches while not ready / torn down.
|
|
126
|
+
isProviderReady: function isProviderReady() {
|
|
127
|
+
return _this.hasDataProvider();
|
|
124
128
|
}
|
|
125
129
|
});
|
|
126
130
|
|
|
@@ -133,6 +137,19 @@ export var ReferenceSyncBlockStoreManager = /*#__PURE__*/function () {
|
|
|
133
137
|
return node.type.name === 'syncBlock';
|
|
134
138
|
}
|
|
135
139
|
|
|
140
|
+
/**
|
|
141
|
+
* Whether the async data provider is wired and the manager is not torn down.
|
|
142
|
+
* Consumers gate fetch/subscribe on this to avoid a false `Data provider not
|
|
143
|
+
* set` fetch error (EDITOR-7860). The `isDestroyed` check covers managers
|
|
144
|
+
* orphaned mid-flight during an async provider swap, where `dataProvider`
|
|
145
|
+
* alone is insufficient.
|
|
146
|
+
*/
|
|
147
|
+
}, {
|
|
148
|
+
key: "hasDataProvider",
|
|
149
|
+
value: function hasDataProvider() {
|
|
150
|
+
return !this.isDestroyed && !!this.dataProvider;
|
|
151
|
+
}
|
|
152
|
+
|
|
136
153
|
/**
|
|
137
154
|
* Enables or disables real-time GraphQL subscriptions for block updates.
|
|
138
155
|
* When enabled, the store manager will subscribe to real-time updates
|
|
@@ -524,22 +541,28 @@ export var ReferenceSyncBlockStoreManager = /*#__PURE__*/function () {
|
|
|
524
541
|
return _context2.abrupt("return");
|
|
525
542
|
case 2:
|
|
526
543
|
if (this.dataProvider) {
|
|
544
|
+
_context2.next = 4;
|
|
545
|
+
break;
|
|
546
|
+
}
|
|
547
|
+
if (!fg('platform_editor_blocks_patch_3')) {
|
|
527
548
|
_context2.next = 3;
|
|
528
549
|
break;
|
|
529
550
|
}
|
|
530
|
-
throw new
|
|
551
|
+
throw new ProviderNotReadyError();
|
|
531
552
|
case 3:
|
|
553
|
+
throw new Error('Data provider not set');
|
|
554
|
+
case 4:
|
|
532
555
|
nodesToFetch.forEach(function (node) {
|
|
533
556
|
_this4.syncBlockFetchDataRequests.set(node.attrs.resourceId, true);
|
|
534
557
|
});
|
|
535
558
|
(_this$fetchExperience6 = this.fetchExperience) === null || _this$fetchExperience6 === void 0 || _this$fetchExperience6.start({});
|
|
536
|
-
_context2.next =
|
|
559
|
+
_context2.next = 5;
|
|
537
560
|
return this.dataProvider.fetchNodesData(nodesToFetch).finally(function () {
|
|
538
561
|
nodesToFetch.forEach(function (node) {
|
|
539
562
|
_this4.syncBlockFetchDataRequests.delete(node.attrs.resourceId);
|
|
540
563
|
});
|
|
541
564
|
});
|
|
542
|
-
case
|
|
565
|
+
case 5:
|
|
543
566
|
data = _context2.sent;
|
|
544
567
|
_this$processFetchedD2 = this.processFetchedData(data), hasUnexpectedError = _this$processFetchedD2.hasUnexpectedError, hasExpectedError = _this$processFetchedD2.hasExpectedError;
|
|
545
568
|
if (hasUnexpectedError) {
|
|
@@ -553,7 +576,7 @@ export var ReferenceSyncBlockStoreManager = /*#__PURE__*/function () {
|
|
|
553
576
|
} else {
|
|
554
577
|
(_this$fetchExperience9 = this.fetchExperience) === null || _this$fetchExperience9 === void 0 || _this$fetchExperience9.success();
|
|
555
578
|
}
|
|
556
|
-
case
|
|
579
|
+
case 6:
|
|
557
580
|
case "end":
|
|
558
581
|
return _context2.stop();
|
|
559
582
|
}
|
|
@@ -931,6 +954,12 @@ export var ReferenceSyncBlockStoreManager = /*#__PURE__*/function () {
|
|
|
931
954
|
return this._subscriptionManager.subscribeToSyncBlock(resourceId, localId, callback);
|
|
932
955
|
} catch (error) {
|
|
933
956
|
var _this$fireAnalyticsEv7;
|
|
957
|
+
// EDITOR-7860: benign not-ready/torn-down case — suppress both the
|
|
958
|
+
// exception-tracker log and the analytics event (checked first so the
|
|
959
|
+
// benign case stays fully silent). Gate-off behaviour is unchanged.
|
|
960
|
+
if (isProviderNotReadyError(error) && fg('platform_editor_blocks_patch_3')) {
|
|
961
|
+
return function () {};
|
|
962
|
+
}
|
|
934
963
|
logException(error, {
|
|
935
964
|
location: 'editor-synced-block-provider/referenceSyncBlockStoreManager'
|
|
936
965
|
});
|
|
@@ -3,6 +3,8 @@ import _createClass from "@babel/runtime/helpers/createClass";
|
|
|
3
3
|
import _defineProperty from "@babel/runtime/helpers/defineProperty";
|
|
4
4
|
import rafSchedule from 'raf-schd';
|
|
5
5
|
import { logException } from '@atlaskit/editor-common/monitoring';
|
|
6
|
+
import { fg } from '@atlaskit/platform-feature-flags';
|
|
7
|
+
import { isProviderNotReadyError } from '../common/types';
|
|
6
8
|
import { fetchErrorPayload } from '../utils/errorHandling';
|
|
7
9
|
import { createSyncBlockNode, getSourceProductFromResourceIdSafe } from '../utils/utils';
|
|
8
10
|
/**
|
|
@@ -25,6 +27,17 @@ export var SyncBlockBatchFetcher = /*#__PURE__*/function () {
|
|
|
25
27
|
if (_this.pendingFetchRequests.size === 0) {
|
|
26
28
|
return;
|
|
27
29
|
}
|
|
30
|
+
|
|
31
|
+
// EDITOR-7860: not ready — skip and leave resourceIds queued for the
|
|
32
|
+
// next batch once the provider resolves. Gate-off is unchanged (the
|
|
33
|
+
// readiness check is nested under the gate so it is not consulted when
|
|
34
|
+
// the gate is off, and `fg()` stays a standalone condition so gate
|
|
35
|
+
// exposure is still tracked — satisfies @atlaskit/platform/no-preconditioning).
|
|
36
|
+
if (fg('platform_editor_blocks_patch_3')) {
|
|
37
|
+
if (_this.deps.isProviderReady && !_this.deps.isProviderReady()) {
|
|
38
|
+
return;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
28
41
|
var resourceIds = Array.from(_this.pendingFetchRequests);
|
|
29
42
|
var syncBlockNodes = resourceIds.map(function (resId) {
|
|
30
43
|
var subscriptions = _this.deps.getSubscriptions().get(resId) || {};
|
|
@@ -38,6 +51,20 @@ export var SyncBlockBatchFetcher = /*#__PURE__*/function () {
|
|
|
38
51
|
return _this.inFlightFetches.add(resId);
|
|
39
52
|
});
|
|
40
53
|
_this.deps.fetchSyncBlocksData(syncBlockNodes).catch(function (error) {
|
|
54
|
+
// EDITOR-7860: benign not-ready throw — re-queue for retry and emit
|
|
55
|
+
// nothing (no analytics, no exception-tracker noise). Checked before
|
|
56
|
+
// `logException` so the benign case stays silent. Re-schedule so the
|
|
57
|
+
// re-queued IDs are retried on the next frame even if no further
|
|
58
|
+
// `queueFetch()` arrives. Gate-off behaviour is unchanged.
|
|
59
|
+
if (isProviderNotReadyError(error) && fg('platform_editor_blocks_patch_3')) {
|
|
60
|
+
resourceIds.forEach(function (resId) {
|
|
61
|
+
return _this.pendingFetchRequests.add(resId);
|
|
62
|
+
});
|
|
63
|
+
if (!_this.isDestroyed) {
|
|
64
|
+
_this.scheduledBatchFetch();
|
|
65
|
+
}
|
|
66
|
+
return;
|
|
67
|
+
}
|
|
41
68
|
logException(error, {
|
|
42
69
|
location: 'editor-synced-block-provider/syncBlockBatchFetcher/batchedFetchSyncBlocks'
|
|
43
70
|
});
|
|
@@ -31,6 +31,7 @@ export var SyncBlockSubscriptionManager = /*#__PURE__*/function () {
|
|
|
31
31
|
// causing the cache to be deleted prematurely. We delay deletion to allow
|
|
32
32
|
// the new component to subscribe and cancel the pending deletion.
|
|
33
33
|
_defineProperty(this, "pendingCacheDeletions", new Map());
|
|
34
|
+
// backoff cap (gate ON)
|
|
34
35
|
_defineProperty(this, "retryAttempts", new Map());
|
|
35
36
|
_defineProperty(this, "pendingRetries", new Map());
|
|
36
37
|
this.deps = deps;
|
|
@@ -41,6 +42,30 @@ export var SyncBlockSubscriptionManager = /*#__PURE__*/function () {
|
|
|
41
42
|
* that need to read the current subscription state.
|
|
42
43
|
*/
|
|
43
44
|
return _createClass(SyncBlockSubscriptionManager, [{
|
|
45
|
+
key: "getMaxRetryAttempts",
|
|
46
|
+
value:
|
|
47
|
+
// EDITOR-7861: higher ceiling lets transient WS-gateway drops self-heal
|
|
48
|
+
// before a terminal failure is surfaced.
|
|
49
|
+
function getMaxRetryAttempts() {
|
|
50
|
+
return fg('platform_editor_blocks_patch_3') ? SyncBlockSubscriptionManager.MAX_RETRY_ATTEMPTS_HARDENED : SyncBlockSubscriptionManager.MAX_RETRY_ATTEMPTS;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
// Backoff delay for the given attempt.
|
|
54
|
+
// Gate OFF: pure exponential (1s, 2s, 4s, 8s, 16s).
|
|
55
|
+
// Gate ON (EDITOR-7861): exponential capped at MAX_RETRY_DELAY_MS with equal
|
|
56
|
+
// jitter (capped/2 + random*capped/2) — de-synchronises simultaneous
|
|
57
|
+
// reconnects while guaranteeing a non-zero delay (full jitter could hit 0).
|
|
58
|
+
}, {
|
|
59
|
+
key: "getReconnectionDelay",
|
|
60
|
+
value: function getReconnectionDelay(attempts) {
|
|
61
|
+
var exponential = SyncBlockSubscriptionManager.INITIAL_RETRY_DELAY_MS * Math.pow(SyncBlockSubscriptionManager.RETRY_BACKOFF_MULTIPLIER, attempts);
|
|
62
|
+
if (!fg('platform_editor_blocks_patch_3')) {
|
|
63
|
+
return exponential;
|
|
64
|
+
}
|
|
65
|
+
var half = Math.min(exponential, SyncBlockSubscriptionManager.MAX_RETRY_DELAY_MS) / 2;
|
|
66
|
+
return Math.round(half + Math.random() * half);
|
|
67
|
+
}
|
|
68
|
+
}, {
|
|
44
69
|
key: "getSubscriptions",
|
|
45
70
|
value: function getSubscriptions() {
|
|
46
71
|
return this.subscriptions;
|
|
@@ -267,11 +292,17 @@ export var SyncBlockSubscriptionManager = /*#__PURE__*/function () {
|
|
|
267
292
|
var unsubscribe = dataProvider.subscribeToBlockUpdates(resourceId, function (syncBlockInstance) {
|
|
268
293
|
_this5.handleGraphQLUpdate(syncBlockInstance);
|
|
269
294
|
}, function (error) {
|
|
270
|
-
var _this5$deps$getFireAn;
|
|
271
295
|
logException(error, {
|
|
272
296
|
location: 'editor-synced-block-provider/syncBlockSubscriptionManager/graphql-subscription'
|
|
273
297
|
});
|
|
274
|
-
|
|
298
|
+
// EDITOR-7861: a single socket drop is usually transient and
|
|
299
|
+
// recovers on reconnect, so under the gate we don't fire a
|
|
300
|
+
// user-facing error here — it's only surfaced on exhaustion (see
|
|
301
|
+
// scheduleReconnection). Gate OFF keeps the legacy fire-on-drop.
|
|
302
|
+
if (!fg('platform_editor_blocks_patch_3')) {
|
|
303
|
+
var _this5$deps$getFireAn;
|
|
304
|
+
(_this5$deps$getFireAn = _this5.deps.getFireAnalyticsEvent()) === null || _this5$deps$getFireAn === void 0 || _this5$deps$getFireAn(fetchErrorPayload(error.message, resourceId, getSourceProductFromResourceIdSafe(resourceId)));
|
|
305
|
+
}
|
|
275
306
|
_this5.handleSubscriptionTerminated(resourceId);
|
|
276
307
|
}, function () {
|
|
277
308
|
_this5.handleSubscriptionTerminated(resourceId);
|
|
@@ -302,19 +333,19 @@ export var SyncBlockSubscriptionManager = /*#__PURE__*/function () {
|
|
|
302
333
|
}
|
|
303
334
|
}
|
|
304
335
|
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
* Delay = INITIAL_RETRY_DELAY_MS * (RETRY_BACKOFF_MULTIPLIER ^ attempts)
|
|
308
|
-
* e.g. 1s, 2s, 4s, 8s, 16s
|
|
309
|
-
*/
|
|
336
|
+
// Schedules a reconnection with backoff (see getMaxRetryAttempts /
|
|
337
|
+
// getReconnectionDelay for the gated behaviour).
|
|
310
338
|
}, {
|
|
311
339
|
key: "scheduleReconnection",
|
|
312
340
|
value: function scheduleReconnection(resourceId) {
|
|
313
341
|
var _this$retryAttempts$g,
|
|
314
342
|
_this6 = this;
|
|
315
343
|
var attempts = (_this$retryAttempts$g = this.retryAttempts.get(resourceId)) !== null && _this$retryAttempts$g !== void 0 ? _this$retryAttempts$g : 0;
|
|
316
|
-
|
|
344
|
+
var maxAttempts = this.getMaxRetryAttempts();
|
|
345
|
+
if (attempts >= maxAttempts) {
|
|
317
346
|
var _this$deps$getFireAna;
|
|
347
|
+
// Exhausted all attempts — the only place a WS drop surfaces as a
|
|
348
|
+
// fetch error under the gate (EDITOR-7861).
|
|
318
349
|
var errorMessage = "Subscription reconnection failed after ".concat(attempts, " attempts");
|
|
319
350
|
logException(new Error(errorMessage), {
|
|
320
351
|
location: 'editor-synced-block-provider/syncBlockSubscriptionManager/max-retries-exhausted'
|
|
@@ -322,7 +353,7 @@ export var SyncBlockSubscriptionManager = /*#__PURE__*/function () {
|
|
|
322
353
|
(_this$deps$getFireAna = this.deps.getFireAnalyticsEvent()) === null || _this$deps$getFireAna === void 0 || _this$deps$getFireAna(fetchErrorPayload(errorMessage, resourceId, getSourceProductFromResourceIdSafe(resourceId)));
|
|
323
354
|
return;
|
|
324
355
|
}
|
|
325
|
-
var delay =
|
|
356
|
+
var delay = this.getReconnectionDelay(attempts);
|
|
326
357
|
var timer = setTimeout(function () {
|
|
327
358
|
_this6.pendingRetries.delete(resourceId);
|
|
328
359
|
|
|
@@ -480,7 +511,11 @@ export var SyncBlockSubscriptionManager = /*#__PURE__*/function () {
|
|
|
480
511
|
}
|
|
481
512
|
}]);
|
|
482
513
|
}();
|
|
483
|
-
// Reconnection with exponential backoff
|
|
514
|
+
// Reconnection with exponential backoff.
|
|
484
515
|
_defineProperty(SyncBlockSubscriptionManager, "INITIAL_RETRY_DELAY_MS", 1000);
|
|
485
516
|
_defineProperty(SyncBlockSubscriptionManager, "RETRY_BACKOFF_MULTIPLIER", 2);
|
|
486
|
-
_defineProperty(SyncBlockSubscriptionManager, "MAX_RETRY_ATTEMPTS", 5);
|
|
517
|
+
_defineProperty(SyncBlockSubscriptionManager, "MAX_RETRY_ATTEMPTS", 5);
|
|
518
|
+
// legacy (gate OFF)
|
|
519
|
+
_defineProperty(SyncBlockSubscriptionManager, "MAX_RETRY_ATTEMPTS_HARDENED", 8);
|
|
520
|
+
// gate ON (EDITOR-7861)
|
|
521
|
+
_defineProperty(SyncBlockSubscriptionManager, "MAX_RETRY_DELAY_MS", 30000);
|