@atlaskit/editor-plugin-local-id 5.0.2 → 5.1.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.
@@ -0,0 +1,512 @@
1
+ import _slicedToArray from "@babel/runtime/helpers/slicedToArray";
2
+ 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; } } }; }
3
+ 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; } }
4
+ 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; }
5
+ import { BatchAttrsStep, SetAttrsStep } from '@atlaskit/adf-schema/steps';
6
+ import { SafePlugin } from '@atlaskit/editor-common/safe-plugin';
7
+ import { PluginKey } from '@atlaskit/editor-prosemirror/state';
8
+ import { AttrStep, DocAttrStep, ReplaceAroundStep, ReplaceStep } from '@atlaskit/editor-prosemirror/transform';
9
+ /**
10
+ * This is a safeguard limit to avoid tracking localIds in extremely large documents with too many localIds.
11
+ * If the number of unique localIds exceeds this limit, the watchmen plugin will disable itself to avoid performance issues.
12
+ * Reminder: The Map has a hard limit of 2^24 (16 million) entries in V8, please keep this value well below that
13
+ * to avoid any potential memory issues.
14
+ */
15
+ var MAX_LOCAL_ID_MAP_SIZE = 2097152; // 2^21
16
+
17
+ /**
18
+ * Plugin state tracking all localIds in the document
19
+ */
20
+
21
+ export var localIdWatchmenPluginKey = new PluginKey('localIdWatchmenPlugin');
22
+
23
+ /**
24
+ * Scans the entire document to find all active localIds
25
+ */
26
+ var scanDocumentForLocalIds = function scanDocumentForLocalIds(doc) {
27
+ var localIds = new Set();
28
+ doc.descendants(function (node) {
29
+ var _node$attrs;
30
+ if ((_node$attrs = node.attrs) !== null && _node$attrs !== void 0 && _node$attrs.localId) {
31
+ localIds.add(node.attrs.localId);
32
+ }
33
+
34
+ // Check marks for localIds
35
+ if (node.marks) {
36
+ node.marks.forEach(function (mark) {
37
+ var _mark$attrs;
38
+ if ((_mark$attrs = mark.attrs) !== null && _mark$attrs !== void 0 && _mark$attrs.localId) {
39
+ localIds.add(mark.attrs.localId);
40
+ }
41
+ });
42
+ }
43
+ return localIds.size < MAX_LOCAL_ID_MAP_SIZE; // Continue traversing
44
+ });
45
+ return localIds;
46
+ };
47
+ var getReplacementStatusCode = function getReplacementStatusCode(tr, step) {
48
+ var method;
49
+ if (step instanceof AttrStep || step instanceof DocAttrStep) {
50
+ method = 'ByAttr';
51
+ } else if (step instanceof SetAttrsStep) {
52
+ method = 'BySetAttrs';
53
+ } else if (step instanceof BatchAttrsStep) {
54
+ method = 'ByBatchAttrs';
55
+ } else if (step instanceof ReplaceStep) {
56
+ var isDeleting = step.from < step.to; // range has content to remove
57
+ var isInserting = step.slice.content.size > 0; // slice has content to insert
58
+ if (isDeleting && !isInserting) {
59
+ method = 'ByDelete'; // removing content, inserting nothing
60
+ //} else if (!isDeleting && isInserting) {
61
+ //method = 'ByInsert'; // This situation cannot be tracked since this would be part of the "current" status
62
+ } else {
63
+ // isDeleting && isInserting
64
+ method = 'ByReplace';
65
+ }
66
+ } else if (step instanceof ReplaceAroundStep) {
67
+ method = 'ByReplaceAround';
68
+ } else {
69
+ method = 'ByUnknown';
70
+ }
71
+ if (tr.getMeta('isAIStreamingTransformation')) {
72
+ return "AIChange".concat(method);
73
+ }
74
+ if (tr.getMeta('replaceDocument')) {
75
+ return "docChange".concat(method);
76
+ }
77
+ if (tr.getMeta('isRemote')) {
78
+ return "remoteChange".concat(method);
79
+ }
80
+ return "localChange".concat(method);
81
+ };
82
+
83
+ /**
84
+ * Handles AttrStep and DocAttrStep which modify a single attribute
85
+ */
86
+ var handleAttrStep = function handleAttrStep(tr, step, localIdStatus, preDoc) {
87
+ if (step.attr !== 'localId') {
88
+ return {
89
+ localIdStatus: localIdStatus,
90
+ modified: false
91
+ };
92
+ }
93
+ var modified = false;
94
+ var newlocalIdStatus = new Map(localIdStatus);
95
+
96
+ // Get the old value if it exists
97
+ var oldLocalId;
98
+ if (step instanceof AttrStep) {
99
+ try {
100
+ var _node$attrs2;
101
+ var node = preDoc.nodeAt(step.pos);
102
+ oldLocalId = node === null || node === void 0 || (_node$attrs2 = node.attrs) === null || _node$attrs2 === void 0 ? void 0 : _node$attrs2.localId;
103
+ } catch (_unused) {
104
+ // Position might be invalid
105
+ }
106
+ }
107
+
108
+ // Handle the new value
109
+ var newLocalId = step.value;
110
+ if (oldLocalId && oldLocalId !== newLocalId) {
111
+ // Old localId is being replaced or removed
112
+ newlocalIdStatus.set(oldLocalId, getReplacementStatusCode(tr, step));
113
+ modified = true;
114
+ }
115
+ if (newLocalId) {
116
+ newlocalIdStatus.set(newLocalId, 'current');
117
+ modified = true;
118
+ }
119
+ return {
120
+ localIdStatus: newlocalIdStatus,
121
+ modified: modified
122
+ };
123
+ };
124
+
125
+ /**
126
+ * Handles SetAttrsStep which sets multiple attributes at once
127
+ */
128
+ var handleSetAttrsStep = function handleSetAttrsStep(tr, step, localIdStatus, preDoc) {
129
+ var attrs = step.attrs;
130
+ if (!attrs || !attrs.hasOwnProperty('localId')) {
131
+ return {
132
+ localIdStatus: localIdStatus,
133
+ modified: false
134
+ };
135
+ }
136
+ var modified = false;
137
+ var newlocalIdStatus = new Map(localIdStatus);
138
+
139
+ // Get old localId from the node being modified
140
+ try {
141
+ var _node$attrs3;
142
+ var node = preDoc.nodeAt(step.pos);
143
+ var oldLocalId = node === null || node === void 0 || (_node$attrs3 = node.attrs) === null || _node$attrs3 === void 0 ? void 0 : _node$attrs3.localId;
144
+ if (oldLocalId && oldLocalId !== attrs.localId) {
145
+ newlocalIdStatus.set(oldLocalId, getReplacementStatusCode(tr, step));
146
+ modified = true;
147
+ }
148
+ } catch (_unused2) {
149
+ // Position might be invalid
150
+ }
151
+ var newLocalId = attrs.localId;
152
+ if (newLocalId) {
153
+ newlocalIdStatus.set(newLocalId, 'current');
154
+ modified = true;
155
+ }
156
+ return {
157
+ localIdStatus: newlocalIdStatus,
158
+ modified: modified
159
+ };
160
+ };
161
+
162
+ /**
163
+ * Handles BatchAttrsStep which applies multiple attribute changes
164
+ */
165
+ var handleBatchAttrsStep = function handleBatchAttrsStep(tr, step, localIdStatus, preDoc) {
166
+ var modified = false;
167
+ var newlocalIdStatus = new Map(localIdStatus);
168
+ step.data.forEach(function (change) {
169
+ var _change$attrs;
170
+ if (!((_change$attrs = change.attrs) !== null && _change$attrs !== void 0 && _change$attrs.hasOwnProperty('localId'))) {
171
+ return;
172
+ }
173
+
174
+ // Get old localId from the node being modified
175
+ try {
176
+ var _node$attrs4;
177
+ var node = preDoc.nodeAt(change.position);
178
+ var oldLocalId = node === null || node === void 0 || (_node$attrs4 = node.attrs) === null || _node$attrs4 === void 0 ? void 0 : _node$attrs4.localId;
179
+ var newLocalId = change.attrs.localId;
180
+ if (oldLocalId && oldLocalId !== newLocalId) {
181
+ newlocalIdStatus.set(oldLocalId, getReplacementStatusCode(tr, step));
182
+ modified = true;
183
+ }
184
+ if (newLocalId) {
185
+ newlocalIdStatus.set(newLocalId, 'current');
186
+ modified = true;
187
+ }
188
+ } catch (_unused3) {
189
+ // Position might be invalid
190
+ }
191
+ });
192
+ return {
193
+ localIdStatus: newlocalIdStatus,
194
+ modified: modified
195
+ };
196
+ };
197
+
198
+ /**
199
+ * Handles ReplaceStep which inserts or deletes content
200
+ */
201
+ var handleReplaceStep = function handleReplaceStep(tr, step, localIdStatus, preDoc, postDoc) {
202
+ var modified = false;
203
+ try {
204
+ // Create a temporary set to collect new localIds
205
+ var changedLocaleIds = new Map();
206
+ var replaceCode = getReplacementStatusCode(tr, step);
207
+ step.getMap().forEach(function (oldStart, oldEnd, newStart, newEnd) {
208
+ // For each step map item we can just look at the nodes in the old doc and mark them as "inactive" and then
209
+ // look at the nodes in the doc after the step has been applied and mark them as "active"
210
+ // then lastly we can compare these to the current states and only update what's different.
211
+ preDoc.nodesBetween(oldStart, oldEnd, function (node) {
212
+ var _node$attrs5;
213
+ if ((_node$attrs5 = node.attrs) !== null && _node$attrs5 !== void 0 && _node$attrs5.localId) {
214
+ changedLocaleIds.set(node.attrs.localId, replaceCode);
215
+ }
216
+ if (node.marks) {
217
+ node.marks.forEach(function (mark) {
218
+ var _mark$attrs2;
219
+ if ((_mark$attrs2 = mark.attrs) !== null && _mark$attrs2 !== void 0 && _mark$attrs2.localId) {
220
+ changedLocaleIds.set(node.attrs.localId, replaceCode);
221
+ }
222
+ });
223
+ }
224
+ });
225
+ postDoc.nodesBetween(newStart, newEnd, function (node) {
226
+ var _node$attrs6;
227
+ if ((_node$attrs6 = node.attrs) !== null && _node$attrs6 !== void 0 && _node$attrs6.localId) {
228
+ changedLocaleIds.set(node.attrs.localId, 'current');
229
+ }
230
+ if (node.marks) {
231
+ node.marks.forEach(function (mark) {
232
+ var _mark$attrs3;
233
+ if ((_mark$attrs3 = mark.attrs) !== null && _mark$attrs3 !== void 0 && _mark$attrs3.localId) {
234
+ changedLocaleIds.set(node.attrs.localId, 'current');
235
+ }
236
+ });
237
+ }
238
+ });
239
+ });
240
+ if (!!changedLocaleIds.size) {
241
+ var newlocalIdStatus = new Map(localIdStatus);
242
+ var _iterator = _createForOfIteratorHelper(changedLocaleIds),
243
+ _step;
244
+ try {
245
+ for (_iterator.s(); !(_step = _iterator.n()).done;) {
246
+ var _step$value = _slicedToArray(_step.value, 2),
247
+ key = _step$value[0],
248
+ value = _step$value[1];
249
+ if (!localIdStatus.has(key) || localIdStatus.get(key) !== value) {
250
+ modified = true;
251
+ newlocalIdStatus.set(key, value);
252
+ }
253
+ }
254
+ } catch (err) {
255
+ _iterator.e(err);
256
+ } finally {
257
+ _iterator.f();
258
+ }
259
+ return {
260
+ localIdStatus: newlocalIdStatus,
261
+ modified: modified
262
+ };
263
+ }
264
+ } catch (_unused4) {
265
+ // If position calculation fails, do a full document rescan as fallback
266
+ // This shouldn't happen often but provides safety
267
+ }
268
+ return {
269
+ localIdStatus: localIdStatus,
270
+ modified: modified
271
+ };
272
+ };
273
+
274
+ /**
275
+ * Handles ReplaceAroundStep which wraps or unwraps content
276
+ */
277
+ var handleReplaceAroundStep = function handleReplaceAroundStep(tr, step, localIdStatus, preDoc, postDoc) {
278
+ var modified = false;
279
+ var newlocalIdStatus = new Map(localIdStatus);
280
+
281
+ // Scan the affected region before and after the step
282
+ var from = step.from;
283
+ var to = step.to;
284
+ try {
285
+ // Collect localIds from the old region
286
+ var oldLocalIds = new Set();
287
+ if (from < to && from >= 0 && to <= preDoc.content.size) {
288
+ preDoc.nodesBetween(from, to, function (node) {
289
+ var _node$attrs7;
290
+ if ((_node$attrs7 = node.attrs) !== null && _node$attrs7 !== void 0 && _node$attrs7.localId) {
291
+ oldLocalIds.add(node.attrs.localId);
292
+ }
293
+ if (node.marks) {
294
+ node.marks.forEach(function (mark) {
295
+ var _mark$attrs4;
296
+ if ((_mark$attrs4 = mark.attrs) !== null && _mark$attrs4 !== void 0 && _mark$attrs4.localId) {
297
+ oldLocalIds.add(mark.attrs.localId);
298
+ }
299
+ });
300
+ }
301
+ });
302
+ }
303
+
304
+ // Collect localIds from the new region
305
+ var map = step.getMap();
306
+ var newFrom = map.map(from, -1);
307
+ var newTo = map.map(to, 1);
308
+ var newLocalIds = new Set();
309
+ if (newFrom < newTo && newFrom >= 0 && newTo <= postDoc.content.size) {
310
+ postDoc.nodesBetween(newFrom, newTo, function (node) {
311
+ var _node$attrs8;
312
+ if ((_node$attrs8 = node.attrs) !== null && _node$attrs8 !== void 0 && _node$attrs8.localId) {
313
+ newLocalIds.add(node.attrs.localId);
314
+ }
315
+ if (node.marks) {
316
+ node.marks.forEach(function (mark) {
317
+ var _mark$attrs5;
318
+ if ((_mark$attrs5 = mark.attrs) !== null && _mark$attrs5 !== void 0 && _mark$attrs5.localId) {
319
+ newLocalIds.add(mark.attrs.localId);
320
+ }
321
+ });
322
+ }
323
+ });
324
+ }
325
+
326
+ // Find localIds that were removed
327
+ oldLocalIds.forEach(function (localId) {
328
+ if (!newLocalIds.has(localId) && newlocalIdStatus.get(localId) === 'current') {
329
+ newlocalIdStatus.set(localId, getReplacementStatusCode(tr, step));
330
+ modified = true;
331
+ }
332
+ });
333
+
334
+ // Find localIds that were added
335
+ newLocalIds.forEach(function (localId) {
336
+ if (!oldLocalIds.has(localId)) {
337
+ newlocalIdStatus.set(localId, 'current');
338
+ modified = true;
339
+ }
340
+ });
341
+ } catch (_unused5) {
342
+ // Position might be invalid, skip this step
343
+ }
344
+ return {
345
+ localIdStatus: newlocalIdStatus,
346
+ modified: modified
347
+ };
348
+ };
349
+
350
+ /**
351
+ * Processes a transaction to update localId tracking state
352
+ */
353
+ var processTransaction = function processTransaction(tr, currentState) {
354
+ var localIdStatus = currentState.localIdStatus;
355
+ var modified = false;
356
+
357
+ // Process each step in the transaction
358
+ try {
359
+ tr.steps.forEach(function (step, index) {
360
+ var _tr$docs$index, _tr$docs, _tr$docs2, _tr$docs3;
361
+ var result;
362
+ // steps are relative to their docs, so we ensure we reference the doc before/after the step was applied.
363
+ var preDoc = (_tr$docs$index = (_tr$docs = tr.docs) === null || _tr$docs === void 0 ? void 0 : _tr$docs[index]) !== null && _tr$docs$index !== void 0 ? _tr$docs$index : tr.doc;
364
+ var postDoc = (_tr$docs2 = (_tr$docs3 = tr.docs) === null || _tr$docs3 === void 0 ? void 0 : _tr$docs3[index + 1]) !== null && _tr$docs2 !== void 0 ? _tr$docs2 : tr.doc;
365
+ if (step instanceof AttrStep || step instanceof DocAttrStep) {
366
+ result = handleAttrStep(tr, step, localIdStatus, preDoc);
367
+ } else if (step instanceof SetAttrsStep) {
368
+ result = handleSetAttrsStep(tr, step, localIdStatus, preDoc);
369
+ } else if (step instanceof BatchAttrsStep) {
370
+ result = handleBatchAttrsStep(tr, step, localIdStatus, preDoc);
371
+ } else if (step instanceof ReplaceStep) {
372
+ result = handleReplaceStep(tr, step, localIdStatus, preDoc, postDoc);
373
+ } else if (step instanceof ReplaceAroundStep) {
374
+ result = handleReplaceAroundStep(tr, step, localIdStatus, preDoc, postDoc);
375
+ } else {
376
+ // Unknown step type, no changes
377
+ result = {
378
+ localIdStatus: localIdStatus,
379
+ modified: false
380
+ };
381
+ }
382
+ localIdStatus = result.localIdStatus;
383
+ modified = modified || result.modified;
384
+ });
385
+ } catch (_unused6) {
386
+ // If any error occurs during step processing, we fallback to disabling the plugin
387
+ return {
388
+ enabled: false,
389
+ initLocalIdSize: currentState.initLocalIdSize,
390
+ localIdStatus: new Map(),
391
+ lastUpdated: Date.now()
392
+ };
393
+ }
394
+
395
+ // If nothing changed, return the same state object
396
+ if (!modified) {
397
+ return currentState;
398
+ }
399
+
400
+ // If we exceeded the max size while processing the steps, we need to disable the watchmen from further processing.
401
+ // If a Map size limit of 2^24 is exceeded then it's more than likely an error would have been thrown during processing
402
+ // which would also disable this plugin.
403
+ if (localIdStatus.size >= MAX_LOCAL_ID_MAP_SIZE) {
404
+ return {
405
+ enabled: false,
406
+ initLocalIdSize: currentState.initLocalIdSize,
407
+ localIdStatus: new Map(),
408
+ lastUpdated: Date.now()
409
+ };
410
+ }
411
+
412
+ // Return new state with updated sets
413
+ return {
414
+ enabled: true,
415
+ initLocalIdSize: currentState.initLocalIdSize,
416
+ localIdStatus: localIdStatus,
417
+ lastUpdated: Date.now()
418
+ };
419
+ };
420
+
421
+ /**
422
+ * Creates the localId watchmen plugin
423
+ */
424
+ export var createWatchmenPlugin = function createWatchmenPlugin(api) {
425
+ // Ensure limited mode is initialized
426
+ return new SafePlugin({
427
+ key: localIdWatchmenPluginKey,
428
+ state: {
429
+ init: function init(_config, state) {
430
+ var _api$limitedMode$shar, _api$limitedMode;
431
+ var isLimitedModeEnabled = (_api$limitedMode$shar = api === null || api === void 0 || (_api$limitedMode = api.limitedMode) === null || _api$limitedMode === void 0 || (_api$limitedMode = _api$limitedMode.sharedState.currentState()) === null || _api$limitedMode === void 0 ? void 0 : _api$limitedMode.enabled) !== null && _api$limitedMode$shar !== void 0 ? _api$limitedMode$shar : false;
432
+ if (isLimitedModeEnabled) {
433
+ return {
434
+ enabled: false,
435
+ initLocalIdSize: -1,
436
+ localIdStatus: new Map(),
437
+ lastUpdated: Date.now()
438
+ };
439
+ }
440
+
441
+ // Initialize by scanning the entire document
442
+ var activeLocalIds = scanDocumentForLocalIds(state.doc);
443
+ if (activeLocalIds.size >= MAX_LOCAL_ID_MAP_SIZE) {
444
+ return {
445
+ enabled: false,
446
+ initLocalIdSize: activeLocalIds.size,
447
+ localIdStatus: new Map(),
448
+ lastUpdated: Date.now()
449
+ };
450
+ }
451
+ return {
452
+ enabled: true,
453
+ initLocalIdSize: activeLocalIds.size,
454
+ localIdStatus: new Map(Array.from(activeLocalIds).map(function (key) {
455
+ return [key, 'current'];
456
+ })),
457
+ lastUpdated: Date.now()
458
+ };
459
+ },
460
+ apply: function apply(tr, currentPluginState) {
461
+ var _ref = tr.getMeta(localIdWatchmenPluginKey) || {
462
+ enabled: currentPluginState.enabled
463
+ },
464
+ enabled = _ref.enabled;
465
+ var newPluginState = currentPluginState;
466
+ if (enabled !== currentPluginState.enabled) {
467
+ // If this plugin enabled state is changing and it's being disabled at runtime then we will kill this plugin
468
+ // to avoid tracking localIds when in limited mode or there after.
469
+ // Once disabled it cannot be re-enabled without a full editor reload.
470
+ if (!enabled) {
471
+ return {
472
+ enabled: false,
473
+ initLocalIdSize: currentPluginState.initLocalIdSize,
474
+ localIdStatus: currentPluginState.localIdStatus,
475
+ lastUpdated: Date.now()
476
+ };
477
+ }
478
+ }
479
+ if (!newPluginState.enabled) {
480
+ // If this plugin has been disabled, do not track localIds.
481
+ return newPluginState;
482
+ }
483
+
484
+ // If no steps, nothing changed
485
+ if (tr.steps.length === 0 || !tr.docChanged) {
486
+ return newPluginState;
487
+ }
488
+
489
+ // Process the transaction to update state
490
+ return processTransaction(tr, newPluginState);
491
+ }
492
+ },
493
+ view: function view(editorView) {
494
+ var _api$limitedMode2;
495
+ // If limited mode changes, for example if we start not limited but then all of a sudden become limited, we kill
496
+ // the watchment plugin to avoid tracking localIds when in limited mode. We also don't want/need to re-enable it once it's disabled.
497
+ var unsub = api === null || api === void 0 || (_api$limitedMode2 = api.limitedMode) === null || _api$limitedMode2 === void 0 ? void 0 : _api$limitedMode2.sharedState.onChange(function (_ref2) {
498
+ var nextSharedState = _ref2.nextSharedState;
499
+ var watchmentPluginState = localIdWatchmenPluginKey.getState(editorView.state);
500
+ if (nextSharedState.enabled && (watchmentPluginState === null || watchmentPluginState === void 0 ? void 0 : watchmentPluginState.enabled) === true) {
501
+ // if nextSharedState.enabled === true, then we need to disable the watchmen plugin, if not already disabled
502
+ editorView.dispatch(editorView.state.tr.setMeta(localIdWatchmenPluginKey, {
503
+ enabled: false
504
+ }));
505
+ }
506
+ });
507
+ return {
508
+ destroy: unsub
509
+ };
510
+ }
511
+ });
512
+ };
@@ -1,11 +1,17 @@
1
1
  import type { NextEditorPlugin, OptionalPlugin } from '@atlaskit/editor-common/types';
