@atlaskit/editor-card-provider 6.3.4 → 6.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,23 @@
1
1
  # @atlaskit/editor-card-provider
2
2
 
3
+ ## 6.4.0
4
+
5
+ ### Minor Changes
6
+
7
+ - [`506d872ef2503`](https://bitbucket.org/atlassian/atlassian-frontend-monorepo/commits/506d872ef2503) -
8
+ Add smartlink response caching to browser storage to reduce layoutshift on transition and page
9
+ load
10
+
11
+ ### Patch Changes
12
+
13
+ - Updated dependencies
14
+
15
+ ## 6.3.5
16
+
17
+ ### Patch Changes
18
+
19
+ - Updated dependencies
20
+
3
21
  ## 6.3.4
4
22
 
5
23
  ### Patch Changes
package/dist/cjs/index.js CHANGED
@@ -9,10 +9,17 @@ Object.defineProperty(exports, "EditorCardProvider", {
9
9
  return _provider.EditorCardProvider;
10
10
  }
11
11
  });
12
+ Object.defineProperty(exports, "SmartCardLocalCacheClient", {
13
+ enumerable: true,
14
+ get: function get() {
15
+ return _smartCardLocalCacheClient.SmartCardLocalCacheClient;
16
+ }
17
+ });
12
18
  Object.defineProperty(exports, "editorCardProvider", {
13
19
  enumerable: true,
14
20
  get: function get() {
15
21
  return _provider.editorCardProvider;
16
22
  }
17
23
  });
