@atlaskit/editor-plugin-autocomplete 6.0.0 → 7.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 (60) hide show
  1. package/CHANGELOG.md +14 -0
  2. package/dist/cjs/analytics/ufo.js +3 -2
  3. package/dist/cjs/pm-plugins/artifacts-manifest.js +210 -0
  4. package/dist/cjs/pm-plugins/autocomplete-plugin.js +10 -2
  5. package/dist/cjs/pm-plugins/local-slow-lane-client.js +68 -153
  6. package/dist/cjs/pm-plugins/scoring-pipeline.js +130 -28
  7. package/dist/cjs/pm-plugins/text-predictor.js +45 -106
  8. package/dist/es2019/analytics/ufo.js +4 -2
  9. package/dist/es2019/pm-plugins/artifacts-manifest.js +126 -0
  10. package/dist/es2019/pm-plugins/autocomplete-plugin.js +10 -2
  11. package/dist/es2019/pm-plugins/local-slow-lane-client.js +8 -78
  12. package/dist/es2019/pm-plugins/scoring-pipeline.js +97 -21
  13. package/dist/es2019/pm-plugins/text-predictor.js +17 -59
  14. package/dist/esm/analytics/ufo.js +3 -2
  15. package/dist/esm/pm-plugins/artifacts-manifest.js +204 -0
  16. package/dist/esm/pm-plugins/autocomplete-plugin.js +10 -2
  17. package/dist/esm/pm-plugins/local-slow-lane-client.js +67 -148
  18. package/dist/esm/pm-plugins/scoring-pipeline.js +128 -26
  19. package/dist/esm/pm-plugins/text-predictor.js +45 -98
  20. package/dist/types/analytics/ufo.d.ts +1 -0
  21. package/dist/types/pm-plugins/artifacts-manifest.d.ts +55 -0
  22. package/dist/types/pm-plugins/autocomplete-plugin.d.ts +5 -11
  23. package/dist/types/pm-plugins/scoring-pipeline.d.ts +14 -0
  24. package/dist/types/pm-plugins/text-predictor.d.ts +0 -1
  25. package/package.json +2 -5
  26. package/src/analytics/ufo.ts +5 -0
  27. package/src/pm-plugins/artifacts-manifest.ts +151 -0
  28. package/src/pm-plugins/autocomplete-plugin.ts +14 -12
  29. package/src/pm-plugins/local-slow-lane-client.ts +10 -92
  30. package/src/pm-plugins/scoring-pipeline.ts +115 -23
  31. package/src/pm-plugins/text-predictor.ts +26 -76
  32. package/dist/cjs/pm-plugins/data/combined_l2_l3_pos_tags.json +0 -73571
  33. package/dist/cjs/pm-plugins/data/first_token_to_words.json +0 -1
  34. package/dist/cjs/pm-plugins/data/ghost_pos_tags.json +0 -43
  35. package/dist/cjs/pm-plugins/data/grammar_transitions_10k.json +0 -46
  36. package/dist/cjs/pm-plugins/data/l3_vocabulary.json +0 -20002
  37. package/dist/cjs/pm-plugins/data/vocabulary_10k.json +0 -38794
  38. package/dist/cjs/pm-plugins/data/word_index_10k.json +0 -7762
  39. package/dist/es2019/pm-plugins/data/combined_l2_l3_pos_tags.json +0 -73571
  40. package/dist/es2019/pm-plugins/data/first_token_to_words.json +0 -1
  41. package/dist/es2019/pm-plugins/data/ghost_pos_tags.json +0 -43
  42. package/dist/es2019/pm-plugins/data/grammar_transitions_10k.json +0 -46
  43. package/dist/es2019/pm-plugins/data/l3_vocabulary.json +0 -20002
  44. package/dist/es2019/pm-plugins/data/vocabulary_10k.json +0 -38794
  45. package/dist/es2019/pm-plugins/data/word_index_10k.json +0 -7762
  46. package/dist/esm/pm-plugins/data/combined_l2_l3_pos_tags.json +0 -73571
  47. package/dist/esm/pm-plugins/data/first_token_to_words.json +0 -1
  48. package/dist/esm/pm-plugins/data/ghost_pos_tags.json +0 -43
  49. package/dist/esm/pm-plugins/data/grammar_transitions_10k.json +0 -46
  50. package/dist/esm/pm-plugins/data/l3_vocabulary.json +0 -20002
  51. package/dist/esm/pm-plugins/data/vocabulary_10k.json +0 -38794
  52. package/dist/esm/pm-plugins/data/word_index_10k.json +0 -7762
  53. package/src/pm-plugins/data/combined_l2_l3_pos_tags.json +0 -73571
  54. package/src/pm-plugins/data/first_token_to_words.json +0 -1
  55. package/src/pm-plugins/data/ghost_pos_tags.json +0 -43
  56. package/src/pm-plugins/data/grammar_transitions_10k.json +0 -46
  57. package/src/pm-plugins/data/l3_vocabulary.json +0 -20002
  58. package/src/pm-plugins/data/vocabulary_10k.json +0 -38794
  59. package/src/pm-plugins/data/word-vectors_10k.bin +0 -3
  60. package/src/pm-plugins/data/word_index_10k.json +0 -7762
