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