@ckeditor/ckeditor5-cloud-services 48.2.0 → 48.3.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/dist/index.js CHANGED
@@ -2,604 +2,630 @@
2
2
  * @license Copyright (c) 2003-2026, CKSource Holding sp. z o.o. All rights reserved.
3
3
  * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-licensing-options
4
4
  */
5
- import { ContextPlugin } from '@ckeditor/ckeditor5-core/dist/index.js';
6
- import { ObservableMixin, CKEditorError, logWarning, EmitterMixin } from '@ckeditor/ckeditor5-utils/dist/index.js';
5
+ import { ContextPlugin } from "@ckeditor/ckeditor5-core";
6
+ import { CKEditorError, EmitterMixin, ObservableMixin, logWarning } from "@ckeditor/ckeditor5-utils";
7
7
 
8
- const DEFAULT_OPTIONS = {
9
- autoRefresh: true
10
- };
11
- const DEFAULT_TOKEN_REFRESH_TIMEOUT_TIME = 3600000; // 1 hour
12
- const TOKEN_FAILED_REFRESH_TIMEOUT_TIME = 5000; // 5 seconds
13
8
  /**
14
- * The class representing the token used for communication with CKEditor Cloud Services.
15
- * The value of the token is retrieved from the specified URL and refreshed every 1 hour by default.
16
- * If the token retrieval fails, the token will automatically retry in 5 seconds intervals.
17
- */ class Token extends /* #__PURE__ */ ObservableMixin() {
18
- /**
19
- * Base refreshing function.
20
- */ _refresh;
21
- /**
22
- * Cached token options.
23
- */ _options;
24
- /**
25
- * `setTimeout()` id for a token refresh when {@link module:cloud-services/token/token~CloudServicesTokenOptions auto refresh}
26
- * is enabled.
27
- */ _tokenRefreshTimeout;
28
- /**
29
- * Flag indicating whether the token has been destroyed.
30
- */ _isDestroyed = false;
31
- /**
32
- * Creates `Token` instance.
33
- * Method `init` should be called after using the constructor or use `create` method instead.
34
- *
35
- * @param tokenUrlOrRefreshToken Endpoint address to download the token or a callback that provides the token. If the
36
- * value is a function it has to match the {@link module:cloud-services/token/token~Token#refreshToken} interface.
37
- */ constructor(tokenUrlOrRefreshToken, options = {}){
38
- super();
39
- if (!tokenUrlOrRefreshToken) {
40
- /**
41
- * A `tokenUrl` must be provided as the first constructor argument.
42
- *
43
- * @error token-missing-token-url
44
- */ throw new CKEditorError('token-missing-token-url', this);
45
- }
46
- if (options.initValue) {
47
- this._validateTokenValue(options.initValue);
48
- }
49
- this.set('value', options.initValue);
50
- if (typeof tokenUrlOrRefreshToken === 'function') {
51
- this._refresh = tokenUrlOrRefreshToken;
52
- } else {
53
- this._refresh = ()=>defaultRefreshToken(tokenUrlOrRefreshToken);
54
- }
55
- this._options = {
56
- ...DEFAULT_OPTIONS,
57
- ...options
58
- };
59
- }
60
- /**
61
- * Initializes the token.
62
- */ init() {
63
- return new Promise((resolve, reject)=>{
64
- if (!this.value) {
65
- this.refreshToken().then(resolve).catch(reject);
66
- return;
67
- }
68
- if (this._options.autoRefresh) {
69
- this._registerRefreshTokenTimeout();
70
- }
71
- resolve(this);
72
- });
73
- }
74
- /**
75
- * Refresh token method. Useful in a method form as it can be overridden in tests.
76
- *
77
- * This method will be invoked periodically based on the token expiry date after first call to keep the token up-to-date
78
- * (requires {@link module:cloud-services/token/token~CloudServicesTokenOptions auto refresh option} to be set).
79
- *
80
- * If the token refresh fails, the method will retry in 5 seconds intervals until success or the token gets
81
- * {@link #destroy destroyed}.
82
- */ refreshToken() {
83
- const autoRefresh = this._options.autoRefresh;
84
- return this._refresh().then((value)=>{
85
- this._validateTokenValue(value);
86
- this.set('value', value);
87
- if (autoRefresh) {
88
- this._registerRefreshTokenTimeout();
89
- }
90
- return this;
91
- }).catch((err)=>{
92
- /**
93
- * You will see this warning when the CKEditor {@link module:cloud-services/token/token~Token token} could not be refreshed.
94
- * This may be a result of a network error, a token endpoint (server) error, or an invalid
95
- * {@link module:cloud-services/cloudservicesconfig~CloudServicesConfig#tokenUrl token URL configuration}.
96
- *
97
- * If this warning repeats, please make sure that the configuration is correct and that the token
98
- * endpoint is up and running. {@link module:cloud-services/cloudservicesconfig~CloudServicesConfig#tokenUrl Learn more}
99
- * about token configuration.
100
- *
101
- * **Note:** If the token's {@link module:cloud-services/token/token~CloudServicesTokenOptions auto refresh option}
102
- * is enabled, attempts to refresh will be made until success or token's
103
- * {@link module:cloud-services/token/token~Token#destroy destruction}.
104
- *
105
- * @error token-refresh-failed
106
- * @param {boolean} autoRefresh Whether the token will keep auto refreshing.
107
- */ logWarning('token-refresh-failed', {
108
- autoRefresh
109
- });
110
- // If the refresh failed, keep trying to refresh the token. Failing to do so will eventually
111
- // lead to the disconnection from the RTC service and the editing session (and potential data loss
112
- // if the user keeps editing).
113
- if (autoRefresh) {
114
- this._registerRefreshTokenTimeout(TOKEN_FAILED_REFRESH_TIMEOUT_TIME);
115
- }
116
- throw err;
117
- });
118
- }
119
- /**
120
- * Destroys token instance. Stops refreshing.
121
- */ destroy() {
122
- this._isDestroyed = true;
123
- clearTimeout(this._tokenRefreshTimeout);
124
- }
125
- /**
126
- * Checks whether the provided token follows the JSON Web Tokens (JWT) format.
127
- *
128
- * @param tokenValue The token to validate.
129
- */ _validateTokenValue(tokenValue) {
130
- // The token must be a string.
131
- const isString = typeof tokenValue === 'string';
132
- // The token must be a plain string without quotes ("").
133
- const isPlainString = !/^".*"$/.test(tokenValue);
134
- // JWT token contains 3 parts: header, payload, and signature.
135
- // Each part is separated by a dot.
136
- const isJWTFormat = isString && tokenValue.split('.').length === 3;
137
- if (!(isPlainString && isJWTFormat)) {
138
- /**
139
- * The provided token must follow the [JSON Web Tokens](https://jwt.io/introduction/) format.
140
- *
141
- * @error token-not-in-jwt-format
142
- */ throw new CKEditorError('token-not-in-jwt-format', this);
143
- }
144
- }
145
- /**
146
- * Registers a refresh token timeout for the time taken from token.
147
- */ _registerRefreshTokenTimeout(timeoutTime) {
148
- clearTimeout(this._tokenRefreshTimeout);
149
- if (this._isDestroyed) {
150
- return;
151
- }
152
- const tokenRefreshTimeoutTime = timeoutTime || this._getTokenRefreshTimeoutTime();
153
- this._tokenRefreshTimeout = setTimeout(()=>{
154
- this.refreshToken();
155
- }, tokenRefreshTimeoutTime);
156
- }
157
- /**
158
- * Returns token refresh timeout time calculated from expire time in the token payload.
159
- *
160
- * If the token parse fails or the token payload doesn't contain, the default DEFAULT_TOKEN_REFRESH_TIMEOUT_TIME is returned.
161
- */ _getTokenRefreshTimeoutTime() {
162
- try {
163
- const [, binaryTokenPayload] = this.value.split('.');
164
- const { exp: tokenExpireTime } = JSON.parse(atob(binaryTokenPayload));
165
- if (!tokenExpireTime) {
166
- return DEFAULT_TOKEN_REFRESH_TIMEOUT_TIME;
167
- }
168
- // Check if the token expire time exceeds 32-bit integer range
169
- // It could happen if the token expire time is provided in milliseconds instead of seconds.
170
- if (tokenExpireTime > 2147483647) {
171
- console.warn('Token expiration time exceeds 32-bit integer range. This might cause unpredictable token refresh timing. ' + 'Token expiration time should always be provided in seconds.', {
172
- tokenExpireTime
173
- });
174
- }
175
- const tokenRefreshTimeoutTime = Math.floor((tokenExpireTime * 1000 - Date.now()) / 2);
176
- return tokenRefreshTimeoutTime;
177
- } catch {
178
- return DEFAULT_TOKEN_REFRESH_TIMEOUT_TIME;
179
- }
180
- }
181
- /**
182
- * Creates a initialized {@link module:cloud-services/token/token~Token} instance.
183
- *
184
- * @param tokenUrlOrRefreshToken Endpoint address to download the token or a callback that provides the token. If the
185
- * value is a function it has to match the {@link module:cloud-services/token/token~Token#refreshToken} interface.
186
- */ static create(tokenUrlOrRefreshToken, options = {}) {
187
- const token = new Token(tokenUrlOrRefreshToken, options);
188
- return token.init();
189
- }
190
- }
9
+ * @license Copyright (c) 2003-2026, CKSource Holding sp. z o.o. All rights reserved.
10
+ * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-licensing-options
11
+ */
12
+ /**
13
+ * @module cloud-services/token/token
14
+ */
15
+ const DEFAULT_OPTIONS = { autoRefresh: true };
16
+ const DEFAULT_TOKEN_REFRESH_TIMEOUT_TIME = 36e5;
17
+ const TOKEN_FAILED_REFRESH_TIMEOUT_TIME = 5e3;
18
+ const TokenBase = /* #__PURE__ */ ObservableMixin();
191
19
  /**
192
- * This function is called in a defined interval by the {@link ~Token} class. It also can be invoked manually.
193
- * It should return a promise, which resolves with the new token value.
194
- * If any error occurs it should return a rejected promise with an error message.
195
- */ function defaultRefreshToken(tokenUrl) {
196
- return new Promise((resolve, reject)=>{
197
- const xhr = new XMLHttpRequest();
198
- xhr.open('GET', tokenUrl);
199
- xhr.addEventListener('load', ()=>{
200
- const statusCode = xhr.status;
201
- const xhrResponse = xhr.response;
202
- if (statusCode < 200 || statusCode > 299) {
203
- /**
204
- * Cannot download new token from the provided url.
205
- *
206
- * @error token-cannot-download-new-token
207
- */ return reject(new CKEditorError('token-cannot-download-new-token', null));
208
- }
209
- return resolve(xhrResponse);
210
- });
211
- xhr.addEventListener('error', ()=>reject(new Error('Network Error')));
212
- xhr.addEventListener('abort', ()=>reject(new Error('Abort')));
213
- xhr.send();
214
- });
20
+ * The class representing the token used for communication with CKEditor Cloud Services.
21
+ * The value of the token is retrieved from the specified URL and refreshed every 1 hour by default.
22
+ * If the token retrieval fails, the token will automatically retry in 5 seconds intervals.
23
+ */
24
+ var Token = class Token extends TokenBase {
25
+ /**
26
+ * Base refreshing function.
27
+ */
28
+ _refresh;
29
+ /**
30
+ * Cached token options.
31
+ */
32
+ _options;
33
+ /**
34
+ * `setTimeout()` id for a token refresh when {@link module:cloud-services/token/token~CloudServicesTokenOptions auto refresh}
35
+ * is enabled.
36
+ */
37
+ _tokenRefreshTimeout;
38
+ /**
39
+ * Flag indicating whether the token has been destroyed.
40
+ */
41
+ _isDestroyed = false;
42
+ /**
43
+ * Creates `Token` instance.
44
+ * Method `init` should be called after using the constructor or use `create` method instead.
45
+ *
46
+ * @param tokenUrlOrRefreshToken Endpoint address to download the token or a callback that provides the token. If the
47
+ * value is a function it has to match the {@link module:cloud-services/token/token~Token#refreshToken} interface.
48
+ */
49
+ constructor(tokenUrlOrRefreshToken, options = {}) {
50
+ super();
51
+ if (!tokenUrlOrRefreshToken)
52
+ /**
53
+ * A `tokenUrl` must be provided as the first constructor argument.
54
+ *
55
+ * @error token-missing-token-url
56
+ */
57
+ throw new CKEditorError("token-missing-token-url", this);
58
+ if (options.initValue) this._validateTokenValue(options.initValue);
59
+ this.set("value", options.initValue);
60
+ if (typeof tokenUrlOrRefreshToken === "function") this._refresh = tokenUrlOrRefreshToken;
61
+ else this._refresh = () => defaultRefreshToken(tokenUrlOrRefreshToken);
62
+ this._options = {
63
+ ...DEFAULT_OPTIONS,
64
+ ...options
65
+ };
66
+ }
67
+ /**
68
+ * Initializes the token.
69
+ */
70
+ init() {
71
+ return new Promise((resolve, reject) => {
72
+ if (!this.value) {
73
+ this.refreshToken().then(resolve).catch(reject);
74
+ return;
75
+ }
76
+ if (this._options.autoRefresh) this._registerRefreshTokenTimeout();
77
+ resolve(this);
78
+ });
79
+ }
80
+ /**
81
+ * Refresh token method. Useful in a method form as it can be overridden in tests.
82
+ *
83
+ * This method will be invoked periodically based on the token expiry date after first call to keep the token up-to-date
84
+ * (requires {@link module:cloud-services/token/token~CloudServicesTokenOptions auto refresh option} to be set).
85
+ *
86
+ * If the token refresh fails, the method will retry in 5 seconds intervals until success or the token gets
87
+ * {@link #destroy destroyed}.
88
+ */
89
+ refreshToken() {
90
+ const autoRefresh = this._options.autoRefresh;
91
+ return this._refresh().then((value) => {
92
+ this._validateTokenValue(value);
93
+ this.set("value", value);
94
+ if (autoRefresh) this._registerRefreshTokenTimeout();
95
+ return this;
96
+ }).catch((err) => {
97
+ /**
98
+ * You will see this warning when the CKEditor {@link module:cloud-services/token/token~Token token} could not be refreshed.
99
+ * This may be a result of a network error, a token endpoint (server) error, or an invalid
100
+ * {@link module:cloud-services/cloudservicesconfig~CloudServicesConfig#tokenUrl token URL configuration}.
101
+ *
102
+ * If this warning repeats, please make sure that the configuration is correct and that the token
103
+ * endpoint is up and running. {@link module:cloud-services/cloudservicesconfig~CloudServicesConfig#tokenUrl Learn more}
104
+ * about token configuration.
105
+ *
106
+ * **Note:** If the token's {@link module:cloud-services/token/token~CloudServicesTokenOptions auto refresh option}
107
+ * is enabled, attempts to refresh will be made until success or token's
108
+ * {@link module:cloud-services/token/token~Token#destroy destruction}.
109
+ *
110
+ * @error token-refresh-failed
111
+ * @param {boolean} autoRefresh Whether the token will keep auto refreshing.
112
+ */
113
+ logWarning("token-refresh-failed", { autoRefresh });
114
+ if (autoRefresh) this._registerRefreshTokenTimeout(TOKEN_FAILED_REFRESH_TIMEOUT_TIME);
115
+ throw err;
116
+ });
117
+ }
118
+ /**
119
+ * Destroys token instance. Stops refreshing.
120
+ */
121
+ destroy() {
122
+ this._isDestroyed = true;
123
+ clearTimeout(this._tokenRefreshTimeout);
124
+ }
125
+ /**
126
+ * Checks whether the provided token follows the JSON Web Tokens (JWT) format.
127
+ *
128
+ * @param tokenValue The token to validate.
129
+ */
130
+ _validateTokenValue(tokenValue) {
131
+ const isString = typeof tokenValue === "string";
132
+ const isPlainString = !/^".*"$/.test(tokenValue);
133
+ const isJWTFormat = isString && tokenValue.split(".").length === 3;
134
+ if (!(isPlainString && isJWTFormat))
135
+ /**
136
+ * The provided token must follow the [JSON Web Tokens](https://jwt.io/introduction/) format.
137
+ *
138
+ * @error token-not-in-jwt-format
139
+ */
140
+ throw new CKEditorError("token-not-in-jwt-format", this);
141
+ }
142
+ /**
143
+ * Registers a refresh token timeout for the time taken from token.
144
+ */
145
+ _registerRefreshTokenTimeout(timeoutTime) {
146
+ clearTimeout(this._tokenRefreshTimeout);
147
+ if (this._isDestroyed) return;
148
+ const tokenRefreshTimeoutTime = timeoutTime || this._getTokenRefreshTimeoutTime();
149
+ this._tokenRefreshTimeout = setTimeout(() => {
150
+ this.refreshToken();
151
+ }, tokenRefreshTimeoutTime);
152
+ }
153
+ /**
154
+ * Returns token refresh timeout time calculated from expire time in the token payload.
155
+ *
156
+ * If the token parse fails or the token payload doesn't contain, the default DEFAULT_TOKEN_REFRESH_TIMEOUT_TIME is returned.
157
+ */
158
+ _getTokenRefreshTimeoutTime() {
159
+ try {
160
+ const [, binaryTokenPayload] = this.value.split(".");
161
+ const { exp: tokenExpireTime } = JSON.parse(atob(binaryTokenPayload));
162
+ if (!tokenExpireTime) return DEFAULT_TOKEN_REFRESH_TIMEOUT_TIME;
163
+ if (tokenExpireTime > 2147483647) console.warn("Token expiration time exceeds 32-bit integer range. This might cause unpredictable token refresh timing. Token expiration time should always be provided in seconds.", { tokenExpireTime });
164
+ return Math.floor((tokenExpireTime * 1e3 - Date.now()) / 2);
165
+ } catch {
166
+ return DEFAULT_TOKEN_REFRESH_TIMEOUT_TIME;
167
+ }
168
+ }
169
+ /**
170
+ * Creates a initialized {@link module:cloud-services/token/token~Token} instance.
171
+ *
172
+ * @param tokenUrlOrRefreshToken Endpoint address to download the token or a callback that provides the token. If the
173
+ * value is a function it has to match the {@link module:cloud-services/token/token~Token#refreshToken} interface.
174
+ */
175
+ static create(tokenUrlOrRefreshToken, options = {}) {
176
+ return new Token(tokenUrlOrRefreshToken, options).init();
177
+ }
178
+ };
179
+ /**
180
+ * This function is called in a defined interval by the {@link ~Token} class. It also can be invoked manually.
181
+ * It should return a promise, which resolves with the new token value.
182
+ * If any error occurs it should return a rejected promise with an error message.
183
+ */
184
+ function defaultRefreshToken(tokenUrl) {
185
+ return new Promise((resolve, reject) => {
186
+ const xhr = new XMLHttpRequest();
187
+ xhr.open("GET", tokenUrl);
188
+ xhr.addEventListener("load", () => {
189
+ const statusCode = xhr.status;
190
+ const xhrResponse = xhr.response;
191
+ if (statusCode < 200 || statusCode > 299)
192
+ /**
193
+ * Cannot download new token from the provided url.
194
+ *
195
+ * @error token-cannot-download-new-token
196
+ */
197
+ return reject(new CKEditorError("token-cannot-download-new-token", null));
198
+ return resolve(xhrResponse);
199
+ });
200
+ xhr.addEventListener("error", () => reject(/* @__PURE__ */ new Error("Network Error")));
201
+ xhr.addEventListener("abort", () => reject(/* @__PURE__ */ new Error("Abort")));
202
+ xhr.send();
203
+ });
215
204
  }
216
205
 
206
+ /**
207
+ * @license Copyright (c) 2003-2026, CKSource Holding sp. z o.o. All rights reserved.
208
+ * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-licensing-options
209
+ */
217
210
  const BASE64_HEADER_REG_EXP = /^data:(\S*?);base64,/;
211
+ const FileUploaderBase = /* #__PURE__ */ EmitterMixin();
218
212
  /**
219
- * FileUploader class used to upload single file.
220
- */ class FileUploader extends /* #__PURE__ */ EmitterMixin() {
221
- /**
222
- * A file that is being uploaded.
223
- */ file;
224
- xhr;
225
- /**
226
- * CKEditor Cloud Services access token.
227
- */ _token;
228
- /**
229
- * CKEditor Cloud Services API address.
230
- */ _apiAddress;
231
- /**
232
- * Creates `FileUploader` instance.
233
- *
234
- * @param fileOrData A blob object or a data string encoded with Base64.
235
- * @param token Token used for authentication.
236
- * @param apiAddress API address.
237
- */ constructor(fileOrData, token, apiAddress){
238
- super();
239
- if (!fileOrData) {
240
- /**
241
- * File must be provided as the first argument.
242
- *
243
- * @error fileuploader-missing-file
244
- */ throw new CKEditorError('fileuploader-missing-file', null);
245
- }
246
- if (!token) {
247
- /**
248
- * Token must be provided as the second argument.
249
- *
250
- * @error fileuploader-missing-token
251
- */ throw new CKEditorError('fileuploader-missing-token', null);
252
- }
253
- if (!apiAddress) {
254
- /**
255
- * Api address must be provided as the third argument.
256
- *
257
- * @error fileuploader-missing-api-address
258
- */ throw new CKEditorError('fileuploader-missing-api-address', null);
259
- }
260
- this.file = _isBase64(fileOrData) ? _base64ToBlob(fileOrData) : fileOrData;
261
- this._token = token;
262
- this._apiAddress = apiAddress;
263
- }
264
- /**
265
- * Registers callback on `progress` event.
266
- */ onProgress(callback) {
267
- this.on('progress', (event, data)=>callback(data));
268
- return this;
269
- }
270
- /**
271
- * Registers callback on `error` event. Event is called once when error occurs.
272
- */ onError(callback) {
273
- this.once('error', (event, data)=>callback(data));
274
- return this;
275
- }
276
- /**
277
- * Aborts upload process.
278
- */ abort() {
279
- this.xhr.abort();
280
- }
281
- /**
282
- * Sends XHR request to API.
283
- */ send() {
284
- this._prepareRequest();
285
- this._attachXHRListeners();
286
- return this._sendRequest();
287
- }
288
- /**
289
- * Prepares XHR request.
290
- */ _prepareRequest() {
291
- const xhr = new XMLHttpRequest();
292
- xhr.open('POST', this._apiAddress);
293
- xhr.setRequestHeader('Authorization', this._token.value);
294
- xhr.responseType = 'json';
295
- this.xhr = xhr;
296
- }
297
- /**
298
- * Attaches listeners to the XHR.
299
- */ _attachXHRListeners() {
300
- const xhr = this.xhr;
301
- const onError = (message)=>{
302
- return ()=>this.fire('error', message);
303
- };
304
- xhr.addEventListener('error', onError('Network Error'));
305
- xhr.addEventListener('abort', onError('Abort'));
306
- /* istanbul ignore else -- @preserve */ if (xhr.upload) {
307
- xhr.upload.addEventListener('progress', (event)=>{
308
- if (event.lengthComputable) {
309
- this.fire('progress', {
310
- total: event.total,
311
- uploaded: event.loaded
312
- });
313
- }
314
- });
315
- }
316
- xhr.addEventListener('load', ()=>{
317
- const statusCode = xhr.status;
318
- const xhrResponse = xhr.response;
319
- if (statusCode < 200 || statusCode > 299) {
320
- return this.fire('error', xhrResponse.message || xhrResponse.error);
321
- }
322
- });
323
- }
324
- /**
325
- * Sends XHR request.
326
- */ _sendRequest() {
327
- const formData = new FormData();
328
- const xhr = this.xhr;
329
- formData.append('file', this.file);
330
- return new Promise((resolve, reject)=>{
331
- xhr.addEventListener('load', ()=>{
332
- const statusCode = xhr.status;
333
- const xhrResponse = xhr.response;
334
- if (statusCode < 200 || statusCode > 299) {
335
- if (xhrResponse.message) {
336
- /**
337
- * Uploading file failed.
338
- *
339
- * @error fileuploader-uploading-data-failed
340
- */ return reject(new CKEditorError('fileuploader-uploading-data-failed', this, {
341
- message: xhrResponse.message
342
- }));
343
- }
344
- return reject(xhrResponse.error);
345
- }
346
- return resolve(xhrResponse);
347
- });
348
- xhr.addEventListener('error', ()=>reject(new Error('Network Error')));
349
- xhr.addEventListener('abort', ()=>reject(new Error('Abort')));
350
- xhr.send(formData);
351
- });
352
- }
353
- }
213
+ * FileUploader class used to upload single file.
214
+ */
215
+ var FileUploader = class extends FileUploaderBase {
216
+ /**
217
+ * A file that is being uploaded.
218
+ */
219
+ file;
220
+ xhr;
221
+ /**
222
+ * CKEditor Cloud Services access token.
223
+ */
224
+ _token;
225
+ /**
226
+ * CKEditor Cloud Services API address.
227
+ */
228
+ _apiAddress;
229
+ /**
230
+ * Creates `FileUploader` instance.
231
+ *
232
+ * @param fileOrData A blob object or a data string encoded with Base64.
233
+ * @param token Token used for authentication.
234
+ * @param apiAddress API address.
235
+ */
236
+ constructor(fileOrData, token, apiAddress) {
237
+ super();
238
+ if (!fileOrData)
239
+ /**
240
+ * File must be provided as the first argument.
241
+ *
242
+ * @error fileuploader-missing-file
243
+ */
244
+ throw new CKEditorError("fileuploader-missing-file", null);
245
+ if (!token)
246
+ /**
247
+ * Token must be provided as the second argument.
248
+ *
249
+ * @error fileuploader-missing-token
250
+ */
251
+ throw new CKEditorError("fileuploader-missing-token", null);
252
+ if (!apiAddress)
253
+ /**
254
+ * Api address must be provided as the third argument.
255
+ *
256
+ * @error fileuploader-missing-api-address
257
+ */
258
+ throw new CKEditorError("fileuploader-missing-api-address", null);
259
+ this.file = _isBase64(fileOrData) ? _base64ToBlob(fileOrData) : fileOrData;
260
+ this._token = token;
261
+ this._apiAddress = apiAddress;
262
+ }
263
+ /**
264
+ * Registers callback on `progress` event.
265
+ */
266
+ onProgress(callback) {
267
+ this.on("progress", (event, data) => callback(data));
268
+ return this;
269
+ }
270
+ /**
271
+ * Registers callback on `error` event. Event is called once when error occurs.
272
+ */
273
+ onError(callback) {
274
+ this.once("error", (event, data) => callback(data));
275
+ return this;
276
+ }
277
+ /**
278
+ * Aborts upload process.
279
+ */
280
+ abort() {
281
+ this.xhr.abort();
282
+ }
283
+ /**
284
+ * Sends XHR request to API.
285
+ */
286
+ send() {
287
+ this._prepareRequest();
288
+ this._attachXHRListeners();
289
+ return this._sendRequest();
290
+ }
291
+ /**
292
+ * Prepares XHR request.
293
+ */
294
+ _prepareRequest() {
295
+ const xhr = new XMLHttpRequest();
296
+ xhr.open("POST", this._apiAddress);
297
+ xhr.setRequestHeader("Authorization", this._token.value);
298
+ xhr.responseType = "json";
299
+ this.xhr = xhr;
300
+ }
301
+ /**
302
+ * Attaches listeners to the XHR.
303
+ */
304
+ _attachXHRListeners() {
305
+ const xhr = this.xhr;
306
+ const onError = (message) => {
307
+ return () => this.fire("error", message);
308
+ };
309
+ xhr.addEventListener("error", onError("Network Error"));
310
+ xhr.addEventListener("abort", onError("Abort"));
311
+ /* v8 ignore else -- @preserve */
312
+ if (xhr.upload) xhr.upload.addEventListener("progress", (event) => {
313
+ if (event.lengthComputable) this.fire("progress", {
314
+ total: event.total,
315
+ uploaded: event.loaded
316
+ });
317
+ });
318
+ xhr.addEventListener("load", () => {
319
+ const statusCode = xhr.status;
320
+ const xhrResponse = xhr.response;
321
+ if (statusCode < 200 || statusCode > 299) return this.fire("error", xhrResponse.message || xhrResponse.error);
322
+ });
323
+ }
324
+ /**
325
+ * Sends XHR request.
326
+ */
327
+ _sendRequest() {
328
+ const formData = new FormData();
329
+ const xhr = this.xhr;
330
+ formData.append("file", this.file);
331
+ return new Promise((resolve, reject) => {
332
+ xhr.addEventListener("load", () => {
333
+ const statusCode = xhr.status;
334
+ const xhrResponse = xhr.response;
335
+ if (statusCode < 200 || statusCode > 299) {
336
+ if (xhrResponse.message)
337
+ /**
338
+ * Uploading file failed.
339
+ *
340
+ * @error fileuploader-uploading-data-failed
341
+ */
342
+ return reject(new CKEditorError("fileuploader-uploading-data-failed", this, { message: xhrResponse.message }));
343
+ return reject(xhrResponse.error);
344
+ }
345
+ return resolve(xhrResponse);
346
+ });
347
+ xhr.addEventListener("error", () => reject(/* @__PURE__ */ new Error("Network Error")));
348
+ xhr.addEventListener("abort", () => reject(/* @__PURE__ */ new Error("Abort")));
349
+ xhr.send(formData);
350
+ });
351
+ }
352
+ };
354
353
  /**
355
- * Transforms Base64 string data into file.
356
- *
357
- * @param base64 String data.
358
- */ function _base64ToBlob(base64, sliceSize = 512) {
359
- try {
360
- const contentType = base64.match(BASE64_HEADER_REG_EXP)[1];
361
- const base64Data = atob(base64.replace(BASE64_HEADER_REG_EXP, ''));
362
- const byteArrays = [];
363
- for(let offset = 0; offset < base64Data.length; offset += sliceSize){
364
- const slice = base64Data.slice(offset, offset + sliceSize);
365
- const byteNumbers = new Array(slice.length);
366
- for(let i = 0; i < slice.length; i++){
367
- byteNumbers[i] = slice.charCodeAt(i);
368
- }
369
- byteArrays.push(new Uint8Array(byteNumbers));
370
- }
371
- return new Blob(byteArrays, {
372
- type: contentType
373
- });
374
- } catch {
375
- /**
376
- * Problem with decoding Base64 image data.
377
- *
378
- * @error fileuploader-decoding-image-data-error
379
- */ throw new CKEditorError('fileuploader-decoding-image-data-error', null);
380
- }
354
+ * Transforms Base64 string data into file.
355
+ *
356
+ * @param base64 String data.
357
+ */
358
+ function _base64ToBlob(base64, sliceSize = 512) {
359
+ try {
360
+ const contentType = base64.match(BASE64_HEADER_REG_EXP)[1];
361
+ const base64Data = atob(base64.replace(BASE64_HEADER_REG_EXP, ""));
362
+ const byteArrays = [];
363
+ for (let offset = 0; offset < base64Data.length; offset += sliceSize) {
364
+ const slice = base64Data.slice(offset, offset + sliceSize);
365
+ const byteNumbers = new Array(slice.length);
366
+ for (let i = 0; i < slice.length; i++) byteNumbers[i] = slice.charCodeAt(i);
367
+ byteArrays.push(new Uint8Array(byteNumbers));
368
+ }
369
+ return new Blob(byteArrays, { type: contentType });
370
+ } catch {
371
+ /**
372
+ * Problem with decoding Base64 image data.
373
+ *
374
+ * @error fileuploader-decoding-image-data-error
375
+ */
376
+ throw new CKEditorError("fileuploader-decoding-image-data-error", null);
377
+ }
381
378
  }
382
379
  /**
383
- * Checks that string is Base64.
384
- */ function _isBase64(string) {
385
- if (typeof string !== 'string') {
386
- return false;
387
- }
388
- return !!string.match(BASE64_HEADER_REG_EXP)?.length;
380
+ * Checks that string is Base64.
381
+ */
382
+ function _isBase64(string) {
383
+ if (typeof string !== "string") return false;
384
+ return !!string.match(BASE64_HEADER_REG_EXP)?.length;
389
385
  }
390
386
 
391
387
  /**
392
- * UploadGateway abstracts file uploads to CKEditor Cloud Services.
393
- */ class UploadGateway {
394
- /**
395
- * CKEditor Cloud Services access token.
396
- */ _token;
397
- /**
398
- * CKEditor Cloud Services API address.
399
- */ _apiAddress;
400
- /**
401
- * Creates `UploadGateway` instance.
402
- *
403
- * @param token Token used for authentication.
404
- * @param apiAddress API address.
405
- */ constructor(token, apiAddress){
406
- if (!token) {
407
- /**
408
- * Token must be provided.
409
- *
410
- * @error uploadgateway-missing-token
411
- */ throw new CKEditorError('uploadgateway-missing-token', null);
412
- }
413
- if (!apiAddress) {
414
- /**
415
- * Api address must be provided.
416
- *
417
- * @error uploadgateway-missing-api-address
418
- */ throw new CKEditorError('uploadgateway-missing-api-address', null);
419
- }
420
- this._token = token;
421
- this._apiAddress = apiAddress;
422
- }
423
- /**
424
- * Creates a {@link module:cloud-services/uploadgateway/fileuploader~FileUploader} instance that wraps
425
- * file upload process. The file is being sent at a time when the
426
- * {@link module:cloud-services/uploadgateway/fileuploader~FileUploader#send} method is called.
427
- *
428
- * ```ts
429
- * const token = await Token.create( 'https://token-endpoint' );
430
- * new UploadGateway( token, 'https://example.org' )
431
- * .upload( 'FILE' )
432
- * .onProgress( ( data ) => console.log( data ) )
433
- * .send()
434
- * .then( ( response ) => console.log( response ) );
435
- * ```
436
- *
437
- * @param {Blob|String} fileOrData A blob object or a data string encoded with Base64.
438
- * @returns {module:cloud-services/uploadgateway/fileuploader~FileUploader} Returns `FileUploader` instance.
439
- */ upload(fileOrData) {
440
- return new FileUploader(fileOrData, this._token, this._apiAddress);
441
- }
442
- }
388
+ * @license Copyright (c) 2003-2026, CKSource Holding sp. z o.o. All rights reserved.
389
+ * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-licensing-options
390
+ */
391
+ /**
392
+ * @module cloud-services/uploadgateway/uploadgateway
393
+ */
394
+ /**
395
+ * UploadGateway abstracts file uploads to CKEditor Cloud Services.
396
+ */
397
+ var UploadGateway = class {
398
+ /**
399
+ * CKEditor Cloud Services access token.
400
+ */
401
+ _token;
402
+ /**
403
+ * CKEditor Cloud Services API address.
404
+ */
405
+ _apiAddress;
406
+ /**
407
+ * Creates `UploadGateway` instance.
408
+ *
409
+ * @param token Token used for authentication.
410
+ * @param apiAddress API address.
411
+ */
412
+ constructor(token, apiAddress) {
413
+ if (!token)
414
+ /**
415
+ * Token must be provided.
416
+ *
417
+ * @error uploadgateway-missing-token
418
+ */
419
+ throw new CKEditorError("uploadgateway-missing-token", null);
420
+ if (!apiAddress)
421
+ /**
422
+ * Api address must be provided.
423
+ *
424
+ * @error uploadgateway-missing-api-address
425
+ */
426
+ throw new CKEditorError("uploadgateway-missing-api-address", null);
427
+ this._token = token;
428
+ this._apiAddress = apiAddress;
429
+ }
430
+ /**
431
+ * Creates a {@link module:cloud-services/uploadgateway/fileuploader~FileUploader} instance that wraps
432
+ * file upload process. The file is being sent at a time when the
433
+ * {@link module:cloud-services/uploadgateway/fileuploader~FileUploader#send} method is called.
434
+ *
435
+ * ```ts
436
+ * const token = await Token.create( 'https://token-endpoint' );
437
+ * new UploadGateway( token, 'https://example.org' )
438
+ * .upload( 'FILE' )
439
+ * .onProgress( ( data ) => console.log( data ) )
440
+ * .send()
441
+ * .then( ( response ) => console.log( response ) );
442
+ * ```
443
+ *
444
+ * @param {Blob|String} fileOrData A blob object or a data string encoded with Base64.
445
+ * @returns {module:cloud-services/uploadgateway/fileuploader~FileUploader} Returns `FileUploader` instance.
446
+ */
447
+ upload(fileOrData) {
448
+ return new FileUploader(fileOrData, this._token, this._apiAddress);
449
+ }
450
+ };
443
451
 
444
452
  /**
445
- * The `CloudServicesCore` plugin exposes the base API for communication with CKEditor Cloud Services.
446
- */ class CloudServicesCore extends ContextPlugin {
447
- /**
448
- * @inheritDoc
449
- */ static get pluginName() {
450
- return 'CloudServicesCore';
451
- }
452
- /**
453
- * @inheritDoc
454
- */ static get isOfficialPlugin() {
455
- return true;
456
- }
457
- /**
458
- * Creates the {@link module:cloud-services/token/token~Token} instance.
459
- *
460
- * @param tokenUrlOrRefreshToken Endpoint address to download the token or a callback that provides the token. If the
461
- * value is a function it has to match the {@link module:cloud-services/token/token~Token#refreshToken} interface.
462
- * @param options.initValue Initial value of the token.
463
- * @param options.autoRefresh Specifies whether to start the refresh automatically.
464
- */ createToken(tokenUrlOrRefreshToken, options) {
465
- return new Token(tokenUrlOrRefreshToken, options);
466
- }
467
- /**
468
- * Creates the {@link module:cloud-services/uploadgateway/uploadgateway~UploadGateway} instance.
469
- *
470
- * @param token Token used for authentication.
471
- * @param apiAddress API address.
472
- */ createUploadGateway(token, apiAddress) {
473
- return new UploadGateway(token, apiAddress);
474
- }
475
- }
453
+ * @license Copyright (c) 2003-2026, CKSource Holding sp. z o.o. All rights reserved.
454
+ * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-licensing-options
455
+ */
456
+ /**
457
+ * @module cloud-services/cloudservicescore
458
+ */
459
+ /**
460
+ * The `CloudServicesCore` plugin exposes the base API for communication with CKEditor Cloud Services.
461
+ */
462
+ var CloudServicesCore = class extends ContextPlugin {
463
+ /**
464
+ * @inheritDoc
465
+ */
466
+ static get pluginName() {
467
+ return "CloudServicesCore";
468
+ }
469
+ /**
470
+ * @inheritDoc
471
+ */
472
+ static get isOfficialPlugin() {
473
+ return true;
474
+ }
475
+ /**
476
+ * Creates the {@link module:cloud-services/token/token~Token} instance.
477
+ *
478
+ * @param tokenUrlOrRefreshToken Endpoint address to download the token or a callback that provides the token. If the
479
+ * value is a function it has to match the {@link module:cloud-services/token/token~Token#refreshToken} interface.
480
+ * @param options.initValue Initial value of the token.
481
+ * @param options.autoRefresh Specifies whether to start the refresh automatically.
482
+ */
483
+ createToken(tokenUrlOrRefreshToken, options) {
484
+ return new Token(tokenUrlOrRefreshToken, options);
485
+ }
486
+ /**
487
+ * Creates the {@link module:cloud-services/uploadgateway/uploadgateway~UploadGateway} instance.
488
+ *
489
+ * @param token Token used for authentication.
490
+ * @param apiAddress API address.
491
+ */
492
+ createUploadGateway(token, apiAddress) {
493
+ return new UploadGateway(token, apiAddress);
494
+ }
495
+ };
476
496
 
477
497
  /**
478
- * Plugin introducing the integration between CKEditor 5 and CKEditor Cloud Services .
479
- *
480
- * It initializes the token provider based on
481
- * the {@link module:cloud-services/cloudservicesconfig~CloudServicesConfig `config.cloudService`}.
482
- */ class CloudServices extends ContextPlugin {
483
- /**
484
- * The authentication token URL for CKEditor Cloud Services or a callback to the token value promise. See the
485
- * {@link module:cloud-services/cloudservicesconfig~CloudServicesConfig#tokenUrl} for more details.
486
- */ tokenUrl;
487
- /**
488
- * The URL to which the files should be uploaded.
489
- */ uploadUrl;
490
- /**
491
- * The URL for web socket communication, used by the `RealTimeCollaborativeEditing` plugin. Every customer (organization in the CKEditor
492
- * Ecosystem dashboard) has their own, unique URLs to communicate with CKEditor Cloud Services. The URL can be found in the
493
- * CKEditor Ecosystem customer dashboard.
494
- *
495
- * Note: Unlike most plugins, `RealTimeCollaborativeEditing` is not included in any CKEditor 5 build and needs to be installed manually.
496
- * Check [Collaboration overview](https://ckeditor.com/docs/ckeditor5/latest/features/collaboration/overview.html) for more details.
497
- */ webSocketUrl;
498
- /**
499
- * An optional parameter used for integration with CKEditor Cloud Services when uploading the editor build to cloud services.
500
- *
501
- * Whenever the editor build or the configuration changes, this parameter should be set to a new, unique value to differentiate
502
- * the new bundle (build + configuration) from the old ones.
503
- */ bundleVersion;
504
- /**
505
- * Specifies whether the token should be automatically refreshed when it expires.
506
- */ autoRefresh = true;
507
- /**
508
- * Other plugins use this token for the authorization process. It handles token requesting and refreshing.
509
- * Its value is `null` when {@link module:cloud-services/cloudservicesconfig~CloudServicesConfig#tokenUrl} is not provided.
510
- *
511
- * @readonly
512
- */ token = null;
513
- /**
514
- * A map of token object instances keyed by the token URLs.
515
- */ _tokens = new Map();
516
- /**
517
- * @inheritDoc
518
- */ static get pluginName() {
519
- return 'CloudServices';
520
- }
521
- /**
522
- * @inheritDoc
523
- */ static get isOfficialPlugin() {
524
- return true;
525
- }
526
- /**
527
- * @inheritDoc
528
- */ static get requires() {
529
- return [
530
- CloudServicesCore
531
- ];
532
- }
533
- /**
534
- * @inheritDoc
535
- */ async init() {
536
- const config = this.context.config;
537
- const options = config.get('cloudServices') || {};
538
- for (const [key, value] of Object.entries(options)){
539
- this[key] = value;
540
- }
541
- if (!this.tokenUrl) {
542
- this.token = null;
543
- return;
544
- }
545
- // Initialization of the token may fail. By default, the token is being refreshed on the failure.
546
- // The problem is that if this happens here, then the token refresh interval will be executed even
547
- // after destroying the editor (as the exception was thrown from `init` method). To prevent that
548
- // behavior we need to catch the exception and destroy the uninitialized token instance.
549
- // See: https://github.com/ckeditor/ckeditor5/issues/17531
550
- const cloudServicesCore = this.context.plugins.get('CloudServicesCore');
551
- const uninitializedToken = cloudServicesCore.createToken(this.tokenUrl, {
552
- autoRefresh: this.autoRefresh
553
- });
554
- try {
555
- this.token = await uninitializedToken.init();
556
- this._tokens.set(this.tokenUrl, this.token);
557
- } catch (error) {
558
- uninitializedToken.destroy();
559
- throw error;
560
- }
561
- }
562
- /**
563
- * Registers an additional authentication token URL for CKEditor Cloud Services or a callback to the token value promise. See the
564
- * {@link module:cloud-services/cloudservicesconfig~CloudServicesConfig#tokenUrl} for more details.
565
- *
566
- * @param tokenUrl The authentication token URL for CKEditor Cloud Services or a callback to the token value promise.
567
- */ async registerTokenUrl(tokenUrl) {
568
- // Reuse the token instance in case of multiple features using the same token URL.
569
- if (this._tokens.has(tokenUrl)) {
570
- return this.getTokenFor(tokenUrl);
571
- }
572
- const cloudServicesCore = this.context.plugins.get('CloudServicesCore');
573
- const token = await cloudServicesCore.createToken(tokenUrl, {
574
- autoRefresh: this.autoRefresh
575
- }).init();
576
- this._tokens.set(tokenUrl, token);
577
- return token;
578
- }
579
- /**
580
- * Returns an authentication token provider previously registered by {@link #registerTokenUrl}.
581
- *
582
- * @param tokenUrl The authentication token URL for CKEditor Cloud Services or a callback to the token value promise.
583
- */ getTokenFor(tokenUrl) {
584
- const token = this._tokens.get(tokenUrl);
585
- if (!token) {
586
- /**
587
- * The provided `tokenUrl` was not registered by {@link module:cloud-services/cloudservices~CloudServices#registerTokenUrl}.
588
- *
589
- * @error cloudservices-token-not-registered
590
- */ throw new CKEditorError('cloudservices-token-not-registered', this);
591
- }
592
- return token;
593
- }
594
- /**
595
- * @inheritDoc
596
- */ destroy() {
597
- super.destroy();
598
- for (const token of this._tokens.values()){
599
- token.destroy();
600
- }
601
- }
602
- }
498
+ * @license Copyright (c) 2003-2026, CKSource Holding sp. z o.o. All rights reserved.
499
+ * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-licensing-options
500
+ */
501
+ /**
502
+ * @module cloud-services/cloudservices
503
+ */
504
+ /**
505
+ * Plugin introducing the integration between CKEditor 5 and CKEditor Cloud Services .
506
+ *
507
+ * It initializes the token provider based on
508
+ * the {@link module:cloud-services/cloudservicesconfig~CloudServicesConfig `config.cloudService`}.
509
+ */
510
+ var CloudServices = class extends ContextPlugin {
511
+ /**
512
+ * The authentication token URL for CKEditor Cloud Services or a callback to the token value promise. See the
513
+ * {@link module:cloud-services/cloudservicesconfig~CloudServicesConfig#tokenUrl} for more details.
514
+ */
515
+ tokenUrl;
516
+ /**
517
+ * The URL to which the files should be uploaded.
518
+ */
519
+ uploadUrl;
520
+ /**
521
+ * The URL for web socket communication, used by the `RealTimeCollaborativeEditing` plugin. Every customer (organization in the CKEditor
522
+ * Ecosystem dashboard) has their own, unique URLs to communicate with CKEditor Cloud Services. The URL can be found in the
523
+ * CKEditor Ecosystem customer dashboard.
524
+ *
525
+ * Note: Unlike most plugins, `RealTimeCollaborativeEditing` is not included in any CKEditor 5 build and needs to be installed manually.
526
+ * Check [Collaboration overview](https://ckeditor.com/docs/ckeditor5/latest/features/collaboration/overview.html) for more details.
527
+ */
528
+ webSocketUrl;
529
+ /**
530
+ * An optional parameter used for integration with CKEditor Cloud Services when uploading the editor build to cloud services.
531
+ *
532
+ * Whenever the editor build or the configuration changes, this parameter should be set to a new, unique value to differentiate
533
+ * the new bundle (build + configuration) from the old ones.
534
+ */
535
+ bundleVersion;
536
+ /**
537
+ * Specifies whether the token should be automatically refreshed when it expires.
538
+ */
539
+ autoRefresh = true;
540
+ /**
541
+ * Other plugins use this token for the authorization process. It handles token requesting and refreshing.
542
+ * Its value is `null` when {@link module:cloud-services/cloudservicesconfig~CloudServicesConfig#tokenUrl} is not provided.
543
+ *
544
+ * @readonly
545
+ */
546
+ token = null;
547
+ /**
548
+ * A map of token object instances keyed by the token URLs.
549
+ */
550
+ _tokens = /* @__PURE__ */ new Map();
551
+ /**
552
+ * @inheritDoc
553
+ */
554
+ static get pluginName() {
555
+ return "CloudServices";
556
+ }
557
+ /**
558
+ * @inheritDoc
559
+ */
560
+ static get isOfficialPlugin() {
561
+ return true;
562
+ }
563
+ /**
564
+ * @inheritDoc
565
+ */
566
+ static get requires() {
567
+ return [CloudServicesCore];
568
+ }
569
+ /**
570
+ * @inheritDoc
571
+ */
572
+ async init() {
573
+ const options = this.context.config.get("cloudServices") || {};
574
+ for (const [key, value] of Object.entries(options)) this[key] = value;
575
+ if (!this.tokenUrl) {
576
+ this.token = null;
577
+ return;
578
+ }
579
+ const uninitializedToken = this.context.plugins.get("CloudServicesCore").createToken(this.tokenUrl, { autoRefresh: this.autoRefresh });
580
+ try {
581
+ this.token = await uninitializedToken.init();
582
+ this._tokens.set(this.tokenUrl, this.token);
583
+ } catch (error) {
584
+ uninitializedToken.destroy();
585
+ throw error;
586
+ }
587
+ }
588
+ /**
589
+ * Registers an additional authentication token URL for CKEditor Cloud Services or a callback to the token value promise. See the
590
+ * {@link module:cloud-services/cloudservicesconfig~CloudServicesConfig#tokenUrl} for more details.
591
+ *
592
+ * @param tokenUrl The authentication token URL for CKEditor Cloud Services or a callback to the token value promise.
593
+ */
594
+ async registerTokenUrl(tokenUrl) {
595
+ if (this._tokens.has(tokenUrl)) return this.getTokenFor(tokenUrl);
596
+ const token = await this.context.plugins.get("CloudServicesCore").createToken(tokenUrl, { autoRefresh: this.autoRefresh }).init();
597
+ this._tokens.set(tokenUrl, token);
598
+ return token;
599
+ }
600
+ /**
601
+ * Returns an authentication token provider previously registered by {@link #registerTokenUrl}.
602
+ *
603
+ * @param tokenUrl The authentication token URL for CKEditor Cloud Services or a callback to the token value promise.
604
+ */
605
+ getTokenFor(tokenUrl) {
606
+ const token = this._tokens.get(tokenUrl);
607
+ if (!token)
608
+ /**
609
+ * The provided `tokenUrl` was not registered by {@link module:cloud-services/cloudservices~CloudServices#registerTokenUrl}.
610
+ *
611
+ * @error cloudservices-token-not-registered
612
+ */
613
+ throw new CKEditorError("cloudservices-token-not-registered", this);
614
+ return token;
615
+ }
616
+ /**
617
+ * @inheritDoc
618
+ */
619
+ destroy() {
620
+ super.destroy();
621
+ for (const token of this._tokens.values()) token.destroy();
622
+ }
623
+ };
624
+
625
+ /**
626
+ * @license Copyright (c) 2003-2026, CKSource Holding sp. z o.o. All rights reserved.
627
+ * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-licensing-options
628
+ */
603
629
 
604
630
  export { CloudServices, CloudServicesCore, FileUploader, Token, UploadGateway };
605
- //# sourceMappingURL=index.js.map
631
+ //# sourceMappingURL=index.js.map