@atlaskit/node-data-provider 4.3.0 → 4.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +25 -0
- package/dist/cjs/node-data-provider.js +60 -33
- package/dist/cjs/utils/find-nodes-to-prefetch.js +19 -2
- package/dist/cjs/utils/prefetch-node-data-providers-data.js +47 -18
- package/dist/es2019/node-data-provider.js +70 -49
- package/dist/es2019/utils/find-nodes-to-prefetch.js +19 -2
- package/dist/es2019/utils/prefetch-node-data-providers-data.js +38 -9
- package/dist/esm/node-data-provider.js +61 -33
- package/dist/esm/utils/find-nodes-to-prefetch.js +19 -2
- package/dist/esm/utils/prefetch-node-data-providers-data.js +47 -18
- package/dist/types/node-data-provider.d.ts +15 -2
- package/dist/types/utils/prefetch-node-data-providers-data.d.ts +25 -7
- package/dist/types-ts4.5/node-data-provider.d.ts +15 -2
- package/dist/types-ts4.5/utils/prefetch-node-data-providers-data.d.ts +25 -7
- package/package.json +4 -4
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,30 @@
|
|
|
1
1
|
# @atlaskit/node-data-provider
|
|
2
2
|
|
|
3
|
+
## 4.5.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- [#193382](https://bitbucket.org/atlassian/atlassian-frontend-monorepo/pull-requests/193382)
|
|
8
|
+
[`5c827a7e9ac42`](https://bitbucket.org/atlassian/atlassian-frontend-monorepo/commits/5c827a7e9ac42) -
|
|
9
|
+
[https://product-fabric.atlassian.net/browse/ED-28627](ED-28627) - add `duration` and `success` to
|
|
10
|
+
`prefetchNodeDataProvidersData` result
|
|
11
|
+
|
|
12
|
+
## 4.4.0
|
|
13
|
+
|
|
14
|
+
### Minor Changes
|
|
15
|
+
|
|
16
|
+
- [#191942](https://bitbucket.org/atlassian/atlassian-frontend-monorepo/pull-requests/191942)
|
|
17
|
+
[`6b5ec599e6dfd`](https://bitbucket.org/atlassian/atlassian-frontend-monorepo/commits/6b5ec599e6dfd) -
|
|
18
|
+
[https://product-fabric.atlassian.net/browse/ED-28596](ED-28596) - extend EditorCardProvider from
|
|
19
|
+
NodeDataProvider
|
|
20
|
+
|
|
21
|
+
### Patch Changes
|
|
22
|
+
|
|
23
|
+
- [#191913](https://bitbucket.org/atlassian/atlassian-frontend-monorepo/pull-requests/191913)
|
|
24
|
+
[`6d1e56695e91d`](https://bitbucket.org/atlassian/atlassian-frontend-monorepo/commits/6d1e56695e91d) -
|
|
25
|
+
EDITOR-1131 Bump adf-schema package to 50.0.0
|
|
26
|
+
- Updated dependencies
|
|
27
|
+
|
|
3
28
|
## 4.3.0
|
|
4
29
|
|
|
5
30
|
### Minor Changes
|
|
@@ -34,6 +34,10 @@ var _coreUtils = require("@atlaskit/editor-common/core-utils");
|
|
|
34
34
|
* 'node-id-2': { source: 'network', data: Promise.resolve({ value: 'other data' }) }
|
|
35
35
|
* }
|
|
36
36
|
*/
|
|
37
|
+
/**
|
|
38
|
+
* Represents the payload passed to the callback function when data is fetched.
|
|
39
|
+
* It can either contain an error or the fetched data.
|
|
40
|
+
*/
|
|
37
41
|
/**
|
|
38
42
|
* A Node Data Provider is responsible for fetching and caching data associated with specific ProseMirror nodes.
|
|
39
43
|
* It supports a cache-first-then-network strategy, with initial data potentially provided via SSR.
|
|
@@ -66,16 +70,15 @@ var NodeDataProvider = exports.NodeDataProvider = /*#__PURE__*/function () {
|
|
|
66
70
|
value: function setSSRData() {
|
|
67
71
|
var ssrData = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
|
|
68
72
|
this.cacheVersion++;
|
|
69
|
-
this.cache = Object.entries(ssrData).
|
|
73
|
+
this.cache = Object.fromEntries(Object.entries(ssrData).map(function (_ref) {
|
|
70
74
|
var _ref2 = (0, _slicedToArray2.default)(_ref, 2),
|
|
71
75
|
key = _ref2[0],
|
|
72
76
|
data = _ref2[1];
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
};
|
|
77
|
-
|
|
78
|
-
}, {});
|
|
77
|
+
return [key, {
|
|
78
|
+
data: data,
|
|
79
|
+
source: 'ssr'
|
|
80
|
+
}];
|
|
81
|
+
}));
|
|
79
82
|
}
|
|
80
83
|
|
|
81
84
|
/**
|
|
@@ -120,7 +123,7 @@ var NodeDataProvider = exports.NodeDataProvider = /*#__PURE__*/function () {
|
|
|
120
123
|
* ```
|
|
121
124
|
*
|
|
122
125
|
* @param node The node (or its ProseMirror representation) for which to fetch data.
|
|
123
|
-
* @param callback The function to call
|
|
126
|
+
* @param callback The callback function to call with the fetched data or an error.
|
|
124
127
|
*/
|
|
125
128
|
}, {
|
|
126
129
|
key: "getData",
|
|
@@ -133,48 +136,54 @@ var NodeDataProvider = exports.NodeDataProvider = /*#__PURE__*/function () {
|
|
|
133
136
|
key: "getDataAsync",
|
|
134
137
|
value: function () {
|
|
135
138
|
var _getDataAsync = (0, _asyncToGenerator2.default)( /*#__PURE__*/_regenerator.default.mark(function _callee(node, callback) {
|
|
136
|
-
var jsonNode,
|
|
139
|
+
var jsonNode, _dataKey, dataFromCache, cacheVersionBeforeRequest, dataPromise, data;
|
|
137
140
|
return _regenerator.default.wrap(function _callee$(_context) {
|
|
138
141
|
while (1) switch (_context.prev = _context.next) {
|
|
139
142
|
case 0:
|
|
143
|
+
_context.prev = 0;
|
|
140
144
|
jsonNode = 'toJSON' in node ? node.toJSON() : node;
|
|
141
145
|
if (this.isNodeSupported(jsonNode)) {
|
|
142
|
-
_context.next =
|
|
146
|
+
_context.next = 5;
|
|
143
147
|
break;
|
|
144
148
|
}
|
|
145
149
|
// eslint-disable-next-line no-console
|
|
146
150
|
console.error("The ".concat(this.constructor.name, " doesn't support Node ").concat(jsonNode.type, "."));
|
|
147
151
|
return _context.abrupt("return");
|
|
148
|
-
case
|
|
149
|
-
|
|
150
|
-
dataFromCache = this.cache[
|
|
152
|
+
case 5:
|
|
153
|
+
_dataKey = this.nodeDataKey(jsonNode);
|
|
154
|
+
dataFromCache = this.cache[_dataKey];
|
|
151
155
|
if (!(dataFromCache !== undefined)) {
|
|
152
|
-
_context.next =
|
|
156
|
+
_context.next = 20;
|
|
153
157
|
break;
|
|
154
158
|
}
|
|
155
159
|
if (!isPromise(dataFromCache.data)) {
|
|
156
|
-
_context.next =
|
|
160
|
+
_context.next = 17;
|
|
157
161
|
break;
|
|
158
162
|
}
|
|
159
163
|
_context.t0 = callback;
|
|
160
|
-
_context.next =
|
|
164
|
+
_context.next = 12;
|
|
161
165
|
return dataFromCache.data;
|
|
162
|
-
case
|
|
166
|
+
case 12:
|
|
163
167
|
_context.t1 = _context.sent;
|
|
164
|
-
|
|
165
|
-
|
|
168
|
+
_context.t2 = {
|
|
169
|
+
data: _context.t1
|
|
170
|
+
};
|
|
171
|
+
(0, _context.t0)(_context.t2);
|
|
172
|
+
_context.next = 18;
|
|
166
173
|
break;
|
|
167
|
-
case
|
|
168
|
-
callback(
|
|
169
|
-
|
|
174
|
+
case 17:
|
|
175
|
+
callback({
|
|
176
|
+
data: dataFromCache.data
|
|
177
|
+
});
|
|
178
|
+
case 18:
|
|
170
179
|
if (!(0, _coreUtils.isSSR)()) {
|
|
171
|
-
_context.next =
|
|
180
|
+
_context.next = 20;
|
|
172
181
|
break;
|
|
173
182
|
}
|
|
174
183
|
return _context.abrupt("return");
|
|
175
|
-
case
|
|
184
|
+
case 20:
|
|
176
185
|
if (!((dataFromCache === null || dataFromCache === void 0 ? void 0 : dataFromCache.source) !== 'network')) {
|
|
177
|
-
_context.next =
|
|
186
|
+
_context.next = 29;
|
|
178
187
|
break;
|
|
179
188
|
}
|
|
180
189
|
// Store the current cache version before making the request,
|
|
@@ -185,32 +194,44 @@ var NodeDataProvider = exports.NodeDataProvider = /*#__PURE__*/function () {
|
|
|
185
194
|
value = _ref4[0];
|
|
186
195
|
return value;
|
|
187
196
|
}); // Store the promise in the cache to avoid multiple requests for the same data
|
|
188
|
-
this.cache[
|
|
197
|
+
this.cache[_dataKey] = {
|
|
189
198
|
source: 'network',
|
|
190
199
|
data: dataPromise
|
|
191
200
|
};
|
|
192
|
-
_context.next =
|
|
201
|
+
_context.next = 26;
|
|
193
202
|
return dataPromise;
|
|
194
|
-
case
|
|
203
|
+
case 26:
|
|
195
204
|
data = _context.sent;
|
|
196
205
|
// We need to call the callback with the data with result even if the cache version has changed,
|
|
197
206
|
// so all promises that are waiting for the data can resolve.
|
|
198
|
-
callback(
|
|
207
|
+
callback({
|
|
208
|
+
data: data
|
|
209
|
+
});
|
|
199
210
|
|
|
200
211
|
// If the cache version has changed, we don't want to use the data from the network
|
|
201
212
|
// because it could be stale data.
|
|
202
213
|
if (cacheVersionBeforeRequest === this.cacheVersion) {
|
|
203
214
|
// Replace promise with the resolved data in the cache
|
|
204
|
-
this.cache[
|
|
215
|
+
this.cache[_dataKey] = {
|
|
205
216
|
source: 'network',
|
|
206
217
|
data: data
|
|
207
218
|
};
|
|
208
219
|
}
|
|
209
|
-
case
|
|
220
|
+
case 29:
|
|
221
|
+
_context.next = 34;
|
|
222
|
+
break;
|
|
223
|
+
case 31:
|
|
224
|
+
_context.prev = 31;
|
|
225
|
+
_context.t3 = _context["catch"](0);
|
|
226
|
+
// If an error occurs, we call the callback with the error
|
|
227
|
+
callback({
|
|
228
|
+
error: _context.t3 instanceof Error ? _context.t3 : new Error(String(_context.t3))
|
|
229
|
+
});
|
|
230
|
+
case 34:
|
|
210
231
|
case "end":
|
|
211
232
|
return _context.stop();
|
|
212
233
|
}
|
|
213
|
-
}, _callee, this);
|
|
234
|
+
}, _callee, this, [[0, 31]]);
|
|
214
235
|
}));
|
|
215
236
|
function getDataAsync(_x, _x2) {
|
|
216
237
|
return _getDataAsync.apply(this, arguments);
|
|
@@ -239,7 +260,13 @@ var NodeDataProvider = exports.NodeDataProvider = /*#__PURE__*/function () {
|
|
|
239
260
|
var _this = this;
|
|
240
261
|
return new Promise(function (resolve, reject) {
|
|
241
262
|
try {
|
|
242
|
-
|
|
263
|
+
_this.getData(node, function (payload) {
|
|
264
|
+
if (payload.error) {
|
|
265
|
+
reject(payload.error);
|
|
266
|
+
} else {
|
|
267
|
+
resolve(payload.data);
|
|
268
|
+
}
|
|
269
|
+
});
|
|
243
270
|
} catch (error) {
|
|
244
271
|
reject(error);
|
|
245
272
|
}
|
|
@@ -15,7 +15,9 @@ var _toConsumableArray2 = _interopRequireDefault(require("@babel/runtime/helpers
|
|
|
15
15
|
* @returns An array of objects, each containing a provider and the nodes that are supported by that provider.
|
|
16
16
|
*/
|
|
17
17
|
function findNodesToPrefetch(doc, providers, maxNodesToVisit) {
|
|
18
|
+
// Counter for the total number of visited nodes to limit the traversal.
|
|
18
19
|
var totalVisitedNodesCount = 0;
|
|
20
|
+
// A map to store the results, with the provider name as the key.
|
|
19
21
|
var resultMap = providers.reduce(function (acc, _ref) {
|
|
20
22
|
var provider = _ref.provider;
|
|
21
23
|
acc[provider.name] = {
|
|
@@ -28,6 +30,7 @@ function findNodesToPrefetch(doc, providers, maxNodesToVisit) {
|
|
|
28
30
|
// It doesn't use `filter` function from `@atlaskit/adf-utils/traverse` because it does not support early stopping.
|
|
29
31
|
// We need to stop traversing when we reach the maximum number of nodes to visit to support large documents.
|
|
30
32
|
|
|
33
|
+
// Create a list of providers for which we still need to find nodes.
|
|
31
34
|
var providersToLookup = providers.filter(function (_ref2) {
|
|
32
35
|
var maxNodesToPrefetch = _ref2.maxNodesToPrefetch;
|
|
33
36
|
return maxNodesToPrefetch > 0;
|
|
@@ -37,32 +40,46 @@ function findNodesToPrefetch(doc, providers, maxNodesToVisit) {
|
|
|
37
40
|
return {
|
|
38
41
|
provider: provider,
|
|
39
42
|
maxNodesToPrefetch: maxNodesToPrefetch,
|
|
40
|
-
foundNodes: 0
|
|
43
|
+
foundNodes: 0 // Counter for nodes found for each provider.
|
|
41
44
|
};
|
|
42
45
|
});
|
|
46
|
+
|
|
47
|
+
// Queue for the breadth-first search (BFS), starting with the root document node.
|
|
43
48
|
var nodesToVisit = [doc];
|
|
44
49
|
var currentIndex = 0;
|
|
50
|
+
// The loop continues as long as there are nodes to visit, providers to look for,
|
|
51
|
+
// and the visited nodes limit has not been reached.
|
|
45
52
|
while (currentIndex < nodesToVisit.length && providersToLookup.length > 0 && totalVisitedNodesCount < maxNodesToVisit) {
|
|
46
53
|
totalVisitedNodesCount += 1;
|
|
47
54
|
var currentNode = nodesToVisit[currentIndex];
|
|
48
55
|
currentIndex++;
|
|
49
56
|
|
|
50
|
-
// Using reverse loop to avoid issues with array mutation
|
|
57
|
+
// Using a reverse loop to avoid issues with array mutation (when removing elements).
|
|
51
58
|
for (var i = providersToLookup.length - 1; i >= 0; i--) {
|
|
52
59
|
var providerToFind = providersToLookup[i];
|
|
60
|
+
|
|
61
|
+
// Check if the current provider supports this node.
|
|
53
62
|
if (providerToFind.provider.isNodeSupported(currentNode)) {
|
|
63
|
+
// If provider supports the node, add it to the result map.
|
|
54
64
|
resultMap[providerToFind.provider.name].nodes.push(currentNode);
|
|
65
|
+
// Increment the count of found nodes for this provider.
|
|
55
66
|
providerToFind.foundNodes += 1;
|
|
67
|
+
|
|
68
|
+
// If the provider has found enough nodes, remove it from the lookup list.
|
|
56
69
|
if (providerToFind.foundNodes >= providerToFind.maxNodesToPrefetch) {
|
|
57
70
|
providersToLookup.splice(i, 1);
|
|
58
71
|
}
|
|
59
72
|
}
|
|
60
73
|
}
|
|
74
|
+
|
|
75
|
+
// If the current node has children, add them to the queue to be visited.
|
|
61
76
|
if (currentNode.content) {
|
|
62
77
|
nodesToVisit.push.apply(nodesToVisit, (0, _toConsumableArray2.default)(currentNode.content.filter(function (node) {
|
|
63
78
|
return node !== undefined;
|
|
64
79
|
})));
|
|
65
80
|
}
|
|
66
81
|
}
|
|
82
|
+
|
|
83
|
+
// Return an array of the found nodes, grouped by provider.
|
|
67
84
|
return Object.values(resultMap);
|
|
68
85
|
}
|
|
@@ -20,14 +20,28 @@ var _findNodesToPrefetch = require("./find-nodes-to-prefetch");
|
|
|
20
20
|
*/
|
|
21
21
|
/**
|
|
22
22
|
* Represents the aggregated SSR data for all node data providers.
|
|
23
|
-
*
|
|
24
|
-
*
|
|
23
|
+
* Each key is a provider's name, and the value contains the fetch status, duration,
|
|
24
|
+
* and a map of node data keys to their prefetched data.
|
|
25
25
|
*
|
|
26
26
|
* @example
|
|
27
|
+
* ```
|
|
27
28
|
* {
|
|
28
|
-
*
|
|
29
|
-
*
|
|
29
|
+
* mentionProvider: {
|
|
30
|
+
* success: true,
|
|
31
|
+
* duration: 220,
|
|
32
|
+
* data: {
|
|
33
|
+
* 'mention-1': { id: '1', name: 'John Doe' }
|
|
34
|
+
* }
|
|
35
|
+
* },
|
|
36
|
+
* emojiProvider: {
|
|
37
|
+
* success: true,
|
|
38
|
+
* duration: 110,
|
|
39
|
+
* data: {
|
|
40
|
+
* 'emoji-123': { shortName: ':smile:', representation: '😊' }
|
|
41
|
+
* }
|
|
42
|
+
* }
|
|
30
43
|
* }
|
|
44
|
+
* ```
|
|
31
45
|
*/
|
|
32
46
|
/**
|
|
33
47
|
* Fetches data for nodes in the document that are supported by the given providers.
|
|
@@ -49,7 +63,7 @@ var _findNodesToPrefetch = require("./find-nodes-to-prefetch");
|
|
|
49
63
|
* },
|
|
50
64
|
* ];
|
|
51
65
|
*
|
|
52
|
-
*
|
|
66
|
+
* const data = await prefetchNodeDataProvidersData({
|
|
53
67
|
* providers,
|
|
54
68
|
* doc,
|
|
55
69
|
* timeout: 1_000,
|
|
@@ -59,7 +73,7 @@ var _findNodesToPrefetch = require("./find-nodes-to-prefetch");
|
|
|
59
73
|
*
|
|
60
74
|
* @param props The properties for prefetching node data.
|
|
61
75
|
* @returns Record of provider names to their respective SSR data,
|
|
62
|
-
*
|
|
76
|
+
* success status, and duration of the fetch operation.
|
|
63
77
|
*/
|
|
64
78
|
function prefetchNodeDataProvidersData(_x) {
|
|
65
79
|
return _prefetchNodeDataProvidersData.apply(this, arguments);
|
|
@@ -101,39 +115,47 @@ function _prefetchNodeDataProvidersData() {
|
|
|
101
115
|
});
|
|
102
116
|
promises = nodesWithProviders.map( /*#__PURE__*/function () {
|
|
103
117
|
var _ref6 = (0, _asyncToGenerator2.default)( /*#__PURE__*/_regenerator.default.mark(function _callee(_ref5) {
|
|
104
|
-
var nodes, provider, timeout, timeoutPromise, data;
|
|
118
|
+
var nodes, provider, timeout, start, getDurationFromStart, timeoutPromise, data;
|
|
105
119
|
return _regenerator.default.wrap(function _callee$(_context) {
|
|
106
120
|
while (1) switch (_context.prev = _context.next) {
|
|
107
121
|
case 0:
|
|
122
|
+
getDurationFromStart = function _getDurationFromStart() {
|
|
123
|
+
return Math.min(performance.now() - start, timeout);
|
|
124
|
+
};
|
|
108
125
|
nodes = _ref5.nodes, provider = _ref5.provider, timeout = _ref5.timeout;
|
|
109
|
-
|
|
110
|
-
|
|
126
|
+
start = performance.now();
|
|
127
|
+
_context.prev = 3;
|
|
128
|
+
timeoutPromise = new Promise(function (_, reject) {
|
|
111
129
|
setTimeout(function () {
|
|
112
|
-
|
|
130
|
+
reject();
|
|
113
131
|
}, timeout);
|
|
114
132
|
});
|
|
115
|
-
_context.next =
|
|
133
|
+
_context.next = 7;
|
|
116
134
|
return Promise.race([provider.fetchNodesData(nodes), timeoutPromise]);
|
|
117
|
-
case
|
|
135
|
+
case 7:
|
|
118
136
|
data = _context.sent;
|
|
119
137
|
return _context.abrupt("return", {
|
|
120
138
|
provider: provider,
|
|
139
|
+
success: true,
|
|
140
|
+
duration: getDurationFromStart(),
|
|
121
141
|
nodes: nodes,
|
|
122
142
|
data: data
|
|
123
143
|
});
|
|
124
|
-
case
|
|
125
|
-
_context.prev =
|
|
126
|
-
_context.t0 = _context["catch"](
|
|
144
|
+
case 11:
|
|
145
|
+
_context.prev = 11;
|
|
146
|
+
_context.t0 = _context["catch"](3);
|
|
127
147
|
return _context.abrupt("return", {
|
|
128
148
|
provider: provider,
|
|
149
|
+
success: false,
|
|
150
|
+
duration: getDurationFromStart(),
|
|
129
151
|
nodes: nodes,
|
|
130
152
|
data: []
|
|
131
153
|
});
|
|
132
|
-
case
|
|
154
|
+
case 14:
|
|
133
155
|
case "end":
|
|
134
156
|
return _context.stop();
|
|
135
157
|
}
|
|
136
|
-
}, _callee, null, [[
|
|
158
|
+
}, _callee, null, [[3, 11]]);
|
|
137
159
|
}));
|
|
138
160
|
return function (_x2) {
|
|
139
161
|
return _ref6.apply(this, arguments);
|
|
@@ -145,14 +167,21 @@ function _prefetchNodeDataProvidersData() {
|
|
|
145
167
|
results = _context2.sent;
|
|
146
168
|
return _context2.abrupt("return", results.reduce(function (acc, _ref7) {
|
|
147
169
|
var provider = _ref7.provider,
|
|
170
|
+
success = _ref7.success,
|
|
171
|
+
duration = _ref7.duration,
|
|
148
172
|
nodes = _ref7.nodes,
|
|
149
173
|
data = _ref7.data;
|
|
150
|
-
|
|
174
|
+
var ssrData = data.reduce(function (providerSsrData, nodeData, nodeIndex) {
|
|
151
175
|
var node = nodes[nodeIndex];
|
|
152
176
|
var nodeDataKey = provider.nodeDataKey(node);
|
|
153
177
|
providerSsrData[nodeDataKey] = nodeData;
|
|
154
178
|
return providerSsrData;
|
|
155
179
|
}, {});
|
|
180
|
+
acc[provider.name] = {
|
|
181
|
+
data: ssrData,
|
|
182
|
+
success: success,
|
|
183
|
+
duration: duration
|
|
184
|
+
};
|
|
156
185
|
return acc;
|
|
157
186
|
}, {}));
|
|
158
187
|
case 9:
|
|
@@ -24,6 +24,11 @@ import { isSSR } from '@atlaskit/editor-common/core-utils';
|
|
|
24
24
|
* }
|
|
25
25
|
*/
|
|
26
26
|
|
|
27
|
+
/**
|
|
28
|
+
* Represents the payload passed to the callback function when data is fetched.
|
|
29
|
+
* It can either contain an error or the fetched data.
|
|
30
|
+
*/
|
|
31
|
+
|
|
27
32
|
/**
|
|
28
33
|
* A Node Data Provider is responsible for fetching and caching data associated with specific ProseMirror nodes.
|
|
29
34
|
* It supports a cache-first-then-network strategy, with initial data potentially provided via SSR.
|
|
@@ -78,13 +83,10 @@ export class NodeDataProvider {
|
|
|
78
83
|
*/
|
|
79
84
|
setSSRData(ssrData = {}) {
|
|
80
85
|
this.cacheVersion++;
|
|
81
|
-
this.cache = Object.entries(ssrData).
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
};
|
|
86
|
-
return acc;
|
|
87
|
-
}, {});
|
|
86
|
+
this.cache = Object.fromEntries(Object.entries(ssrData).map(([key, data]) => [key, {
|
|
87
|
+
data,
|
|
88
|
+
source: 'ssr'
|
|
89
|
+
}]));
|
|
88
90
|
}
|
|
89
91
|
|
|
90
92
|
/**
|
|
@@ -127,7 +129,7 @@ export class NodeDataProvider {
|
|
|
127
129
|
* ```
|
|
128
130
|
*
|
|
129
131
|
* @param node The node (or its ProseMirror representation) for which to fetch data.
|
|
130
|
-
* @param callback The function to call
|
|
132
|
+
* @param callback The callback function to call with the fetched data or an error.
|
|
131
133
|
*/
|
|
132
134
|
getData(node, callback) {
|
|
133
135
|
// Move implementation to a separate async method
|
|
@@ -135,53 +137,66 @@ export class NodeDataProvider {
|
|
|
135
137
|
void this.getDataAsync(node, callback);
|
|
136
138
|
}
|
|
137
139
|
async getDataAsync(node, callback) {
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
}
|
|
144
|
-
const dataKey = this.nodeDataKey(jsonNode);
|
|
145
|
-
const dataFromCache = this.cache[dataKey];
|
|
146
|
-
if (dataFromCache !== undefined) {
|
|
147
|
-
// If we have the data in the SSR data, we can use it directly
|
|
148
|
-
if (isPromise(dataFromCache.data)) {
|
|
149
|
-
callback(await dataFromCache.data);
|
|
150
|
-
} else {
|
|
151
|
-
callback(dataFromCache.data);
|
|
152
|
-
}
|
|
153
|
-
if (isSSR()) {
|
|
154
|
-
// If we are in SSR, we don't want to fetch the data again, as it is already available in the SSR data
|
|
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}.`);
|
|
155
145
|
return;
|
|
156
146
|
}
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
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)) {
|
|
152
|
+
callback({
|
|
153
|
+
data: await dataFromCache.data
|
|
154
|
+
});
|
|
155
|
+
} else {
|
|
156
|
+
callback({
|
|
157
|
+
data: dataFromCache.data
|
|
158
|
+
});
|
|
159
|
+
}
|
|
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
|
+
}
|
|
164
|
+
}
|
|
175
165
|
|
|
176
|
-
// If
|
|
177
|
-
//
|
|
178
|
-
if (
|
|
179
|
-
//
|
|
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;
|
|
172
|
+
const dataPromise = this.fetchNodesData([jsonNode]).then(([value]) => value);
|
|
173
|
+
// Store the promise in the cache to avoid multiple requests for the same data
|
|
180
174
|
this.cache[dataKey] = {
|
|
181
175
|
source: 'network',
|
|
182
|
-
data
|
|
176
|
+
data: dataPromise
|
|
183
177
|
};
|
|
178
|
+
const data = await dataPromise;
|
|
179
|
+
// We need to call the callback with the data with result even if the cache version has changed,
|
|
180
|
+
// so all promises that are waiting for the data can resolve.
|
|
181
|
+
callback({
|
|
182
|
+
data
|
|
183
|
+
});
|
|
184
|
+
|
|
185
|
+
// If the cache version has changed, we don't want to use the data from the network
|
|
186
|
+
// because it could be stale data.
|
|
187
|
+
if (cacheVersionBeforeRequest === this.cacheVersion) {
|
|
188
|
+
// Replace promise with the resolved data in the cache
|
|
189
|
+
this.cache[dataKey] = {
|
|
190
|
+
source: 'network',
|
|
191
|
+
data
|
|
192
|
+
};
|
|
193
|
+
}
|
|
184
194
|
}
|
|
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
|
+
});
|
|
185
200
|
}
|
|
186
201
|
}
|
|
187
202
|
|
|
@@ -204,7 +219,13 @@ export class NodeDataProvider {
|
|
|
204
219
|
getDataAsPromise_DO_NOT_USE_OUTSIDE_MIGRATIONS(node) {
|
|
205
220
|
return new Promise((resolve, reject) => {
|
|
206
221
|
try {
|
|
207
|
-
|
|
222
|
+
this.getData(node, payload => {
|
|
223
|
+
if (payload.error) {
|
|
224
|
+
reject(payload.error);
|
|
225
|
+
} else {
|
|
226
|
+
resolve(payload.data);
|
|
227
|
+
}
|
|
228
|
+
});
|
|
208
229
|
} catch (error) {
|
|
209
230
|
reject(error);
|
|
210
231
|
}
|
|
@@ -7,7 +7,9 @@
|
|
|
7
7
|
* @returns An array of objects, each containing a provider and the nodes that are supported by that provider.
|
|
8
8
|
*/
|
|
9
9
|
export function findNodesToPrefetch(doc, providers, maxNodesToVisit) {
|
|
10
|
+
// Counter for the total number of visited nodes to limit the traversal.
|
|
10
11
|
let totalVisitedNodesCount = 0;
|
|
12
|
+
// A map to store the results, with the provider name as the key.
|
|
11
13
|
const resultMap = providers.reduce((acc, {
|
|
12
14
|
provider
|
|
13
15
|
}) => {
|
|
@@ -21,6 +23,7 @@ export function findNodesToPrefetch(doc, providers, maxNodesToVisit) {
|
|
|
21
23
|
// It doesn't use `filter` function from `@atlaskit/adf-utils/traverse` because it does not support early stopping.
|
|
22
24
|
// We need to stop traversing when we reach the maximum number of nodes to visit to support large documents.
|
|
23
25
|
|
|
26
|
+
// Create a list of providers for which we still need to find nodes.
|
|
24
27
|
const providersToLookup = providers.filter(({
|
|
25
28
|
maxNodesToPrefetch
|
|
26
29
|
}) => maxNodesToPrefetch > 0).map(({
|
|
@@ -29,29 +32,43 @@ export function findNodesToPrefetch(doc, providers, maxNodesToVisit) {
|
|
|
29
32
|
}) => ({
|
|
30
33
|
provider,
|
|
31
34
|
maxNodesToPrefetch,
|
|
32
|
-
foundNodes: 0
|
|
35
|
+
foundNodes: 0 // Counter for nodes found for each provider.
|
|
33
36
|
}));
|
|
37
|
+
|
|
38
|
+
// Queue for the breadth-first search (BFS), starting with the root document node.
|
|
34
39
|
const nodesToVisit = [doc];
|
|
35
40
|
let currentIndex = 0;
|
|
41
|
+
// The loop continues as long as there are nodes to visit, providers to look for,
|
|
42
|
+
// and the visited nodes limit has not been reached.
|
|
36
43
|
while (currentIndex < nodesToVisit.length && providersToLookup.length > 0 && totalVisitedNodesCount < maxNodesToVisit) {
|
|
37
44
|
totalVisitedNodesCount += 1;
|
|
38
45
|
const currentNode = nodesToVisit[currentIndex];
|
|
39
46
|
currentIndex++;
|
|
40
47
|
|
|
41
|
-
// Using reverse loop to avoid issues with array mutation
|
|
48
|
+
// Using a reverse loop to avoid issues with array mutation (when removing elements).
|
|
42
49
|
for (let i = providersToLookup.length - 1; i >= 0; i--) {
|
|
43
50
|
const providerToFind = providersToLookup[i];
|
|
51
|
+
|
|
52
|
+
// Check if the current provider supports this node.
|
|
44
53
|
if (providerToFind.provider.isNodeSupported(currentNode)) {
|
|
54
|
+
// If provider supports the node, add it to the result map.
|
|
45
55
|
resultMap[providerToFind.provider.name].nodes.push(currentNode);
|
|
56
|
+
// Increment the count of found nodes for this provider.
|
|
46
57
|
providerToFind.foundNodes += 1;
|
|
58
|
+
|
|
59
|
+
// If the provider has found enough nodes, remove it from the lookup list.
|
|
47
60
|
if (providerToFind.foundNodes >= providerToFind.maxNodesToPrefetch) {
|
|
48
61
|
providersToLookup.splice(i, 1);
|
|
49
62
|
}
|
|
50
63
|
}
|
|
51
64
|
}
|
|
65
|
+
|
|
66
|
+
// If the current node has children, add them to the queue to be visited.
|
|
52
67
|
if (currentNode.content) {
|
|
53
68
|
nodesToVisit.push(...currentNode.content.filter(node => node !== undefined));
|
|
54
69
|
}
|
|
55
70
|
}
|
|
71
|
+
|
|
72
|
+
// Return an array of the found nodes, grouped by provider.
|
|
56
73
|
return Object.values(resultMap);
|
|
57
74
|
}
|