@atlaskit/editor-synced-block-provider 8.5.7 → 8.6.1
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 +24 -0
- package/dist/cjs/store-manager/referenceSyncBlockStoreManager.js +1 -1
- package/dist/cjs/store-manager/sourceSyncBlockStoreManager.js +72 -13
- package/dist/cjs/store-manager/syncBlockSubscriptionManager.js +216 -66
- package/dist/cjs/utils/errorHandling.js +47 -9
- package/dist/es2019/store-manager/referenceSyncBlockStoreManager.js +1 -1
- package/dist/es2019/store-manager/sourceSyncBlockStoreManager.js +79 -22
- package/dist/es2019/store-manager/syncBlockSubscriptionManager.js +133 -7
- package/dist/es2019/utils/errorHandling.js +50 -6
- package/dist/esm/store-manager/referenceSyncBlockStoreManager.js +1 -1
- package/dist/esm/store-manager/sourceSyncBlockStoreManager.js +73 -14
- package/dist/esm/store-manager/syncBlockSubscriptionManager.js +216 -66
- package/dist/esm/utils/errorHandling.js +46 -8
- package/dist/types/entry-points/errorHandling.d.ts +1 -0
- package/dist/types/store-manager/sourceSyncBlockStoreManager.d.ts +27 -1
- package/dist/types/store-manager/syncBlockSubscriptionManager.d.ts +28 -0
- package/dist/types/utils/errorHandling.d.ts +31 -6
- package/package.json +5 -6
|
@@ -6,6 +6,8 @@ function _unsupportedIterableToArray(r, a) { if (r) { if ("string" == typeof r)
|
|
|
6
6
|
function _arrayLikeToArray(r, a) { (null == a || a > r.length) && (a = r.length); for (var e = 0, n = Array(a); e < a; e++) n[e] = r[e]; return n; }
|
|
7
7
|
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; }
|
|
8
8
|
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; }
|
|
9
|
+
import { bind } from 'bind-event-listener';
|
|
10
|
+
import { getDocument } from '@atlaskit/browser-apis';
|
|
9
11
|
import { logException } from '@atlaskit/editor-common/monitoring';
|
|
10
12
|
import { fg } from '@atlaskit/platform-feature-flags';
|
|
11
13
|
import { buildFetchErrorAttribution, fetchErrorPayload, fetchSuccessPayload } from '../utils/errorHandling';
|
|
@@ -20,6 +22,7 @@ import { getSourceProductFromResourceIdSafe } from '../utils/utils';
|
|
|
20
22
|
|
|
21
23
|
export var SyncBlockSubscriptionManager = /*#__PURE__*/function () {
|
|
22
24
|
function SyncBlockSubscriptionManager(deps) {
|
|
25
|
+
var _this = this;
|
|
23
26
|
_classCallCheck(this, SyncBlockSubscriptionManager);
|
|
24
27
|
_defineProperty(this, "subscriptions", new Map());
|
|
25
28
|
_defineProperty(this, "titleSubscriptions", new Map());
|
|
@@ -34,6 +37,24 @@ export var SyncBlockSubscriptionManager = /*#__PURE__*/function () {
|
|
|
34
37
|
// backoff cap (gate ON)
|
|
35
38
|
_defineProperty(this, "retryAttempts", new Map());
|
|
36
39
|
_defineProperty(this, "pendingRetries", new Map());
|
|
40
|
+
// Resources whose reconnection exhausted while the tab was hidden: parked here and
|
|
41
|
+
// re-armed on the next online / visibilitychange→visible instead of surfacing a
|
|
42
|
+
// terminal error. Only a re-armed attempt that also exhausts while visible fails.
|
|
43
|
+
_defineProperty(this, "deferredExhausted", new Set());
|
|
44
|
+
_defineProperty(this, "wakeDebounceTimer", null);
|
|
45
|
+
// `online` always sweeps (network is back regardless of tab visibility); a
|
|
46
|
+
// `visibilitychange` only sweeps when the tab became visible.
|
|
47
|
+
_defineProperty(this, "onOnline", function () {
|
|
48
|
+
return _this.scheduleWakeSweep();
|
|
49
|
+
});
|
|
50
|
+
_defineProperty(this, "onVisibilityChange", function () {
|
|
51
|
+
if (!_this.isDocumentHidden()) {
|
|
52
|
+
_this.scheduleWakeSweep();
|
|
53
|
+
}
|
|
54
|
+
});
|
|
55
|
+
// Cleanup fns returned by `bind` for the registered wake listeners; a non-empty
|
|
56
|
+
// array means listeners are currently registered. Emptied on teardown.
|
|
57
|
+
_defineProperty(this, "wakeListenerCleanups", []);
|
|
37
58
|
this.deps = deps;
|
|
38
59
|
}
|
|
39
60
|
|
|
@@ -96,25 +117,25 @@ export var SyncBlockSubscriptionManager = /*#__PURE__*/function () {
|
|
|
96
117
|
}, {
|
|
97
118
|
key: "onSubscriptionsChanged",
|
|
98
119
|
value: function onSubscriptionsChanged(listener) {
|
|
99
|
-
var
|
|
120
|
+
var _this2 = this;
|
|
100
121
|
this.subscriptionChangeListeners.add(listener);
|
|
101
122
|
return function () {
|
|
102
|
-
|
|
123
|
+
_this2.subscriptionChangeListeners.delete(listener);
|
|
103
124
|
};
|
|
104
125
|
}
|
|
105
126
|
}, {
|
|
106
127
|
key: "notifySubscriptionChangeListeners",
|
|
107
128
|
value: function notifySubscriptionChangeListeners() {
|
|
108
|
-
var
|
|
129
|
+
var _this3 = this;
|
|
109
130
|
this.subscriptionChangeListeners.forEach(function (listener) {
|
|
110
131
|
try {
|
|
111
132
|
listener();
|
|
112
133
|
} catch (error) {
|
|
113
|
-
var
|
|
134
|
+
var _this3$deps$getFireAn;
|
|
114
135
|
logException(error, {
|
|
115
136
|
location: 'editor-synced-block-provider/syncBlockSubscriptionManager/notifySubscriptionChangeListeners'
|
|
116
137
|
});
|
|
117
|
-
(
|
|
138
|
+
(_this3$deps$getFireAn = _this3.deps.getFireAnalyticsEvent()) === null || _this3$deps$getFireAn === void 0 || _this3$deps$getFireAn(fetchErrorPayload(error.message, undefined, undefined, buildFetchErrorAttribution(fg('platform_editor_blocks_patch_3'), error.message)));
|
|
118
139
|
}
|
|
119
140
|
});
|
|
120
141
|
}
|
|
@@ -134,7 +155,7 @@ export var SyncBlockSubscriptionManager = /*#__PURE__*/function () {
|
|
|
134
155
|
}, {
|
|
135
156
|
key: "subscribeToSyncBlock",
|
|
136
157
|
value: function subscribeToSyncBlock(resourceId, localId, callback) {
|
|
137
|
-
var
|
|
158
|
+
var _this4 = this;
|
|
138
159
|
// Cancel any pending cache deletion for this resourceId.
|
|
139
160
|
// This handles the case where a block is moved - the old component unmounts
|
|
140
161
|
// (scheduling deletion) but the new component mounts and subscribes before
|
|
@@ -174,25 +195,25 @@ export var SyncBlockSubscriptionManager = /*#__PURE__*/function () {
|
|
|
174
195
|
this.setupSubscription(resourceId);
|
|
175
196
|
}
|
|
176
197
|
return function () {
|
|
177
|
-
var resourceSubscriptions =
|
|
198
|
+
var resourceSubscriptions = _this4.subscriptions.get(resourceId);
|
|
178
199
|
if (resourceSubscriptions) {
|
|
179
200
|
// Unsubscription means a reference synced block is removed from the document
|
|
180
|
-
|
|
201
|
+
_this4.deps.markCacheDirty();
|
|
181
202
|
delete resourceSubscriptions[localId];
|
|
182
203
|
var remainingIds = Object.keys(resourceSubscriptions);
|
|
183
204
|
if (remainingIds.length === 0) {
|
|
184
|
-
|
|
205
|
+
_this4.subscriptions.delete(resourceId);
|
|
185
206
|
|
|
186
207
|
// Clean up GraphQL subscription when no more local subscribers
|
|
187
|
-
|
|
208
|
+
_this4.cleanupSubscription(resourceId);
|
|
188
209
|
|
|
189
210
|
// Notify listeners that subscription was removed
|
|
190
|
-
|
|
211
|
+
_this4.notifySubscriptionChangeListeners();
|
|
191
212
|
|
|
192
213
|
// Under the flag, delegate cache deletion to the store manager
|
|
193
214
|
// which uses a 30s grace period with guard re-checks.
|
|
194
215
|
if (fg('platform_synced_block_patch_14')) {
|
|
195
|
-
|
|
216
|
+
_this4.deps.scheduleCacheDeletion(resourceId);
|
|
196
217
|
} else {
|
|
197
218
|
// Legacy path (unchanged): delay cache deletion to handle
|
|
198
219
|
// block moves (unmount/remount). When a block is moved, the
|
|
@@ -203,18 +224,18 @@ export var SyncBlockSubscriptionManager = /*#__PURE__*/function () {
|
|
|
203
224
|
// TODO: EDITOR-4152 - Rework this logic (superseded by
|
|
204
225
|
// `platform_synced_block_patch_14`).
|
|
205
226
|
var deletionTimeout = setTimeout(function () {
|
|
206
|
-
var hasSubscribers =
|
|
227
|
+
var hasSubscribers = _this4.subscriptions.has(resourceId);
|
|
207
228
|
|
|
208
229
|
// Only delete if still no subscribers (wasn't re-subscribed)
|
|
209
230
|
if (!hasSubscribers) {
|
|
210
|
-
|
|
231
|
+
_this4.deps.deleteFromCache(resourceId);
|
|
211
232
|
}
|
|
212
|
-
|
|
233
|
+
_this4.pendingCacheDeletions.delete(resourceId);
|
|
213
234
|
}, 1000);
|
|
214
|
-
|
|
235
|
+
_this4.pendingCacheDeletions.set(resourceId, deletionTimeout);
|
|
215
236
|
}
|
|
216
237
|
} else {
|
|
217
|
-
|
|
238
|
+
_this4.subscriptions.set(resourceId, resourceSubscriptions);
|
|
218
239
|
}
|
|
219
240
|
}
|
|
220
241
|
};
|
|
@@ -223,7 +244,7 @@ export var SyncBlockSubscriptionManager = /*#__PURE__*/function () {
|
|
|
223
244
|
key: "subscribeToSourceTitle",
|
|
224
245
|
value: function subscribeToSourceTitle(node, callback) {
|
|
225
246
|
var _cachedData$data,
|
|
226
|
-
|
|
247
|
+
_this5 = this;
|
|
227
248
|
// check node is a sync block, as we only support sync block subscriptions
|
|
228
249
|
if (node.type.name !== 'syncBlock') {
|
|
229
250
|
return function () {};
|
|
@@ -243,13 +264,13 @@ export var SyncBlockSubscriptionManager = /*#__PURE__*/function () {
|
|
|
243
264
|
var resourceSubscriptions = this.titleSubscriptions.get(resourceId) || {};
|
|
244
265
|
this.titleSubscriptions.set(resourceId, _objectSpread(_objectSpread({}, resourceSubscriptions), {}, _defineProperty({}, localId, callback)));
|
|
245
266
|
return function () {
|
|
246
|
-
var resourceSubscriptions =
|
|
267
|
+
var resourceSubscriptions = _this5.titleSubscriptions.get(resourceId);
|
|
247
268
|
if (resourceSubscriptions) {
|
|
248
269
|
delete resourceSubscriptions[localId];
|
|
249
270
|
if (Object.keys(resourceSubscriptions).length === 0) {
|
|
250
|
-
|
|
271
|
+
_this5.titleSubscriptions.delete(resourceId);
|
|
251
272
|
} else {
|
|
252
|
-
|
|
273
|
+
_this5.titleSubscriptions.set(resourceId, resourceSubscriptions);
|
|
253
274
|
}
|
|
254
275
|
}
|
|
255
276
|
};
|
|
@@ -281,7 +302,7 @@ export var SyncBlockSubscriptionManager = /*#__PURE__*/function () {
|
|
|
281
302
|
}, {
|
|
282
303
|
key: "setupSubscription",
|
|
283
304
|
value: function setupSubscription(resourceId) {
|
|
284
|
-
var
|
|
305
|
+
var _this6 = this;
|
|
285
306
|
if (this.graphqlSubscriptions.has(resourceId)) {
|
|
286
307
|
return;
|
|
287
308
|
}
|
|
@@ -290,7 +311,7 @@ export var SyncBlockSubscriptionManager = /*#__PURE__*/function () {
|
|
|
290
311
|
return;
|
|
291
312
|
}
|
|
292
313
|
var unsubscribe = dataProvider.subscribeToBlockUpdates(resourceId, function (syncBlockInstance) {
|
|
293
|
-
|
|
314
|
+
_this6.handleGraphQLUpdate(syncBlockInstance);
|
|
294
315
|
}, function (error) {
|
|
295
316
|
logException(error, {
|
|
296
317
|
location: 'editor-synced-block-provider/syncBlockSubscriptionManager/graphql-subscription'
|
|
@@ -303,12 +324,12 @@ export var SyncBlockSubscriptionManager = /*#__PURE__*/function () {
|
|
|
303
324
|
// would return undefined; the structured attribution (EDITOR-7862) is therefore
|
|
304
325
|
// applied at the gate-ON exhaustion site in scheduleReconnection instead.
|
|
305
326
|
if (!fg('platform_editor_blocks_patch_3')) {
|
|
306
|
-
var
|
|
307
|
-
(
|
|
327
|
+
var _this6$deps$getFireAn;
|
|
328
|
+
(_this6$deps$getFireAn = _this6.deps.getFireAnalyticsEvent()) === null || _this6$deps$getFireAn === void 0 || _this6$deps$getFireAn(fetchErrorPayload(error.message, resourceId, getSourceProductFromResourceIdSafe(resourceId)));
|
|
308
329
|
}
|
|
309
|
-
|
|
330
|
+
_this6.handleSubscriptionTerminated(resourceId);
|
|
310
331
|
}, function () {
|
|
311
|
-
|
|
332
|
+
_this6.handleSubscriptionTerminated(resourceId);
|
|
312
333
|
});
|
|
313
334
|
if (unsubscribe) {
|
|
314
335
|
this.graphqlSubscriptions.set(resourceId, unsubscribe);
|
|
@@ -342,36 +363,48 @@ export var SyncBlockSubscriptionManager = /*#__PURE__*/function () {
|
|
|
342
363
|
key: "scheduleReconnection",
|
|
343
364
|
value: function scheduleReconnection(resourceId) {
|
|
344
365
|
var _this$retryAttempts$g,
|
|
345
|
-
|
|
366
|
+
_this7 = this;
|
|
346
367
|
var attempts = (_this$retryAttempts$g = this.retryAttempts.get(resourceId)) !== null && _this$retryAttempts$g !== void 0 ? _this$retryAttempts$g : 0;
|
|
347
368
|
var maxAttempts = this.getMaxRetryAttempts();
|
|
348
369
|
if (attempts >= maxAttempts) {
|
|
349
|
-
var _this$deps$
|
|
370
|
+
var _this$deps$getFireAna2;
|
|
350
371
|
// Exhausted all attempts — the only place a WS drop surfaces as a
|
|
351
372
|
// fetch error under the gate (EDITOR-7861).
|
|
352
373
|
var errorMessage = "Subscription reconnection failed after ".concat(attempts, " attempts");
|
|
374
|
+
|
|
375
|
+
// Tab hidden at exhaustion: don't surface a terminal failure (user isn't
|
|
376
|
+
// looking, and most exhaustions self-recover once foregrounded). Park + re-arm
|
|
377
|
+
// on wake, emitting a benign `deferred` signal so suppression stays auditable.
|
|
378
|
+
var shouldDefer = fg('platform_editor_blocks_patch_3') && this.isDocumentHidden();
|
|
379
|
+
if (shouldDefer) {
|
|
380
|
+
var _this$deps$getFireAna;
|
|
381
|
+
this.deferredExhausted.add(resourceId);
|
|
382
|
+
this.registerWakeListeners();
|
|
383
|
+
(_this$deps$getFireAna = this.deps.getFireAnalyticsEvent()) === null || _this$deps$getFireAna === void 0 || _this$deps$getFireAna(fetchErrorPayload(errorMessage, resourceId, getSourceProductFromResourceIdSafe(resourceId), buildFetchErrorAttribution(true, errorMessage, undefined, /* deferred */true)));
|
|
384
|
+
return;
|
|
385
|
+
}
|
|
353
386
|
logException(new Error(errorMessage), {
|
|
354
387
|
location: 'editor-synced-block-provider/syncBlockSubscriptionManager/max-retries-exhausted'
|
|
355
388
|
});
|
|
356
|
-
(_this$deps$
|
|
389
|
+
(_this$deps$getFireAna2 = this.deps.getFireAnalyticsEvent()) === null || _this$deps$getFireAna2 === void 0 || _this$deps$getFireAna2(fetchErrorPayload(errorMessage, resourceId, getSourceProductFromResourceIdSafe(resourceId), buildFetchErrorAttribution(fg('platform_editor_blocks_patch_3'), errorMessage)));
|
|
357
390
|
return;
|
|
358
391
|
}
|
|
359
392
|
var delay = this.getReconnectionDelay(attempts);
|
|
360
393
|
var timer = setTimeout(function () {
|
|
361
|
-
|
|
394
|
+
_this7.pendingRetries.delete(resourceId);
|
|
362
395
|
|
|
363
396
|
// Only re-subscribe if still relevant
|
|
364
|
-
if (
|
|
365
|
-
|
|
397
|
+
if (_this7.subscriptions.has(resourceId) && _this7.shouldUseRealTime()) {
|
|
398
|
+
_this7.setupSubscription(resourceId);
|
|
366
399
|
|
|
367
400
|
// Only increment if the subscription was actually established
|
|
368
401
|
// (setupSubscription may be a no-op if another code path already re-established it)
|
|
369
|
-
if (
|
|
370
|
-
|
|
402
|
+
if (_this7.graphqlSubscriptions.has(resourceId)) {
|
|
403
|
+
_this7.retryAttempts.set(resourceId, attempts + 1);
|
|
371
404
|
}
|
|
372
405
|
} else {
|
|
373
406
|
// Conditions no longer met — clean up stale retry state
|
|
374
|
-
|
|
407
|
+
_this7.retryAttempts.delete(resourceId);
|
|
375
408
|
}
|
|
376
409
|
}, delay);
|
|
377
410
|
this.pendingRetries.set(resourceId, timer);
|
|
@@ -386,6 +419,106 @@ export var SyncBlockSubscriptionManager = /*#__PURE__*/function () {
|
|
|
386
419
|
value: function resetRetryCount(resourceId) {
|
|
387
420
|
this.retryAttempts.delete(resourceId);
|
|
388
421
|
}
|
|
422
|
+
|
|
423
|
+
/**
|
|
424
|
+
* Whether the tab is currently hidden. SSR/non-browser is treated as "not hidden" so
|
|
425
|
+
* we never defer where wake events can't fire (realtime never subscribes there anyway).
|
|
426
|
+
*/
|
|
427
|
+
}, {
|
|
428
|
+
key: "isDocumentHidden",
|
|
429
|
+
value: function isDocumentHidden() {
|
|
430
|
+
var _getDocument;
|
|
431
|
+
return ((_getDocument = getDocument()) === null || _getDocument === void 0 ? void 0 : _getDocument.hidden) === true;
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
/**
|
|
435
|
+
* Lazily register shared `online` / `visibilitychange` listeners on first deferral.
|
|
436
|
+
* Torn down by `unregisterWakeListeners` (via `handleWake`). No-op in SSR / non-browser.
|
|
437
|
+
*/
|
|
438
|
+
}, {
|
|
439
|
+
key: "registerWakeListeners",
|
|
440
|
+
value: function registerWakeListeners() {
|
|
441
|
+
if (this.wakeListenerCleanups.length > 0 || typeof window === 'undefined') {
|
|
442
|
+
return;
|
|
443
|
+
}
|
|
444
|
+
this.wakeListenerCleanups.push(bind(window, {
|
|
445
|
+
type: 'online',
|
|
446
|
+
listener: this.onOnline
|
|
447
|
+
}));
|
|
448
|
+
var doc = getDocument();
|
|
449
|
+
if (doc) {
|
|
450
|
+
this.wakeListenerCleanups.push(bind(doc, {
|
|
451
|
+
type: 'visibilitychange',
|
|
452
|
+
listener: this.onVisibilityChange
|
|
453
|
+
}));
|
|
454
|
+
}
|
|
455
|
+
}
|
|
456
|
+
}, {
|
|
457
|
+
key: "unregisterWakeListeners",
|
|
458
|
+
value: function unregisterWakeListeners() {
|
|
459
|
+
if (this.wakeDebounceTimer !== null) {
|
|
460
|
+
clearTimeout(this.wakeDebounceTimer);
|
|
461
|
+
this.wakeDebounceTimer = null;
|
|
462
|
+
}
|
|
463
|
+
var _iterator = _createForOfIteratorHelper(this.wakeListenerCleanups),
|
|
464
|
+
_step;
|
|
465
|
+
try {
|
|
466
|
+
for (_iterator.s(); !(_step = _iterator.n()).done;) {
|
|
467
|
+
var cleanup = _step.value;
|
|
468
|
+
cleanup();
|
|
469
|
+
}
|
|
470
|
+
} catch (err) {
|
|
471
|
+
_iterator.e(err);
|
|
472
|
+
} finally {
|
|
473
|
+
_iterator.f();
|
|
474
|
+
}
|
|
475
|
+
this.wakeListenerCleanups = [];
|
|
476
|
+
}
|
|
477
|
+
|
|
478
|
+
/**
|
|
479
|
+
* Coalesce a burst of wake events into one debounced re-arm sweep. Callers decide
|
|
480
|
+
* eligibility: `onOnline` always sweeps; `onVisibilityChange` only sweeps when visible.
|
|
481
|
+
*/
|
|
482
|
+
}, {
|
|
483
|
+
key: "scheduleWakeSweep",
|
|
484
|
+
value: function scheduleWakeSweep() {
|
|
485
|
+
var _this8 = this;
|
|
486
|
+
if (this.wakeDebounceTimer !== null) {
|
|
487
|
+
return;
|
|
488
|
+
}
|
|
489
|
+
this.wakeDebounceTimer = setTimeout(function () {
|
|
490
|
+
_this8.wakeDebounceTimer = null;
|
|
491
|
+
_this8.handleWake();
|
|
492
|
+
}, SyncBlockSubscriptionManager.WAKE_DEBOUNCE_MS);
|
|
493
|
+
}
|
|
494
|
+
|
|
495
|
+
/**
|
|
496
|
+
* Re-arm every deferred resource: full reset (attempts→0) + immediate reconnect now
|
|
497
|
+
* the tab is visible / online. Healthy subscriptions are untouched. A re-armed cycle
|
|
498
|
+
* that later exhausts while visible fires the terminal error normally.
|
|
499
|
+
*/
|
|
500
|
+
}, {
|
|
501
|
+
key: "handleWake",
|
|
502
|
+
value: function handleWake() {
|
|
503
|
+
if (this.deferredExhausted.size === 0) {
|
|
504
|
+
this.unregisterWakeListeners();
|
|
505
|
+
return;
|
|
506
|
+
}
|
|
507
|
+
var toRearm = Array.from(this.deferredExhausted);
|
|
508
|
+
this.deferredExhausted.clear();
|
|
509
|
+
for (var _i = 0, _toRearm = toRearm; _i < _toRearm.length; _i++) {
|
|
510
|
+
var resourceId = _toRearm[_i];
|
|
511
|
+
// Only re-arm resources that still have active subscribers and realtime on.
|
|
512
|
+
if (!this.subscriptions.has(resourceId) || !this.shouldUseRealTime()) {
|
|
513
|
+
continue;
|
|
514
|
+
}
|
|
515
|
+
this.resetRetryCount(resourceId);
|
|
516
|
+
this.cancelPendingRetry(resourceId);
|
|
517
|
+
this.setupSubscription(resourceId);
|
|
518
|
+
}
|
|
519
|
+
// No deferred resources remain until another hidden exhaustion re-adds one.
|
|
520
|
+
this.unregisterWakeListeners();
|
|
521
|
+
}
|
|
389
522
|
}, {
|
|
390
523
|
key: "cancelPendingRetry",
|
|
391
524
|
value: function cancelPendingRetry(resourceId) {
|
|
@@ -398,20 +531,23 @@ export var SyncBlockSubscriptionManager = /*#__PURE__*/function () {
|
|
|
398
531
|
}, {
|
|
399
532
|
key: "cancelAllPendingRetries",
|
|
400
533
|
value: function cancelAllPendingRetries() {
|
|
401
|
-
var
|
|
402
|
-
|
|
534
|
+
var _iterator2 = _createForOfIteratorHelper(this.pendingRetries.values()),
|
|
535
|
+
_step2;
|
|
403
536
|
try {
|
|
404
|
-
for (
|
|
405
|
-
var timer =
|
|
537
|
+
for (_iterator2.s(); !(_step2 = _iterator2.n()).done;) {
|
|
538
|
+
var timer = _step2.value;
|
|
406
539
|
clearTimeout(timer);
|
|
407
540
|
}
|
|
408
541
|
} catch (err) {
|
|
409
|
-
|
|
542
|
+
_iterator2.e(err);
|
|
410
543
|
} finally {
|
|
411
|
-
|
|
544
|
+
_iterator2.f();
|
|
412
545
|
}
|
|
413
546
|
this.pendingRetries.clear();
|
|
414
547
|
this.retryAttempts.clear();
|
|
548
|
+
// Nothing left to re-arm: drop parked exhaustions and tear down wake listeners.
|
|
549
|
+
this.deferredExhausted.clear();
|
|
550
|
+
this.unregisterWakeListeners();
|
|
415
551
|
}
|
|
416
552
|
}, {
|
|
417
553
|
key: "cleanupSubscription",
|
|
@@ -423,37 +559,42 @@ export var SyncBlockSubscriptionManager = /*#__PURE__*/function () {
|
|
|
423
559
|
}
|
|
424
560
|
this.cancelPendingRetry(resourceId);
|
|
425
561
|
this.retryAttempts.delete(resourceId);
|
|
562
|
+
// No subscribers left, so this resource can't be re-armed.
|
|
563
|
+
this.deferredExhausted.delete(resourceId);
|
|
564
|
+
if (this.deferredExhausted.size === 0) {
|
|
565
|
+
this.unregisterWakeListeners();
|
|
566
|
+
}
|
|
426
567
|
}
|
|
427
568
|
}, {
|
|
428
569
|
key: "setupSubscriptionsForAllBlocks",
|
|
429
570
|
value: function setupSubscriptionsForAllBlocks() {
|
|
430
|
-
var
|
|
431
|
-
|
|
571
|
+
var _iterator3 = _createForOfIteratorHelper(this.subscriptions.keys()),
|
|
572
|
+
_step3;
|
|
432
573
|
try {
|
|
433
|
-
for (
|
|
434
|
-
var resourceId =
|
|
574
|
+
for (_iterator3.s(); !(_step3 = _iterator3.n()).done;) {
|
|
575
|
+
var resourceId = _step3.value;
|
|
435
576
|
this.setupSubscription(resourceId);
|
|
436
577
|
}
|
|
437
578
|
} catch (err) {
|
|
438
|
-
|
|
579
|
+
_iterator3.e(err);
|
|
439
580
|
} finally {
|
|
440
|
-
|
|
581
|
+
_iterator3.f();
|
|
441
582
|
}
|
|
442
583
|
}
|
|
443
584
|
}, {
|
|
444
585
|
key: "cleanupAll",
|
|
445
586
|
value: function cleanupAll() {
|
|
446
|
-
var
|
|
447
|
-
|
|
587
|
+
var _iterator4 = _createForOfIteratorHelper(this.graphqlSubscriptions.values()),
|
|
588
|
+
_step4;
|
|
448
589
|
try {
|
|
449
|
-
for (
|
|
450
|
-
var unsubscribe =
|
|
590
|
+
for (_iterator4.s(); !(_step4 = _iterator4.n()).done;) {
|
|
591
|
+
var unsubscribe = _step4.value;
|
|
451
592
|
unsubscribe();
|
|
452
593
|
}
|
|
453
594
|
} catch (err) {
|
|
454
|
-
|
|
595
|
+
_iterator4.e(err);
|
|
455
596
|
} finally {
|
|
456
|
-
|
|
597
|
+
_iterator4.f();
|
|
457
598
|
}
|
|
458
599
|
this.graphqlSubscriptions.clear();
|
|
459
600
|
this.cancelAllPendingRetries();
|
|
@@ -468,17 +609,17 @@ export var SyncBlockSubscriptionManager = /*#__PURE__*/function () {
|
|
|
468
609
|
this.useRealTimeSubscriptions = false;
|
|
469
610
|
|
|
470
611
|
// Clear any pending cache deletions
|
|
471
|
-
var
|
|
472
|
-
|
|
612
|
+
var _iterator5 = _createForOfIteratorHelper(this.pendingCacheDeletions.values()),
|
|
613
|
+
_step5;
|
|
473
614
|
try {
|
|
474
|
-
for (
|
|
475
|
-
var timeout =
|
|
615
|
+
for (_iterator5.s(); !(_step5 = _iterator5.n()).done;) {
|
|
616
|
+
var timeout = _step5.value;
|
|
476
617
|
clearTimeout(timeout);
|
|
477
618
|
}
|
|
478
619
|
} catch (err) {
|
|
479
|
-
|
|
620
|
+
_iterator5.e(err);
|
|
480
621
|
} finally {
|
|
481
|
-
|
|
622
|
+
_iterator5.f();
|
|
482
623
|
}
|
|
483
624
|
this.pendingCacheDeletions.clear();
|
|
484
625
|
}
|
|
@@ -490,7 +631,7 @@ export var SyncBlockSubscriptionManager = /*#__PURE__*/function () {
|
|
|
490
631
|
}, {
|
|
491
632
|
key: "handleGraphQLUpdate",
|
|
492
633
|
value: function handleGraphQLUpdate(syncBlockInstance) {
|
|
493
|
-
var
|
|
634
|
+
var _this9 = this;
|
|
494
635
|
if (!syncBlockInstance.resourceId) {
|
|
495
636
|
return;
|
|
496
637
|
}
|
|
@@ -499,21 +640,28 @@ export var SyncBlockSubscriptionManager = /*#__PURE__*/function () {
|
|
|
499
640
|
this.deps.updateCache(resolved);
|
|
500
641
|
if (!syncBlockInstance.error) {
|
|
501
642
|
this.resetRetryCount(syncBlockInstance.resourceId);
|
|
643
|
+
// A healthy update means a parked exhaustion recovered on its own before any
|
|
644
|
+
// wake sweep — drop it and tear down listeners if nothing else is parked.
|
|
645
|
+
if (this.deferredExhausted.delete(syncBlockInstance.resourceId)) {
|
|
646
|
+
if (this.deferredExhausted.size === 0) {
|
|
647
|
+
this.unregisterWakeListeners();
|
|
648
|
+
}
|
|
649
|
+
}
|
|
502
650
|
var callbacks = this.subscriptions.get(syncBlockInstance.resourceId);
|
|
503
651
|
var localIds = callbacks ? Object.keys(callbacks) : [];
|
|
504
652
|
localIds.forEach(function (localId) {
|
|
505
|
-
var
|
|
506
|
-
(
|
|
653
|
+
var _this9$deps$getFireAn, _syncBlockInstance$da, _syncBlockInstance$da2;
|
|
654
|
+
(_this9$deps$getFireAn = _this9.deps.getFireAnalyticsEvent()) === null || _this9$deps$getFireAn === void 0 || _this9$deps$getFireAn(fetchSuccessPayload(syncBlockInstance.resourceId, localId, (_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)));
|
|
507
655
|
});
|
|
508
656
|
this.deps.fetchSyncBlockSourceInfo(resolved.resourceId);
|
|
509
657
|
} else {
|
|
510
|
-
var _syncBlockInstance$er, _syncBlockInstance$er2, _this$deps$
|
|
658
|
+
var _syncBlockInstance$er, _syncBlockInstance$er2, _this$deps$getFireAna3, _syncBlockInstance$da3, _syncBlockInstance$da4, _syncBlockInstance$er3, _syncBlockInstance$er4, _syncBlockInstance$er5;
|
|
511
659
|
var errorMessage = ((_syncBlockInstance$er = syncBlockInstance.error) === null || _syncBlockInstance$er === void 0 ? void 0 : _syncBlockInstance$er.reason) || ((_syncBlockInstance$er2 = syncBlockInstance.error) === null || _syncBlockInstance$er2 === void 0 ? void 0 : _syncBlockInstance$er2.type);
|
|
512
660
|
|
|
513
661
|
// Prefer the structured `type` (a `SyncBlockError` enum value) for classification
|
|
514
662
|
// and fall back to the free-text `reason` so source-state/permission strings are
|
|
515
663
|
// still bucketed (EDITOR-7862). The emitted free-text `error` attribute is unchanged.
|
|
516
|
-
(_this$deps$
|
|
664
|
+
(_this$deps$getFireAna3 = this.deps.getFireAnalyticsEvent()) === null || _this$deps$getFireAna3 === void 0 || _this$deps$getFireAna3(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 : getSourceProductFromResourceIdSafe(syncBlockInstance.resourceId), buildFetchErrorAttribution(fg('platform_editor_blocks_patch_3'), ((_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)));
|
|
517
665
|
}
|
|
518
666
|
}
|
|
519
667
|
}]);
|
|
@@ -525,4 +673,6 @@ _defineProperty(SyncBlockSubscriptionManager, "MAX_RETRY_ATTEMPTS", 5);
|
|
|
525
673
|
// legacy (gate OFF)
|
|
526
674
|
_defineProperty(SyncBlockSubscriptionManager, "MAX_RETRY_ATTEMPTS_HARDENED", 8);
|
|
527
675
|
// gate ON (EDITOR-7861)
|
|
528
|
-
_defineProperty(SyncBlockSubscriptionManager, "MAX_RETRY_DELAY_MS", 30000);
|
|
676
|
+
_defineProperty(SyncBlockSubscriptionManager, "MAX_RETRY_DELAY_MS", 30000);
|
|
677
|
+
// Coalesce wake-event bursts into one re-arm sweep to avoid a reconnection storm.
|
|
678
|
+
_defineProperty(SyncBlockSubscriptionManager, "WAKE_DEBOUNCE_MS", 1000);
|
|
@@ -193,16 +193,19 @@ export var classifyFetchErrorReason = function classifyFetchErrorReason(error) {
|
|
|
193
193
|
* `gateEnabled` is injected by the caller (the store managers evaluate `fg(...)`) so this
|
|
194
194
|
* helper stays pure and trivially unit-testable for both gate states.
|
|
195
195
|
*/
|
|
196
|
-
export var buildFetchErrorAttribution = function buildFetchErrorAttribution(gateEnabled, error, statusCode) {
|
|
196
|
+
export var buildFetchErrorAttribution = function buildFetchErrorAttribution(gateEnabled, error, statusCode, deferred) {
|
|
197
197
|
if (!gateEnabled) {
|
|
198
198
|
return undefined;
|
|
199
199
|
}
|
|
200
200
|
var reason = classifyFetchErrorReason(error);
|
|
201
|
-
return _objectSpread({
|
|
201
|
+
return _objectSpread(_objectSpread({
|
|
202
202
|
reason: reason,
|
|
203
|
-
benign
|
|
203
|
+
// A deferred (tab-hidden) exhaustion is benign regardless of reason bucket.
|
|
204
|
+
benign: deferred === true || FETCH_BENIGN_REASONS.has(reason)
|
|
204
205
|
}, statusCode !== undefined && {
|
|
205
206
|
statusCode: statusCode
|
|
207
|
+
}), deferred === true && {
|
|
208
|
+
deferred: true
|
|
206
209
|
});
|
|
207
210
|
};
|
|
208
211
|
|
|
@@ -230,7 +233,7 @@ export function getErrorPayload(actionSubjectId, error, resourceId, sourceProduc
|
|
|
230
233
|
actionSubject: ACTION_SUBJECT.SYNCED_BLOCK,
|
|
231
234
|
actionSubjectId: actionSubjectId,
|
|
232
235
|
eventType: EVENT_TYPE.OPERATIONAL,
|
|
233
|
-
attributes: _objectSpread(_objectSpread(_objectSpread(_objectSpread(_objectSpread({
|
|
236
|
+
attributes: _objectSpread(_objectSpread(_objectSpread(_objectSpread(_objectSpread(_objectSpread({
|
|
234
237
|
error: error
|
|
235
238
|
}, resourceId && {
|
|
236
239
|
resourceId: resourceId
|
|
@@ -242,6 +245,8 @@ export function getErrorPayload(actionSubjectId, error, resourceId, sourceProduc
|
|
|
242
245
|
statusCode: attribution.statusCode
|
|
243
246
|
}), attribution && 'benign' in attribution && {
|
|
244
247
|
benign: attribution.benign
|
|
248
|
+
}), attribution && 'deferred' in attribution && attribution.deferred === true && {
|
|
249
|
+
deferred: true
|
|
245
250
|
})
|
|
246
251
|
};
|
|
247
252
|
}
|
|
@@ -348,16 +353,49 @@ export var createSuccessPayload = function createSuccessPayload(resourceId, sour
|
|
|
348
353
|
};
|
|
349
354
|
|
|
350
355
|
/**
|
|
351
|
-
*
|
|
352
|
-
*
|
|
353
|
-
*
|
|
356
|
+
* Optional enrichment for the `syncedBlockCreate` success event. All fields
|
|
357
|
+
* optional so gate-off/legacy payloads are unchanged. `inputMethod`: creating
|
|
358
|
+
* surface (enum, PII-safe). `createdEmpty`: true from an empty selection, false
|
|
359
|
+
* when content was converted.
|
|
354
360
|
*/
|
|
355
|
-
|
|
361
|
+
|
|
362
|
+
/**
|
|
363
|
+
* Operational `syncedBlockCreate` success event, behind
|
|
364
|
+
* `platform_editor_blocks_patch_4`, with the `blockInstanceId` join key and,
|
|
365
|
+
* when available, the `inputMethod` + `createdEmpty` creation-type signals.
|
|
366
|
+
*/
|
|
367
|
+
export var createSuccessOperationalPayload = function createSuccessOperationalPayload(resourceId, blockInstanceId, sourceProduct, enrichment) {
|
|
356
368
|
return {
|
|
357
369
|
action: ACTION.INSERTED,
|
|
358
370
|
actionSubject: ACTION_SUBJECT.SYNCED_BLOCK,
|
|
359
371
|
actionSubjectId: ACTION_SUBJECT_ID.SYNCED_BLOCK_CREATE,
|
|
360
372
|
eventType: EVENT_TYPE.OPERATIONAL,
|
|
373
|
+
attributes: _objectSpread(_objectSpread(_objectSpread(_objectSpread({
|
|
374
|
+
resourceId: resourceId
|
|
375
|
+
}, blockInstanceId && {
|
|
376
|
+
blockInstanceId: blockInstanceId
|
|
377
|
+
}), sourceProduct && {
|
|
378
|
+
sourceProduct: sourceProduct
|
|
379
|
+
}), (enrichment === null || enrichment === void 0 ? void 0 : enrichment.inputMethod) && {
|
|
380
|
+
inputMethod: enrichment.inputMethod
|
|
381
|
+
}), (enrichment === null || enrichment === void 0 ? void 0 : enrichment.createdEmpty) !== undefined && {
|
|
382
|
+
createdEmpty: enrichment.createdEmpty
|
|
383
|
+
})
|
|
384
|
+
};
|
|
385
|
+
};
|
|
386
|
+
|
|
387
|
+
/**
|
|
388
|
+
* Operational first-content-added event, behind
|
|
389
|
+
* `platform_editor_blocks_patch_4`. Fired once when a block created empty first
|
|
390
|
+
* gains user content. Join keys only (`resourceId` + `blockInstanceId`), no user
|
|
391
|
+
* content (PII-safe).
|
|
392
|
+
*/
|
|
393
|
+
export var addContentSuccessPayload = function addContentSuccessPayload(resourceId, blockInstanceId, sourceProduct) {
|
|
394
|
+
return {
|
|
395
|
+
action: ACTION.ADDED,
|
|
396
|
+
actionSubject: ACTION_SUBJECT.SYNCED_BLOCK,
|
|
397
|
+
actionSubjectId: ACTION_SUBJECT_ID.SYNCED_BLOCK_ADD_CONTENT,
|
|
398
|
+
eventType: EVENT_TYPE.OPERATIONAL,
|
|
361
399
|
attributes: _objectSpread(_objectSpread({
|
|
362
400
|
resourceId: resourceId
|
|
363
401
|
}, blockInstanceId && {
|