@atlaskit/editor-synced-block-provider 2.15.5 → 2.16.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 CHANGED
@@ -1,5 +1,22 @@
1
1
  # @atlaskit/editor-synced-block-provider
2
2
 
3
+ ## 2.16.0
4
+
5
+ ### Minor Changes
6
+
7
+ - [`1e626a0fb59a7`](https://bitbucket.org/atlassian/atlassian-frontend-monorepo/commits/1e626a0fb59a7) -
8
+ Can pass in get NCS step version function
9
+
10
+ ## 2.15.6
11
+
12
+ ### Patch Changes
13
+
14
+ - [`d40079fdeef5d`](https://bitbucket.org/atlassian/atlassian-frontend-monorepo/commits/d40079fdeef5d) -
15
+ EDITOR-4044 Fix a race condition in source sync block dirty tracking logic
16
+ - [`267f0abf6b4cf`](https://bitbucket.org/atlassian/atlassian-frontend-monorepo/commits/267f0abf6b4cf) -
17
+ EDITOR-4044 Fix a race condition in reference sync block dirty tracking logic
18
+ - Updated dependencies
19
+
3
20
  ## 2.15.5
4
21
 
5
22
  ### Patch Changes
@@ -177,27 +177,31 @@ var deleteSyncedBlock = exports.deleteSyncedBlock = /*#__PURE__*/function () {
177
177
  }();
178
178
  var updateSyncedBlock = exports.updateSyncedBlock = /*#__PURE__*/function () {
179
179
  var _ref7 = (0, _asyncToGenerator2.default)( /*#__PURE__*/_regenerator.default.mark(function _callee4(_ref6) {
180
- var blockAri, content, response;
180
+ var blockAri, content, stepVersion, requestBody, response;
181
181
  return _regenerator.default.wrap(function _callee4$(_context4) {
182
182
  while (1) switch (_context4.prev = _context4.next) {
183
183
  case 0:
184
- blockAri = _ref6.blockAri, content = _ref6.content;
185
- _context4.next = 3;
184
+ blockAri = _ref6.blockAri, content = _ref6.content, stepVersion = _ref6.stepVersion;
185
+ requestBody = {
186
+ content: content
187
+ };
188
+ if (stepVersion !== undefined) {
189
+ requestBody.stepVersion = stepVersion;
190
+ }
191
+ _context4.next = 5;
186
192
  return (0, _retry.fetchWithRetry)("".concat(BLOCK_SERVICE_API_URL, "/block/").concat(encodeURIComponent(blockAri)), {
187
193
  method: 'PUT',
188
194
  headers: COMMON_HEADERS,
189
- body: JSON.stringify({
190
- content: content
191
- })
195
+ body: JSON.stringify(requestBody)
192
196
  });
193
- case 3:
197
+ case 5:
194
198
  response = _context4.sent;
195
199
  if (response.ok) {
196
- _context4.next = 6;
200
+ _context4.next = 8;
197
201
  break;
198
202
  }
199
203
  throw new BlockError(response.status);
200
- case 6:
204
+ case 8:
201
205
  case "end":
202
206
  return _context4.stop();
203
207
  }
@@ -209,36 +213,40 @@ var updateSyncedBlock = exports.updateSyncedBlock = /*#__PURE__*/function () {
209
213
  }();
210
214
  var createSyncedBlock = exports.createSyncedBlock = /*#__PURE__*/function () {
211
215
  var _ref9 = (0, _asyncToGenerator2.default)( /*#__PURE__*/_regenerator.default.mark(function _callee5(_ref8) {
212
- var blockAri, blockInstanceId, sourceAri, product, content, response;
216
+ var blockAri, blockInstanceId, sourceAri, product, content, stepVersion, requestBody, response;
213
217
  return _regenerator.default.wrap(function _callee5$(_context5) {
214
218
  while (1) switch (_context5.prev = _context5.next) {
215
219
  case 0:
216
- blockAri = _ref8.blockAri, blockInstanceId = _ref8.blockInstanceId, sourceAri = _ref8.sourceAri, product = _ref8.product, content = _ref8.content;
217
- _context5.next = 3;
220
+ blockAri = _ref8.blockAri, blockInstanceId = _ref8.blockInstanceId, sourceAri = _ref8.sourceAri, product = _ref8.product, content = _ref8.content, stepVersion = _ref8.stepVersion;
221
+ requestBody = {
222
+ blockAri: blockAri,
223
+ blockInstanceId: blockInstanceId,
224
+ sourceAri: sourceAri,
225
+ product: product,
226
+ content: content
227
+ };
228
+ if (stepVersion !== undefined) {
229
+ requestBody.stepVersion = stepVersion;
230
+ }
231
+ _context5.next = 5;
218
232
  return (0, _retry.fetchWithRetry)("".concat(BLOCK_SERVICE_API_URL, "/block"), {
219
233
  method: 'POST',
220
234
  headers: COMMON_HEADERS,
221
- body: JSON.stringify({
222
- blockAri: blockAri,
223
- blockInstanceId: blockInstanceId,
224
- sourceAri: sourceAri,
225
- product: product,
226
- content: content
227
- })
235
+ body: JSON.stringify(requestBody)
228
236
  });
229
- case 3:
237
+ case 5:
230
238
  response = _context5.sent;
231
239
  if (response.ok) {
232
- _context5.next = 6;
240
+ _context5.next = 8;
233
241
  break;
234
242
  }
235
243
  throw new BlockError(response.status);
236
- case 6:
237
- _context5.next = 8;
238
- return response.json();
239
244
  case 8:
245
+ _context5.next = 10;
246
+ return response.json();
247
+ case 10:
240
248
  return _context5.abrupt("return", _context5.sent);
241
- case 9:
249
+ case 11:
242
250
  case "end":
243
251
  return _context5.stop();
244
252
  }
@@ -196,11 +196,12 @@ var BlockServiceADFFetchProvider = /*#__PURE__*/function () {
196
196
  * ADFWriteProvider implementation that writes synced block data to Block Service API
197
197
  */
198
198
  var BlockServiceADFWriteProvider = /*#__PURE__*/function () {
199
- function BlockServiceADFWriteProvider(sourceAri, product, sourceDocumentId) {
199
+ function BlockServiceADFWriteProvider(sourceAri, product, sourceDocumentId, getVersion) {
200
200
  (0, _classCallCheck2.default)(this, BlockServiceADFWriteProvider);
201
201
  this.sourceAri = sourceAri;
202
202
  this.product = product;
203
203
  this.sourceDocumentId = sourceDocumentId;
204
+ this.getVersion = getVersion;
204
205
  }
205
206
 
206
207
  // it will first try to update and if it can't (404) then it will try to create
@@ -208,43 +209,45 @@ var BlockServiceADFWriteProvider = /*#__PURE__*/function () {
208
209
  key: "writeData",
209
210
  value: function () {
210
211
  var _writeData = (0, _asyncToGenerator2.default)( /*#__PURE__*/_regenerator.default.mark(function _callee3(data) {
211
- var resourceId, blockAri;
212
+ var resourceId, blockAri, stepVersion;
212
213
  return _regenerator.default.wrap(function _callee3$(_context3) {
213
214
  while (1) switch (_context3.prev = _context3.next) {
214
215
  case 0:
215
216
  resourceId = data.resourceId;
216
217
  blockAri = (0, _ari.generateBlockAri)(this.sourceAri, resourceId, this.product);
217
- _context3.prev = 2;
218
- _context3.next = 5;
218
+ stepVersion = this.getVersion ? this.getVersion() : undefined;
219
+ _context3.prev = 3;
220
+ _context3.next = 6;
219
221
  return (0, _blockService.updateSyncedBlock)({
220
222
  blockAri: blockAri,
221
- content: JSON.stringify(data.content)
223
+ content: JSON.stringify(data.content),
224
+ stepVersion: stepVersion
222
225
  });
223
- case 5:
226
+ case 6:
224
227
  return _context3.abrupt("return", {
225
228
  resourceId: resourceId
226
229
  });
227
- case 8:
228
- _context3.prev = 8;
229
- _context3.t0 = _context3["catch"](2);
230
+ case 9:
231
+ _context3.prev = 9;
232
+ _context3.t0 = _context3["catch"](3);
230
233
  if (!(_context3.t0 instanceof _blockService.BlockError)) {
231
- _context3.next = 12;
234
+ _context3.next = 13;
232
235
  break;
233
236
  }
234
237
  return _context3.abrupt("return", {
235
238
  error: mapBlockError(_context3.t0),
236
239
  resourceId: resourceId
237
240
  });
238
- case 12:
241
+ case 13:
239
242
  return _context3.abrupt("return", {
240
243
  error: (0, _errorHandling.stringifyError)(_context3.t0),
241
244
  resourceId: resourceId
242
245
  });
243
- case 13:
246
+ case 14:
244
247
  case "end":
245
248
  return _context3.stop();
246
249
  }
247
- }, _callee3, this, [[2, 8]]);
250
+ }, _callee3, this, [[3, 9]]);
248
251
  }));
249
252
  function writeData(_x3) {
250
253
  return _writeData.apply(this, arguments);
@@ -255,46 +258,48 @@ var BlockServiceADFWriteProvider = /*#__PURE__*/function () {
255
258
  key: "createData",
256
259
  value: function () {
257
260
  var _createData = (0, _asyncToGenerator2.default)( /*#__PURE__*/_regenerator.default.mark(function _callee4(data) {
258
- var resourceId, blockAri;
261
+ var resourceId, blockAri, stepVersion;
259
262
  return _regenerator.default.wrap(function _callee4$(_context4) {
260
263
  while (1) switch (_context4.prev = _context4.next) {
261
264
  case 0:
262
265
  resourceId = data.resourceId;
263
266
  blockAri = (0, _ari.generateBlockAri)(this.sourceAri, resourceId, this.product);
264
- _context4.prev = 2;
265
- _context4.next = 5;
267
+ stepVersion = this.getVersion ? this.getVersion() : undefined;
268
+ _context4.prev = 3;
269
+ _context4.next = 6;
266
270
  return (0, _blockService.createSyncedBlock)({
267
271
  blockAri: blockAri,
268
272
  blockInstanceId: data.blockInstanceId,
269
273
  sourceAri: this.sourceAri,
270
274
  product: this.product,
271
- content: JSON.stringify(data.content)
275
+ content: JSON.stringify(data.content),
276
+ stepVersion: stepVersion
272
277
  });
273
- case 5:
278
+ case 6:
274
279
  return _context4.abrupt("return", {
275
280
  resourceId: resourceId
276
281
  });
277
- case 8:
278
- _context4.prev = 8;
279
- _context4.t0 = _context4["catch"](2);
282
+ case 9:
283
+ _context4.prev = 9;
284
+ _context4.t0 = _context4["catch"](3);
280
285
  if (!(_context4.t0 instanceof _blockService.BlockError)) {
281
- _context4.next = 12;
286
+ _context4.next = 13;
282
287
  break;
283
288
  }
284
289
  return _context4.abrupt("return", {
285
290
  error: mapBlockError(_context4.t0),
286
291
  resourceId: resourceId
287
292
  });
288
- case 12:
293
+ case 13:
289
294
  return _context4.abrupt("return", {
290
295
  error: (0, _errorHandling.stringifyError)(_context4.t0),
291
296
  resourceId: resourceId
292
297
  });
293
- case 13:
298
+ case 14:
294
299
  case "end":
295
300
  return _context4.stop();
296
301
  }
297
- }, _callee4, this, [[2, 8]]);
302
+ }, _callee4, this, [[3, 9]]);
298
303
  }));
299
304
  function createData(_x4) {
300
305
  return _createData.apply(this, arguments);
@@ -425,16 +430,16 @@ var BlockServiceADFWriteProvider = /*#__PURE__*/function () {
425
430
  /**
426
431
  * Factory function to create both providers with shared configuration
427
432
  */
428
- var createBlockServiceAPIProviders = function createBlockServiceAPIProviders(sourceAri, product, sourceDocumentId) {
433
+ var createBlockServiceAPIProviders = function createBlockServiceAPIProviders(sourceAri, product, sourceDocumentId, getVersion) {
429
434
  var fetchProvider = new BlockServiceADFFetchProvider(sourceAri);
430
- var writeProvider = new BlockServiceADFWriteProvider(sourceAri, product, sourceDocumentId);
435
+ var writeProvider = new BlockServiceADFWriteProvider(sourceAri, product, sourceDocumentId, getVersion);
431
436
  return {
432
437
  fetchProvider: fetchProvider,
433
438
  writeProvider: writeProvider
434
439
  };
435
440
  };
436
- var useMemoizedBlockServiceAPIProviders = exports.useMemoizedBlockServiceAPIProviders = function useMemoizedBlockServiceAPIProviders(sourceAri, product, sourceDocumentId) {
441
+ var useMemoizedBlockServiceAPIProviders = exports.useMemoizedBlockServiceAPIProviders = function useMemoizedBlockServiceAPIProviders(sourceAri, product, sourceDocumentId, getVersion) {
437
442
  return (0, _react.useMemo)(function () {
438
- return createBlockServiceAPIProviders(sourceAri, product, sourceDocumentId);
439
- }, [sourceAri, product, sourceDocumentId]);
443
+ return createBlockServiceAPIProviders(sourceAri, product, sourceDocumentId, getVersion);
444
+ }, [sourceAri, product, sourceDocumentId, getVersion]);
440
445
  };
@@ -574,42 +574,50 @@ var ReferenceSyncBlockStoreManager = exports.ReferenceSyncBlockStoreManager = /*
574
574
  });
575
575
  });
576
576
  if (!(blocks.length === 0)) {
577
- _context4.next = 10;
577
+ _context4.next = 11;
578
578
  break;
579
579
  }
580
+ this.isCacheDirty = false;
580
581
  return _context4.abrupt("return", true);
581
- case 10:
582
- _context4.next = 12;
582
+ case 11:
583
+ // reset isCacheDirty early to prevent race condition
584
+ // There is a race condition where if a user makes changes (create/delete) to a reference sync block
585
+ // on a live page and the reference sync block is being saved while the user
586
+ // is still making changes, the new changes might not be saved if they all happen
587
+ // exactly at a time when the updateReferenceData is being executed asynchronously.
588
+ this.isCacheDirty = false;
589
+ _context4.next = 14;
583
590
  return this.dataProvider.updateReferenceData(blocks);
584
- case 12:
591
+ case 14:
585
592
  updateResult = _context4.sent;
586
593
  if (!updateResult.success) {
587
594
  success = false;
588
595
  (_this$fireAnalyticsEv9 = this.fireAnalyticsEvent) === null || _this$fireAnalyticsEv9 === void 0 || _this$fireAnalyticsEv9.call(this, (0, _errorHandling.updateReferenceErrorPayload)(updateResult.error || 'Failed to update reference synced blocks on the document'));
589
596
  }
590
- _context4.next = 21;
597
+ _context4.next = 23;
591
598
  break;
592
- case 16:
593
- _context4.prev = 16;
599
+ case 18:
600
+ _context4.prev = 18;
594
601
  _context4.t0 = _context4["catch"](3);
595
602
  success = false;
596
603
  (0, _monitoring.logException)(_context4.t0, {
597
604
  location: 'editor-synced-block-provider/referenceSyncBlockStoreManager'
598
605
  });
599
606
  (_this$fireAnalyticsEv0 = this.fireAnalyticsEvent) === null || _this$fireAnalyticsEv0 === void 0 || _this$fireAnalyticsEv0.call(this, (0, _errorHandling.updateReferenceErrorPayload)(_context4.t0.message));
600
- case 21:
601
- _context4.prev = 21;
602
- if (success) {
603
- this.isCacheDirty = false;
607
+ case 23:
608
+ _context4.prev = 23;
609
+ if (!success) {
610
+ // set isCacheDirty back to true for cases where it failed to update the reference synced blocks on the BE
611
+ this.isCacheDirty = true;
604
612
  }
605
- return _context4.finish(21);
606
- case 24:
613
+ return _context4.finish(23);
614
+ case 26:
607
615
  return _context4.abrupt("return", success);
608
- case 25:
616
+ case 27:
609
617
  case "end":
610
618
  return _context4.stop();
611
619
  }
612
- }, _callee3, this, [[3, 16, 21, 24]]);
620
+ }, _callee3, this, [[3, 18, 23, 26]]);
613
621
  }));
614
622
  function flush() {
615
623
  return _flush.apply(this, arguments);
@@ -109,6 +109,13 @@ var SourceSyncBlockStoreManager = exports.SourceSyncBlockStoreManager = /*#__PUR
109
109
  }
110
110
  });
111
111
  bodiedSyncBlockData.push(syncBlockData);
112
+
113
+ // reset isDirty early to prevent race condition
114
+ // There is a race condition where if a user makes changes to a source sync block
115
+ // on a live page and the source sync block is being saved while the user
116
+ // is still making changes, the new changes might not be saved if they all happen
117
+ // exactly at a time when the writeNodesData is being executed asynchronously.
118
+ syncBlockData.isDirty = false;
112
119
  }
113
120
  });
114
121
  if (!(bodiedSyncBlockNodes.length === 0)) {
@@ -122,11 +129,11 @@ var SourceSyncBlockStoreManager = exports.SourceSyncBlockStoreManager = /*#__PUR
122
129
  case 10:
123
130
  writeResults = _context.sent;
124
131
  writeResults.forEach(function (result) {
125
- // set isDirty to false on write success and unrecoverable errors like not found
126
- if (result.resourceId && (result.error === _types.SyncBlockError.NotFound || !result.error)) {
132
+ // set isDirty to true for cases where it failed to save the sync block to the BE
133
+ if (result.resourceId && result.error && result.error !== _types.SyncBlockError.NotFound) {
127
134
  var cachedData = _this2.syncBlockCache.get(result.resourceId);
128
135
  if (cachedData) {
129
- cachedData.isDirty = false;
136
+ cachedData.isDirty = true;
130
137
  }
131
138
  }
132
139
  });
@@ -92,14 +92,19 @@ export const deleteSyncedBlock = async ({
92
92
  };
93
93
  export const updateSyncedBlock = async ({
94
94
  blockAri,
95
- content
95
+ content,
96
+ stepVersion
96
97
  }) => {
98
+ const requestBody = {
99
+ content
100
+ };
101
+ if (stepVersion !== undefined) {
102
+ requestBody.stepVersion = stepVersion;
103
+ }
97
104
  const response = await fetchWithRetry(`${BLOCK_SERVICE_API_URL}/block/${encodeURIComponent(blockAri)}`, {
98
105
  method: 'PUT',
99
106
  headers: COMMON_HEADERS,
100
- body: JSON.stringify({
101
- content
102
- })
107
+ body: JSON.stringify(requestBody)
103
108
  });
104
109
  if (!response.ok) {
105
110
  throw new BlockError(response.status);
@@ -110,18 +115,23 @@ export const createSyncedBlock = async ({
110
115
  blockInstanceId,
111
116
  sourceAri,
112
117
  product,
113
- content
118
+ content,
119
+ stepVersion
114
120
  }) => {
121
+ const requestBody = {
122
+ blockAri,
123
+ blockInstanceId,
124
+ sourceAri,
125
+ product,
126
+ content
127
+ };
128
+ if (stepVersion !== undefined) {
129
+ requestBody.stepVersion = stepVersion;
130
+ }
115
131
  const response = await fetchWithRetry(`${BLOCK_SERVICE_API_URL}/block`, {
116
132
  method: 'POST',
117
133
  headers: COMMON_HEADERS,
118
- body: JSON.stringify({
119
- blockAri,
120
- blockInstanceId,
121
- sourceAri,
122
- product,
123
- content
124
- })
134
+ body: JSON.stringify(requestBody)
125
135
  });
126
136
  if (!response.ok) {
127
137
  throw new BlockError(response.status);
@@ -134,10 +134,11 @@ class BlockServiceADFFetchProvider {
134
134
  * ADFWriteProvider implementation that writes synced block data to Block Service API
135
135
  */
136
136
  class BlockServiceADFWriteProvider {
137
- constructor(sourceAri, product, sourceDocumentId) {
137
+ constructor(sourceAri, product, sourceDocumentId, getVersion) {
138
138
  this.sourceAri = sourceAri;
139
139
  this.product = product;
140
140
  this.sourceDocumentId = sourceDocumentId;
141
+ this.getVersion = getVersion;
141
142
  }
142
143
 
143
144
  // it will first try to update and if it can't (404) then it will try to create
@@ -146,11 +147,13 @@ class BlockServiceADFWriteProvider {
146
147
  resourceId
147
148
  } = data;
148
149
  const blockAri = generateBlockAri(this.sourceAri, resourceId, this.product);
150
+ const stepVersion = this.getVersion ? this.getVersion() : undefined;
149
151
  try {
150
152
  // Try update existing block's content
151
153
  await updateSyncedBlock({
152
154
  blockAri,
153
- content: JSON.stringify(data.content)
155
+ content: JSON.stringify(data.content),
156
+ stepVersion
154
157
  });
155
158
  return {
156
159
  resourceId
@@ -173,13 +176,15 @@ class BlockServiceADFWriteProvider {
173
176
  resourceId
174
177
  } = data;
175
178
  const blockAri = generateBlockAri(this.sourceAri, resourceId, this.product);
179
+ const stepVersion = this.getVersion ? this.getVersion() : undefined;
176
180
  try {
177
181
  await createSyncedBlock({
178
182
  blockAri,
179
183
  blockInstanceId: data.blockInstanceId,
180
184
  sourceAri: this.sourceAri,
181
185
  product: this.product,
182
- content: JSON.stringify(data.content)
186
+ content: JSON.stringify(data.content),
187
+ stepVersion
183
188
  });
184
189
  return {
185
190
  resourceId
@@ -272,14 +277,14 @@ class BlockServiceADFWriteProvider {
272
277
  /**
273
278
  * Factory function to create both providers with shared configuration
274
279
  */
275
- const createBlockServiceAPIProviders = (sourceAri, product, sourceDocumentId) => {
280
+ const createBlockServiceAPIProviders = (sourceAri, product, sourceDocumentId, getVersion) => {
276
281
  const fetchProvider = new BlockServiceADFFetchProvider(sourceAri);
277
- const writeProvider = new BlockServiceADFWriteProvider(sourceAri, product, sourceDocumentId);
282
+ const writeProvider = new BlockServiceADFWriteProvider(sourceAri, product, sourceDocumentId, getVersion);
278
283
  return {
279
284
  fetchProvider,
280
285
  writeProvider
281
286
  };
282
287
  };
283
- export const useMemoizedBlockServiceAPIProviders = (sourceAri, product, sourceDocumentId) => {
284
- return useMemo(() => createBlockServiceAPIProviders(sourceAri, product, sourceDocumentId), [sourceAri, product, sourceDocumentId]);
288
+ export const useMemoizedBlockServiceAPIProviders = (sourceAri, product, sourceDocumentId, getVersion) => {
289
+ return useMemo(() => createBlockServiceAPIProviders(sourceAri, product, sourceDocumentId, getVersion), [sourceAri, product, sourceDocumentId, getVersion]);
285
290
  };
@@ -441,8 +441,16 @@ export class ReferenceSyncBlockStoreManager {
441
441
  });
442
442
  });
443
443
  if (blocks.length === 0) {
444
+ this.isCacheDirty = false;
444
445
  return true;
445
446
  }
447
+
448
+ // reset isCacheDirty early to prevent race condition
449
+ // There is a race condition where if a user makes changes (create/delete) to a reference sync block
450
+ // on a live page and the reference sync block is being saved while the user
451
+ // is still making changes, the new changes might not be saved if they all happen
452
+ // exactly at a time when the updateReferenceData is being executed asynchronously.
453
+ this.isCacheDirty = false;
446
454
  const updateResult = await this.dataProvider.updateReferenceData(blocks);
447
455
  if (!updateResult.success) {
448
456
  var _this$fireAnalyticsEv10;
@@ -457,8 +465,9 @@ export class ReferenceSyncBlockStoreManager {
457
465
  });
458
466
  (_this$fireAnalyticsEv11 = this.fireAnalyticsEvent) === null || _this$fireAnalyticsEv11 === void 0 ? void 0 : _this$fireAnalyticsEv11.call(this, updateReferenceErrorPayload(error.message));
459
467
  } finally {
460
- if (success) {
461
- this.isCacheDirty = false;
468
+ if (!success) {
469
+ // set isCacheDirty back to true for cases where it failed to update the reference synced blocks on the BE
470
+ this.isCacheDirty = true;
462
471
  }
463
472
  }
464
473
  return success;
@@ -82,6 +82,13 @@ export class SourceSyncBlockStoreManager {
82
82
  }
83
83
  });
84
84
  bodiedSyncBlockData.push(syncBlockData);
85
+
86
+ // reset isDirty early to prevent race condition
87
+ // There is a race condition where if a user makes changes to a source sync block
88
+ // on a live page and the source sync block is being saved while the user
89
+ // is still making changes, the new changes might not be saved if they all happen
90
+ // exactly at a time when the writeNodesData is being executed asynchronously.
91
+ syncBlockData.isDirty = false;
85
92
  }
86
93
  });
87
94
  if (bodiedSyncBlockNodes.length === 0) {
@@ -89,11 +96,11 @@ export class SourceSyncBlockStoreManager {
89
96
  }
90
97
  const writeResults = await this.dataProvider.writeNodesData(bodiedSyncBlockNodes, bodiedSyncBlockData);
91
98
  writeResults.forEach(result => {
92
- // set isDirty to false on write success and unrecoverable errors like not found
93
- if (result.resourceId && (result.error === SyncBlockError.NotFound || !result.error)) {
99
+ // set isDirty to true for cases where it failed to save the sync block to the BE
100
+ if (result.resourceId && result.error && result.error !== SyncBlockError.NotFound) {
94
101
  const cachedData = this.syncBlockCache.get(result.resourceId);
95
102
  if (cachedData) {
96
- cachedData.isDirty = false;
103
+ cachedData.isDirty = true;
97
104
  }
98
105
  }
99
106
  });
@@ -170,27 +170,31 @@ export var deleteSyncedBlock = /*#__PURE__*/function () {
170
170
  }();
171
171
  export var updateSyncedBlock = /*#__PURE__*/function () {
172
172
  var _ref7 = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee4(_ref6) {
173
- var blockAri, content, response;
173
+ var blockAri, content, stepVersion, requestBody, response;
174
174
  return _regeneratorRuntime.wrap(function _callee4$(_context4) {
175
175
  while (1) switch (_context4.prev = _context4.next) {
176
176
  case 0:
177
- blockAri = _ref6.blockAri, content = _ref6.content;
178
- _context4.next = 3;
177
+ blockAri = _ref6.blockAri, content = _ref6.content, stepVersion = _ref6.stepVersion;
178
+ requestBody = {
179
+ content: content
180
+ };
181
+ if (stepVersion !== undefined) {
182
+ requestBody.stepVersion = stepVersion;
183
+ }
184
+ _context4.next = 5;
179
185
  return fetchWithRetry("".concat(BLOCK_SERVICE_API_URL, "/block/").concat(encodeURIComponent(blockAri)), {
180
186
  method: 'PUT',
181
187
  headers: COMMON_HEADERS,
182
- body: JSON.stringify({
183
- content: content
184
- })
188
+ body: JSON.stringify(requestBody)
185
189
  });
186
- case 3:
190
+ case 5:
187
191
  response = _context4.sent;
188
192
  if (response.ok) {
189
- _context4.next = 6;
193
+ _context4.next = 8;
190
194
  break;
191
195
  }
192
196
  throw new BlockError(response.status);
193
- case 6:
197
+ case 8:
194
198
  case "end":
195
199
  return _context4.stop();
196
200
  }
@@ -202,36 +206,40 @@ export var updateSyncedBlock = /*#__PURE__*/function () {
202
206
  }();
203
207
  export var createSyncedBlock = /*#__PURE__*/function () {
204
208
  var _ref9 = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee5(_ref8) {
205
- var blockAri, blockInstanceId, sourceAri, product, content, response;
209
+ var blockAri, blockInstanceId, sourceAri, product, content, stepVersion, requestBody, response;
206
210
  return _regeneratorRuntime.wrap(function _callee5$(_context5) {
207
211
  while (1) switch (_context5.prev = _context5.next) {
208
212
  case 0:
209
- blockAri = _ref8.blockAri, blockInstanceId = _ref8.blockInstanceId, sourceAri = _ref8.sourceAri, product = _ref8.product, content = _ref8.content;
210
- _context5.next = 3;
213
+ blockAri = _ref8.blockAri, blockInstanceId = _ref8.blockInstanceId, sourceAri = _ref8.sourceAri, product = _ref8.product, content = _ref8.content, stepVersion = _ref8.stepVersion;
214
+ requestBody = {
215
+ blockAri: blockAri,
216
+ blockInstanceId: blockInstanceId,
217
+ sourceAri: sourceAri,
218
+ product: product,
219
+ content: content
220
+ };
221
+ if (stepVersion !== undefined) {
222
+ requestBody.stepVersion = stepVersion;
223
+ }
224
+ _context5.next = 5;
211
225
  return fetchWithRetry("".concat(BLOCK_SERVICE_API_URL, "/block"), {
212
226
  method: 'POST',
213
227
  headers: COMMON_HEADERS,
214
- body: JSON.stringify({
215
- blockAri: blockAri,
216
- blockInstanceId: blockInstanceId,
217
- sourceAri: sourceAri,
218
- product: product,
219
- content: content
220
- })
228
+ body: JSON.stringify(requestBody)
221
229
  });
222
- case 3:
230
+ case 5:
223
231
  response = _context5.sent;
224
232
  if (response.ok) {
225
- _context5.next = 6;
233
+ _context5.next = 8;
226
234
  break;
227
235
  }
228
236
  throw new BlockError(response.status);
229
- case 6:
230
- _context5.next = 8;
231
- return response.json();
232
237
  case 8:
238
+ _context5.next = 10;
239
+ return response.json();
240
+ case 10:
233
241
  return _context5.abrupt("return", _context5.sent);
234
- case 9:
242
+ case 11:
235
243
  case "end":
236
244
  return _context5.stop();
237
245
  }
@@ -188,11 +188,12 @@ var BlockServiceADFFetchProvider = /*#__PURE__*/function () {
188
188
  * ADFWriteProvider implementation that writes synced block data to Block Service API
189
189
  */
190
190
  var BlockServiceADFWriteProvider = /*#__PURE__*/function () {
191
- function BlockServiceADFWriteProvider(sourceAri, product, sourceDocumentId) {
191
+ function BlockServiceADFWriteProvider(sourceAri, product, sourceDocumentId, getVersion) {
192
192
  _classCallCheck(this, BlockServiceADFWriteProvider);
193
193
  this.sourceAri = sourceAri;
194
194
  this.product = product;
195
195
  this.sourceDocumentId = sourceDocumentId;
196
+ this.getVersion = getVersion;
196
197
  }
197
198
 
198
199
  // it will first try to update and if it can't (404) then it will try to create
@@ -200,43 +201,45 @@ var BlockServiceADFWriteProvider = /*#__PURE__*/function () {
200
201
  key: "writeData",
201
202
  value: function () {
202
203
  var _writeData = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee3(data) {
203
- var resourceId, blockAri;
204
+ var resourceId, blockAri, stepVersion;
204
205
  return _regeneratorRuntime.wrap(function _callee3$(_context3) {
205
206
  while (1) switch (_context3.prev = _context3.next) {
206
207
  case 0:
207
208
  resourceId = data.resourceId;
208
209
  blockAri = generateBlockAri(this.sourceAri, resourceId, this.product);
209
- _context3.prev = 2;
210
- _context3.next = 5;
210
+ stepVersion = this.getVersion ? this.getVersion() : undefined;
211
+ _context3.prev = 3;
212
+ _context3.next = 6;
211
213
  return updateSyncedBlock({
212
214
  blockAri: blockAri,
213
- content: JSON.stringify(data.content)
215
+ content: JSON.stringify(data.content),
216
+ stepVersion: stepVersion
214
217
  });
215
- case 5:
218
+ case 6:
216
219
  return _context3.abrupt("return", {
217
220
  resourceId: resourceId
218
221
  });
219
- case 8:
220
- _context3.prev = 8;
221
- _context3.t0 = _context3["catch"](2);
222
+ case 9:
223
+ _context3.prev = 9;
224
+ _context3.t0 = _context3["catch"](3);
222
225
  if (!(_context3.t0 instanceof BlockError)) {
223
- _context3.next = 12;
226
+ _context3.next = 13;
224
227
  break;
225
228
  }
226
229
  return _context3.abrupt("return", {
227
230
  error: mapBlockError(_context3.t0),
228
231
  resourceId: resourceId
229
232
  });
230
- case 12:
233
+ case 13:
231
234
  return _context3.abrupt("return", {
232
235
  error: stringifyError(_context3.t0),
233
236
  resourceId: resourceId
234
237
  });
235
- case 13:
238
+ case 14:
236
239
  case "end":
237
240
  return _context3.stop();
238
241
  }
239
- }, _callee3, this, [[2, 8]]);
242
+ }, _callee3, this, [[3, 9]]);
240
243
  }));
241
244
  function writeData(_x3) {
242
245
  return _writeData.apply(this, arguments);
@@ -247,46 +250,48 @@ var BlockServiceADFWriteProvider = /*#__PURE__*/function () {
247
250
  key: "createData",
248
251
  value: function () {
249
252
  var _createData = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee4(data) {
250
- var resourceId, blockAri;
253
+ var resourceId, blockAri, stepVersion;
251
254
  return _regeneratorRuntime.wrap(function _callee4$(_context4) {
252
255
  while (1) switch (_context4.prev = _context4.next) {
253
256
  case 0:
254
257
  resourceId = data.resourceId;
255
258
  blockAri = generateBlockAri(this.sourceAri, resourceId, this.product);
256
- _context4.prev = 2;
257
- _context4.next = 5;
259
+ stepVersion = this.getVersion ? this.getVersion() : undefined;
260
+ _context4.prev = 3;
261
+ _context4.next = 6;
258
262
  return createSyncedBlock({
259
263
  blockAri: blockAri,
260
264
  blockInstanceId: data.blockInstanceId,
261
265
  sourceAri: this.sourceAri,
262
266
  product: this.product,
263
- content: JSON.stringify(data.content)
267
+ content: JSON.stringify(data.content),
268
+ stepVersion: stepVersion
264
269
  });
265
- case 5:
270
+ case 6:
266
271
  return _context4.abrupt("return", {
267
272
  resourceId: resourceId
268
273
  });
269
- case 8:
270
- _context4.prev = 8;
271
- _context4.t0 = _context4["catch"](2);
274
+ case 9:
275
+ _context4.prev = 9;
276
+ _context4.t0 = _context4["catch"](3);
272
277
  if (!(_context4.t0 instanceof BlockError)) {
273
- _context4.next = 12;
278
+ _context4.next = 13;
274
279
  break;
275
280
  }
276
281
  return _context4.abrupt("return", {
277
282
  error: mapBlockError(_context4.t0),
278
283
  resourceId: resourceId
279
284
  });
280
- case 12:
285
+ case 13:
281
286
  return _context4.abrupt("return", {
282
287
  error: stringifyError(_context4.t0),
283
288
  resourceId: resourceId
284
289
  });
285
- case 13:
290
+ case 14:
286
291
  case "end":
287
292
  return _context4.stop();
288
293
  }
289
- }, _callee4, this, [[2, 8]]);
294
+ }, _callee4, this, [[3, 9]]);
290
295
  }));
291
296
  function createData(_x4) {
292
297
  return _createData.apply(this, arguments);
@@ -417,16 +422,16 @@ var BlockServiceADFWriteProvider = /*#__PURE__*/function () {
417
422
  /**
418
423
  * Factory function to create both providers with shared configuration
419
424
  */
420
- var createBlockServiceAPIProviders = function createBlockServiceAPIProviders(sourceAri, product, sourceDocumentId) {
425
+ var createBlockServiceAPIProviders = function createBlockServiceAPIProviders(sourceAri, product, sourceDocumentId, getVersion) {
421
426
  var fetchProvider = new BlockServiceADFFetchProvider(sourceAri);
422
- var writeProvider = new BlockServiceADFWriteProvider(sourceAri, product, sourceDocumentId);
427
+ var writeProvider = new BlockServiceADFWriteProvider(sourceAri, product, sourceDocumentId, getVersion);
423
428
  return {
424
429
  fetchProvider: fetchProvider,
425
430
  writeProvider: writeProvider
426
431
  };
427
432
  };
428
- export var useMemoizedBlockServiceAPIProviders = function useMemoizedBlockServiceAPIProviders(sourceAri, product, sourceDocumentId) {
433
+ export var useMemoizedBlockServiceAPIProviders = function useMemoizedBlockServiceAPIProviders(sourceAri, product, sourceDocumentId, getVersion) {
429
434
  return useMemo(function () {
430
- return createBlockServiceAPIProviders(sourceAri, product, sourceDocumentId);
431
- }, [sourceAri, product, sourceDocumentId]);
435
+ return createBlockServiceAPIProviders(sourceAri, product, sourceDocumentId, getVersion);
436
+ }, [sourceAri, product, sourceDocumentId, getVersion]);
432
437
  };
@@ -568,42 +568,50 @@ export var ReferenceSyncBlockStoreManager = /*#__PURE__*/function () {
568
568
  });
569
569
  });
570
570
  if (!(blocks.length === 0)) {
571
- _context4.next = 10;
571
+ _context4.next = 11;
572
572
  break;
573
573
  }
574
+ this.isCacheDirty = false;
574
575
  return _context4.abrupt("return", true);
575
- case 10:
576
- _context4.next = 12;
576
+ case 11:
577
+ // reset isCacheDirty early to prevent race condition
578
+ // There is a race condition where if a user makes changes (create/delete) to a reference sync block
579
+ // on a live page and the reference sync block is being saved while the user
580
+ // is still making changes, the new changes might not be saved if they all happen
581
+ // exactly at a time when the updateReferenceData is being executed asynchronously.
582
+ this.isCacheDirty = false;
583
+ _context4.next = 14;
577
584
  return this.dataProvider.updateReferenceData(blocks);
578
- case 12:
585
+ case 14:
579
586
  updateResult = _context4.sent;
580
587
  if (!updateResult.success) {
581
588
  success = false;
582
589
  (_this$fireAnalyticsEv9 = this.fireAnalyticsEvent) === null || _this$fireAnalyticsEv9 === void 0 || _this$fireAnalyticsEv9.call(this, updateReferenceErrorPayload(updateResult.error || 'Failed to update reference synced blocks on the document'));
583
590
  }
584
- _context4.next = 21;
591
+ _context4.next = 23;
585
592
  break;
586
- case 16:
587
- _context4.prev = 16;
593
+ case 18:
594
+ _context4.prev = 18;
588
595
  _context4.t0 = _context4["catch"](3);
589
596
  success = false;
590
597
  logException(_context4.t0, {
591
598
  location: 'editor-synced-block-provider/referenceSyncBlockStoreManager'
592
599
  });
593
600
  (_this$fireAnalyticsEv0 = this.fireAnalyticsEvent) === null || _this$fireAnalyticsEv0 === void 0 || _this$fireAnalyticsEv0.call(this, updateReferenceErrorPayload(_context4.t0.message));
594
- case 21:
595
- _context4.prev = 21;
596
- if (success) {
597
- this.isCacheDirty = false;
601
+ case 23:
602
+ _context4.prev = 23;
603
+ if (!success) {
604
+ // set isCacheDirty back to true for cases where it failed to update the reference synced blocks on the BE
605
+ this.isCacheDirty = true;
598
606
  }
599
- return _context4.finish(21);
600
- case 24:
607
+ return _context4.finish(23);
608
+ case 26:
601
609
  return _context4.abrupt("return", success);
602
- case 25:
610
+ case 27:
603
611
  case "end":
604
612
  return _context4.stop();
605
613
  }
606
- }, _callee3, this, [[3, 16, 21, 24]]);
614
+ }, _callee3, this, [[3, 18, 23, 26]]);
607
615
  }));
608
616
  function flush() {
609
617
  return _flush.apply(this, arguments);
@@ -103,6 +103,13 @@ export var SourceSyncBlockStoreManager = /*#__PURE__*/function () {
103
103
  }
104
104
  });
105
105
  bodiedSyncBlockData.push(syncBlockData);
106
+
107
+ // reset isDirty early to prevent race condition
108
+ // There is a race condition where if a user makes changes to a source sync block
109
+ // on a live page and the source sync block is being saved while the user
110
+ // is still making changes, the new changes might not be saved if they all happen
111
+ // exactly at a time when the writeNodesData is being executed asynchronously.
112
+ syncBlockData.isDirty = false;
106
113
  }
107
114
  });
108
115
  if (!(bodiedSyncBlockNodes.length === 0)) {
@@ -116,11 +123,11 @@ export var SourceSyncBlockStoreManager = /*#__PURE__*/function () {
116
123
  case 10:
117
124
  writeResults = _context.sent;
118
125
  writeResults.forEach(function (result) {
119
- // set isDirty to false on write success and unrecoverable errors like not found
120
- if (result.resourceId && (result.error === SyncBlockError.NotFound || !result.error)) {
126
+ // set isDirty to true for cases where it failed to save the sync block to the BE
127
+ if (result.resourceId && result.error && result.error !== SyncBlockError.NotFound) {
121
128
  var cachedData = _this2.syncBlockCache.get(result.resourceId);
122
129
  if (cachedData) {
123
- cachedData.isDirty = false;
130
+ cachedData.isDirty = true;
124
131
  }
125
132
  }
126
133
  });
@@ -72,6 +72,7 @@ export type DeleteSyncedBlockRequest = {
72
72
  export type UpdateSyncedBlockRequest = {
73
73
  blockAri: string;
74
74
  content: string;
75
+ stepVersion?: number;
75
76
  };
76
77
  export type CreateSyncedBlockRequest = {
77
78
  blockAri: string;
@@ -79,6 +80,7 @@ export type CreateSyncedBlockRequest = {
79
80
  content: string;
80
81
  product: SyncBlockProduct;
81
82
  sourceAri: string;
83
+ stepVersion?: number;
82
84
  };
83
85
  type ReferenceSyncedBlockIDs = {
84
86
  blockAri: string;
@@ -95,7 +97,7 @@ export declare class BlockError extends Error {
95
97
  }
96
98
  export declare const getSyncedBlockContent: ({ blockAri, }: GetSyncedBlockContentRequest) => Promise<BlockContentResponse>;
97
99
  export declare const deleteSyncedBlock: ({ blockAri }: DeleteSyncedBlockRequest) => Promise<void>;
98
- export declare const updateSyncedBlock: ({ blockAri, content, }: UpdateSyncedBlockRequest) => Promise<void>;
99
- export declare const createSyncedBlock: ({ blockAri, blockInstanceId, sourceAri, product, content, }: CreateSyncedBlockRequest) => Promise<BlockContentResponse>;
100
+ export declare const updateSyncedBlock: ({ blockAri, content, stepVersion, }: UpdateSyncedBlockRequest) => Promise<void>;
101
+ export declare const createSyncedBlock: ({ blockAri, blockInstanceId, sourceAri, product, content, stepVersion, }: CreateSyncedBlockRequest) => Promise<BlockContentResponse>;
100
102
  export declare const updateReferenceSyncedBlockOnDocument: ({ documentAri, blocks, noContent, }: UpdateReferenceSyncedBlockOnDocumentRequest) => Promise<ReferenceSyncedBlockResponse | void>;
101
103
  export {};
@@ -15,8 +15,9 @@ declare class BlockServiceADFFetchProvider implements ADFFetchProvider {
15
15
  declare class BlockServiceADFWriteProvider implements ADFWriteProvider {
16
16
  private sourceAri;
17
17
  private sourceDocumentId;
18
+ private getVersion?;
18
19
  product: SyncBlockProduct;
19
- constructor(sourceAri: string, product: SyncBlockProduct, sourceDocumentId: string);
20
+ constructor(sourceAri: string, product: SyncBlockProduct, sourceDocumentId: string, getVersion?: () => number | undefined);
20
21
  writeData(data: SyncBlockData): Promise<WriteSyncBlockResult>;
21
22
  createData(data: SyncBlockData): Promise<WriteSyncBlockResult>;
22
23
  deleteData(resourceId: string): Promise<DeleteSyncBlockResult>;
@@ -24,7 +25,7 @@ declare class BlockServiceADFWriteProvider implements ADFWriteProvider {
24
25
  generateResourceId(): ResourceId;
25
26
  updateReferenceData(blocks: SyncBlockAttrs[], noContent?: boolean): Promise<UpdateReferenceSyncBlockResult>;
26
27
  }
27
- export declare const useMemoizedBlockServiceAPIProviders: (sourceAri: string, product: SyncBlockProduct, sourceDocumentId: string) => {
28
+ export declare const useMemoizedBlockServiceAPIProviders: (sourceAri: string, product: SyncBlockProduct, sourceDocumentId: string, getVersion?: () => number | undefined) => {
28
29
  fetchProvider: BlockServiceADFFetchProvider;
29
30
  writeProvider: BlockServiceADFWriteProvider;
30
31
  };
@@ -72,6 +72,7 @@ export type DeleteSyncedBlockRequest = {
72
72
  export type UpdateSyncedBlockRequest = {
73
73
  blockAri: string;
74
74
  content: string;
75
+ stepVersion?: number;
75
76
  };
76
77
  export type CreateSyncedBlockRequest = {
77
78
  blockAri: string;
@@ -79,6 +80,7 @@ export type CreateSyncedBlockRequest = {
79
80
  content: string;
80
81
  product: SyncBlockProduct;
81
82
  sourceAri: string;
83
+ stepVersion?: number;
82
84
  };
83
85
  type ReferenceSyncedBlockIDs = {
84
86
  blockAri: string;
@@ -95,7 +97,7 @@ export declare class BlockError extends Error {
95
97
  }
96
98
  export declare const getSyncedBlockContent: ({ blockAri, }: GetSyncedBlockContentRequest) => Promise<BlockContentResponse>;
97
99
  export declare const deleteSyncedBlock: ({ blockAri }: DeleteSyncedBlockRequest) => Promise<void>;
98
- export declare const updateSyncedBlock: ({ blockAri, content, }: UpdateSyncedBlockRequest) => Promise<void>;
99
- export declare const createSyncedBlock: ({ blockAri, blockInstanceId, sourceAri, product, content, }: CreateSyncedBlockRequest) => Promise<BlockContentResponse>;
100
+ export declare const updateSyncedBlock: ({ blockAri, content, stepVersion, }: UpdateSyncedBlockRequest) => Promise<void>;
101
+ export declare const createSyncedBlock: ({ blockAri, blockInstanceId, sourceAri, product, content, stepVersion, }: CreateSyncedBlockRequest) => Promise<BlockContentResponse>;
100
102
  export declare const updateReferenceSyncedBlockOnDocument: ({ documentAri, blocks, noContent, }: UpdateReferenceSyncedBlockOnDocumentRequest) => Promise<ReferenceSyncedBlockResponse | void>;
101
103
  export {};
@@ -15,8 +15,9 @@ declare class BlockServiceADFFetchProvider implements ADFFetchProvider {
15
15
  declare class BlockServiceADFWriteProvider implements ADFWriteProvider {
16
16
  private sourceAri;
17
17
  private sourceDocumentId;
18
+ private getVersion?;
18
19
  product: SyncBlockProduct;
19
- constructor(sourceAri: string, product: SyncBlockProduct, sourceDocumentId: string);
20
+ constructor(sourceAri: string, product: SyncBlockProduct, sourceDocumentId: string, getVersion?: () => number | undefined);
20
21
  writeData(data: SyncBlockData): Promise<WriteSyncBlockResult>;
21
22
  createData(data: SyncBlockData): Promise<WriteSyncBlockResult>;
22
23
  deleteData(resourceId: string): Promise<DeleteSyncBlockResult>;
@@ -24,7 +25,7 @@ declare class BlockServiceADFWriteProvider implements ADFWriteProvider {
24
25
  generateResourceId(): ResourceId;
25
26
  updateReferenceData(blocks: SyncBlockAttrs[], noContent?: boolean): Promise<UpdateReferenceSyncBlockResult>;
26
27
  }
27
- export declare const useMemoizedBlockServiceAPIProviders: (sourceAri: string, product: SyncBlockProduct, sourceDocumentId: string) => {
28
+ export declare const useMemoizedBlockServiceAPIProviders: (sourceAri: string, product: SyncBlockProduct, sourceDocumentId: string, getVersion?: () => number | undefined) => {
28
29
  fetchProvider: BlockServiceADFFetchProvider;
29
30
  writeProvider: BlockServiceADFWriteProvider;
30
31
  };
package/package.json CHANGED
@@ -33,7 +33,7 @@
33
33
  "uuid": "^3.1.0"
34
34
  },
35
35
  "peerDependencies": {
36
- "@atlaskit/editor-common": "^110.46.0",
36
+ "@atlaskit/editor-common": "^110.49.0",
37
37
  "react": "^18.2.0"
38
38
  },
39
39
  "devDependencies": {
@@ -76,7 +76,7 @@
76
76
  }
77
77
  },
78
78
  "name": "@atlaskit/editor-synced-block-provider",
79
- "version": "2.15.5",
79
+ "version": "2.16.0",
80
80
  "description": "Synced Block Provider for @atlaskit/editor-plugin-synced-block",
81
81
  "author": "Atlassian Pty Ltd",
82
82
  "license": "Apache-2.0",