@atlaskit/editor-synced-block-provider 8.5.6 → 8.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +19 -0
- package/dist/cjs/store-manager/sourceSyncBlockStoreManager.js +72 -13
- package/dist/cjs/utils/errorHandling.js +38 -5
- package/dist/es2019/store-manager/sourceSyncBlockStoreManager.js +79 -22
- package/dist/es2019/utils/errorHandling.js +39 -4
- package/dist/esm/store-manager/sourceSyncBlockStoreManager.js +73 -14
- package/dist/esm/utils/errorHandling.js +37 -4
- package/dist/types/entry-points/errorHandling.d.ts +1 -0
- package/dist/types/store-manager/sourceSyncBlockStoreManager.d.ts +27 -1
- package/dist/types/utils/errorHandling.d.ts +22 -5
- package/package.json +3 -3
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,24 @@
|
|
|
1
1
|
# @atlaskit/editor-synced-block-provider
|
|
2
2
|
|
|
3
|
+
## 8.6.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- [`c0b0b98789f7a`](https://bitbucket.org/atlassian/atlassian-frontend-monorepo/commits/c0b0b98789f7a) -
|
|
8
|
+
Add analytics for synced block source creation: record the creation input method and whether the
|
|
9
|
+
block was created empty, and emit a one-off event the first time an empty source block gains
|
|
10
|
+
content.
|
|
11
|
+
|
|
12
|
+
### Patch Changes
|
|
13
|
+
|
|
14
|
+
- Updated dependencies
|
|
15
|
+
|
|
16
|
+
## 8.5.7
|
|
17
|
+
|
|
18
|
+
### Patch Changes
|
|
19
|
+
|
|
20
|
+
- Updated dependencies
|
|
21
|
+
|
|
3
22
|
## 8.5.6
|
|
4
23
|
|
|
5
24
|
### Patch Changes
|
|
@@ -59,6 +59,20 @@ var SourceSyncBlockStoreManager = exports.SourceSyncBlockStoreManager = /*#__PUR
|
|
|
59
59
|
* multiple blocks are created concurrently. See EDITOR-7112.
|
|
60
60
|
*/
|
|
61
61
|
(0, _defineProperty2.default)(this, "creationsTimedOutDuringFlush", new Set());
|
|
62
|
+
/**
|
|
63
|
+
* Creation-type signals captured at `createBodiedSyncBlockNode` time and read
|
|
64
|
+
* back in `commitPendingCreation` to enrich the `syncedBlockCreate` event.
|
|
65
|
+
* Kept out of the flushed cache record so it never leaks into Block Service /
|
|
66
|
+
* ADF; removed once consumed.
|
|
67
|
+
*/
|
|
68
|
+
(0, _defineProperty2.default)(this, "creationEnrichment", new Map());
|
|
69
|
+
/**
|
|
70
|
+
* Resource IDs of blocks created empty this session that have not yet fired
|
|
71
|
+
* the first-content-added event. Added on empty creation, removed when the
|
|
72
|
+
* block first gains content (the event fires once). Blocks created with
|
|
73
|
+
* content or not created this session are never tracked.
|
|
74
|
+
*/
|
|
75
|
+
(0, _defineProperty2.default)(this, "awaitingFirstContent", new Set());
|
|
62
76
|
/**
|
|
63
77
|
* resourceId -> timestamp (ms) of the last `syncedBlockDelete` emission, used
|
|
64
78
|
* to suppress duplicates within {@link DELETE_DEDUPE_WINDOW_MS}. Only consulted
|
|
@@ -411,17 +425,48 @@ var SourceSyncBlockStoreManager = exports.SourceSyncBlockStoreManager = /*#__PUR
|
|
|
411
425
|
var _this$fireAnalyticsEv4;
|
|
412
426
|
var sourceProduct = (0, _utils.getSourceProductFromResourceIdSafe)(resourceId);
|
|
413
427
|
(_this$fireAnalyticsEv4 = this.fireAnalyticsEvent) === null || _this$fireAnalyticsEv4 === void 0 || _this$fireAnalyticsEv4.call(this, (0, _errorHandling.createSuccessPayload)(resourceId || '', sourceProduct));
|
|
414
|
-
// Operational create-success event with the join key
|
|
428
|
+
// Operational create-success event with the join key + creation-type
|
|
429
|
+
// signals (inputMethod / createdEmpty) captured at the command layer.
|
|
415
430
|
if ((0, _platformFeatureFlags.fg)('platform_editor_blocks_patch_4')) {
|
|
416
431
|
var _this$fireAnalyticsEv5, _this$syncBlockCache$;
|
|
417
|
-
(_this$fireAnalyticsEv5 = this.fireAnalyticsEvent) === null || _this$fireAnalyticsEv5 === void 0 || _this$fireAnalyticsEv5.call(this, (0, _errorHandling.createSuccessOperationalPayload)(resourceId || '', (_this$syncBlockCache$ = this.syncBlockCache.get(resourceId)) === null || _this$syncBlockCache$ === void 0 ? void 0 : _this$syncBlockCache$.blockInstanceId, sourceProduct));
|
|
432
|
+
(_this$fireAnalyticsEv5 = this.fireAnalyticsEvent) === null || _this$fireAnalyticsEv5 === void 0 || _this$fireAnalyticsEv5.call(this, (0, _errorHandling.createSuccessOperationalPayload)(resourceId || '', (_this$syncBlockCache$ = this.syncBlockCache.get(resourceId)) === null || _this$syncBlockCache$ === void 0 ? void 0 : _this$syncBlockCache$.blockInstanceId, sourceProduct, this.creationEnrichment.get(resourceId)));
|
|
418
433
|
}
|
|
419
434
|
} else {
|
|
420
435
|
var _this$fireAnalyticsEv6;
|
|
421
436
|
// Delete the node from cache if fail to create so it's not flushed to BE
|
|
422
437
|
this.syncBlockCache.delete(resourceId || '');
|
|
438
|
+
// Creation failed, so there is no block to add content to — drop the
|
|
439
|
+
// first-content tracking entry so a later unrelated edit cannot fire it.
|
|
440
|
+
this.awaitingFirstContent.delete(resourceId || '');
|
|
423
441
|
(_this$fireAnalyticsEv6 = this.fireAnalyticsEvent) === null || _this$fireAnalyticsEv6 === void 0 || _this$fireAnalyticsEv6.call(this, (0, _errorHandling.createErrorPayload)('Fail to create bodied sync block', resourceId, (0, _utils.getSourceProductFromResourceIdSafe)(resourceId)));
|
|
424
442
|
}
|
|
443
|
+
// Enrichment is single-use — drop it once the create has resolved either way.
|
|
444
|
+
this.creationEnrichment.delete(resourceId || '');
|
|
445
|
+
}
|
|
446
|
+
|
|
447
|
+
/**
|
|
448
|
+
* Fire the first-content-added event once when a block created empty this
|
|
449
|
+
* session first gains content. The caller detects the in-block edit; this owns
|
|
450
|
+
* dedupe + emission (fires at most once per block, only for empty→content).
|
|
451
|
+
* Gated behind `platform_editor_blocks_patch_4`.
|
|
452
|
+
*/
|
|
453
|
+
}, {
|
|
454
|
+
key: "maybeEmitFirstContentAdded",
|
|
455
|
+
value: function maybeEmitFirstContentAdded(resourceId, blockInstanceId) {
|
|
456
|
+
var _this$fireAnalyticsEv7;
|
|
457
|
+
if (this.viewMode === 'view') {
|
|
458
|
+
return;
|
|
459
|
+
}
|
|
460
|
+
if (!(0, _platformFeatureFlags.fg)('platform_editor_blocks_patch_4')) {
|
|
461
|
+
return;
|
|
462
|
+
}
|
|
463
|
+
// `delete` returns false when the id was not tracked (already emitted, or
|
|
464
|
+
// the block was not created empty this session), so this both dedupes and
|
|
465
|
+
// scopes emission to genuine first-content transitions in one check.
|
|
466
|
+
if (!this.awaitingFirstContent.delete(resourceId)) {
|
|
467
|
+
return;
|
|
468
|
+
}
|
|
469
|
+
(_this$fireAnalyticsEv7 = this.fireAnalyticsEvent) === null || _this$fireAnalyticsEv7 === void 0 || _this$fireAnalyticsEv7.call(this, (0, _errorHandling.addContentSuccessPayload)(resourceId, blockInstanceId, (0, _utils.getSourceProductFromResourceIdSafe)(resourceId)));
|
|
425
470
|
}
|
|
426
471
|
}, {
|
|
427
472
|
key: "registerConfirmationCallback",
|
|
@@ -461,16 +506,28 @@ var SourceSyncBlockStoreManager = exports.SourceSyncBlockStoreManager = /*#__PUR
|
|
|
461
506
|
* @param attrs attributes Ids of the node
|
|
462
507
|
* @param node the ProseMirror node to cache
|
|
463
508
|
* @param onCompletion callback invoked when creation completes
|
|
509
|
+
* @param enrichment optional creation-type signals stashed and attached to the
|
|
510
|
+
* `syncedBlockCreate` event when the async create resolves. When
|
|
511
|
+
* `createdEmpty` is true the block is also registered for the
|
|
512
|
+
* first-content-added event.
|
|
464
513
|
*/
|
|
465
514
|
}, {
|
|
466
515
|
key: "createBodiedSyncBlockNode",
|
|
467
|
-
value: function createBodiedSyncBlockNode(attrs, node, onCompletion) {
|
|
516
|
+
value: function createBodiedSyncBlockNode(attrs, node, onCompletion, enrichment) {
|
|
468
517
|
var _this4 = this;
|
|
469
518
|
if (this.viewMode === 'view') {
|
|
470
519
|
return;
|
|
471
520
|
}
|
|
472
521
|
var resourceId = attrs.resourceId,
|
|
473
522
|
blockInstanceId = attrs.localId;
|
|
523
|
+
// Stash creation-type signals for commitPendingCreation to read.
|
|
524
|
+
if (enrichment) {
|
|
525
|
+
this.creationEnrichment.set(resourceId, enrichment);
|
|
526
|
+
// Only empty-created blocks can produce an empty→content transition.
|
|
527
|
+
if (enrichment.createdEmpty) {
|
|
528
|
+
this.awaitingFirstContent.add(resourceId);
|
|
529
|
+
}
|
|
530
|
+
}
|
|
474
531
|
try {
|
|
475
532
|
var _this$createExperienc;
|
|
476
533
|
if (!this.dataProvider) {
|
|
@@ -520,14 +577,14 @@ var SourceSyncBlockStoreManager = exports.SourceSyncBlockStoreManager = /*#__PUR
|
|
|
520
577
|
});
|
|
521
578
|
this.pendingCreationPromises.set(resourceId, creationPromise);
|
|
522
579
|
} catch (error) {
|
|
523
|
-
var _this$
|
|
580
|
+
var _this$fireAnalyticsEv8;
|
|
524
581
|
if (this.isPendingCreation(resourceId)) {
|
|
525
582
|
this.commitPendingCreation(false, resourceId);
|
|
526
583
|
}
|
|
527
584
|
(0, _monitoring.logException)(error, {
|
|
528
585
|
location: 'editor-synced-block-provider/sourceSyncBlockStoreManager'
|
|
529
586
|
});
|
|
530
|
-
(_this$
|
|
587
|
+
(_this$fireAnalyticsEv8 = this.fireAnalyticsEvent) === null || _this$fireAnalyticsEv8 === void 0 || _this$fireAnalyticsEv8.call(this, (0, _errorHandling.createErrorPayload)(error.message, resourceId, (0, _utils.getSourceProductFromResourceIdSafe)(resourceId)));
|
|
531
588
|
}
|
|
532
589
|
}
|
|
533
590
|
}, {
|
|
@@ -565,11 +622,11 @@ var SourceSyncBlockStoreManager = exports.SourceSyncBlockStoreManager = /*#__PUR
|
|
|
565
622
|
}, {
|
|
566
623
|
key: "emitDeleteSuccess",
|
|
567
624
|
value: function emitDeleteSuccess(resourceId, reason, mechanism) {
|
|
568
|
-
var _this$syncBlockCache$2, _this$
|
|
625
|
+
var _this$syncBlockCache$2, _this$fireAnalyticsEv0;
|
|
569
626
|
var sourceProduct = (0, _utils.getSourceProductFromResourceIdSafe)(resourceId);
|
|
570
627
|
if (!(0, _platformFeatureFlags.fg)('platform_editor_blocks_patch_4')) {
|
|
571
|
-
var _this$
|
|
572
|
-
(_this$
|
|
628
|
+
var _this$fireAnalyticsEv9;
|
|
629
|
+
(_this$fireAnalyticsEv9 = this.fireAnalyticsEvent) === null || _this$fireAnalyticsEv9 === void 0 || _this$fireAnalyticsEv9.call(this, (0, _errorHandling.deleteSuccessPayload)(resourceId, sourceProduct));
|
|
573
630
|
return;
|
|
574
631
|
}
|
|
575
632
|
var now = Date.now();
|
|
@@ -584,7 +641,7 @@ var SourceSyncBlockStoreManager = exports.SourceSyncBlockStoreManager = /*#__PUR
|
|
|
584
641
|
deletionReason: reason,
|
|
585
642
|
mechanism: mechanism
|
|
586
643
|
};
|
|
587
|
-
(_this$
|
|
644
|
+
(_this$fireAnalyticsEv0 = this.fireAnalyticsEvent) === null || _this$fireAnalyticsEv0 === void 0 || _this$fireAnalyticsEv0.call(this, (0, _errorHandling.deleteSuccessPayload)(resourceId, sourceProduct, enrichment));
|
|
588
645
|
}
|
|
589
646
|
}, {
|
|
590
647
|
key: "delete",
|
|
@@ -933,11 +990,11 @@ var SourceSyncBlockStoreManager = exports.SourceSyncBlockStoreManager = /*#__PUR
|
|
|
933
990
|
return sourceInfo;
|
|
934
991
|
});
|
|
935
992
|
} catch (error) {
|
|
936
|
-
var _this$
|
|
993
|
+
var _this$fireAnalyticsEv1;
|
|
937
994
|
(0, _monitoring.logException)(error, {
|
|
938
995
|
location: 'editor-synced-block-provider/sourceSyncBlockStoreManager'
|
|
939
996
|
});
|
|
940
|
-
(_this$
|
|
997
|
+
(_this$fireAnalyticsEv1 = this.fireAnalyticsEvent) === null || _this$fireAnalyticsEv1 === void 0 || _this$fireAnalyticsEv1.call(this, (0, _errorHandling.getSourceInfoErrorPayload)(error.message));
|
|
941
998
|
return Promise.resolve(undefined);
|
|
942
999
|
}
|
|
943
1000
|
}
|
|
@@ -950,11 +1007,11 @@ var SourceSyncBlockStoreManager = exports.SourceSyncBlockStoreManager = /*#__PUR
|
|
|
950
1007
|
}
|
|
951
1008
|
return this.dataProvider.fetchReferences(resourceId, true);
|
|
952
1009
|
} catch (error) {
|
|
953
|
-
var _this$
|
|
1010
|
+
var _this$fireAnalyticsEv10;
|
|
954
1011
|
(0, _monitoring.logException)(error, {
|
|
955
1012
|
location: 'editor-synced-block-provider/sourceSyncBlockStoreManager'
|
|
956
1013
|
});
|
|
957
|
-
(_this$
|
|
1014
|
+
(_this$fireAnalyticsEv10 = this.fireAnalyticsEvent) === null || _this$fireAnalyticsEv10 === void 0 || _this$fireAnalyticsEv10.call(this, (0, _errorHandling.fetchReferencesErrorPayload)(error.message, resourceId, (0, _utils.getSourceProductFromResourceIdSafe)(resourceId)));
|
|
958
1015
|
return Promise.resolve({
|
|
959
1016
|
error: _types.SyncBlockError.Errored
|
|
960
1017
|
});
|
|
@@ -971,6 +1028,8 @@ var SourceSyncBlockStoreManager = exports.SourceSyncBlockStoreManager = /*#__PUR
|
|
|
971
1028
|
this.postCreationFlushCallback = undefined;
|
|
972
1029
|
this.pendingCreationPromises.clear();
|
|
973
1030
|
this.creationsTimedOutDuringFlush.clear();
|
|
1031
|
+
this.creationEnrichment.clear();
|
|
1032
|
+
this.awaitingFirstContent.clear();
|
|
974
1033
|
this.recentDeleteEmissions.clear();
|
|
975
1034
|
this.dataProvider = undefined;
|
|
976
1035
|
(_this$saveExperience4 = this.saveExperience) === null || _this$saveExperience4 === void 0 || _this$saveExperience4.abort({
|
|
@@ -4,7 +4,7 @@ var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefau
|
|
|
4
4
|
Object.defineProperty(exports, "__esModule", {
|
|
5
5
|
value: true
|
|
6
6
|
});
|
|
7
|
-
exports.fetchSuccessPayload = exports.fetchReferencesErrorPayload = exports.fetchErrorPayload = exports.deleteSuccessPayload = exports.deleteErrorPayload = exports.createSuccessPayload = exports.createSuccessOperationalPayload = exports.createErrorPayload = exports.classifyFetchErrorReason = exports.classifyErrorReason = exports.cacheDeletionForcedPayload = exports.buildFetchErrorAttribution = exports.buildErrorAttribution = exports.FETCH_BENIGN_REASONS = void 0;
|
|
7
|
+
exports.fetchSuccessPayload = exports.fetchReferencesErrorPayload = exports.fetchErrorPayload = exports.deleteSuccessPayload = exports.deleteErrorPayload = exports.createSuccessPayload = exports.createSuccessOperationalPayload = exports.createErrorPayload = exports.classifyFetchErrorReason = exports.classifyErrorReason = exports.cacheDeletionForcedPayload = exports.buildFetchErrorAttribution = exports.buildErrorAttribution = exports.addContentSuccessPayload = exports.FETCH_BENIGN_REASONS = void 0;
|
|
8
8
|
exports.getErrorPayload = getErrorPayload;
|
|
9
9
|
exports.updateSuccessPayload = exports.updateReferenceErrorPayload = exports.updateErrorPayload = exports.updateCacheErrorPayload = exports.stringifyError = exports.sourceInfoOrphanedPayload = exports.getSourceInfoErrorPayload = exports.getPiiSafeOriginalError = void 0;
|
|
10
10
|
var _slicedToArray2 = _interopRequireDefault(require("@babel/runtime/helpers/slicedToArray"));
|
|
@@ -357,16 +357,49 @@ var createSuccessPayload = exports.createSuccessPayload = function createSuccess
|
|
|
357
357
|
};
|
|
358
358
|
|
|
359
359
|
/**
|
|
360
|
-
*
|
|
361
|
-
*
|
|
362
|
-
*
|
|
360
|
+
* Optional enrichment for the `syncedBlockCreate` success event. All fields
|
|
361
|
+
* optional so gate-off/legacy payloads are unchanged. `inputMethod`: creating
|
|
362
|
+
* surface (enum, PII-safe). `createdEmpty`: true from an empty selection, false
|
|
363
|
+
* when content was converted.
|
|
363
364
|
*/
|
|
364
|
-
|
|
365
|
+
|
|
366
|
+
/**
|
|
367
|
+
* Operational `syncedBlockCreate` success event, behind
|
|
368
|
+
* `platform_editor_blocks_patch_4`, with the `blockInstanceId` join key and,
|
|
369
|
+
* when available, the `inputMethod` + `createdEmpty` creation-type signals.
|
|
370
|
+
*/
|
|
371
|
+
var createSuccessOperationalPayload = exports.createSuccessOperationalPayload = function createSuccessOperationalPayload(resourceId, blockInstanceId, sourceProduct, enrichment) {
|
|
365
372
|
return {
|
|
366
373
|
action: _analytics.ACTION.INSERTED,
|
|
367
374
|
actionSubject: _analytics.ACTION_SUBJECT.SYNCED_BLOCK,
|
|
368
375
|
actionSubjectId: _analytics.ACTION_SUBJECT_ID.SYNCED_BLOCK_CREATE,
|
|
369
376
|
eventType: _analytics.EVENT_TYPE.OPERATIONAL,
|
|
377
|
+
attributes: _objectSpread(_objectSpread(_objectSpread(_objectSpread({
|
|
378
|
+
resourceId: resourceId
|
|
379
|
+
}, blockInstanceId && {
|
|
380
|
+
blockInstanceId: blockInstanceId
|
|
381
|
+
}), sourceProduct && {
|
|
382
|
+
sourceProduct: sourceProduct
|
|
383
|
+
}), (enrichment === null || enrichment === void 0 ? void 0 : enrichment.inputMethod) && {
|
|
384
|
+
inputMethod: enrichment.inputMethod
|
|
385
|
+
}), (enrichment === null || enrichment === void 0 ? void 0 : enrichment.createdEmpty) !== undefined && {
|
|
386
|
+
createdEmpty: enrichment.createdEmpty
|
|
387
|
+
})
|
|
388
|
+
};
|
|
389
|
+
};
|
|
390
|
+
|
|
391
|
+
/**
|
|
392
|
+
* Operational first-content-added event, behind
|
|
393
|
+
* `platform_editor_blocks_patch_4`. Fired once when a block created empty first
|
|
394
|
+
* gains user content. Join keys only (`resourceId` + `blockInstanceId`), no user
|
|
395
|
+
* content (PII-safe).
|
|
396
|
+
*/
|
|
397
|
+
var addContentSuccessPayload = exports.addContentSuccessPayload = function addContentSuccessPayload(resourceId, blockInstanceId, sourceProduct) {
|
|
398
|
+
return {
|
|
399
|
+
action: _analytics.ACTION.ADDED,
|
|
400
|
+
actionSubject: _analytics.ACTION_SUBJECT.SYNCED_BLOCK,
|
|
401
|
+
actionSubjectId: _analytics.ACTION_SUBJECT_ID.SYNCED_BLOCK_ADD_CONTENT,
|
|
402
|
+
eventType: _analytics.EVENT_TYPE.OPERATIONAL,
|
|
370
403
|
attributes: _objectSpread(_objectSpread({
|
|
371
404
|
resourceId: resourceId
|
|
372
405
|
}, blockInstanceId && {
|
|
@@ -2,7 +2,7 @@ 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, createSuccessOperationalPayload, deleteSuccessPayload, fetchReferencesErrorPayload, buildErrorAttribution } from '../utils/errorHandling';
|
|
5
|
+
import { updateErrorPayload, createErrorPayload, deleteErrorPayload, updateCacheErrorPayload, getSourceInfoErrorPayload, updateSuccessPayload, createSuccessPayload, createSuccessOperationalPayload, addContentSuccessPayload, 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. */
|
|
@@ -40,6 +40,20 @@ export class SourceSyncBlockStoreManager {
|
|
|
40
40
|
* multiple blocks are created concurrently. See EDITOR-7112.
|
|
41
41
|
*/
|
|
42
42
|
_defineProperty(this, "creationsTimedOutDuringFlush", new Set());
|
|
43
|
+
/**
|
|
44
|
+
* Creation-type signals captured at `createBodiedSyncBlockNode` time and read
|
|
45
|
+
* back in `commitPendingCreation` to enrich the `syncedBlockCreate` event.
|
|
46
|
+
* Kept out of the flushed cache record so it never leaks into Block Service /
|
|
47
|
+
* ADF; removed once consumed.
|
|
48
|
+
*/
|
|
49
|
+
_defineProperty(this, "creationEnrichment", new Map());
|
|
50
|
+
/**
|
|
51
|
+
* Resource IDs of blocks created empty this session that have not yet fired
|
|
52
|
+
* the first-content-added event. Added on empty creation, removed when the
|
|
53
|
+
* block first gains content (the event fires once). Blocks created with
|
|
54
|
+
* content or not created this session are never tracked.
|
|
55
|
+
*/
|
|
56
|
+
_defineProperty(this, "awaitingFirstContent", new Set());
|
|
43
57
|
/**
|
|
44
58
|
* resourceId -> timestamp (ms) of the last `syncedBlockDelete` emission, used
|
|
45
59
|
* to suppress duplicates within {@link DELETE_DEDUPE_WINDOW_MS}. Only consulted
|
|
@@ -338,17 +352,46 @@ export class SourceSyncBlockStoreManager {
|
|
|
338
352
|
var _this$fireAnalyticsEv6;
|
|
339
353
|
const sourceProduct = getSourceProductFromResourceIdSafe(resourceId);
|
|
340
354
|
(_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
|
|
355
|
+
// Operational create-success event with the join key + creation-type
|
|
356
|
+
// signals (inputMethod / createdEmpty) captured at the command layer.
|
|
342
357
|
if (fg('platform_editor_blocks_patch_4')) {
|
|
343
358
|
var _this$fireAnalyticsEv7, _this$syncBlockCache$;
|
|
344
|
-
(_this$fireAnalyticsEv7 = this.fireAnalyticsEvent) === null || _this$fireAnalyticsEv7 === void 0 ? void 0 : _this$fireAnalyticsEv7.call(this, createSuccessOperationalPayload(resourceId || '', (_this$syncBlockCache$ = this.syncBlockCache.get(resourceId)) === null || _this$syncBlockCache$ === void 0 ? void 0 : _this$syncBlockCache$.blockInstanceId, sourceProduct));
|
|
359
|
+
(_this$fireAnalyticsEv7 = this.fireAnalyticsEvent) === null || _this$fireAnalyticsEv7 === void 0 ? void 0 : _this$fireAnalyticsEv7.call(this, createSuccessOperationalPayload(resourceId || '', (_this$syncBlockCache$ = this.syncBlockCache.get(resourceId)) === null || _this$syncBlockCache$ === void 0 ? void 0 : _this$syncBlockCache$.blockInstanceId, sourceProduct, this.creationEnrichment.get(resourceId)));
|
|
345
360
|
}
|
|
346
361
|
} else {
|
|
347
362
|
var _this$fireAnalyticsEv8;
|
|
348
363
|
// Delete the node from cache if fail to create so it's not flushed to BE
|
|
349
364
|
this.syncBlockCache.delete(resourceId || '');
|
|
365
|
+
// Creation failed, so there is no block to add content to — drop the
|
|
366
|
+
// first-content tracking entry so a later unrelated edit cannot fire it.
|
|
367
|
+
this.awaitingFirstContent.delete(resourceId || '');
|
|
350
368
|
(_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)));
|
|
351
369
|
}
|
|
370
|
+
// Enrichment is single-use — drop it once the create has resolved either way.
|
|
371
|
+
this.creationEnrichment.delete(resourceId || '');
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
/**
|
|
375
|
+
* Fire the first-content-added event once when a block created empty this
|
|
376
|
+
* session first gains content. The caller detects the in-block edit; this owns
|
|
377
|
+
* dedupe + emission (fires at most once per block, only for empty→content).
|
|
378
|
+
* Gated behind `platform_editor_blocks_patch_4`.
|
|
379
|
+
*/
|
|
380
|
+
maybeEmitFirstContentAdded(resourceId, blockInstanceId) {
|
|
381
|
+
var _this$fireAnalyticsEv9;
|
|
382
|
+
if (this.viewMode === 'view') {
|
|
383
|
+
return;
|
|
384
|
+
}
|
|
385
|
+
if (!fg('platform_editor_blocks_patch_4')) {
|
|
386
|
+
return;
|
|
387
|
+
}
|
|
388
|
+
// `delete` returns false when the id was not tracked (already emitted, or
|
|
389
|
+
// the block was not created empty this session), so this both dedupes and
|
|
390
|
+
// scopes emission to genuine first-content transitions in one check.
|
|
391
|
+
if (!this.awaitingFirstContent.delete(resourceId)) {
|
|
392
|
+
return;
|
|
393
|
+
}
|
|
394
|
+
(_this$fireAnalyticsEv9 = this.fireAnalyticsEvent) === null || _this$fireAnalyticsEv9 === void 0 ? void 0 : _this$fireAnalyticsEv9.call(this, addContentSuccessPayload(resourceId, blockInstanceId, getSourceProductFromResourceIdSafe(resourceId)));
|
|
352
395
|
}
|
|
353
396
|
registerConfirmationCallback(callback) {
|
|
354
397
|
this.confirmationCallback = callback;
|
|
@@ -382,8 +425,12 @@ export class SourceSyncBlockStoreManager {
|
|
|
382
425
|
* @param attrs attributes Ids of the node
|
|
383
426
|
* @param node the ProseMirror node to cache
|
|
384
427
|
* @param onCompletion callback invoked when creation completes
|
|
428
|
+
* @param enrichment optional creation-type signals stashed and attached to the
|
|
429
|
+
* `syncedBlockCreate` event when the async create resolves. When
|
|
430
|
+
* `createdEmpty` is true the block is also registered for the
|
|
431
|
+
* first-content-added event.
|
|
385
432
|
*/
|
|
386
|
-
createBodiedSyncBlockNode(attrs, node, onCompletion) {
|
|
433
|
+
createBodiedSyncBlockNode(attrs, node, onCompletion, enrichment) {
|
|
387
434
|
if (this.viewMode === 'view') {
|
|
388
435
|
return;
|
|
389
436
|
}
|
|
@@ -391,6 +438,14 @@ export class SourceSyncBlockStoreManager {
|
|
|
391
438
|
resourceId,
|
|
392
439
|
localId: blockInstanceId
|
|
393
440
|
} = attrs;
|
|
441
|
+
// Stash creation-type signals for commitPendingCreation to read.
|
|
442
|
+
if (enrichment) {
|
|
443
|
+
this.creationEnrichment.set(resourceId, enrichment);
|
|
444
|
+
// Only empty-created blocks can produce an empty→content transition.
|
|
445
|
+
if (enrichment.createdEmpty) {
|
|
446
|
+
this.awaitingFirstContent.add(resourceId);
|
|
447
|
+
}
|
|
448
|
+
}
|
|
394
449
|
try {
|
|
395
450
|
var _this$createExperienc;
|
|
396
451
|
if (!this.dataProvider) {
|
|
@@ -418,15 +473,15 @@ export class SourceSyncBlockStoreManager {
|
|
|
418
473
|
this.commitPendingCreation(true, resourceId);
|
|
419
474
|
(_this$createExperienc2 = this.createExperience) === null || _this$createExperienc2 === void 0 ? void 0 : _this$createExperienc2.success();
|
|
420
475
|
} else {
|
|
421
|
-
var _this$createExperienc3, _this$
|
|
476
|
+
var _this$createExperienc3, _this$fireAnalyticsEv0;
|
|
422
477
|
this.commitPendingCreation(false, resourceId);
|
|
423
478
|
(_this$createExperienc3 = this.createExperience) === null || _this$createExperienc3 === void 0 ? void 0 : _this$createExperienc3.failure({
|
|
424
479
|
reason: result.error || 'Failed to create bodied sync block'
|
|
425
480
|
});
|
|
426
|
-
(_this$
|
|
481
|
+
(_this$fireAnalyticsEv0 = this.fireAnalyticsEvent) === null || _this$fireAnalyticsEv0 === void 0 ? void 0 : _this$fireAnalyticsEv0.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)));
|
|
427
482
|
}
|
|
428
483
|
}).catch(error => {
|
|
429
|
-
var _this$createExperienc4, _this$
|
|
484
|
+
var _this$createExperienc4, _this$fireAnalyticsEv1;
|
|
430
485
|
this.commitPendingCreation(false, resourceId);
|
|
431
486
|
logException(error, {
|
|
432
487
|
location: 'editor-synced-block-provider/sourceSyncBlockStoreManager'
|
|
@@ -434,20 +489,20 @@ export class SourceSyncBlockStoreManager {
|
|
|
434
489
|
(_this$createExperienc4 = this.createExperience) === null || _this$createExperienc4 === void 0 ? void 0 : _this$createExperienc4.failure({
|
|
435
490
|
reason: error.message
|
|
436
491
|
});
|
|
437
|
-
(_this$
|
|
492
|
+
(_this$fireAnalyticsEv1 = this.fireAnalyticsEvent) === null || _this$fireAnalyticsEv1 === void 0 ? void 0 : _this$fireAnalyticsEv1.call(this, createErrorPayload(error.message, resourceId, getSourceProductFromResourceIdSafe(resourceId)));
|
|
438
493
|
}).finally(() => {
|
|
439
494
|
this.pendingCreationPromises.delete(resourceId);
|
|
440
495
|
});
|
|
441
496
|
this.pendingCreationPromises.set(resourceId, creationPromise);
|
|
442
497
|
} catch (error) {
|
|
443
|
-
var _this$
|
|
498
|
+
var _this$fireAnalyticsEv10;
|
|
444
499
|
if (this.isPendingCreation(resourceId)) {
|
|
445
500
|
this.commitPendingCreation(false, resourceId);
|
|
446
501
|
}
|
|
447
502
|
logException(error, {
|
|
448
503
|
location: 'editor-synced-block-provider/sourceSyncBlockStoreManager'
|
|
449
504
|
});
|
|
450
|
-
(_this$
|
|
505
|
+
(_this$fireAnalyticsEv10 = this.fireAnalyticsEvent) === null || _this$fireAnalyticsEv10 === void 0 ? void 0 : _this$fireAnalyticsEv10.call(this, createErrorPayload(error.message, resourceId, getSourceProductFromResourceIdSafe(resourceId)));
|
|
451
506
|
}
|
|
452
507
|
}
|
|
453
508
|
/**
|
|
@@ -469,11 +524,11 @@ export class SourceSyncBlockStoreManager {
|
|
|
469
524
|
* while the cache entry still exists so `blockInstanceId` is available.
|
|
470
525
|
*/
|
|
471
526
|
emitDeleteSuccess(resourceId, reason, mechanism) {
|
|
472
|
-
var _this$syncBlockCache$2, _this$
|
|
527
|
+
var _this$syncBlockCache$2, _this$fireAnalyticsEv12;
|
|
473
528
|
const sourceProduct = getSourceProductFromResourceIdSafe(resourceId);
|
|
474
529
|
if (!fg('platform_editor_blocks_patch_4')) {
|
|
475
|
-
var _this$
|
|
476
|
-
(_this$
|
|
530
|
+
var _this$fireAnalyticsEv11;
|
|
531
|
+
(_this$fireAnalyticsEv11 = this.fireAnalyticsEvent) === null || _this$fireAnalyticsEv11 === void 0 ? void 0 : _this$fireAnalyticsEv11.call(this, deleteSuccessPayload(resourceId, sourceProduct));
|
|
477
532
|
return;
|
|
478
533
|
}
|
|
479
534
|
const now = Date.now();
|
|
@@ -488,7 +543,7 @@ export class SourceSyncBlockStoreManager {
|
|
|
488
543
|
deletionReason: reason,
|
|
489
544
|
mechanism
|
|
490
545
|
};
|
|
491
|
-
(_this$
|
|
546
|
+
(_this$fireAnalyticsEv12 = this.fireAnalyticsEvent) === null || _this$fireAnalyticsEv12 === void 0 ? void 0 : _this$fireAnalyticsEv12.call(this, deleteSuccessPayload(resourceId, sourceProduct, enrichment));
|
|
492
547
|
}
|
|
493
548
|
async delete(syncBlockIds, onDelete, onDeleteCompleted, reason, mechanism) {
|
|
494
549
|
try {
|
|
@@ -529,8 +584,8 @@ export class SourceSyncBlockStoreManager {
|
|
|
529
584
|
if (result.success) {
|
|
530
585
|
this.emitDeleteSuccess(result.resourceId, reason, mechanism);
|
|
531
586
|
} else {
|
|
532
|
-
var _this$
|
|
533
|
-
(_this$
|
|
587
|
+
var _this$fireAnalyticsEv13;
|
|
588
|
+
(_this$fireAnalyticsEv13 = this.fireAnalyticsEvent) === null || _this$fireAnalyticsEv13 === void 0 ? void 0 : _this$fireAnalyticsEv13.call(this, deleteErrorPayload(result.error || 'Failed to delete synced block', result.resourceId, getSourceProductFromResourceIdSafe(result.resourceId), buildErrorAttribution(attributionEnabled, result.error, result.statusCode)));
|
|
534
589
|
}
|
|
535
590
|
});
|
|
536
591
|
}
|
|
@@ -541,9 +596,9 @@ export class SourceSyncBlockStoreManager {
|
|
|
541
596
|
// so the attribution `reason` falls back to `unknown` when the gate is on.
|
|
542
597
|
const attribution = buildErrorAttribution(fg('platform_editor_blocks_patch_3'));
|
|
543
598
|
syncBlockIds.forEach(Ids => {
|
|
544
|
-
var _this$
|
|
599
|
+
var _this$fireAnalyticsEv14;
|
|
545
600
|
this.setPendingDeletion(Ids, false);
|
|
546
|
-
(_this$
|
|
601
|
+
(_this$fireAnalyticsEv14 = this.fireAnalyticsEvent) === null || _this$fireAnalyticsEv14 === void 0 ? void 0 : _this$fireAnalyticsEv14.call(this, deleteErrorPayload(error.message, Ids.resourceId, getSourceProductFromResourceIdSafe(Ids.resourceId), attribution));
|
|
547
602
|
});
|
|
548
603
|
logException(error, {
|
|
549
604
|
location: 'editor-synced-block-provider/sourceSyncBlockStoreManager'
|
|
@@ -703,11 +758,11 @@ export class SourceSyncBlockStoreManager {
|
|
|
703
758
|
return sourceInfo;
|
|
704
759
|
});
|
|
705
760
|
} catch (error) {
|
|
706
|
-
var _this$
|
|
761
|
+
var _this$fireAnalyticsEv15;
|
|
707
762
|
logException(error, {
|
|
708
763
|
location: 'editor-synced-block-provider/sourceSyncBlockStoreManager'
|
|
709
764
|
});
|
|
710
|
-
(_this$
|
|
765
|
+
(_this$fireAnalyticsEv15 = this.fireAnalyticsEvent) === null || _this$fireAnalyticsEv15 === void 0 ? void 0 : _this$fireAnalyticsEv15.call(this, getSourceInfoErrorPayload(error.message));
|
|
711
766
|
return Promise.resolve(undefined);
|
|
712
767
|
}
|
|
713
768
|
}
|
|
@@ -718,11 +773,11 @@ export class SourceSyncBlockStoreManager {
|
|
|
718
773
|
}
|
|
719
774
|
return this.dataProvider.fetchReferences(resourceId, true);
|
|
720
775
|
} catch (error) {
|
|
721
|
-
var _this$
|
|
776
|
+
var _this$fireAnalyticsEv16;
|
|
722
777
|
logException(error, {
|
|
723
778
|
location: 'editor-synced-block-provider/sourceSyncBlockStoreManager'
|
|
724
779
|
});
|
|
725
|
-
(_this$
|
|
780
|
+
(_this$fireAnalyticsEv16 = this.fireAnalyticsEvent) === null || _this$fireAnalyticsEv16 === void 0 ? void 0 : _this$fireAnalyticsEv16.call(this, fetchReferencesErrorPayload(error.message, resourceId, getSourceProductFromResourceIdSafe(resourceId)));
|
|
726
781
|
return Promise.resolve({
|
|
727
782
|
error: SyncBlockError.Errored
|
|
728
783
|
});
|
|
@@ -737,6 +792,8 @@ export class SourceSyncBlockStoreManager {
|
|
|
737
792
|
this.postCreationFlushCallback = undefined;
|
|
738
793
|
this.pendingCreationPromises.clear();
|
|
739
794
|
this.creationsTimedOutDuringFlush.clear();
|
|
795
|
+
this.creationEnrichment.clear();
|
|
796
|
+
this.awaitingFirstContent.clear();
|
|
740
797
|
this.recentDeleteEmissions.clear();
|
|
741
798
|
this.dataProvider = undefined;
|
|
742
799
|
(_this$saveExperience4 = this.saveExperience) === null || _this$saveExperience4 === void 0 ? void 0 : _this$saveExperience4.abort({
|
|
@@ -339,15 +339,50 @@ export const createSuccessPayload = (resourceId, sourceProduct) => {
|
|
|
339
339
|
};
|
|
340
340
|
|
|
341
341
|
/**
|
|
342
|
-
*
|
|
343
|
-
*
|
|
344
|
-
*
|
|
342
|
+
* Optional enrichment for the `syncedBlockCreate` success event. All fields
|
|
343
|
+
* optional so gate-off/legacy payloads are unchanged. `inputMethod`: creating
|
|
344
|
+
* surface (enum, PII-safe). `createdEmpty`: true from an empty selection, false
|
|
345
|
+
* when content was converted.
|
|
345
346
|
*/
|
|
346
|
-
|
|
347
|
+
|
|
348
|
+
/**
|
|
349
|
+
* Operational `syncedBlockCreate` success event, behind
|
|
350
|
+
* `platform_editor_blocks_patch_4`, with the `blockInstanceId` join key and,
|
|
351
|
+
* when available, the `inputMethod` + `createdEmpty` creation-type signals.
|
|
352
|
+
*/
|
|
353
|
+
export const createSuccessOperationalPayload = (resourceId, blockInstanceId, sourceProduct, enrichment) => ({
|
|
347
354
|
action: ACTION.INSERTED,
|
|
348
355
|
actionSubject: ACTION_SUBJECT.SYNCED_BLOCK,
|
|
349
356
|
actionSubjectId: ACTION_SUBJECT_ID.SYNCED_BLOCK_CREATE,
|
|
350
357
|
eventType: EVENT_TYPE.OPERATIONAL,
|
|
358
|
+
attributes: {
|
|
359
|
+
resourceId,
|
|
360
|
+
...(blockInstanceId && {
|
|
361
|
+
blockInstanceId
|
|
362
|
+
}),
|
|
363
|
+
...(sourceProduct && {
|
|
364
|
+
sourceProduct
|
|
365
|
+
}),
|
|
366
|
+
...((enrichment === null || enrichment === void 0 ? void 0 : enrichment.inputMethod) && {
|
|
367
|
+
inputMethod: enrichment.inputMethod
|
|
368
|
+
}),
|
|
369
|
+
...((enrichment === null || enrichment === void 0 ? void 0 : enrichment.createdEmpty) !== undefined && {
|
|
370
|
+
createdEmpty: enrichment.createdEmpty
|
|
371
|
+
})
|
|
372
|
+
}
|
|
373
|
+
});
|
|
374
|
+
|
|
375
|
+
/**
|
|
376
|
+
* Operational first-content-added event, behind
|
|
377
|
+
* `platform_editor_blocks_patch_4`. Fired once when a block created empty first
|
|
378
|
+
* gains user content. Join keys only (`resourceId` + `blockInstanceId`), no user
|
|
379
|
+
* content (PII-safe).
|
|
380
|
+
*/
|
|
381
|
+
export const addContentSuccessPayload = (resourceId, blockInstanceId, sourceProduct) => ({
|
|
382
|
+
action: ACTION.ADDED,
|
|
383
|
+
actionSubject: ACTION_SUBJECT.SYNCED_BLOCK,
|
|
384
|
+
actionSubjectId: ACTION_SUBJECT_ID.SYNCED_BLOCK_ADD_CONTENT,
|
|
385
|
+
eventType: EVENT_TYPE.OPERATIONAL,
|
|
351
386
|
attributes: {
|
|
352
387
|
resourceId,
|
|
353
388
|
...(blockInstanceId && {
|
|
@@ -12,7 +12,7 @@ function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t =
|
|
|
12
12
|
import { logException } from '@atlaskit/editor-common/monitoring';
|
|
13
13
|
import { fg } from '@atlaskit/platform-feature-flags';
|
|
14
14
|
import { SyncBlockError } from '../common/types';
|
|
15
|
-
import { updateErrorPayload, createErrorPayload, deleteErrorPayload, updateCacheErrorPayload, getSourceInfoErrorPayload, updateSuccessPayload, createSuccessPayload, createSuccessOperationalPayload, deleteSuccessPayload, fetchReferencesErrorPayload, buildErrorAttribution } from '../utils/errorHandling';
|
|
15
|
+
import { updateErrorPayload, createErrorPayload, deleteErrorPayload, updateCacheErrorPayload, getSourceInfoErrorPayload, updateSuccessPayload, createSuccessPayload, createSuccessOperationalPayload, addContentSuccessPayload, deleteSuccessPayload, fetchReferencesErrorPayload, buildErrorAttribution } from '../utils/errorHandling';
|
|
16
16
|
import { getCreateSourceExperience, getDeleteSourceExperience, getSaveSourceExperience, getFetchSourceInfoExperience } from '../utils/experienceTracking';
|
|
17
17
|
import { convertSyncBlockPMNodeToSyncBlockData, getSourceProductFromResourceIdSafe } from '../utils/utils';
|
|
18
18
|
/** Maximum time (ms) flush() will wait for in-flight block creations before proceeding. */
|
|
@@ -52,6 +52,20 @@ export var SourceSyncBlockStoreManager = /*#__PURE__*/function () {
|
|
|
52
52
|
* multiple blocks are created concurrently. See EDITOR-7112.
|
|
53
53
|
*/
|
|
54
54
|
_defineProperty(this, "creationsTimedOutDuringFlush", new Set());
|
|
55
|
+
/**
|
|
56
|
+
* Creation-type signals captured at `createBodiedSyncBlockNode` time and read
|
|
57
|
+
* back in `commitPendingCreation` to enrich the `syncedBlockCreate` event.
|
|
58
|
+
* Kept out of the flushed cache record so it never leaks into Block Service /
|
|
59
|
+
* ADF; removed once consumed.
|
|
60
|
+
*/
|
|
61
|
+
_defineProperty(this, "creationEnrichment", new Map());
|
|
62
|
+
/**
|
|
63
|
+
* Resource IDs of blocks created empty this session that have not yet fired
|
|
64
|
+
* the first-content-added event. Added on empty creation, removed when the
|
|
65
|
+
* block first gains content (the event fires once). Blocks created with
|
|
66
|
+
* content or not created this session are never tracked.
|
|
67
|
+
*/
|
|
68
|
+
_defineProperty(this, "awaitingFirstContent", new Set());
|
|
55
69
|
/**
|
|
56
70
|
* resourceId -> timestamp (ms) of the last `syncedBlockDelete` emission, used
|
|
57
71
|
* to suppress duplicates within {@link DELETE_DEDUPE_WINDOW_MS}. Only consulted
|
|
@@ -404,17 +418,48 @@ export var SourceSyncBlockStoreManager = /*#__PURE__*/function () {
|
|
|
404
418
|
var _this$fireAnalyticsEv4;
|
|
405
419
|
var sourceProduct = getSourceProductFromResourceIdSafe(resourceId);
|
|
406
420
|
(_this$fireAnalyticsEv4 = this.fireAnalyticsEvent) === null || _this$fireAnalyticsEv4 === void 0 || _this$fireAnalyticsEv4.call(this, createSuccessPayload(resourceId || '', sourceProduct));
|
|
407
|
-
// Operational create-success event with the join key
|
|
421
|
+
// Operational create-success event with the join key + creation-type
|
|
422
|
+
// signals (inputMethod / createdEmpty) captured at the command layer.
|
|
408
423
|
if (fg('platform_editor_blocks_patch_4')) {
|
|
409
424
|
var _this$fireAnalyticsEv5, _this$syncBlockCache$;
|
|
410
|
-
(_this$fireAnalyticsEv5 = this.fireAnalyticsEvent) === null || _this$fireAnalyticsEv5 === void 0 || _this$fireAnalyticsEv5.call(this, createSuccessOperationalPayload(resourceId || '', (_this$syncBlockCache$ = this.syncBlockCache.get(resourceId)) === null || _this$syncBlockCache$ === void 0 ? void 0 : _this$syncBlockCache$.blockInstanceId, sourceProduct));
|
|
425
|
+
(_this$fireAnalyticsEv5 = this.fireAnalyticsEvent) === null || _this$fireAnalyticsEv5 === void 0 || _this$fireAnalyticsEv5.call(this, createSuccessOperationalPayload(resourceId || '', (_this$syncBlockCache$ = this.syncBlockCache.get(resourceId)) === null || _this$syncBlockCache$ === void 0 ? void 0 : _this$syncBlockCache$.blockInstanceId, sourceProduct, this.creationEnrichment.get(resourceId)));
|
|
411
426
|
}
|
|
412
427
|
} else {
|
|
413
428
|
var _this$fireAnalyticsEv6;
|
|
414
429
|
// Delete the node from cache if fail to create so it's not flushed to BE
|
|
415
430
|
this.syncBlockCache.delete(resourceId || '');
|
|
431
|
+
// Creation failed, so there is no block to add content to — drop the
|
|
432
|
+
// first-content tracking entry so a later unrelated edit cannot fire it.
|
|
433
|
+
this.awaitingFirstContent.delete(resourceId || '');
|
|
416
434
|
(_this$fireAnalyticsEv6 = this.fireAnalyticsEvent) === null || _this$fireAnalyticsEv6 === void 0 || _this$fireAnalyticsEv6.call(this, createErrorPayload('Fail to create bodied sync block', resourceId, getSourceProductFromResourceIdSafe(resourceId)));
|
|
417
435
|
}
|
|
436
|
+
// Enrichment is single-use — drop it once the create has resolved either way.
|
|
437
|
+
this.creationEnrichment.delete(resourceId || '');
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
/**
|
|
441
|
+
* Fire the first-content-added event once when a block created empty this
|
|
442
|
+
* session first gains content. The caller detects the in-block edit; this owns
|
|
443
|
+
* dedupe + emission (fires at most once per block, only for empty→content).
|
|
444
|
+
* Gated behind `platform_editor_blocks_patch_4`.
|
|
445
|
+
*/
|
|
446
|
+
}, {
|
|
447
|
+
key: "maybeEmitFirstContentAdded",
|
|
448
|
+
value: function maybeEmitFirstContentAdded(resourceId, blockInstanceId) {
|
|
449
|
+
var _this$fireAnalyticsEv7;
|
|
450
|
+
if (this.viewMode === 'view') {
|
|
451
|
+
return;
|
|
452
|
+
}
|
|
453
|
+
if (!fg('platform_editor_blocks_patch_4')) {
|
|
454
|
+
return;
|
|
455
|
+
}
|
|
456
|
+
// `delete` returns false when the id was not tracked (already emitted, or
|
|
457
|
+
// the block was not created empty this session), so this both dedupes and
|
|
458
|
+
// scopes emission to genuine first-content transitions in one check.
|
|
459
|
+
if (!this.awaitingFirstContent.delete(resourceId)) {
|
|
460
|
+
return;
|
|
461
|
+
}
|
|
462
|
+
(_this$fireAnalyticsEv7 = this.fireAnalyticsEvent) === null || _this$fireAnalyticsEv7 === void 0 || _this$fireAnalyticsEv7.call(this, addContentSuccessPayload(resourceId, blockInstanceId, getSourceProductFromResourceIdSafe(resourceId)));
|
|
418
463
|
}
|
|
419
464
|
}, {
|
|
420
465
|
key: "registerConfirmationCallback",
|
|
@@ -454,16 +499,28 @@ export var SourceSyncBlockStoreManager = /*#__PURE__*/function () {
|
|
|
454
499
|
* @param attrs attributes Ids of the node
|
|
455
500
|
* @param node the ProseMirror node to cache
|
|
456
501
|
* @param onCompletion callback invoked when creation completes
|
|
502
|
+
* @param enrichment optional creation-type signals stashed and attached to the
|
|
503
|
+
* `syncedBlockCreate` event when the async create resolves. When
|
|
504
|
+
* `createdEmpty` is true the block is also registered for the
|
|
505
|
+
* first-content-added event.
|
|
457
506
|
*/
|
|
458
507
|
}, {
|
|
459
508
|
key: "createBodiedSyncBlockNode",
|
|
460
|
-
value: function createBodiedSyncBlockNode(attrs, node, onCompletion) {
|
|
509
|
+
value: function createBodiedSyncBlockNode(attrs, node, onCompletion, enrichment) {
|
|
461
510
|
var _this4 = this;
|
|
462
511
|
if (this.viewMode === 'view') {
|
|
463
512
|
return;
|
|
464
513
|
}
|
|
465
514
|
var resourceId = attrs.resourceId,
|
|
466
515
|
blockInstanceId = attrs.localId;
|
|
516
|
+
// Stash creation-type signals for commitPendingCreation to read.
|
|
517
|
+
if (enrichment) {
|
|
518
|
+
this.creationEnrichment.set(resourceId, enrichment);
|
|
519
|
+
// Only empty-created blocks can produce an empty→content transition.
|
|
520
|
+
if (enrichment.createdEmpty) {
|
|
521
|
+
this.awaitingFirstContent.add(resourceId);
|
|
522
|
+
}
|
|
523
|
+
}
|
|
467
524
|
try {
|
|
468
525
|
var _this$createExperienc;
|
|
469
526
|
if (!this.dataProvider) {
|
|
@@ -513,14 +570,14 @@ export var SourceSyncBlockStoreManager = /*#__PURE__*/function () {
|
|
|
513
570
|
});
|
|
514
571
|
this.pendingCreationPromises.set(resourceId, creationPromise);
|
|
515
572
|
} catch (error) {
|
|
516
|
-
var _this$
|
|
573
|
+
var _this$fireAnalyticsEv8;
|
|
517
574
|
if (this.isPendingCreation(resourceId)) {
|
|
518
575
|
this.commitPendingCreation(false, resourceId);
|
|
519
576
|
}
|
|
520
577
|
logException(error, {
|
|
521
578
|
location: 'editor-synced-block-provider/sourceSyncBlockStoreManager'
|
|
522
579
|
});
|
|
523
|
-
(_this$
|
|
580
|
+
(_this$fireAnalyticsEv8 = this.fireAnalyticsEvent) === null || _this$fireAnalyticsEv8 === void 0 || _this$fireAnalyticsEv8.call(this, createErrorPayload(error.message, resourceId, getSourceProductFromResourceIdSafe(resourceId)));
|
|
524
581
|
}
|
|
525
582
|
}
|
|
526
583
|
}, {
|
|
@@ -558,11 +615,11 @@ export var SourceSyncBlockStoreManager = /*#__PURE__*/function () {
|
|
|
558
615
|
}, {
|
|
559
616
|
key: "emitDeleteSuccess",
|
|
560
617
|
value: function emitDeleteSuccess(resourceId, reason, mechanism) {
|
|
561
|
-
var _this$syncBlockCache$2, _this$
|
|
618
|
+
var _this$syncBlockCache$2, _this$fireAnalyticsEv0;
|
|
562
619
|
var sourceProduct = getSourceProductFromResourceIdSafe(resourceId);
|
|
563
620
|
if (!fg('platform_editor_blocks_patch_4')) {
|
|
564
|
-
var _this$
|
|
565
|
-
(_this$
|
|
621
|
+
var _this$fireAnalyticsEv9;
|
|
622
|
+
(_this$fireAnalyticsEv9 = this.fireAnalyticsEvent) === null || _this$fireAnalyticsEv9 === void 0 || _this$fireAnalyticsEv9.call(this, deleteSuccessPayload(resourceId, sourceProduct));
|
|
566
623
|
return;
|
|
567
624
|
}
|
|
568
625
|
var now = Date.now();
|
|
@@ -577,7 +634,7 @@ export var SourceSyncBlockStoreManager = /*#__PURE__*/function () {
|
|
|
577
634
|
deletionReason: reason,
|
|
578
635
|
mechanism: mechanism
|
|
579
636
|
};
|
|
580
|
-
(_this$
|
|
637
|
+
(_this$fireAnalyticsEv0 = this.fireAnalyticsEvent) === null || _this$fireAnalyticsEv0 === void 0 || _this$fireAnalyticsEv0.call(this, deleteSuccessPayload(resourceId, sourceProduct, enrichment));
|
|
581
638
|
}
|
|
582
639
|
}, {
|
|
583
640
|
key: "delete",
|
|
@@ -926,11 +983,11 @@ export var SourceSyncBlockStoreManager = /*#__PURE__*/function () {
|
|
|
926
983
|
return sourceInfo;
|
|
927
984
|
});
|
|
928
985
|
} catch (error) {
|
|
929
|
-
var _this$
|
|
986
|
+
var _this$fireAnalyticsEv1;
|
|
930
987
|
logException(error, {
|
|
931
988
|
location: 'editor-synced-block-provider/sourceSyncBlockStoreManager'
|
|
932
989
|
});
|
|
933
|
-
(_this$
|
|
990
|
+
(_this$fireAnalyticsEv1 = this.fireAnalyticsEvent) === null || _this$fireAnalyticsEv1 === void 0 || _this$fireAnalyticsEv1.call(this, getSourceInfoErrorPayload(error.message));
|
|
934
991
|
return Promise.resolve(undefined);
|
|
935
992
|
}
|
|
936
993
|
}
|
|
@@ -943,11 +1000,11 @@ export var SourceSyncBlockStoreManager = /*#__PURE__*/function () {
|
|
|
943
1000
|
}
|
|
944
1001
|
return this.dataProvider.fetchReferences(resourceId, true);
|
|
945
1002
|
} catch (error) {
|
|
946
|
-
var _this$
|
|
1003
|
+
var _this$fireAnalyticsEv10;
|
|
947
1004
|
logException(error, {
|
|
948
1005
|
location: 'editor-synced-block-provider/sourceSyncBlockStoreManager'
|
|
949
1006
|
});
|
|
950
|
-
(_this$
|
|
1007
|
+
(_this$fireAnalyticsEv10 = this.fireAnalyticsEvent) === null || _this$fireAnalyticsEv10 === void 0 || _this$fireAnalyticsEv10.call(this, fetchReferencesErrorPayload(error.message, resourceId, getSourceProductFromResourceIdSafe(resourceId)));
|
|
951
1008
|
return Promise.resolve({
|
|
952
1009
|
error: SyncBlockError.Errored
|
|
953
1010
|
});
|
|
@@ -964,6 +1021,8 @@ export var SourceSyncBlockStoreManager = /*#__PURE__*/function () {
|
|
|
964
1021
|
this.postCreationFlushCallback = undefined;
|
|
965
1022
|
this.pendingCreationPromises.clear();
|
|
966
1023
|
this.creationsTimedOutDuringFlush.clear();
|
|
1024
|
+
this.creationEnrichment.clear();
|
|
1025
|
+
this.awaitingFirstContent.clear();
|
|
967
1026
|
this.recentDeleteEmissions.clear();
|
|
968
1027
|
this.dataProvider = undefined;
|
|
969
1028
|
(_this$saveExperience4 = this.saveExperience) === null || _this$saveExperience4 === void 0 || _this$saveExperience4.abort({
|
|
@@ -348,16 +348,49 @@ export var createSuccessPayload = function createSuccessPayload(resourceId, sour
|
|
|
348
348
|
};
|
|
349
349
|
|
|
350
350
|
/**
|
|
351
|
-
*
|
|
352
|
-
*
|
|
353
|
-
*
|
|
351
|
+
* Optional enrichment for the `syncedBlockCreate` success event. All fields
|
|
352
|
+
* optional so gate-off/legacy payloads are unchanged. `inputMethod`: creating
|
|
353
|
+
* surface (enum, PII-safe). `createdEmpty`: true from an empty selection, false
|
|
354
|
+
* when content was converted.
|
|
354
355
|
*/
|
|
355
|
-
|
|
356
|
+
|
|
357
|
+
/**
|
|
358
|
+
* Operational `syncedBlockCreate` success event, behind
|
|
359
|
+
* `platform_editor_blocks_patch_4`, with the `blockInstanceId` join key and,
|
|
360
|
+
* when available, the `inputMethod` + `createdEmpty` creation-type signals.
|
|
361
|
+
*/
|
|
362
|
+
export var createSuccessOperationalPayload = function createSuccessOperationalPayload(resourceId, blockInstanceId, sourceProduct, enrichment) {
|
|
356
363
|
return {
|
|
357
364
|
action: ACTION.INSERTED,
|
|
358
365
|
actionSubject: ACTION_SUBJECT.SYNCED_BLOCK,
|
|
359
366
|
actionSubjectId: ACTION_SUBJECT_ID.SYNCED_BLOCK_CREATE,
|
|
360
367
|
eventType: EVENT_TYPE.OPERATIONAL,
|
|
368
|
+
attributes: _objectSpread(_objectSpread(_objectSpread(_objectSpread({
|
|
369
|
+
resourceId: resourceId
|
|
370
|
+
}, blockInstanceId && {
|
|
371
|
+
blockInstanceId: blockInstanceId
|
|
372
|
+
}), sourceProduct && {
|
|
373
|
+
sourceProduct: sourceProduct
|
|
374
|
+
}), (enrichment === null || enrichment === void 0 ? void 0 : enrichment.inputMethod) && {
|
|
375
|
+
inputMethod: enrichment.inputMethod
|
|
376
|
+
}), (enrichment === null || enrichment === void 0 ? void 0 : enrichment.createdEmpty) !== undefined && {
|
|
377
|
+
createdEmpty: enrichment.createdEmpty
|
|
378
|
+
})
|
|
379
|
+
};
|
|
380
|
+
};
|
|
381
|
+
|
|
382
|
+
/**
|
|
383
|
+
* Operational first-content-added event, behind
|
|
384
|
+
* `platform_editor_blocks_patch_4`. Fired once when a block created empty first
|
|
385
|
+
* gains user content. Join keys only (`resourceId` + `blockInstanceId`), no user
|
|
386
|
+
* content (PII-safe).
|
|
387
|
+
*/
|
|
388
|
+
export var addContentSuccessPayload = function addContentSuccessPayload(resourceId, blockInstanceId, sourceProduct) {
|
|
389
|
+
return {
|
|
390
|
+
action: ACTION.ADDED,
|
|
391
|
+
actionSubject: ACTION_SUBJECT.SYNCED_BLOCK,
|
|
392
|
+
actionSubjectId: ACTION_SUBJECT_ID.SYNCED_BLOCK_ADD_CONTENT,
|
|
393
|
+
eventType: EVENT_TYPE.OPERATIONAL,
|
|
361
394
|
attributes: _objectSpread(_objectSpread({
|
|
362
395
|
resourceId: resourceId
|
|
363
396
|
}, blockInstanceId && {
|
|
@@ -3,6 +3,7 @@ import type { ViewMode } from '@atlaskit/editor-plugin-editor-viewmode';
|
|
|
3
3
|
import type { Node as PMNode } from '@atlaskit/editor-prosemirror/model';
|
|
4
4
|
import type { ResourceId, SyncBlockAttrs, BlockInstanceId, DeletionReason, DeletionMechanism, ReferenceSyncBlockData } from '../common/types';
|
|
5
5
|
import type { SyncBlockDataProviderInterface, SyncBlockSourceInfo } from '../providers/types';
|
|
6
|
+
import type { CreateSuccessEnrichment } from '../utils/errorHandling';
|
|
6
7
|
export type ConfirmationCallback = (syncBlockIds: SyncBlockAttrs[], deleteReason: DeletionReason | undefined) => Promise<boolean>;
|
|
7
8
|
type OnDelete = () => void;
|
|
8
9
|
type OnCompletion = (success: boolean) => void;
|
|
@@ -35,6 +36,20 @@ export declare class SourceSyncBlockStoreManager {
|
|
|
35
36
|
* multiple blocks are created concurrently. See EDITOR-7112.
|
|
36
37
|
*/
|
|
37
38
|
private creationsTimedOutDuringFlush;
|
|
39
|
+
/**
|
|
40
|
+
* Creation-type signals captured at `createBodiedSyncBlockNode` time and read
|
|
41
|
+
* back in `commitPendingCreation` to enrich the `syncedBlockCreate` event.
|
|
42
|
+
* Kept out of the flushed cache record so it never leaks into Block Service /
|
|
43
|
+
* ADF; removed once consumed.
|
|
44
|
+
*/
|
|
45
|
+
private creationEnrichment;
|
|
46
|
+
/**
|
|
47
|
+
* Resource IDs of blocks created empty this session that have not yet fired
|
|
48
|
+
* the first-content-added event. Added on empty creation, removed when the
|
|
49
|
+
* block first gains content (the event fires once). Blocks created with
|
|
50
|
+
* content or not created this session are never tracked.
|
|
51
|
+
*/
|
|
52
|
+
private awaitingFirstContent;
|
|
38
53
|
private createExperience;
|
|
39
54
|
private saveExperience;
|
|
40
55
|
private deleteExperience;
|
|
@@ -86,6 +101,13 @@ export declare class SourceSyncBlockStoreManager {
|
|
|
86
101
|
* @param success
|
|
87
102
|
*/
|
|
88
103
|
commitPendingCreation(success: boolean, resourceId: ResourceId): void;
|
|
104
|
+
/**
|
|
105
|
+
* Fire the first-content-added event once when a block created empty this
|
|
106
|
+
* session first gains content. The caller detects the in-block edit; this owns
|
|
107
|
+
* dedupe + emission (fires at most once per block, only for empty→content).
|
|
108
|
+
* Gated behind `platform_editor_blocks_patch_4`.
|
|
109
|
+
*/
|
|
110
|
+
maybeEmitFirstContentAdded(resourceId: ResourceId, blockInstanceId?: BlockInstanceId): void;
|
|
89
111
|
registerConfirmationCallback(callback: ConfirmationCallback): () => void;
|
|
90
112
|
requireConfirmationBeforeDelete(): boolean;
|
|
91
113
|
/**
|
|
@@ -97,8 +119,12 @@ export declare class SourceSyncBlockStoreManager {
|
|
|
97
119
|
* @param attrs attributes Ids of the node
|
|
98
120
|
* @param node the ProseMirror node to cache
|
|
99
121
|
* @param onCompletion callback invoked when creation completes
|
|
122
|
+
* @param enrichment optional creation-type signals stashed and attached to the
|
|
123
|
+
* `syncedBlockCreate` event when the async create resolves. When
|
|
124
|
+
* `createdEmpty` is true the block is also registered for the
|
|
125
|
+
* first-content-added event.
|
|
100
126
|
*/
|
|
101
|
-
createBodiedSyncBlockNode(attrs: SyncBlockAttrs, node: PMNode, onCompletion: OnCompletion): void;
|
|
127
|
+
createBodiedSyncBlockNode(attrs: SyncBlockAttrs, node: PMNode, onCompletion: OnCompletion, enrichment?: CreateSuccessEnrichment): void;
|
|
102
128
|
private setPendingDeletion;
|
|
103
129
|
/**
|
|
104
130
|
* Drop dedupe entries older than {@link DELETE_DEDUPE_WINDOW_MS} so
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { ACTION, ACTION_SUBJECT, ACTION_SUBJECT_ID } from '@atlaskit/editor-common/analytics';
|
|
2
|
-
import type { RendererSyncBlockEventPayload, OperationalAEP, SyncBlockEventPayload } from '@atlaskit/editor-common/analytics';
|
|
2
|
+
import type { INPUT_METHOD, RendererSyncBlockEventPayload, OperationalAEP, SyncBlockEventPayload } from '@atlaskit/editor-common/analytics';
|
|
3
3
|
import { SyncBlockError } from '../common/types';
|
|
4
4
|
import type { DeletionMechanism, DeletionReason } from '../common/types';
|
|
5
5
|
export declare const stringifyError: (error: unknown) => string | undefined;
|
|
@@ -175,11 +175,28 @@ export declare const fetchReferencesErrorPayload: (error: string, resourceId?: s
|
|
|
175
175
|
export declare const fetchSuccessPayload: (resourceId: string, blockInstanceId?: string, sourceProduct?: string) => RendererSyncBlockEventPayload;
|
|
176
176
|
export declare const createSuccessPayload: (resourceId: string, sourceProduct?: string) => SyncBlockEventPayload;
|
|
177
177
|
/**
|
|
178
|
-
*
|
|
179
|
-
*
|
|
180
|
-
*
|
|
178
|
+
* Optional enrichment for the `syncedBlockCreate` success event. All fields
|
|
179
|
+
* optional so gate-off/legacy payloads are unchanged. `inputMethod`: creating
|
|
180
|
+
* surface (enum, PII-safe). `createdEmpty`: true from an empty selection, false
|
|
181
|
+
* when content was converted.
|
|
181
182
|
*/
|
|
182
|
-
export
|
|
183
|
+
export type CreateSuccessEnrichment = {
|
|
184
|
+
createdEmpty?: boolean;
|
|
185
|
+
inputMethod?: INPUT_METHOD;
|
|
186
|
+
};
|
|
187
|
+
/**
|
|
188
|
+
* Operational `syncedBlockCreate` success event, behind
|
|
189
|
+
* `platform_editor_blocks_patch_4`, with the `blockInstanceId` join key and,
|
|
190
|
+
* when available, the `inputMethod` + `createdEmpty` creation-type signals.
|
|
191
|
+
*/
|
|
192
|
+
export declare const createSuccessOperationalPayload: (resourceId: string, blockInstanceId?: string, sourceProduct?: string, enrichment?: CreateSuccessEnrichment) => SyncBlockEventPayload;
|
|
193
|
+
/**
|
|
194
|
+
* Operational first-content-added event, behind
|
|
195
|
+
* `platform_editor_blocks_patch_4`. Fired once when a block created empty first
|
|
196
|
+
* gains user content. Join keys only (`resourceId` + `blockInstanceId`), no user
|
|
197
|
+
* content (PII-safe).
|
|
198
|
+
*/
|
|
199
|
+
export declare const addContentSuccessPayload: (resourceId: string, blockInstanceId?: string, sourceProduct?: string) => SyncBlockEventPayload;
|
|
183
200
|
export declare const updateSuccessPayload: (resourceId: string, hasReference?: boolean, sourceProduct?: string) => SyncBlockEventPayload;
|
|
184
201
|
/**
|
|
185
202
|
* Optional enrichment for the `syncedBlockDelete` success event behind
|
package/package.json
CHANGED
|
@@ -21,7 +21,7 @@
|
|
|
21
21
|
"@atlaskit/editor-prosemirror": "^8.0.0",
|
|
22
22
|
"@atlaskit/node-data-provider": "^13.0.0",
|
|
23
23
|
"@atlaskit/platform-feature-flags": "^2.0.0",
|
|
24
|
-
"@atlaskit/tmp-editor-statsig": "^
|
|
24
|
+
"@atlaskit/tmp-editor-statsig": "^124.1.0",
|
|
25
25
|
"@babel/runtime": "^7.0.0",
|
|
26
26
|
"@compiled/react": "^0.20.0",
|
|
27
27
|
"graphql-ws": "^5.14.2",
|
|
@@ -30,7 +30,7 @@
|
|
|
30
30
|
"uuid": "^3.1.0"
|
|
31
31
|
},
|
|
32
32
|
"peerDependencies": {
|
|
33
|
-
"@atlaskit/editor-common": "^116.
|
|
33
|
+
"@atlaskit/editor-common": "^116.25.0",
|
|
34
34
|
"react": "^18.2.0"
|
|
35
35
|
},
|
|
36
36
|
"devDependencies": {
|
|
@@ -74,7 +74,7 @@
|
|
|
74
74
|
}
|
|
75
75
|
},
|
|
76
76
|
"name": "@atlaskit/editor-synced-block-provider",
|
|
77
|
-
"version": "8.
|
|
77
|
+
"version": "8.6.0",
|
|
78
78
|
"description": "Synced Block Provider for @atlaskit/editor-plugin-synced-block",
|
|
79
79
|
"author": "Atlassian Pty Ltd",
|
|
80
80
|
"license": "Apache-2.0",
|