# Editor bundle

<a id="overview">

## Overview

An editor bundle is the source code of a pre-configured and ready-to-use CKEditor 5, which is minified and built as a single file. To use the [Server-side Editor API](server-side-editor-api.md), [document storage](document-storage.md), [import and export](import-and-export.md), or [connection optimization](connection-optimization.md) features, you need to upload an exact copy of the editor file from your application to the CKEditor Cloud Services server.

CKEditor Cloud Services uses your editor bundle to convert operations in the collaboration session into data. Thanks to this, CKEditor Cloud Services can ensure data consistency between users and the stored data. Furthermore, if your editor bundle is using some custom plugins that can output some custom data, it will be then properly handled by CKEditor Cloud Services.

Whenever you change something in your editor bundle or its configuration, you should update it on the CKEditor Cloud Services server, too. If the users use different versions of the editor than the one uploaded to the server, it may lead to data loss or even editor crashes.

<a id="features-that-require-the-editor-bundle">

## Features that require the editor bundle

| Feature group                    | Requires editor bundle                                                                |
| -------------------------------- | ------------------------------------------------------------------------------------- |
| Collaboration Import / Export    | Yes                                                                                   |
| Documents Import / Export / Copy | Yes                                                                                   |
| Document Storage                 | Yes                                                                                   |
| Revision History                 | No                                                                                    |
| Server-side Editor API           | Yes                                                                                   |
| Connection optimization          | Yes                                                                                   |
| Real-Time Collaboration          | No                                                                                    |
| Comments                         | No                                                                                    |
| Track Changes (Suggestions)      | No                                                                                    |
| Users                            | No                                                                                    |
| Webhooks                         | No (but webhook events emitted by bundle-requiring features inherit that requirement) |

<a id="building-the-editor-bundle">

## Building the editor bundle

First, you need to create an editor bundle. You can follow our step-by-step [editor bundling guide](../../examples/editor-bundle/advanced-editor-bundle-nodejs.md) or go with ready-to-use [editor bundle examples](../../examples/editor-bundle/editor-bundle-nodejs.md).

<a id="uploading-the-editor-bundle">

## Uploading the editor bundle