@@ -4,19 +4,27 @@ var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefau
4
4
  Object.defineProperty(exports, "__esModule", {
5
5
  value: true
6
6
  });
7
- exports.STAGE2_WEIGHT = exports.STAGE1_WEIGHT = exports.MIN_STAGE1_SCORE = void 0;
7
+ exports.loadGrammarDataAsync = exports.isGrammarDataLoaded = exports.STAGE2_WEIGHT = exports.STAGE1_WEIGHT = exports.MIN_STAGE1_SCORE = void 0;
8
8
  exports.rankCandidates = rankCandidates;
9
+ var _regenerator = _interopRequireDefault(require("@babel/runtime/regenerator"));
10
+ var _defineProperty2 = _interopRequireDefault(require("@babel/runtime/helpers/defineProperty"));
11
+ var _asyncToGenerator2 = _interopRequireDefault(require("@babel/runtime/helpers/asyncToGenerator"));
9
12
  var _slicedToArray2 = _interopRequireDefault(require("@babel/runtime/helpers/slicedToArray"));
10
- var _combined_l2_l3_pos_tags = _interopRequireDefault(require("./data/combined_l2_l3_pos_tags.json"));
11
- var _ghost_pos_tags = _interopRequireDefault(require("./data/ghost_pos_tags.json"));
12
- var _grammar_transitions_10k = _interopRequireDefault(require("./data/grammar_transitions_10k.json"));
13
+ var _ufo = require("../analytics/ufo");
14
+ var _artifactsManifest = require("./artifacts-manifest");
15
+ var _debugMode = require("./debug-mode");
13
16
  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; } } }; }
14
17
  function _unsupportedIterableToArray(r, a) { if (r) { if ("string" == typeof r) return _arrayLikeToArray(r, a); var t = {}.toString.call(r).slice(8, -1); return "Object" === t && r.constructor && (t = r.constructor.name), "Map" === t || "Set" === t ? Array.from(r) : "Arguments" === t || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(t) ? _arrayLikeToArray(r, a) : void 0; } }
15
- function _arrayLikeToArray(r, a) { (null == a || a > r.length) && (a = r.length); for (var e = 0, n = Array(a); e < a; e++) n[e] = r[e]; return n; } /**
18
+ function _arrayLikeToArray(r, a) { (null == a || a > r.length) && (a = r.length); for (var e = 0, n = Array(a); e < a; e++) n[e] = r[e]; return n; }
19
+ 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; }
20
+ 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; } /**
16
21
  * Scoring Pipeline: Stage 1 (Semantic + Frequency), Grammar Filter, Stage 2 (LM Re-ranking).
17
22
  *
18
23
  * Operates synchronously on pre-loaded data. Each stage gracefully degrades
19
24
  * when its required data isn't available (cold → warm → full warm).
25
+ *
26
+ * The grammar/POS payloads are fetched from the CDN by loadGrammarDataAsync, so
27
+ * the grammar filter is a pass-through until that resolves.
20
28
  */
21
29
  // ─── Types ──────────────────────────────────────────────────
22
30
 
@@ -37,22 +45,119 @@ var L1_SESSION_CAP = 1.2;
37
45
  // at 0.00024) from triggering re-ranking.
38
46
  var LM_GATE_THRESHOLD = 0.0005;
39
47
 
