@atlaskit/node-data-provider 7.2.2 → 7.4.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,28 @@
1
1
  # @atlaskit/node-data-provider
2
2
 
3
+ ## 7.4.0
4
+
5
+ ### Minor Changes
6
+
7
+ - [`fdba2e94783b7`](https://bitbucket.org/atlassian/atlassian-frontend-monorepo/commits/fdba2e94783b7) -
8
+ [https://product-fabric.atlassian.net/browse/ED-29638](ED-29638) - fix editor NodeDataProvider
9
+ network requests deduplication
10
+
11
+ ### Patch Changes
12
+
13
+ - Updated dependencies
14
+
15
+ ## 7.3.0
16
+
17
+ ### Minor Changes
18
+
19
+ - [`5319dac3f8740`](https://bitbucket.org/atlassian/atlassian-frontend-monorepo/commits/5319dac3f8740) -
20
+ Do not re-fetch emoji on a client after successful ssr
21
+
22
+ ### Patch Changes
23
+
24
+ - Updated dependencies
25
+
3
26
  ## 7.2.2
4
27
 
5
28
  ### Patch Changes
@@ -6,7 +6,6 @@ Object.defineProperty(exports, "__esModule", {
6
6
  });
7
7
  exports.NodeDataProvider = void 0;
8
8
  var _regenerator = _interopRequireDefault(require("@babel/runtime/regenerator"));
9
- var _typeof2 = _interopRequireDefault(require("@babel/runtime/helpers/typeof"));
10
9
  var _asyncToGenerator2 = _interopRequireDefault(require("@babel/runtime/helpers/asyncToGenerator"));
11
10
  var _slicedToArray2 = _interopRequireDefault(require("@babel/runtime/helpers/slicedToArray"));
12
11
  var _classCallCheck2 = _interopRequireDefault(require("@babel/runtime/helpers/classCallCheck"));
@@ -31,7 +30,7 @@ var _coreUtils = require("@atlaskit/editor-common/core-utils");
31
30
  * @example
32
31
  * {
33
32
  * 'node-id-1': { source: 'ssr', data: { value: 'some data' } },
34
- * 'node-id-2': { source: 'network', data: Promise.resolve({ value: 'other data' }) }
33
+ * 'node-id-2': { source: 'network', data: { value: 'other data' } }
35
34
  * }
36
35
  */
37
36
  /**
@@ -50,6 +49,7 @@ var NodeDataProvider = exports.NodeDataProvider = /*#__PURE__*/function () {
50
49
  (0, _classCallCheck2.default)(this, NodeDataProvider);
51
50
  this.cacheVersion = 0;
52
51
  this.cache = {};
52
+ this.networkRequestsInFlight = {};
53
53
  }
54
54
 
55
55
  /**
@@ -136,102 +136,114 @@ var NodeDataProvider = exports.NodeDataProvider = /*#__PURE__*/function () {
136
136
  key: "getDataAsync",
137
137
  value: function () {
138
138
  var _getDataAsync = (0, _asyncToGenerator2.default)( /*#__PURE__*/_regenerator.default.mark(function _callee(node, callback) {
139
- var jsonNode, _dataKey, dataFromCache, cacheVersionBeforeRequest, dataPromise, data;
139
+ var jsonNode, dataKey, dataFromCache, cacheVersionBeforeRequest, networkRequestInFlightKey, networkRequestInFlight, data, dataPromise, _data;
140
140
  return _regenerator.default.wrap(function _callee$(_context) {
141
141
  while (1) switch (_context.prev = _context.next) {
142
142
  case 0:
143
- _context.prev = 0;
144
143
  jsonNode = 'toJSON' in node ? node.toJSON() : node;
145
144
  if (this.isNodeSupported(jsonNode)) {
146
- _context.next = 5;
145
+ _context.next = 4;
147
146
  break;
148
147
  }
149
148
  // eslint-disable-next-line no-console
150
149
  console.error("The ".concat(this.constructor.name, " doesn't support Node ").concat(jsonNode.type, "."));
151
150
  return _context.abrupt("return");
152
- case 5:
153
- _dataKey = this.nodeDataKey(jsonNode);
154
- dataFromCache = this.cache[_dataKey];
151
+ case 4:
152
+ dataKey = this.nodeDataKey(jsonNode);
153
+ dataFromCache = this.cache[dataKey];
155
154
  if (!(dataFromCache !== undefined)) {
156
- _context.next = 20;
155
+ _context.next = 10;
157
156
  break;
158
157
  }
159
- if (!isPromise(dataFromCache.data)) {
160
- _context.next = 17;
161
- break;
162
- }
163
- _context.t0 = callback;
164
- _context.next = 12;
165
- return dataFromCache.data;
166
- case 12:
167
- _context.t1 = _context.sent;
168
- _context.t2 = {
169
- data: _context.t1
170
- };
171
- (0, _context.t0)(_context.t2);
172
- _context.next = 18;
173
- break;
174
- case 17:
158
+ // If we have the data in the SSR data, we can use it directly
175
159
  callback({
176
160
  data: dataFromCache.data
177
161
  });
178
- case 18:
179
162
  if (!(0, _coreUtils.isSSR)()) {
180
- _context.next = 20;
163
+ _context.next = 10;
181
164
  break;
182
165
  }
183
166
  return _context.abrupt("return");
184
- case 20:
167
+ case 10:
185
168
  if (!((dataFromCache === null || dataFromCache === void 0 ? void 0 : dataFromCache.source) !== 'network')) {
186
- _context.next = 29;
169
+ _context.next = 42;
187
170
  break;
188
171
  }
189
172
  // Store the current cache version before making the request,
190
173
  // so we can check if the cache has changed while we are waiting for the network response.
191
- cacheVersionBeforeRequest = this.cacheVersion;
174
+ cacheVersionBeforeRequest = this.cacheVersion; // Create a unique key for the in-flight network request
175
+ // based on the cache version and the data key.
176
+ networkRequestInFlightKey = "".concat(cacheVersionBeforeRequest, "-").concat(dataKey); // Check if there is already a network request in flight for this data
177
+ // to avoid duplicate requests.
178
+ networkRequestInFlight = this.networkRequestsInFlight[networkRequestInFlightKey];
179
+ if (!networkRequestInFlight) {
180
+ _context.next = 26;
181
+ break;
182
+ }
183
+ _context.prev = 15;
184
+ _context.next = 18;
185
+ return networkRequestInFlight;
186
+ case 18:
187
+ data = _context.sent;
188
+ callback({
189
+ data: data
190
+ });
191
+ _context.next = 25;
192
+ break;
193
+ case 22:
194
+ _context.prev = 22;
195
+ _context.t0 = _context["catch"](15);
196
+ callback({
197
+ error: _context.t0 instanceof Error ? _context.t0 : new Error(String(_context.t0))
198
+ });
199
+ case 25:
200
+ return _context.abrupt("return");
201
+ case 26:
202
+ _context.prev = 26;
192
203
  dataPromise = this.fetchNodesData([jsonNode]).then(function (_ref3) {
193
204
  var _ref4 = (0, _slicedToArray2.default)(_ref3, 1),
194
205
  value = _ref4[0];
195
206
  return value;
196
- }); // Store the promise in the cache to avoid multiple requests for the same data
197
- this.cache[_dataKey] = {
198
- source: 'network',
199
- data: dataPromise
200
- };
201
- _context.next = 26;
207
+ }); // Store the promise in the in-flight requests map
208
+ this.networkRequestsInFlight[networkRequestInFlightKey] = dataPromise;
209
+ _context.next = 31;
202
210
  return dataPromise;
203
- case 26:
204
- data = _context.sent;
211
+ case 31:
212
+ _data = _context.sent;
205
213
  // We need to call the callback with the data with result even if the cache version has changed,
206
214
  // so all promises that are waiting for the data can resolve.
207
215
  callback({
208
- data: data
216
+ data: _data
209
217
  });
210
218
 
211
219
  // If the cache version has changed, we don't want to use the data from the network
212
220
  // because it could be stale data.
213
221
  if (cacheVersionBeforeRequest === this.cacheVersion) {
214
222
  // Replace promise with the resolved data in the cache
215
- this.cache[_dataKey] = {
223
+ this.cache[dataKey] = {
216
224
  source: 'network',
217
- data: data
225
+ data: _data
218
226
  };
219
227
  }
220
- case 29:
221
- _context.next = 34;
228
+ _context.next = 39;
222
229
  break;
223
- case 31:
224
- _context.prev = 31;
225
- _context.t3 = _context["catch"](0);
230
+ case 36:
231
+ _context.prev = 36;
232
+ _context.t1 = _context["catch"](26);
226
233
  // If an error occurs, we call the callback with the error
227
234
  callback({
228
- error: _context.t3 instanceof Error ? _context.t3 : new Error(String(_context.t3))
235
+ error: _context.t1 instanceof Error ? _context.t1 : new Error(String(_context.t1))
229
236
  });
230
- case 34:
237
+ case 39:
238
+ _context.prev = 39;
239
+ // Ensure we clean up the in-flight request entry
240
+ delete this.networkRequestsInFlight[networkRequestInFlightKey];
241
+ return _context.finish(39);
242
+ case 42:
231
243
  case "end":
232
244
  return _context.stop();
233
245
  }
234
- }, _callee, this, [[0, 31]]);
246
+ }, _callee, this, [[15, 22], [26, 36, 39, 42]]);
235
247
  }));
236
248
  function getDataAsync(_x, _x2) {
237
249
  return _getDataAsync.apply(this, arguments);
@@ -287,18 +299,27 @@ var NodeDataProvider = exports.NodeDataProvider = /*#__PURE__*/function () {
287
299
  }, {
288
300
  key: "getCacheStatusForNode",
289
301
  value: function getCacheStatusForNode(node) {
302
+ var dataFromCache = this.getNodeDataFromCache(node);
303
+ return dataFromCache ? dataFromCache.source : false;
304
+ }
305
+
306
+ /**
307
+ * Retrieves the cached data for a given node, if available.
308
+ *
309
+ * @param node The node (or its ProseMirror representation) for which to retrieve cached data.
310
+ * @returns The cached data object containing `data` and `source`, or `undefined` if no cache entry exists.
311
+ */
312
+ }, {
313
+ key: "getNodeDataFromCache",
314
+ value: function getNodeDataFromCache(node) {
290
315
  var jsonNode = 'toJSON' in node ? node.toJSON() : node;
291
316
  if (!this.isNodeSupported(jsonNode)) {
292
317
  // eslint-disable-next-line no-console
293
318
  console.error("The ".concat(this.constructor.name, " doesn't support Node ").concat(jsonNode.type, "."));
294
- return false;
319
+ return undefined;
295
320
  }
296
321
  var dataKey = this.nodeDataKey(jsonNode);
297
- var dataFromCache = this.cache[dataKey];
298
- return dataFromCache ? dataFromCache.source : false;
322
+ return this.cache[dataKey];
299
323
  }
300
324
  }]);
301
- }();
302
- function isPromise(value) {
303
- return (0, _typeof2.default)(value) === 'object' && value !== null && 'then' in value && typeof value.then === 'function';
304
- }
325
+ }();
@@ -20,7 +20,7 @@ import { isSSR } from '@atlaskit/editor-common/core-utils';
20
20
  * @example