18
- var _provider = require("./provider");
24
+ var _provider = require("./provider");
25
+ var _smartCardLocalCacheClient = require("./smart-card-local-cache-client");
@@ -13,6 +13,8 @@ var _classCallCheck2 = _interopRequireDefault(require("@babel/runtime/helpers/cl
13
13
  var _createClass2 = _interopRequireDefault(require("@babel/runtime/helpers/createClass"));
14
14
  var _possibleConstructorReturn2 = _interopRequireDefault(require("@babel/runtime/helpers/possibleConstructorReturn"));
15
15
  var _getPrototypeOf2 = _interopRequireDefault(require("@babel/runtime/helpers/getPrototypeOf"));
16
+ var _get2 = _interopRequireDefault(require("@babel/runtime/helpers/get"));
17
+ var _defineProperty2 = _interopRequireDefault(require("@babel/runtime/helpers/defineProperty"));
16
18
  var _inherits2 = _interopRequireDefault(require("@babel/runtime/helpers/inherits"));
17
19
  var _setPrototypeOf2 = _interopRequireDefault(require("@babel/runtime/helpers/setPrototypeOf"));
18
20
  var _linkExtractors = require("@atlaskit/link-extractors");
@@ -24,8 +26,10 @@ var _transformer = require("./transformer");
24
26
  var _linkProvider = require("@atlaskit/link-provider");
25
27
  var _platformFeatureFlags = require("@atlaskit/platform-feature-flags");
26
28
  var _api = require("./api");
29
+ var _smartCardLocalCacheClient = require("./smart-card-local-cache-client");
27
30
  function _callSuper(t, o, e) { return o = (0, _getPrototypeOf2.default)(o), (0, _possibleConstructorReturn2.default)(t, _isNativeReflectConstruct() ? Reflect.construct(o, e || [], (0, _getPrototypeOf2.default)(t).constructor) : o.apply(t, e)); }
28
31
  function _isNativeReflectConstruct() { try { var t = !Boolean.prototype.valueOf.call(Reflect.construct(Boolean, [], function () {})); } catch (t) {} return (_isNativeReflectConstruct = function _isNativeReflectConstruct() { return !!t; })(); }
32
+ function _superPropGet(t, o, e, r) { var p = (0, _get2.default)((0, _getPrototypeOf2.default)(1 & r ? t.prototype : t), o, e); return 2 & r && "function" == typeof p ? function (t) { return p.apply(e, t); } : p; }
29
33
  function _wrapRegExp() { _wrapRegExp = function _wrapRegExp(e, r) { return new BabelRegExp(e, void 0, r); }; var e = RegExp.prototype, r = new WeakMap(); function BabelRegExp(e, t, p) { var o = RegExp(e, t); return r.set(o, p || r.get(e)), (0, _setPrototypeOf2.default)(o, BabelRegExp.prototype); } function buildGroups(e, t) { var p = r.get(t); return Object.keys(p).reduce(function (r, t) { var o = p[t]; if ("number" == typeof o) r[t] = e[o];else { for (var i = 0; void 0 === e[o[i]] && i + 1 < o.length;) i++; r[t] = e[o[i]]; } return r; }, Object.create(null)); } return (0, _inherits2.default)(BabelRegExp, RegExp), BabelRegExp.prototype.exec = function (r) { var t = e.exec.call(this, r); if (t) { t.groups = buildGroups(t, this); var p = t.indices; p && (p.groups = buildGroups(p, this)); } return t; }, BabelRegExp.prototype[Symbol.replace] = function (t, p) { if ("string" == typeof p) { var o = r.get(this); return e[Symbol.replace].call(this, t, p.replace(/\$<([^>]+)(>|$)/g, function (e, r, t) { if ("" === t) return e; var p = o[r]; return Array.isArray(p) ? "$" + p.join("$") : "number" == typeof p ? "$" + p : ""; })); } if ("function" == typeof p) { var i = this; return e[Symbol.replace].call(this, t, function () { var e = arguments; return "object" != _typeof(e[e.length - 1]) && (e = [].slice.call(e)).push(buildGroups(e, i)), p.apply(this, e); }); } return e[Symbol.replace].call(this, t, p); }, _wrapRegExp.apply(this, arguments); } /* eslint-disable require-unicode-regexp,prefer-regex-literals */
30
34
  var BATCH_WAIT_TIME = 50;
31
35
 
@@ -137,6 +141,7 @@ var EditorCardProvider = exports.EditorCardProvider = /*#__PURE__*/function (_re
137
141
  var _this;
138
142
  (0, _classCallCheck2.default)(this, EditorCardProvider);
139
143
  _this = _callSuper(this, EditorCardProvider);
144
+ (0, _defineProperty2.default)(_this, "smartCardLocalCacheClient", new _smartCardLocalCacheClient.SmartCardLocalCacheClient());
140
145
  _this.name = 'editorCardProvider';
141
146
  _this.baseUrl = baseUrlOverride || (0, _linkingCommon.getBaseUrl)(envKey);
142
147
  _this.resolverUrl = (0, _linkingCommon.getResolverUrl)(envKey, baseUrlOverride);
@@ -160,6 +165,36 @@ var EditorCardProvider = exports.EditorCardProvider = /*#__PURE__*/function (_re
160
165
  }
161
166
  (0, _inherits2.default)(EditorCardProvider, _ref);
162
167
  return (0, _createClass2.default)(EditorCardProvider, [{
168
+ key: "getData",
169
+ value: function getData(node, callback) {
170
+ var _this2 = this;
171
+ if ((0, _expValEquals.expValEquals)('platform_editor_smartlink_local_cache', 'isEnabled', true) === false) {
172
+ // if local cache feature flag is disabled, fall back to the base implementation
173
+ return _superPropGet(EditorCardProvider, "getData", this, 3)([node, callback]);
174
+ }
175
+ var jsonNode = 'toJSON' in node ? node.toJSON() : node;
176
+ if (!this.isNodeSupported(jsonNode)) {
177
+ return;
178
+ }
179
+
180
+ // if we can load a response from cache, use it first
181
+ var key = this.nodeDataKey(jsonNode);
182
+ var details = this.smartCardLocalCacheClient.getItem(key);
183
+ if (details) {
184
+ callback({
185
+ data: details
186
+ });
187
+ }
188
+
189
+ // fetch the latest data async and update the cache
190
+ this.getDataAsync(node, function (payload) {
191
+ if (payload.data && !payload.error) {
192
+ _this2.smartCardLocalCacheClient.setItem(key, payload.data);
193
+ }
194
+ callback(payload);
195
+ });
196
+ }
197
+ }, {
163
198
  key: "nodeDataKey",
164
199
  value: function nodeDataKey(node) {
165
200
  // We can use URL as a key here, because CardClient returns the same data for the same URL
@@ -169,10 +204,10 @@ var EditorCardProvider = exports.EditorCardProvider = /*#__PURE__*/function (_re
169
204
  }, {
170
205
  key: "fetchNodesData",
171
206
  value: function fetchNodesData(nodes) {
172
- var _this2 = this;
207
+ var _this3 = this;
173
208
  var promises = nodes.map(function (node) {
174
209
  var url = node.attrs.url;
175
- return _this2.cardClient.fetchData(url);
210
+ return _this3.cardClient.fetchData(url);
176
211
  });
177
212
  return Promise.all(promises);
178
213
  }
@@ -396,7 +431,7 @@ var EditorCardProvider = exports.EditorCardProvider = /*#__PURE__*/function (_re
396
431
  value: function () {
397
432
  var _findUserPreference = (0, _asyncToGenerator2.default)( /*#__PURE__*/_regenerator.default.mark(function _callee7(url) {
398
433
  var _this$providersData,
399
- _this3 = this;
434
+ _this4 = this;
400
435
  var userPreferences, defaultAppearance, appearances, allMatchedLabeledAppearances, longestMatchedLabeledAppearance;
401
436
  return _regenerator.default.wrap(function _callee7$(_context7) {
402
437
  while (1) switch (_context7.prev = _context7.next) {
@@ -416,7 +451,7 @@ var EditorCardProvider = exports.EditorCardProvider = /*#__PURE__*/function (_re
416
451
  defaultAppearance = userPreferences.defaultAppearance, appearances = userPreferences.appearances;
417
452
  allMatchedLabeledAppearances = appearances.filter(function (_ref2) {
418
453
  var urlSegment = _ref2.urlSegment;
419
- return _this3.doesUrlMatchPath(urlSegment, url);
454
+ return _this4.doesUrlMatchPath(urlSegment, url);
420
455
  });
421
456
  if (!(allMatchedLabeledAppearances.length > 0)) {
422
457
  _context7.next = 10;
@@ -0,0 +1,86 @@
1
+ "use strict";
2
+
3
+ var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
4
+ Object.defineProperty(exports, "__esModule", {
5
+ value: true
6
+ });
7
+ exports.SmartCardLocalCacheClient = void 0;
8
+ var _createClass2 = _interopRequireDefault(require("@babel/runtime/helpers/createClass"));
9
+ var _classCallCheck2 = _interopRequireDefault(require("@babel/runtime/helpers/classCallCheck"));
10
+ var _defineProperty2 = _interopRequireDefault(require("@babel/runtime/helpers/defineProperty"));
11
+ var _storageClient = require("@atlaskit/frontend-utilities/storage-client");
12
+ var SMART_CARD_CACHE_CLIENT_KEY = 'smart-card-url-data';
13
+ var SMART_CARD_CACHE_KEY = 'response-cache';
14
+ var SMART_CARD_CACHE_DEFAULT_MAX_ITEMS = 100;
15
+ var SmartCardLocalCacheClient = exports.SmartCardLocalCacheClient = /*#__PURE__*/(0, _createClass2.default)(function SmartCardLocalCacheClient() {
16
+ var _this = this;
17
+ var maxItems = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : SMART_CARD_CACHE_DEFAULT_MAX_ITEMS;
18
+ (0, _classCallCheck2.default)(this, SmartCardLocalCacheClient);
19
+ (0, _defineProperty2.default)(this, "storageClient", new _storageClient.StorageClient(SMART_CARD_CACHE_CLIENT_KEY, {
20
+ storageEngine: 'sessionStorage'
21
+ }));
22
+ /**
23
+ * Retrieves the entire cache object from storage
24
+ * @returns The entire cache object
25
+ */
26
+ (0, _defineProperty2.default)(this, "getCache", function () {
27
+ var stored = _this.storageClient.getItem(SMART_CARD_CACHE_KEY, {
28
+ useExpiredItem: true
29
+ });
30
+ var persistentCache = stored ? JSON.parse(stored) : {};
31
+ return persistentCache;
32
+ });
33
+ /**
34
+ * Caches the SmartLinkResponse against the provided url in storage
35
+ * @param url The url (key) to store the response against
36
+ * @param response The SmartLinkResponse to be cached in storage
37
+ */
38
+ (0, _defineProperty2.default)(this, "setItem", function (url, response) {
39
+ var newCache = _this.getCache();
40
+
41
+ // Add or update the item in the cache with current timestamp
42
+ newCache[url] = {
43
+ data: response,
44
+ timestamp: Date.now()
45
+ };
46
+
47
+ // Apply FIFO behavior if cache exceeds maxItems
48
+ var cacheKeys = Object.keys(newCache);
49
+ if (cacheKeys.length > _this.maxItems) {
50
+ // Sort keys by timestamp (oldest first) and remove the oldest items
51
+ var sortedKeys = cacheKeys.sort(function (a, b) {
52
+ return newCache[a].timestamp - newCache[b].timestamp;
53
+ });
54
+ var itemsToRemove = cacheKeys.length - _this.maxItems;
55
+ var keysToRemove = sortedKeys.slice(0, itemsToRemove);
56
+ keysToRemove.forEach(function (key) {
57
+ delete newCache[key];
58
+ });
59
+ }
60
+ _this.storageClient.setItemWithExpiry(SMART_CARD_CACHE_KEY, JSON.stringify(newCache));
61
+ });
62
+ /**
63
+ * Retrieves the cached SmartLinkResponse for the provided url from storage (if it exists)
64
+ * @param url The url (key) to retrieve the cached response for
65
+ * @returns The cached SmartLinkResponse if it exists, else undefined
66
+ */
67
+ (0, _defineProperty2.default)(this, "getItem", function (url) {
68
+ var _persistentCache$url;
69
+ if (!url) {
70
+ return;
71
+ }
72
+ var persistentCache = _this.getCache();
73
+ var response = persistentCache === null || persistentCache === void 0 || (_persistentCache$url = persistentCache[url]) === null || _persistentCache$url === void 0 ? void 0 : _persistentCache$url.data;
74
+ return response;
75
+ });
76
+ /**
77
+ * Checks if a cached entry exists for the provided url in storage
78
+ * @param url The url (key) to check for existence in the cache
79
+ * @returns True if a cached entry exists, else false
80
+ */
81
+ (0, _defineProperty2.default)(this, "isUrlInCache", function (url) {
82
+ var persistentCache = _this.getCache();
83
+ return persistentCache ? url in persistentCache : false;
84
+ });
85
+ this.maxItems = maxItems;
86
+ });
@@ -1,4 +1,5 @@
1
1
  // Disable no-re-export rule for entry point files
2
2
  /* eslint-disable @atlaskit/editor/no-re-export */
3
3
 
4
- export { EditorCardProvider, editorCardProvider } from './provider';
4
+ export { EditorCardProvider, editorCardProvider } from './provider';
5
+ export { SmartCardLocalCacheClient } from './smart-card-local-cache-client';
@@ -1,3 +1,4 @@
1
+ import _defineProperty from "@babel/runtime/helpers/defineProperty";
1
2
  /* eslint-disable require-unicode-regexp,prefer-regex-literals */
2
3
 
3
4
  import { extractSmartLinkEmbed } from '@atlaskit/link-extractors';
@@ -9,6 +10,7 @@ import { Transformer } from './transformer';
9
10
  import { CardClient } from '@atlaskit/link-provider';
10
11
  import { fg } from '@atlaskit/platform-feature-flags';
11
12
  import { request } from './api';
13
+ import { SmartCardLocalCacheClient } from './smart-card-local-cache-client';
12
14
  const BATCH_WAIT_TIME = 50;
13
15
 
14
16
  // Check if it is matching a Jira Roadmaps or Jira Timeline url
@@ -70,6 +72,7 @@ export const isJiraWorkItem = url => /\/browse\/((?:\w+)-(?:\d+))/i.test(url);
70
72
  export class EditorCardProvider extends NodeDataProvider {
71
73
  constructor(envKey, baseUrlOverride, product, onResolve, customCardClient) {
72
74
  super();
75
+ _defineProperty(this, "smartCardLocalCacheClient", new SmartCardLocalCacheClient());
73
76
  this.name = 'editorCardProvider';
74
77
  this.baseUrl = baseUrlOverride || getBaseUrl(envKey);
75
78
  this.resolverUrl = getResolverUrl(envKey, baseUrlOverride);
@@ -86,6 +89,33 @@ export class EditorCardProvider extends NodeDataProvider {
86
89
  this.cardClient.setProduct(product);
87
90
  }
88
91
  }
92
+ getData(node, callback) {
93
+ if (expValEquals('platform_editor_smartlink_local_cache', 'isEnabled', true) === false) {
94
+ // if local cache feature flag is disabled, fall back to the base implementation
95
+ return super.getData(node, callback);
96
+ }
97
+ const jsonNode = 'toJSON' in node ? node.toJSON() : node;
98
+ if (!this.isNodeSupported(jsonNode)) {
99
+ return;
100
+ }
101
+
102
+ // if we can load a response from cache, use it first
103
+ const key = this.nodeDataKey(jsonNode);
104
+ const details = this.smartCardLocalCacheClient.getItem(key);
105
+ if (details) {
106
+ callback({
107
+ data: details
108
+ });
109
+ }
110
+
111
+ // fetch the latest data async and update the cache
112
+ this.getDataAsync(node, payload => {
113
+ if (payload.data && !payload.error) {
114
+ this.smartCardLocalCacheClient.setItem(key, payload.data);
115
+ }
116
+ callback(payload);
117
+ });
118
+ }
89
119
  nodeDataKey(node) {
90
120
  // We can use URL as a key here, because CardClient returns the same data for the same URL
91
121
  // regardless of the type of card (inline, block, embed).
@@ -0,0 +1,74 @@
1
+ import _defineProperty from "@babel/runtime/helpers/defineProperty";
2
+ import { StorageClient } from '@atlaskit/frontend-utilities/storage-client';
3
+ const SMART_CARD_CACHE_CLIENT_KEY = 'smart-card-url-data';
4
+ const SMART_CARD_CACHE_KEY = 'response-cache';
5
+ const SMART_CARD_CACHE_DEFAULT_MAX_ITEMS = 100;
6
+ export class SmartCardLocalCacheClient {
7
+ constructor(maxItems = SMART_CARD_CACHE_DEFAULT_MAX_ITEMS) {
8
+ _defineProperty(this, "storageClient", new StorageClient(SMART_CARD_CACHE_CLIENT_KEY, {
9
+ storageEngine: 'sessionStorage'
10
+ }));
11
+ /**
12
+ * Retrieves the entire cache object from storage
13
+ * @returns The entire cache object
14
+ */
15
+ _defineProperty(this, "getCache", () => {
16
+ const stored = this.storageClient.getItem(SMART_CARD_CACHE_KEY, {
17
+ useExpiredItem: true
18
+ });
19
+ const persistentCache = stored ? JSON.parse(stored) : {};
20
+ return persistentCache;
21
+ });
22
+ /**
23
+ * Caches the SmartLinkResponse against the provided url in storage
24
+ * @param url The url (key) to store the response against
25
+ * @param response The SmartLinkResponse to be cached in storage
26
+ */
27
+ _defineProperty(this, "setItem", (url, response) => {
28
+ const newCache = this.getCache();
29
+
30
+ // Add or update the item in the cache with current timestamp
31
+ newCache[url] = {
32
+ data: response,
33
+ timestamp: Date.now()
34
+ };
35
+
36
+ // Apply FIFO behavior if cache exceeds maxItems
37
+ const cacheKeys = Object.keys(newCache);
38
+ if (cacheKeys.length > this.maxItems) {
39
+ // Sort keys by timestamp (oldest first) and remove the oldest items
40
+ const sortedKeys = cacheKeys.sort((a, b) => newCache[a].timestamp - newCache[b].timestamp);
41
+ const itemsToRemove = cacheKeys.length - this.maxItems;
42
+ const keysToRemove = sortedKeys.slice(0, itemsToRemove);
43
+ keysToRemove.forEach(key => {
44
+ delete newCache[key];
45
+ });
46
+ }
47
+ this.storageClient.setItemWithExpiry(SMART_CARD_CACHE_KEY, JSON.stringify(newCache));
48
+ });
49
+ /**
50
+ * Retrieves the cached SmartLinkResponse for the provided url from storage (if it exists)
51
+ * @param url The url (key) to retrieve the cached response for
52
+ * @returns The cached SmartLinkResponse if it exists, else undefined
53
+ */
54
+ _defineProperty(this, "getItem", url => {
55
+ var _persistentCache$url;
56
+ if (!url) {
57
+ return;
58
+ }
59
+ const persistentCache = this.getCache();
60
+ const response = persistentCache === null || persistentCache === void 0 ? void 0 : (_persistentCache$url = persistentCache[url]) === null || _persistentCache$url === void 0 ? void 0 : _persistentCache$url.data;
61
+ return response;
62
+ });
63
+ /**
64
+ * Checks if a cached entry exists for the provided url in storage
65
+ * @param url The url (key) to check for existence in the cache
66
+ * @returns True if a cached entry exists, else false
67
+ */
68
+ _defineProperty(this, "isUrlInCache", url => {
69
+ const persistentCache = this.getCache();
70
+ return persistentCache ? url in persistentCache : false;
71
+ });
72
+ this.maxItems = maxItems;
73
+ }
74
+ }
package/dist/esm/index.js CHANGED
@@ -1,4 +1,5 @@
1
1
  // Disable no-re-export rule for entry point files
2
2
  /* eslint-disable @atlaskit/editor/no-re-export */
3
3
 
4
- export { EditorCardProvider, editorCardProvider } from './provider';
4
+ export { EditorCardProvider, editorCardProvider } from './provider';
5
+ export { SmartCardLocalCacheClient } from './smart-card-local-cache-client';
@@ -5,11 +5,14 @@ import _classCallCheck from "@babel/runtime/helpers/classCallCheck";
5
5
  import _createClass from "@babel/runtime/helpers/createClass";
6
6
  import _possibleConstructorReturn from "@babel/runtime/helpers/possibleConstructorReturn";
7
7
  import _getPrototypeOf from "@babel/runtime/helpers/getPrototypeOf";
8
+ import _get from "@babel/runtime/helpers/get";
9
+ import _defineProperty from "@babel/runtime/helpers/defineProperty";
8
10
  import _inherits from "@babel/runtime/helpers/inherits";
9
11
  import _setPrototypeOf from "@babel/runtime/helpers/setPrototypeOf";
10
12
  import _regeneratorRuntime from "@babel/runtime/regenerator";
11
13
  function _callSuper(t, o, e) { return o = _getPrototypeOf(o), _possibleConstructorReturn(t, _isNativeReflectConstruct() ? Reflect.construct(o, e || [], _getPrototypeOf(t).constructor) : o.apply(t, e)); }
12
14
  function _isNativeReflectConstruct() { try { var t = !Boolean.prototype.valueOf.call(Reflect.construct(Boolean, [], function () {})); } catch (t) {} return (_isNativeReflectConstruct = function _isNativeReflectConstruct() { return !!t; })(); }
15
+ function _superPropGet(t, o, e, r) { var p = _get(_getPrototypeOf(1 & r ? t.prototype : t), o, e); return 2 & r && "function" == typeof p ? function (t) { return p.apply(e, t); } : p; }
13
16
  function _wrapRegExp() { _wrapRegExp = function _wrapRegExp(e, r) { return new BabelRegExp(e, void 0, r); }; var e = RegExp.prototype, r = new WeakMap(); function BabelRegExp(e, t, p) { var o = RegExp(e, t); return r.set(o, p || r.get(e)), _setPrototypeOf(o, BabelRegExp.prototype); } function buildGroups(e, t) { var p = r.get(t); return Object.keys(p).reduce(function (r, t) { var o = p[t]; if ("number" == typeof o) r[t] = e[o];else { for (var i = 0; void 0 === e[o[i]] && i + 1 < o.length;) i++; r[t] = e[o[i]]; } return r; }, Object.create(null)); } return _inherits(BabelRegExp, RegExp), BabelRegExp.prototype.exec = function (r) { var t = e.exec.call(this, r); if (t) { t.groups = buildGroups(t, this); var p = t.indices; p && (p.groups = buildGroups(p, this)); } return t; }, BabelRegExp.prototype[Symbol.replace] = function (t, p) { if ("string" == typeof p) { var o = r.get(this); return e[Symbol.replace].call(this, t, p.replace(/\$<([^>]+)(>|$)/g, function (e, r, t) { if ("" === t) return e; var p = o[r]; return Array.isArray(p) ? "$" + p.join("$") : "number" == typeof p ? "$" + p : ""; })); } if ("function" == typeof p) { var i = this; return e[Symbol.replace].call(this, t, function () { var e = arguments; return "object" != _typeof(e[e.length - 1]) && (e = [].slice.call(e)).push(buildGroups(e, i)), p.apply(this, e); }); } return e[Symbol.replace].call(this, t, p); }, _wrapRegExp.apply(this, arguments); }
14
17
  /* eslint-disable require-unicode-regexp,prefer-regex-literals */
15
18
 
@@ -22,6 +25,7 @@ import { Transformer } from './transformer';
22
25
  import { CardClient } from '@atlaskit/link-provider';
23
26
  import { fg } from '@atlaskit/platform-feature-flags';
24
27
  import { request } from './api';
28
+ import { SmartCardLocalCacheClient } from './smart-card-local-cache-client';
25
29
  var BATCH_WAIT_TIME = 50;
26
30
 
27
31
  // Check if it is matching a Jira Roadmaps or Jira Timeline url
@@ -132,6 +136,7 @@ export var EditorCardProvider = /*#__PURE__*/function (_ref) {
132
136
  var _this;
133
137
  _classCallCheck(this, EditorCardProvider);
134
138
  _this = _callSuper(this, EditorCardProvider);
139
+ _defineProperty(_this, "smartCardLocalCacheClient", new SmartCardLocalCacheClient());
135
140
  _this.name = 'editorCardProvider';
136
141
  _this.baseUrl = baseUrlOverride || getBaseUrl(envKey);
137
142
  _this.resolverUrl = getResolverUrl(envKey, baseUrlOverride);
@@ -155,6 +160,36 @@ export var EditorCardProvider = /*#__PURE__*/function (_ref) {
155
160
  }
156
161
  _inherits(EditorCardProvider, _ref);
157
162
  return _createClass(EditorCardProvider, [{
163
+ key: "getData",
164
+ value: function getData(node, callback) {
165
+ var _this2 = this;
166
+ if (expValEquals('platform_editor_smartlink_local_cache', 'isEnabled', true) === false) {
167
+ // if local cache feature flag is disabled, fall back to the base implementation
168
+ return _superPropGet(EditorCardProvider, "getData", this, 3)([node, callback]);
169
+ }
170
+ var jsonNode = 'toJSON' in node ? node.toJSON() : node;
171
+ if (!this.isNodeSupported(jsonNode)) {
172
+ return;
173
+ }
174
+
175
+ // if we can load a response from cache, use it first
176
+ var key = this.nodeDataKey(jsonNode);
177
+ var details = this.smartCardLocalCacheClient.getItem(key);
178
+ if (details) {
179
+ callback({
180
+ data: details
181
+ });
182
+ }
183
+
184
+ // fetch the latest data async and update the cache
185
+ this.getDataAsync(node, function (payload) {
186
+ if (payload.data && !payload.error) {
187
+ _this2.smartCardLocalCacheClient.setItem(key, payload.data);
188
+ }
189
+ callback(payload);
190
+ });
191
+ }
192
+ }, {
158
193
  key: "nodeDataKey",
159
194
  value: function nodeDataKey(node) {
160
195
  // We can use URL as a key here, because CardClient returns the same data for the same URL
@@ -164,10 +199,10 @@ export var EditorCardProvider = /*#__PURE__*/function (_ref) {
164
199
  }, {
165
200
  key: "fetchNodesData",
166
201
  value: function fetchNodesData(nodes) {
167
- var _this2 = this;
202
+ var _this3 = this;
168
203
  var promises = nodes.map(function (node) {
169
204
  var url = node.attrs.url;
170
- return _this2.cardClient.fetchData(url);
205
+ return _this3.cardClient.fetchData(url);
171
206
  });
172
207
  return Promise.all(promises);
173
208
  }
@@ -391,7 +426,7 @@ export var EditorCardProvider = /*#__PURE__*/function (_ref) {
391
426
  value: function () {
392
427
  var _findUserPreference = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee7(url) {
393
428
  var _this$providersData,
394
- _this3 = this;
429
+ _this4 = this;
395
430
  var userPreferences, defaultAppearance, appearances, allMatchedLabeledAppearances, longestMatchedLabeledAppearance;
396
431
  return _regeneratorRuntime.wrap(function _callee7$(_context7) {
397
432
  while (1) switch (_context7.prev = _context7.next) {
@@ -411,7 +446,7 @@ export var EditorCardProvider = /*#__PURE__*/function (_ref) {
411
446
  defaultAppearance = userPreferences.defaultAppearance, appearances = userPreferences.appearances;
412
447
  allMatchedLabeledAppearances = appearances.filter(function (_ref2) {
413
448
  var urlSegment = _ref2.urlSegment;
414
- return _this3.doesUrlMatchPath(urlSegment, url);
449
+ return _this4.doesUrlMatchPath(urlSegment, url);
415
450
  });
416
451
  if (!(allMatchedLabeledAppearances.length > 0)) {
417
452
  _context7.next = 10;
@@ -0,0 +1,79 @@
1
+ import _createClass from "@babel/runtime/helpers/createClass";
2
+ import _classCallCheck from "@babel/runtime/helpers/classCallCheck";
3
+ import _defineProperty from "@babel/runtime/helpers/defineProperty";
4
+ import { StorageClient } from '@atlaskit/frontend-utilities/storage-client';
5
+ var SMART_CARD_CACHE_CLIENT_KEY = 'smart-card-url-data';
6
+ var SMART_CARD_CACHE_KEY = 'response-cache';
7
+ var SMART_CARD_CACHE_DEFAULT_MAX_ITEMS = 100;
8
+ export var SmartCardLocalCacheClient = /*#__PURE__*/_createClass(function SmartCardLocalCacheClient() {
9
+ var _this = this;
10
+ var maxItems = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : SMART_CARD_CACHE_DEFAULT_MAX_ITEMS;
11
+ _classCallCheck(this, SmartCardLocalCacheClient);
12
+ _defineProperty(this, "storageClient", new StorageClient(SMART_CARD_CACHE_CLIENT_KEY, {
13
+ storageEngine: 'sessionStorage'
14
+ }));
15
+ /**
16
+ * Retrieves the entire cache object from storage
17
+ * @returns The entire cache object
18
+ */
19
+ _defineProperty(this, "getCache", function () {
20
+ var stored = _this.storageClient.getItem(SMART_CARD_CACHE_KEY, {
21
+ useExpiredItem: true
22
+ });
23
+ var persistentCache = stored ? JSON.parse(stored) : {};
24
+ return persistentCache;
25
+ });
26
+ /**
27
+ * Caches the SmartLinkResponse against the provided url in storage
28
+ * @param url The url (key) to store the response against
29
+ * @param response The SmartLinkResponse to be cached in storage
30
+ */
31
+ _defineProperty(this, "setItem", function (url, response) {
32
+ var newCache = _this.getCache();
33
+
34
+ // Add or update the item in the cache with current timestamp
35
+ newCache[url] = {
36
+ data: response,
37
+ timestamp: Date.now()
38
+ };
39
+
40
+ // Apply FIFO behavior if cache exceeds maxItems
41
+ var cacheKeys = Object.keys(newCache);
42
+ if (cacheKeys.length > _this.maxItems) {
43
+ // Sort keys by timestamp (oldest first) and remove the oldest items
44
+ var sortedKeys = cacheKeys.sort(function (a, b) {
45
+ return newCache[a].timestamp - newCache[b].timestamp;
46
+ });
47
+ var itemsToRemove = cacheKeys.length - _this.maxItems;
48
+ var keysToRemove = sortedKeys.slice(0, itemsToRemove);
49
+ keysToRemove.forEach(function (key) {
50
+ delete newCache[key];
51
+ });
52
+ }
53
+ _this.storageClient.setItemWithExpiry(SMART_CARD_CACHE_KEY, JSON.stringify(newCache));
54
+ });
55
+ /**
56
+ * Retrieves the cached SmartLinkResponse for the provided url from storage (if it exists)
57
+ * @param url The url (key) to retrieve the cached response for
58
+ * @returns The cached SmartLinkResponse if it exists, else undefined
59
+ */
60
+ _defineProperty(this, "getItem", function (url) {
61
+ var _persistentCache$url;
62
+ if (!url) {
63
+ return;
64
+ }
65
+ var persistentCache = _this.getCache();
66
+ var response = persistentCache === null || persistentCache === void 0 || (_persistentCache$url = persistentCache[url]) === null || _persistentCache$url === void 0 ? void 0 : _persistentCache$url.data;
67
+ return response;
68
+ });
69
+ /**
70
+ * Checks if a cached entry exists for the provided url in storage
71
+ * @param url The url (key) to check for existence in the cache
72
+ * @returns True if a cached entry exists, else false
73
+ */
74
+ _defineProperty(this, "isUrlInCache", function (url) {
75
+ var persistentCache = _this.getCache();
76
+ return persistentCache ? url in persistentCache : false;
77
+ });
78
+ this.maxItems = maxItems;
79
+ });
@@ -1 +1,2 @@
1
1
  export { EditorCardProvider, editorCardProvider } from './provider';
2
+ export { SmartCardLocalCacheClient } from './smart-card-local-cache-client';
@@ -1,4 +1,6 @@
1
1
  import type { JSONNode } from '@atlaskit/editor-json-transformer';
2
+ import type { CallbackPayload } from '@atlaskit/node-data-provider';
3
+ import type { Node as PMNode } from '@atlaskit/editor-prosemirror/model';
2
4
  import { NodeDataProvider } from '@atlaskit/node-data-provider';
3
5
  import { type BlockCardAdf, type EmbedCardAdf, type CardAdf, type InlineCardAdf, type CardAppearance, type DatasourceAdf, type ProductType, type EnvironmentsKeys } from '@atlaskit/linking-common';
4
6
  import { type CardProvider } from './types';
@@ -15,8 +17,10 @@ export declare class EditorCardProvider extends NodeDataProvider<CardNode, JsonL
15
17
  private transformer;
16
18
  private providersLoader;
17
19
  private cardClient;
20
+ private smartCardLocalCacheClient;
18
21
  private onResolve;
19
22
  constructor(envKey?: EnvironmentsKeys, baseUrlOverride?: string, product?: ProductType, onResolve?: (url: string) => void, customCardClient?: CardClient);
23
+ getData(node: CardNode | PMNode, callback: (payload: CallbackPayload<JsonLd.Response>) => void): void;
20
24
  nodeDataKey(node: CardNode): string;
21
25
  fetchNodesData(nodes: CardNode[]): Promise<JsonLd.Response[]>;
22
26
  isNodeSupported(node: JSONNode): node is CardNode;
@@ -0,0 +1,34 @@
1
+ import type { SmartLinkResponse } from '@atlaskit/linking-types';
2
+ type CacheEntry = {
3
+ data: SmartLinkResponse;
4
+ timestamp: number;
5
+ };
6
+ export declare class SmartCardLocalCacheClient {
7
+ private readonly storageClient;
8
+ private maxItems;
9
+ constructor(maxItems?: number);
10
+ /**
11
+ * Retrieves the entire cache object from storage
12
+ * @returns The entire cache object
13
+ */
14
+ getCache: () => Record<string, CacheEntry>;
15
+ /**
16
+ * Caches the SmartLinkResponse against the provided url in storage
17
+ * @param url The url (key) to store the response against
18
+ * @param response The SmartLinkResponse to be cached in storage
19
+ */
20
+ setItem: (url: string, response: SmartLinkResponse) => void;
21
+ /**
22
+ * Retrieves the cached SmartLinkResponse for the provided url from storage (if it exists)
23
+ * @param url The url (key) to retrieve the cached response for
24
+ * @returns The cached SmartLinkResponse if it exists, else undefined
25
+ */
26
+ getItem: (url: string | undefined) => SmartLinkResponse | undefined;
27
+ /**
28
+ * Checks if a cached entry exists for the provided url in storage
29
+ * @param url The url (key) to check for existence in the cache
30
+ * @returns True if a cached entry exists, else false
31
+ */
32
+ isUrlInCache: (url: string) => boolean;
33
+ }
34
+ export {};
@@ -1 +1,2 @@
1
1
  export { EditorCardProvider, editorCardProvider } from './provider';
2
+ export { SmartCardLocalCacheClient } from './smart-card-local-cache-client';
@@ -1,4 +1,6 @@
1
1
  import type { JSONNode } from '@atlaskit/editor-json-transformer';
2
+ import type { CallbackPayload } from '@atlaskit/node-data-provider';
3
+ import type { Node as PMNode } from '@atlaskit/editor-prosemirror/model';
2
4
  import { NodeDataProvider } from '@atlaskit/node-data-provider';
3
5
  import { type BlockCardAdf, type EmbedCardAdf, type CardAdf, type InlineCardAdf, type CardAppearance, type DatasourceAdf, type ProductType, type EnvironmentsKeys } from '@atlaskit/linking-common';
4
6
  import { type CardProvider } from './types';
@@ -15,8 +17,10 @@ export declare class EditorCardProvider extends NodeDataProvider<CardNode, JsonL
15
17
  private transformer;
16
18
  private providersLoader;
17
19
  private cardClient;
20
+ private smartCardLocalCacheClient;
18
21
  private onResolve;
19
22
  constructor(envKey?: EnvironmentsKeys, baseUrlOverride?: string, product?: ProductType, onResolve?: (url: string) => void, customCardClient?: CardClient);
23
+ getData(node: CardNode | PMNode, callback: (payload: CallbackPayload<JsonLd.Response>) => void): void;
20
24
  nodeDataKey(node: CardNode): string;
21
25
  fetchNodesData(nodes: CardNode[]): Promise<JsonLd.Response[]>;
22
26
  isNodeSupported(node: JSONNode): node is CardNode;
@@ -0,0 +1,34 @@
1
+ import type { SmartLinkResponse } from '@atlaskit/linking-types';
2
+ type CacheEntry = {
3
+ data: SmartLinkResponse;
4
+ timestamp: number;
5
+ };
6
+ export declare class SmartCardLocalCacheClient {
7
+ private readonly storageClient;
8
+ private maxItems;
9
+ constructor(maxItems?: number);
10
+ /**
11
+ * Retrieves the entire cache object from storage
12
+ * @returns The entire cache object
13
+ */
14
+ getCache: () => Record<string, CacheEntry>;
15
+ /**
16
+ * Caches the SmartLinkResponse against the provided url in storage
17
+ * @param url The url (key) to store the response against
18
+ * @param response The SmartLinkResponse to be cached in storage
19
+ */
20
+ setItem: (url: string, response: SmartLinkResponse) => void;
21
+ /**
22
+ * Retrieves the cached SmartLinkResponse for the provided url from storage (if it exists)
23
+ * @param url The url (key) to retrieve the cached response for
24
+ * @returns The cached SmartLinkResponse if it exists, else undefined
25
+ */
26
+ getItem: (url: string | undefined) => SmartLinkResponse | undefined;
27
+ /**
28
+ * Checks if a cached entry exists for the provided url in storage
29
+ * @param url The url (key) to check for existence in the cache
30
+ * @returns True if a cached entry exists, else false
31
+ */
32
+ isUrlInCache: (url: string) => boolean;
33
+ }
34
+ export {};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@atlaskit/editor-card-provider",
3
- "version": "6.3.4",
3
+ "version": "6.4.0",
4
4
  "description": "Contains the EditorCardProvider, for determining smart card behaviour in the editor.",
5
5
  "author": "Atlassian Pty Ltd",
6
6
  "license": "Apache-2.0",
@@ -31,14 +31,15 @@
31
31
  "dependencies": {
32
32
  "@atlaskit/editor-json-transformer": "^8.31.0",
33
33
  "@atlaskit/feature-gate-js-client": "^5.5.0",
34
+ "@atlaskit/frontend-utilities": "^3.2.0",
34
35
  "@atlaskit/json-ld-types": "^1.4.0",
35
36
  "@atlaskit/link-client-extension": "^6.0.0",
36
37
  "@atlaskit/link-extractors": "^2.4.0",
37
38
  "@atlaskit/linking-common": "^9.9.0",
38
39
  "@atlaskit/linking-types": "^14.2.0",
39
- "@atlaskit/node-data-provider": "^8.1.0",
40
+ "@atlaskit/node-data-provider": "^8.3.0",
40
41
  "@atlaskit/platform-feature-flags": "^1.1.0",
41
- "@atlaskit/tmp-editor-statsig": "^20.0.0",
42
+ "@atlaskit/tmp-editor-statsig": "^21.2.0",
42
43
  "@babel/runtime": "^7.0.0",
43
44
  "dataloader": "^2.1.0"
44
45
  },