@atlaskit/editor-synced-block-provider 8.4.3 → 8.5.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.
@@ -2,12 +2,20 @@ import _defineProperty from "@babel/runtime/helpers/defineProperty";
2
2
  import { logException } from '@atlaskit/editor-common/monitoring';
3
3
  import { fg } from '@atlaskit/platform-feature-flags';
4
4
  import { SyncBlockError } from '../common/types';
5
- import { updateErrorPayload, createErrorPayload, deleteErrorPayload, updateCacheErrorPayload, getSourceInfoErrorPayload, updateSuccessPayload, createSuccessPayload, deleteSuccessPayload, fetchReferencesErrorPayload, buildErrorAttribution } from '../utils/errorHandling';
5
+ import { updateErrorPayload, createErrorPayload, deleteErrorPayload, updateCacheErrorPayload, getSourceInfoErrorPayload, updateSuccessPayload, createSuccessPayload, createSuccessPayloadNew, deleteSuccessPayload, fetchReferencesErrorPayload, buildErrorAttribution } from '../utils/errorHandling';
6
6
  import { getCreateSourceExperience, getDeleteSourceExperience, getSaveSourceExperience, getFetchSourceInfoExperience } from '../utils/experienceTracking';
7
7
  import { convertSyncBlockPMNodeToSyncBlockData, getSourceProductFromResourceIdSafe } from '../utils/utils';
8
8
  /** Maximum time (ms) flush() will wait for in-flight block creations before proceeding. */
9
9
  const FLUSH_CREATION_AWAIT_TIMEOUT_MS = 1000;
10
10
 
11
+ /**
12
+ * Window (ms) within which a repeated `syncedBlockDelete` emission for the same
13
+ * resourceId is suppressed. The same logical removal can re-fire across
14
+ * rebased/remote/redo transactions; a short window collapses those bursts to one
15
+ * event while still allowing a genuine delete→recreate→delete later.
16
+ */
17
+ const DELETE_DEDUPE_WINDOW_MS = 5000;
18
+
11
19
  // A store manager responsible for the lifecycle and state management of source sync blocks in an editor instance.
12
20
  // Designed to manage local in-memory state and synchronize with an external data provider.
13
21
  // Supports create, flush, and delete operations for source sync blocks.