21
21
  * {
22
22
  * 'node-id-1': { source: 'ssr', data: { value: 'some data' } },
23
- * 'node-id-2': { source: 'network', data: Promise.resolve({ value: 'other data' }) }
23
+ * 'node-id-2': { source: 'network', data: { value: 'other data' } }
24
24
  * }
25
25
  */
26
26
 
@@ -66,6 +66,7 @@ export class NodeDataProvider {
66
66
  constructor() {
67
67
  this.cacheVersion = 0;
68
68
  this.cache = {};
69
+ this.networkRequestsInFlight = {};
69
70
  }
70
71
 
71
72
  /**
@@ -137,45 +138,59 @@ export class NodeDataProvider {
137
138
  void this.getDataAsync(node, callback);
138
139
  }
139
140
  async getDataAsync(node, callback) {
140
- try {
141
- const jsonNode = 'toJSON' in node ? node.toJSON() : node;
142
- if (!this.isNodeSupported(jsonNode)) {
143
- // eslint-disable-next-line no-console
144
- console.error(`The ${this.constructor.name} doesn't support Node ${jsonNode.type}.`);
141
+ const jsonNode = 'toJSON' in node ? node.toJSON() : node;
142
+ if (!this.isNodeSupported(jsonNode)) {
143
+ // eslint-disable-next-line no-console
144
+ console.error(`The ${this.constructor.name} doesn't support Node ${jsonNode.type}.`);
145
+ return;
146
+ }
147
+ const dataKey = this.nodeDataKey(jsonNode);
148
+ const dataFromCache = this.cache[dataKey];
149
+ if (dataFromCache !== undefined) {
150
+ // If we have the data in the SSR data, we can use it directly
151
+ callback({
152
+ data: dataFromCache.data
153
+ });
154
+ if (isSSR()) {
155
+ // If we are in SSR, we don't want to fetch the data again, as it is already available in the SSR data
145
156
  return;
146
157
  }
147
- const dataKey = this.nodeDataKey(jsonNode);
148
- const dataFromCache = this.cache[dataKey];
149
- if (dataFromCache !== undefined) {
150
- // If we have the data in the SSR data, we can use it directly
151
- if (isPromise(dataFromCache.data)) {
158
+ }
159
+
160
+ // If no data is available in the cache, or the data is from the network,
161
+ // we need to fetch it from the network.
162
+ if ((dataFromCache === null || dataFromCache === void 0 ? void 0 : dataFromCache.source) !== 'network') {
163
+ // Store the current cache version before making the request,
164
+ // so we can check if the cache has changed while we are waiting for the network response.
165
+ const cacheVersionBeforeRequest = this.cacheVersion;
166
+
167
+ // Create a unique key for the in-flight network request
168
+ // based on the cache version and the data key.
169
+ const networkRequestInFlightKey = `${cacheVersionBeforeRequest}-${dataKey}`;
170
+
171
+ // Check if there is already a network request in flight for this data
172
+ // to avoid duplicate requests.
173
+ const networkRequestInFlight = this.networkRequestsInFlight[networkRequestInFlightKey];
174
+ if (networkRequestInFlight) {
175
+ try {
176
+ const data = await networkRequestInFlight;
152
177
  callback({
153
- data: await dataFromCache.data
178
+ data
154
179
  });
155
- } else {
180
+ } catch (error) {
156
181
  callback({
157
- data: dataFromCache.data
182
+ error: error instanceof Error ? error : new Error(String(error))
158
183
  });
159
184
  }
160
- if (isSSR()) {
161
- // If we are in SSR, we don't want to fetch the data again, as it is already available in the SSR data
162
- return;
163
- }
185
+ return;
164
186
  }
165
-
166
- // If no data is available in the cache or the data is from the network,
167
- // we need to fetch it from the network.
168
- if ((dataFromCache === null || dataFromCache === void 0 ? void 0 : dataFromCache.source) !== 'network') {
169
- // Store the current cache version before making the request,
170
- // so we can check if the cache has changed while we are waiting for the network response.
171
- const cacheVersionBeforeRequest = this.cacheVersion;
187
+ try {
172
188
  const dataPromise = this.fetchNodesData([jsonNode]).then(([value]) => value);
173
- // Store the promise in the cache to avoid multiple requests for the same data
174
- this.cache[dataKey] = {
175
- source: 'network',
176
- data: dataPromise
177
- };
189
+
190
+ // Store the promise in the in-flight requests map
191
+ this.networkRequestsInFlight[networkRequestInFlightKey] = dataPromise;
178
192
  const data = await dataPromise;
193
+
179
194
  // We need to call the callback with the data with result even if the cache version has changed,
180
195
  // so all promises that are waiting for the data can resolve.
181
196
  callback({
@@ -191,12 +206,15 @@ export class NodeDataProvider {
191
206
  data
192
207
  };
193
208
  }
209
+ } catch (error) {
210
+ // If an error occurs, we call the callback with the error
211
+ callback({
212
+ error: error instanceof Error ? error : new Error(String(error))
213
+ });
214
+ } finally {
215
+ // Ensure we clean up the in-flight request entry
216
+ delete this.networkRequestsInFlight[networkRequestInFlightKey];
194
217
  }
195
- } catch (error) {
196
- // If an error occurs, we call the callback with the error
197
- callback({
198
- error: error instanceof Error ? error : new Error(String(error))
199
- });
200
218
  }
201
219
  }
202
220
 
@@ -244,17 +262,24 @@ export class NodeDataProvider {
244
262
  * @returns The cache status: `false`, `'ssr'`, or `'network'`.
245
263
  */
246
264
  getCacheStatusForNode(node) {
265
+ const dataFromCache = this.getNodeDataFromCache(node);
266
+ return dataFromCache ? dataFromCache.source : false;
267
+ }
268
+
269
+ /**
270
+ * Retrieves the cached data for a given node, if available.
271
+ *
272
+ * @param node The node (or its ProseMirror representation) for which to retrieve cached data.
273
+ * @returns The cached data object containing `data` and `source`, or `undefined` if no cache entry exists.
274
+ */
275
+ getNodeDataFromCache(node) {
247
276
  const jsonNode = 'toJSON' in node ? node.toJSON() : node;
248
277
  if (!this.isNodeSupported(jsonNode)) {
249
278
  // eslint-disable-next-line no-console
250
279
  console.error(`The ${this.constructor.name} doesn't support Node ${jsonNode.type}.`);
251
- return false;
280
+ return undefined;
252
281
  }
253
282
  const dataKey = this.nodeDataKey(jsonNode);
254
- const dataFromCache = this.cache[dataKey];
255
- return dataFromCache ? dataFromCache.source : false;
283
+ return this.cache[dataKey];
256
284
  }
257
- }
258
- function isPromise(value) {
259
- return typeof value === 'object' && value !== null && 'then' in value && typeof value.then === 'function';
260
285
  }
