@atlaskit/editor-plugin-autocomplete 6.0.0 → 8.0.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.
Files changed (61) hide show
  1. package/CHANGELOG.md +20 -0
  2. package/compass.yml +2 -2
  3. package/dist/cjs/analytics/ufo.js +3 -2
  4. package/dist/cjs/pm-plugins/artifacts-manifest.js +210 -0
  5. package/dist/cjs/pm-plugins/autocomplete-plugin.js +10 -2
  6. package/dist/cjs/pm-plugins/local-slow-lane-client.js +68 -153
  7. package/dist/cjs/pm-plugins/scoring-pipeline.js +130 -28
  8. package/dist/cjs/pm-plugins/text-predictor.js +45 -106
  9. package/dist/es2019/analytics/ufo.js +4 -2
  10. package/dist/es2019/pm-plugins/artifacts-manifest.js +126 -0
  11. package/dist/es2019/pm-plugins/autocomplete-plugin.js +10 -2
  12. package/dist/es2019/pm-plugins/local-slow-lane-client.js +8 -78
  13. package/dist/es2019/pm-plugins/scoring-pipeline.js +97 -21
  14. package/dist/es2019/pm-plugins/text-predictor.js +17 -59
  15. package/dist/esm/analytics/ufo.js +3 -2
  16. package/dist/esm/pm-plugins/artifacts-manifest.js +204 -0
  17. package/dist/esm/pm-plugins/autocomplete-plugin.js +10 -2
  18. package/dist/esm/pm-plugins/local-slow-lane-client.js +67 -148
  19. package/dist/esm/pm-plugins/scoring-pipeline.js +128 -26
  20. package/dist/esm/pm-plugins/text-predictor.js +45 -98
  21. package/dist/types/analytics/ufo.d.ts +1 -0
  22. package/dist/types/pm-plugins/artifacts-manifest.d.ts +55 -0
  23. package/dist/types/pm-plugins/autocomplete-plugin.d.ts +5 -11
  24. package/dist/types/pm-plugins/scoring-pipeline.d.ts +14 -0
  25. package/dist/types/pm-plugins/text-predictor.d.ts +0 -1
  26. package/package.json +3 -6
  27. package/src/analytics/ufo.ts +5 -0
  28. package/src/pm-plugins/artifacts-manifest.ts +151 -0
  29. package/src/pm-plugins/autocomplete-plugin.ts +14 -12
  30. package/src/pm-plugins/local-slow-lane-client.ts +10 -92
  31. package/src/pm-plugins/scoring-pipeline.ts +115 -23
  32. package/src/pm-plugins/text-predictor.ts +28 -76
  33. package/dist/cjs/pm-plugins/data/combined_l2_l3_pos_tags.json +0 -73571
  34. package/dist/cjs/pm-plugins/data/first_token_to_words.json +0 -1
  35. package/dist/cjs/pm-plugins/data/ghost_pos_tags.json +0 -43
  36. package/dist/cjs/pm-plugins/data/grammar_transitions_10k.json +0 -46
  37. package/dist/cjs/pm-plugins/data/l3_vocabulary.json +0 -20002
  38. package/dist/cjs/pm-plugins/data/vocabulary_10k.json +0 -38794
  39. package/dist/cjs/pm-plugins/data/word_index_10k.json +0 -7762
  40. package/dist/es2019/pm-plugins/data/combined_l2_l3_pos_tags.json +0 -73571
  41. package/dist/es2019/pm-plugins/data/first_token_to_words.json +0 -1
  42. package/dist/es2019/pm-plugins/data/ghost_pos_tags.json +0 -43
  43. package/dist/es2019/pm-plugins/data/grammar_transitions_10k.json +0 -46
  44. package/dist/es2019/pm-plugins/data/l3_vocabulary.json +0 -20002
  45. package/dist/es2019/pm-plugins/data/vocabulary_10k.json +0 -38794
  46. package/dist/es2019/pm-plugins/data/word_index_10k.json +0 -7762
  47. package/dist/esm/pm-plugins/data/combined_l2_l3_pos_tags.json +0 -73571
  48. package/dist/esm/pm-plugins/data/first_token_to_words.json +0 -1
  49. package/dist/esm/pm-plugins/data/ghost_pos_tags.json +0 -43
  50. package/dist/esm/pm-plugins/data/grammar_transitions_10k.json +0 -46
  51. package/dist/esm/pm-plugins/data/l3_vocabulary.json +0 -20002
  52. package/dist/esm/pm-plugins/data/vocabulary_10k.json +0 -38794
  53. package/dist/esm/pm-plugins/data/word_index_10k.json +0 -7762
  54. package/src/pm-plugins/data/combined_l2_l3_pos_tags.json +0 -73571
  55. package/src/pm-plugins/data/first_token_to_words.json +0 -1
  56. package/src/pm-plugins/data/ghost_pos_tags.json +0 -43
  57. package/src/pm-plugins/data/grammar_transitions_10k.json +0 -46
  58. package/src/pm-plugins/data/l3_vocabulary.json +0 -20002
  59. package/src/pm-plugins/data/vocabulary_10k.json +0 -38794
  60. package/src/pm-plugins/data/word-vectors_10k.bin +0 -3
  61. package/src/pm-plugins/data/word_index_10k.json +0 -7762
