@ckeditor/ckeditor5-watchdog 41.4.2 → 42.0.0-alpha.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/README.md +6 -0
- package/dist/index.js +435 -372
- package/dist/index.js.map +1 -1
- package/dist/types/index.d.ts +2 -2
- package/package.json +1 -1
- package/src/index.d.ts +2 -2
package/dist/index.js
CHANGED
|
@@ -2,12 +2,15 @@
|
|
|
2
2
|
* @license Copyright (c) 2003-2024, CKSource Holding sp. z o.o. All rights reserved.
|
|
3
3
|
* For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
|
|
4
4
|
*/
|
|
5
|
-
import { isElement, cloneDeepWith
|
|
5
|
+
import { throttle, isElement, cloneDeepWith } from 'lodash-es';
|
|
6
6
|
|
|
7
7
|
/**
|
|
8
8
|
* @license Copyright (c) 2003-2024, CKSource Holding sp. z o.o. All rights reserved.
|
|
9
9
|
* For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
|
|
10
10
|
*/ /**
|
|
11
|
+
* @module watchdog/watchdog
|
|
12
|
+
*/ /* globals window */ // eslint-disable-next-line ckeditor5-rules/no-cross-package-imports
|
|
13
|
+
/**
|
|
11
14
|
* An abstract watchdog class that handles most of the error handling process and the state of the underlying component.
|
|
12
15
|
*
|
|
13
16
|
* See the {@glink features/watchdog Watchdog feature guide} to learn the rationale behind it and how to use it.
|
|
@@ -15,40 +18,96 @@ import { isElement, cloneDeepWith, throttle } from 'lodash-es';
|
|
|
15
18
|
* @internal
|
|
16
19
|
*/ class Watchdog {
|
|
17
20
|
/**
|
|
18
|
-
|
|
19
|
-
|
|
21
|
+
* An array of crashes saved as an object with the following properties:
|
|
22
|
+
*
|
|
23
|
+
* * `message`: `String`,
|
|
24
|
+
* * `stack`: `String`,
|
|
25
|
+
* * `date`: `Number`,
|
|
26
|
+
* * `filename`: `String | undefined`,
|
|
27
|
+
* * `lineno`: `Number | undefined`,
|
|
28
|
+
* * `colno`: `Number | undefined`,
|
|
29
|
+
*/ crashes = [];
|
|
30
|
+
/**
|
|
31
|
+
* Specifies the state of the item watched by the watchdog. The state can be one of the following values:
|
|
32
|
+
*
|
|
33
|
+
* * `initializing` – Before the first initialization, and after crashes, before the item is ready.
|
|
34
|
+
* * `ready` – A state when the user can interact with the item.
|
|
35
|
+
* * `crashed` – A state when an error occurs. It quickly changes to `initializing` or `crashedPermanently`
|
|
36
|
+
* depending on how many and how frequent errors have been caught recently.
|
|
37
|
+
* * `crashedPermanently` – A state when the watchdog stops reacting to errors and keeps the item it is watching crashed,
|
|
38
|
+
* * `destroyed` – A state when the item is manually destroyed by the user after calling `watchdog.destroy()`.
|
|
39
|
+
*/ state = 'initializing';
|
|
40
|
+
/**
|
|
41
|
+
* @see module:watchdog/watchdog~WatchdogConfig
|
|
42
|
+
*/ _crashNumberLimit;
|
|
43
|
+
/**
|
|
44
|
+
* Returns the result of the `Date.now()` call. It can be overridden in tests to mock time as some popular
|
|
45
|
+
* approaches like `sinon.useFakeTimers()` do not work well with error handling.
|
|
46
|
+
*/ _now = Date.now;
|
|
47
|
+
/**
|
|
48
|
+
* @see module:watchdog/watchdog~WatchdogConfig
|
|
49
|
+
*/ _minimumNonErrorTimePeriod;
|
|
50
|
+
/**
|
|
51
|
+
* Checks if the event error comes from the underlying item and restarts the item.
|
|
52
|
+
*/ _boundErrorHandler;
|
|
53
|
+
/**
|
|
54
|
+
* A dictionary of event emitter listeners.
|
|
55
|
+
*/ _listeners;
|
|
56
|
+
/**
|
|
57
|
+
* @param {module:watchdog/watchdog~WatchdogConfig} config The watchdog plugin configuration.
|
|
58
|
+
*/ constructor(config){
|
|
59
|
+
this.crashes = [];
|
|
60
|
+
this._crashNumberLimit = typeof config.crashNumberLimit === 'number' ? config.crashNumberLimit : 3;
|
|
61
|
+
this._minimumNonErrorTimePeriod = typeof config.minimumNonErrorTimePeriod === 'number' ? config.minimumNonErrorTimePeriod : 5000;
|
|
62
|
+
this._boundErrorHandler = (evt)=>{
|
|
63
|
+
// `evt.error` is exposed by EventError while `evt.reason` is available in PromiseRejectionEvent.
|
|
64
|
+
const error = 'error' in evt ? evt.error : evt.reason;
|
|
65
|
+
// Note that `evt.reason` might be everything that is in the promise rejection.
|
|
66
|
+
// Similarly everything that is thrown lands in `evt.error`.
|
|
67
|
+
if (error instanceof Error) {
|
|
68
|
+
this._handleError(error, evt);
|
|
69
|
+
}
|
|
70
|
+
};
|
|
71
|
+
this._listeners = {};
|
|
72
|
+
if (!this._restart) {
|
|
73
|
+
throw new Error('The Watchdog class was split into the abstract `Watchdog` class and the `EditorWatchdog` class. ' + 'Please, use `EditorWatchdog` if you have used the `Watchdog` class previously.');
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* Destroys the watchdog and releases the resources.
|
|
78
|
+
*/ destroy() {
|
|
20
79
|
this._stopErrorHandling();
|
|
21
80
|
this._listeners = {};
|
|
22
81
|
}
|
|
23
82
|
/**
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
83
|
+
* Starts listening to a specific event name by registering a callback that will be executed
|
|
84
|
+
* whenever an event with a given name fires.
|
|
85
|
+
*
|
|
86
|
+
* Note that this method differs from the CKEditor 5's default `EventEmitterMixin` implementation.
|
|
87
|
+
*
|
|
88
|
+
* @param eventName The event name.
|
|
89
|
+
* @param callback A callback which will be added to event listeners.
|
|
90
|
+
*/ on(eventName, callback) {
|
|
32
91
|
if (!this._listeners[eventName]) {
|
|
33
92
|
this._listeners[eventName] = [];
|
|
34
93
|
}
|
|
35
94
|
this._listeners[eventName].push(callback);
|
|
36
95
|
}
|
|
37
96
|
/**
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
97
|
+
* Stops listening to the specified event name by removing the callback from event listeners.
|
|
98
|
+
*
|
|
99
|
+
* Note that this method differs from the CKEditor 5's default `EventEmitterMixin` implementation.
|
|
100
|
+
*
|
|
101
|
+
* @param eventName The event name.
|
|
102
|
+
* @param callback A callback which will be removed from event listeners.
|
|
103
|
+
*/ off(eventName, callback) {
|
|
45
104
|
this._listeners[eventName] = this._listeners[eventName].filter((cb)=>cb !== callback);
|
|
46
105
|
}
|
|
47
106
|
/**
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
107
|
+
* Fires an event with a given event name and arguments.
|
|
108
|
+
*
|
|
109
|
+
* Note that this method differs from the CKEditor 5's default `EventEmitterMixin` implementation.
|
|
110
|
+
*/ _fire(eventName, ...args) {
|
|
52
111
|
const callbacks = this._listeners[eventName] || [];
|
|
53
112
|
for (const callback of callbacks){
|
|
54
113
|
callback.apply(this, [
|
|
@@ -58,25 +117,25 @@ import { isElement, cloneDeepWith, throttle } from 'lodash-es';
|
|
|
58
117
|
}
|
|
59
118
|
}
|
|
60
119
|
/**
|
|
61
|
-
|
|
62
|
-
|
|
120
|
+
* Starts error handling by attaching global error handlers.
|
|
121
|
+
*/ _startErrorHandling() {
|
|
63
122
|
window.addEventListener('error', this._boundErrorHandler);
|
|
64
123
|
window.addEventListener('unhandledrejection', this._boundErrorHandler);
|
|
65
124
|
}
|
|
66
125
|
/**
|
|
67
|
-
|
|
68
|
-
|
|
126
|
+
* Stops error handling by detaching global error handlers.
|
|
127
|
+
*/ _stopErrorHandling() {
|
|
69
128
|
window.removeEventListener('error', this._boundErrorHandler);
|
|
70
129
|
window.removeEventListener('unhandledrejection', this._boundErrorHandler);
|
|
71
130
|
}
|
|
72
131
|
/**
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
132
|
+
* Checks if an error comes from the watched item and restarts it.
|
|
133
|
+
* It reacts to {@link module:utils/ckeditorerror~CKEditorError `CKEditorError` errors} only.
|
|
134
|
+
*
|
|
135
|
+
* @fires error
|
|
136
|
+
* @param error Error.
|
|
137
|
+
* @param evt An error event.
|
|
138
|
+
*/ _handleError(error, evt) {
|
|
80
139
|
// @if CK_DEBUG // const err = error as CKEditorError;
|
|
81
140
|
// @if CK_DEBUG // if ( err.is && err.is( 'CKEditorError' ) && err.context === undefined ) {
|
|
82
141
|
// @if CK_DEBUG // console.warn( 'The error is missing its context and Watchdog cannot restart the proper item.' );
|
|
@@ -107,18 +166,18 @@ import { isElement, cloneDeepWith, throttle } from 'lodash-es';
|
|
|
107
166
|
}
|
|
108
167
|
}
|
|
109
168
|
/**
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
169
|
+
* Checks whether an error should be handled by the watchdog.
|
|
170
|
+
*
|
|
171
|
+
* @param error An error that was caught by the error handling process.
|
|
172
|
+
*/ _shouldReactToError(error) {
|
|
114
173
|
return error.is && error.is('CKEditorError') && error.context !== undefined && // In some cases the watched item should not be restarted - e.g. during the item initialization.
|
|
115
174
|
// That's why the `null` was introduced as a correct error context which does cause restarting.
|
|
116
175
|
error.context !== null && // Do not react to errors if the watchdog is in states other than `ready`.
|
|
117
176
|
this.state === 'ready' && this._isErrorComingFromThisItem(error);
|
|
118
177
|
}
|
|
119
178
|
/**
|
|
120
|
-
|
|
121
|
-
|
|
179
|
+
* Checks if the watchdog should restart the underlying item.
|
|
180
|
+
*/ _shouldRestart() {
|
|
122
181
|
if (this.crashes.length <= this._crashNumberLimit) {
|
|
123
182
|
return true;
|
|
124
183
|
}
|
|
@@ -127,50 +186,6 @@ import { isElement, cloneDeepWith, throttle } from 'lodash-es';
|
|
|
127
186
|
const averageNonErrorTimePeriod = (lastErrorTime - firstMeaningfulErrorTime) / this._crashNumberLimit;
|
|
128
187
|
return averageNonErrorTimePeriod > this._minimumNonErrorTimePeriod;
|
|
129
188
|
}
|
|
130
|
-
/**
|
|
131
|
-
* @param {module:watchdog/watchdog~WatchdogConfig} config The watchdog plugin configuration.
|
|
132
|
-
*/ constructor(config){
|
|
133
|
-
/**
|
|
134
|
-
* An array of crashes saved as an object with the following properties:
|
|
135
|
-
*
|
|
136
|
-
* * `message`: `String`,
|
|
137
|
-
* * `stack`: `String`,
|
|
138
|
-
* * `date`: `Number`,
|
|
139
|
-
* * `filename`: `String | undefined`,
|
|
140
|
-
* * `lineno`: `Number | undefined`,
|
|
141
|
-
* * `colno`: `Number | undefined`,
|
|
142
|
-
*/ this.crashes = [];
|
|
143
|
-
/**
|
|
144
|
-
* Specifies the state of the item watched by the watchdog. The state can be one of the following values:
|
|
145
|
-
*
|
|
146
|
-
* * `initializing` – Before the first initialization, and after crashes, before the item is ready.
|
|
147
|
-
* * `ready` – A state when the user can interact with the item.
|
|
148
|
-
* * `crashed` – A state when an error occurs. It quickly changes to `initializing` or `crashedPermanently`
|
|
149
|
-
* depending on how many and how frequent errors have been caught recently.
|
|
150
|
-
* * `crashedPermanently` – A state when the watchdog stops reacting to errors and keeps the item it is watching crashed,
|
|
151
|
-
* * `destroyed` – A state when the item is manually destroyed by the user after calling `watchdog.destroy()`.
|
|
152
|
-
*/ this.state = 'initializing';
|
|
153
|
-
/**
|
|
154
|
-
* Returns the result of the `Date.now()` call. It can be overridden in tests to mock time as some popular
|
|
155
|
-
* approaches like `sinon.useFakeTimers()` do not work well with error handling.
|
|
156
|
-
*/ this._now = Date.now;
|
|
157
|
-
this.crashes = [];
|
|
158
|
-
this._crashNumberLimit = typeof config.crashNumberLimit === 'number' ? config.crashNumberLimit : 3;
|
|
159
|
-
this._minimumNonErrorTimePeriod = typeof config.minimumNonErrorTimePeriod === 'number' ? config.minimumNonErrorTimePeriod : 5000;
|
|
160
|
-
this._boundErrorHandler = (evt)=>{
|
|
161
|
-
// `evt.error` is exposed by EventError while `evt.reason` is available in PromiseRejectionEvent.
|
|
162
|
-
const error = 'error' in evt ? evt.error : evt.reason;
|
|
163
|
-
// Note that `evt.reason` might be everything that is in the promise rejection.
|
|
164
|
-
// Similarly everything that is thrown lands in `evt.error`.
|
|
165
|
-
if (error instanceof Error) {
|
|
166
|
-
this._handleError(error, evt);
|
|
167
|
-
}
|
|
168
|
-
};
|
|
169
|
-
this._listeners = {};
|
|
170
|
-
if (!this._restart) {
|
|
171
|
-
throw new Error('The Watchdog class was split into the abstract `Watchdog` class and the `EditorWatchdog` class. ' + 'Please, use `EditorWatchdog` if you have used the `Watchdog` class previously.');
|
|
172
|
-
}
|
|
173
|
-
}
|
|
174
189
|
}
|
|
175
190
|
|
|
176
191
|
/**
|
|
@@ -257,52 +272,108 @@ function isObject(structure) {
|
|
|
257
272
|
return typeof structure === 'object' && structure !== null;
|
|
258
273
|
}
|
|
259
274
|
|
|
260
|
-
|
|
275
|
+
/**
|
|
276
|
+
* @license Copyright (c) 2003-2024, CKSource Holding sp. z o.o. All rights reserved.
|
|
277
|
+
* For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
|
|
278
|
+
*/ /**
|
|
279
|
+
* @module watchdog/editorwatchdog
|
|
280
|
+
*/ /* globals console */ // eslint-disable-next-line ckeditor5-rules/no-cross-package-imports
|
|
281
|
+
/**
|
|
282
|
+
* A watchdog for CKEditor 5 editors.
|
|
283
|
+
*
|
|
284
|
+
* See the {@glink features/watchdog Watchdog feature guide} to learn the rationale behind it and
|
|
285
|
+
* how to use it.
|
|
286
|
+
*/ class EditorWatchdog extends Watchdog {
|
|
287
|
+
/**
|
|
288
|
+
* The current editor instance.
|
|
289
|
+
*/ _editor = null;
|
|
290
|
+
/**
|
|
291
|
+
* A promise associated with the life cycle of the editor (creation or destruction processes).
|
|
292
|
+
*
|
|
293
|
+
* It is used to prevent the initialization of the editor if the previous instance has not been destroyed yet,
|
|
294
|
+
* and conversely, to prevent the destruction of the editor if it has not been initialized.
|
|
295
|
+
*/ _lifecyclePromise = null;
|
|
296
|
+
/**
|
|
297
|
+
* Throttled save method. The `save()` method is called the specified `saveInterval` after `throttledSave()` is called,
|
|
298
|
+
* unless a new action happens in the meantime.
|
|
299
|
+
*/ _throttledSave;
|
|
300
|
+
/**
|
|
301
|
+
* The latest saved editor data represented as a root name -> root data object.
|
|
302
|
+
*/ _data;
|
|
303
|
+
/**
|
|
304
|
+
* The last document version.
|
|
305
|
+
*/ _lastDocumentVersion;
|
|
306
|
+
/**
|
|
307
|
+
* The editor source element or data.
|
|
308
|
+
*/ _elementOrData;
|
|
309
|
+
/**
|
|
310
|
+
* Specifies whether the editor was initialized using document data (`true`) or HTML elements (`false`).
|
|
311
|
+
*/ _initUsingData = true;
|
|
312
|
+
/**
|
|
313
|
+
* The latest record of the editor editable elements. Used to restart the editor.
|
|
314
|
+
*/ _editables = {};
|
|
315
|
+
/**
|
|
316
|
+
* The editor configuration.
|
|
317
|
+
*/ _config;
|
|
318
|
+
_excludedProps;
|
|
319
|
+
/**
|
|
320
|
+
* @param Editor The editor class.
|
|
321
|
+
* @param watchdogConfig The watchdog plugin configuration.
|
|
322
|
+
*/ constructor(Editor, watchdogConfig = {}){
|
|
323
|
+
super(watchdogConfig);
|
|
324
|
+
// this._editorClass = Editor;
|
|
325
|
+
this._throttledSave = throttle(this._save.bind(this), typeof watchdogConfig.saveInterval === 'number' ? watchdogConfig.saveInterval : 5000);
|
|
326
|
+
// Set default creator and destructor functions:
|
|
327
|
+
if (Editor) {
|
|
328
|
+
this._creator = (elementOrData, config)=>Editor.create(elementOrData, config);
|
|
329
|
+
}
|
|
330
|
+
this._destructor = (editor)=>editor.destroy();
|
|
331
|
+
}
|
|
261
332
|
/**
|
|
262
|
-
|
|
263
|
-
|
|
333
|
+
* The current editor instance.
|
|
334
|
+
*/ get editor() {
|
|
264
335
|
return this._editor;
|
|
265
336
|
}
|
|
266
337
|
/**
|
|
267
|
-
|
|
268
|
-
|
|
338
|
+
* @internal
|
|
339
|
+
*/ get _item() {
|
|
269
340
|
return this._editor;
|
|
270
341
|
}
|
|
271
342
|
/**
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
343
|
+
* Sets the function that is responsible for the editor creation.
|
|
344
|
+
* It expects a function that should return a promise.
|
|
345
|
+
*
|
|
346
|
+
* ```ts
|
|
347
|
+
* watchdog.setCreator( ( element, config ) => ClassicEditor.create( element, config ) );
|
|
348
|
+
* ```
|
|
349
|
+
*/ setCreator(creator) {
|
|
279
350
|
this._creator = creator;
|
|
280
351
|
}
|
|
281
352
|
/**
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
353
|
+
* Sets the function that is responsible for the editor destruction.
|
|
354
|
+
* Overrides the default destruction function, which destroys only the editor instance.
|
|
355
|
+
* It expects a function that should return a promise or `undefined`.
|
|
356
|
+
*
|
|
357
|
+
* ```ts
|
|
358
|
+
* watchdog.setDestructor( editor => {
|
|
359
|
+
* // Do something before the editor is destroyed.
|
|
360
|
+
*
|
|
361
|
+
* return editor
|
|
362
|
+
* .destroy()
|
|
363
|
+
* .then( () => {
|
|
364
|
+
* // Do something after the editor is destroyed.
|
|
365
|
+
* } );
|
|
366
|
+
* } );
|
|
367
|
+
* ```
|
|
368
|
+
*/ setDestructor(destructor) {
|
|
298
369
|
this._destructor = destructor;
|
|
299
370
|
}
|
|
300
371
|
/**
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
372
|
+
* Restarts the editor instance. This method is called whenever an editor error occurs. It fires the `restart` event and changes
|
|
373
|
+
* the state to `initializing`.
|
|
374
|
+
*
|
|
375
|
+
* @fires restart
|
|
376
|
+
*/ _restart() {
|
|
306
377
|
return Promise.resolve().then(()=>{
|
|
307
378
|
this.state = 'initializing';
|
|
308
379
|
this._fire('stateChange');
|
|
@@ -363,12 +434,12 @@ class EditorWatchdog extends Watchdog {
|
|
|
363
434
|
});
|
|
364
435
|
}
|
|
365
436
|
/**
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
437
|
+
* Creates the editor instance and keeps it running, using the defined creator and destructor.
|
|
438
|
+
*
|
|
439
|
+
* @param elementOrData The editor source element or the editor data.
|
|
440
|
+
* @param config The editor configuration.
|
|
441
|
+
* @param context A context for the editor.
|
|
442
|
+
*/ create(elementOrData = this._elementOrData, config = this._config, context) {
|
|
372
443
|
this._lifecyclePromise = Promise.resolve(this._lifecyclePromise).then(()=>{
|
|
373
444
|
super._startErrorHandling();
|
|
374
445
|
this._elementOrData = elementOrData;
|
|
@@ -396,10 +467,10 @@ class EditorWatchdog extends Watchdog {
|
|
|
396
467
|
return this._lifecyclePromise;
|
|
397
468
|
}
|
|
398
469
|
/**
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
470
|
+
* Destroys the watchdog and the current editor instance. It fires the callback
|
|
471
|
+
* registered in {@link #setDestructor `setDestructor()`} and uses it to destroy the editor instance.
|
|
472
|
+
* It also sets the state to `destroyed`.
|
|
473
|
+
*/ destroy() {
|
|
403
474
|
this._lifecyclePromise = Promise.resolve(this._lifecyclePromise).then(()=>{
|
|
404
475
|
this.state = 'destroyed';
|
|
405
476
|
this._fire('stateChange');
|
|
@@ -424,9 +495,9 @@ class EditorWatchdog extends Watchdog {
|
|
|
424
495
|
});
|
|
425
496
|
}
|
|
426
497
|
/**
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
498
|
+
* Saves the editor data, so it can be restored after the crash even if the data cannot be fetched at
|
|
499
|
+
* the moment of the crash.
|
|
500
|
+
*/ _save() {
|
|
430
501
|
const version = this._editor.model.document.version;
|
|
431
502
|
try {
|
|
432
503
|
this._data = this._getData();
|
|
@@ -439,13 +510,13 @@ class EditorWatchdog extends Watchdog {
|
|
|
439
510
|
}
|
|
440
511
|
}
|
|
441
512
|
/**
|
|
442
|
-
|
|
443
|
-
|
|
513
|
+
* @internal
|
|
514
|
+
*/ _setExcludedProperties(props) {
|
|
444
515
|
this._excludedProps = props;
|
|
445
516
|
}
|
|
446
517
|
/**
|
|
447
|
-
|
|
448
|
-
|
|
518
|
+
* Gets all data that is required to reinitialize editor instance.
|
|
519
|
+
*/ _getData() {
|
|
449
520
|
const editor = this._editor;
|
|
450
521
|
const roots = editor.model.document.roots.filter((root)=>root.isAttached() && root.rootName != '$graveyard');
|
|
451
522
|
const { plugins } = editor;
|
|
@@ -490,8 +561,8 @@ class EditorWatchdog extends Watchdog {
|
|
|
490
561
|
return data;
|
|
491
562
|
}
|
|
492
563
|
/**
|
|
493
|
-
|
|
494
|
-
|
|
564
|
+
* For each attached model root, returns its HTML editable element (if available).
|
|
565
|
+
*/ _getEditables() {
|
|
495
566
|
const editables = {};
|
|
496
567
|
for (const rootName of this.editor.model.document.getRootNames()){
|
|
497
568
|
const editable = this.editor.ui.getEditableElement(rootName);
|
|
@@ -502,16 +573,16 @@ class EditorWatchdog extends Watchdog {
|
|
|
502
573
|
return editables;
|
|
503
574
|
}
|
|
504
575
|
/**
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
576
|
+
* Traverses the error context and the current editor to find out whether these structures are connected
|
|
577
|
+
* to each other via properties.
|
|
578
|
+
*
|
|
579
|
+
* @internal
|
|
580
|
+
*/ _isErrorComingFromThisItem(error) {
|
|
510
581
|
return areConnectedThroughProperties(this._editor, error.context, this._excludedProps);
|
|
511
582
|
}
|
|
512
583
|
/**
|
|
513
|
-
|
|
514
|
-
|
|
584
|
+
* Clones the editor configuration.
|
|
585
|
+
*/ _cloneEditorConfiguration(config) {
|
|
515
586
|
return cloneDeepWith(config, (value, key)=>{
|
|
516
587
|
// Leave DOM references.
|
|
517
588
|
if (isElement(value)) {
|
|
@@ -522,42 +593,20 @@ class EditorWatchdog extends Watchdog {
|
|
|
522
593
|
}
|
|
523
594
|
});
|
|
524
595
|
}
|
|
525
|
-
/**
|
|
526
|
-
* @param Editor The editor class.
|
|
527
|
-
* @param watchdogConfig The watchdog plugin configuration.
|
|
528
|
-
*/ constructor(Editor, watchdogConfig = {}){
|
|
529
|
-
super(watchdogConfig);
|
|
530
|
-
/**
|
|
531
|
-
* The current editor instance.
|
|
532
|
-
*/ this._editor = null;
|
|
533
|
-
/**
|
|
534
|
-
* A promise associated with the life cycle of the editor (creation or destruction processes).
|
|
535
|
-
*
|
|
536
|
-
* It is used to prevent the initialization of the editor if the previous instance has not been destroyed yet,
|
|
537
|
-
* and conversely, to prevent the destruction of the editor if it has not been initialized.
|
|
538
|
-
*/ this._lifecyclePromise = null;
|
|
539
|
-
/**
|
|
540
|
-
* Specifies whether the editor was initialized using document data (`true`) or HTML elements (`false`).
|
|
541
|
-
*/ this._initUsingData = true;
|
|
542
|
-
/**
|
|
543
|
-
* The latest record of the editor editable elements. Used to restart the editor.
|
|
544
|
-
*/ this._editables = {};
|
|
545
|
-
// this._editorClass = Editor;
|
|
546
|
-
this._throttledSave = throttle(this._save.bind(this), typeof watchdogConfig.saveInterval === 'number' ? watchdogConfig.saveInterval : 5000);
|
|
547
|
-
// Set default creator and destructor functions:
|
|
548
|
-
if (Editor) {
|
|
549
|
-
this._creator = (elementOrData, config)=>Editor.create(elementOrData, config);
|
|
550
|
-
}
|
|
551
|
-
this._destructor = (editor)=>editor.destroy();
|
|
552
|
-
}
|
|
553
596
|
}
|
|
554
597
|
/**
|
|
555
598
|
* Internal plugin that is used to stop the default editor initialization and restoring the editor state
|
|
556
599
|
* based on the `editor.config._watchdogInitialData` data.
|
|
557
600
|
*/ class EditorWatchdogInitPlugin {
|
|
601
|
+
editor;
|
|
602
|
+
_data;
|
|
603
|
+
constructor(editor){
|
|
604
|
+
this.editor = editor;
|
|
605
|
+
this._data = editor.config.get('_watchdogInitialData');
|
|
606
|
+
}
|
|
558
607
|
/**
|
|
559
|
-
|
|
560
|
-
|
|
608
|
+
* @inheritDoc
|
|
609
|
+
*/ init() {
|
|
561
610
|
// Stops the default editor initialization and use the saved data to restore the editor state.
|
|
562
611
|
// Some of data could not be initialize as a config properties. It is important to keep the data
|
|
563
612
|
// in the same form as it was before the restarting.
|
|
@@ -576,8 +625,8 @@ class EditorWatchdog extends Watchdog {
|
|
|
576
625
|
});
|
|
577
626
|
}
|
|
578
627
|
/**
|
|
579
|
-
|
|
580
|
-
|
|
628
|
+
* Creates a model node (element or text) based on provided JSON.
|
|
629
|
+
*/ _createNode(writer, jsonNode) {
|
|
581
630
|
if ('name' in jsonNode) {
|
|
582
631
|
// If child has name property, it is an Element.
|
|
583
632
|
const element = writer.createElement(jsonNode.name, jsonNode.attributes);
|
|
@@ -593,8 +642,8 @@ class EditorWatchdog extends Watchdog {
|
|
|
593
642
|
}
|
|
594
643
|
}
|
|
595
644
|
/**
|
|
596
|
-
|
|
597
|
-
|
|
645
|
+
* Restores the editor by setting the document data, roots attributes and markers.
|
|
646
|
+
*/ _restoreEditorData(writer) {
|
|
598
647
|
const editor = this.editor;
|
|
599
648
|
Object.entries(this._data.roots).forEach(([rootName, { content, attributes }])=>{
|
|
600
649
|
const parsedNodes = JSON.parse(content);
|
|
@@ -622,8 +671,8 @@ class EditorWatchdog extends Watchdog {
|
|
|
622
671
|
});
|
|
623
672
|
}
|
|
624
673
|
/**
|
|
625
|
-
|
|
626
|
-
|
|
674
|
+
* Restores the editor collaboration data - comment threads and suggestions.
|
|
675
|
+
*/ _restoreCollaborationData() {
|
|
627
676
|
// `as any` to avoid linking from external private repo.
|
|
628
677
|
const parsedCommentThreads = JSON.parse(this._data.commentThreads);
|
|
629
678
|
const parsedSuggestions = JSON.parse(this._data.suggestions);
|
|
@@ -649,136 +698,193 @@ class EditorWatchdog extends Watchdog {
|
|
|
649
698
|
}
|
|
650
699
|
});
|
|
651
700
|
}
|
|
652
|
-
constructor(editor){
|
|
653
|
-
this.editor = editor;
|
|
654
|
-
this._data = editor.config.get('_watchdogInitialData');
|
|
655
|
-
}
|
|
656
701
|
}
|
|
657
702
|
|
|
703
|
+
/**
|
|
704
|
+
* @license Copyright (c) 2003-2024, CKSource Holding sp. z o.o. All rights reserved.
|
|
705
|
+
* For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
|
|
706
|
+
*/ /**
|
|
707
|
+
* @module watchdog/contextwatchdog
|
|
708
|
+
*/ /* globals console */ // eslint-disable-next-line ckeditor5-rules/no-cross-package-imports
|
|
658
709
|
const mainQueueId = Symbol('MainQueueId');
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
710
|
+
/**
|
|
711
|
+
* A watchdog for the {@link module:core/context~Context} class.
|
|
712
|
+
*
|
|
713
|
+
* See the {@glink features/watchdog Watchdog feature guide} to learn the rationale behind it and
|
|
714
|
+
* how to use it.
|
|
715
|
+
*/ class ContextWatchdog extends Watchdog {
|
|
716
|
+
/**
|
|
717
|
+
* A map of internal watchdogs for added items.
|
|
718
|
+
*/ _watchdogs = new Map();
|
|
719
|
+
/**
|
|
720
|
+
* The watchdog configuration.
|
|
721
|
+
*/ _watchdogConfig;
|
|
722
|
+
/**
|
|
723
|
+
* The current context instance.
|
|
724
|
+
*/ _context = null;
|
|
725
|
+
/**
|
|
726
|
+
* Context properties (nodes/references) that are gathered during the initial context creation
|
|
727
|
+
* and are used to distinguish the origin of an error.
|
|
728
|
+
*/ _contextProps = new Set();
|
|
729
|
+
/**
|
|
730
|
+
* An action queue, which is used to handle async functions queuing.
|
|
731
|
+
*/ _actionQueues = new ActionQueues();
|
|
732
|
+
/**
|
|
733
|
+
* The configuration for the {@link module:core/context~Context}.
|
|
734
|
+
*/ _contextConfig;
|
|
735
|
+
/**
|
|
736
|
+
* The watched item.
|
|
737
|
+
*/ _item;
|
|
738
|
+
/**
|
|
739
|
+
* The context watchdog class constructor.
|
|
740
|
+
*
|
|
741
|
+
* ```ts
|
|
742
|
+
* const watchdog = new ContextWatchdog( Context );
|
|
743
|
+
*
|
|
744
|
+
* await watchdog.create( contextConfiguration );
|
|
745
|
+
*
|
|
746
|
+
* await watchdog.add( item );
|
|
747
|
+
* ```
|
|
748
|
+
*
|
|
749
|
+
* See the {@glink features/watchdog Watchdog feature guide} to learn more how to use this feature.
|
|
750
|
+
*
|
|
751
|
+
* @param Context The {@link module:core/context~Context} class.
|
|
752
|
+
* @param watchdogConfig The watchdog configuration.
|
|
753
|
+
*/ constructor(Context, watchdogConfig = {}){
|
|
754
|
+
super(watchdogConfig);
|
|
755
|
+
this._watchdogConfig = watchdogConfig;
|
|
756
|
+
// Default creator and destructor.
|
|
757
|
+
this._creator = (contextConfig)=>Context.create(contextConfig);
|
|
758
|
+
this._destructor = (context)=>context.destroy();
|
|
759
|
+
this._actionQueues.onEmpty(()=>{
|
|
760
|
+
if (this.state === 'initializing') {
|
|
761
|
+
this.state = 'ready';
|
|
762
|
+
this._fire('stateChange');
|
|
763
|
+
}
|
|
764
|
+
});
|
|
765
|
+
}
|
|
766
|
+
/**
|
|
767
|
+
* Sets the function that is responsible for the context creation.
|
|
768
|
+
* It expects a function that should return a promise (or `undefined`).
|
|
769
|
+
*
|
|
770
|
+
* ```ts
|
|
771
|
+
* watchdog.setCreator( config => Context.create( config ) );
|
|
772
|
+
* ```
|
|
773
|
+
*/ setCreator(creator) {
|
|
668
774
|
this._creator = creator;
|
|
669
775
|
}
|
|
670
776
|
/**
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
777
|
+
* Sets the function that is responsible for the context destruction.
|
|
778
|
+
* Overrides the default destruction function, which destroys only the context instance.
|
|
779
|
+
* It expects a function that should return a promise (or `undefined`).
|
|
780
|
+
*
|
|
781
|
+
* ```ts
|
|
782
|
+
* watchdog.setDestructor( context => {
|
|
783
|
+
* // Do something before the context is destroyed.
|
|
784
|
+
*
|
|
785
|
+
* return context
|
|
786
|
+
* .destroy()
|
|
787
|
+
* .then( () => {
|
|
788
|
+
* // Do something after the context is destroyed.
|
|
789
|
+
* } );
|
|
790
|
+
* } );
|
|
791
|
+
* ```
|
|
792
|
+
*/ setDestructor(destructor) {
|
|
687
793
|
this._destructor = destructor;
|
|
688
794
|
}
|
|
689
795
|
/**
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
796
|
+
* The context instance. Keep in mind that this property might be changed when the context watchdog restarts,
|
|
797
|
+
* so do not keep this instance internally. Always operate on the `ContextWatchdog#context` property.
|
|
798
|
+
*/ get context() {
|
|
693
799
|
return this._context;
|
|
694
800
|
}
|
|
695
801
|
/**
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
802
|
+
* Initializes the context watchdog. Once it is created, the watchdog takes care about
|
|
803
|
+
* recreating the context and the provided items, and starts the error handling mechanism.
|
|
804
|
+
*
|
|
805
|
+
* ```ts
|
|
806
|
+
* await watchdog.create( {
|
|
807
|
+
* plugins: []
|
|
808
|
+
* } );
|
|
809
|
+
* ```
|
|
810
|
+
*
|
|
811
|
+
* @param contextConfig The context configuration. See {@link module:core/context~Context}.
|
|
812
|
+
*/ create(contextConfig = {}) {
|
|
707
813
|
return this._actionQueues.enqueue(mainQueueId, ()=>{
|
|
708
814
|
this._contextConfig = contextConfig;
|
|
709
815
|
return this._create();
|
|
710
816
|
});
|
|
711
817
|
}
|
|
712
818
|
/**
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
819
|
+
* Returns an item instance with the given `itemId`.
|
|
820
|
+
*
|
|
821
|
+
* ```ts
|
|
822
|
+
* const editor1 = watchdog.getItem( 'editor1' );
|
|
823
|
+
* ```
|
|
824
|
+
*
|
|
825
|
+
* @param itemId The item ID.
|
|
826
|
+
* @returns The item instance or `undefined` if an item with a given ID has not been found.
|
|
827
|
+
*/ getItem(itemId) {
|
|
722
828
|
const watchdog = this._getWatchdog(itemId);
|
|
723
829
|
return watchdog._item;
|
|
724
830
|
}
|
|
725
831
|
/**
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
832
|
+
* Gets the state of the given item. See {@link #state} for a list of available states.
|
|
833
|
+
*
|
|
834
|
+
* ```ts
|
|
835
|
+
* const editor1State = watchdog.getItemState( 'editor1' );
|
|
836
|
+
* ```
|
|
837
|
+
*
|
|
838
|
+
* @param itemId Item ID.
|
|
839
|
+
* @returns The state of the item.
|
|
840
|
+
*/ getItemState(itemId) {
|
|
735
841
|
const watchdog = this._getWatchdog(itemId);
|
|
736
842
|
return watchdog.state;
|
|
737
843
|
}
|
|
738
844
|
/**
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
845
|
+
* Adds items to the watchdog. Once created, instances of these items will be available using the {@link #getItem} method.
|
|
846
|
+
*
|
|
847
|
+
* Items can be passed together as an array of objects:
|
|
848
|
+
*
|
|
849
|
+
* ```ts
|
|
850
|
+
* await watchdog.add( [ {
|
|
851
|
+
* id: 'editor1',
|
|
852
|
+
* type: 'editor',
|
|
853
|
+
* sourceElementOrData: document.querySelector( '#editor' ),
|
|
854
|
+
* config: {
|
|
855
|
+
* plugins: [ Essentials, Paragraph, Bold, Italic ],
|
|
856
|
+
* toolbar: [ 'bold', 'italic', 'alignment' ]
|
|
857
|
+
* },
|
|
858
|
+
* creator: ( element, config ) => ClassicEditor.create( element, config )
|
|
859
|
+
* } ] );
|
|
860
|
+
* ```
|
|
861
|
+
*
|
|
862
|
+
* Or one by one as objects:
|
|
863
|
+
*
|
|
864
|
+
* ```ts
|
|
865
|
+
* await watchdog.add( {
|
|
866
|
+
* id: 'editor1',
|
|
867
|
+
* type: 'editor',
|
|
868
|
+
* sourceElementOrData: document.querySelector( '#editor' ),
|
|
869
|
+
* config: {
|
|
870
|
+
* plugins: [ Essentials, Paragraph, Bold, Italic ],
|
|
871
|
+
* toolbar: [ 'bold', 'italic', 'alignment' ]
|
|
872
|
+
* },
|
|
873
|
+
* creator: ( element, config ) => ClassicEditor.create( element, config )
|
|
874
|
+
* ] );
|
|
875
|
+
* ```
|
|
876
|
+
*
|
|
877
|
+
* Then an instance can be retrieved using the {@link #getItem} method:
|
|
878
|
+
*
|
|
879
|
+
* ```ts
|
|
880
|
+
* const editor1 = watchdog.getItem( 'editor1' );
|
|
881
|
+
* ```
|
|
882
|
+
*
|
|
883
|
+
* Note that this method can be called multiple times, but for performance reasons it is better
|
|
884
|
+
* to pass all items together.
|
|
885
|
+
*
|
|
886
|
+
* @param itemConfigurationOrItemConfigurations An item configuration object or an array of item configurations.
|
|
887
|
+
*/ add(itemConfigurationOrItemConfigurations) {
|
|
782
888
|
const itemConfigurations = toArray(itemConfigurationOrItemConfigurations);
|
|
783
889
|
return Promise.all(itemConfigurations.map((item)=>{
|
|
784
890
|
return this._actionQueues.enqueue(item.id, ()=>{
|
|
@@ -830,20 +936,20 @@ class ContextWatchdog extends Watchdog {
|
|
|
830
936
|
}));
|
|
831
937
|
}
|
|
832
938
|
/**
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
939
|
+
* Removes and destroys item(s) with given ID(s).
|
|
940
|
+
*
|
|
941
|
+
* ```ts
|
|
942
|
+
* await watchdog.remove( 'editor1' );
|
|
943
|
+
* ```
|
|
944
|
+
*
|
|
945
|
+
* Or
|
|
946
|
+
*
|
|
947
|
+
* ```ts
|
|
948
|
+
* await watchdog.remove( [ 'editor1', 'editor2' ] );
|
|
949
|
+
* ```
|
|
950
|
+
*
|
|
951
|
+
* @param itemIdOrItemIds Item ID or an array of item IDs.
|
|
952
|
+
*/ remove(itemIdOrItemIds) {
|
|
847
953
|
const itemIds = toArray(itemIdOrItemIds);
|
|
848
954
|
return Promise.all(itemIds.map((itemId)=>{
|
|
849
955
|
return this._actionQueues.enqueue(itemId, ()=>{
|
|
@@ -854,13 +960,13 @@ class ContextWatchdog extends Watchdog {
|
|
|
854
960
|
}));
|
|
855
961
|
}
|
|
856
962
|
/**
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
963
|
+
* Destroys the context watchdog and all added items.
|
|
964
|
+
* Once the context watchdog is destroyed, new items cannot be added.
|
|
965
|
+
*
|
|
966
|
+
* ```ts
|
|
967
|
+
* await watchdog.destroy();
|
|
968
|
+
* ```
|
|
969
|
+
*/ destroy() {
|
|
864
970
|
return this._actionQueues.enqueue(mainQueueId, ()=>{
|
|
865
971
|
this.state = 'destroyed';
|
|
866
972
|
this._fire('stateChange');
|
|
@@ -869,8 +975,8 @@ class ContextWatchdog extends Watchdog {
|
|
|
869
975
|
});
|
|
870
976
|
}
|
|
871
977
|
/**
|
|
872
|
-
|
|
873
|
-
|
|
978
|
+
* Restarts the context watchdog.
|
|
979
|
+
*/ _restart() {
|
|
874
980
|
return this._actionQueues.enqueue(mainQueueId, ()=>{
|
|
875
981
|
this.state = 'initializing';
|
|
876
982
|
this._fire('stateChange');
|
|
@@ -880,8 +986,8 @@ class ContextWatchdog extends Watchdog {
|
|
|
880
986
|
});
|
|
881
987
|
}
|
|
882
988
|
/**
|
|
883
|
-
|
|
884
|
-
|
|
989
|
+
* Initializes the context watchdog.
|
|
990
|
+
*/ _create() {
|
|
885
991
|
return Promise.resolve().then(()=>{
|
|
886
992
|
this._startErrorHandling();
|
|
887
993
|
return this._creator(this._contextConfig);
|
|
@@ -895,8 +1001,8 @@ class ContextWatchdog extends Watchdog {
|
|
|
895
1001
|
});
|
|
896
1002
|
}
|
|
897
1003
|
/**
|
|
898
|
-
|
|
899
|
-
|
|
1004
|
+
* Destroys the context instance and all added items.
|
|
1005
|
+
*/ _destroy() {
|
|
900
1006
|
return Promise.resolve().then(()=>{
|
|
901
1007
|
this._stopErrorHandling();
|
|
902
1008
|
const context = this._context;
|
|
@@ -907,10 +1013,10 @@ class ContextWatchdog extends Watchdog {
|
|
|
907
1013
|
});
|
|
908
1014
|
}
|
|
909
1015
|
/**
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
1016
|
+
* Returns the watchdog for a given item ID.
|
|
1017
|
+
*
|
|
1018
|
+
* @param itemId Item ID.
|
|
1019
|
+
*/ _getWatchdog(itemId) {
|
|
914
1020
|
const watchdog = this._watchdogs.get(itemId);
|
|
915
1021
|
if (!watchdog) {
|
|
916
1022
|
throw new Error(`Item with the given id was not registered: ${itemId}.`);
|
|
@@ -918,10 +1024,10 @@ class ContextWatchdog extends Watchdog {
|
|
|
918
1024
|
return watchdog;
|
|
919
1025
|
}
|
|
920
1026
|
/**
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
1027
|
+
* Checks whether an error comes from the context instance and not from the item instances.
|
|
1028
|
+
*
|
|
1029
|
+
* @internal
|
|
1030
|
+
*/ _isErrorComingFromThisItem(error) {
|
|
925
1031
|
for (const watchdog of this._watchdogs.values()){
|
|
926
1032
|
if (watchdog._isErrorComingFromThisItem(error)) {
|
|
927
1033
|
return false;
|
|
@@ -929,64 +1035,26 @@ class ContextWatchdog extends Watchdog {
|
|
|
929
1035
|
}
|
|
930
1036
|
return areConnectedThroughProperties(this._context, error.context);
|
|
931
1037
|
}
|
|
932
|
-
/**
|
|
933
|
-
* The context watchdog class constructor.
|
|
934
|
-
*
|
|
935
|
-
* ```ts
|
|
936
|
-
* const watchdog = new ContextWatchdog( Context );
|
|
937
|
-
*
|
|
938
|
-
* await watchdog.create( contextConfiguration );
|
|
939
|
-
*
|
|
940
|
-
* await watchdog.add( item );
|
|
941
|
-
* ```
|
|
942
|
-
*
|
|
943
|
-
* See the {@glink features/watchdog Watchdog feature guide} to learn more how to use this feature.
|
|
944
|
-
*
|
|
945
|
-
* @param Context The {@link module:core/context~Context} class.
|
|
946
|
-
* @param watchdogConfig The watchdog configuration.
|
|
947
|
-
*/ constructor(Context, watchdogConfig = {}){
|
|
948
|
-
super(watchdogConfig);
|
|
949
|
-
/**
|
|
950
|
-
* A map of internal watchdogs for added items.
|
|
951
|
-
*/ this._watchdogs = new Map();
|
|
952
|
-
/**
|
|
953
|
-
* The current context instance.
|
|
954
|
-
*/ this._context = null;
|
|
955
|
-
/**
|
|
956
|
-
* Context properties (nodes/references) that are gathered during the initial context creation
|
|
957
|
-
* and are used to distinguish the origin of an error.
|
|
958
|
-
*/ this._contextProps = new Set();
|
|
959
|
-
/**
|
|
960
|
-
* An action queue, which is used to handle async functions queuing.
|
|
961
|
-
*/ this._actionQueues = new ActionQueues();
|
|
962
|
-
this._watchdogConfig = watchdogConfig;
|
|
963
|
-
// Default creator and destructor.
|
|
964
|
-
this._creator = (contextConfig)=>Context.create(contextConfig);
|
|
965
|
-
this._destructor = (context)=>context.destroy();
|
|
966
|
-
this._actionQueues.onEmpty(()=>{
|
|
967
|
-
if (this.state === 'initializing') {
|
|
968
|
-
this.state = 'ready';
|
|
969
|
-
this._fire('stateChange');
|
|
970
|
-
}
|
|
971
|
-
});
|
|
972
|
-
}
|
|
973
1038
|
}
|
|
974
1039
|
/**
|
|
975
1040
|
* Manager of action queues that allows queuing async functions.
|
|
976
1041
|
*/ class ActionQueues {
|
|
1042
|
+
_onEmptyCallbacks = [];
|
|
1043
|
+
_queues = new Map();
|
|
1044
|
+
_activeActions = 0;
|
|
977
1045
|
/**
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
|
|
1046
|
+
* Used to register callbacks that will be run when the queue becomes empty.
|
|
1047
|
+
*
|
|
1048
|
+
* @param onEmptyCallback A callback that will be run whenever the queue becomes empty.
|
|
1049
|
+
*/ onEmpty(onEmptyCallback) {
|
|
982
1050
|
this._onEmptyCallbacks.push(onEmptyCallback);
|
|
983
1051
|
}
|
|
984
1052
|
/**
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
1053
|
+
* It adds asynchronous actions (functions) to the proper queue and runs them one by one.
|
|
1054
|
+
*
|
|
1055
|
+
* @param queueId The action queue ID.
|
|
1056
|
+
* @param action A function that should be enqueued.
|
|
1057
|
+
*/ enqueue(queueId, action) {
|
|
990
1058
|
const isMainAction = queueId === mainQueueId;
|
|
991
1059
|
this._activeActions++;
|
|
992
1060
|
if (!this._queues.get(queueId)) {
|
|
@@ -1010,11 +1078,6 @@ class ContextWatchdog extends Watchdog {
|
|
|
1010
1078
|
}
|
|
1011
1079
|
});
|
|
1012
1080
|
}
|
|
1013
|
-
constructor(){
|
|
1014
|
-
this._onEmptyCallbacks = [];
|
|
1015
|
-
this._queues = new Map();
|
|
1016
|
-
this._activeActions = 0;
|
|
1017
|
-
}
|
|
1018
1081
|
}
|
|
1019
1082
|
/**
|
|
1020
1083
|
* Transforms any value to an array. If the provided value is already an array, it is returned unchanged.
|