@@ -1,4 +1,3 @@
1
- import _typeof from "@babel/runtime/helpers/typeof";
2
1
  import _asyncToGenerator from "@babel/runtime/helpers/asyncToGenerator";
3
2
  import _slicedToArray from "@babel/runtime/helpers/slicedToArray";
4
3
  import _classCallCheck from "@babel/runtime/helpers/classCallCheck";
@@ -26,7 +25,7 @@ import { isSSR } from '@atlaskit/editor-common/core-utils';
26
25
  * @example
27
26
  * {
28
27
  * 'node-id-1': { source: 'ssr', data: { value: 'some data' } },
29
- * 'node-id-2': { source: 'network', data: Promise.resolve({ value: 'other data' }) }
28
+ * 'node-id-2': { source: 'network', data: { value: 'other data' } }
30
29
  * }
31
30
  */
32
31
 
@@ -47,6 +46,7 @@ export var NodeDataProvider = /*#__PURE__*/function () {
47
46
  _classCallCheck(this, NodeDataProvider);
48
47
  this.cacheVersion = 0;
49
48
  this.cache = {};
49
+ this.networkRequestsInFlight = {};
50
50
  }
51
51
 
52
52
  /**
@@ -133,102 +133,114 @@ export var NodeDataProvider = /*#__PURE__*/function () {
133
133
  key: "getDataAsync",
134
134
  value: function () {
135
135
  var _getDataAsync = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee(node, callback) {
136
- var jsonNode, _dataKey, dataFromCache, cacheVersionBeforeRequest, dataPromise, data;
136
+ var jsonNode, dataKey, dataFromCache, cacheVersionBeforeRequest, networkRequestInFlightKey, networkRequestInFlight, data, dataPromise, _data;
137
137
  return _regeneratorRuntime.wrap(function _callee$(_context) {
138
138
  while (1) switch (_context.prev = _context.next) {
139
139
  case 0:
140
- _context.prev = 0;
141
140
  jsonNode = 'toJSON' in node ? node.toJSON() : node;
142
141
  if (this.isNodeSupported(jsonNode)) {
143
- _context.next = 5;
142
+ _context.next = 4;
144
143
  break;
145
144
  }
146
145
  // eslint-disable-next-line no-console
147
146
  console.error("The ".concat(this.constructor.name, " doesn't support Node ").concat(jsonNode.type, "."));
148
147
  return _context.abrupt("return");
149
- case 5:
150
- _dataKey = this.nodeDataKey(jsonNode);
151
- dataFromCache = this.cache[_dataKey];
148
+ case 4:
149
+ dataKey = this.nodeDataKey(jsonNode);
150
+ dataFromCache = this.cache[dataKey];
152
151
  if (!(dataFromCache !== undefined)) {
153
- _context.next = 20;
152
+ _context.next = 10;
154
153
  break;
155
154
  }
156
- if (!isPromise(dataFromCache.data)) {
157
- _context.next = 17;
158
- break;
159
- }
160
- _context.t0 = callback;
161
- _context.next = 12;
162
- return dataFromCache.data;
163
- case 12:
164
- _context.t1 = _context.sent;
165
- _context.t2 = {
166
- data: _context.t1
167
- };
168
- (0, _context.t0)(_context.t2);
169
- _context.next = 18;
170
- break;
171
- case 17:
155
+ // If we have the data in the SSR data, we can use it directly
172
156
  callback({
173
157
  data: dataFromCache.data
174
158
  });
175
- case 18:
176
159
  if (!isSSR()) {
177
- _context.next = 20;
160
+ _context.next = 10;
178
161
  break;
179
162
  }
180
163
  return _context.abrupt("return");
181
- case 20:
164
+ case 10:
182
165
  if (!((dataFromCache === null || dataFromCache === void 0 ? void 0 : dataFromCache.source) !== 'network')) {
183
- _context.next = 29;
166
+ _context.next = 42;
184
167
  break;
185
168
  }
186
169
  // Store the current cache version before making the request,
187
170
  // so we can check if the cache has changed while we are waiting for the network response.
188
- cacheVersionBeforeRequest = this.cacheVersion;
171
+ cacheVersionBeforeRequest = this.cacheVersion; // Create a unique key for the in-flight network request
172
+ // based on the cache version and the data key.
173
+ networkRequestInFlightKey = "".concat(cacheVersionBeforeRequest, "-").concat(dataKey); // Check if there is already a network request in flight for this data
174
+ // to avoid duplicate requests.
175
+ networkRequestInFlight = this.networkRequestsInFlight[networkRequestInFlightKey];
176
+ if (!networkRequestInFlight) {
177
+ _context.next = 26;
178
+ break;
179
+ }
180
+ _context.prev = 15;
181
+ _context.next = 18;
182
+ return networkRequestInFlight;
183
+ case 18:
184
+ data = _context.sent;
185
+ callback({
186
+ data: data
187
+ });
188
+ _context.next = 25;
189
+ break;
190
+ case 22:
191
+ _context.prev = 22;
192
+ _context.t0 = _context["catch"](15);
193
+ callback({
194
+ error: _context.t0 instanceof Error ? _context.t0 : new Error(String(_context.t0))
195
+ });
196
+ case 25:
197
+ return _context.abrupt("return");
198
+ case 26:
199
+ _context.prev = 26;
189
200
  dataPromise = this.fetchNodesData([jsonNode]).then(function (_ref3) {
190
201
  var _ref4 = _slicedToArray(_ref3, 1),
191
202
  value = _ref4[0];
192
203
  return value;
193
- }); // Store the promise in the cache to avoid multiple requests for the same data
194
- this.cache[_dataKey] = {
195
- source: 'network',
196
- data: dataPromise
197
- };
198
- _context.next = 26;
204
+ }); // Store the promise in the in-flight requests map
205
+ this.networkRequestsInFlight[networkRequestInFlightKey] = dataPromise;
206
+ _context.next = 31;
199
207
  return dataPromise;
200
- case 26:
201
- data = _context.sent;
208
+ case 31:
209
+ _data = _context.sent;
202
210
  // We need to call the callback with the data with result even if the cache version has changed,
203
211
  // so all promises that are waiting for the data can resolve.
204
212
  callback({
205
- data: data
213
+ data: _data
206
214
  });
207
215
 
208
216
  // If the cache version has changed, we don't want to use the data from the network
209
217
  // because it could be stale data.
210
218
  if (cacheVersionBeforeRequest === this.cacheVersion) {
211
219
  // Replace promise with the resolved data in the cache
212
- this.cache[_dataKey] = {
220
+ this.cache[dataKey] = {
213
221
  source: 'network',
214
- data: data
222
+ data: _data
215
223
  };
216
224
  }
217
- case 29:
218
- _context.next = 34;
225
+ _context.next = 39;
219
226
  break;
220
- case 31:
221
- _context.prev = 31;
222
- _context.t3 = _context["catch"](0);
227
+ case 36:
228
+ _context.prev = 36;
229
+ _context.t1 = _context["catch"](26);
223
230
  // If an error occurs, we call the callback with the error
224
231
  callback({
225
- error: _context.t3 instanceof Error ? _context.t3 : new Error(String(_context.t3))
232
+ error: _context.t1 instanceof Error ? _context.t1 : new Error(String(_context.t1))
226
233
  });
227
- case 34:
234
+ case 39:
235
+ _context.prev = 39;
236
+ // Ensure we clean up the in-flight request entry
237
+ delete this.networkRequestsInFlight[networkRequestInFlightKey];
238
+ return _context.finish(39);
239
+ case 42:
228
240
  case "end":
229
241
  return _context.stop();
230
242
  }
231
- }, _callee, this, [[0, 31]]);
243
+ }, _callee, this, [[15, 22], [26, 36, 39, 42]]);
232
244
  }));