40
- // ─── Grammar Data (loaded once on import) ───────────────────
48
+ // ─── Grammar Data (fetched once from the CDN) ────────────────
49
+
50
+ var posTags = new Map();
51
+
52
+ /**
53
+ * GHOST POS DICTIONARY
54
+ * A mapping of common structural English words that were stripped from the main
55
+ * domain vocabulary. This allows the grammar filter to understand context
56
+ * without suggesting these words to the user.
57
+ */
58
+ var ghostPosTags = {};
41
59
 
42
- var posTags = new Map(Object.entries(_combined_l2_l3_pos_tags.default));
43
- var grammarTransitions = _grammar_transitions_10k.default;
60
+ /**
61
+ * Precomputed map from each POS tag to the set of allowed next POS tags, so
62
+ * applyGrammarFilter never re-iterates the transition rules per call.
63
+ */
64
+ var precomputedAllowedByPos = new Map();
65
+ var grammarDataPromise;
66
+ var setGrammarData = function setGrammarData(data) {
67
+ var _data$grammarTransiti;
68
+ posTags = new Map(Object.entries(data.posTags));
69
+ ghostPosTags = data.ghostPosTags;
70
+ precomputedAllowedByPos = new Map(Object.entries((_data$grammarTransiti = data.grammarTransitions.transitions) !== null && _data$grammarTransiti !== void 0 ? _data$grammarTransiti : {}).map(function (_ref) {
71
+ var _ref2 = (0, _slicedToArray2.default)(_ref, 2),
72
+ pos = _ref2[0],
73
+ rule = _ref2[1];
74
+ return [pos, new Set(rule.allowed)];
75
+ }));
76
+ };
77
+ var isGrammarDataLoaded = exports.isGrammarDataLoaded = function isGrammarDataLoaded() {
78
+ return precomputedAllowedByPos.size > 0;
79
+ };
44
80
 
45
81
  /**
46
- * Precomputed map from each POS tag to the set of allowed next POS tags.
47
- * Built once at module load from grammarTransitions so applyGrammarFilter
48
- * never re-iterates the transition rules per call.
82
+ * Fetch the POS tag and grammar transition payloads from the CDN.
83
+ *
84
+ * Safe to call repeatedly: the in-flight promise is shared, and a failure is not
85
+ * cached so a later call can retry.
49
86
  */
