@atlaskit/editor-synced-block-provider 3.30.5 → 3.31.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.
Files changed (83) hide show
  1. package/CHANGELOG.md +21 -0
  2. package/dist/cjs/clients/block-service/blockService.js +121 -17
  3. package/dist/cjs/clients/block-service/blockSubscription.js +48 -3
  4. package/dist/cjs/clients/confluence/fetchMediaToken.js +7 -5
  5. package/dist/cjs/hooks/useFetchSyncBlockTitle.js +40 -2
  6. package/dist/cjs/hooks/useHandleContentChanges.js +3 -0
  7. package/dist/cjs/index.js +12 -33
  8. package/dist/cjs/providers/block-service/blockServiceAPI.js +509 -185
  9. package/dist/cjs/providers/syncBlockProvider.js +68 -6
  10. package/dist/cjs/store-manager/referenceSyncBlockStoreManager.js +3 -0
  11. package/dist/cjs/store-manager/syncBlockStoreManager.js +20 -5
  12. package/dist/cjs/utils/experienceTracking.js +10 -10
  13. package/dist/cjs/utils/resourceId.js +2 -2
  14. package/dist/cjs/utils/retry.js +33 -7
  15. package/dist/cjs/utils/utils.js +1 -1
  16. package/dist/cjs/utils/validValue.js +2 -1
  17. package/dist/es2019/clients/block-service/blockService.js +108 -13
  18. package/dist/es2019/clients/block-service/blockSubscription.js +62 -2
  19. package/dist/es2019/clients/confluence/fetchMediaToken.js +5 -3
  20. package/dist/es2019/hooks/useFetchSyncBlockTitle.js +36 -3
  21. package/dist/es2019/hooks/useHandleContentChanges.js +3 -0
  22. package/dist/es2019/index.js +2 -6
  23. package/dist/es2019/providers/block-service/blockServiceAPI.js +172 -23
  24. package/dist/es2019/providers/syncBlockProvider.js +56 -3
  25. package/dist/es2019/store-manager/referenceSyncBlockStoreManager.js +3 -0
  26. package/dist/es2019/store-manager/syncBlockStoreManager.js +19 -6
  27. package/dist/es2019/utils/experienceTracking.js +10 -10
  28. package/dist/es2019/utils/resourceId.js +2 -2
  29. package/dist/es2019/utils/retry.js +26 -6
  30. package/dist/es2019/utils/utils.js +1 -1
  31. package/dist/es2019/utils/validValue.js +2 -1
  32. package/dist/esm/clients/block-service/blockService.js +120 -16
  33. package/dist/esm/clients/block-service/blockSubscription.js +47 -2
  34. package/dist/esm/clients/confluence/fetchMediaToken.js +7 -5
  35. package/dist/esm/hooks/useFetchSyncBlockTitle.js +41 -3
  36. package/dist/esm/hooks/useHandleContentChanges.js +3 -0
  37. package/dist/esm/index.js +2 -6
  38. package/dist/esm/providers/block-service/blockServiceAPI.js +513 -189
  39. package/dist/esm/providers/syncBlockProvider.js +69 -7
  40. package/dist/esm/store-manager/referenceSyncBlockStoreManager.js +3 -0
  41. package/dist/esm/store-manager/syncBlockStoreManager.js +21 -6
  42. package/dist/esm/utils/experienceTracking.js +10 -10
  43. package/dist/esm/utils/resourceId.js +2 -2
  44. package/dist/esm/utils/retry.js +33 -7
  45. package/dist/esm/utils/utils.js +1 -1
  46. package/dist/esm/utils/validValue.js +2 -1
  47. package/dist/types/clients/block-service/blockService.d.ts +36 -0
  48. package/dist/types/clients/block-service/blockSubscription.d.ts +26 -1
  49. package/dist/types/index.d.ts +2 -6
  50. package/dist/types/providers/block-service/blockServiceAPI.d.ts +15 -7
  51. package/dist/types/providers/syncBlockProvider.d.ts +1 -1
  52. package/dist/types/providers/types.d.ts +6 -0
  53. package/dist/types/store-manager/syncBlockStoreManager.d.ts +1 -1
  54. package/dist/types/utils/experienceTracking.d.ts +10 -10
  55. package/dist/types-ts4.5/clients/block-service/blockService.d.ts +36 -0
  56. package/dist/types-ts4.5/clients/block-service/blockSubscription.d.ts +26 -1
  57. package/dist/types-ts4.5/index.d.ts +2 -6
  58. package/dist/types-ts4.5/providers/block-service/blockServiceAPI.d.ts +15 -7
  59. package/dist/types-ts4.5/providers/syncBlockProvider.d.ts +1 -1
  60. package/dist/types-ts4.5/providers/types.d.ts +6 -0
  61. package/dist/types-ts4.5/store-manager/syncBlockStoreManager.d.ts +1 -1
  62. package/dist/types-ts4.5/utils/experienceTracking.d.ts +10 -10
  63. package/package.json +5 -2
  64. package/dist/cjs/clients/block-service/sharedSubscriptionUtils.js +0 -82
  65. package/dist/cjs/utils/__generated__/relaySubscriptionUtilsSubscription.graphql.js +0 -94
  66. package/dist/cjs/utils/relayResponseConverter.js +0 -76
  67. package/dist/cjs/utils/relaySubscriptionUtils.js +0 -130
  68. package/dist/es2019/clients/block-service/sharedSubscriptionUtils.js +0 -91
  69. package/dist/es2019/utils/__generated__/relaySubscriptionUtilsSubscription.graphql.js +0 -88
  70. package/dist/es2019/utils/relayResponseConverter.js +0 -69
  71. package/dist/es2019/utils/relaySubscriptionUtils.js +0 -125
  72. package/dist/esm/clients/block-service/sharedSubscriptionUtils.js +0 -76
  73. package/dist/esm/utils/__generated__/relaySubscriptionUtilsSubscription.graphql.js +0 -88
  74. package/dist/esm/utils/relayResponseConverter.js +0 -69
  75. package/dist/esm/utils/relaySubscriptionUtils.js +0 -123
  76. package/dist/types/clients/block-service/sharedSubscriptionUtils.d.ts +0 -61
  77. package/dist/types/utils/__generated__/relaySubscriptionUtilsSubscription.graphql.d.ts +0 -31
  78. package/dist/types/utils/relayResponseConverter.d.ts +0 -47
  79. package/dist/types/utils/relaySubscriptionUtils.d.ts +0 -61
  80. package/dist/types-ts4.5/clients/block-service/sharedSubscriptionUtils.d.ts +0 -61
  81. package/dist/types-ts4.5/utils/__generated__/relaySubscriptionUtilsSubscription.graphql.d.ts +0 -31
  82. package/dist/types-ts4.5/utils/relayResponseConverter.d.ts +0 -47
  83. package/dist/types-ts4.5/utils/relaySubscriptionUtils.d.ts +0 -61