233
245
  function getDataAsync(_x, _x2) {
234
246
  return _getDataAsync.apply(this, arguments);
@@ -284,18 +296,27 @@ export var NodeDataProvider = /*#__PURE__*/function () {
284
296
  }, {
285
297
  key: "getCacheStatusForNode",
286
298
  value: function getCacheStatusForNode(node) {
299
+ var dataFromCache = this.getNodeDataFromCache(node);
300
+ return dataFromCache ? dataFromCache.source : false;
301
+ }
302
+
303
+ /**
304
+ * Retrieves the cached data for a given node, if available.
305
+ *
306
+ * @param node The node (or its ProseMirror representation) for which to retrieve cached data.
307
+ * @returns The cached data object containing `data` and `source`, or `undefined` if no cache entry exists.
308
+ */
309
+ }, {
310
+ key: "getNodeDataFromCache",
311
+ value: function getNodeDataFromCache(node) {
287
312
  var jsonNode = 'toJSON' in node ? node.toJSON() : node;
288
313
  if (!this.isNodeSupported(jsonNode)) {
289
314
  // eslint-disable-next-line no-console
290
315
  console.error("The ".concat(this.constructor.name, " doesn't support Node ").concat(jsonNode.type, "."));
291
- return false;
316
+ return undefined;
292
317
  }
293
318
  var dataKey = this.nodeDataKey(jsonNode);
294
- var dataFromCache = this.cache[dataKey];
295
- return dataFromCache ? dataFromCache.source : false;
319
+ return this.cache[dataKey];
296
320
  }
297
321
  }]);
