@atlaskit/editor-plugin-limited-mode 4.0.4 → 4.0.6

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,20 @@
1
1
  # @atlaskit/editor-plugin-limited-mode
2
2
 
3
+ ## 4.0.6
4
+
5
+ ### Patch Changes
6
+
7
+ - [`49dad8567c387`](https://bitbucket.org/atlassian/atlassian-frontend-monorepo/commits/49dad8567c387) -
8
+ EDITOR-4948 - Change performance mode threshold condition to include doc size, node count, and LCM
9
+ check.
10
+ - Updated dependencies
11
+
12
+ ## 4.0.5
13
+
14
+ ### Patch Changes
15
+
16
+ - Updated dependencies
17
+
3
18
  ## 4.0.4
4
19
 
5
20
  ### Patch Changes
@@ -10,36 +10,98 @@ var _expVal = require("@atlaskit/tmp-editor-statsig/expVal");
10
10
  var limitedModePluginKey = exports.limitedModePluginKey = new _state.PluginKey('limitedModePlugin');
11
11
  var LIMITED_MODE_NODE_SIZE_THRESHOLD = 40000;
12
12
  /**
13
- * Counts nodes in the document.
14
- *
15
- * Note: legacy-content macros add a damped contribution based on ADF length to avoid
16
- * parsing nested ADF on every check, which is inefficient.
13
+ * Gets a numeric experiment param, returning undefined if the value is not a valid number.
14
+ * This guards against test overrides returning booleans or strings for numeric params.
17
15
  */
18
- var countNodesInDoc = function countNodesInDoc(doc, lcmDampingFactor) {
19
- var nodeCount = 0;
16
+ var getNumericExperimentParam = function getNumericExperimentParam(paramName, fallbackValue) {
17
+ var rawValue = (0, _expVal.expVal)('cc_editor_limited_mode_expanded', paramName, fallbackValue);
18
+ if (typeof rawValue === 'number') {
19
+ return rawValue;
20
+ }
21
+
22
+ // Handle string values from test overrides
23
+ if (typeof rawValue === 'string') {
24
+ var parsed = parseInt(rawValue, 10);
25
+ if (!isNaN(parsed)) {
26
+ return parsed;
27
+ }
28
+ }
29
+ return undefined;
30
+ };
31
+
32
+ /**
33
+ * Calculates custom document size including LCM ADF lengths (for non-expanded path).
34
+ * This function can be removed when cc_editor_limited_mode_expanded is cleaned up.
35
+ */
36
+ var getCustomDocSize = function getCustomDocSize(doc) {
37
+ var lcmAdfLength = 0;
20
38
  doc.descendants(function (node) {
21
39
  var _node$attrs;
22
- nodeCount += 1;
23
40
  if (((_node$attrs = node.attrs) === null || _node$attrs === void 0 ? void 0 : _node$attrs.extensionKey) === 'legacy-content') {
24
- var _node$attrs2;
25
- var adfLength = (_node$attrs2 = node.attrs) === null || _node$attrs2 === void 0 || (_node$attrs2 = _node$attrs2.parameters) === null || _node$attrs2 === void 0 || (_node$attrs2 = _node$attrs2.adf) === null || _node$attrs2 === void 0 ? void 0 : _node$attrs2.length;
26
- if (typeof adfLength === 'number' && lcmDampingFactor > 0) {
27
- nodeCount += Math.ceil(adfLength / lcmDampingFactor);
28
- }
41
+ var _node$attrs$parameter, _node$attrs2;
42
+ lcmAdfLength += (_node$attrs$parameter = (_node$attrs2 = node.attrs) === null || _node$attrs2 === void 0 || (_node$attrs2 = _node$attrs2.parameters) === null || _node$attrs2 === void 0 || (_node$attrs2 = _node$attrs2.adf) === null || _node$attrs2 === void 0 ? void 0 : _node$attrs2.length) !== null && _node$attrs$parameter !== void 0 ? _node$attrs$parameter : 0;
29
43
  }
30
44
  });
31
- return nodeCount;
45
+ return doc.nodeSize + lcmAdfLength;
32
46
  };
33
47
 
34
48
  /**
35
- * Guard against test overrides returning booleans for numeric params.
49
+ * Determines whether limited mode should be enabled under the expanded gate.
50
+ * If this logic changes, update the duplicate in `editor-common/src/node-anchor/node-anchor-provider.ts` to avoid drift.
51
+ *
52
+ * Limited mode is activated when ANY of the following conditions are met:
53
+ * 1. Document size exceeds `docSizeThreshold` (if defined)
54
+ * 2. Node count exceeds `nodeCountThreshold` (if defined)
55
+ * 3. Document contains a legacy-content macro (LCM) (if `includeLcmInThreshold` is true)
56
+ *
57
+ * Performance optimisations:
58
+ * - Doc size is checked first (O(1)) - if it exceeds threshold, we skip traversal entirely.
59
+ * - If `includeLcmInThreshold` is enabled and we find an LCM, we exit traversal early
60
+ * since we already know limited mode will be enabled.
61
+ * - If neither node count nor LCM conditions are configured, we skip traversal entirely.
36
62
  */
37
- var getNumericExperimentParam = function getNumericExperimentParam(experimentName, paramName, fallbackValue) {
38
- var rawValue = (0, _expVal.expVal)(experimentName, paramName, fallbackValue);
39
- if (typeof rawValue === 'number') {
40
- return rawValue;
63
+ var shouldEnableLimitedModeExpanded = function shouldEnableLimitedModeExpanded(doc) {
64
+ var nodeCountThreshold = getNumericExperimentParam('nodeCountThreshold', 5000);
65
+ var docSizeThreshold = getNumericExperimentParam('docSizeThreshold', 30000);
66
+ var includeLcmInThreshold = Boolean((0, _expVal.expVal)('cc_editor_limited_mode_expanded', 'includeLcmInThreshold', false));
67
+
68
+ // Early exit: doc size exceeds threshold - O(1), no traversal needed
69
+ if (docSizeThreshold !== undefined && doc.nodeSize > docSizeThreshold) {
70
+ return true;
71
+ }
72
+
73
+ // Early exit: no traversal needed if neither condition is configured
74
+ var needNodeCount = nodeCountThreshold !== undefined;
75
+ if (!needNodeCount && !includeLcmInThreshold) {
76
+ return false;
77
+ }
78
+
79
+ // Single traversal for node count and/or LCM detection
80
+ var nodeCount = 0;
81
+ var hasLcm = false;
82
+ doc.descendants(function (node) {
83
+ var _node$attrs3;
84
+ nodeCount += 1;
85
+ if (((_node$attrs3 = node.attrs) === null || _node$attrs3 === void 0 ? void 0 : _node$attrs3.extensionKey) === 'legacy-content') {
86
+ hasLcm = true;
87
+
88
+ // Early exit: LCM found and condition is enabled - no need to continue counting
89
+ if (includeLcmInThreshold) {
90
+ return false;
91
+ }
92
+ }
93
+ });
94
+
95
+ // LCM condition takes precedence (if we early exited traversal, this is why)
96
+ if (includeLcmInThreshold && hasLcm) {
97
+ return true;
98
+ }
99
+
100
+ // Check node count threshold
101
+ if (needNodeCount && nodeCount > nodeCountThreshold) {
102
+ return true;
41
103
  }
42
- return fallbackValue;
104
+ return false;
43
105
  };
44
106
  var createPlugin = exports.createPlugin = function createPlugin() {
45
107
  return new _safePlugin.SafePlugin({
@@ -50,24 +112,13 @@ var createPlugin = exports.createPlugin = function createPlugin() {
50
112
  state: {
51
113
  init: function init(config, editorState) {
52
114
  if ((0, _expVal.expVal)('cc_editor_limited_mode_expanded', 'isEnabled', false)) {
53
- var lcmNodeCountDampingFactor = getNumericExperimentParam('cc_editor_limited_mode_expanded', 'lcmNodeCountDampingFactor', 10);
54
- var nodeCountThreshold = getNumericExperimentParam('cc_editor_limited_mode_expanded', 'nodeCountThreshold', 1000);
55
- var nodeCount = countNodesInDoc(editorState.doc, lcmNodeCountDampingFactor);
56
115
  return {
57
- documentSizeBreachesThreshold: nodeCount > nodeCountThreshold
116
+ documentSizeBreachesThreshold: shouldEnableLimitedModeExpanded(editorState.doc)
58
117
  };
59
118
  } else {
60
119
  // calculates the size of the doc, where when there are legacy content macros, the content
61
120
  // is stored in the attrs.
62
- // This is essentiall doc.nod
63
- var customDocSize = editorState.doc.nodeSize;
64
- editorState.doc.descendants(function (node) {
65
- var _node$attrs3;
66
- if (((_node$attrs3 = node.attrs) === null || _node$attrs3 === void 0 ? void 0 : _node$attrs3.extensionKey) === 'legacy-content') {
67
- var _node$attrs$parameter, _node$attrs4;
68
- customDocSize += (_node$attrs$parameter = (_node$attrs4 = node.attrs) === null || _node$attrs4 === void 0 || (_node$attrs4 = _node$attrs4.parameters) === null || _node$attrs4 === void 0 || (_node$attrs4 = _node$attrs4.adf) === null || _node$attrs4 === void 0 ? void 0 : _node$attrs4.length) !== null && _node$attrs$parameter !== void 0 ? _node$attrs$parameter : 0;
69
- }
70
- });
121
+ var customDocSize = getCustomDocSize(editorState.doc);
71
122
  return {
72
123
  documentSizeBreachesThreshold: customDocSize > LIMITED_MODE_NODE_SIZE_THRESHOLD
73
124
  };
@@ -81,24 +132,13 @@ var createPlugin = exports.createPlugin = function createPlugin() {
81
132
  return currentPluginState;
82
133
  }
83
134
  if ((0, _expVal.expVal)('cc_editor_limited_mode_expanded', 'isEnabled', false)) {
84
- var lcmNodeCountDampingFactor = getNumericExperimentParam('cc_editor_limited_mode_expanded', 'lcmNodeCountDampingFactor', 10);
85
- var nodeCountThreshold = getNumericExperimentParam('cc_editor_limited_mode_expanded', 'nodeCountThreshold', 1000);
86
- var nodeCount = countNodesInDoc(tr.doc, lcmNodeCountDampingFactor);
87
135
  return {
88
- documentSizeBreachesThreshold: nodeCount > nodeCountThreshold
136
+ documentSizeBreachesThreshold: shouldEnableLimitedModeExpanded(tr.doc)
89
137
  };
90
138
  } else {
91
139
  // calculates the size of the doc, where when there are legacy content macros, the content
92
140
  // is stored in the attrs.
93
- // This is essentiall doc.nod
94
- var customDocSize = tr.doc.nodeSize;
95
- tr.doc.descendants(function (node) {
96
- var _node$attrs5;
97
- if (((_node$attrs5 = node.attrs) === null || _node$attrs5 === void 0 ? void 0 : _node$attrs5.extensionKey) === 'legacy-content') {
98
- var _node$attrs$parameter2, _node$attrs6;
99
- customDocSize += (_node$attrs$parameter2 = (_node$attrs6 = node.attrs) === null || _node$attrs6 === void 0 || (_node$attrs6 = _node$attrs6.parameters) === null || _node$attrs6 === void 0 || (_node$attrs6 = _node$attrs6.adf) === null || _node$attrs6 === void 0 ? void 0 : _node$attrs6.length) !== null && _node$attrs$parameter2 !== void 0 ? _node$attrs$parameter2 : 0;
100
- }
101
- });
141
+ var customDocSize = getCustomDocSize(tr.doc);
102
142
  return {
103
143
  documentSizeBreachesThreshold: customDocSize > LIMITED_MODE_NODE_SIZE_THRESHOLD
104
144
  };
@@ -4,36 +4,98 @@ import { expVal } from '@atlaskit/tmp-editor-statsig/expVal';
4
4
  export const limitedModePluginKey = new PluginKey('limitedModePlugin');
5
5
  const LIMITED_MODE_NODE_SIZE_THRESHOLD = 40000;
6
6
  /**
7
- * Counts nodes in the document.
8
- *
9
- * Note: legacy-content macros add a damped contribution based on ADF length to avoid
10
- * parsing nested ADF on every check, which is inefficient.
7
+ * Gets a numeric experiment param, returning undefined if the value is not a valid number.
8
+ * This guards against test overrides returning booleans or strings for numeric params.
11
9
  */
12
- const countNodesInDoc = (doc, lcmDampingFactor) => {
13
- let nodeCount = 0;
10
+ const getNumericExperimentParam = (paramName, fallbackValue) => {
11
+ const rawValue = expVal('cc_editor_limited_mode_expanded', paramName, fallbackValue);
12
+ if (typeof rawValue === 'number') {
13
+ return rawValue;
14
+ }
15
+
16
+ // Handle string values from test overrides
17
+ if (typeof rawValue === 'string') {
18
+ const parsed = parseInt(rawValue, 10);
19
+ if (!isNaN(parsed)) {
20
+ return parsed;
21
+ }
22
+ }
23
+ return undefined;
24
+ };
25
+
26
+ /**
27
+ * Calculates custom document size including LCM ADF lengths (for non-expanded path).
28
+ * This function can be removed when cc_editor_limited_mode_expanded is cleaned up.
29
+ */
30
+ const getCustomDocSize = doc => {
31
+ let lcmAdfLength = 0;
14
32
  doc.descendants(node => {
15
33
  var _node$attrs;
16
- nodeCount += 1;
17
34
  if (((_node$attrs = node.attrs) === null || _node$attrs === void 0 ? void 0 : _node$attrs.extensionKey) === 'legacy-content') {
18
- var _node$attrs2, _node$attrs2$paramete, _node$attrs2$paramete2;
19
- const adfLength = (_node$attrs2 = node.attrs) === null || _node$attrs2 === void 0 ? void 0 : (_node$attrs2$paramete = _node$attrs2.parameters) === null || _node$attrs2$paramete === void 0 ? void 0 : (_node$attrs2$paramete2 = _node$attrs2$paramete.adf) === null || _node$attrs2$paramete2 === void 0 ? void 0 : _node$attrs2$paramete2.length;
20
- if (typeof adfLength === 'number' && lcmDampingFactor > 0) {
21
- nodeCount += Math.ceil(adfLength / lcmDampingFactor);
22
- }
35
+ var _node$attrs$parameter, _node$attrs2, _node$attrs2$paramete, _node$attrs2$paramete2;
36
+ lcmAdfLength += (_node$attrs$parameter = (_node$attrs2 = node.attrs) === null || _node$attrs2 === void 0 ? void 0 : (_node$attrs2$paramete = _node$attrs2.parameters) === null || _node$attrs2$paramete === void 0 ? void 0 : (_node$attrs2$paramete2 = _node$attrs2$paramete.adf) === null || _node$attrs2$paramete2 === void 0 ? void 0 : _node$attrs2$paramete2.length) !== null && _node$attrs$parameter !== void 0 ? _node$attrs$parameter : 0;
23
37
  }
24
38
  });
25
- return nodeCount;
39
+ return doc.nodeSize + lcmAdfLength;
26
40
  };
27
41
 
28
42
  /**
29
- * Guard against test overrides returning booleans for numeric params.
43
+ * Determines whether limited mode should be enabled under the expanded gate.
44
+ * If this logic changes, update the duplicate in `editor-common/src/node-anchor/node-anchor-provider.ts` to avoid drift.
45
+ *
46
+ * Limited mode is activated when ANY of the following conditions are met:
47
+ * 1. Document size exceeds `docSizeThreshold` (if defined)
48
+ * 2. Node count exceeds `nodeCountThreshold` (if defined)
49
+ * 3. Document contains a legacy-content macro (LCM) (if `includeLcmInThreshold` is true)
50
+ *
51
+ * Performance optimisations:
52
+ * - Doc size is checked first (O(1)) - if it exceeds threshold, we skip traversal entirely.
53
+ * - If `includeLcmInThreshold` is enabled and we find an LCM, we exit traversal early
54
+ * since we already know limited mode will be enabled.
55
+ * - If neither node count nor LCM conditions are configured, we skip traversal entirely.
30
56
  */
31
- const getNumericExperimentParam = (experimentName, paramName, fallbackValue) => {
32
- const rawValue = expVal(experimentName, paramName, fallbackValue);
33
- if (typeof rawValue === 'number') {
34
- return rawValue;
57
+ const shouldEnableLimitedModeExpanded = doc => {
58
+ const nodeCountThreshold = getNumericExperimentParam('nodeCountThreshold', 5000);
59
+ const docSizeThreshold = getNumericExperimentParam('docSizeThreshold', 30000);
60
+ const includeLcmInThreshold = Boolean(expVal('cc_editor_limited_mode_expanded', 'includeLcmInThreshold', false));
61
+
62
+ // Early exit: doc size exceeds threshold - O(1), no traversal needed
63
+ if (docSizeThreshold !== undefined && doc.nodeSize > docSizeThreshold) {
64
+ return true;
65
+ }
66
+
67
+ // Early exit: no traversal needed if neither condition is configured
68
+ const needNodeCount = nodeCountThreshold !== undefined;
69
+ if (!needNodeCount && !includeLcmInThreshold) {
70
+ return false;
71
+ }
72
+
73
+ // Single traversal for node count and/or LCM detection
74
+ let nodeCount = 0;
75
+ let hasLcm = false;
76
+ doc.descendants(node => {
77
+ var _node$attrs3;
78
+ nodeCount += 1;
79
+ if (((_node$attrs3 = node.attrs) === null || _node$attrs3 === void 0 ? void 0 : _node$attrs3.extensionKey) === 'legacy-content') {
80
+ hasLcm = true;
81
+
82
+ // Early exit: LCM found and condition is enabled - no need to continue counting
83
+ if (includeLcmInThreshold) {
84
+ return false;
85
+ }
86
+ }
87
+ });
88
+
89
+ // LCM condition takes precedence (if we early exited traversal, this is why)
90
+ if (includeLcmInThreshold && hasLcm) {
91
+ return true;
92
+ }
93
+
94
+ // Check node count threshold
95
+ if (needNodeCount && nodeCount > nodeCountThreshold) {
96
+ return true;
35
97
  }
36
- return fallbackValue;
98
+ return false;
37
99
  };
38
100
  export const createPlugin = () => {
39
101
  return new SafePlugin({
@@ -44,24 +106,13 @@ export const createPlugin = () => {
44
106
  state: {
45
107
  init(config, editorState) {
46
108
  if (expVal('cc_editor_limited_mode_expanded', 'isEnabled', false)) {
47
- const lcmNodeCountDampingFactor = getNumericExperimentParam('cc_editor_limited_mode_expanded', 'lcmNodeCountDampingFactor', 10);
48
- const nodeCountThreshold = getNumericExperimentParam('cc_editor_limited_mode_expanded', 'nodeCountThreshold', 1000);
49
- const nodeCount = countNodesInDoc(editorState.doc, lcmNodeCountDampingFactor);
50
109
  return {
51
- documentSizeBreachesThreshold: nodeCount > nodeCountThreshold
110
+ documentSizeBreachesThreshold: shouldEnableLimitedModeExpanded(editorState.doc)
52
111
  };
53
112
  } else {
54
113
  // calculates the size of the doc, where when there are legacy content macros, the content
55
114
  // is stored in the attrs.
56
- // This is essentiall doc.nod
57
- let customDocSize = editorState.doc.nodeSize;
58
- editorState.doc.descendants(node => {
59
- var _node$attrs3;
60
- if (((_node$attrs3 = node.attrs) === null || _node$attrs3 === void 0 ? void 0 : _node$attrs3.extensionKey) === 'legacy-content') {
61
- var _node$attrs$parameter, _node$attrs4, _node$attrs4$paramete, _node$attrs4$paramete2;
62
- customDocSize += (_node$attrs$parameter = (_node$attrs4 = node.attrs) === null || _node$attrs4 === void 0 ? void 0 : (_node$attrs4$paramete = _node$attrs4.parameters) === null || _node$attrs4$paramete === void 0 ? void 0 : (_node$attrs4$paramete2 = _node$attrs4$paramete.adf) === null || _node$attrs4$paramete2 === void 0 ? void 0 : _node$attrs4$paramete2.length) !== null && _node$attrs$parameter !== void 0 ? _node$attrs$parameter : 0;
63
- }
64
- });
115
+ const customDocSize = getCustomDocSize(editorState.doc);
65
116
  return {
66
117
  documentSizeBreachesThreshold: customDocSize > LIMITED_MODE_NODE_SIZE_THRESHOLD
67
118
  };
@@ -75,24 +126,13 @@ export const createPlugin = () => {
75
126
  return currentPluginState;
76
127
  }
77
128
  if (expVal('cc_editor_limited_mode_expanded', 'isEnabled', false)) {
78
- const lcmNodeCountDampingFactor = getNumericExperimentParam('cc_editor_limited_mode_expanded', 'lcmNodeCountDampingFactor', 10);
79
- const nodeCountThreshold = getNumericExperimentParam('cc_editor_limited_mode_expanded', 'nodeCountThreshold', 1000);
80
- const nodeCount = countNodesInDoc(tr.doc, lcmNodeCountDampingFactor);
81
129
  return {
82
- documentSizeBreachesThreshold: nodeCount > nodeCountThreshold
130
+ documentSizeBreachesThreshold: shouldEnableLimitedModeExpanded(tr.doc)
83
131
  };
84
132
  } else {
85
133
  // calculates the size of the doc, where when there are legacy content macros, the content
86
134
  // is stored in the attrs.
87
- // This is essentiall doc.nod
88
- let customDocSize = tr.doc.nodeSize;
89
- tr.doc.descendants(node => {
90
- var _node$attrs5;
91
- if (((_node$attrs5 = node.attrs) === null || _node$attrs5 === void 0 ? void 0 : _node$attrs5.extensionKey) === 'legacy-content') {
92
- var _node$attrs$parameter2, _node$attrs6, _node$attrs6$paramete, _node$attrs6$paramete2;
93
- customDocSize += (_node$attrs$parameter2 = (_node$attrs6 = node.attrs) === null || _node$attrs6 === void 0 ? void 0 : (_node$attrs6$paramete = _node$attrs6.parameters) === null || _node$attrs6$paramete === void 0 ? void 0 : (_node$attrs6$paramete2 = _node$attrs6$paramete.adf) === null || _node$attrs6$paramete2 === void 0 ? void 0 : _node$attrs6$paramete2.length) !== null && _node$attrs$parameter2 !== void 0 ? _node$attrs$parameter2 : 0;
94
- }
95
- });
135
+ const customDocSize = getCustomDocSize(tr.doc);
96
136
  return {
97
137
  documentSizeBreachesThreshold: customDocSize > LIMITED_MODE_NODE_SIZE_THRESHOLD
98
138
  };
@@ -4,36 +4,98 @@ import { expVal } from '@atlaskit/tmp-editor-statsig/expVal';
4
4
  export var limitedModePluginKey = new PluginKey('limitedModePlugin');
5
5
  var LIMITED_MODE_NODE_SIZE_THRESHOLD = 40000;
6
6
  /**
7
- * Counts nodes in the document.
8
- *
9
- * Note: legacy-content macros add a damped contribution based on ADF length to avoid
10
- * parsing nested ADF on every check, which is inefficient.
7
+ * Gets a numeric experiment param, returning undefined if the value is not a valid number.
8
+ * This guards against test overrides returning booleans or strings for numeric params.
11
9
  */
12
- var countNodesInDoc = function countNodesInDoc(doc, lcmDampingFactor) {
13
- var nodeCount = 0;
10
+ var getNumericExperimentParam = function getNumericExperimentParam(paramName, fallbackValue) {
11
+ var rawValue = expVal('cc_editor_limited_mode_expanded', paramName, fallbackValue);
12
+ if (typeof rawValue === 'number') {
13
+ return rawValue;
14
+ }
15
+
16
+ // Handle string values from test overrides
17
+ if (typeof rawValue === 'string') {
18
+ var parsed = parseInt(rawValue, 10);
19
+ if (!isNaN(parsed)) {
20
+ return parsed;
21
+ }
22
+ }
23
+ return undefined;
24
+ };
25
+
26
+ /**
27
+ * Calculates custom document size including LCM ADF lengths (for non-expanded path).
28
+ * This function can be removed when cc_editor_limited_mode_expanded is cleaned up.
29
+ */
30
+ var getCustomDocSize = function getCustomDocSize(doc) {
31
+ var lcmAdfLength = 0;
14
32
  doc.descendants(function (node) {
15
33
  var _node$attrs;
16
- nodeCount += 1;
17
34
  if (((_node$attrs = node.attrs) === null || _node$attrs === void 0 ? void 0 : _node$attrs.extensionKey) === 'legacy-content') {
18
- var _node$attrs2;
19
- var adfLength = (_node$attrs2 = node.attrs) === null || _node$attrs2 === void 0 || (_node$attrs2 = _node$attrs2.parameters) === null || _node$attrs2 === void 0 || (_node$attrs2 = _node$attrs2.adf) === null || _node$attrs2 === void 0 ? void 0 : _node$attrs2.length;
20
- if (typeof adfLength === 'number' && lcmDampingFactor > 0) {
21
- nodeCount += Math.ceil(adfLength / lcmDampingFactor);
22
- }
35
+ var _node$attrs$parameter, _node$attrs2;
36
+ lcmAdfLength += (_node$attrs$parameter = (_node$attrs2 = node.attrs) === null || _node$attrs2 === void 0 || (_node$attrs2 = _node$attrs2.parameters) === null || _node$attrs2 === void 0 || (_node$attrs2 = _node$attrs2.adf) === null || _node$attrs2 === void 0 ? void 0 : _node$attrs2.length) !== null && _node$attrs$parameter !== void 0 ? _node$attrs$parameter : 0;
23
37
  }
24
38
  });
25
- return nodeCount;
39
+ return doc.nodeSize + lcmAdfLength;
26
40
  };
27
41
 
28
42
  /**
29
- * Guard against test overrides returning booleans for numeric params.
43
+ * Determines whether limited mode should be enabled under the expanded gate.
44
+ * If this logic changes, update the duplicate in `editor-common/src/node-anchor/node-anchor-provider.ts` to avoid drift.
45
+ *
46
+ * Limited mode is activated when ANY of the following conditions are met:
47
+ * 1. Document size exceeds `docSizeThreshold` (if defined)
48
+ * 2. Node count exceeds `nodeCountThreshold` (if defined)
49
+ * 3. Document contains a legacy-content macro (LCM) (if `includeLcmInThreshold` is true)
50
+ *
51
+ * Performance optimisations:
52
+ * - Doc size is checked first (O(1)) - if it exceeds threshold, we skip traversal entirely.
53
+ * - If `includeLcmInThreshold` is enabled and we find an LCM, we exit traversal early
54
+ * since we already know limited mode will be enabled.
55
+ * - If neither node count nor LCM conditions are configured, we skip traversal entirely.
30
56
  */
31
- var getNumericExperimentParam = function getNumericExperimentParam(experimentName, paramName, fallbackValue) {
32
- var rawValue = expVal(experimentName, paramName, fallbackValue);
33
- if (typeof rawValue === 'number') {
34
- return rawValue;
57
+ var shouldEnableLimitedModeExpanded = function shouldEnableLimitedModeExpanded(doc) {
58
+ var nodeCountThreshold = getNumericExperimentParam('nodeCountThreshold', 5000);
59
+ var docSizeThreshold = getNumericExperimentParam('docSizeThreshold', 30000);
60
+ var includeLcmInThreshold = Boolean(expVal('cc_editor_limited_mode_expanded', 'includeLcmInThreshold', false));
61
+
62
+ // Early exit: doc size exceeds threshold - O(1), no traversal needed
63
+ if (docSizeThreshold !== undefined && doc.nodeSize > docSizeThreshold) {
64
+ return true;
65
+ }
66
+
67
+ // Early exit: no traversal needed if neither condition is configured
68
+ var needNodeCount = nodeCountThreshold !== undefined;
69
+ if (!needNodeCount && !includeLcmInThreshold) {
70
+ return false;
71
+ }
72
+
73
+ // Single traversal for node count and/or LCM detection
74
+ var nodeCount = 0;
75
+ var hasLcm = false;
76
+ doc.descendants(function (node) {
77
+ var _node$attrs3;
78
+ nodeCount += 1;
79
+ if (((_node$attrs3 = node.attrs) === null || _node$attrs3 === void 0 ? void 0 : _node$attrs3.extensionKey) === 'legacy-content') {
80
+ hasLcm = true;
81
+
82
+ // Early exit: LCM found and condition is enabled - no need to continue counting
83
+ if (includeLcmInThreshold) {
84
+ return false;
85
+ }
86
+ }
87
+ });
88
+
89
+ // LCM condition takes precedence (if we early exited traversal, this is why)
90
+ if (includeLcmInThreshold && hasLcm) {
91
+ return true;
92
+ }
93
+
94
+ // Check node count threshold
95
+ if (needNodeCount && nodeCount > nodeCountThreshold) {
96
+ return true;
35
97
  }
36
- return fallbackValue;
98
+ return false;
37
99
  };
38
100
  export var createPlugin = function createPlugin() {
39
101
  return new SafePlugin({
@@ -44,24 +106,13 @@ export var createPlugin = function createPlugin() {
44
106
  state: {
45
107
  init: function init(config, editorState) {
46
108
  if (expVal('cc_editor_limited_mode_expanded', 'isEnabled', false)) {
47
- var lcmNodeCountDampingFactor = getNumericExperimentParam('cc_editor_limited_mode_expanded', 'lcmNodeCountDampingFactor', 10);
48
- var nodeCountThreshold = getNumericExperimentParam('cc_editor_limited_mode_expanded', 'nodeCountThreshold', 1000);
49
- var nodeCount = countNodesInDoc(editorState.doc, lcmNodeCountDampingFactor);
50
109
  return {
51
- documentSizeBreachesThreshold: nodeCount > nodeCountThreshold
110
+ documentSizeBreachesThreshold: shouldEnableLimitedModeExpanded(editorState.doc)
52
111
  };
53
112
  } else {
54
113
  // calculates the size of the doc, where when there are legacy content macros, the content
55
114
  // is stored in the attrs.
56
- // This is essentiall doc.nod
57
- var customDocSize = editorState.doc.nodeSize;
58
- editorState.doc.descendants(function (node) {
59
- var _node$attrs3;
60
- if (((_node$attrs3 = node.attrs) === null || _node$attrs3 === void 0 ? void 0 : _node$attrs3.extensionKey) === 'legacy-content') {
61
- var _node$attrs$parameter, _node$attrs4;
62
- customDocSize += (_node$attrs$parameter = (_node$attrs4 = node.attrs) === null || _node$attrs4 === void 0 || (_node$attrs4 = _node$attrs4.parameters) === null || _node$attrs4 === void 0 || (_node$attrs4 = _node$attrs4.adf) === null || _node$attrs4 === void 0 ? void 0 : _node$attrs4.length) !== null && _node$attrs$parameter !== void 0 ? _node$attrs$parameter : 0;
63
- }
64
- });
115
+ var customDocSize = getCustomDocSize(editorState.doc);
65
116
  return {
66
117
  documentSizeBreachesThreshold: customDocSize > LIMITED_MODE_NODE_SIZE_THRESHOLD
67
118
  };
@@ -75,24 +126,13 @@ export var createPlugin = function createPlugin() {
75
126
  return currentPluginState;
76
127
  }
77
128
  if (expVal('cc_editor_limited_mode_expanded', 'isEnabled', false)) {
78
- var lcmNodeCountDampingFactor = getNumericExperimentParam('cc_editor_limited_mode_expanded', 'lcmNodeCountDampingFactor', 10);
79
- var nodeCountThreshold = getNumericExperimentParam('cc_editor_limited_mode_expanded', 'nodeCountThreshold', 1000);
80
- var nodeCount = countNodesInDoc(tr.doc, lcmNodeCountDampingFactor);
81
129
  return {
82
- documentSizeBreachesThreshold: nodeCount > nodeCountThreshold
130
+ documentSizeBreachesThreshold: shouldEnableLimitedModeExpanded(tr.doc)
83
131
  };
84
132
  } else {
85
133
  // calculates the size of the doc, where when there are legacy content macros, the content
86
134
  // is stored in the attrs.
87
- // This is essentiall doc.nod
88
- var customDocSize = tr.doc.nodeSize;
89
- tr.doc.descendants(function (node) {
90
- var _node$attrs5;
91
- if (((_node$attrs5 = node.attrs) === null || _node$attrs5 === void 0 ? void 0 : _node$attrs5.extensionKey) === 'legacy-content') {
92
- var _node$attrs$parameter2, _node$attrs6;
93
- customDocSize += (_node$attrs$parameter2 = (_node$attrs6 = node.attrs) === null || _node$attrs6 === void 0 || (_node$attrs6 = _node$attrs6.parameters) === null || _node$attrs6 === void 0 || (_node$attrs6 = _node$attrs6.adf) === null || _node$attrs6 === void 0 ? void 0 : _node$attrs6.length) !== null && _node$attrs$parameter2 !== void 0 ? _node$attrs$parameter2 : 0;
94
- }
95
- });
135
+ var customDocSize = getCustomDocSize(tr.doc);
96
136
  return {
97
137
  documentSizeBreachesThreshold: customDocSize > LIMITED_MODE_NODE_SIZE_THRESHOLD
98
138
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@atlaskit/editor-plugin-limited-mode",
3
- "version": "4.0.4",
3
+ "version": "4.0.6",
4
4
  "description": "LimitedMode plugin for @atlaskit/editor-core",
5
5
  "author": "Atlassian Pty Ltd",
6
6
  "license": "Apache-2.0",
@@ -30,7 +30,7 @@
30
30
  "dependencies": {
31
31
  "@atlaskit/editor-prosemirror": "^7.3.0",
32
32
  "@atlaskit/platform-feature-flags": "^1.1.0",
33
- "@atlaskit/tmp-editor-statsig": "^18.0.0",
33
+ "@atlaskit/tmp-editor-statsig": "^20.0.0",
34
34
  "@babel/runtime": "^7.0.0",
35
35
  "bind-event-listener": "^3.0.0",
36
36
  "react-intl-next": "npm:react-intl@^5.18.1"