50
- var precomputedAllowedByPos = new Map(Object.entries(grammarTransitions.transitions).map(function (_ref) {
51
- var _ref2 = (0, _slicedToArray2.default)(_ref, 2),
52
- pos = _ref2[0],
53
- rule = _ref2[1];
54
- return [pos, new Set(rule.allowed)];
55
- }));
87
+ var loadGrammarDataAsync = exports.loadGrammarDataAsync = function loadGrammarDataAsync(options) {
88
+ var _options$isLocalLLM;
89
+ if (grammarDataPromise) {
90
+ return grammarDataPromise;
91
+ }
92
+ var isLocalLLM = (_options$isLocalLLM = options === null || options === void 0 ? void 0 : options.isLocalLLM) !== null && _options$isLocalLLM !== void 0 ? _options$isLocalLLM : false;
93
+ var surface = options === null || options === void 0 ? void 0 : options.surface;
94
+ grammarDataPromise = (0, _asyncToGenerator2.default)( /*#__PURE__*/_regenerator.default.mark(function _callee() {
95
+ var _yield$Promise$all, _yield$Promise$all2, posTagsData, ghostPosTagsData, grammarTransitionsData, _t;
96
+ return _regenerator.default.wrap(function (_context) {
97
+ while (1) switch (_context.prev = _context.next) {
98
+ case 0:
99
+ (0, _ufo.startExp)(_ufo.EXPERIENCE_NAME.LOAD_GRAMMAR, 'singleton', _objectSpread({
100
+ isLocalLLM: isLocalLLM
101
+ }, surface ? {
102
+ surface: surface
103
+ } : {}));
104
+ _context.prev = 1;
105
+ _context.next = 2;
106
+ return Promise.all([(0, _artifactsManifest.fetchArtifactJson)(_artifactsManifest.ARTIFACT_NAME.POS_TAGS), (0, _artifactsManifest.fetchArtifactJson)(_artifactsManifest.ARTIFACT_NAME.GHOST_POS_TAGS), (0, _artifactsManifest.fetchArtifactJson)(_artifactsManifest.ARTIFACT_NAME.GRAMMAR_TRANSITIONS)]);
107
+ case 2:
108
+ _yield$Promise$all = _context.sent;
109
+ _yield$Promise$all2 = (0, _slicedToArray2.default)(_yield$Promise$all, 3);
110
+ posTagsData = _yield$Promise$all2[0];
111
+ ghostPosTagsData = _yield$Promise$all2[1];
112
+ grammarTransitionsData = _yield$Promise$all2[2];
113
+ if (!((grammarTransitionsData === null || grammarTransitionsData === void 0 ? void 0 : grammarTransitionsData.transitions) == null)) {
114
+ _context.next = 3;
115
+ break;
116
+ }
117
+ throw new Error("[scoring-pipeline] ".concat(_artifactsManifest.ARTIFACT_NAME.GRAMMAR_TRANSITIONS, " is missing its `transitions` wrapper"));
118
+ case 3:
119
+ setGrammarData({
120
+ posTags: posTagsData !== null && posTagsData !== void 0 ? posTagsData : {},
121
+ ghostPosTags: ghostPosTagsData !== null && ghostPosTagsData !== void 0 ? ghostPosTagsData : {},
122
+ grammarTransitions: grammarTransitionsData
123
+ });
124
+ (0, _ufo.succeedExp)(_ufo.EXPERIENCE_NAME.LOAD_GRAMMAR, 'singleton', _objectSpread({
125
+ isLocalLLM: isLocalLLM,
126
+ posTagCount: posTags.size,
127
+ transitionCount: precomputedAllowedByPos.size
128
+ }, surface ? {
129
+ surface: surface
130
+ } : {}));
131
+ if ((0, _debugMode.isAutocompleteDebugEnabled)()) {
132
+ // eslint-disable-next-line no-console
133
+ console.log('[scoring-pipeline] Grammar data loaded:', {
134
+ posTagCount: posTags.size,
135
+ ghostPosTagCount: Object.keys(ghostPosTags).length,
136
+ transitionCount: precomputedAllowedByPos.size
137
+ });
138
+ }
139
+ _context.next = 5;
140
+ break;
141
+ case 4:
142
+ _context.prev = 4;
143
+ _t = _context["catch"](1);
144
+ (0, _ufo.failExp)(_ufo.EXPERIENCE_NAME.LOAD_GRAMMAR, 'singleton', _objectSpread({
145
+ isLocalLLM: isLocalLLM,
146
+ errorType: 'network'
147
+ }, surface ? {
148
+ surface: surface
149
+ } : {}));
150
+ // Allow a later call to retry the load rather than caching the failure.
151
+ grammarDataPromise = undefined;
152
+ throw _t;
153
+ case 5:
154
+ case "end":
155
+ return _context.stop();
156
+ }
157
+ }, _callee, null, [[1, 4]]);
158
+ }))();
159
+ return grammarDataPromise;
160
+ };
56
161
 
57
162
  // ─── Math ───────────────────────────────────────────────────
58
163
 
@@ -101,18 +206,15 @@ function scoreStage1(candidate, contextVector, getWordVector, maxTenantFreq) {
101
206
 
102
207
  // ─── Grammar Filter ─────────────────────────────────────────
103
208
 
104
- /**
105
- * GHOST POS DICTIONARY
106
- * A hardcoded mapping of common structural English words that were stripped
107
- * from the main domain vocabulary. This allows the grammar filter to understand
108
- * context without suggesting these words to the user.
109
- */
110
- var ghostPosTags = _ghost_pos_tags.default;
111
209
  function applyGrammarFilter(candidates, previousWord) {
112
- if (!previousWord) return {
113
- filtered: candidates,
114
- grammarMeta: null
115
- };
210
+ // Pass everything through until loadGrammarDataAsync resolves — filtering on a
211
+ // partially loaded ruleset would drop valid candidates.
212
+ if (!previousWord || !isGrammarDataLoaded()) {
213
+ return {
214
+ filtered: candidates,
215
+ grammarMeta: null
216
+ };
217
+ }
116
218
  var lowerPrev = previousWord.toLowerCase();
117
219
  var prevTags = ghostPosTags[lowerPrev] || posTags.get(lowerPrev);
118
220
  if (!prevTags || prevTags.length === 0) {
@@ -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({