298
- }();
299
- function isPromise(value) {
300
- return _typeof(value) === 'object' && value !== null && 'then' in value && typeof value.then === 'function';
301
- }
322
+ }();
@@ -13,6 +13,22 @@ import type { Node as PMNode } from '@atlaskit/editor-prosemirror/model';
13
13
  type SSRData<Data> = {
14
14
  [dataKey: string]: Data;
15
15
  };
16
+ /**
17
+ * Represents the cached data for a Node Data Provider.
18
+ * Each key is a unique node data key, and the value is an object containing:
19
+ * - `source`: Indicates whether the data was fetched from SSR or the network.
20
+ * - `data`: The actual data, which can be either a resolved value or a Promise.
21
+ *
22
+ * @example
23
+ * {
24
+ * 'node-id-1': { source: 'ssr', data: { value: 'some data' } },
25
+ * 'node-id-2': { source: 'network', data: { value: 'other data' } }
26
+ * }
27
+ */
28
+ type CacheData<Data> = Record<string, {
29
+ data: Data;
30
+ source: 'ssr' | 'network';
31
+ }>;
16
32
  /**
17
33
  * Represents the payload passed to the callback function when data is fetched.
18
34
  * It can either contain an error or the fetched data.
@@ -36,6 +52,7 @@ type CallbackPayload<Data> = {
36
52
  export declare abstract class NodeDataProvider<Node extends JSONNode, Data> {
37
53
  private cacheVersion;
38
54
  private cache;
55
+ private readonly networkRequestsInFlight;
39
56
  /**
40
57
  * A unique name for the provider. Used for identification in SSR.
41
58
  */