@@ -1,23 +1,22 @@
1
1
  "use strict";
2
2
 
3
3
  var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
4
- var _typeof3 = require("@babel/runtime/helpers/typeof");
5
4
  Object.defineProperty(exports, "__esModule", {
6
5
  value: true
7
6
  });
8
7
  exports.predict = exports.loadVectorsAsync = exports.loadDefaultVocabulary = exports.initVocabulary = exports.initVectors = exports.initL3Vocabulary = exports.ingestDocumentPage = exports.incrementSessionFreq = exports.getPredictorStatus = exports.getLastPredictionDebug = void 0;
9
8
  var _regenerator = _interopRequireDefault(require("@babel/runtime/regenerator"));
10
- var _asyncToGenerator2 = _interopRequireDefault(require("@babel/runtime/helpers/asyncToGenerator"));
11
9
  var _typeof2 = _interopRequireDefault(require("@babel/runtime/helpers/typeof"));
10
+ var _asyncToGenerator2 = _interopRequireDefault(require("@babel/runtime/helpers/asyncToGenerator"));
12
11
  var _slicedToArray2 = _interopRequireDefault(require("@babel/runtime/helpers/slicedToArray"));
13
12
  var _createClass2 = _interopRequireDefault(require("@babel/runtime/helpers/createClass"));
14
13
  var _classCallCheck2 = _interopRequireDefault(require("@babel/runtime/helpers/classCallCheck"));
15
14
  var _defineProperty2 = _interopRequireDefault(require("@babel/runtime/helpers/defineProperty"));
16
15
  var _ufo = require("../analytics/ufo");
16
+ var _artifactsManifest = require("./artifacts-manifest");
17
17
  var _debugMode = require("./debug-mode");
18
18
  var _scoringPipeline = require("./scoring-pipeline");
19
19
  var _slowLaneClient = require("./slow-lane-client");
20
- function _interopRequireWildcard(e, t) { if ("function" == typeof WeakMap) var r = new WeakMap(), n = new WeakMap(); return (_interopRequireWildcard = function _interopRequireWildcard(e, t) { if (!t && e && e.__esModule) return e; var o, i, f = { __proto__: null, default: e }; if (null === e || "object" != _typeof3(e) && "function" != typeof e) return f; if (o = t ? n : r) { if (o.has(e)) return o.get(e); o.set(e, f); } for (var _t4 in e) "default" !== _t4 && {}.hasOwnProperty.call(e, _t4) && ((i = (o = Object.defineProperty) && Object.getOwnPropertyDescriptor(e, _t4)) && (i.get || i.set) ? o(f, _t4, i) : f[_t4] = e[_t4]); return f; })(e, t); }
21
20
  function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; }