@@ -32,6 +40,12 @@ export class SourceSyncBlockStoreManager {
32
40
  * multiple blocks are created concurrently. See EDITOR-7112.
33
41
  */
34
42
  _defineProperty(this, "creationsTimedOutDuringFlush", new Set());
43
+ /**
44
+ * resourceId -> timestamp (ms) of the last `syncedBlockDelete` emission, used
45
+ * to suppress duplicates within {@link DELETE_DEDUPE_WINDOW_MS}. Only consulted
46
+ * behind `platform_editor_blocks_patch_4`.
47
+ */
48
+ _defineProperty(this, "recentDeleteEmissions", new Map());
35
49
  _defineProperty(this, "setPendingDeletion", (Ids, value) => {
36
50
  if (this.viewMode === 'view') {
37
51
  return;
@@ -322,12 +336,18 @@ export class SourceSyncBlockStoreManager {
322
336
  }
323
337
  if (success) {
324
338
  var _this$fireAnalyticsEv6;
325
- (_this$fireAnalyticsEv6 = this.fireAnalyticsEvent) === null || _this$fireAnalyticsEv6 === void 0 ? void 0 : _this$fireAnalyticsEv6.call(this, createSuccessPayload(resourceId || '', getSourceProductFromResourceIdSafe(resourceId)));
339
+ const sourceProduct = getSourceProductFromResourceIdSafe(resourceId);
340
+ (_this$fireAnalyticsEv6 = this.fireAnalyticsEvent) === null || _this$fireAnalyticsEv6 === void 0 ? void 0 : _this$fireAnalyticsEv6.call(this, createSuccessPayload(resourceId || '', sourceProduct));
341
+ // Operational create-success event with the join key.
342
+ if (fg('platform_editor_blocks_patch_4')) {
343
+ var _this$fireAnalyticsEv7, _this$syncBlockCache$;
344
+ (_this$fireAnalyticsEv7 = this.fireAnalyticsEvent) === null || _this$fireAnalyticsEv7 === void 0 ? void 0 : _this$fireAnalyticsEv7.call(this, createSuccessPayloadNew(resourceId || '', (_this$syncBlockCache$ = this.syncBlockCache.get(resourceId)) === null || _this$syncBlockCache$ === void 0 ? void 0 : _this$syncBlockCache$.blockInstanceId, sourceProduct));
345
+ }
326
346
  } else {
327
- var _this$fireAnalyticsEv7;
347
+ var _this$fireAnalyticsEv8;
328
348
  // Delete the node from cache if fail to create so it's not flushed to BE
329
349
  this.syncBlockCache.delete(resourceId || '');
330
- (_this$fireAnalyticsEv7 = this.fireAnalyticsEvent) === null || _this$fireAnalyticsEv7 === void 0 ? void 0 : _this$fireAnalyticsEv7.call(this, createErrorPayload('Fail to create bodied sync block', resourceId, getSourceProductFromResourceIdSafe(resourceId)));
350
+ (_this$fireAnalyticsEv8 = this.fireAnalyticsEvent) === null || _this$fireAnalyticsEv8 === void 0 ? void 0 : _this$fireAnalyticsEv8.call(this, createErrorPayload('Fail to create bodied sync block', resourceId, getSourceProductFromResourceIdSafe(resourceId)));
331
351
  }
332
352
  }
333
353
  registerConfirmationCallback(callback) {
@@ -398,15 +418,15 @@ export class SourceSyncBlockStoreManager {
398
418
  this.commitPendingCreation(true, resourceId);
399
419
  (_this$createExperienc2 = this.createExperience) === null || _this$createExperienc2 === void 0 ? void 0 : _this$createExperienc2.success();
400
420
  } else {
401
- var _this$createExperienc3, _this$fireAnalyticsEv8;
421
+ var _this$createExperienc3, _this$fireAnalyticsEv9;
402
422
  this.commitPendingCreation(false, resourceId);
403
423
  (_this$createExperienc3 = this.createExperience) === null || _this$createExperienc3 === void 0 ? void 0 : _this$createExperienc3.failure({
404
424
  reason: result.error || 'Failed to create bodied sync block'
405
425
  });
406
- (_this$fireAnalyticsEv8 = this.fireAnalyticsEvent) === null || _this$fireAnalyticsEv8 === void 0 ? void 0 : _this$fireAnalyticsEv8.call(this, createErrorPayload(result.error || 'Failed to create bodied sync block', resourceId, getSourceProductFromResourceIdSafe(resourceId), buildErrorAttribution(fg('platform_editor_blocks_patch_3'), result.error, result.statusCode)));
426
+ (_this$fireAnalyticsEv9 = this.fireAnalyticsEvent) === null || _this$fireAnalyticsEv9 === void 0 ? void 0 : _this$fireAnalyticsEv9.call(this, createErrorPayload(result.error || 'Failed to create bodied sync block', resourceId, getSourceProductFromResourceIdSafe(resourceId), buildErrorAttribution(fg('platform_editor_blocks_patch_3'), result.error, result.statusCode)));
407
427
  }
408
428
  }).catch(error => {
409
- var _this$createExperienc4, _this$fireAnalyticsEv9;
429
+ var _this$createExperienc4, _this$fireAnalyticsEv0;
410
430
  this.commitPendingCreation(false, resourceId);
411
431
  logException(error, {
412
432
  location: 'editor-synced-block-provider/sourceSyncBlockStoreManager'
@@ -414,23 +434,63 @@ export class SourceSyncBlockStoreManager {
414
434
  (_this$createExperienc4 = this.createExperience) === null || _this$createExperienc4 === void 0 ? void 0 : _this$createExperienc4.failure({
415
435
  reason: error.message
416
436
  });
417
- (_this$fireAnalyticsEv9 = this.fireAnalyticsEvent) === null || _this$fireAnalyticsEv9 === void 0 ? void 0 : _this$fireAnalyticsEv9.call(this, createErrorPayload(error.message, resourceId, getSourceProductFromResourceIdSafe(resourceId)));
437
+ (_this$fireAnalyticsEv0 = this.fireAnalyticsEvent) === null || _this$fireAnalyticsEv0 === void 0 ? void 0 : _this$fireAnalyticsEv0.call(this, createErrorPayload(error.message, resourceId, getSourceProductFromResourceIdSafe(resourceId)));
418
438
  }).finally(() => {
419
439
  this.pendingCreationPromises.delete(resourceId);
420
440
  });
421
441
  this.pendingCreationPromises.set(resourceId, creationPromise);
422
442
  } catch (error) {
423
- var _this$fireAnalyticsEv0;
443
+ var _this$fireAnalyticsEv1;
424
444
  if (this.isPendingCreation(resourceId)) {
425
445
  this.commitPendingCreation(false, resourceId);
426
446
  }
427
447
  logException(error, {
428
448
  location: 'editor-synced-block-provider/sourceSyncBlockStoreManager'
429
449
  });
430
- (_this$fireAnalyticsEv0 = this.fireAnalyticsEvent) === null || _this$fireAnalyticsEv0 === void 0 ? void 0 : _this$fireAnalyticsEv0.call(this, createErrorPayload(error.message, resourceId, getSourceProductFromResourceIdSafe(resourceId)));
450
+ (_this$fireAnalyticsEv1 = this.fireAnalyticsEvent) === null || _this$fireAnalyticsEv1 === void 0 ? void 0 : _this$fireAnalyticsEv1.call(this, createErrorPayload(error.message, resourceId, getSourceProductFromResourceIdSafe(resourceId)));
451
+ }
452
+ }
453
+ /**
454
+ * Drop dedupe entries older than {@link DELETE_DEDUPE_WINDOW_MS} so
455
+ * `recentDeleteEmissions` stays bounded by the number of recent deletes.
456
+ */
457
+ pruneRecentDeleteEmissions(now) {
458
+ for (const [resourceId, emittedAt] of this.recentDeleteEmissions) {
459
+ if (now - emittedAt >= DELETE_DEDUPE_WINDOW_MS) {
460
+ this.recentDeleteEmissions.delete(resourceId);
461
+ }
431
462
  }
432
463
  }
433
- async delete(syncBlockIds, onDelete, onDeleteCompleted, reason) {
464
+
465
+ /**
466
+ * Emit the `syncedBlockDelete` success event. Gate off: legacy payload. Gate
467
+ * on: attaches `deletionReason`, `mechanism` and `blockInstanceId`, and
468
+ * suppresses repeat emissions within {@link DELETE_DEDUPE_WINDOW_MS}. Must run
469
+ * while the cache entry still exists so `blockInstanceId` is available.
470
+ */
471
+ emitDeleteSuccess(resourceId, reason, mechanism) {
472
+ var _this$syncBlockCache$2, _this$fireAnalyticsEv11;
473
+ const sourceProduct = getSourceProductFromResourceIdSafe(resourceId);
474
+ if (!fg('platform_editor_blocks_patch_4')) {
475
+ var _this$fireAnalyticsEv10;
476
+ (_this$fireAnalyticsEv10 = this.fireAnalyticsEvent) === null || _this$fireAnalyticsEv10 === void 0 ? void 0 : _this$fireAnalyticsEv10.call(this, deleteSuccessPayload(resourceId, sourceProduct));
477
+ return;
478
+ }
479
+ const now = Date.now();
480
+ const lastEmittedAt = this.recentDeleteEmissions.get(resourceId);
481
+ if (lastEmittedAt !== undefined && now - lastEmittedAt < DELETE_DEDUPE_WINDOW_MS) {
482
+ return; // duplicate emission for the same logical removal
483
+ }
484
+ this.pruneRecentDeleteEmissions(now);
485
+ this.recentDeleteEmissions.set(resourceId, now);
486
+ const enrichment = {
487
+ blockInstanceId: (_this$syncBlockCache$2 = this.syncBlockCache.get(resourceId)) === null || _this$syncBlockCache$2 === void 0 ? void 0 : _this$syncBlockCache$2.blockInstanceId,
488
+ deletionReason: reason,
489
+ mechanism
490
+ };
491
+ (_this$fireAnalyticsEv11 = this.fireAnalyticsEvent) === null || _this$fireAnalyticsEv11 === void 0 ? void 0 : _this$fireAnalyticsEv11.call(this, deleteSuccessPayload(resourceId, sourceProduct, enrichment));
492
+ }
493
+ async delete(syncBlockIds, onDelete, onDeleteCompleted, reason, mechanism) {
434
494
  try {
435
495
  var _this$deleteExperienc;
436
496
  if (this.viewMode === 'view') {
@@ -450,13 +510,14 @@ export class SourceSyncBlockStoreManager {
450
510
  if (isDeleteSuccessful) {
451
511
  var _this$deleteExperienc2;
452
512
  onDelete();
513
+ // Emit before the cache entry is deleted below, so blockInstanceId
514
+ // is still available to emitDeleteSuccess.
515
+ results.forEach(result => {
516
+ this.emitDeleteSuccess(result.resourceId, reason, mechanism);
517
+ });
453
518
  callback = Ids => this.syncBlockCache.delete(Ids.resourceId);
454
519
  this.clearPendingDeletion();
455
520
  (_this$deleteExperienc2 = this.deleteExperience) === null || _this$deleteExperienc2 === void 0 ? void 0 : _this$deleteExperienc2.success();
456
- results.forEach(result => {
457
- var _this$fireAnalyticsEv1;
458
- (_this$fireAnalyticsEv1 = this.fireAnalyticsEvent) === null || _this$fireAnalyticsEv1 === void 0 ? void 0 : _this$fireAnalyticsEv1.call(this, deleteSuccessPayload(result.resourceId, getSourceProductFromResourceIdSafe(result.resourceId)));
459
- });
460
521
  } else {
461
522
  var _this$deleteExperienc3;
462
523
  callback = Ids => {
@@ -466,11 +527,10 @@ export class SourceSyncBlockStoreManager {
466
527
  const attributionEnabled = fg('platform_editor_blocks_patch_3');
467
528
  results.forEach(result => {
468
529
  if (result.success) {
469
- var _this$fireAnalyticsEv10;
470
- (_this$fireAnalyticsEv10 = this.fireAnalyticsEvent) === null || _this$fireAnalyticsEv10 === void 0 ? void 0 : _this$fireAnalyticsEv10.call(this, deleteSuccessPayload(result.resourceId, getSourceProductFromResourceIdSafe(result.resourceId)));
530
+ this.emitDeleteSuccess(result.resourceId, reason, mechanism);
471
531
  } else {
472
- var _this$fireAnalyticsEv11;
473
- (_this$fireAnalyticsEv11 = this.fireAnalyticsEvent) === null || _this$fireAnalyticsEv11 === void 0 ? void 0 : _this$fireAnalyticsEv11.call(this, deleteErrorPayload(result.error || 'Failed to delete synced block', result.resourceId, getSourceProductFromResourceIdSafe(result.resourceId), buildErrorAttribution(attributionEnabled, result.error, result.statusCode)));
532
+ var _this$fireAnalyticsEv12;
533
+ (_this$fireAnalyticsEv12 = this.fireAnalyticsEvent) === null || _this$fireAnalyticsEv12 === void 0 ? void 0 : _this$fireAnalyticsEv12.call(this, deleteErrorPayload(result.error || 'Failed to delete synced block', result.resourceId, getSourceProductFromResourceIdSafe(result.resourceId), buildErrorAttribution(attributionEnabled, result.error, result.statusCode)));
474
534
  }
475
535
  });
476
536
  }
@@ -481,9 +541,9 @@ export class SourceSyncBlockStoreManager {
481
541
  // so the attribution `reason` falls back to `unknown` when the gate is on.
482
542
  const attribution = buildErrorAttribution(fg('platform_editor_blocks_patch_3'));
483
543
  syncBlockIds.forEach(Ids => {
484
- var _this$fireAnalyticsEv12;
544
+ var _this$fireAnalyticsEv13;
485
545
  this.setPendingDeletion(Ids, false);
486
- (_this$fireAnalyticsEv12 = this.fireAnalyticsEvent) === null || _this$fireAnalyticsEv12 === void 0 ? void 0 : _this$fireAnalyticsEv12.call(this, deleteErrorPayload(error.message, Ids.resourceId, getSourceProductFromResourceIdSafe(Ids.resourceId), attribution));
546
+ (_this$fireAnalyticsEv13 = this.fireAnalyticsEvent) === null || _this$fireAnalyticsEv13 === void 0 ? void 0 : _this$fireAnalyticsEv13.call(this, deleteErrorPayload(error.message, Ids.resourceId, getSourceProductFromResourceIdSafe(Ids.resourceId), attribution));
487
547
  });
488
548
  logException(error, {
489
549
  location: 'editor-synced-block-provider/sourceSyncBlockStoreManager'
@@ -506,10 +566,11 @@ export class SourceSyncBlockStoreManager {
506
566
  syncBlockIds,
507
567
  onDelete,
508
568
  onDeleteCompleted,
509
- deletionReason
569
+ deletionReason,
570
+ mechanism
510
571
  } = this.deletionRetryInfo;
511
572
  if (this.confirmationCallback) {
512
- await this.delete(syncBlockIds, onDelete, onDeleteCompleted, deletionReason);
573
+ await this.delete(syncBlockIds, onDelete, onDeleteCompleted, deletionReason, mechanism);
513
574
  }
514
575
  }
515
576
  clearPendingDeletion() {
@@ -583,7 +644,9 @@ export class SourceSyncBlockStoreManager {
583
644
  // onDelete: no-op, document is being discarded
584
645
  () => {},
585
646
  // onDeleteCompleted: no-op
586
- 'source-block-unpublished');
647
+ 'source-block-unpublished',
648
+ // Cancel/discard cleanup is code-initiated, not a direct user edit.
649
+ 'other');
587
650
  }
588
651
 
589
652
  /**
@@ -594,14 +657,14 @@ export class SourceSyncBlockStoreManager {
594
657
  * @param onDeleteCompleted - The callback for after the deletion is saved to BE (whether successful or not)
595
658
  * @param destroyCallback - The callback to clear any reference stored for deletion (regardless if deletion is completed or abort)
596
659
  */
597
- async deleteSyncBlocksWithConfirmation(syncBlockIds, deletionReason, onDelete, onDeleteCompleted, destroyCallback) {
660
+ async deleteSyncBlocksWithConfirmation(syncBlockIds, deletionReason, onDelete, onDeleteCompleted, destroyCallback, mechanism) {
598
661
  if (this.viewMode === 'view') {
599
662
  return Promise.resolve();
600
663
  }
601
664
  if (this.confirmationCallback) {
602
665
  const confirmed = await this.confirmationCallback(syncBlockIds, deletionReason);
603
666
  if (confirmed) {
604
- const isDeleteSuccessful = await this.delete(syncBlockIds, onDelete, onDeleteCompleted, deletionReason);
667
+ const isDeleteSuccessful = await this.delete(syncBlockIds, onDelete, onDeleteCompleted, deletionReason, mechanism);
605
668
  if (!isDeleteSuccessful) {
606
669
  // If deletion failed, save deletion info for potential retry
607
670
  this.deletionRetryInfo = {
@@ -609,7 +672,8 @@ export class SourceSyncBlockStoreManager {
609
672
  onDelete,
610
673
  onDeleteCompleted,
611
674
  destroyCallback,
612
- deletionReason
675
+ deletionReason,
676
+ mechanism
613
677
  };
614
678
  } else {
615
679
  destroyCallback();
@@ -639,11 +703,11 @@ export class SourceSyncBlockStoreManager {
639
703
  return sourceInfo;
640
704
  });
641
705
  } catch (error) {
642
- var _this$fireAnalyticsEv13;
706
+ var _this$fireAnalyticsEv14;
643
707
  logException(error, {
644
708
  location: 'editor-synced-block-provider/sourceSyncBlockStoreManager'
645
709
  });
646
- (_this$fireAnalyticsEv13 = this.fireAnalyticsEvent) === null || _this$fireAnalyticsEv13 === void 0 ? void 0 : _this$fireAnalyticsEv13.call(this, getSourceInfoErrorPayload(error.message));
710
+ (_this$fireAnalyticsEv14 = this.fireAnalyticsEvent) === null || _this$fireAnalyticsEv14 === void 0 ? void 0 : _this$fireAnalyticsEv14.call(this, getSourceInfoErrorPayload(error.message));
647
711
  return Promise.resolve(undefined);
648
712
  }
649
713
  }
@@ -654,11 +718,11 @@ export class SourceSyncBlockStoreManager {
654
718
  }
655
719
  return this.dataProvider.fetchReferences(resourceId, true);
656
720
  } catch (error) {
657
- var _this$fireAnalyticsEv14;
721
+ var _this$fireAnalyticsEv15;
658
722
  logException(error, {
659
723
  location: 'editor-synced-block-provider/sourceSyncBlockStoreManager'
660
724
  });
661
- (_this$fireAnalyticsEv14 = this.fireAnalyticsEvent) === null || _this$fireAnalyticsEv14 === void 0 ? void 0 : _this$fireAnalyticsEv14.call(this, fetchReferencesErrorPayload(error.message, resourceId, getSourceProductFromResourceIdSafe(resourceId)));
725
+ (_this$fireAnalyticsEv15 = this.fireAnalyticsEvent) === null || _this$fireAnalyticsEv15 === void 0 ? void 0 : _this$fireAnalyticsEv15.call(this, fetchReferencesErrorPayload(error.message, resourceId, getSourceProductFromResourceIdSafe(resourceId)));
662
726
  return Promise.resolve({
663
727
  error: SyncBlockError.Errored
664
728
  });
@@ -673,6 +737,7 @@ export class SourceSyncBlockStoreManager {
673
737
  this.postCreationFlushCallback = undefined;
674
738
  this.pendingCreationPromises.clear();
675
739
  this.creationsTimedOutDuringFlush.clear();
740
+ this.recentDeleteEmissions.clear();
676
741
  this.dataProvider = undefined;
677
742
  (_this$saveExperience4 = this.saveExperience) === null || _this$saveExperience4 === void 0 ? void 0 : _this$saveExperience4.abort({
678
743
  reason: 'editorDestroyed'
@@ -313,13 +313,25 @@ export const createSuccessPayload = (resourceId, sourceProduct) => {
313
313
  }
314
314
  };
315
315
  };
316
- export const createSuccessPayloadNew = resourceId => ({
316
+
317
+ /**
318
+ * Operational `syncedBlockCreate` success event (previously only error rows
319
+ * existed, so dashboards had no create denominator). Fired behind
320
+ * `platform_editor_blocks_patch_4` with the `blockInstanceId` join key.
321
+ */
322
+ export const createSuccessPayloadNew = (resourceId, blockInstanceId, sourceProduct) => ({
317
323
  action: ACTION.INSERTED,
318
324
  actionSubject: ACTION_SUBJECT.SYNCED_BLOCK,
319
325
  actionSubjectId: ACTION_SUBJECT_ID.SYNCED_BLOCK_CREATE,
320
326
  eventType: EVENT_TYPE.OPERATIONAL,
321
327
  attributes: {
322
- resourceId
328
+ resourceId,
329
+ ...(blockInstanceId && {
330
+ blockInstanceId
331
+ }),
332
+ ...(sourceProduct && {
333
+ sourceProduct
334
+ })
323
335
  }
324
336
  });
325
337
  export const updateSuccessPayload = (resourceId, hasReference, sourceProduct) => ({
@@ -337,7 +349,14 @@ export const updateSuccessPayload = (resourceId, hasReference, sourceProduct) =>
337
349
  })
338
350
  }
339
351
  });
340
- export const deleteSuccessPayload = (resourceId, sourceProduct) => ({
352
+
353
+ /**
354
+ * Optional enrichment for the `syncedBlockDelete` success event behind
355
+ * `platform_editor_blocks_patch_4`. All fields optional so the gate-off payload
356
+ * is unchanged; `blockInstanceId` is the bare-uuid join key.
357
+ */
358
+
359
+ export const deleteSuccessPayload = (resourceId, sourceProduct, enrichment) => ({
341
360
  action: ACTION.DELETED,
342
361
  actionSubject: ACTION_SUBJECT.SYNCED_BLOCK,
343
362
  actionSubjectId: ACTION_SUBJECT_ID.SYNCED_BLOCK_DELETE,
@@ -346,6 +365,15 @@ export const deleteSuccessPayload = (resourceId, sourceProduct) => ({
346
365
  resourceId,
347
366
  ...(sourceProduct && {
348
367
  sourceProduct
368
+ }),
369
+ ...((enrichment === null || enrichment === void 0 ? void 0 : enrichment.blockInstanceId) && {
370
+ blockInstanceId: enrichment.blockInstanceId
371
+ }),
372
+ ...((enrichment === null || enrichment === void 0 ? void 0 : enrichment.deletionReason) && {
373
+ deletionReason: enrichment.deletionReason
374
+ }),
375
+ ...((enrichment === null || enrichment === void 0 ? void 0 : enrichment.mechanism) && {
376
+ mechanism: enrichment.mechanism
349
377
  })
350
378
  }
351
379
  });
@@ -27,6 +27,22 @@ export var SyncBlockError = /*#__PURE__*/function (SyncBlockError) {
27
27
  SyncBlockError["EntityNotFound"] = "entity_not_found";
28
28
  return SyncBlockError;
29
29
  }({});
30
+
31
+ /**
32
+ * How a source bodiedSyncBlock removal was performed, derived from the
33
+ * originating transaction. Distinct from {@link DeletionReason} (the outcome):
34
+ * `mechanism` tells deliberate deletes apart from undos and accidental overwrites.
35
+ *
36
+ * Values name the user action that produced the removal:
37
+ * - `undo` / `redo` — removal came from a history undo/redo.
38
+ * - `deleteButton` — the explicit "Delete" control in the synced-block toolbar.
39
+ * - `keyboardDelete` — Backspace/Delete key on a caret or range selection.
40
+ * - `selectionReplaced` — the block was selected and replaced by typing or
41
+ * pasting over it (the accidental-overwrite path).
42
+ * - `other` — any non-direct removal (unsync, conversion, code-dispatched, or a
43
+ * structural wrap/lift/unwrap edit).
44
+ */
45
+
30
46
  /**
31
47
  * Helpers to distinguish "data provider not ready / torn down" from a genuine
32
48
  * fetch/subscribe failure (EDITOR-7860). On Jira the provider is wired