@@ -116,7 +133,7 @@ export declare abstract class NodeDataProvider<Node extends JSONNode, Data> {
116
133
  * @param callback The callback function to call with the fetched data or an error.
117
134
  */
118
135
  getData(node: Node | PMNode, callback: (payload: CallbackPayload<Data>) => void): void;
119
- private getDataAsync;
136
+ protected getDataAsync(node: Node | PMNode, callback: (payload: CallbackPayload<Data>) => void): Promise<void>;
120
137
  /**
121
138
  * Fetches data for a given node and returns it as a Promise.
122
139
  * This is a convenience wrapper around the `data` method for use with async/await.
@@ -146,5 +163,12 @@ export declare abstract class NodeDataProvider<Node extends JSONNode, Data> {
146
163
  * @returns The cache status: `false`, `'ssr'`, or `'network'`.
147
164
  */
148
165
  getCacheStatusForNode(node: Node | PMNode): false | 'ssr' | 'network';
166
+ /**
167
+ * Retrieves the cached data for a given node, if available.
168
+ *
169
+ * @param node The node (or its ProseMirror representation) for which to retrieve cached data.
170
+ * @returns The cached data object containing `data` and `source`, or `undefined` if no cache entry exists.
171
+ */
172
+ getNodeDataFromCache(node: JSONNode | PMNode): CacheData<Data>[string] | undefined;
149
173
  }