2
2
  import type { CollabEditPlugin } from '@atlaskit/editor-plugin-collab-edit';
3
3
  import type { CompositionPlugin } from '@atlaskit/editor-plugin-composition';
4
+ import type { LimitedModePlugin } from '@atlaskit/editor-plugin-limited-mode';
4
5
  import type { Node } from '@atlaskit/editor-prosemirror/model';
5
6
  import type { NodeWithPos } from '@atlaskit/editor-prosemirror/utils';
6
7
  export type ActionProps = {
7
8
  localId: string;
8
9
  };
10
+ export type LocalIdStatusCode = 'current' | 'localChangeByAttr' | 'localChangeBySetAttrs' | 'localChangeByBatchAttrs' | 'localChangeByReplace' | 'localChangeByDelete' | 'localChangeByReplaceAround' | 'localChangeByUnknown' | 'remoteChangeByAttr' | 'remoteChangeBySetAttrs' | 'remoteChangeByBatchAttrs' | 'remoteChangeByReplace' | 'remoteChangeByDelete' | 'remoteChangeByReplaceAround' | 'remoteChangeByUnknown' | 'AIChangeByAttr' | 'AIChangeBySetAttrs' | 'AIChangeByBatchAttrs' | 'AIChangeByReplace' | 'AIChangeByDelete' | 'AIChangeByReplaceAround' | 'AIChangeByUnknown' | 'docChangeByAttr' | 'docChangeBySetAttrs' | 'docChangeByBatchAttrs' | 'docChangeByReplace' | 'docChangeByDelete' | 'docChangeByReplaceAround' | 'docChangeByUnknown';
11
+ export interface LocalIdSharedState {
12
+ localIdStatus: Map<string, LocalIdStatusCode> | undefined;
13
+ localIdWatchmenEnabled: boolean | undefined;
14
+ }
9
15
  export type LocalIdPlugin = NextEditorPlugin<'localId', {
10
16
  actions: {
11
17
  /**
@@ -26,5 +32,10 @@ export type LocalIdPlugin = NextEditorPlugin<'localId', {
26
32
  value: Node;
27
33
  }) => boolean;
28
34
  };
29
- dependencies: [CompositionPlugin, OptionalPlugin<CollabEditPlugin>];
35
+ dependencies: [
36
+ CompositionPlugin,
37
+ OptionalPlugin<CollabEditPlugin>,
38
+ OptionalPlugin<LimitedModePlugin>
39
+ ];
40
+ sharedState: LocalIdSharedState | undefined;
30
41
  }>;
@@ -0,0 +1,32 @@
1
+ import { SafePlugin } from '@atlaskit/editor-common/safe-plugin';
2
+ import type { ExtractInjectionAPI } from '@atlaskit/editor-common/types';
3
+ import { PluginKey } from '@atlaskit/editor-prosemirror/state';
4
+ import type { LocalIdPlugin, LocalIdStatusCode } from '../localIdPluginType';
5
+ /**
6
+ * Plugin state tracking all localIds in the document
7
+ */
8
+ export type LocalIdWatchmenState = {
9
+ /**
10
+ * This flag controls whether or not the watchmen plugin will process transactions. Caution! disabling and then re-enabling
11
+ * this plugin will cause the watchment to rescan the whole document to sync the localId state.
12
+ */
13
+ enabled: boolean;
14
+ /**
15
+ * This is the initial count of localIds when the plugin was first initialized.
16
+ */
17
+ initLocalIdSize: number;
18
+ /**
19
+ * Timestamp of last state update for debugging
20
+ */
21
+ lastUpdated: number;
22
+ /**
23
+ * This is a list of all localIds which have ever existed in the document and whether or not they're currently
24
+ * on the page (ie "active") or not.
25
+ */
26
+ localIdStatus: Map<string, LocalIdStatusCode>;
27
+ };
28
+ export declare const localIdWatchmenPluginKey: PluginKey<LocalIdWatchmenState>;
29
+ /**
30
+ * Creates the localId watchmen plugin
31
+ */
32
+ export declare const createWatchmenPlugin: (api: ExtractInjectionAPI<LocalIdPlugin> | undefined) => SafePlugin<LocalIdWatchmenState>;
@@ -1,11 +1,17 @@
1
1
  import type { NextEditorPlugin, OptionalPlugin } from '@atlaskit/editor-common/types';
2
2
  import type { CollabEditPlugin } from '@atlaskit/editor-plugin-collab-edit';
3
3
  import type { CompositionPlugin } from '@atlaskit/editor-plugin-composition';
4
+ import type { LimitedModePlugin } from '@atlaskit/editor-plugin-limited-mode';
4
5
  import type { Node } from '@atlaskit/editor-prosemirror/model';
5
6
  import type { NodeWithPos } from '@atlaskit/editor-prosemirror/utils';
6
7
  export type ActionProps = {
7
8
  localId: string;
8
9
  };
10
+ export type LocalIdStatusCode = 'current' | 'localChangeByAttr' | 'localChangeBySetAttrs' | 'localChangeByBatchAttrs' | 'localChangeByReplace' | 'localChangeByDelete' | 'localChangeByReplaceAround' | 'localChangeByUnknown' | 'remoteChangeByAttr' | 'remoteChangeBySetAttrs' | 'remoteChangeByBatchAttrs' | 'remoteChangeByReplace' | 'remoteChangeByDelete' | 'remoteChangeByReplaceAround' | 'remoteChangeByUnknown' | 'AIChangeByAttr' | 'AIChangeBySetAttrs' | 'AIChangeByBatchAttrs' | 'AIChangeByReplace' | 'AIChangeByDelete' | 'AIChangeByReplaceAround' | 'AIChangeByUnknown' | 'docChangeByAttr' | 'docChangeBySetAttrs' | 'docChangeByBatchAttrs' | 'docChangeByReplace' | 'docChangeByDelete' | 'docChangeByReplaceAround' | 'docChangeByUnknown';
11
+ export interface LocalIdSharedState {
12
+ localIdStatus: Map<string, LocalIdStatusCode> | undefined;
13
+ localIdWatchmenEnabled: boolean | undefined;
14
+ }
9
15
  export type LocalIdPlugin = NextEditorPlugin<'localId', {
10
16
  actions: {
11
17
  /**
@@ -28,6 +34,8 @@ export type LocalIdPlugin = NextEditorPlugin<'localId', {
28
34
  };
29
35
  dependencies: [
30
36
  CompositionPlugin,
31
- OptionalPlugin<CollabEditPlugin>
37
+ OptionalPlugin<CollabEditPlugin>,
38
+ OptionalPlugin<LimitedModePlugin>
32
39
  ];
40
+ sharedState: LocalIdSharedState | undefined;
33
41
  }>;
@@ -0,0 +1,32 @@
1
+ import { SafePlugin } from '@atlaskit/editor-common/safe-plugin';
2
+ import type { ExtractInjectionAPI } from '@atlaskit/editor-common/types';
3
+ import { PluginKey } from '@atlaskit/editor-prosemirror/state';
4
+ import type { LocalIdPlugin, LocalIdStatusCode } from '../localIdPluginType';
5
+ /**
6
+ * Plugin state tracking all localIds in the document
7
+ */
8
+ export type LocalIdWatchmenState = {
9
+ /**
10
+ * This flag controls whether or not the watchmen plugin will process transactions. Caution! disabling and then re-enabling
11
+ * this plugin will cause the watchment to rescan the whole document to sync the localId state.
12
+ */
13
+ enabled: boolean;
14
+ /**
15
+ * This is the initial count of localIds when the plugin was first initialized.
16
+ */
17
+ initLocalIdSize: number;
18
+ /**
19
+ * Timestamp of last state update for debugging
20
+ */
21
+ lastUpdated: number;
22
+ /**
23
+ * This is a list of all localIds which have ever existed in the document and whether or not they're currently
24
+ * on the page (ie "active") or not.
25
+ */
26
+ localIdStatus: Map<string, LocalIdStatusCode>;
27
+ };
28
+ export declare const localIdWatchmenPluginKey: PluginKey<LocalIdWatchmenState>;
29
+ /**
30
+ * Creates the localId watchmen plugin
31
+ */
32
+ export declare const createWatchmenPlugin: (api: ExtractInjectionAPI<LocalIdPlugin> | undefined) => SafePlugin<LocalIdWatchmenState>;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@atlaskit/editor-plugin-local-id",
3
- "version": "5.0.2",
3
+ "version": "5.1.0",
4
4
  "description": "LocalId plugin for @atlaskit/editor-core",
5
5
  "author": "Atlassian Pty Ltd",
6
6
  "license": "Apache-2.0",
@@ -29,14 +29,15 @@
29
29
  "atlaskit:src": "src/index.ts",
30
30
  "dependencies": {
31
31
  "@atlaskit/adf-schema": "^51.5.0",
32
+ "@atlaskit/editor-plugin-limited-mode": "^4.0.0",
32
33
  "@atlaskit/editor-prosemirror": "^7.2.0",
33
34
  "@atlaskit/platform-feature-flags": "^1.1.0",
34
- "@atlaskit/tmp-editor-statsig": "^16.31.0",
35
+ "@atlaskit/tmp-editor-statsig": "^16.34.0",
35
36
  "@babel/runtime": "^7.0.0",
36
37
  "raf-schd": "^4.0.3"
37
38
  },
38
39
  "peerDependencies": {
39
- "@atlaskit/editor-common": "^111.8.0",
40
+ "@atlaskit/editor-common": "^111.9.0",
40
41
  "react": "^18.2.0"
41
42
  },
42
43
  "devDependencies": {
@@ -87,6 +88,9 @@
87
88
  },
88
89
  "platform_editor_localid_improvements": {
89
90
  "type": "boolean"
91
+ },
92
+ "platform_editor_ai_aifc_localid_error_reporting": {
93
+ "type": "boolean"
90
94
  }
91
95
  }
92
96
  }