@atlaskit/editor-plugin-autocomplete 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,21 @@
1
1
  # @atlaskit/editor-plugin-autocomplete
2
2
 
3
+ ## 4.0.6
4
+
5
+ ### Patch Changes
6
+
7
+ - [`c2986ab2c7a01`](https://bitbucket.org/atlassian/atlassian-frontend-monorepo/commits/c2986ab2c7a01) -
8
+ Cleans up prefer static regex violations and enables e18e rule
9
+ - Updated dependencies
10
+
11
+ ## 4.0.5
12
+
13
+ ### Patch Changes
14
+
15
+ - [`73743eb8b36e6`](https://bitbucket.org/atlassian/atlassian-frontend-monorepo/commits/73743eb8b36e6) -
16
+ CLeanup prefer static regex violations
17
+ - Updated dependencies
18
+
3
19
  ## 4.0.4
4
20
 
5
21
  ### Patch Changes
package/compass.yml ADDED
@@ -0,0 +1,41 @@
1
+ configVersion: 1
2
+ id: ari:cloud:compass:a436116f-02ce-4520-8fbb-7301462a1674:component/c5751cc6-3513-4070-9deb-af31e86aed34/45ef6a6c-98b8-4615-940d-e9013db36999
3
+ name: '@atlaskit/editor-plugin-autocomplete'
4
+ ownerId: ari:cloud:identity::team/0250ee69-f755-47fd-a468-bf8a9d10a3c5 # Editor AI
5
+ labels:
6
+ - platform-code
7
+ - platform-afm
8
+ typeId: OTHER
9
+ fields:
10
+ tier: 4
11
+ lifecycle: Active
12
+ isMonorepoProject: true
13
+ links:
14
+ - name: Root Repository
15
+ type: REPOSITORY
16
+ url: https://bitbucket.org/atlassian/atlassian-frontend-monorepo/src/master
17
+ - name: Slack Channel
18
+ type: CHAT_CHANNEL
19
+ url: https://atlassian.enterprise.slack.com/archives/CFG3PSQ9E # #help-editor
20
+ - name: Editor Plugin Autocomplete
21
+ type: REPOSITORY
22
+ url: https://bitbucket.org/atlassian/atlassian-frontend-monorepo/src/master/platform/packages/editor/editor-plugin-autocomplete
23
+ customFields:
24
+ - name: Department
25
+ type: text
26
+ value: Eng - Editor AI
27
+ - name: Trusted Reviewer Teams
28
+ type: text
29
+ value: ari:cloud:identity::team/cf6a670b-5252-4c68-a769-9207de366beb
30
+ - name: Technical Owner
31
+ type: user
32
+ value: ari:cloud:identity::user/612646c53fe26c00694fbe6a # Chris Kimber
33
+ - name: Required Reviewers Opt In
34
+ type: boolean
35
+ value: true
36
+ - name: Reviewer Selection Mechanism
37
+ type: text
38
+ value: random(2)
39
+ - name: Required Reviewer Approvals
40
+ type: number
41
+ value: 1
@@ -38,6 +38,11 @@ var MAX_INGESTED_CONTEXT_TEXTS = 50;
38
38
  // non-comment editors (where parentCommentContent never arrives) don't refetch
39
39
  // on every word boundary for the plugin's lifetime.
40
40
  var MAX_CONTEXT_REFRESH_ATTEMPTS = 5;
41
+
42
+ // eslint-disable-next-line require-unicode-regexp
43
+ var TRAILING_NON_BOUNDARY_TOKEN_REGEX = /([^\s.,;:!?]*)$/;
44
+ // eslint-disable-next-line require-unicode-regexp
45
+ var WORD_BOUNDARY_CHARS_REGEX = /[\s.,;:!?]/;
41
46
  var hasDestroy = function hasDestroy(client) {
42
47
  return 'destroy' in client && typeof client.destroy === 'function';
43
48
  };
@@ -191,6 +196,8 @@ var buildSlowLaneText = function buildSlowLaneText(docText, context) {
191
196
  var createMidWordCharacterRegex = function createMidWordCharacterRegex() {
192
197
  try {
193
198
  // string-built regex avoids TS1501 under the package's ES5 build target
199
+ // Ignored via go/ees019
200
+ // eslint-disable-next-line e18e/prefer-static-regex
194
201
  return new RegExp('[\\p{L}\\p{N}_]', 'u');
195
202
  } catch (_unused) {
196
203
  return null;
@@ -225,9 +232,7 @@ var getTypedLengthForPrediction = function getTypedLengthForPrediction(textBefor
225
232
  if ((0, _slowLaneClient.isWordBoundary)(textBefore)) {
226
233
  return 0;
227
234
  }
228
-
229
- // eslint-disable-next-line require-unicode-regexp
230
- var trailingToken = (_textBefore$match$ = (_textBefore$match = textBefore.match(/([^\s.,;:!?]*)$/)) === null || _textBefore$match === void 0 ? void 0 : _textBefore$match[1]) !== null && _textBefore$match$ !== void 0 ? _textBefore$match$ : '';
235
+ var trailingToken = (_textBefore$match$ = (_textBefore$match = textBefore.match(TRAILING_NON_BOUNDARY_TOKEN_REGEX)) === null || _textBefore$match === void 0 ? void 0 : _textBefore$match[1]) !== null && _textBefore$match$ !== void 0 ? _textBefore$match$ : '';
231
236
  return trailingToken.length;
232
237
  };
233
238
  var isEnglishLocale = function isEnglishLocale(locale) {
@@ -539,16 +544,14 @@ var createAutocompletePlugin = exports.createAutocompletePlugin = function creat
539
544
  return;
540
545
  }
541
546
  var lastChar = newText[newText.length - 1];
542
- // eslint-disable-next-line require-unicode-regexp
543
- if (!/[\s.,;:!?]/.test(lastChar)) {
547
+ if (!WORD_BOUNDARY_CHARS_REGEX.test(lastChar)) {
544
548
  return;
545
549
  }
546
550
 
547
551
  // Only fire if the previous state did not already end on a boundary,
548
552
  // so we don't double-count when multiple boundary chars are inserted.
549
553
  var prevLastChar = prevText[prevText.length - 1];
550
- // eslint-disable-next-line require-unicode-regexp
551
- if (prevLastChar && /[\s.,;:!?]/.test(prevLastChar)) {
554
+ if (prevLastChar && WORD_BOUNDARY_CHARS_REGEX.test(prevLastChar)) {
552
555
  return;
553
556
  }
554
557
  var beforeBoundary = newText.slice(0, -1).trimEnd();
@@ -78,6 +78,8 @@ function _arrayLikeToArray(r, a) { (null == a || a > r.length) && (a = r.length)
78
78
  // ─── Constants ───────────────────────────────────────────────────────────────
79
79
 
80
80
  var DEFAULT_DEBOUNCE_MS = 300;
81
+ // eslint-disable-next-line require-unicode-regexp
82
+ var OOM_REGEX = /\boom\b/;
81
83
  var LOCAL_MLC_CAUSAL_MODEL_ID = exports.LOCAL_MLC_CAUSAL_MODEL_ID = 'SmolLM2-135M-Instruct-q0f16-MLC';
82
84
 
83
85
  /**
@@ -766,9 +768,7 @@ var createLocalSlowLaneClient = exports.createLocalSlowLaneClient = function cre
766
768
  if (lower.includes('loading chunk') || lower.includes('dynamically imported module') || lower.includes('dynamic import')) {
767
769
  return 'module_load_failed';
768
770
  }
769
- if (lower.includes('out of memory') ||
770
- // eslint-disable-next-line require-unicode-regexp
771
- /\boom\b/.test(lower) || lower.includes('allocation') || lower.includes('exceeds') || lower.includes('buffer size') || lower.includes('not enough memory')) {
771
+ if (lower.includes('out of memory') || OOM_REGEX.test(lower) || lower.includes('allocation') || lower.includes('exceeds') || lower.includes('buffer size') || lower.includes('not enough memory')) {
772
772
  return 'insufficient_memory';
773
773
  }
774
774
  // Pre-flight already returns missing_shader_f16 when the feature is absent,
@@ -29,6 +29,8 @@ var _debugMode = require("./debug-mode");
29
29
 
30
30
  // eslint-disable-next-line require-unicode-regexp
31
31
  var WORD_BOUNDARY_CHARS = /[\s.,;:!?]/;
32
+ // eslint-disable-next-line require-unicode-regexp
33
+ var TRAILING_SLASH_REGEX = /\/$/;
32
34
  var DEFAULT_DEBOUNCE_MS = 300;
33
35
 
34
36
  /**
@@ -80,8 +82,7 @@ var createSlowLaneClient = exports.createSlowLaneClient = function createSlowLan
80
82
  }
81
83
  return _context.abrupt("return");
82
84
  case 1:
83
- // eslint-disable-next-line require-unicode-regexp
84
- url = "".concat(baseUrl.replace(/\/$/, '')).concat(endpoint);
85
+ url = "".concat(baseUrl.replace(TRAILING_SLASH_REGEX, '')).concat(endpoint);
85
86
  payload = {
86
87
  text: text,
87
88
  session_id: sessionId
@@ -42,6 +42,12 @@ function _arrayLikeToArray(r, a) { (null == a || a > r.length) && (a = r.length)
42
42
 
43
43
  // eslint-disable-next-line require-unicode-regexp
44
44
  var PUNCTUATION_BOUNDARY_REGEX = /^[.,;:!?()\[\]{}"'`]+|[.,;:!?()\[\]{}"'`]+$/g;
45
+ // eslint-disable-next-line require-unicode-regexp
46
+ var WHITESPACE_SPLIT_REGEX = /\s+/;
47
+ // eslint-disable-next-line require-unicode-regexp
48
+ var SENTENCE_BOUNDARY_REGEX = /[\n.?!]+/;
49
+ // eslint-disable-next-line require-unicode-regexp
50
+ var TRAILING_WHITESPACE_REGEX = /\s$/;
45
51
  var MIN_PREFIX_LENGTH = 3;
46
52
  var MAX_CANDIDATES = 200;
47
53
  var CONTEXT_WORDS = 10;
@@ -312,8 +318,8 @@ var getContextVectorForScoring = function getContextVectorForScoring(textBefore)
312
318
  };
313
319
  var tokenize = function tokenize(text) {
314
320
  var tokens = [];
315
- // eslint-disable-next-line require-unicode-regexp, @atlassian/perf-linting/no-expensive-split-replace
316
- var _iterator7 = _createForOfIteratorHelper(text.toLowerCase().split(/\s+/)),
321
+ // eslint-disable-next-line @atlassian/perf-linting/no-expensive-split-replace
322
+ var _iterator7 = _createForOfIteratorHelper(text.toLowerCase().split(WHITESPACE_SPLIT_REGEX)),
317
323
  _step7;
318
324
  try {
319
325
  for (_iterator7.s(); !(_step7 = _iterator7.n()).done;) {
@@ -333,12 +339,9 @@ var tokenize = function tokenize(text) {
333
339
  };
334
340
  var extractPreviousWord = function extractPreviousWord(text) {
335
341
  // Only consider the current sentence/line the user is typing in.
336
- // eslint-disable-next-line require-unicode-regexp
337
- var sentences = text.split(/[\n.?!]+/);
342
+ var sentences = text.split(SENTENCE_BOUNDARY_REGEX);
338
343
  var currentSentence = sentences[sentences.length - 1];
339
-
340
- // eslint-disable-next-line require-unicode-regexp
341
- var words = currentSentence.trimEnd().split(/\s+/);
344
+ var words = currentSentence.trimEnd().split(WHITESPACE_SPLIT_REGEX);
342
345
  return words.length >= 2 ? words[words.length - 2] : '';
343
346
  };
344
347
 
@@ -477,8 +480,7 @@ var predict = exports.predict = function predict(textBefore) {
477
480
  // }
478
481
 
479
482
  // ── Step 2: Prefix completion (≥3 chars typed) ──────────────────────────
480
- // eslint-disable-next-line require-unicode-regexp
481
- if (textBefore.length > 0 && /\s$/.test(textBefore)) {
483
+ if (textBefore.length > 0 && TRAILING_WHITESPACE_REGEX.test(textBefore)) {
482
484
  return null;
483
485
  }
484
486
  var trimmed = textBefore.trimEnd();
@@ -24,6 +24,11 @@ const MAX_INGESTED_CONTEXT_TEXTS = 50;
24
24
  // non-comment editors (where parentCommentContent never arrives) don't refetch
25
25
  // on every word boundary for the plugin's lifetime.
26
26
  const MAX_CONTEXT_REFRESH_ATTEMPTS = 5;
27
+
28
+ // eslint-disable-next-line require-unicode-regexp
29
+ const TRAILING_NON_BOUNDARY_TOKEN_REGEX = /([^\s.,;:!?]*)$/;
30
+ // eslint-disable-next-line require-unicode-regexp
31
+ const WORD_BOUNDARY_CHARS_REGEX = /[\s.,;:!?]/;
27
32
  const hasDestroy = client => 'destroy' in client && typeof client.destroy === 'function';
28
33
  const createInitialState = () => ({
29
34
  ghostText: '',
@@ -179,6 +184,8 @@ const buildSlowLaneText = (docText, context) => {
179
184
  const createMidWordCharacterRegex = () => {
180
185
  try {
181
186
  // string-built regex avoids TS1501 under the package's ES5 build target
187
+ // Ignored via go/ees019
188
+ // eslint-disable-next-line e18e/prefer-static-regex
182
189
  return new RegExp('[\\p{L}\\p{N}_]', 'u');
183
190
  } catch {
184
191
  return null;
@@ -213,9 +220,7 @@ const getTypedLengthForPrediction = textBefore => {
213
220
  if (isWordBoundary(textBefore)) {
214
221
  return 0;
215
222
  }
216
-
217
- // eslint-disable-next-line require-unicode-regexp
218
- const trailingToken = (_textBefore$match$ = (_textBefore$match = textBefore.match(/([^\s.,;:!?]*)$/)) === null || _textBefore$match === void 0 ? void 0 : _textBefore$match[1]) !== null && _textBefore$match$ !== void 0 ? _textBefore$match$ : '';
223
+ const trailingToken = (_textBefore$match$ = (_textBefore$match = textBefore.match(TRAILING_NON_BOUNDARY_TOKEN_REGEX)) === null || _textBefore$match === void 0 ? void 0 : _textBefore$match[1]) !== null && _textBefore$match$ !== void 0 ? _textBefore$match$ : '';
219
224
  return trailingToken.length;
220
225
  };
221
226
  const isEnglishLocale = locale => {
@@ -523,16 +528,14 @@ export const createAutocompletePlugin = (options, api) => {
523
528
  return;
524
529
  }
525
530
  const lastChar = newText[newText.length - 1];
526
- // eslint-disable-next-line require-unicode-regexp
527
- if (!/[\s.,;:!?]/.test(lastChar)) {
531
+ if (!WORD_BOUNDARY_CHARS_REGEX.test(lastChar)) {
528
532
  return;
529
533
  }
530
534
 
531
535
  // Only fire if the previous state did not already end on a boundary,
532
536
  // so we don't double-count when multiple boundary chars are inserted.
533
537
  const prevLastChar = prevText[prevText.length - 1];
534
- // eslint-disable-next-line require-unicode-regexp
535
- if (prevLastChar && /[\s.,;:!?]/.test(prevLastChar)) {
538
+ if (prevLastChar && WORD_BOUNDARY_CHARS_REGEX.test(prevLastChar)) {
536
539
  return;
537
540
  }
538
541
  const beforeBoundary = newText.slice(0, -1).trimEnd();
@@ -60,6 +60,8 @@ import { isWordBoundary } from './slow-lane-client';
60
60
  // ─── Constants ───────────────────────────────────────────────────────────────
61
61
 
62
62
  const DEFAULT_DEBOUNCE_MS = 300;
63
+ // eslint-disable-next-line require-unicode-regexp
64
+ const OOM_REGEX = /\boom\b/;
63
65
  export const LOCAL_MLC_CAUSAL_MODEL_ID = 'SmolLM2-135M-Instruct-q0f16-MLC';
64
66
 
65
67
  /**
@@ -587,9 +589,7 @@ export const createLocalSlowLaneClient = (config = {}) => {
587
589
  if (lower.includes('loading chunk') || lower.includes('dynamically imported module') || lower.includes('dynamic import')) {
588
590
  return 'module_load_failed';
589
591
  }
590
- if (lower.includes('out of memory') ||
591
- // eslint-disable-next-line require-unicode-regexp
592
- /\boom\b/.test(lower) || lower.includes('allocation') || lower.includes('exceeds') || lower.includes('buffer size') || lower.includes('not enough memory')) {
592
+ if (lower.includes('out of memory') || OOM_REGEX.test(lower) || lower.includes('allocation') || lower.includes('exceeds') || lower.includes('buffer size') || lower.includes('not enough memory')) {
593
593
  return 'insufficient_memory';
594
594
  }
595
595
  // Pre-flight already returns missing_shader_f16 when the feature is absent,
@@ -20,6 +20,8 @@ import { isAutocompleteDebugEnabled } from './debug-mode';
20
20
 
21
21
  // eslint-disable-next-line require-unicode-regexp
22
22
  const WORD_BOUNDARY_CHARS = /[\s.,;:!?]/;
23
+ // eslint-disable-next-line require-unicode-regexp
24
+ const TRAILING_SLASH_REGEX = /\/$/;
23
25
  const DEFAULT_DEBOUNCE_MS = 300;
24
26
 
25
27
  /**
@@ -61,9 +63,7 @@ export const createSlowLaneClient = config => {
61
63
  if (!text || text.trim().length === 0) {
62
64
  return;
63
65
  }
64
-
65
- // eslint-disable-next-line require-unicode-regexp
66
- const url = `${baseUrl.replace(/\/$/, '')}${endpoint}`;
66
+ const url = `${baseUrl.replace(TRAILING_SLASH_REGEX, '')}${endpoint}`;
67
67
  const payload = {
68
68
  text,
69
69
  session_id: sessionId
@@ -29,6 +29,12 @@ import { getStoredContextVector, getStoredLmLogits } from './slow-lane-client';
29
29
 
30
30
  // eslint-disable-next-line require-unicode-regexp
31
31
  const PUNCTUATION_BOUNDARY_REGEX = /^[.,;:!?()\[\]{}"'`]+|[.,;:!?()\[\]{}"'`]+$/g;
32
+ // eslint-disable-next-line require-unicode-regexp
33
+ const WHITESPACE_SPLIT_REGEX = /\s+/;
34
+ // eslint-disable-next-line require-unicode-regexp
35
+ const SENTENCE_BOUNDARY_REGEX = /[\n.?!]+/;
36
+ // eslint-disable-next-line require-unicode-regexp
37
+ const TRAILING_WHITESPACE_REGEX = /\s$/;
32
38
  const MIN_PREFIX_LENGTH = 3;
33
39
  const MAX_CANDIDATES = 200;
34
40
  const CONTEXT_WORDS = 10;
@@ -235,8 +241,8 @@ const getContextVectorForScoring = textBefore => {
235
241
  };
236
242
  const tokenize = text => {
237
243
  const tokens = [];
238
- // eslint-disable-next-line require-unicode-regexp, @atlassian/perf-linting/no-expensive-split-replace
239
- for (const raw of text.toLowerCase().split(/\s+/)) {
244
+ // eslint-disable-next-line @atlassian/perf-linting/no-expensive-split-replace
245
+ for (const raw of text.toLowerCase().split(WHITESPACE_SPLIT_REGEX)) {
240
246
  // eslint-disable-next-line @atlassian/perf-linting/no-expensive-split-replace
241
247
  const clean = raw.replace(PUNCTUATION_BOUNDARY_REGEX, '');
242
248
  if (clean.length >= 2) {
@@ -247,12 +253,9 @@ const tokenize = text => {
247
253
  };
248
254
  const extractPreviousWord = text => {
249
255
  // Only consider the current sentence/line the user is typing in.
250
- // eslint-disable-next-line require-unicode-regexp
251
- const sentences = text.split(/[\n.?!]+/);
256
+ const sentences = text.split(SENTENCE_BOUNDARY_REGEX);
252
257
  const currentSentence = sentences[sentences.length - 1];
253
-
254
- // eslint-disable-next-line require-unicode-regexp
255
- const words = currentSentence.trimEnd().split(/\s+/);
258
+ const words = currentSentence.trimEnd().split(WHITESPACE_SPLIT_REGEX);
256
259
  return words.length >= 2 ? words[words.length - 2] : '';
257
260
  };
258
261
 
@@ -373,8 +376,7 @@ export const predict = textBefore => {
373
376
  // }
374
377
 
375
378
  // ── Step 2: Prefix completion (≥3 chars typed) ──────────────────────────
376
- // eslint-disable-next-line require-unicode-regexp
377
- if (textBefore.length > 0 && /\s$/.test(textBefore)) {
379
+ if (textBefore.length > 0 && TRAILING_WHITESPACE_REGEX.test(textBefore)) {
378
380
  return null;
379
381
  }
380
382
  const trimmed = textBefore.trimEnd();
@@ -31,6 +31,11 @@ var MAX_INGESTED_CONTEXT_TEXTS = 50;
31
31
  // non-comment editors (where parentCommentContent never arrives) don't refetch
32
32
  // on every word boundary for the plugin's lifetime.
33
33
  var MAX_CONTEXT_REFRESH_ATTEMPTS = 5;
34
+
35
+ // eslint-disable-next-line require-unicode-regexp
36
+ var TRAILING_NON_BOUNDARY_TOKEN_REGEX = /([^\s.,;:!?]*)$/;
37
+ // eslint-disable-next-line require-unicode-regexp
38
+ var WORD_BOUNDARY_CHARS_REGEX = /[\s.,;:!?]/;
34
39
  var hasDestroy = function hasDestroy(client) {
35
40
  return 'destroy' in client && typeof client.destroy === 'function';
36
41
  };
@@ -184,6 +189,8 @@ var buildSlowLaneText = function buildSlowLaneText(docText, context) {
184
189
  var createMidWordCharacterRegex = function createMidWordCharacterRegex() {
185
190
  try {
186
191
  // string-built regex avoids TS1501 under the package's ES5 build target
192
+ // Ignored via go/ees019
193
+ // eslint-disable-next-line e18e/prefer-static-regex
187
194
  return new RegExp('[\\p{L}\\p{N}_]', 'u');
188
195
  } catch (_unused) {
189
196
  return null;
@@ -218,9 +225,7 @@ var getTypedLengthForPrediction = function getTypedLengthForPrediction(textBefor
218
225
  if (isWordBoundary(textBefore)) {
219
226
  return 0;
220
227
  }
221
-
222
- // eslint-disable-next-line require-unicode-regexp
223
- var trailingToken = (_textBefore$match$ = (_textBefore$match = textBefore.match(/([^\s.,;:!?]*)$/)) === null || _textBefore$match === void 0 ? void 0 : _textBefore$match[1]) !== null && _textBefore$match$ !== void 0 ? _textBefore$match$ : '';
228
+ var trailingToken = (_textBefore$match$ = (_textBefore$match = textBefore.match(TRAILING_NON_BOUNDARY_TOKEN_REGEX)) === null || _textBefore$match === void 0 ? void 0 : _textBefore$match[1]) !== null && _textBefore$match$ !== void 0 ? _textBefore$match$ : '';
224
229
  return trailingToken.length;
225
230
  };
226
231
  var isEnglishLocale = function isEnglishLocale(locale) {
@@ -532,16 +537,14 @@ export var createAutocompletePlugin = function createAutocompletePlugin(options,
532
537
  return;
533
538
  }
534
539
  var lastChar = newText[newText.length - 1];
535
- // eslint-disable-next-line require-unicode-regexp
536
- if (!/[\s.,;:!?]/.test(lastChar)) {
540
+ if (!WORD_BOUNDARY_CHARS_REGEX.test(lastChar)) {
537
541
  return;
538
542
  }
539
543
 
540
544
  // Only fire if the previous state did not already end on a boundary,
541
545
  // so we don't double-count when multiple boundary chars are inserted.
542
546
  var prevLastChar = prevText[prevText.length - 1];
543
- // eslint-disable-next-line require-unicode-regexp
544
- if (prevLastChar && /[\s.,;:!?]/.test(prevLastChar)) {
547
+ if (prevLastChar && WORD_BOUNDARY_CHARS_REGEX.test(prevLastChar)) {
545
548
  return;
546
549
  }
547
550
  var beforeBoundary = newText.slice(0, -1).trimEnd();
@@ -72,6 +72,8 @@ import { isWordBoundary } from './slow-lane-client';
72
72
  // ─── Constants ───────────────────────────────────────────────────────────────
73
73
 
74
74
  var DEFAULT_DEBOUNCE_MS = 300;
75
+ // eslint-disable-next-line require-unicode-regexp
76
+ var OOM_REGEX = /\boom\b/;
75
77
  export var LOCAL_MLC_CAUSAL_MODEL_ID = 'SmolLM2-135M-Instruct-q0f16-MLC';
76
78
 
77
79
  /**
@@ -756,9 +758,7 @@ export var createLocalSlowLaneClient = function createLocalSlowLaneClient() {
756
758
  if (lower.includes('loading chunk') || lower.includes('dynamically imported module') || lower.includes('dynamic import')) {
757
759
  return 'module_load_failed';
758
760
  }
759
- if (lower.includes('out of memory') ||
760
- // eslint-disable-next-line require-unicode-regexp
761
- /\boom\b/.test(lower) || lower.includes('allocation') || lower.includes('exceeds') || lower.includes('buffer size') || lower.includes('not enough memory')) {
761
+ if (lower.includes('out of memory') || OOM_REGEX.test(lower) || lower.includes('allocation') || lower.includes('exceeds') || lower.includes('buffer size') || lower.includes('not enough memory')) {
762
762
  return 'insufficient_memory';
763
763
  }
764
764
  // Pre-flight already returns missing_shader_f16 when the feature is absent,
@@ -23,6 +23,8 @@ import { isAutocompleteDebugEnabled } from './debug-mode';
23
23
 
24
24
  // eslint-disable-next-line require-unicode-regexp
25
25
  var WORD_BOUNDARY_CHARS = /[\s.,;:!?]/;
26
+ // eslint-disable-next-line require-unicode-regexp
27
+ var TRAILING_SLASH_REGEX = /\/$/;
26
28
  var DEFAULT_DEBOUNCE_MS = 300;
27
29
 
28
30
  /**
@@ -74,8 +76,7 @@ export var createSlowLaneClient = function createSlowLaneClient(config) {
74
76
  }
75
77
  return _context.abrupt("return");
76
78
  case 1:
77
- // eslint-disable-next-line require-unicode-regexp
78
- url = "".concat(baseUrl.replace(/\/$/, '')).concat(endpoint);
79
+ url = "".concat(baseUrl.replace(TRAILING_SLASH_REGEX, '')).concat(endpoint);
79
80
  payload = {
80
81
  text: text,
81
82
  session_id: sessionId
@@ -38,6 +38,12 @@ import { getStoredContextVector, getStoredLmLogits } from './slow-lane-client';
38
38
 
39
39
  // eslint-disable-next-line require-unicode-regexp
40
40
  var PUNCTUATION_BOUNDARY_REGEX = /^[.,;:!?()\[\]{}"'`]+|[.,;:!?()\[\]{}"'`]+$/g;
41
+ // eslint-disable-next-line require-unicode-regexp
42
+ var WHITESPACE_SPLIT_REGEX = /\s+/;
43
+ // eslint-disable-next-line require-unicode-regexp
44
+ var SENTENCE_BOUNDARY_REGEX = /[\n.?!]+/;
45
+ // eslint-disable-next-line require-unicode-regexp
46
+ var TRAILING_WHITESPACE_REGEX = /\s$/;
41
47
  var MIN_PREFIX_LENGTH = 3;
42
48
  var MAX_CANDIDATES = 200;
43
49
  var CONTEXT_WORDS = 10;
@@ -308,8 +314,8 @@ var getContextVectorForScoring = function getContextVectorForScoring(textBefore)
308
314
  };
309
315
  var tokenize = function tokenize(text) {
310
316
  var tokens = [];
311
- // eslint-disable-next-line require-unicode-regexp, @atlassian/perf-linting/no-expensive-split-replace
312
- var _iterator7 = _createForOfIteratorHelper(text.toLowerCase().split(/\s+/)),
317
+ // eslint-disable-next-line @atlassian/perf-linting/no-expensive-split-replace
318
+ var _iterator7 = _createForOfIteratorHelper(text.toLowerCase().split(WHITESPACE_SPLIT_REGEX)),
313
319
  _step7;
314
320
  try {
315
321
  for (_iterator7.s(); !(_step7 = _iterator7.n()).done;) {
@@ -329,12 +335,9 @@ var tokenize = function tokenize(text) {
329
335
  };
330
336
  var extractPreviousWord = function extractPreviousWord(text) {
331
337
  // Only consider the current sentence/line the user is typing in.
332
- // eslint-disable-next-line require-unicode-regexp
333
- var sentences = text.split(/[\n.?!]+/);
338
+ var sentences = text.split(SENTENCE_BOUNDARY_REGEX);
334
339
  var currentSentence = sentences[sentences.length - 1];
335
-
336
- // eslint-disable-next-line require-unicode-regexp
337
- var words = currentSentence.trimEnd().split(/\s+/);
340
+ var words = currentSentence.trimEnd().split(WHITESPACE_SPLIT_REGEX);
338
341
  return words.length >= 2 ? words[words.length - 2] : '';
339
342
  };
340
343
 
@@ -473,8 +476,7 @@ export var predict = function predict(textBefore) {
473
476
  // }
474
477
 
475
478
  // ── Step 2: Prefix completion (≥3 chars typed) ──────────────────────────
476
- // eslint-disable-next-line require-unicode-regexp
477
- if (textBefore.length > 0 && /\s$/.test(textBefore)) {
479
+ if (textBefore.length > 0 && TRAILING_WHITESPACE_REGEX.test(textBefore)) {
478
480
  return null;
479
481
  }
480
482
  var trimmed = textBefore.trimEnd();
@@ -40,17 +40,17 @@ export interface AutocompletePluginOptions {
40
40
  * Use this to serve vectors from a CDN or media service in production.
41
41
  */
42
42
  getVectorsBinaryUrl?: () => Promise<string>;
43
+ /**
44
+ * User locale used to determine whether autocomplete should run.
45
+ * Defaults to browser locale when omitted.
46
+ */
47
+ locale?: string;
43
48
  /**
44
49
  * Subscription for hosts with live external context (e.g. Rovo chat). The plugin
45
50
  * re-fetches via getContext() on each notification and unsubscribes on destroy.
46
51
  * Only meaningful alongside `getContext`; without it each notification is a no-op.
47
52
  */
48
53
  subscribeToContextUpdates?: (onContextUpdated: () => void) => () => void;
49
- /**
50
- * User locale used to determine whether autocomplete should run.
51
- * Defaults to browser locale when omitted.
52
- */
53
- locale?: string;
54
54
  /**
55
55
  * When true, uses on-device inference via WebGPU (MLC WebLLM) instead of
56
56
  * the network-based slow-lane backend. Defaults to false (network client).
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@atlaskit/editor-plugin-autocomplete",
3
- "version": "4.0.4",
3
+ "version": "4.0.6",
4
4
  "description": "Client-side text autocomplete plugin for @atlaskit/editor-core",
5
5
  "author": "Atlassian Pty Ltd",
6
6
  "license": "Apache-2.0",
@@ -27,7 +27,7 @@
27
27
  "wink-nlp": "^2.4.0"
28
28
  },
29
29
  "peerDependencies": {
30
- "@atlaskit/editor-common": "^116.15.0",
30
+ "@atlaskit/editor-common": "^116.21.0",
31
31
  "@atlaskit/editor-plugin-analytics": "^12.0.0",
32
32
  "react": "^18.2.0"
33
33
  },
@@ -48,6 +48,11 @@ const MAX_INGESTED_CONTEXT_TEXTS = 50;
48
48
  // on every word boundary for the plugin's lifetime.
49
49
  const MAX_CONTEXT_REFRESH_ATTEMPTS = 5;
50
50
 
51
+ // eslint-disable-next-line require-unicode-regexp
52
+ const TRAILING_NON_BOUNDARY_TOKEN_REGEX = /([^\s.,;:!?]*)$/;
53
+ // eslint-disable-next-line require-unicode-regexp
54
+ const WORD_BOUNDARY_CHARS_REGEX = /[\s.,;:!?]/;
55
+
51
56
  const hasDestroy = (
52
57
  client: ReturnType<typeof createSlowLaneClient> | LocalSlowLaneClient,
53
58
  ): client is LocalSlowLaneClient => 'destroy' in client && typeof client.destroy === 'function';
@@ -214,17 +219,17 @@ export interface AutocompletePluginOptions {
214
219
  * Use this to serve vectors from a CDN or media service in production.
215
220
  */
216
221
  getVectorsBinaryUrl?: () => Promise<string>;
222
+ /**
223
+ * User locale used to determine whether autocomplete should run.
224
+ * Defaults to browser locale when omitted.
225
+ */
226
+ locale?: string;
217
227
  /**
218
228
  * Subscription for hosts with live external context (e.g. Rovo chat). The plugin
219
229
  * re-fetches via getContext() on each notification and unsubscribes on destroy.
220
230
  * Only meaningful alongside `getContext`; without it each notification is a no-op.
221
231
  */
222
232
  subscribeToContextUpdates?: (onContextUpdated: () => void) => () => void;
223
- /**
224
- * User locale used to determine whether autocomplete should run.
225
- * Defaults to browser locale when omitted.
226
- */
227
- locale?: string;
228
233
  /**
229
234
  * When true, uses on-device inference via WebGPU (MLC WebLLM) instead of
230
235
  * the network-based slow-lane backend. Defaults to false (network client).
@@ -257,6 +262,8 @@ const buildSlowLaneText = (docText: string, context?: AutocompleteContext): stri
257
262
  const createMidWordCharacterRegex = (): RegExp | null => {
258
263
  try {
259
264
  // string-built regex avoids TS1501 under the package's ES5 build target
265
+ // Ignored via go/ees019
266
+ // eslint-disable-next-line e18e/prefer-static-regex
260
267
  return new RegExp('[\\p{L}\\p{N}_]', 'u');
261
268
  } catch {
262
269
  return null;
@@ -305,8 +312,7 @@ const getTypedLengthForPrediction = (textBefore: string): number => {
305
312
  return 0;
306
313
  }
307
314
 
308
- // eslint-disable-next-line require-unicode-regexp
309
- const trailingToken = textBefore.match(/([^\s.,;:!?]*)$/)?.[1] ?? '';
315
+ const trailingToken = textBefore.match(TRAILING_NON_BOUNDARY_TOKEN_REGEX)?.[1] ?? '';
310
316
  return trailingToken.length;
311
317
  };
312
318
 
@@ -647,16 +653,14 @@ export const createAutocompletePlugin = (
647
653
  }
648
654
 
649
655
  const lastChar = newText[newText.length - 1];
650
- // eslint-disable-next-line require-unicode-regexp
651
- if (!/[\s.,;:!?]/.test(lastChar)) {
656
+ if (!WORD_BOUNDARY_CHARS_REGEX.test(lastChar)) {
652
657
  return;
653
658
  }
654
659
 
655
660
  // Only fire if the previous state did not already end on a boundary,
656
661
  // so we don't double-count when multiple boundary chars are inserted.
657
662
  const prevLastChar = prevText[prevText.length - 1];
658
- // eslint-disable-next-line require-unicode-regexp
659
- if (prevLastChar && /[\s.,;:!?]/.test(prevLastChar)) {
663
+ if (prevLastChar && WORD_BOUNDARY_CHARS_REGEX.test(prevLastChar)) {
660
664
  return;
661
665
  }
662
666
 
@@ -174,6 +174,8 @@ interface MinimalGpu {
174
174
  // ─── Constants ───────────────────────────────────────────────────────────────
175
175
 
176
176
  const DEFAULT_DEBOUNCE_MS = 300;
177
+ // eslint-disable-next-line require-unicode-regexp
178
+ const OOM_REGEX = /\boom\b/;
177
179
 
178
180
  export const LOCAL_MLC_CAUSAL_MODEL_ID = 'SmolLM2-135M-Instruct-q0f16-MLC';
179
181
 
@@ -769,8 +771,7 @@ export const createLocalSlowLaneClient = (
769
771
  }
770
772
  if (
771
773
  lower.includes('out of memory') ||
772
- // eslint-disable-next-line require-unicode-regexp
773
- /\boom\b/.test(lower) ||
774
+ OOM_REGEX.test(lower) ||
774
775
  lower.includes('allocation') ||
775
776
  lower.includes('exceeds') ||
776
777
  lower.includes('buffer size') ||
@@ -29,6 +29,8 @@ export interface TypeaheadEncodingsResponse {
29
29
 
30
30
  // eslint-disable-next-line require-unicode-regexp
31
31
  const WORD_BOUNDARY_CHARS = /[\s.,;:!?]/;
32
+ // eslint-disable-next-line require-unicode-regexp
33
+ const TRAILING_SLASH_REGEX = /\/$/;
32
34
  const DEFAULT_DEBOUNCE_MS = 300;
33
35
 
34
36
  /**
@@ -94,8 +96,7 @@ export const createSlowLaneClient = (
94
96
  return;
95
97
  }
96
98
 
97
- // eslint-disable-next-line require-unicode-regexp
98
- const url = `${baseUrl.replace(/\/$/, '')}${endpoint}`;
99
+ const url = `${baseUrl.replace(TRAILING_SLASH_REGEX, '')}${endpoint}`;
99
100
  const payload: TypeaheadEncodingsRequest = {
100
101
  text,
101
102
  session_id: sessionId,
@@ -29,6 +29,12 @@ import { getStoredContextVector, getStoredLmLogits } from './slow-lane-client';
29
29
 
30
30
  // eslint-disable-next-line require-unicode-regexp
31
31
  const PUNCTUATION_BOUNDARY_REGEX = /^[.,;:!?()\[\]{}"'`]+|[.,;:!?()\[\]{}"'`]+$/g;
32
+ // eslint-disable-next-line require-unicode-regexp
33
+ const WHITESPACE_SPLIT_REGEX = /\s+/;
34
+ // eslint-disable-next-line require-unicode-regexp
35
+ const SENTENCE_BOUNDARY_REGEX = /[\n.?!]+/;
36
+ // eslint-disable-next-line require-unicode-regexp
37
+ const TRAILING_WHITESPACE_REGEX = /\s$/;
32
38
 
33
39
  const MIN_PREFIX_LENGTH = 3;
34
40
  const MAX_CANDIDATES = 200;
@@ -275,8 +281,8 @@ const getContextVectorForScoring = (textBefore: string): Float32Array | null =>
275
281
 
276
282
  const tokenize = (text: string): string[] => {
277
283
  const tokens: string[] = [];
278
- // eslint-disable-next-line require-unicode-regexp, @atlassian/perf-linting/no-expensive-split-replace
279
- for (const raw of text.toLowerCase().split(/\s+/)) {
284
+ // eslint-disable-next-line @atlassian/perf-linting/no-expensive-split-replace
285
+ for (const raw of text.toLowerCase().split(WHITESPACE_SPLIT_REGEX)) {
280
286
  // eslint-disable-next-line @atlassian/perf-linting/no-expensive-split-replace
281
287
  const clean = raw.replace(PUNCTUATION_BOUNDARY_REGEX, '');
282
288
  if (clean.length >= 2) {
@@ -288,12 +294,10 @@ const tokenize = (text: string): string[] => {
288
294
 
289
295
  const extractPreviousWord = (text: string): string => {
290
296
  // Only consider the current sentence/line the user is typing in.
291
- // eslint-disable-next-line require-unicode-regexp
292
- const sentences = text.split(/[\n.?!]+/);
297
+ const sentences = text.split(SENTENCE_BOUNDARY_REGEX);
293
298
  const currentSentence = sentences[sentences.length - 1];
294
299
 
295
- // eslint-disable-next-line require-unicode-regexp
296
- const words = currentSentence.trimEnd().split(/\s+/);
300
+ const words = currentSentence.trimEnd().split(WHITESPACE_SPLIT_REGEX);
297
301
  return words.length >= 2 ? words[words.length - 2] : '';
298
302
  };
299
303
 
@@ -442,8 +446,7 @@ export const predict = (textBefore: string): string | null => {
442
446
  // }
443
447
 
444
448
  // ── Step 2: Prefix completion (≥3 chars typed) ──────────────────────────
445
- // eslint-disable-next-line require-unicode-regexp
446
- if (textBefore.length > 0 && /\s$/.test(textBefore)) {
449
+ if (textBefore.length > 0 && TRAILING_WHITESPACE_REGEX.test(textBefore)) {
447
450
  return null;
448
451
  }
449
452