If you have already built your editor that meets the [requirements](#building-the-editor-bundle), you can upload it to CKEditor Cloud Services together with the editor configuration. The editor upload process requires the use of the [`POST /editors`](https://help.cke-cs.com/api/v5/docs#tag/Editors/paths/~1editors/post) method from the Cloud Services REST API.

Every request made to the CKEditor Cloud Services REST API needs to have additional headers: `X-CS-Timestamp` and `X-CS-Signature`. Refer to the [request signature](../../developer-resources/security/request-signature.md) guide for more details.

Three parameters are passed to the body of this method:

* `bundle` – The code of your minified editor passed as a string.
* `config` – This is the editor configuration that will be used by the editor in CKEditor Cloud Services. This should be the same config as used by the editor in your application, especially when it can alter the data output.
* `test_data` – This is the real document content generated by the editor you upload to the CKEditor Cloud Services server. It helps to detect if any of your custom plugins modify the document content incorrectly or if an invalid configuration has been passed. This parameter is optional but it is highly recommended to use it. You should make sure that your test data has been created using all the custom plugins you have implemented.

The body of the upload request should look like this:

```json
{
	"bundle": "the content of editor bundle file",
	"config": {
		"cloudServices": {
			"bundleVersion": "unique_editor_bundle_version"
		},
		"removePlugins": [ "myCustomAutoSavePlugin" ],
		... other config options
	},
	"test_data": "<p>example output from your editor</p>"
}
```

The `bundleVersion` property inside the `config.cloudServices` object is required.

> **Note**
>
> Remember that the uploaded editor bundle is only available within the environment to which it was assigned. If you use the same editor for different environments, you must upload it separately for each of them.

<a id="editor-configuration">

### Editor configuration

The `bundleVersion` property set during an upload acts as a unique build identifier. It tells CKEditor Cloud Services which editor should be used when a user starts the collaboration session. You need to make sure that the editor configuration in your application also has the [`bundleVersion`](../../../../ckeditor5/latest/api/module_cloud-services_cloudservicesconfig-CloudServicesConfig.md#member-bundleVersion) property and its value matches the editor uploaded to the CKEditor Cloud Services server. An example editor configuration with set `bundleVersion` looks like this:

```js
return CKEditorCS.create( {
	cloudServices: {
		tokenUrl: 'TOKEN_URL',
		uploadUrl: 'UPLOAD_URL',
		webSocketUrl: 'WEBSOCKET_URL',
		bundleVersion: 'editor-1.0.0'
	},
	collaboration: {
		channelId: 'CHANNEL_ID'
	},
	toolbar: [
		// Toolbar items
	],
	// Other config options
} );
```

> **Note**
>
> The editor configuration uploaded to the CKEditor Cloud Services server should be in `JSON` format. Therefore, in some cases, the config object needs to be modified before sending:
>
> * properties of particular plugins that require DOM elements should be set to `null`,
> * regex values in [General HTML Support](../../../../ckeditor5/latest/features/html/general-html-support.md) configuration should be converted to strings, e.g. `"/.*/"`.

<a id="multi-root-editor-builds">

### Multi-root editor builds

For [**multi-root** editor builds](../../../../ckeditor5/latest/examples/builds/multi-root-editor.md), an upload request body requires an additional `is_multi_root_editor` field set to `true`. The field indicates that the uploaded editor is a multi-root build. Additionally, the `test_data` property is expected to be a stringified JSON structure where the keys are the root names and the values are the HTML content for particular roots. Moreover, if you want to use the roots’ attributes you must provide example values for each root in the editor config.

```json
{
	"bundle": "the content of a multi-root editor bundle file",
	"config": {
		"cloudServices": {
			"bundleVersion": "unique_multiroot_editor_bundle_version"
		},
		"removePlugins": [
			"myCustomAutoSavePlugin"
		],
		"rootsAttributes": {
			"main": {
			"order": 1
			}
		},
		... other config options
	},
	"test_data": "{\"header\":\"<p>Multi-root document header.</p>\",\"footer\":\"<p>Multi-root document footer.</p>\"}",
	"is_multi_root_editor": true
}
```

<a id="example">

### Example

Follow this example to learn how to properly upload the editor bundle to CKEditor Cloud Services. This example is using Node.js and `npm`, hence it is required to have these tools installed. It also assumes that you have already [built the editor bundle](#building-the-editor-bundle). This example will continue using the files from the previous example.

1. In your terminal, use `cd` to get into the extracted folder and install the `axios` package with the `npm i axios` command.

2. Create a new `upload.js` file inside the extracted folder with the following content:

```js
const crypto = require( 'crypto' );
const fs = require( 'fs' );
const path = require( 'path' );
const axios = require( 'axios' );

// Update with your credentials and application endpoint
const environmentId = 'txQ9sTfqmXUyWU5LmDbr';
const apiSecret = '4zZBCQoPfRZ7Rr7TEnGAuRsGgbfF58Eg0PA8xcLD2kvPhjGjy4VGgB8k0hXn';
const applicationEndpoint = 'https://33333.cke-cs.com';

const apiEndpoint = `${ applicationEndpoint }/api/v5/${ environmentId }/editors/`;
const editorBundle = fs.readFileSync( path.resolve( './dist/editor.bundle.js' ) );

const body = {
	bundle: editorBundle.toString(),
	config: {
		cloudServices: {
			bundleVersion: 'editor-1.0.0' // Set a unique name for the uploaded bundle
		},
		toolbar: [
			// Toolbar items
		],
		// Other config options
	}
};

const CSTimestamp = Date.now();
const axiosConfig = {
	headers: {
		'X-CS-Timestamp': CSTimestamp,
		'X-CS-Signature': generateSignature( apiSecret, 'POST', apiEndpoint, CSTimestamp, body )
	}
};

axios.post( apiEndpoint, body, axiosConfig )
	.then( response => {
		console.log ( response.status );
	} ).catch( error => {
		console.log( error.message );
		console.log( error.response.data );
	} );

function generateSignature( apiSecret, method, uri, timestamp, body ) {
	const url = new URL( uri );
	const path = url.pathname + url.search;
	const hmac = crypto.createHmac( 'SHA256', apiSecret );

	hmac.update( `${ method.toUpperCase() }${ path }${ timestamp }` );

	if ( body ) {
		hmac.update( Buffer.from( JSON.stringify( body ) ) );
	}

	return hmac.digest( 'hex' );
}
```

Run the above code with the `node upload.js` command.

You should see the `201` status code in your console. You can run `node upload.js` once again to make sure that the bundle is already uploaded. In this case, you will get a 409 error with message `Editor editor-1.0.0 already exists`.

<a id="updating-the-editor-bundle">

## Updating the editor bundle

As shown in the example above, it is not possible to overwrite an editor bundle on the CKEditor Cloud Services server. You may, however, encounter situations where you need to rebuild your editor, for example when you add a new plugin, update CKEditor 5 version or fix a bug in your custom plugin. You should follow these steps to start using a new editor bundle:

1. Rebuild your editor with the necessary changes to the configuration or plugins.
2. Upload the new editor bundle with the `bundleVersion` value being new and unique for your environment.
3. Update the [`bundleVersion`](../../../../ckeditor5/latest/api/module_cloud-services_cloudservicesconfig-CloudServicesConfig.md#member-bundleVersion) in the editor configuration in your application to match the new value from step 2.
4. Wait for the active collaboration sessions [to be removed automatically](data.md#temporary-and-permanent-data) or remove them manually using the [`DELETE /collaborations/{document_id}`](https://help.cke-cs.com/api/v5/docs#tag/Collaboration/paths/~1collaborations~1%7Bdocument_id%7D/delete) from the CKEditor Cloud Services REST API.
5. Open the document in your new editor.

A collaboration session initiated with certain `bundleVersion` can be opened only by editors with the same `bundleVersion` value. Because of this you can not connect to the previously edited documents with the new editor, without reinitializing such collaboration session (steps 4 and 5).

> **Note**
>
> To ensure data consistency you should update the editor bundle on CKEditor Cloud Services every time there is a change in your editor. This is especially important when the changes are related to the conversions or the data outputs.

---

Full index of the Cloud Services documentation: [llms.txt](../../../llms.txt)
