@atlaskit/editor-synced-block-provider 10.0.2 → 10.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 +14 -0
- package/dist/cjs/common/types.js +1 -2
- package/dist/cjs/hooks/useFetchSyncBlockData.js +4 -4
- package/dist/cjs/store-manager/referenceSyncBlockStoreManager.js +24 -49
- package/dist/cjs/store-manager/sourceSyncBlockStoreManager.js +17 -36
- package/dist/cjs/store-manager/syncBlockBatchFetcher.js +6 -12
- package/dist/cjs/store-manager/syncBlockProviderFactoryManager.js +3 -3
- package/dist/cjs/store-manager/syncBlockStoreManager.js +1 -5
- package/dist/cjs/store-manager/syncBlockSubscriptionManager.js +11 -52
- package/dist/cjs/utils/errorHandling.js +10 -29
- package/dist/es2019/common/types.js +1 -2
- package/dist/es2019/hooks/useFetchSyncBlockData.js +4 -5
- package/dist/es2019/store-manager/referenceSyncBlockStoreManager.js +23 -45
- package/dist/es2019/store-manager/sourceSyncBlockStoreManager.js +18 -37
- package/dist/es2019/store-manager/syncBlockBatchFetcher.js +6 -12
- package/dist/es2019/store-manager/syncBlockProviderFactoryManager.js +3 -3
- package/dist/es2019/store-manager/syncBlockStoreManager.js +1 -5
- package/dist/es2019/store-manager/syncBlockSubscriptionManager.js +16 -57
- package/dist/es2019/utils/errorHandling.js +10 -29
- package/dist/esm/common/types.js +1 -2
- package/dist/esm/hooks/useFetchSyncBlockData.js +4 -4
- package/dist/esm/store-manager/referenceSyncBlockStoreManager.js +24 -49
- package/dist/esm/store-manager/sourceSyncBlockStoreManager.js +17 -36
- package/dist/esm/store-manager/syncBlockBatchFetcher.js +6 -12
- package/dist/esm/store-manager/syncBlockProviderFactoryManager.js +3 -3
- package/dist/esm/store-manager/syncBlockStoreManager.js +1 -5
- package/dist/esm/store-manager/syncBlockSubscriptionManager.js +11 -52
- package/dist/esm/utils/errorHandling.js +10 -29
- package/dist/types/common/types.d.ts +1 -2
- package/dist/types/store-manager/referenceSyncBlockStoreManager.d.ts +3 -3
- package/dist/types/store-manager/sourceSyncBlockStoreManager.d.ts +1 -3
- package/dist/types/store-manager/syncBlockSubscriptionManager.d.ts +0 -1
- package/dist/types/utils/errorHandling.d.ts +10 -23
- package/package.json +3 -15
|
@@ -11,7 +11,6 @@ var _defineProperty2 = _interopRequireDefault(require("@babel/runtime/helpers/de
|
|
|
11
11
|
var _bindEventListener = require("bind-event-listener");
|
|
12
12
|
var _browserApis = require("@atlaskit/browser-apis");
|
|
13
13
|
var _monitoring = require("@atlaskit/editor-common/monitoring");
|
|
14
|
-
var _platformFeatureFlags = require("@atlaskit/platform-feature-flags");
|
|
15
14
|
var _errorHandling = require("../utils/errorHandling");
|
|
16
15
|
var _resolveSyncBlockInstance = require("../utils/resolveSyncBlockInstance");
|
|
17
16
|
var _utils = require("../utils/utils");
|
|
@@ -40,7 +39,7 @@ var SyncBlockSubscriptionManager = exports.SyncBlockSubscriptionManager = /*#__P
|
|
|
40
39
|
// causing the cache to be deleted prematurely. We delay deletion to allow
|
|
41
40
|
// the new component to subscribe and cancel the pending deletion.
|
|
42
41
|
(0, _defineProperty2.default)(this, "pendingCacheDeletions", new Map());
|
|
43
|
-
// backoff cap
|
|
42
|
+
// backoff cap
|
|
44
43
|
(0, _defineProperty2.default)(this, "retryAttempts", new Map());
|
|
45
44
|
(0, _defineProperty2.default)(this, "pendingRetries", new Map());
|
|
46
45
|
// Resources whose reconnection exhausted while the tab was hidden: parked here and
|
|
@@ -74,7 +73,7 @@ var SyncBlockSubscriptionManager = exports.SyncBlockSubscriptionManager = /*#__P
|
|
|
74
73
|
// EDITOR-7861: higher ceiling lets transient WS-gateway drops self-heal
|
|
75
74
|
// before a terminal failure is surfaced.
|
|
76
75
|
function getMaxRetryAttempts() {
|
|
77
|
-
return
|
|
76
|
+
return SyncBlockSubscriptionManager.MAX_RETRY_ATTEMPTS_HARDENED;
|
|
78
77
|
}
|
|
79
78
|
|
|
80
79
|
// Backoff delay for the given attempt.
|
|
@@ -86,9 +85,6 @@ var SyncBlockSubscriptionManager = exports.SyncBlockSubscriptionManager = /*#__P
|
|
|
86
85
|
key: "getReconnectionDelay",
|
|
87
86
|
value: function getReconnectionDelay(attempts) {
|
|
88
87
|
var exponential = SyncBlockSubscriptionManager.INITIAL_RETRY_DELAY_MS * Math.pow(SyncBlockSubscriptionManager.RETRY_BACKOFF_MULTIPLIER, attempts);
|
|
89
|
-
if (!(0, _platformFeatureFlags.fg)('platform_editor_blocks_patch_3')) {
|
|
90
|
-
return exponential;
|
|
91
|
-
}
|
|
92
88
|
var half = Math.min(exponential, SyncBlockSubscriptionManager.MAX_RETRY_DELAY_MS) / 2;
|
|
93
89
|
return Math.round(half + Math.random() * half);
|
|
94
90
|
}
|
|
@@ -141,7 +137,7 @@ var SyncBlockSubscriptionManager = exports.SyncBlockSubscriptionManager = /*#__P
|
|
|
141
137
|
(0, _monitoring.logException)(error, {
|
|
142
138
|
location: 'editor-synced-block-provider/syncBlockSubscriptionManager/notifySubscriptionChangeListeners'
|
|
143
139
|
});
|
|
144
|
-
(_this3$deps$getFireAn = _this3.deps.getFireAnalyticsEvent()) === null || _this3$deps$getFireAn === void 0 || _this3$deps$getFireAn((0, _errorHandling.fetchErrorPayload)(error.message, undefined, undefined, (0, _errorHandling.buildFetchErrorAttribution)(
|
|
140
|
+
(_this3$deps$getFireAn = _this3.deps.getFireAnalyticsEvent()) === null || _this3$deps$getFireAn === void 0 || _this3$deps$getFireAn((0, _errorHandling.fetchErrorPayload)(error.message, undefined, undefined, (0, _errorHandling.buildFetchErrorAttribution)(error.message)));
|
|
145
141
|
}
|
|
146
142
|
});
|
|
147
143
|
}
|
|
@@ -166,16 +162,7 @@ var SyncBlockSubscriptionManager = exports.SyncBlockSubscriptionManager = /*#__P
|
|
|
166
162
|
// This handles the case where a block is moved - the old component unmounts
|
|
167
163
|
// (scheduling deletion) but the new component mounts and subscribes before
|
|
168
164
|
// the deletion timeout fires.
|
|
169
|
-
|
|
170
|
-
// Under the flag, cache deletion is owned by the store manager.
|
|
171
|
-
// With the flag off, the legacy 1s timer path is preserved.
|
|
172
|
-
var pendingDeletion = this.pendingCacheDeletions.get(resourceId);
|
|
173
|
-
if ((0, _platformFeatureFlags.fg)('platform_synced_block_patch_14')) {
|
|
174
|
-
this.deps.cancelPendingCacheDeletion(resourceId);
|
|
175
|
-
} else if (pendingDeletion) {
|
|
176
|
-
clearTimeout(pendingDeletion);
|
|
177
|
-
this.pendingCacheDeletions.delete(resourceId);
|
|
178
|
-
}
|
|
165
|
+
this.deps.cancelPendingCacheDeletion(resourceId);
|
|
179
166
|
|
|
180
167
|
// add to subscriptions map
|
|
181
168
|
var resourceSubscriptions = this.subscriptions.get(resourceId) || {};
|
|
@@ -216,30 +203,9 @@ var SyncBlockSubscriptionManager = exports.SyncBlockSubscriptionManager = /*#__P
|
|
|
216
203
|
// Notify listeners that subscription was removed
|
|
217
204
|
_this4.notifySubscriptionChangeListeners();
|
|
218
205
|
|
|
219
|
-
//
|
|
206
|
+
// Delegate cache deletion to the store manager
|
|
220
207
|
// which uses a 30s grace period with guard re-checks.
|
|
221
|
-
|
|
222
|
-
_this4.deps.scheduleCacheDeletion(resourceId);
|
|
223
|
-
} else {
|
|
224
|
-
// Legacy path (unchanged): delay cache deletion to handle
|
|
225
|
-
// block moves (unmount/remount). When a block is moved, the
|
|
226
|
-
// old component unmounts before the new one mounts. By
|
|
227
|
-
// delaying deletion, we give the new component time to
|
|
228
|
-
// subscribe and cancel this pending deletion, preserving
|
|
229
|
-
// the cached data.
|
|
230
|
-
// TODO: EDITOR-4152 - Rework this logic (superseded by
|
|
231
|
-
// `platform_synced_block_patch_14`).
|
|
232
|
-
var deletionTimeout = setTimeout(function () {
|
|
233
|
-
var hasSubscribers = _this4.subscriptions.has(resourceId);
|
|
234
|
-
|
|
235
|
-
// Only delete if still no subscribers (wasn't re-subscribed)
|
|
236
|
-
if (!hasSubscribers) {
|
|
237
|
-
_this4.deps.deleteFromCache(resourceId);
|
|
238
|
-
}
|
|
239
|
-
_this4.pendingCacheDeletions.delete(resourceId);
|
|
240
|
-
}, 1000);
|
|
241
|
-
_this4.pendingCacheDeletions.set(resourceId, deletionTimeout);
|
|
242
|
-
}
|
|
208
|
+
_this4.deps.scheduleCacheDeletion(resourceId);
|
|
243
209
|
} else {
|
|
244
210
|
_this4.subscriptions.set(resourceId, resourceSubscriptions);
|
|
245
211
|
}
|
|
@@ -329,10 +295,6 @@ var SyncBlockSubscriptionManager = exports.SyncBlockSubscriptionManager = /*#__P
|
|
|
329
295
|
// This branch only runs when the gate is OFF, so buildFetchErrorAttribution
|
|
330
296
|
// would return undefined; the structured attribution (EDITOR-7862) is therefore
|
|
331
297
|
// applied at the gate-ON exhaustion site in scheduleReconnection instead.
|
|
332
|
-
if (!(0, _platformFeatureFlags.fg)('platform_editor_blocks_patch_3')) {
|
|
333
|
-
var _this6$deps$getFireAn;
|
|
334
|
-
(_this6$deps$getFireAn = _this6.deps.getFireAnalyticsEvent()) === null || _this6$deps$getFireAn === void 0 || _this6$deps$getFireAn((0, _errorHandling.fetchErrorPayload)(error.message, resourceId, (0, _utils.getSourceProductFromResourceIdSafe)(resourceId)));
|
|
335
|
-
}
|
|
336
298
|
_this6.handleSubscriptionTerminated(resourceId);
|
|
337
299
|
}, function () {
|
|
338
300
|
_this6.handleSubscriptionTerminated(resourceId);
|
|
@@ -381,18 +343,17 @@ var SyncBlockSubscriptionManager = exports.SyncBlockSubscriptionManager = /*#__P
|
|
|
381
343
|
// Tab hidden at exhaustion: don't surface a terminal failure (user isn't
|
|
382
344
|
// looking, and most exhaustions self-recover once foregrounded). Park + re-arm
|
|
383
345
|
// on wake, emitting a benign `deferred` signal so suppression stays auditable.
|
|
384
|
-
|
|
385
|
-
if (shouldDefer) {
|
|
346
|
+
if (this.isDocumentHidden()) {
|
|
386
347
|
var _this$deps$getFireAna;
|
|
387
348
|
this.deferredExhausted.add(resourceId);
|
|
388
349
|
this.registerWakeListeners();
|
|
389
|
-
(_this$deps$getFireAna = this.deps.getFireAnalyticsEvent()) === null || _this$deps$getFireAna === void 0 || _this$deps$getFireAna((0, _errorHandling.fetchErrorPayload)(errorMessage, resourceId, (0, _utils.getSourceProductFromResourceIdSafe)(resourceId), (0, _errorHandling.buildFetchErrorAttribution)(
|
|
350
|
+
(_this$deps$getFireAna = this.deps.getFireAnalyticsEvent()) === null || _this$deps$getFireAna === void 0 || _this$deps$getFireAna((0, _errorHandling.fetchErrorPayload)(errorMessage, resourceId, (0, _utils.getSourceProductFromResourceIdSafe)(resourceId), (0, _errorHandling.buildFetchErrorAttribution)(errorMessage, undefined, /* deferred */true)));
|
|
390
351
|
return;
|
|
391
352
|
}
|
|
392
353
|
(0, _monitoring.logException)(new Error(errorMessage), {
|
|
393
354
|
location: 'editor-synced-block-provider/syncBlockSubscriptionManager/max-retries-exhausted'
|
|
394
355
|
});
|
|
395
|
-
(_this$deps$getFireAna2 = this.deps.getFireAnalyticsEvent()) === null || _this$deps$getFireAna2 === void 0 || _this$deps$getFireAna2((0, _errorHandling.fetchErrorPayload)(errorMessage, resourceId, (0, _utils.getSourceProductFromResourceIdSafe)(resourceId), (0, _errorHandling.buildFetchErrorAttribution)(
|
|
356
|
+
(_this$deps$getFireAna2 = this.deps.getFireAnalyticsEvent()) === null || _this$deps$getFireAna2 === void 0 || _this$deps$getFireAna2((0, _errorHandling.fetchErrorPayload)(errorMessage, resourceId, (0, _utils.getSourceProductFromResourceIdSafe)(resourceId), (0, _errorHandling.buildFetchErrorAttribution)(errorMessage)));
|
|
396
357
|
return;
|
|
397
358
|
}
|
|
398
359
|
var delay = this.getReconnectionDelay(attempts);
|
|
@@ -667,7 +628,7 @@ var SyncBlockSubscriptionManager = exports.SyncBlockSubscriptionManager = /*#__P
|
|
|
667
628
|
// Prefer the structured `type` (a `SyncBlockError` enum value) for classification
|
|
668
629
|
// and fall back to the free-text `reason` so source-state/permission strings are
|
|
669
630
|
// still bucketed (EDITOR-7862). The emitted free-text `error` attribute is unchanged.
|
|
670
|
-
(_this$deps$getFireAna3 = this.deps.getFireAnalyticsEvent()) === null || _this$deps$getFireAna3 === void 0 || _this$deps$getFireAna3((0, _errorHandling.fetchErrorPayload)(errorMessage, syncBlockInstance.resourceId, (_syncBlockInstance$da3 = (_syncBlockInstance$da4 = syncBlockInstance.data) === null || _syncBlockInstance$da4 === void 0 ? void 0 : _syncBlockInstance$da4.product) !== null && _syncBlockInstance$da3 !== void 0 ? _syncBlockInstance$da3 : (0, _utils.getSourceProductFromResourceIdSafe)(syncBlockInstance.resourceId), (0, _errorHandling.buildFetchErrorAttribution)((
|
|
631
|
+
(_this$deps$getFireAna3 = this.deps.getFireAnalyticsEvent()) === null || _this$deps$getFireAna3 === void 0 || _this$deps$getFireAna3((0, _errorHandling.fetchErrorPayload)(errorMessage, syncBlockInstance.resourceId, (_syncBlockInstance$da3 = (_syncBlockInstance$da4 = syncBlockInstance.data) === null || _syncBlockInstance$da4 === void 0 ? void 0 : _syncBlockInstance$da4.product) !== null && _syncBlockInstance$da3 !== void 0 ? _syncBlockInstance$da3 : (0, _utils.getSourceProductFromResourceIdSafe)(syncBlockInstance.resourceId), (0, _errorHandling.buildFetchErrorAttribution)(((_syncBlockInstance$er3 = syncBlockInstance.error) === null || _syncBlockInstance$er3 === void 0 ? void 0 : _syncBlockInstance$er3.type) || ((_syncBlockInstance$er4 = syncBlockInstance.error) === null || _syncBlockInstance$er4 === void 0 ? void 0 : _syncBlockInstance$er4.reason), (_syncBlockInstance$er5 = syncBlockInstance.error) === null || _syncBlockInstance$er5 === void 0 ? void 0 : _syncBlockInstance$er5.statusCode)));
|
|
671
632
|
}
|
|
672
633
|
}
|
|
673
634
|
}]);
|
|
@@ -675,10 +636,8 @@ var SyncBlockSubscriptionManager = exports.SyncBlockSubscriptionManager = /*#__P
|
|
|
675
636
|
// Reconnection with exponential backoff.
|
|
676
637
|
(0, _defineProperty2.default)(SyncBlockSubscriptionManager, "INITIAL_RETRY_DELAY_MS", 1000);
|
|
677
638
|
(0, _defineProperty2.default)(SyncBlockSubscriptionManager, "RETRY_BACKOFF_MULTIPLIER", 2);
|
|
678
|
-
(0, _defineProperty2.default)(SyncBlockSubscriptionManager, "MAX_RETRY_ATTEMPTS", 5);
|
|
679
|
-
// legacy (gate OFF)
|
|
680
639
|
(0, _defineProperty2.default)(SyncBlockSubscriptionManager, "MAX_RETRY_ATTEMPTS_HARDENED", 8);
|
|
681
|
-
//
|
|
640
|
+
// EDITOR-7861
|
|
682
641
|
(0, _defineProperty2.default)(SyncBlockSubscriptionManager, "MAX_RETRY_DELAY_MS", 30000);
|
|
683
642
|
// Coalesce wake-event bursts into one re-arm sweep to avoid a reconnection storm.
|
|
684
643
|
(0, _defineProperty2.default)(SyncBlockSubscriptionManager, "WAKE_DEBOUNCE_MS", 1000);
|
|
@@ -70,17 +70,9 @@ var classifyErrorReason = exports.classifyErrorReason = function classifyErrorRe
|
|
|
70
70
|
|
|
71
71
|
/**
|
|
72
72
|
* Builds the {@link ErrorAttributionAttributes} for a failed synced-block operation from
|
|
73
|
-
* the raw result `error` field and optional backend `statusCode
|
|
74
|
-
* when the `platform_editor_blocks_patch_3` gate is OFF, so the new `reason`/`statusCode`
|
|
75
|
-
* attributes are only emitted once the gate is rolled out (EDITOR-7796).
|
|
76
|
-
*
|
|
77
|
-
* `gateEnabled` is injected by the caller (the store managers evaluate `fg(...)`) so this
|
|
78
|
-
* helper stays pure and trivially unit-testable for both gate states.
|
|
73
|
+
* the raw result `error` field and optional backend `statusCode` (EDITOR-7796).
|
|
79
74
|
*/
|
|
80
|
-
var buildErrorAttribution = exports.buildErrorAttribution = function buildErrorAttribution(
|
|
81
|
-
if (!gateEnabled) {
|
|
82
|
-
return undefined;
|
|
83
|
-
}
|
|
75
|
+
var buildErrorAttribution = exports.buildErrorAttribution = function buildErrorAttribution(error, statusCode) {
|
|
84
76
|
return _objectSpread({
|
|
85
77
|
reason: classifyErrorReason(error)
|
|
86
78
|
}, statusCode !== undefined && {
|
|
@@ -195,17 +187,10 @@ var classifyFetchErrorReason = exports.classifyFetchErrorReason = function class
|
|
|
195
187
|
/**
|
|
196
188
|
* Builds the {@link FetchErrorAttributionAttributes} for a failed fetch/subscribe
|
|
197
189
|
* synced-block operation from the raw `error` field and optional backend `statusCode`.
|
|
198
|
-
*
|
|
199
|
-
* `
|
|
200
|
-
* (EDITOR-7862). The existing free-text `error` attribute is always left unchanged.
|
|
201
|
-
*
|
|
202
|
-
* `gateEnabled` is injected by the caller (the store managers evaluate `fg(...)`) so this
|
|
203
|
-
* helper stays pure and trivially unit-testable for both gate states.
|
|
190
|
+
* Emits the `reason`/`statusCode`/`benign` attributes (EDITOR-7862). The existing
|
|
191
|
+
* free-text `error` attribute is always left unchanged.
|
|
204
192
|
*/
|
|
205
|
-
var buildFetchErrorAttribution = exports.buildFetchErrorAttribution = function buildFetchErrorAttribution(
|
|
206
|
-
if (!gateEnabled) {
|
|
207
|
-
return undefined;
|
|
208
|
-
}
|
|
193
|
+
var buildFetchErrorAttribution = exports.buildFetchErrorAttribution = function buildFetchErrorAttribution(error, statusCode, deferred) {
|
|
209
194
|
var reason = classifyFetchErrorReason(error);
|
|
210
195
|
return _objectSpread(_objectSpread({
|
|
211
196
|
reason: reason,
|
|
@@ -287,8 +272,7 @@ var updateCacheErrorPayload = exports.updateCacheErrorPayload = function updateC
|
|
|
287
272
|
};
|
|
288
273
|
/**
|
|
289
274
|
* Payload for `SYNCED_BLOCK_SOURCE_INFO_ORPHANED`. Fired when source-info
|
|
290
|
-
* resolves into a cache that has already been deleted — should be unreachable
|
|
291
|
-
* under `platform_synced_block_patch_14`.
|
|
275
|
+
* resolves into a cache that has already been deleted — should be unreachable.
|
|
292
276
|
*/
|
|
293
277
|
var sourceInfoOrphanedPayload = exports.sourceInfoOrphanedPayload = function sourceInfoOrphanedPayload(resourceId, sourceProduct, context) {
|
|
294
278
|
return {
|
|
@@ -369,8 +353,7 @@ var createSuccessPayload = exports.createSuccessPayload = function createSuccess
|
|
|
369
353
|
*/
|
|
370
354
|
|
|
371
355
|
/**
|
|
372
|
-
* Operational `syncedBlockCreate` success event,
|
|
373
|
-
* `platform_editor_blocks_patch_4`, with the `blockInstanceId` join key and,
|
|
356
|
+
* Operational `syncedBlockCreate` success event with the `blockInstanceId` join key and,
|
|
374
357
|
* when available, the `inputMethod` + `createdEmpty` creation-type signals.
|
|
375
358
|
*/
|
|
376
359
|
var createSuccessOperationalPayload = exports.createSuccessOperationalPayload = function createSuccessOperationalPayload(resourceId, blockInstanceId, sourceProduct, enrichment) {
|
|
@@ -394,8 +377,7 @@ var createSuccessOperationalPayload = exports.createSuccessOperationalPayload =
|
|
|
394
377
|
};
|
|
395
378
|
|
|
396
379
|
/**
|
|
397
|
-
* Operational first-content-added event
|
|
398
|
-
* `platform_editor_blocks_patch_4`. Fired once when a block created empty first
|
|
380
|
+
* Operational first-content-added event. Fired once when a block created empty first
|
|
399
381
|
* gains user content. Join keys only (`resourceId` + `blockInstanceId`), no user
|
|
400
382
|
* content (PII-safe).
|
|
401
383
|
*/
|
|
@@ -431,9 +413,8 @@ var updateSuccessPayload = exports.updateSuccessPayload = function updateSuccess
|
|
|
431
413
|
};
|
|
432
414
|
|
|
433
415
|
/**
|
|
434
|
-
* Optional enrichment for the `syncedBlockDelete` success event
|
|
435
|
-
*
|
|
436
|
-
* is unchanged; `blockInstanceId` is the bare-uuid join key.
|
|
416
|
+
* Optional enrichment for the `syncedBlockDelete` success event. All fields are
|
|
417
|
+
* optional for extensibility; `blockInstanceId` is the bare-uuid join key.
|
|
437
418
|
*/
|
|
438
419
|
|
|
439
420
|
var deleteSuccessPayload = exports.deleteSuccessPayload = function deleteSuccessPayload(resourceId, sourceProduct, enrichment) {
|
|
@@ -40,8 +40,7 @@ export let SyncBlockError = /*#__PURE__*/function (SyncBlockError) {
|
|
|
40
40
|
* asynchronously and `destroy()` nulls it on orphaned managers, so queued/
|
|
41
41
|
* in-flight ops throw `Data provider not set` — previously mis-logged as a real
|
|
42
42
|
* error. These let throw and catch sites agree on one non-string-matched signal
|
|
43
|
-
* so the residual false errors are suppressed.
|
|
44
|
-
* `platform_editor_blocks_patch_3`.
|
|
43
|
+
* so the residual false errors are suppressed.
|
|
45
44
|
*
|
|
46
45
|
* NB: these intentionally live here rather than in a dedicated module to avoid
|
|
47
46
|
* adding a downstream file to consuming Jira packages' Thunderstone complexity.
|
|
@@ -32,10 +32,10 @@ export const useFetchSyncBlockData = (manager, resourceId, localId, fireAnalytic
|
|
|
32
32
|
// On Jira the data provider is wired asynchronously, so the manager can be
|
|
33
33
|
// constructed with `dataProvider === undefined`. Fetching/subscribing in that
|
|
34
34
|
// window throws `Data provider not set`, logged as a false fetch error
|
|
35
|
-
// (EDITOR-7860).
|
|
35
|
+
// (EDITOR-7860). Check readiness; once the provider resolves a new manager
|
|
36
36
|
// instance is created so `referenceManager` changes identity and the effect
|
|
37
37
|
// below re-runs, subscribing exactly once.
|
|
38
|
-
const isDataProviderReady =
|
|
38
|
+
const isDataProviderReady = (_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;
|
|
39
39
|
const reloadData = useCallback(async () => {
|
|
40
40
|
if (isLoading) {
|
|
41
41
|
return;
|
|
@@ -61,8 +61,7 @@ export const useFetchSyncBlockData = (manager, resourceId, localId, fireAnalytic
|
|
|
61
61
|
// EDITOR-7860: benign not-ready throw — emit no error and stay loading
|
|
62
62
|
// so it resolves on retry once the provider is wired. Checked before
|
|
63
63
|
// `logException` so the benign case produces no exception-tracker noise.
|
|
64
|
-
|
|
65
|
-
if (isProviderNotReadyError(error) && fg('platform_editor_blocks_patch_3')) {
|
|
64
|
+
if (isProviderNotReadyError(error)) {
|
|
66
65
|
setFetchState(prev => ({
|
|
67
66
|
...prev,
|
|
68
67
|
isLoading: true
|
|
@@ -72,7 +71,7 @@ export const useFetchSyncBlockData = (manager, resourceId, localId, fireAnalytic
|
|
|
72
71
|
logException(error, {
|
|
73
72
|
location: 'editor-synced-block-provider/useFetchSyncBlockData'
|
|
74
73
|
});
|
|
75
|
-
fireAnalyticsEvent === null || fireAnalyticsEvent === void 0 ? void 0 : fireAnalyticsEvent(fetchErrorPayload(error.message, resourceId, getSourceProductFromResourceIdSafe(resourceId), buildFetchErrorAttribution(
|
|
74
|
+
fireAnalyticsEvent === null || fireAnalyticsEvent === void 0 ? void 0 : fireAnalyticsEvent(fetchErrorPayload(error.message, resourceId, getSourceProductFromResourceIdSafe(resourceId), buildFetchErrorAttribution(error.message)));
|
|
76
75
|
|
|
77
76
|
// Thread the PII-safe original message/name so the renderer can de-opaque
|
|
78
77
|
// this otherwise-bare `errored` failure. `originalMessage` carries the
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import _defineProperty from "@babel/runtime/helpers/defineProperty";
|
|
2
2
|
import isEqual from 'lodash/isEqual';
|
|
3
3
|
import { logException } from '@atlaskit/editor-common/monitoring';
|
|
4
|
-
import { fg } from '@atlaskit/platform-feature-flags';
|
|
5
4
|
import { isProviderNotReadyError, ProviderNotReadyError, SyncBlockError } from '../common/types';
|
|
6
5
|
import { buildErrorAttribution, buildFetchErrorAttribution, cacheDeletionForcedPayload, fetchErrorPayload, fetchSuccessPayload, getSourceInfoErrorPayload, sourceInfoOrphanedPayload, updateReferenceErrorPayload } from '../utils/errorHandling';
|
|
7
6
|
import { getFetchExperience, getFetchSourceInfoExperience, getSaveReferenceExperience } from '../utils/experienceTracking';
|
|
@@ -16,7 +15,7 @@ const ENTITY_NOT_FOUND_MAX_RETRIES = 3;
|
|
|
16
15
|
const ENTITY_NOT_FOUND_INITIAL_DELAY_MS = 2000;
|
|
17
16
|
|
|
18
17
|
// Grace period before a cache entry is removed after the last subscriber
|
|
19
|
-
// unsubscribes
|
|
18
|
+
// unsubscribes. Guards are
|
|
20
19
|
// re-checked at fire time; if any are positive, the timer is rescheduled.
|
|
21
20
|
const CACHE_DELETION_GRACE_PERIOD_MS = 30_000;
|
|
22
21
|
// Max reschedules before force-deleting with an analytics event (~5 min).
|
|
@@ -44,7 +43,7 @@ export class ReferenceSyncBlockStoreManager {
|
|
|
44
43
|
_defineProperty(this, "entityNotFoundRetryCount", new Map());
|
|
45
44
|
_defineProperty(this, "entityNotFoundRetryTimers", new Map());
|
|
46
45
|
// Pending cache deletion timers keyed by resourceId (gated by
|
|
47
|
-
//
|
|
46
|
+
// Cancelled when a subscriber re-attaches.
|
|
48
47
|
_defineProperty(this, "pendingCacheDeletions", new Map());
|
|
49
48
|
// Reschedule counter per resource — reset on actual deletion or re-subscribe.
|
|
50
49
|
_defineProperty(this, "cacheDeletionRescheduleCounts", new Map());
|
|
@@ -67,7 +66,7 @@ export class ReferenceSyncBlockStoreManager {
|
|
|
67
66
|
this.isCacheDirty = true;
|
|
68
67
|
},
|
|
69
68
|
// Delegate cache lifecycle to the store manager so guards can be
|
|
70
|
-
// checked atomically
|
|
69
|
+
// checked atomically.
|
|
71
70
|
scheduleCacheDeletion: rid => this.scheduleCacheDeletion(rid),
|
|
72
71
|
cancelPendingCacheDeletion: rid => this.cancelPendingCacheDeletion(rid)
|
|
73
72
|
});
|
|
@@ -426,11 +425,8 @@ export class ReferenceSyncBlockStoreManager {
|
|
|
426
425
|
}
|
|
427
426
|
if (!this.dataProvider) {
|
|
428
427
|
// EDITOR-7860: tag the throw so catch sites can suppress the benign
|
|
429
|
-
// not-ready/torn-down case.
|
|
430
|
-
|
|
431
|
-
throw new ProviderNotReadyError();
|
|
432
|
-
}
|
|
433
|
-
throw new Error('Data provider not set');
|
|
428
|
+
// not-ready/torn-down case.
|
|
429
|
+
throw new ProviderNotReadyError();
|
|
434
430
|
}
|
|
435
431
|
nodesToFetch.forEach(node => {
|
|
436
432
|
this.syncBlockFetchDataRequests.set(node.attrs.resourceId, true);
|
|
@@ -477,7 +473,7 @@ export class ReferenceSyncBlockStoreManager {
|
|
|
477
473
|
// No resourceId means we cannot derive a sourceProduct here; intentionally omit.
|
|
478
474
|
// Classify on the structured `type` first, falling back to the free-text
|
|
479
475
|
// `reason`/payload (EDITOR-7862).
|
|
480
|
-
(_this$fireAnalyticsEv8 = this.fireAnalyticsEvent) === null || _this$fireAnalyticsEv8 === void 0 ? void 0 : _this$fireAnalyticsEv8.call(this, fetchErrorPayload(payload, undefined, undefined, buildFetchErrorAttribution(
|
|
476
|
+
(_this$fireAnalyticsEv8 = this.fireAnalyticsEvent) === null || _this$fireAnalyticsEv8 === void 0 ? void 0 : _this$fireAnalyticsEv8.call(this, fetchErrorPayload(payload, undefined, undefined, buildFetchErrorAttribution(((_syncBlockInstance$er3 = syncBlockInstance.error) === null || _syncBlockInstance$er3 === void 0 ? void 0 : _syncBlockInstance$er3.type) || ((_syncBlockInstance$er4 = syncBlockInstance.error) === null || _syncBlockInstance$er4 === void 0 ? void 0 : _syncBlockInstance$er4.reason) || payload, (_syncBlockInstance$er5 = syncBlockInstance.error) === null || _syncBlockInstance$er5 === void 0 ? void 0 : _syncBlockInstance$er5.statusCode)));
|
|
481
477
|
return;
|
|
482
478
|
}
|
|
483
479
|
const existingSyncBlock = this.getFromCache(syncBlockInstance.resourceId);
|
|
@@ -508,13 +504,13 @@ export class ReferenceSyncBlockStoreManager {
|
|
|
508
504
|
var _this$entityNotFoundR;
|
|
509
505
|
// Skip error analytics when EntityNotFound will be retried, to avoid
|
|
510
506
|
// inflating error-rate metrics with expected transient failures
|
|
511
|
-
const isRetryingEntityNotFound = syncBlockInstance.error.type === SyncBlockError.EntityNotFound && ((_this$entityNotFoundR = this.entityNotFoundRetryCount.get(syncBlockInstance.resourceId)) !== null && _this$entityNotFoundR !== void 0 ? _this$entityNotFoundR : 0) < ENTITY_NOT_FOUND_MAX_RETRIES
|
|
507
|
+
const isRetryingEntityNotFound = syncBlockInstance.error.type === SyncBlockError.EntityNotFound && ((_this$entityNotFoundR = this.entityNotFoundRetryCount.get(syncBlockInstance.resourceId)) !== null && _this$entityNotFoundR !== void 0 ? _this$entityNotFoundR : 0) < ENTITY_NOT_FOUND_MAX_RETRIES;
|
|
512
508
|
if (!isRetryingEntityNotFound) {
|
|
513
509
|
var _this$fireAnalyticsEv9, _syncBlockInstance$da, _syncBlockInstance$da2;
|
|
514
510
|
// Classify on the structured `type` (a `SyncBlockError` enum value) first,
|
|
515
511
|
// falling back to the free-text `reason` so source-state/permission strings
|
|
516
512
|
// are still bucketed (EDITOR-7862). The emitted `error` attribute is unchanged.
|
|
517
|
-
(_this$fireAnalyticsEv9 = this.fireAnalyticsEvent) === null || _this$fireAnalyticsEv9 === void 0 ? void 0 : _this$fireAnalyticsEv9.call(this, fetchErrorPayload(syncBlockInstance.error.reason || syncBlockInstance.error.type, syncBlockInstance.resourceId, (_syncBlockInstance$da = (_syncBlockInstance$da2 = syncBlockInstance.data) === null || _syncBlockInstance$da2 === void 0 ? void 0 : _syncBlockInstance$da2.product) !== null && _syncBlockInstance$da !== void 0 ? _syncBlockInstance$da : getSourceProductFromResourceIdSafe(syncBlockInstance.resourceId), buildFetchErrorAttribution(
|
|
513
|
+
(_this$fireAnalyticsEv9 = this.fireAnalyticsEvent) === null || _this$fireAnalyticsEv9 === void 0 ? void 0 : _this$fireAnalyticsEv9.call(this, fetchErrorPayload(syncBlockInstance.error.reason || syncBlockInstance.error.type, syncBlockInstance.resourceId, (_syncBlockInstance$da = (_syncBlockInstance$da2 = syncBlockInstance.data) === null || _syncBlockInstance$da2 === void 0 ? void 0 : _syncBlockInstance$da2.product) !== null && _syncBlockInstance$da !== void 0 ? _syncBlockInstance$da : getSourceProductFromResourceIdSafe(syncBlockInstance.resourceId), buildFetchErrorAttribution(syncBlockInstance.error.type || syncBlockInstance.error.reason, syncBlockInstance.error.statusCode)));
|
|
518
514
|
}
|
|
519
515
|
if (syncBlockInstance.error.type === SyncBlockError.NotFound || syncBlockInstance.error.type === SyncBlockError.Forbidden) {
|
|
520
516
|
hasExpectedError = true;
|
|
@@ -522,9 +518,7 @@ export class ReferenceSyncBlockStoreManager {
|
|
|
522
518
|
// Schedule a retry for EntityNotFound — the source block may be in
|
|
523
519
|
// the process of being created by a collaborator (race condition
|
|
524
520
|
// between NCS propagation and Block Service createBlock call).
|
|
525
|
-
|
|
526
|
-
this.scheduleEntityNotFoundRetry(syncBlockInstance.resourceId);
|
|
527
|
-
}
|
|
521
|
+
this.scheduleEntityNotFoundRetry(syncBlockInstance.resourceId);
|
|
528
522
|
if (!isRetryingEntityNotFound) {
|
|
529
523
|
hasUnexpectedError = true;
|
|
530
524
|
}
|
|
@@ -550,7 +544,7 @@ export class ReferenceSyncBlockStoreManager {
|
|
|
550
544
|
const existingSyncBlock = this.getFromCache(resourceId);
|
|
551
545
|
// If the cache entry was deleted while the source-info request was
|
|
552
546
|
// in flight, fire an analytics event so the race is observable.
|
|
553
|
-
if (!existingSyncBlock
|
|
547
|
+
if (!existingSyncBlock) {
|
|
554
548
|
var _this$fireAnalyticsEv1;
|
|
555
549
|
(_this$fireAnalyticsEv1 = this.fireAnalyticsEvent) === null || _this$fireAnalyticsEv1 === void 0 ? void 0 : _this$fireAnalyticsEv1.call(this, sourceInfoOrphanedPayload(resourceId, getSourceProductFromResourceIdSafe(resourceId), {
|
|
556
550
|
hasPendingDeletion: this.pendingCacheDeletions.has(resourceId),
|
|
@@ -602,16 +596,14 @@ export class ReferenceSyncBlockStoreManager {
|
|
|
602
596
|
this._providerFactoryManager.deleteFactory(resourceId);
|
|
603
597
|
// Evict in-flight source-info promise and reset reschedule counter
|
|
604
598
|
// so a stale resolution can't silently merge into a re-fetched entry.
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
this.cacheDeletionRescheduleCounts.delete(resourceId);
|
|
608
|
-
}
|
|
599
|
+
this.syncBlockSourceInfoRequests.delete(resourceId);
|
|
600
|
+
this.cacheDeletionRescheduleCounts.delete(resourceId);
|
|
609
601
|
}
|
|
610
602
|
|
|
611
603
|
/**
|
|
612
604
|
* Returns true if the cache entry for `resourceId` is safe to delete:
|
|
613
605
|
* no active subscribers, no in-flight source-info request, and no
|
|
614
|
-
* queued/in-flight batch fetch
|
|
606
|
+
* queued/in-flight batch fetch.
|
|
615
607
|
*/
|
|
616
608
|
canDeleteCache(resourceId) {
|
|
617
609
|
if (this._subscriptionManager.getSubscriptions().has(resourceId)) {
|
|
@@ -628,14 +620,11 @@ export class ReferenceSyncBlockStoreManager {
|
|
|
628
620
|
|
|
629
621
|
/**
|
|
630
622
|
* Schedules cache deletion for `resourceId` after the grace period
|
|
631
|
-
*
|
|
623
|
+
* Called when the last
|
|
632
624
|
* subscriber unsubscribes. Guards are re-checked at fire time; if any
|
|
633
625
|
* are positive the timer is rescheduled up to MAX_RESCHEDULES times.
|
|
634
626
|
*/
|
|
635
627
|
scheduleCacheDeletion(resourceId) {
|
|
636
|
-
if (!fg('platform_synced_block_patch_14')) {
|
|
637
|
-
return;
|
|
638
|
-
}
|
|
639
628
|
if (this.isDestroyed) {
|
|
640
629
|
return;
|
|
641
630
|
}
|
|
@@ -663,13 +652,10 @@ export class ReferenceSyncBlockStoreManager {
|
|
|
663
652
|
|
|
664
653
|
/**
|
|
665
654
|
* Cancels any pending cache deletion timer for `resourceId` and resets the
|
|
666
|
-
* reschedule counter
|
|
655
|
+
* reschedule counter. Called
|
|
667
656
|
* when a new subscriber arrives.
|
|
668
657
|
*/
|
|
669
658
|
cancelPendingCacheDeletion(resourceId) {
|
|
670
|
-
if (!fg('platform_synced_block_patch_14')) {
|
|
671
|
-
return;
|
|
672
|
-
}
|
|
673
659
|
const existing = this.pendingCacheDeletions.get(resourceId);
|
|
674
660
|
if (existing) {
|
|
675
661
|
clearTimeout(existing);
|
|
@@ -804,14 +790,14 @@ export class ReferenceSyncBlockStoreManager {
|
|
|
804
790
|
var _this$fireAnalyticsEv11;
|
|
805
791
|
// EDITOR-7860: benign not-ready/torn-down case — suppress both the
|
|
806
792
|
// exception-tracker log and the analytics event (checked first so the
|
|
807
|
-
// benign case stays fully silent).
|
|
808
|
-
if (isProviderNotReadyError(error)
|
|
793
|
+
// benign case stays fully silent).
|
|
794
|
+
if (isProviderNotReadyError(error)) {
|
|
809
795
|
return () => {};
|
|
810
796
|
}
|
|
811
797
|
logException(error, {
|
|
812
798
|
location: 'editor-synced-block-provider/referenceSyncBlockStoreManager'
|
|
813
799
|
});
|
|
814
|
-
(_this$fireAnalyticsEv11 = this.fireAnalyticsEvent) === null || _this$fireAnalyticsEv11 === void 0 ? void 0 : _this$fireAnalyticsEv11.call(this, fetchErrorPayload(error.message, undefined, undefined, buildFetchErrorAttribution(
|
|
800
|
+
(_this$fireAnalyticsEv11 = this.fireAnalyticsEvent) === null || _this$fireAnalyticsEv11 === void 0 ? void 0 : _this$fireAnalyticsEv11.call(this, fetchErrorPayload(error.message, undefined, undefined, buildFetchErrorAttribution(error.message)));
|
|
815
801
|
return () => {};
|
|
816
802
|
}
|
|
817
803
|
}
|
|
@@ -919,7 +905,7 @@ export class ReferenceSyncBlockStoreManager {
|
|
|
919
905
|
(_this$saveExperience2 = this.saveExperience) === null || _this$saveExperience2 === void 0 ? void 0 : _this$saveExperience2.failure({
|
|
920
906
|
reason: updateResult.error || 'Failed to update reference synced blocks on the document'
|
|
921
907
|
});
|
|
922
|
-
(_this$fireAnalyticsEv12 = this.fireAnalyticsEvent) === null || _this$fireAnalyticsEv12 === void 0 ? void 0 : _this$fireAnalyticsEv12.call(this, updateReferenceErrorPayload(updateResult.error || 'Failed to update reference synced blocks on the document', undefined, undefined, buildErrorAttribution(
|
|
908
|
+
(_this$fireAnalyticsEv12 = this.fireAnalyticsEvent) === null || _this$fireAnalyticsEv12 === void 0 ? void 0 : _this$fireAnalyticsEv12.call(this, updateReferenceErrorPayload(updateResult.error || 'Failed to update reference synced blocks on the document', undefined, undefined, buildErrorAttribution(updateResult.error, updateResult.statusCode)));
|
|
923
909
|
}
|
|
924
910
|
} catch (error) {
|
|
925
911
|
var _this$saveExperience3, _this$fireAnalyticsEv13;
|
|
@@ -933,7 +919,7 @@ export class ReferenceSyncBlockStoreManager {
|
|
|
933
919
|
// No `resourceId` available in this catch — sourceProduct is intentionally omitted.
|
|
934
920
|
// No structured SyncBlockError/status here, so the attribution `reason` falls back
|
|
935
921
|
// to `unknown` when the gate is on.
|
|
936
|
-
(_this$fireAnalyticsEv13 = this.fireAnalyticsEvent) === null || _this$fireAnalyticsEv13 === void 0 ? void 0 : _this$fireAnalyticsEv13.call(this, updateReferenceErrorPayload(error.message, undefined, undefined, buildErrorAttribution(
|
|
922
|
+
(_this$fireAnalyticsEv13 = this.fireAnalyticsEvent) === null || _this$fireAnalyticsEv13 === void 0 ? void 0 : _this$fireAnalyticsEv13.call(this, updateReferenceErrorPayload(error.message, undefined, undefined, buildErrorAttribution()));
|
|
937
923
|
} finally {
|
|
938
924
|
if (!success) {
|
|
939
925
|
// set isCacheDirty back to true for cases where it failed to update the reference synced blocks on the BE
|
|
@@ -998,16 +984,8 @@ export class ReferenceSyncBlockStoreManager {
|
|
|
998
984
|
});
|
|
999
985
|
this.fireAnalyticsEvent = undefined;
|
|
1000
986
|
|
|
1001
|
-
//
|
|
1002
|
-
//
|
|
1003
|
-
//
|
|
1004
|
-
// cache data that a sibling/successor manager (e.g. the editor
|
|
1005
|
-
// instance that mounts immediately after the renderer unmounts during
|
|
1006
|
-
// the view-mode transition) is about to read.
|
|
1007
|
-
// Let entries age out naturally instead — the in-memory cache is
|
|
1008
|
-
// naturally bounded by `maxSize` (LRU) and cleared on hard navigation.
|
|
1009
|
-
if (!fg('platform_synced_block_patch_14')) {
|
|
1010
|
-
syncBlockInMemorySessionCache.clear();
|
|
1011
|
-
}
|
|
987
|
+
// `destroy()` is wired to React component unmount via
|
|
988
|
+
// `useMemoizedSyncBlockStoreManager`. Let the in-memory session cache age
|
|
989
|
+
// out naturally instead of clearing it during a view-mode transition.
|
|
1012
990
|
}
|
|
1013
991
|
}
|