150
174
  export {};
@@ -13,6 +13,22 @@ import type { Node as PMNode } from '@atlaskit/editor-prosemirror/model';
13
13
  type SSRData<Data> = {
14
14
  [dataKey: string]: Data;
15
15
  };
16
+ /**
17
+ * Represents the cached data for a Node Data Provider.
18
+ * Each key is a unique node data key, and the value is an object containing:
19
+ * - `source`: Indicates whether the data was fetched from SSR or the network.
20
+ * - `data`: The actual data, which can be either a resolved value or a Promise.
21
+ *
22
+ * @example
23
+ * {
24
+ * 'node-id-1': { source: 'ssr', data: { value: 'some data' } },
25
+ * 'node-id-2': { source: 'network', data: { value: 'other data' } }
26
+ * }
27
+ */
28
+ type CacheData<Data> = Record<string, {
29
+ data: Data;
30
+ source: 'ssr' | 'network';
31
+ }>;
16
32
  /**
17
33
  * Represents the payload passed to the callback function when data is fetched.
18
34
  * It can either contain an error or the fetched data.
@@ -36,6 +52,7 @@ type CallbackPayload<Data> = {
36
52
  export declare abstract class NodeDataProvider<Node extends JSONNode, Data> {
37
53
  private cacheVersion;
38
54
  private cache;
55
+ private readonly networkRequestsInFlight;
39
56
  /**
40
57
  * A unique name for the provider. Used for identification in SSR.
41
58
  */