22
21
  function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { (0, _defineProperty2.default)(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; }
23
22
  function _createForOfIteratorHelper(r, e) { var t = "undefined" != typeof Symbol && r[Symbol.iterator] || r["@@iterator"]; if (!t) { if (Array.isArray(r) || (t = _unsupportedIterableToArray(r)) || e && r && "number" == typeof r.length) { t && (r = t); var _n = 0, F = function F() {}; return { s: F, n: function n() { return _n >= r.length ? { done: !0 } : { done: !1, value: r[_n++] }; }, e: function e(r) { throw r; }, f: F }; } throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } var o, a = !0, u = !1; return { s: function s() { t = t.call(r); }, n: function n() { var r = t.next(); return a = r.done, r; }, e: function e(r) { u = !0, o = r; }, f: function f() { try { a || null == t.return || t.return(); } finally { if (u) throw o; } } }; }
@@ -37,9 +36,9 @@ function _arrayLikeToArray(r, a) { (null == a || a > r.length) && (a = r.length)
37
36
  *
38
37
  * Session personalization (L1): words the user types are incrementally boosted
39
38
  * via incrementSessionFreq(), called on word boundaries from the plugin.
40
- */ // The vocabulary, L3 and word-index JSON payloads are dynamically imported in
41
- // loadDefaultVocabulary / loadVectorsAsync so their (large) contents stay out of
42
- // the editor's main chunk and only load when autocomplete is initialised.
39
+ */ // The vocabulary, L3 and word-index payloads are fetched from the CDN in
40
+ // loadDefaultVocabulary / loadVectorsAsync so their (large) contents never enter
41
+ // the editor's bundle and only load when autocomplete is initialised.
43
42
  // ─── Constants ───────────────────────────────────────────────────────────────
44
43
 
45
44
  // eslint-disable-next-line require-unicode-regexp
@@ -689,56 +688,10 @@ var predict = exports.predict = function predict(textBefore) {
689
688
 
690
689
  // ─── Data Loading ────────────────────────────────────────────────────────────
691
690
 
692
- /**
693
- * Unwrap a dynamically imported JSON module to its parsed value, handling both
694
- * interop modes AFM's bundler chain emits: a `.default`-wrapped namespace
695
- * (classic webpack) and a named-exports namespace (webpack 5 / atlaspack JSON
696
- * modules, where `default` can be a misleading scalar). Named exports are
697
- * preferred when present. The caller declares the JSON `shape` because a dense
698
- * array and a sparse numeric-keyed object are emitted identically as named
699
- * exports. Kept in lock-step with the matching helper in local-slow-lane-client.ts.
700
- */
701
- function unwrapJsonModule(mod, shape) {
702
- if (mod == null || (0, _typeof2.default)(mod) !== 'object') {
703
- return null;
704
- }
705
- var namespace = mod;
706
- var ownKeys = Object.keys(namespace).filter(function (k) {
707
- return k !== 'default' && k !== '__esModule';
708
- });
709
- if (ownKeys.length > 0) {
710
- if (shape === 'array') {
711
- var len = ownKeys.length;
712
- var arr = new Array(len);
713
- for (var i = 0; i < len; i++) {
714
- arr[i] = namespace[String(i)];
715
- }
716
- return arr;
717
- }
718
- var obj = {};
719
- var _iterator1 = _createForOfIteratorHelper(ownKeys),
720
- _step1;
721
- try {
722
- for (_iterator1.s(); !(_step1 = _iterator1.n()).done;) {
723
- var k = _step1.value;
724
- obj[k] = namespace[k];
725
- }
726
- } catch (err) {
727
- _iterator1.e(err);
728
- } finally {
729
- _iterator1.f();
730
- }
731
- return obj;
732
- }
733
- if ('default' in namespace && namespace.default != null) {
734
- return namespace.default;
735
- }
736
- return null;
737
- }
738
691
  var loadVectorsAsync = exports.loadVectorsAsync = /*#__PURE__*/function () {
739
692
  var _ref6 = (0, _asyncToGenerator2.default)( /*#__PURE__*/_regenerator.default.mark(function _callee(options) {
740
693
  var _options$isLocalLLM;
741
- var isLocalLLM, surface, url, _wordIndexOuter$index, res, buffer, float32, wordIndexModule, wordIndexOuter, wordIndex, nWords, dim, _t, _t2;
694
+ var isLocalLLM, surface, url, res, buffer, float32, wordIndexPayload, wrappedIndex, wordIndex, nWords, dim, _t, _t2;
742
695
  return _regenerator.default.wrap(function (_context) {
743
696
  while (1) switch (_context.prev = _context.next) {
744
697
  case 0:
@@ -748,14 +701,6 @@ var loadVectorsAsync = exports.loadVectorsAsync = /*#__PURE__*/function () {
748
701
  }
749
702
  return _context.abrupt("return");
750
703
  case 1:
751
- if (options !== null && options !== void 0 && options.getBinaryUrl) {
752
- _context.next = 2;
753
- break;
754
- }
755
- // eslint-disable-next-line no-console
756
- console.warn('[text-predictor] loadVectorsAsync called without a getBinaryUrl — vectors will not load. Pass getVectorsBinaryUrl via plugin options.');
757
- return _context.abrupt("return");
758
- case 2:
759
704
  isLocalLLM = (_options$isLocalLLM = options === null || options === void 0 ? void 0 : options.isLocalLLM) !== null && _options$isLocalLLM !== void 0 ? _options$isLocalLLM : false;
760
705
  surface = options === null || options === void 0 ? void 0 : options.surface;
761
706
  vectorsLoadStarted = true;
@@ -764,16 +709,16 @@ var loadVectorsAsync = exports.loadVectorsAsync = /*#__PURE__*/function () {
764
709
  }, surface ? {
765
710
  surface: surface
766
711
  } : {}));
767
- _context.prev = 3;
768
- _context.next = 4;
769
- return options.getBinaryUrl();
770
- case 4:
712
+ _context.prev = 2;
713
+ _context.next = 3;
714
+ return (0, _artifactsManifest.getArtifactUrl)(_artifactsManifest.ARTIFACT_NAME.WORD_VECTORS);
715
+ case 3:
771
716
  url = _context.sent;
772
- _context.next = 6;
717
+ _context.next = 5;
773
718
  break;
774
- case 5:
775
- _context.prev = 5;
776
- _t = _context["catch"](3);
719
+ case 4:
720
+ _context.prev = 4;
721
+ _t = _context["catch"](2);
777
722
  vectorsLoadStarted = false;
778
723
  (0, _ufo.failExp)(_ufo.EXPERIENCE_NAME.LOAD_VECTORS, 'singleton', _objectSpread({
779
724
  isLocalLLM: isLocalLLM,
@@ -784,14 +729,14 @@ var loadVectorsAsync = exports.loadVectorsAsync = /*#__PURE__*/function () {
784
729
  // eslint-disable-next-line no-console
785
730
  console.warn('[text-predictor] Failed to resolve vectors URL:', _t);
786
731
  return _context.abrupt("return");
787
- case 6:
788
- _context.prev = 6;
789
- _context.next = 7;
732
+ case 5:
733
+ _context.prev = 5;
734
+ _context.next = 6;
790
735
  return fetch(url);
791
- case 7:
736
+ case 6:
792
737
  res = _context.sent;
793
738
  if (res.ok) {
794
- _context.next = 8;
739
+ _context.next = 7;
795
740
  break;
796
741
  }
797
742
  vectorsLoadStarted = false;
@@ -805,26 +750,26 @@ var loadVectorsAsync = exports.loadVectorsAsync = /*#__PURE__*/function () {
805
750
  // eslint-disable-next-line no-console
806
751
  console.warn("[text-predictor] Failed to load vectors: ".concat(res.status));
807
752
  return _context.abrupt("return");
753
+ case 7:
754
+ _context.next = 8;
755
+ return res.arrayBuffer();
808
756
  case 8:
757
+ buffer = _context.sent;
758
+ float32 = new Float32Array(buffer); // The word index is published either bare or wrapped as `{ "index": {…} }`
759
+ // (the wrapper exists so no real entry, e.g. the word "default", can shadow
760
+ // a bundler-synthesised ESM export). Detect the wrapper by value type, not
761
+ // by key presence — "index" is itself a plausible vocabulary word, and in a
762
+ // bare index every value is a number.
809
763
  _context.next = 9;
810
- return res.arrayBuffer();
764
+ return (0, _artifactsManifest.fetchArtifactJson)(_artifactsManifest.ARTIFACT_NAME.WORD_INDEX);
811
765
  case 9:
812
- buffer = _context.sent;
813
- float32 = new Float32Array(buffer); // word_index_10k.json is wrapped as `{ "index": {…} }` so no real entry
814
- // (e.g. the word "default") can shadow the synthetic ESM `default` export
815
- // the bundler creates for dynamically-imported JSON.
816
- _context.next = 10;
817
- return Promise.resolve().then(function () {
818
- return _interopRequireWildcard(require( /* webpackChunkName: "@atlaskit-internal_editor-plugin-autocomplete-word-index-10k" */'./data/word_index_10k.json'));
819
- });
820
- case 10:
821
- wordIndexModule = _context.sent;
822
- wordIndexOuter = unwrapJsonModule(wordIndexModule, 'object');
823
- wordIndex = (_wordIndexOuter$index = wordIndexOuter === null || wordIndexOuter === void 0 ? void 0 : wordIndexOuter.index) !== null && _wordIndexOuter$index !== void 0 ? _wordIndexOuter$index : {};
766
+ wordIndexPayload = _context.sent;
767
+ wrappedIndex = wordIndexPayload === null || wordIndexPayload === void 0 ? void 0 : wordIndexPayload.index;
768
+ wordIndex = (0, _typeof2.default)(wrappedIndex) === 'object' && wrappedIndex !== null ? wrappedIndex : wordIndexPayload !== null && wordIndexPayload !== void 0 ? wordIndexPayload : {};
824
769
  nWords = Object.keys(wordIndex).length;
825
770
  if (nWords === 0) {
826
771
  // eslint-disable-next-line no-console
827
- console.warn('[text-predictor] word_index_10k.json missing its `index` wrapper — wordIndex is empty, semantic scoring will be a no-op.');
772
+ console.warn("[text-predictor] ".concat(_artifactsManifest.ARTIFACT_NAME.WORD_INDEX, " was empty \u2014 semantic scoring will be a no-op."));
828
773
  }
829
774
  dim = float32.length / nWords;
830
775
  vectorStore = {
@@ -848,11 +793,11 @@ var loadVectorsAsync = exports.loadVectorsAsync = /*#__PURE__*/function () {
848
793
  sizeBytes: float32.byteLength
849
794
  });
850
795
  }
851
- _context.next = 12;
796
+ _context.next = 11;
852
797
  break;
853
- case 11:
854
- _context.prev = 11;
855
- _t2 = _context["catch"](6);
798
+ case 10:
799
+ _context.prev = 10;
800
+ _t2 = _context["catch"](5);
856
801
  vectorsLoadStarted = false;
857
802
  (0, _ufo.failExp)(_ufo.EXPERIENCE_NAME.LOAD_VECTORS, 'singleton', _objectSpread({
858
803
  isLocalLLM: isLocalLLM,
@@ -862,11 +807,11 @@ var loadVectorsAsync = exports.loadVectorsAsync = /*#__PURE__*/function () {
862
807
  } : {}));
863
808
  // eslint-disable-next-line no-console
864
809
  console.warn('[text-predictor] Failed to load vectors:', _t2);
865
- case 12:
810
+ case 11:
866
811
  case "end":
867
812
  return _context.stop();
868
813
  }
869
- }, _callee, null, [[3, 5], [6, 11]]);
814
+ }, _callee, null, [[2, 4], [5, 10]]);
870
815
  }));
871
816
  return function loadVectorsAsync(_x) {
872
817
  return _ref6.apply(this, arguments);
@@ -887,7 +832,7 @@ var loadDefaultVocabulary = exports.loadDefaultVocabulary = function loadDefault
887
832
  var isLocalLLM = (_options$isLocalLLM2 = options === null || options === void 0 ? void 0 : options.isLocalLLM) !== null && _options$isLocalLLM2 !== void 0 ? _options$isLocalLLM2 : false;
888
833
  var surface = options === null || options === void 0 ? void 0 : options.surface;
889
834
  vocabularyLoadPromise = (0, _asyncToGenerator2.default)( /*#__PURE__*/_regenerator.default.mark(function _callee2() {
890
- var _yield$Promise$all, _yield$Promise$all2, vocabularyModule, l3VocabularyModule, vocabularyData, l3VocabularyData, terms, _t3;
835
+ var _yield$Promise$all, _yield$Promise$all2, vocabularyData, l3VocabularyData, terms, _t3;
891
836
  return _regenerator.default.wrap(function (_context2) {
892
837
  while (1) switch (_context2.prev = _context2.next) {
893
838
  case 0:
@@ -898,23 +843,17 @@ var loadDefaultVocabulary = exports.loadDefaultVocabulary = function loadDefault
898
843
  } : {}));
899
844
  _context2.prev = 1;
900
845
  _context2.next = 2;
901
- return Promise.all([Promise.resolve().then(function () {
902
- return _interopRequireWildcard(require( /* webpackChunkName: "@atlaskit-internal_editor-plugin-autocomplete-vocabulary-10k" */'./data/vocabulary_10k.json'));
903
- }), Promise.resolve().then(function () {
904
- return _interopRequireWildcard(require( /* webpackChunkName: "@atlaskit-internal_editor-plugin-autocomplete-l3-vocabulary" */'./data/l3_vocabulary.json'));
905
- })]);
846
+ return Promise.all([(0, _artifactsManifest.fetchArtifactJson)(_artifactsManifest.ARTIFACT_NAME.VOCABULARY), (0, _artifactsManifest.fetchArtifactJson)(_artifactsManifest.ARTIFACT_NAME.L3_VOCABULARY)]);
906
847
  case 2:
907
848
  _yield$Promise$all = _context2.sent;
908
849
  _yield$Promise$all2 = (0, _slicedToArray2.default)(_yield$Promise$all, 2);
909
- vocabularyModule = _yield$Promise$all2[0];
910
- l3VocabularyModule = _yield$Promise$all2[1];
911
- vocabularyData = unwrapJsonModule(vocabularyModule, 'object');
912
- l3VocabularyData = unwrapJsonModule(l3VocabularyModule, 'array');
850
+ vocabularyData = _yield$Promise$all2[0];
851
+ l3VocabularyData = _yield$Promise$all2[1];
913
852
  if (!((vocabularyData === null || vocabularyData === void 0 ? void 0 : vocabularyData.words) == null || !Array.isArray(l3VocabularyData))) {
914
853
  _context2.next = 3;
915
854
  break;
916
855
  }
917
- throw new Error('[text-predictor] vocabulary JSON modules could not be unwrapped');
856
+ throw new Error("[text-predictor] unexpected vocabulary payload \u2014 " + "".concat(_artifactsManifest.ARTIFACT_NAME.VOCABULARY, " ").concat((vocabularyData === null || vocabularyData === void 0 ? void 0 : vocabularyData.words) == null ? 'is missing its `words` wrapper' : 'is valid', ", ") + "".concat(_artifactsManifest.ARTIFACT_NAME.L3_VOCABULARY, " ").concat(Array.isArray(l3VocabularyData) ? 'is valid' : 'is not an array'));
918
857
  case 3:
919
858
  terms = Object.entries(vocabularyData.words).map(function (_ref8) {
920
859
  var _ref9 = (0, _slicedToArray2.default)(_ref8, 2),
@@ -947,7 +886,7 @@ var loadDefaultVocabulary = exports.loadDefaultVocabulary = function loadDefault
947
886
  _t3 = _context2["catch"](1);
948
887
  (0, _ufo.failExp)(_ufo.EXPERIENCE_NAME.LOAD_VOCABULARY, 'singleton', _objectSpread({
949
888
  isLocalLLM: isLocalLLM,
950
- errorType: 'parse_error'
889
+ errorType: 'load_error'
951
890
  }, surface ? {
952
891
  surface: surface
953
892
  } : {}));
@@ -31,7 +31,8 @@ import { ConcurrentExperience, ExperiencePerformanceTypes, ExperienceTypes } fro
31
31
  export const EXPERIENCE_NAME = {
32
32
  SLOW_LANE_FETCH: 'slow-lane-fetch',
33
33
  LOAD_VOCABULARY: 'load-vocabulary',
34
- LOAD_VECTORS: 'load-vectors'
34
+ LOAD_VECTORS: 'load-vectors',
35
+ LOAD_GRAMMAR: 'load-grammar'
35
36
  };
36
37
  const isUfoEnabled = () => {
37
38
  var _process, _process$env;
@@ -54,7 +55,8 @@ const operationConfig = {
54
55
  const experiences = {
55
56
  [EXPERIENCE_NAME.SLOW_LANE_FETCH]: new ConcurrentExperience(EXPERIENCE_NAME.SLOW_LANE_FETCH, operationConfig),
56
57
  [EXPERIENCE_NAME.LOAD_VOCABULARY]: new ConcurrentExperience(EXPERIENCE_NAME.LOAD_VOCABULARY, operationConfig),
57
- [EXPERIENCE_NAME.LOAD_VECTORS]: new ConcurrentExperience(EXPERIENCE_NAME.LOAD_VECTORS, operationConfig)
58
+ [EXPERIENCE_NAME.LOAD_VECTORS]: new ConcurrentExperience(EXPERIENCE_NAME.LOAD_VECTORS, operationConfig),
59
+ [EXPERIENCE_NAME.LOAD_GRAMMAR]: new ConcurrentExperience(EXPERIENCE_NAME.LOAD_GRAMMAR, operationConfig)
58
60
  };
59
61
  export const startExp = (name, id, metadata) => {
60
62
  if (!isUfoEnabled()) {
@@ -0,0 +1,126 @@
1
+ /**
2
+ * Artifacts Manifest Client: resolves the autocomplete model artifacts hosted on
3
+ * the CDN.
4
+ *
5
+ * `GET /gateway/api/v1/autocomplete/artifacts` returns one presigned CDN URL per
6
+ * artifact for the tenant's active model version:
7
+ *
8
+ * { activeVersion, artifacts: [{ name, url, contentType, sizeBytes, checksum }] }
9
+ *
10
+ * Every loader in the plugin resolves its payload through here, so no model data
11
+ * is bundled with the package.
12
+ */
13
+
14
+ import { isAutocompleteDebugEnabled } from './debug-mode';
15
+
16
+ // ─── Types ───────────────────────────────────────────────────────────────────
17
+
18
+ /**
19
+ * Manifest artifact names. These are the manifest's lookup keys and are
20
+ * hyphenated, unlike the underscored filenames the artifacts are generated from.
21
+ */
22
+ export const ARTIFACT_NAME = {
23
+ FIRST_TOKEN_TO_WORDS: 'first-token-to-words.json',
24
+ GHOST_POS_TAGS: 'ghost-pos-tags.json',
25
+ GRAMMAR_TRANSITIONS: 'grammar-transitions-10k.json',
26
+ L3_VOCABULARY: 'l3-vocabulary.json',
27
+ POS_TAGS: 'combined-l2-l3-pos-tags.json',
28
+ VOCABULARY: 'vocabulary-10k.json',
29
+ WORD_INDEX: 'word-index-10k.json',
30
+ WORD_VECTORS: 'word-vectors-10k.bin'
31
+ };
32
+
33
+ /** A single entry in the artifacts manifest. */
34
+
35
+ /** Response body of the artifacts endpoint. */
36
+
37
+ // ─── Constants ───────────────────────────────────────────────────────────────
38
+
39
+ const MANIFEST_ENDPOINT = '/gateway/api/v1/autocomplete/artifacts';
40
+
41
+ /**
42
+ * Presigned artifact URLs expire ~10 minutes after the manifest is issued, so
43
+ * the manifest is re-requested well inside that window instead of being cached
44
+ * for the session and handing out dead URLs to a late loader.
45
+ */
46
+ const MANIFEST_TTL_MS = 5 * 60 * 1000;
47
+
48
+ // ─── Manifest ────────────────────────────────────────────────────────────────
49
+
50
+ let manifestPromise;
51
+ let manifestRequestedAt = 0;
52
+
53
+ /** Test-only: drops the memoised manifest so the next lookup re-requests it. */
54
+ export const resetArtifactsManifestCache = () => {
55
+ manifestPromise = undefined;
56
+ manifestRequestedAt = 0;
57
+ };
58
+ const requestManifest = async () => {
59
+ const res = await fetch(MANIFEST_ENDPOINT, {
60
+ method: 'GET',
61
+ headers: {
62
+ 'x-experience-id': 'confluence-smart-typeahead-artifacts',
63
+ 'x-product': 'confluence'
64
+ }
65
+ });
66
+ if (!res.ok) {
67
+ throw new Error(`[autocomplete-artifacts] Manifest request failed: ${res.status}`);
68
+ }
69
+ const body = await res.json();
70
+ if (!Array.isArray(body === null || body === void 0 ? void 0 : body.artifacts) || body.artifacts.length === 0) {
71
+ throw new Error('[autocomplete-artifacts] Manifest response contained no artifacts');
72
+ }
73
+ if (isAutocompleteDebugEnabled()) {
74
+ // eslint-disable-next-line no-console
75
+ console.log('[autocomplete-artifacts] Manifest loaded:', {
76
+ activeVersion: body.activeVersion,
77
+ names: body.artifacts.map(artifact => artifact.name)
78
+ });
79
+ }
80
+ return new Map(body.artifacts.map(artifact => [artifact.name, artifact]));
81
+ };
82
+ const getManifest = () => {
83
+ if (manifestPromise && Date.now() - manifestRequestedAt < MANIFEST_TTL_MS) {
84
+ return manifestPromise;
85
+ }
86
+ manifestRequestedAt = Date.now();
87
+ manifestPromise = requestManifest().catch(e => {
88
+ // Never cache a rejection — a transient failure would otherwise stop every
89
+ // later loader in the session from retrying.
90
+ resetArtifactsManifestCache();
91
+ throw e;
92
+ });
93
+ return manifestPromise;
94
+ };
95
+
96
+ // ─── Public API ──────────────────────────────────────────────────────────────
97
+
98
+ /**
99
+ * Resolve the CDN URL for an artifact.
100
+ *
101
+ * :raises Error: when the manifest cannot be fetched or omits the artifact.
102
+ */
103
+ export const getArtifactUrl = async name => {
104
+ var _manifest$get;
105
+ const manifest = await getManifest();
106
+ const url = (_manifest$get = manifest.get(name)) === null || _manifest$get === void 0 ? void 0 : _manifest$get.url;
107
+ if (!url) {
108
+ throw new Error(`[autocomplete-artifacts] Manifest has no URL for "${name}"`);
109
+ }
110
+ return url;
111
+ };
112
+
113
+ /**
114
+ * Download and parse a JSON artifact. The presigned URL carries its own auth
115
+ * token, so the request is sent without Atlassian headers or credentials.
116
+ *
117
+ * :raises Error: when the manifest lookup or the download fails.
118
+ */
119
+ export const fetchArtifactJson = async name => {
120
+ const url = await getArtifactUrl(name);
121
+ const res = await fetch(url);
122
+ if (!res.ok) {
123
+ throw new Error(`[autocomplete-artifacts] Failed to download "${name}": ${res.status}`);
124
+ }
125
+ return await res.json();
126
+ };
@@ -7,6 +7,7 @@ import { DecorationSet } from '@atlaskit/editor-prosemirror/view';
7
7
  import { isAutocompleteDebugEnabled } from './debug-mode';
8
8
  import { createGhostTextDecorationSet } from './ghost-text-decoration';
9
9
  import { createLocalSlowLaneClient } from './local-slow-lane-client';
10
+ import { loadGrammarDataAsync } from './scoring-pipeline';
10
11
  import { createSlowLaneClient, setDefaultSlowLaneClient, isWordBoundary } from './slow-lane-client';
11
12
  import { predict, loadDefaultVocabulary, loadVectorsAsync, incrementSessionFreq, ingestDocumentPage } from './text-predictor';
12
13
  export const autocompletePluginKey = new PluginKey('autocomplete');
@@ -665,7 +666,7 @@ export const createAutocompletePlugin = (options, api) => {
665
666
  return accepted;
666
667
  },
667
668
  focus: () => {
668
- var _options$useLocalMode, _options$useLocalMode2;
669
+ var _options$useLocalMode, _options$useLocalMode2, _options$useLocalMode3;
669
670
  if (!isAutocompleteEnabled) {
670
671
  return false;
671
672
  }
@@ -678,7 +679,6 @@ export const createAutocompletePlugin = (options, api) => {
678
679
  });
679
680
  });
680
681
  loadVectorsAsync({
681
- getBinaryUrl: options === null || options === void 0 ? void 0 : options.getVectorsBinaryUrl,
682
682
  isLocalLLM: (_options$useLocalMode2 = options === null || options === void 0 ? void 0 : options.useLocalModel) !== null && _options$useLocalMode2 !== void 0 ? _options$useLocalMode2 : false,
683
683
  surface
684
684
  }).catch(error => {
@@ -686,6 +686,14 @@ export const createAutocompletePlugin = (options, api) => {
686
686
  location: 'editor-plugin-autocomplete/loadVectorsAsync'
687
687
  });
688
688
  });
689
+ loadGrammarDataAsync({
690
+ isLocalLLM: (_options$useLocalMode3 = options === null || options === void 0 ? void 0 : options.useLocalModel) !== null && _options$useLocalMode3 !== void 0 ? _options$useLocalMode3 : false,
691
+ surface
692
+ }).catch(error => {
693
+ logException(error, {
694
+ location: 'editor-plugin-autocomplete/loadGrammarDataAsync'
695
+ });
696
+ });
689
697
  if (!hasIngestedPage) {
690
698
  hasIngestedPage = true;
691
699
  refreshContext({
@@ -37,6 +37,7 @@ import _defineProperty from "@babel/runtime/helpers/defineProperty";
37
37
  */
38
38
 
39
39
  import { abortExp, EXPERIENCE_NAME, failExp, startExp, succeedExp } from '../analytics/ufo';
40
+ import { ARTIFACT_NAME, fetchArtifactJson } from './artifacts-manifest';
40
41
  import { isAutocompleteDebugEnabled } from './debug-mode';
41
42
  import { isWordBoundary } from './slow-lane-client';
42
43
 
@@ -225,77 +226,9 @@ let prefixMapTokenIds = [];
225
226
  let bePayloadDataPromise;
226
227
 
227
228
  /**
228
- * Unwrap a dynamically imported JSON module to the parsed JSON value, working
229
- * across the two interop modes AFM's bundler chain emits:
230
- *
231
- * 1. **`.default`-wrapped namespace** — classic webpack (and Jest) hang the
232
- * JSON value under the `default` export.
233
- * 2. **Named-exports namespace** — webpack 5 / atlaspack with JSON
234
- * named-exports (or native ESM JSON modules) expose each top-level key as
235
- * a named export and shadow `default`, so `mod.default` can be `undefined`
236
- * (or some unrelated value) even though `mod` itself holds the data.
237
- *
238
- * The caller MUST declare the underlying JSON shape via `shape` because, in
239
- * named-exports mode, a dense array `["a","b"]` and a sparse numeric-keyed
240
- * object `{"5":"a","12":"b"}` are emitted identically (`{"0":..}` / `{"5":..}`);
241
- * no runtime heuristic can tell them apart, so only the caller knows which:
242
- *
243
- * - `'object'` — the JSON is a `{...}` (including sparse maps keyed by integer
244
- * IDs). The named exports are rebuilt into a plain object so `Object.entries`
245
- * yields the real keys, not synthetic array indices.
246
- * - `'array'` — the JSON is a `[...]`, reconstructed from the `0..n-1` indices.
247
- *
248
- * :param mod: The raw module object returned by `await import('./*.json')`.
249
- * :param shape: `'object'` if the source JSON is `{...}`, `'array'` if `[...]`.
250
- * :returns: The parsed JSON value, or `null` if neither interop mode applies.
251
- */
252
- function unwrapJsonModule(mod, shape) {
253
- if (mod == null || typeof mod !== 'object') {
254
- return null;
255
- }
256
- const namespace = mod;
257
-
258
- // Compute the named-export own-keys (strip synthetic markers).
259
- const ownKeys = Object.keys(namespace).filter(k => k !== 'default' && k !== '__esModule');
260
-
261
- // PREFER named exports when present — they always reflect the JSON's real
262
- // top-level keys / indices, regardless of what `default` happens to be.
263
- // Under JSON named-exports mode `default` is not necessarily the parsed
264
- // value (e.g. for `{"service": 0, ...}` it can be the number `0`, with the
265
- // real data in the named exports), so taking `default` first would corrupt it.
266
- if (ownKeys.length > 0) {
267
- if (shape === 'array') {
268
- // JSON arrays are dense; reconstruct from `0..length-1` indices.
269
- const len = ownKeys.length;
270
- const arr = new Array(len);
271
- for (let i = 0; i < len; i++) {
272
- arr[i] = namespace[String(i)];
273
- }
274
- return arr;
275
- }
276
- // shape === 'object'. Rebuild a plain object from the (stripped) own
277
- // keys so callers can `Object.entries()` it without iterating over
278
- // `default` / `__esModule`, and to detach from the module-namespace
279
- // object (which is sealed/non-extensible on some bundler outputs).
280
- const obj = {};
281
- for (const k of ownKeys) {
282
- obj[k] = namespace[k];
283
- }
284
- return obj;
285
- }
286
-
287
- // Fallback: no named exports — classic webpack JSON-module interop where
288
- // the whole parsed JSON value is hung under `default`. Trust it.
289
- if ('default' in namespace && namespace.default != null) {
290
- return namespace.default;
291
- }
292
- return null;
293
- }
294
-
295
- /**
296
- * Lazily load and build the BE-parity lookup tables from their JSON payloads.
297
- * The dynamic imports are split into their own async chunks so neither file is
298
- * bundled into the editor's main chunk unless local inference is initialised.
229
+ * Lazily load and build the BE-parity lookup tables from their CDN payloads.
230
+ * Both artifacts are fetched on demand so neither is downloaded unless local
231
+ * inference is initialised.
299
232
  *
300
233
  * :returns:
301
234
  * A promise that resolves once `firstTokenToWords`, `l2Words` and
@@ -304,15 +237,12 @@ function unwrapJsonModule(mod, shape) {
304
237
  const loadBePayloadData = () => {
305
238
  if (!bePayloadDataPromise) {
306
239
  bePayloadDataPromise = (async () => {
307
- const [firstTokenToWordsModule, vocabularyModule] = await Promise.all([import( /* webpackChunkName: "@atlaskit-internal_editor-plugin-autocomplete-first-token-to-words" */'./data/first_token_to_words.json'), import( /* webpackChunkName: "@atlaskit-internal_editor-plugin-autocomplete-vocabulary-10k" */'./data/vocabulary_10k.json')]);
308
- const firstTokenToWordsData = unwrapJsonModule(firstTokenToWordsModule, 'object');
309
- const vocabularyData = unwrapJsonModule(vocabularyModule, 'object');
240
+ const [firstTokenToWordsData, vocabularyData] = await Promise.all([fetchArtifactJson(ARTIFACT_NAME.FIRST_TOKEN_TO_WORDS), fetchArtifactJson(ARTIFACT_NAME.VOCABULARY)]);
310
241
  if (firstTokenToWordsData == null || (vocabularyData === null || vocabularyData === void 0 ? void 0 : vocabularyData.words) == null) {
311
242
  // Hard-fail with a precise message so the catch() in initEngine logs
312
- // exactly which import couldn't be unwrapped, rather than the generic
313
- // V8 "Cannot convert undefined or null to object" we hit before the
314
- // helper was added.
315
- throw new Error(`[LocalSlowLane] JSON module could not be unwrapped — ` + `firstTokenToWordsData=${firstTokenToWordsData == null ? 'null/undefined' : 'defined'}, ` + `vocabularyData=${vocabularyData == null ? 'null/undefined' : vocabularyData.words == null ? 'defined but missing .words' : 'defined'}`);
243
+ // exactly which payload was malformed, rather than the generic V8
244
+ // "Cannot convert undefined or null to object".
245
+ throw new Error(`[LocalSlowLane] unexpected artifact payload — ` + `firstTokenToWordsData=${firstTokenToWordsData == null ? 'null/undefined' : 'defined'}, ` + `vocabularyData=${vocabularyData == null ? 'null/undefined' : vocabularyData.words == null ? 'defined but missing .words' : 'defined'}`);
316
246
  }
317
247
  firstTokenToWords = new Map(Object.entries(firstTokenToWordsData).map(([tokenId, words]) => [Number(tokenId), words]));
318
248
  l2Words = new Set(Object.keys(vocabularyData.words));