@@ -6,6 +6,7 @@ Object.defineProperty(exports, "__esModule", {
6
6
  });
7
7
  exports.useMemoizedSyncedBlockProvider = exports.SyncedBlockProvider = void 0;
8
8
  var _regenerator = _interopRequireDefault(require("@babel/runtime/regenerator"));
9
+ var _toConsumableArray2 = _interopRequireDefault(require("@babel/runtime/helpers/toConsumableArray"));
9
10
  var _asyncToGenerator2 = _interopRequireDefault(require("@babel/runtime/helpers/asyncToGenerator"));
10
11
  var _classCallCheck2 = _interopRequireDefault(require("@babel/runtime/helpers/classCallCheck"));
11
12
  var _createClass2 = _interopRequireDefault(require("@babel/runtime/helpers/createClass"));
@@ -15,6 +16,7 @@ var _inherits2 = _interopRequireDefault(require("@babel/runtime/helpers/inherits
15
16
  var _defineProperty2 = _interopRequireDefault(require("@babel/runtime/helpers/defineProperty"));
16
17
  var _react = require("react");
17
18
  var _platformFeatureFlags = require("@atlaskit/platform-feature-flags");
19
+ var _platformFeatureFlagsReact = require("@atlaskit/platform-feature-flags-react");
18
20
  var _ari = require("../clients/block-service/ari");
19
21
  var _ari2 = require("../clients/confluence/ari");
20
22
  var _sourceInfo2 = require("../clients/confluence/sourceInfo");
@@ -159,7 +161,7 @@ var SyncedBlockProvider = exports.SyncedBlockProvider = /*#__PURE__*/function (_
159
161
  value: (function () {
160
162
  var _writeNodesData = (0, _asyncToGenerator2.default)( /*#__PURE__*/_regenerator.default.mark(function _callee2(nodes, data) {
161
163
  var _this3 = this;
162
- var results;
164
+ var validDataWithIndices, invalidResults, batchResults, results;
163
165
  return _regenerator.default.wrap(function _callee2$(_context2) {
164
166
  while (1) switch (_context2.prev = _context2.next) {
165
167
  case 0:
@@ -169,7 +171,38 @@ var SyncedBlockProvider = exports.SyncedBlockProvider = /*#__PURE__*/function (_
169
171
  }
170
172
  return _context2.abrupt("return", Promise.reject(new Error('Write provider not set')));
171
173
  case 2:
172
- _context2.next = 4;
174
+ if (!(this.writeProvider.writeDataBatch && (0, _platformFeatureFlags.fg)('platform_synced_block_patch_4'))) {
175
+ _context2.next = 12;
176
+ break;
177
+ }
178
+ // Separate data into valid (with content) and invalid (without content)
179
+ validDataWithIndices = [];
180
+ invalidResults = [];
181
+ data.forEach(function (blockData) {
182
+ if (blockData.content) {
183
+ validDataWithIndices.push(blockData);
184
+ } else {
185
+ invalidResults.push({
186
+ error: 'No Synced Block content to write',
187
+ resourceId: blockData.resourceId
188
+ });
189
+ }
190
+ });
191
+
192
+ // Process valid data in batch
193
+ batchResults = [];
194
+ if (!(validDataWithIndices.length > 0)) {
195
+ _context2.next = 11;
196
+ break;
197
+ }
198
+ _context2.next = 10;
199
+ return this.writeProvider.writeDataBatch(validDataWithIndices);
200
+ case 10:
201
+ batchResults = _context2.sent;
202
+ case 11:
203
+ return _context2.abrupt("return", [].concat((0, _toConsumableArray2.default)(batchResults), invalidResults));
204
+ case 12:
205
+ _context2.next = 14;
173
206
  return Promise.allSettled(nodes.map(function (_node, index) {
174
207
  var _this3$writeProvider;
175
208
  if (!_this3.writeProvider) {
@@ -180,7 +213,7 @@ var SyncedBlockProvider = exports.SyncedBlockProvider = /*#__PURE__*/function (_
180
213
  }
181
214
  return (_this3$writeProvider = _this3.writeProvider) === null || _this3$writeProvider === void 0 ? void 0 : _this3$writeProvider.writeData(data[index]);
182
215
  }));
183
- case 4:
216
+ case 14:
184
217
  results = _context2.sent;
185
218
  return _context2.abrupt("return", results.map(function (result) {
186
219
  if (result.status === 'fulfilled') {
@@ -191,7 +224,7 @@ var SyncedBlockProvider = exports.SyncedBlockProvider = /*#__PURE__*/function (_
191
224
  };
192
225
  }
193
226
  }));
194
- case 6:
227
+ case 16:
195
228
  case "end":
196
229
  return _context2.stop();
197
230
  }
@@ -451,7 +484,7 @@ var createSyncedBlockProvider = function createSyncedBlockProvider(_ref) {
451
484
  writeProvider = _ref.writeProvider;
452
485
  return new SyncedBlockProvider(fetchProvider, writeProvider);
453
486
  };
454
- var useMemoizedSyncedBlockProvider = exports.useMemoizedSyncedBlockProvider = function useMemoizedSyncedBlockProvider(_ref2) {
487
+ var useMemoizedSyncedBlockProviderBase = function useMemoizedSyncedBlockProviderBase(_ref2) {
455
488
  var fetchProvider = _ref2.fetchProvider,
456
489
  writeProvider = _ref2.writeProvider,
457
490
  providerOptions = _ref2.providerOptions,
@@ -468,4 +501,33 @@ var useMemoizedSyncedBlockProvider = exports.useMemoizedSyncedBlockProvider = fu
468
501
  syncBlockProvider.setSSRData(ssrData);
469
502
  }
470
503
  return syncBlockProvider;
471
- };
504
+ };
505
+ var useMemoizedSyncedBlockProviderPatched = function useMemoizedSyncedBlockProviderPatched(_ref3) {
506
+ var fetchProvider = _ref3.fetchProvider,
507
+ writeProvider = _ref3.writeProvider,
508
+ providerOptions = _ref3.providerOptions,
509
+ getSSRData = _ref3.getSSRData;
510
+ var syncBlockProvider = (0, _react.useMemo)(function () {
511
+ return createSyncedBlockProvider({
512
+ fetchProvider: fetchProvider,
513
+ writeProvider: writeProvider
514
+ });
515
+ }, [fetchProvider, writeProvider]);
516
+ var prevProviderOptionsRef = (0, _react.useRef)(undefined);
517
+ if (providerOptions !== prevProviderOptionsRef.current) {
518
+ prevProviderOptionsRef.current = providerOptions;
519
+ syncBlockProvider.setProviderOptions(providerOptions);
520
+ }
521
+ var prevSSRDataRef = (0, _react.useRef)(undefined);
522
+ var ssrData = getSSRData === null || getSSRData === void 0 ? void 0 : getSSRData();
523
+ if (ssrData !== prevSSRDataRef.current) {
524
+ prevSSRDataRef.current = ssrData;
525
+ if (ssrData) {
526
+ syncBlockProvider.setSSRData(ssrData);
527
+ }
528
+ }
529
+ return syncBlockProvider;
530
+ };
531
+ var useMemoizedSyncedBlockProvider = exports.useMemoizedSyncedBlockProvider = (0, _platformFeatureFlagsReact.conditionalHooksFactory)(function () {
532
+ return (0, _platformFeatureFlags.fg)('platform_synced_block_patch_4');
533
+ }, useMemoizedSyncedBlockProviderPatched, useMemoizedSyncedBlockProviderBase);
@@ -392,6 +392,9 @@ var ReferenceSyncBlockStoreManager = exports.ReferenceSyncBlockStoreManager = /*
392
392
  value: function handleGraphQLSubscriptionUpdate(syncBlockInstance) {
393
393
  var _this5 = this;
394
394
  if (!syncBlockInstance.resourceId) {
395
+ if ((0, _platformFeatureFlags.fg)('platform_synced_block_patch_4')) {
396
+ return;
397
+ }
395
398
  throw new Error('Sync block instance provided to graphql subscription update missing resource id');
396
399
  }
397
400
  var existingSyncBlock = this.getFromCache(syncBlockInstance.resourceId);
@@ -12,6 +12,8 @@ var _classCallCheck2 = _interopRequireDefault(require("@babel/runtime/helpers/cl
12
12
  var _createClass2 = _interopRequireDefault(require("@babel/runtime/helpers/createClass"));
13
13
  var _react = require("react");
14
14
  var _monitoring = require("@atlaskit/editor-common/monitoring");
15
+ var _platformFeatureFlags = require("@atlaskit/platform-feature-flags");
16
+ var _platformFeatureFlagsReact = require("@atlaskit/platform-feature-flags-react");
15
17
  var _ari = require("../clients/block-service/ari");
16
18
  var _types = require("../common/types");
17
19
  var _errorHandling = require("../utils/errorHandling");
@@ -28,7 +30,7 @@ function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t =
28
30
  var SyncBlockStoreManager = exports.SyncBlockStoreManager = /*#__PURE__*/function () {
29
31
  function SyncBlockStoreManager(dataProvider) {
30
32
  (0, _classCallCheck2.default)(this, SyncBlockStoreManager);
31
- // In future, if reference manager needs to reach to source manager and read it's current in memorey cache
33
+ // In future, if reference manager needs to reach to source manager and read its current in memory cache
32
34
  // we can pass the source manager as a parameter to the reference manager constructor
33
35
  this.sourceSyncBlockStoreManager = new _sourceSyncBlockStoreManager.SourceSyncBlockStoreManager(dataProvider);
34
36
  this.referenceSyncBlockStoreManager = new _referenceSyncBlockStoreManager.ReferenceSyncBlockStoreManager(dataProvider);
@@ -200,11 +202,24 @@ var SyncBlockStoreManager = exports.SyncBlockStoreManager = /*#__PURE__*/functio
200
202
  var createSyncBlockStoreManager = function createSyncBlockStoreManager(dataProvider) {
201
203
  return new SyncBlockStoreManager(dataProvider);
202
204
  };
203
- var useMemoizedSyncBlockStoreManager = exports.useMemoizedSyncBlockStoreManager = function useMemoizedSyncBlockStoreManager(dataProvider, fireAnalyticsEvent) {
205
+ var useMemoizedSyncBlockStoreManagerBase = function useMemoizedSyncBlockStoreManagerBase(dataProvider, fireAnalyticsEvent) {
204
206
  var syncBlockStoreManager = (0, _react.useMemo)(function () {
205
- var syncBlockStoreManager = createSyncBlockStoreManager(dataProvider);
206
- return syncBlockStoreManager;
207
+ return createSyncBlockStoreManager(dataProvider);
207
208
  }, [dataProvider]);
208
209
  syncBlockStoreManager.setFireAnalyticsEvent(fireAnalyticsEvent);
209
210
  return syncBlockStoreManager;
210
- };
211
+ };
212
+ var useMemoizedSyncBlockStoreManagerPatched = function useMemoizedSyncBlockStoreManagerPatched(dataProvider, fireAnalyticsEvent) {
213
+ var syncBlockStoreManager = (0, _react.useMemo)(function () {
214
+ return createSyncBlockStoreManager(dataProvider);
215
+ }, [dataProvider]);
216
+ var prevFireAnalyticsEventRef = (0, _react.useRef)(undefined);
217
+ if (fireAnalyticsEvent !== prevFireAnalyticsEventRef.current) {
218
+ prevFireAnalyticsEventRef.current = fireAnalyticsEvent;
219
+ syncBlockStoreManager.setFireAnalyticsEvent(fireAnalyticsEvent);
220
+ }
221
+ return syncBlockStoreManager;
222
+ };
223
+ var useMemoizedSyncBlockStoreManager = exports.useMemoizedSyncBlockStoreManager = (0, _platformFeatureFlagsReact.conditionalHooksFactory)(function () {
224
+ return (0, _platformFeatureFlags.fg)('platform_synced_block_patch_4');
225
+ }, useMemoizedSyncBlockStoreManagerPatched, useMemoizedSyncBlockStoreManagerBase);
@@ -87,9 +87,9 @@ var getFetchSourceInfoExperience = exports.getFetchSourceInfoExperience = functi
87
87
  /**
88
88
  * This experience tracks when a source sync block is deleted from the BE.
89
89
  *
90
- * Start: When the fetchSourceInfo function is called.
91
- * Success: When the fetching the data is successful within the timeout duration of start.
92
- * Failure: When the timeout duration passes without the data being successfully fetched, or the fetch fails
90
+ * Start: When the delete source sync block function is called.
91
+ * Success: When the sync block deletion is successful within the timeout duration of start.
92
+ * Failure: When the timeout duration passes without the sync block being successfully deleted, or the deletion fails
93
93
  */
94
94
  var getDeleteSourceExperience = exports.getDeleteSourceExperience = function getDeleteSourceExperience(fireAnalyticsEvent) {
95
95
  return new _experiences.Experience(_experiences.EXPERIENCE_ID.ASYNC_OPERATION, {
@@ -104,9 +104,9 @@ var getDeleteSourceExperience = exports.getDeleteSourceExperience = function get
104
104
  /**
105
105
  * This experience tracks when a source sync block is created and registered to the BE.
106
106
  *
107
- * Start: When the fetchSourceInfo function is called.
108
- * Success: When the fetching the data is successful within the timeout duration of start.
109
- * Failure: When the timeout duration passes without the data being successfully fetched, or the fetch fails
107
+ * Start: When the create source sync block function is called.
108
+ * Success: When the sync block creation is successful within the timeout duration of start.
109
+ * Failure: When the timeout duration passes without the sync block being successfully created, or the creation fails
110
110
  */
111
111
  var getCreateSourceExperience = exports.getCreateSourceExperience = function getCreateSourceExperience(fireAnalyticsEvent) {
112
112
  return new _experiences.Experience(_experiences.EXPERIENCE_ID.ASYNC_OPERATION, {
@@ -119,11 +119,11 @@ var getCreateSourceExperience = exports.getCreateSourceExperience = function get
119
119
  };
120
120
 
121
121
  /**
122
- * This experience tracks when a source sync block is created and registered to the BE.
122
+ * This experience tracks when references for a sync block are fetched from the BE.
123
123
  *
124
- * Start: When the fetchSourceInfo function is called.
125
- * Success: When the fetching the data is successful within the timeout duration of start.
126
- * Failure: When the timeout duration passes without the data being successfully fetched, or the fetch fails
124
+ * Start: When the fetchReferences function is called.
125
+ * Success: When the fetching of references is successful within the timeout duration of start.
126
+ * Failure: When the timeout duration passes without references being successfully fetched, or the fetch fails
127
127
  */
128
128
  var getFetchReferencesExperience = exports.getFetchReferencesExperience = function getFetchReferencesExperience(fireAnalyticsEvent) {
129
129
  return new _experiences.Experience(_experiences.EXPERIENCE_ID.ASYNC_OPERATION, {
@@ -19,7 +19,7 @@ var isSyncBlockProduct = function isSyncBlockProduct(product) {
19
19
  *
20
20
  * Format
21
21
  * - {product}/{contentId}/{uuid}
22
- * - product: a recognized `SyncBlockProduct` (e.g. 'confluence-page', 'jira-issue')
22
+ * - product: a recognized `SyncBlockProduct` (e.g. 'confluence-page', 'jira-work-item')
23
23
  * - contentId: the host content identifier (e.g. page ID or issue ID)
24
24
  * - uuid: the UUID for the specific synced block instance
25
25
  *
@@ -32,7 +32,7 @@ var isSyncBlockProduct = function isSyncBlockProduct(product) {
32
32
  * - No extra segments; returns `undefined` on any invalid input
33
33
  *
34
34
  * Notes
35
- * - `product` is a qualified domain like 'confluence-page' or 'jira-issue',
35
+ * - `product` is a qualified domain like 'confluence-page' or 'jira-work-item',
36
36
  * not just 'confluence' or 'jira'.
37
37
  */
38
38
 
@@ -7,10 +7,13 @@ Object.defineProperty(exports, "__esModule", {
7
7
  exports.fetchWithRetry = void 0;
8
8
  var _regenerator = _interopRequireDefault(require("@babel/runtime/regenerator"));
9
9
  var _asyncToGenerator2 = _interopRequireDefault(require("@babel/runtime/helpers/asyncToGenerator"));
10
- var parseRetryAfter = function parseRetryAfter(retryAfter) {
10
+ var _platformFeatureFlags = require("@atlaskit/platform-feature-flags");
11
+ var _platformFeatureFlagsReact = require("@atlaskit/platform-feature-flags-react");
12
+ var MAX_RETRY_DELAY = 30000;
13
+ var parseRetryAfterBase = function parseRetryAfterBase(retryAfter) {
11
14
  var newDelay;
12
15
 
13
- // retryAfter can either be in ms or HTTP date
16
+ // retryAfter can either be in seconds or HTTP date
14
17
  var parsedRetryAfter = parseInt(retryAfter);
15
18
  if (!isNaN(parsedRetryAfter)) {
16
19
  newDelay = parsedRetryAfter * 1000;
@@ -23,13 +26,35 @@ var parseRetryAfter = function parseRetryAfter(retryAfter) {
23
26
  }
24
27
  return newDelay;
25
28
  };
29
+ var parseRetryAfterPatched = function parseRetryAfterPatched(retryAfter) {
30
+ // retryAfter can either be in seconds or HTTP date
31
+ var parsedRetryAfter = parseInt(retryAfter, 10);
32
+ if (!isNaN(parsedRetryAfter) && parsedRetryAfter > 0) {
33
+ return parsedRetryAfter * 1000;
34
+ }
35
+ var retryDate = new Date(retryAfter);
36
+ if (isNaN(retryDate.getTime())) {
37
+ return undefined;
38
+ }
39
+ var delayFromDate = retryDate.getTime() - Date.now();
40
+ if (delayFromDate > 0) {
41
+ return delayFromDate;
42
+ }
43
+ return undefined;
44
+ };
45
+ var parseRetryAfter = (0, _platformFeatureFlagsReact.functionWithCondition)(function () {
46
+ return (0, _platformFeatureFlags.fg)('platform_synced_block_patch_4');
47
+ }, parseRetryAfterPatched, parseRetryAfterBase);
26
48
  var _fetchWithRetry = exports.fetchWithRetry = /*#__PURE__*/function () {
27
49
  var _ref = (0, _asyncToGenerator2.default)( /*#__PURE__*/_regenerator.default.mark(function _callee(url, options) {
50
+ var _ref2;
28
51
  var retriesRemaining,
29
52
  delay,
30
53
  response,
31
54
  shouldRetry,
32
55
  retryAfter,
56
+ parsedDelay,
57
+ retryDelay,
33
58
  _args = arguments;
34
59
  return _regenerator.default.wrap(function _callee$(_context) {
35
60
  while (1) switch (_context.prev = _context.next) {
@@ -48,14 +73,15 @@ var _fetchWithRetry = exports.fetchWithRetry = /*#__PURE__*/function () {
48
73
  return _context.abrupt("return", response);
49
74
  case 8:
50
75
  retryAfter = response.headers.get('Retry-After');
51
- _context.next = 11;
76
+ parsedDelay = (_ref2 = retryAfter ? parseRetryAfter(retryAfter) : undefined) !== null && _ref2 !== void 0 ? _ref2 : delay;
77
+ retryDelay = (0, _platformFeatureFlags.fg)('platform_synced_block_patch_4') ? Math.min(parsedDelay, MAX_RETRY_DELAY) : parsedDelay;
78
+ _context.next = 13;
52
79
  return new Promise(function (resolve) {
53
- var _ref2;
54
- return setTimeout(resolve, (_ref2 = retryAfter ? parseRetryAfter(retryAfter) : undefined) !== null && _ref2 !== void 0 ? _ref2 : delay);
80
+ return setTimeout(resolve, retryDelay);
55
81
  });
56
- case 11:
82
+ case 13:
57
83
  return _context.abrupt("return", _fetchWithRetry(url, options, retriesRemaining - 1, delay * 2));
58
- case 12:
84
+ case 14:
59
85
  case "end":
60
86
  return _context.stop();
61
87
  }
@@ -40,7 +40,7 @@ var convertPMNodesToSyncBlockNodes = exports.convertPMNodesToSyncBlockNodes = fu
40
40
  return convertPMNodeToSyncBlockNode(node);
41
41
  }).filter(function (node) {
42
42
  return node !== undefined;
43
- }) || [];
43
+ });
44
44
  };
45
45
 
46
46
  /*
@@ -4,8 +4,9 @@ Object.defineProperty(exports, "__esModule", {
4
4
  value: true
5
5
  });
6
6
  exports.normaliseSyncBlockStatus = exports.normaliseSyncBlockProduct = void 0;
7
+ var _consts = require("../common/consts");
7
8
  var normaliseSyncBlockProduct = exports.normaliseSyncBlockProduct = function normaliseSyncBlockProduct(value) {
8
- return value === 'confluence-page' || value === 'jira-work-item' ? value : undefined;
9
+ return _consts.SYNC_BLOCK_PRODUCTS.includes(value) ? value : undefined;
9
10
  };
10
11
  var normaliseSyncBlockStatus = exports.normaliseSyncBlockStatus = function normaliseSyncBlockStatus(value) {
11
12
  return value === 'active' || value === 'unpublished' || value === 'deleted' ? value : undefined;
@@ -81,8 +81,9 @@ const UPDATE_DOCUMENT_REFERENCES_OPERATION_NAME = 'EDITOR_SYNCED_BLOCK_UPDATE_DO
81
81
  const BATCH_RETRIEVE_BLOCKS_OPERATION_NAME = 'EDITOR_SYNCED_BLOCK_BATCH_RETRIEVE_BLOCKS';
82
82
  const GET_BLOCK_REFERENCES_OPERATION_NAME = 'EDITOR_SYNCED_BLOCK_GET_REFERENCES';
83
83
  const GET_BLOCK_OPERATION_NAME = 'EDITOR_SYNCED_BLOCK_GET_BLOCK';
84
+ const BATCH_UPDATE_BLOCKS_OPERATION_NAME = 'EDITOR_SYNCED_BLOCK_BATCH_UPDATE_BLOCKS';
84
85
  const buildGetDocumentReferenceBlocksQuery = documentAri => `query ${GET_DOCUMENT_REFERENCE_BLOCKS_OPERATION_NAME} {
85
- blockService_getDocumentReferenceBlocks(documentAri: "${documentAri}") {
86
+ blockService_getDocumentReferenceBlocks(documentAri: ${fg('platform_synced_block_patch_4') ? JSON.stringify(documentAri) : `"${documentAri}"`}) {
86
87
  blocks {
87
88
  blockAri
88
89
  blockInstanceId
@@ -151,11 +152,17 @@ const buildDeleteBlockMutation = (blockAri, deletionReason) => {
151
152
  * 'jira-work-item' -> 'JIRA_WORK_ITEM'
152
153
  */
153
154
  const convertProductToGraphQLEnum = product => {
154
- if (product === 'confluence-page') {
155
- return 'CONFLUENCE_PAGE';
155
+ switch (product) {
156
+ case 'confluence-page':
157
+ return 'CONFLUENCE_PAGE';
158
+ case 'jira-work-item':
159
+ return 'JIRA_WORK_ITEM';
160
+ default:
161
+ {
162
+ const exhaustiveCheck = product;
163
+ throw new Error(`Unsupported product: ${exhaustiveCheck}`);
164
+ }
156
165
  }
157
- // product must be 'jira-work-item' at this point
158
- return 'JIRA_WORK_ITEM';
159
166
  };
160
167
  const buildCreateBlockMutation = (blockAri, blockInstanceId, content, product, sourceAri, stepVersion, status) => {
161
168
  const inputParts = [`blockAri: ${JSON.stringify(blockAri)}`, `blockInstanceId: ${JSON.stringify(blockInstanceId)}`, `content: ${JSON.stringify(content)}`, `product: ${convertProductToGraphQLEnum(product)}`, `sourceAri: ${JSON.stringify(sourceAri)}`];
@@ -251,6 +258,40 @@ const buildGetBlockReferencesQuery = blockAri => {
251
258
  }
252
259
  }`;
253
260
  };
261
+ const buildBatchUpdateBlocksMutation = blocks => {
262
+ const blocksArray = blocks.map(block => {
263
+ const inputParts = [`blockAri: ${JSON.stringify(block.blockAri)}`, `content: ${JSON.stringify(block.content)}`];
264
+ if (block.stepVersion !== undefined) {
265
+ inputParts.push(`stepVersion: ${block.stepVersion}`);
266
+ }
267
+ if (block.status !== undefined) {
268
+ inputParts.push(`status: ${JSON.stringify(block.status)}`);
269
+ }
270
+ return `{ ${inputParts.join(', ')} }`;
271
+ }).join(', ');
272
+ return `mutation ${BATCH_UPDATE_BLOCKS_OPERATION_NAME} {
273
+ blockService_batchUpdateBlocks(input: { blocks: [${blocksArray}] }) {
274
+ success {
275
+ blockAri
276
+ blockInstanceId
277
+ content
278
+ contentUpdatedAt
279
+ createdAt
280
+ createdBy
281
+ deletionReason
282
+ product
283
+ sourceAri
284
+ status
285
+ version
286
+ }
287
+ error {
288
+ blockAri
289
+ code
290
+ reason
291
+ }
292
+ }
293
+ }`;
294
+ };
254
295
  export class BlockError extends Error {
255
296
  constructor(status) {
256
297
  super(`Block error`);
@@ -327,7 +368,7 @@ export const deleteSyncedBlock = async ({
327
368
  blockAri,
328
369
  deleteReason
329
370
  }) => {
330
- var _result$data3;
371
+ var _result$data3, _result$data3$blockSe, _result$data4;
331
372
  const bodyData = {
332
373
  query: buildDeleteBlockMutation(blockAri, deleteReason),
333
374
  operationName: DELETE_BLOCK_OPERATION_NAME
@@ -345,7 +386,8 @@ export const deleteSyncedBlock = async ({
345
386
  if (result.errors && result.errors.length > 0) {
346
387
  throw new Error(result.errors.map(e => e.message).join(', '));
347
388
  }
348
- if (!((_result$data3 = result.data) !== null && _result$data3 !== void 0 && _result$data3.blockService_deleteBlock.deleted)) {
389
+ const isDeleted = fg('platform_synced_block_patch_4') ? (_result$data3 = result.data) === null || _result$data3 === void 0 ? void 0 : (_result$data3$blockSe = _result$data3.blockService_deleteBlock) === null || _result$data3$blockSe === void 0 ? void 0 : _result$data3$blockSe.deleted : (_result$data4 = result.data) === null || _result$data4 === void 0 ? void 0 : _result$data4.blockService_deleteBlock.deleted;
390
+ if (!isDeleted) {
349
391
  throw new Error('Block deletion failed; deleted flag is false');
350
392
  }
351
393
  };
@@ -382,7 +424,7 @@ export const createSyncedBlock = async ({
382
424
  stepVersion,
383
425
  status
384
426
  }) => {
385
- var _result$data4;
427
+ var _result$data5;
386
428
  const bodyData = {
387
429
  query: buildCreateBlockMutation(blockAri, blockInstanceId, content, product, sourceAri, stepVersion, status),
388
430
  operationName: CREATE_BLOCK_OPERATION_NAME
@@ -400,7 +442,7 @@ export const createSyncedBlock = async ({
400
442
  if (result.errors && result.errors.length > 0) {
401
443
  throw new Error(result.errors.map(e => e.message).join(', '));
402
444
  }
403
- if (!((_result$data4 = result.data) !== null && _result$data4 !== void 0 && _result$data4.blockService_createBlock)) {
445
+ if (!((_result$data5 = result.data) !== null && _result$data5 !== void 0 && _result$data5.blockService_createBlock)) {
404
446
  throw new Error('No data returned from GraphQL mutation');
405
447
  }
406
448
  return result.data.blockService_createBlock;
@@ -429,8 +471,8 @@ export const updateReferenceSyncedBlockOnDocument = async ({
429
471
  throw new Error(result.errors.map(e => e.message).join(', '));
430
472
  }
431
473
  if (!noContent) {
432
- var _result$data5;
433
- if (!((_result$data5 = result.data) !== null && _result$data5 !== void 0 && _result$data5.blockService_updateDocumentReferences)) {
474
+ var _result$data6;
475
+ if (!((_result$data6 = result.data) !== null && _result$data6 !== void 0 && _result$data6.blockService_updateDocumentReferences)) {
434
476
  throw new Error('No data returned from GraphQL mutation');
435
477
  }
436
478
  return result.data.blockService_updateDocumentReferences;
@@ -439,7 +481,7 @@ export const updateReferenceSyncedBlockOnDocument = async ({
439
481
  export const getReferenceSyncedBlocksByBlockAri = async ({
440
482
  blockAri
441
483
  }) => {
442
- var _result$data6;
484
+ var _result$data7;
443
485
  const bodyData = {
444
486
  query: buildGetBlockReferencesQuery(blockAri),
445
487
  operationName: GET_BLOCK_REFERENCES_OPERATION_NAME
@@ -457,7 +499,7 @@ export const getReferenceSyncedBlocksByBlockAri = async ({
457
499
  if (result.errors && result.errors.length > 0) {
458
500
  throw new Error(result.errors.map(e => e.message).join(', '));
459
501
  }
460
- if (!((_result$data6 = result.data) !== null && _result$data6 !== void 0 && _result$data6.blockService_getReferences)) {
502
+ if (!((_result$data7 = result.data) !== null && _result$data7 !== void 0 && _result$data7.blockService_getReferences)) {
461
503
  throw new Error('No data returned from GraphQL query');
462
504
  }
463
505
  const graphqlResponse = result.data.blockService_getReferences;
@@ -466,4 +508,57 @@ export const getReferenceSyncedBlocksByBlockAri = async ({
466
508
  references: graphqlResponse.references || [],
467
509
  errors: graphqlResponse.errors || []
468
510
  };
511
+ };
512
+
513
+ /**
514
+ * Batch updates multiple synced blocks.
515
+ *
516
+ * Calls the Block Service GraphQL API: `blockService_batchUpdateBlocks`
517
+ *
518
+ * @param blocks - Array of block updates to apply
519
+ * @returns A promise containing arrays of successfully updated blocks and any errors encountered
520
+ *
521
+ * @example
522
+ * ```typescript
523
+ * const result = await updateSyncedBlocks({
524
+ * blocks: [
525
+ * {
526
+ * blockAri: 'ari:cloud:blocks:site-123:synced-block/uuid-456',
527
+ * content: '{"type":"doc","version":1,"content":[]}',
528
+ * status: 'active',
529
+ * stepVersion: 42
530
+ * }
531
+ * ]
532
+ * });
533
+ * ```
534
+ */
535
+ export const updateSyncedBlocks = async ({
536
+ blocks
537
+ }) => {
538
+ var _result$data8;
539
+ const bodyData = {
540
+ query: buildBatchUpdateBlocksMutation(blocks),
541
+ operationName: BATCH_UPDATE_BLOCKS_OPERATION_NAME
542
+ };
543
+ const url = fg('platform_synced_block_patch_3') ? `${GRAPHQL_ENDPOINT}?operation=editorSyncedBlockBatchUpdateBlocks` : GRAPHQL_ENDPOINT;
544
+ const response = await fetchWithRetry(url, {
545
+ method: 'POST',
546
+ headers: COMMON_HEADERS,
547
+ body: JSON.stringify(bodyData)
548
+ });
549
+ if (!response.ok) {
550
+ throw new BlockError(response.status);
551
+ }
552
+ const result = await response.json();
553
+ if (result.errors && result.errors.length > 0) {
554
+ throw new Error(result.errors.map(e => e.message).join(', '));
555
+ }
556
+ if (!((_result$data8 = result.data) !== null && _result$data8 !== void 0 && _result$data8.blockService_batchUpdateBlocks)) {
557
+ throw new Error('No data returned from GraphQL mutation');
558
+ }
559
+ const graphqlResponse = result.data.blockService_batchUpdateBlocks;
560
+ return {
561
+ success: graphqlResponse.success,
562
+ error: graphqlResponse.error
563
+ };
469
564
  };
@@ -1,6 +1,6 @@
1
1
  import { createClient } from 'graphql-ws';
2
2
  import { isSSR } from '@atlaskit/editor-common/core-utils';
3
- import { BLOCK_SERVICE_SUBSCRIPTION_QUERY, parseSubscriptionPayload } from './sharedSubscriptionUtils';
3
+ import { convertContentUpdatedAt } from '../../utils/utils';
4
4
  const GRAPHQL_WS_ENDPOINT = '/gateway/api/graphql/subscriptions';
5
5
  let blockServiceClient = null;
6
6
  const getBlockServiceClient = () => {
@@ -19,6 +19,66 @@ const getBlockServiceClient = () => {
19
19
  }
20
20
  return blockServiceClient;
21
21
  };
22
+ const SUBSCRIPTION_QUERY = `
23
+ subscription EDITOR_SYNCED_BLOCK_ON_BLOCK_UPDATED($resourceId: ID!) {
24
+ blockService_onBlockUpdated(resourceId: $resourceId) {
25
+ blockAri
26
+ blockInstanceId
27
+ content
28
+ contentUpdatedAt
29
+ createdAt
30
+ createdBy
31
+ deletionReason
32
+ product
33
+ sourceAri
34
+ status
35
+ }
36
+ }
37
+ `;
38
+ /**
39
+ * Extracts the resourceId from a block ARI.
40
+ * Block ARI format: ari:cloud:blocks:<cloudId>:synced-block/<resourceId>
41
+ * @param blockAri - The block ARI string
42
+ * @returns The resourceId portion of the ARI
43
+ */
44
+ const extractResourceIdFromBlockAri = blockAri => {
45
+ // eslint-disable-next-line require-unicode-regexp
46
+ const match = blockAri.match(/ari:cloud:blocks:[^:]+:synced-block\/(.+)$/);
47
+ return (match === null || match === void 0 ? void 0 : match[1]) || null;
48
+ };
49
+
50
+ /**
51
+ * Parses the subscription payload into a standardized format.
52
+ * @param payload - The raw subscription payload
53
+ * @returns Parsed block data or null if parsing fails
54
+ */
55
+ const parseSubscriptionPayload = payload => {
56
+ try {
57
+ const resourceId = extractResourceIdFromBlockAri(payload.blockAri);
58
+ if (!resourceId) {
59
+ return null;
60
+ }
61
+ let createdAt;
62
+ if (payload.createdAt !== undefined && payload.createdAt !== null) {
63
+ createdAt = new Date(payload.createdAt).toISOString();
64
+ }
65
+ return {
66
+ blockAri: payload.blockAri,
67
+ blockInstanceId: payload.blockInstanceId,
68
+ content: JSON.parse(payload.content),
69
+ contentUpdatedAt: convertContentUpdatedAt(payload.contentUpdatedAt),
70
+ createdAt,
71
+ createdBy: payload.createdBy,
72
+ product: payload.product,
73
+ resourceId,
74
+ sourceAri: payload.sourceAri,
75
+ status: payload.status
76
+ };
77
+ } catch {
78
+ return null;
79
+ }
80
+ };
81
+
22
82
  /**
23
83
  * Creates a GraphQL subscription to block updates using the shared graphql-ws client.
24
84
  *
@@ -34,7 +94,7 @@ export const subscribeToBlockUpdates = (blockAri, onData, onError) => {
34
94
  return () => {};
35
95
  }
36
96
  const unsubscribe = client.subscribe({
37
- query: BLOCK_SERVICE_SUBSCRIPTION_QUERY,
97
+ query: SUBSCRIPTION_QUERY,
38
98
  variables: {
39
99
  resourceId: blockAri
40
100
  },
@@ -1,4 +1,5 @@
1
1
  import { logException } from '@atlaskit/editor-common/monitoring';
2
+ import { fg } from '@atlaskit/platform-feature-flags';
2
3
  import { fetchWithRetry } from '../../utils/retry';
3
4
  const COMMON_HEADERS = {
4
5
  'Content-Type': 'application/json',
@@ -54,15 +55,16 @@ export const fetchMediaToken = async contentId => {
54
55
  if (!token || !configuration || !collection) {
55
56
  throw new Error('Failed to get content media session data');
56
57
  }
57
- return Promise.resolve({
58
+ return {
58
59
  config: configuration,
59
60
  token,
60
61
  collectionId: collection
61
- });
62
+ };
62
63
  } catch (error) {
63
64
  logException(error, {
64
65
  location: 'editor-synced-block-provider/fetchMediaToken'
65
66
  });
66
- throw new Error(`Failed to get content media session: ${error}`);
67
+ const errorMsg = fg('platform_synced_block_patch_4') ? error instanceof Error ? error.message : String(error) : String(error);
68
+ throw new Error(`Failed to get content media session: ${errorMsg}`);
67
69
  }
68
70
  };