@@ -116,7 +133,7 @@ export declare abstract class NodeDataProvider<Node extends JSONNode, Data> {
116
133
  * @param callback The callback function to call with the fetched data or an error.
117
134
  */
118
135
  getData(node: Node | PMNode, callback: (payload: CallbackPayload<Data>) => void): void;
119
- private getDataAsync;
136
+ protected getDataAsync(node: Node | PMNode, callback: (payload: CallbackPayload<Data>) => void): Promise<void>;
120
137
  /**
121
138
  * Fetches data for a given node and returns it as a Promise.
122
139
  * This is a convenience wrapper around the `data` method for use with async/await.
@@ -146,5 +163,12 @@ export declare abstract class NodeDataProvider<Node extends JSONNode, Data> {
146
163
  * @returns The cache status: `false`, `'ssr'`, or `'network'`.
147
164
  */
148
165
  getCacheStatusForNode(node: Node | PMNode): false | 'ssr' | 'network';
166
+ /**
167
+ * Retrieves the cached data for a given node, if available.
168
+ *
169
+ * @param node The node (or its ProseMirror representation) for which to retrieve cached data.
170
+ * @returns The cached data object containing `data` and `source`, or `undefined` if no cache entry exists.
171
+ */
172
+ getNodeDataFromCache(node: JSONNode | PMNode): CacheData<Data>[string] | undefined;
149
173
  }
150
174
  export {};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@atlaskit/node-data-provider",
3
- "version": "7.2.2",
3
+ "version": "7.4.0",
4
4
  "description": "Node data provider for @atlaskit/editor-core plugins and @atlaskit/renderer",
5
5
  "author": "Atlassian Pty Ltd",
6
6
  "license": "Apache-2.0",
@@ -26,7 +26,7 @@
26
26
  "@babel/runtime": "^7.0.0"
27
27
  },
28
28
  "peerDependencies": {
29
- "@atlaskit/editor-common": "^110.18.0"
29
+ "@atlaskit/editor-common": "^110.21.0"
30
30
  },
31
31
  "techstack": {
32
32
  "@atlassian/frontend": {