@atlaskit/collab-provider 11.0.6 → 11.0.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +6 -0
- package/afm-cc/tsconfig.json +64 -58
- package/afm-jira/tsconfig.json +7 -1
- package/build/tsconfig.json +8 -2
- package/dist/cjs/version-wrapper.js +1 -1
- package/dist/es2019/version-wrapper.js +1 -1
- package/dist/esm/version-wrapper.js +1 -1
- package/docs/document-service-send-function-analysis.md +211 -127
- package/package.json +5 -5
package/CHANGELOG.md
CHANGED
package/afm-cc/tsconfig.json
CHANGED
|
@@ -1,60 +1,66 @@
|
|
|
1
1
|
{
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
2
|
+
"extends": "../../../../tsconfig.entry-points.confluence.json",
|
|
3
|
+
"compilerOptions": {
|
|
4
|
+
"declaration": true,
|
|
5
|
+
"target": "es5",
|
|
6
|
+
"composite": true,
|
|
7
|
+
"outDir": "../../../../../confluence/tsDist/@atlaskit__collab-provider",
|
|
8
|
+
"rootDir": "../"
|
|
9
|
+
},
|
|
10
|
+
"include": [
|
|
11
|
+
"../src/**/*.ts",
|
|
12
|
+
"../src/**/*.tsx"
|
|
13
|
+
],
|
|
14
|
+
"exclude": [
|
|
15
|
+
"../src/**/__tests__/*",
|
|
16
|
+
"../src/**/*.test.*",
|
|
17
|
+
"../src/**/test.*",
|
|
18
|
+
"../src/**/examples.*",
|
|
19
|
+
"../src/**/examples/*",
|
|
20
|
+
"../src/**/examples/**/*",
|
|
21
|
+
"../src/**/*.stories.*",
|
|
22
|
+
"../src/**/stories/*",
|
|
23
|
+
"../src/**/stories/**/*"
|
|
24
|
+
],
|
|
25
|
+
"references": [
|
|
26
|
+
{
|
|
27
|
+
"path": "../../adf-utils/afm-cc/tsconfig.json"
|
|
28
|
+
},
|
|
29
|
+
{
|
|
30
|
+
"path": "../../../analytics/analytics-gas-types/afm-cc/tsconfig.json"
|
|
31
|
+
},
|
|
32
|
+
{
|
|
33
|
+
"path": "../../../analytics/analytics-listeners/afm-cc/tsconfig.json"
|
|
34
|
+
},
|
|
35
|
+
{
|
|
36
|
+
"path": "../../../elements/anonymous-assets/afm-cc/tsconfig.json"
|
|
37
|
+
},
|
|
38
|
+
{
|
|
39
|
+
"path": "../../editor-json-transformer/afm-cc/tsconfig.json"
|
|
40
|
+
},
|
|
41
|
+
{
|
|
42
|
+
"path": "../../../measurement/feature-gate-js-client/afm-cc/tsconfig.json"
|
|
43
|
+
},
|
|
44
|
+
{
|
|
45
|
+
"path": "../../../platform/feature-flags/afm-cc/tsconfig.json"
|
|
46
|
+
},
|
|
47
|
+
{
|
|
48
|
+
"path": "../../prosemirror-collab/afm-cc/tsconfig.json"
|
|
49
|
+
},
|
|
50
|
+
{
|
|
51
|
+
"path": "../../../react-ufo/atlaskit/afm-cc/tsconfig.json"
|
|
52
|
+
},
|
|
53
|
+
{
|
|
54
|
+
"path": "../../tmp-editor-statsig/afm-cc/tsconfig.json"
|
|
55
|
+
},
|
|
56
|
+
{
|
|
57
|
+
"path": "../../../data/ufo-external/afm-cc/tsconfig.json"
|
|
58
|
+
},
|
|
59
|
+
{
|
|
60
|
+
"path": "../../../elements/util-service-support/afm-cc/tsconfig.json"
|
|
61
|
+
},
|
|
62
|
+
{
|
|
63
|
+
"path": "../../editor-common/afm-cc/tsconfig.json"
|
|
64
|
+
}
|
|
65
|
+
]
|
|
60
66
|
}
|
package/afm-jira/tsconfig.json
CHANGED
|
@@ -14,7 +14,13 @@
|
|
|
14
14
|
"exclude": [
|
|
15
15
|
"../src/**/__tests__/*",
|
|
16
16
|
"../src/**/*.test.*",
|
|
17
|
-
"../src/**/test.*"
|
|
17
|
+
"../src/**/test.*",
|
|
18
|
+
"../src/**/examples.*",
|
|
19
|
+
"../src/**/examples/*",
|
|
20
|
+
"../src/**/examples/**/*",
|
|
21
|
+
"../src/**/*.stories.*",
|
|
22
|
+
"../src/**/stories/*",
|
|
23
|
+
"../src/**/stories/**/*"
|
|
18
24
|
],
|
|
19
25
|
"references": [
|
|
20
26
|
{
|
package/build/tsconfig.json
CHANGED
|
@@ -12,6 +12,12 @@
|
|
|
12
12
|
"exclude": [
|
|
13
13
|
"../src/**/__tests__/*",
|
|
14
14
|
"../src/**/*.test.*",
|
|
15
|
-
"../src/**/test.*"
|
|
15
|
+
"../src/**/test.*",
|
|
16
|
+
"../src/**/examples.*",
|
|
17
|
+
"../src/**/examples/*",
|
|
18
|
+
"../src/**/examples/**/*",
|
|
19
|
+
"../src/**/*.stories.*",
|
|
20
|
+
"../src/**/stories/*",
|
|
21
|
+
"../src/**/stories/**/*"
|
|
16
22
|
]
|
|
17
|
-
}
|
|
23
|
+
}
|
|
@@ -5,7 +5,7 @@ Object.defineProperty(exports, "__esModule", {
|
|
|
5
5
|
});
|
|
6
6
|
exports.version = exports.nextMajorVersion = exports.name = void 0;
|
|
7
7
|
var name = exports.name = "@atlaskit/collab-provider";
|
|
8
|
-
var version = exports.version = "11.0.
|
|
8
|
+
var version = exports.version = "11.0.6";
|
|
9
9
|
var nextMajorVersion = exports.nextMajorVersion = function nextMajorVersion() {
|
|
10
10
|
return [Number(version.split('.')[0]) + 1, 0, 0].join('.');
|
|
11
11
|
};
|
|
@@ -2,45 +2,50 @@
|
|
|
2
2
|
|
|
3
3
|
## Overview
|
|
4
4
|
|
|
5
|
-
The `send` function in `src/document/document-service.ts` is a critical component of the
|
|
5
|
+
The `send` function in `src/document/document-service.ts` is a critical component of the
|
|
6
|
+
collaborative editing system that handles the transmission of document changes (steps) from the
|
|
7
|
+
local editor to other participants. This document provides a deep dive into how the function works,
|
|
8
|
+
with particular focus on how unconfirmed steps are stored and retrieved, and how the commit queue
|
|
9
|
+
manages step transmission.
|
|
6
10
|
|
|
7
11
|
## Main Workflow
|
|
8
12
|
|
|
9
|
-
The `send` function orchestrates the process of sending ProseMirror steps to the collaboration
|
|
13
|
+
The `send` function orchestrates the process of sending ProseMirror steps to the collaboration
|
|
14
|
+
service. Here's the detailed workflow showing the integration with `@atlaskit/prosemirror-collab`:
|
|
10
15
|
|
|
11
16
|
```mermaid
|
|
12
17
|
flowchart TD
|
|
13
18
|
A[send function called] --> B{Check offline editing/single player mode}
|
|
14
19
|
B -->|Offline or single player merging| C[Return early - don't send]
|
|
15
20
|
B -->|Online and ready| D["Call sendableSteps(newState)<br/>@atlaskit/prosemirror-collab"]
|
|
16
|
-
|
|
21
|
+
|
|
17
22
|
D --> D1["ProseMirror Collab Plugin<br/>extracts unconfirmed steps<br/>from plugin state"]
|
|
18
23
|
D1 --> E{unconfirmedStepsData exists?}
|
|
19
24
|
E -->|No| F[Return early]
|
|
20
25
|
E -->|Yes| G["Call getCollabState(newState)<br/>@atlaskit/prosemirror-collab<br/>to get version"]
|
|
21
|
-
|
|
26
|
+
|
|
22
27
|
G --> G1["ProseMirror Collab Plugin<br/>returns {version, unconfirmed}"]
|
|
23
28
|
G1 --> H{Lock steps if feature enabled}
|
|
24
29
|
H --> I[Extract unconfirmed steps from data]
|
|
25
|
-
|
|
30
|
+
|
|
26
31
|
I --> J{Send analytics event?}
|
|
27
32
|
J -->|Yes| K[Send HAS_UNCONFIRMED_STEPS analytics]
|
|
28
33
|
J -->|No| L{Any unconfirmed steps?}
|
|
29
34
|
K --> L
|
|
30
|
-
|
|
35
|
+
|
|
31
36
|
L -->|No steps| M[Return early]
|
|
32
37
|
L -->|Has steps| N{Check for rebased data}
|
|
33
|
-
|
|
38
|
+
|
|
34
39
|
N -->|Has rebased data| O[Send STEPS_REBASED analytics]
|
|
35
40
|
N -->|No rebased data| P{Check offline steps timeout}
|
|
36
|
-
|
|
41
|
+
|
|
37
42
|
O --> P
|
|
38
43
|
P -->|Has offline steps & timeout not exceeded| Q[Start/continue timeout timer]
|
|
39
44
|
P -->|No offline steps or timeout exceeded| R[Call commitStepService.commitStepQueue]
|
|
40
|
-
|
|
45
|
+
|
|
41
46
|
Q --> S[Return - wait for timeout]
|
|
42
47
|
R --> T[Steps sent to collaboration service]
|
|
43
|
-
|
|
48
|
+
|
|
44
49
|
style D fill:#e1f5fe
|
|
45
50
|
style D1 fill:#e1f5fe
|
|
46
51
|
style G fill:#e1f5fe
|
|
@@ -52,24 +57,32 @@ flowchart TD
|
|
|
52
57
|
|
|
53
58
|
## @atlaskit/prosemirror-collab Integration
|
|
54
59
|
|
|
55
|
-
The `@atlaskit/prosemirror-collab` package is the core component that manages collaborative editing
|
|
60
|
+
The `@atlaskit/prosemirror-collab` package is the core component that manages collaborative editing
|
|
61
|
+
state within ProseMirror. It provides the fundamental infrastructure for tracking unconfirmed steps
|
|
62
|
+
and handling collaborative operations.
|
|
56
63
|
|
|
57
64
|
### Key Functions from @atlaskit/prosemirror-collab
|
|
58
65
|
|
|
59
66
|
#### 1. `sendableSteps(state: EditorState)`
|
|
67
|
+
|
|
60
68
|
- **Purpose**: Extracts steps that are ready to be sent to the collaboration server
|
|
61
69
|
- **Returns**: `{ steps: Step[], origins: Transaction[] } | null`
|
|
62
70
|
- **Usage**: Primary method used by the send function to get unconfirmed steps
|
|
63
|
-
- **Internal Logic**: Accesses the collab plugin state and returns pending steps with their original
|
|
71
|
+
- **Internal Logic**: Accesses the collab plugin state and returns pending steps with their original
|
|
72
|
+
transactions
|
|
64
73
|
|
|
65
74
|
#### 2. `getCollabState(state: EditorState)`
|
|
75
|
+
|
|
66
76
|
- **Purpose**: Direct access to the collaboration plugin's internal state
|
|
67
77
|
- **Returns**: `{ version: number, unconfirmed: Step[], ... } | null`
|
|
68
78
|
- **Usage**: Used to get version information and direct access to unconfirmed steps
|
|
69
|
-
- **Internal State**: Contains version tracking, unconfirmed steps array, and other collaboration
|
|
79
|
+
- **Internal State**: Contains version tracking, unconfirmed steps array, and other collaboration
|
|
80
|
+
metadata
|
|
70
81
|
|
|
71
82
|
#### 3. Plugin State Management
|
|
83
|
+
|
|
72
84
|
The collab plugin maintains its state within the ProseMirror editor state:
|
|
85
|
+
|
|
73
86
|
```typescript
|
|
74
87
|
// Plugin state structure (conceptual)
|
|
75
88
|
{
|
|
@@ -82,9 +95,11 @@ The collab plugin maintains its state within the ProseMirror editor state:
|
|
|
82
95
|
|
|
83
96
|
### Integration Points with Document Service
|
|
84
97
|
|
|
85
|
-
The Document Service relies heavily on `@atlaskit/prosemirror-collab` for all collaboration-related
|
|
98
|
+
The Document Service relies heavily on `@atlaskit/prosemirror-collab` for all collaboration-related
|
|
99
|
+
operations:
|
|
86
100
|
|
|
87
101
|
#### 1. **Step Extraction** (Primary Integration)
|
|
102
|
+
|
|
88
103
|
```typescript
|
|
89
104
|
// In send() function
|
|
90
105
|
const unconfirmedStepsData = sendableSteps(newState);
|
|
@@ -94,6 +109,7 @@ const { steps, origins } = unconfirmedStepsData;
|
|
|
94
109
|
```
|
|
95
110
|
|
|
96
111
|
#### 2. **Version Management**
|
|
112
|
+
|
|
97
113
|
```typescript
|
|
98
114
|
// Getting current version for synchronization
|
|
99
115
|
private getVersionFromCollabState(state: EditorState, resource: string) {
|
|
@@ -103,87 +119,110 @@ private getVersionFromCollabState(state: EditorState, resource: string) {
|
|
|
103
119
|
```
|
|
104
120
|
|
|
105
121
|
#### 3. **Conflict Detection** (Reconnection Scenarios)
|
|
122
|
+
|
|
106
123
|
```typescript
|
|
107
124
|
// Checking for unconfirmed steps during reconnection
|
|
108
125
|
const unconfirmedSteps = state ? getCollabState(state)?.unconfirmed : undefined;
|
|
109
126
|
if (steps.length > 0 && unconfirmedSteps && unconfirmedSteps.length > 0) {
|
|
110
|
-
|
|
127
|
+
// Handle potential conflicts
|
|
111
128
|
}
|
|
112
129
|
```
|
|
113
130
|
|
|
114
131
|
#### 4. **Step Acknowledgment Flow**
|
|
132
|
+
|
|
115
133
|
When steps are successfully sent and acknowledged:
|
|
134
|
+
|
|
116
135
|
- Document Service receives confirmation via `onStepsAdded()`
|
|
117
136
|
- Emits 'data' event to ProseMirror via `providerEmitCallback`
|
|
118
|
-
- `@atlaskit/prosemirror-collab` processes the event and removes confirmed steps from unconfirmed
|
|
137
|
+
- `@atlaskit/prosemirror-collab` processes the event and removes confirmed steps from unconfirmed
|
|
138
|
+
array
|
|
119
139
|
|
|
120
140
|
#### 5. **Error Recovery Integration**
|
|
121
|
-
|
|
122
|
-
- **
|
|
123
|
-
|
|
141
|
+
|
|
142
|
+
- **Catchup Operations**: When version conflicts occur, the collab plugin state is updated with new
|
|
143
|
+
document versions
|
|
144
|
+
- **Step Rebasing**: The plugin handles automatic rebasing of unconfirmed steps against incoming
|
|
145
|
+
remote steps
|
|
146
|
+
- **State Restoration**: During document recovery, the plugin state is reset while preserving
|
|
147
|
+
unconfirmed steps for reconciliation
|
|
124
148
|
|
|
125
149
|
## Unconfirmed Steps Storage and Retrieval
|
|
126
150
|
|
|
127
151
|
### How Unconfirmed Steps are Stored
|
|
128
152
|
|
|
129
|
-
Unconfirmed steps are stored within the ProseMirror editor state using the
|
|
153
|
+
Unconfirmed steps are stored within the ProseMirror editor state using the
|
|
154
|
+
`@atlaskit/prosemirror-collab` plugin. The storage mechanism works as follows:
|
|
130
155
|
|
|
131
|
-
1. **Plugin State Integration**: Unconfirmed steps are stored as part of the ProseMirror collab
|
|
156
|
+
1. **Plugin State Integration**: Unconfirmed steps are stored as part of the ProseMirror collab
|
|
157
|
+
plugin state, which is embedded within the main editor state as a plugin-specific state slice.
|
|
132
158
|
|
|
133
|
-
2. **Step Origins Tracking**: Each step maintains a reference to its original transaction
|
|
159
|
+
2. **Step Origins Tracking**: Each step maintains a reference to its original transaction
|
|
160
|
+
(`origins`) to track the source of changes even after rebasing operations.
|
|
134
161
|
|
|
135
162
|
3. **Automatic Management**: The collab plugin automatically manages the unconfirmed steps array:
|
|
136
|
-
|
|
137
|
-
- **
|
|
163
|
+
|
|
164
|
+
- **Adding Steps**: When transactions are applied locally, new steps are added to the unconfirmed
|
|
165
|
+
array
|
|
166
|
+
- **Removing Steps**: When acknowledgments are received from the server, corresponding steps are
|
|
167
|
+
removed
|
|
138
168
|
- **Rebasing**: When remote steps are received, local unconfirmed steps are rebased against them
|
|
139
169
|
|
|
140
|
-
4. **Version Synchronization**: The plugin tracks the document version to ensure proper ordering and
|
|
170
|
+
4. **Version Synchronization**: The plugin tracks the document version to ensure proper ordering and
|
|
171
|
+
conflict resolution.
|
|
141
172
|
|
|
142
173
|
### How Unconfirmed Steps are Retrieved
|
|
143
174
|
|
|
144
175
|
The retrieval process involves several key functions:
|
|
145
176
|
|
|
146
177
|
#### 1. `sendableSteps(state)` - Primary Retrieval Function
|
|
178
|
+
|
|
147
179
|
```typescript
|
|
148
180
|
const unconfirmedStepsData = sendableSteps(newState);
|
|
149
181
|
```
|
|
182
|
+
|
|
150
183
|
- **Source**: `@atlaskit/prosemirror-collab` package
|
|
151
184
|
- **Returns**: Object containing `{ steps: ProseMirrorStep[], origins: Transaction[] }`
|
|
152
185
|
- **Purpose**: Gets steps that are ready to be sent to the server
|
|
153
186
|
|
|
154
187
|
#### 2. `getUnconfirmedSteps()` - Service Method
|
|
188
|
+
|
|
155
189
|
```typescript
|
|
156
190
|
getUnconfirmedSteps = (): readonly ProseMirrorStep[] | undefined => {
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
191
|
+
const state = this.getState?.();
|
|
192
|
+
if (!state) {
|
|
193
|
+
// Error handling
|
|
194
|
+
return;
|
|
195
|
+
}
|
|
196
|
+
return sendableSteps(state)?.steps;
|
|
163
197
|
};
|
|
164
198
|
```
|
|
199
|
+
|
|
165
200
|
- **Purpose**: Public method to access unconfirmed steps
|
|
166
201
|
- **Error Handling**: Includes analytics for missing state scenarios
|
|
167
202
|
|
|
168
203
|
#### 3. `getUnconfirmedStepsOrigins()` - Transaction Origins
|
|
204
|
+
|
|
169
205
|
```typescript
|
|
170
206
|
getUnconfirmedStepsOrigins = () => {
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
207
|
+
const state = this.getState?.();
|
|
208
|
+
if (!state) {
|
|
209
|
+
// Error handling
|
|
210
|
+
return;
|
|
211
|
+
}
|
|
212
|
+
return sendableSteps(state)?.origins;
|
|
177
213
|
};
|
|
178
214
|
```
|
|
215
|
+
|
|
179
216
|
- **Purpose**: Gets original transactions for tracking during rebasing
|
|
180
217
|
- **Use Case**: Used in `commitUnconfirmedSteps` to track completion
|
|
181
218
|
|
|
182
219
|
#### 4. `getCollabState(state)` - Low-level Access
|
|
220
|
+
|
|
183
221
|
```typescript
|
|
184
222
|
const collabState = getCollabState(state);
|
|
185
223
|
const unconfirmedSteps = state ? getCollabState(state)?.unconfirmed : undefined;
|
|
186
224
|
```
|
|
225
|
+
|
|
187
226
|
- **Purpose**: Direct access to collab plugin state
|
|
188
227
|
- **Contains**: Version info, unconfirmed steps, and other collab metadata
|
|
189
228
|
|
|
@@ -199,30 +238,30 @@ sequenceDiagram
|
|
|
199
238
|
participant DocService as Document Service
|
|
200
239
|
participant CommitService as Commit Step Service
|
|
201
240
|
participant Server as Collaboration Server
|
|
202
|
-
|
|
241
|
+
|
|
203
242
|
User->>Editor: Make edit (type, delete, etc.)
|
|
204
243
|
Editor->>Editor: Create transaction with steps
|
|
205
244
|
Editor->>CollabPlugin: Apply transaction
|
|
206
245
|
CollabPlugin->>CollabPlugin: Add steps to unconfirmed array<br/>Update version tracking
|
|
207
|
-
|
|
246
|
+
|
|
208
247
|
Note over Editor,DocService: send() function workflow
|
|
209
248
|
Editor->>DocService: send() called with new state
|
|
210
249
|
DocService->>CollabPlugin: sendableSteps(newState)
|
|
211
250
|
CollabPlugin->>CollabPlugin: Extract unconfirmed steps<br/>and origins from plugin state
|
|
212
251
|
CollabPlugin-->>DocService: {steps: Step[], origins: Transaction[]}
|
|
213
|
-
|
|
252
|
+
|
|
214
253
|
DocService->>CollabPlugin: getCollabState(newState)
|
|
215
254
|
CollabPlugin-->>DocService: {version, unconfirmed, ...}
|
|
216
|
-
|
|
255
|
+
|
|
217
256
|
DocService->>DocService: Process steps (lock, validate, etc.)
|
|
218
257
|
DocService->>CommitService: commitStepQueue(steps, version, ...)
|
|
219
258
|
CommitService->>Server: Broadcast steps with metadata
|
|
220
|
-
|
|
259
|
+
|
|
221
260
|
Server-->>CommitService: Acknowledgment (success/error)
|
|
222
261
|
CommitService->>DocService: onStepsAdded(confirmedSteps)
|
|
223
262
|
DocService->>CollabPlugin: Apply confirmed steps via 'data' event
|
|
224
263
|
CollabPlugin->>CollabPlugin: Remove confirmed steps<br/>from unconfirmed array
|
|
225
|
-
|
|
264
|
+
|
|
226
265
|
Note over CollabPlugin: Steps now confirmed,<br/>removed from unconfirmed state
|
|
227
266
|
```
|
|
228
267
|
|
|
@@ -232,17 +271,18 @@ For offline editing and single-player step merging, steps are locked to prevent
|
|
|
232
271
|
|
|
233
272
|
```typescript
|
|
234
273
|
lockSteps = (stepOrigins?: readonly Transaction[]) => {
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
274
|
+
origins?.forEach((origin) => {
|
|
275
|
+
if (origin instanceof Transaction) {
|
|
276
|
+
return origin.setMeta('mergeIsLocked', true);
|
|
277
|
+
}
|
|
278
|
+
});
|
|
240
279
|
};
|
|
241
280
|
```
|
|
242
281
|
|
|
243
282
|
### 3. Commit Step Service Integration
|
|
244
283
|
|
|
245
|
-
The `CommitStepService` handles the actual transmission and implements sophisticated queue
|
|
284
|
+
The `CommitStepService` handles the actual transmission and implements sophisticated queue
|
|
285
|
+
management to prevent overwhelming the server and ensure proper step ordering.
|
|
246
286
|
|
|
247
287
|
#### CommitStepQueue Workflow
|
|
248
288
|
|
|
@@ -251,27 +291,27 @@ flowchart TD
|
|
|
251
291
|
A[commitStepQueue called] --> B{readyToCommit flag?}
|
|
252
292
|
B -->|false| C[Skip - not ready to commit]
|
|
253
293
|
B -->|true| D[Set readyToCommit = false<br/>Set lastBroadcastRequestAcked = false]
|
|
254
|
-
|
|
294
|
+
|
|
255
295
|
D --> E[Start fallback timer<br/>5000ms timeout]
|
|
256
296
|
E --> F[Process steps<br/>Add clientId, userId metadata]
|
|
257
297
|
F --> G[Broadcast 'steps:commit' to server]
|
|
258
|
-
|
|
298
|
+
|
|
259
299
|
G --> H[Server processes steps]
|
|
260
300
|
H --> I[Server sends acknowledgment]
|
|
261
301
|
I --> J{Ack type?}
|
|
262
|
-
|
|
302
|
+
|
|
263
303
|
J -->|SUCCESS| K[Set lastBroadcastRequestAcked = true<br/>Calculate delay]
|
|
264
304
|
J -->|ERROR| L[Handle error<br/>Set lastBroadcastRequestAcked = true]
|
|
265
|
-
|
|
305
|
+
|
|
266
306
|
K --> M{Delay calculation}
|
|
267
307
|
M --> N[Start commit wait timer<br/>with calculated delay]
|
|
268
308
|
N --> O[Timer expires:<br/>Set readyToCommit = true]
|
|
269
|
-
|
|
309
|
+
|
|
270
310
|
L --> P[Immediate error handling<br/>Set readyToCommit = true]
|
|
271
|
-
|
|
311
|
+
|
|
272
312
|
E --> Q[Fallback timer expires<br/>5000ms later]
|
|
273
313
|
Q --> R[Force readyToCommit = true<br/>lastBroadcastRequestAcked = true]
|
|
274
|
-
|
|
314
|
+
|
|
275
315
|
style D fill:#ffebee
|
|
276
316
|
style K fill:#e8f5e8
|
|
277
317
|
style L fill:#fff3e0
|
|
@@ -282,6 +322,7 @@ flowchart TD
|
|
|
282
322
|
#### Queue Control Mechanisms
|
|
283
323
|
|
|
284
324
|
##### 1. **readyToCommit Flag**
|
|
325
|
+
|
|
285
326
|
```typescript
|
|
286
327
|
// In CommitStepService constructor
|
|
287
328
|
this.readyToCommit = true;
|
|
@@ -289,8 +330,8 @@ this.lastBroadcastRequestAcked = true;
|
|
|
289
330
|
|
|
290
331
|
// In commitStepQueue
|
|
291
332
|
if (!this.readyToCommit) {
|
|
292
|
-
|
|
293
|
-
|
|
333
|
+
logger('Not ready to commit, skip');
|
|
334
|
+
return;
|
|
294
335
|
}
|
|
295
336
|
// Block other sending requests before ACK
|
|
296
337
|
this.readyToCommit = false;
|
|
@@ -298,37 +339,41 @@ this.lastBroadcastRequestAcked = false;
|
|
|
298
339
|
```
|
|
299
340
|
|
|
300
341
|
**Purpose**: Prevents concurrent step submissions and ensures proper ordering
|
|
342
|
+
|
|
301
343
|
- **Initial State**: `true` (ready to send)
|
|
302
344
|
- **During Transmission**: `false` (blocks new sends)
|
|
303
345
|
- **After ACK**: Reset to `true` after server-calculated delay
|
|
304
346
|
|
|
305
347
|
##### 2. **Server-Side Delay Management**
|
|
348
|
+
|
|
306
349
|
```typescript
|
|
307
350
|
// In acknowledgment callback
|
|
308
351
|
let delay = latency < 680 ? 680 - latency : 1;
|
|
309
352
|
if (response.delay) {
|
|
310
|
-
|
|
353
|
+
delay = response.delay; // Server-provided backpressure delay
|
|
311
354
|
}
|
|
312
355
|
|
|
313
356
|
commitWaitTimer = setTimeout(() => {
|
|
314
|
-
|
|
315
|
-
|
|
357
|
+
this.readyToCommit = true;
|
|
358
|
+
logger('reset readyToCommit');
|
|
316
359
|
}, delay);
|
|
317
360
|
```
|
|
318
361
|
|
|
319
362
|
**Delay Calculation Logic**:
|
|
363
|
+
|
|
320
364
|
- **Minimum Delay**: 680ms (500ms hardcoded + ~180ms network delay)
|
|
321
365
|
- **Dynamic Adjustment**: If latency < 680ms, delay = 680 - latency
|
|
322
366
|
- **Server Override**: Server can provide custom delay for backpressure control
|
|
323
367
|
- **Fallback**: Minimum 1ms delay if latency is high
|
|
324
368
|
|
|
325
369
|
##### 3. **Fallback Timer Protection**
|
|
370
|
+
|
|
326
371
|
```typescript
|
|
327
372
|
// Fallback timer to prevent indefinite blocking
|
|
328
373
|
const fallbackTimer = setTimeout(() => {
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
374
|
+
this.readyToCommit = true;
|
|
375
|
+
this.lastBroadcastRequestAcked = true;
|
|
376
|
+
logger('reset readyToCommit by timer');
|
|
332
377
|
}, RESET_READYTOCOMMIT_INTERVAL_MS); // 5000ms
|
|
333
378
|
|
|
334
379
|
// Clear fallback timer when ACK received
|
|
@@ -336,6 +381,7 @@ clearTimeout(fallbackTimer);
|
|
|
336
381
|
```
|
|
337
382
|
|
|
338
383
|
**Purpose**: Prevents queue from being permanently blocked if ACK is lost
|
|
384
|
+
|
|
339
385
|
- **Timeout**: 5 seconds
|
|
340
386
|
- **Action**: Force reset both flags to allow new transmissions
|
|
341
387
|
- **Cleanup**: Cleared when proper ACK is received
|
|
@@ -344,22 +390,23 @@ clearTimeout(fallbackTimer);
|
|
|
344
390
|
|
|
345
391
|
```typescript
|
|
346
392
|
this.commitStepService.commitStepQueue({
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
393
|
+
userId: this.getUserId()!,
|
|
394
|
+
clientId: this.clientId!,
|
|
395
|
+
steps: unconfirmedSteps,
|
|
396
|
+
version,
|
|
397
|
+
onStepsAdded: this.onStepsAdded,
|
|
398
|
+
__livePage: this.options.__livePage,
|
|
399
|
+
hasRecovered: this.hasRecovered,
|
|
400
|
+
collabMode: this.participantsService.getCollabMode(),
|
|
401
|
+
reason,
|
|
402
|
+
numberOfStepCommitsSent: this.numberOfStepCommitsSent,
|
|
403
|
+
setNumberOfCommitsSent: this.setNumberOfCommitsSent,
|
|
404
|
+
lockSteps: this.lockSteps,
|
|
359
405
|
});
|
|
360
406
|
```
|
|
361
407
|
|
|
362
408
|
**Step Enhancement Process**:
|
|
409
|
+
|
|
363
410
|
1. **Metadata Addition**: Each step gets `clientId` and `userId`
|
|
364
411
|
2. **Live Page Processing**: Special handling for expand/collapse changes
|
|
365
412
|
3. **Recovery Tagging**: Steps marked if sent after page recovery
|
|
@@ -367,7 +414,9 @@ this.commitStepService.commitStepQueue({
|
|
|
367
414
|
|
|
368
415
|
## Queue Management and Step Ordering Integration
|
|
369
416
|
|
|
370
|
-
This section details how `CommitStepService` and `@atlaskit/prosemirror-collab` work together to
|
|
417
|
+
This section details how `CommitStepService` and `@atlaskit/prosemirror-collab` work together to
|
|
418
|
+
ensure steps are sent to the server in the correct order, preventing race conditions and maintaining
|
|
419
|
+
document consistency.
|
|
371
420
|
|
|
372
421
|
### Step Ordering Sequence Diagram
|
|
373
422
|
|
|
@@ -379,9 +428,9 @@ sequenceDiagram
|
|
|
379
428
|
participant DocService as Document Service
|
|
380
429
|
participant CommitService as Commit Step Service
|
|
381
430
|
participant Server as Collaboration Server
|
|
382
|
-
|
|
431
|
+
|
|
383
432
|
Note over User,Server: Multiple rapid edits scenario
|
|
384
|
-
|
|
433
|
+
|
|
385
434
|
User->>Editor: Edit 1 (type "A")
|
|
386
435
|
Editor->>CollabPlugin: Apply transaction T1
|
|
387
436
|
CollabPlugin->>CollabPlugin: Add step S1 to unconfirmed<br/>version: 10 → 11
|
|
@@ -389,11 +438,11 @@ sequenceDiagram
|
|
|
389
438
|
DocService->>CollabPlugin: sendableSteps(state)
|
|
390
439
|
CollabPlugin-->>DocService: {steps: [S1], origins: [T1]}
|
|
391
440
|
DocService->>CommitService: commitStepQueue(S1, v11)
|
|
392
|
-
|
|
441
|
+
|
|
393
442
|
Note over CommitService: Check readyToCommit = true
|
|
394
443
|
CommitService->>CommitService: Set readyToCommit = false<br/>Set lastBroadcastRequestAcked = false
|
|
395
444
|
CommitService->>Server: Broadcast S1 (version 11)
|
|
396
|
-
|
|
445
|
+
|
|
397
446
|
User->>Editor: Edit 2 (type "B") - RAPID
|
|
398
447
|
Editor->>CollabPlugin: Apply transaction T2
|
|
399
448
|
CollabPlugin->>CollabPlugin: Add step S2 to unconfirmed<br/>version: 11 → 12
|
|
@@ -401,10 +450,10 @@ sequenceDiagram
|
|
|
401
450
|
DocService->>CollabPlugin: sendableSteps(state)
|
|
402
451
|
CollabPlugin-->>DocService: {steps: [S1, S2], origins: [T1, T2]}
|
|
403
452
|
DocService->>CommitService: commitStepQueue([S1, S2], v12)
|
|
404
|
-
|
|
453
|
+
|
|
405
454
|
Note over CommitService: Check readyToCommit = false<br/>SKIP - Not ready to commit
|
|
406
455
|
CommitService-->>DocService: Return early (no send)
|
|
407
|
-
|
|
456
|
+
|
|
408
457
|
User->>Editor: Edit 3 (type "C") - RAPID
|
|
409
458
|
Editor->>CollabPlugin: Apply transaction T3
|
|
410
459
|
CollabPlugin->>CollabPlugin: Add step S3 to unconfirmed<br/>version: 12 → 13
|
|
@@ -412,53 +461,54 @@ sequenceDiagram
|
|
|
412
461
|
DocService->>CollabPlugin: sendableSteps(state)
|
|
413
462
|
CollabPlugin-->>DocService: {steps: [S1, S2, S3], origins: [T1, T2, T3]}
|
|
414
463
|
DocService->>CommitService: commitStepQueue([S1, S2, S3], v13)
|
|
415
|
-
|
|
464
|
+
|
|
416
465
|
Note over CommitService: Check readyToCommit = false<br/>SKIP - Still not ready
|
|
417
466
|
CommitService-->>DocService: Return early (no send)
|
|
418
|
-
|
|
467
|
+
|
|
419
468
|
Note over Server: Server processes S1
|
|
420
469
|
Server-->>CommitService: ACK SUCCESS (S1 confirmed, new version 11)
|
|
421
470
|
CommitService->>CommitService: Set lastBroadcastRequestAcked = true<br/>Calculate delay (680ms)
|
|
422
471
|
CommitService->>DocService: onStepsAdded([S1], v11)
|
|
423
472
|
DocService->>CollabPlugin: Emit 'data' event with S1
|
|
424
473
|
CollabPlugin->>CollabPlugin: Remove S1 from unconfirmed<br/>Now unconfirmed: [S2, S3]
|
|
425
|
-
|
|
474
|
+
|
|
426
475
|
Note over CommitService: Start delay timer (680ms)
|
|
427
|
-
|
|
476
|
+
|
|
428
477
|
rect rgb(255, 248, 220)
|
|
429
478
|
Note over CommitService: 680ms delay period<br/>readyToCommit remains false
|
|
430
479
|
end
|
|
431
|
-
|
|
480
|
+
|
|
432
481
|
CommitService->>CommitService: Timer expires<br/>Set readyToCommit = true
|
|
433
|
-
|
|
482
|
+
|
|
434
483
|
Note over DocService: Next send() call will now succeed
|
|
435
484
|
User->>Editor: Edit 4 (type "D") or automatic retry
|
|
436
485
|
Editor->>DocService: send() called
|
|
437
486
|
DocService->>CollabPlugin: sendableSteps(state)
|
|
438
487
|
CollabPlugin-->>DocService: {steps: [S2, S3], origins: [T2, T3]}
|
|
439
488
|
DocService->>CommitService: commitStepQueue([S2, S3], v13)
|
|
440
|
-
|
|
489
|
+
|
|
441
490
|
Note over CommitService: Check readyToCommit = true ✓
|
|
442
491
|
CommitService->>CommitService: Set readyToCommit = false<br/>Set lastBroadcastRequestAcked = false
|
|
443
492
|
CommitService->>Server: Broadcast [S2, S3] (version 13)
|
|
444
|
-
|
|
493
|
+
|
|
445
494
|
Server-->>CommitService: ACK SUCCESS ([S2, S3] confirmed, new version 13)
|
|
446
495
|
CommitService->>CommitService: Set lastBroadcastRequestAcked = true<br/>Calculate delay
|
|
447
496
|
CommitService->>DocService: onStepsAdded([S2, S3], v13)
|
|
448
497
|
DocService->>CollabPlugin: Emit 'data' event with [S2, S3]
|
|
449
498
|
CollabPlugin->>CollabPlugin: Remove S2, S3 from unconfirmed<br/>Now unconfirmed: []
|
|
450
|
-
|
|
499
|
+
|
|
451
500
|
Note over CollabPlugin,Server: All steps confirmed and ordered correctly:<br/>S1 (v11) → [S2, S3] (v13)
|
|
452
501
|
```
|
|
453
502
|
|
|
454
503
|
### Key Ordering Mechanisms
|
|
455
504
|
|
|
456
505
|
#### 1. **Unconfirmed Steps Accumulation**
|
|
506
|
+
|
|
457
507
|
```typescript
|
|
458
508
|
// @atlaskit/prosemirror-collab behavior
|
|
459
509
|
// When rapid edits occur:
|
|
460
510
|
// Edit 1: unconfirmed = [S1]
|
|
461
|
-
// Edit 2: unconfirmed = [S1, S2]
|
|
511
|
+
// Edit 2: unconfirmed = [S1, S2]
|
|
462
512
|
// Edit 3: unconfirmed = [S1, S2, S3]
|
|
463
513
|
|
|
464
514
|
// sendableSteps() always returns ALL unconfirmed steps
|
|
@@ -467,47 +517,54 @@ const unconfirmedStepsData = sendableSteps(newState);
|
|
|
467
517
|
```
|
|
468
518
|
|
|
469
519
|
#### 2. **Queue Blocking Mechanism**
|
|
520
|
+
|
|
470
521
|
```typescript
|
|
471
522
|
// CommitStepService prevents concurrent sends
|
|
472
523
|
if (!this.readyToCommit) {
|
|
473
|
-
|
|
474
|
-
|
|
524
|
+
logger('Not ready to commit, skip');
|
|
525
|
+
return; // Prevents out-of-order transmission
|
|
475
526
|
}
|
|
476
527
|
```
|
|
477
528
|
|
|
478
529
|
#### 3. **Batching Behavior**
|
|
530
|
+
|
|
479
531
|
When the queue becomes ready again, multiple accumulated steps are sent together:
|
|
532
|
+
|
|
480
533
|
- **Prevents**: Individual steps being sent out of order
|
|
481
534
|
- **Ensures**: Steps are transmitted in batches that maintain sequence
|
|
482
535
|
- **Benefits**: Reduces server load and network overhead
|
|
483
536
|
|
|
484
537
|
#### 4. **Version Consistency**
|
|
538
|
+
|
|
485
539
|
```typescript
|
|
486
540
|
// Each batch maintains version consistency
|
|
487
541
|
commitStepQueue({
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
542
|
+
steps: [S2, S3], // Accumulated unconfirmed steps
|
|
543
|
+
version: 13, // Expected final version after these steps
|
|
544
|
+
// ...
|
|
491
545
|
});
|
|
492
546
|
```
|
|
493
547
|
|
|
494
548
|
### Race Condition Prevention
|
|
495
549
|
|
|
496
550
|
#### **Scenario: Rapid User Input**
|
|
551
|
+
|
|
497
552
|
1. **User types quickly**: "ABCD"
|
|
498
553
|
2. **Without queue management**: Could send S1, S2, S3, S4 concurrently
|
|
499
|
-
3. **With queue management**:
|
|
554
|
+
3. **With queue management**:
|
|
500
555
|
- Send S1, block queue
|
|
501
556
|
- Accumulate S2, S3, S4 in unconfirmed array
|
|
502
557
|
- After S1 ACK + delay, send [S2, S3, S4] as batch
|
|
503
558
|
|
|
504
559
|
#### **Scenario: Network Delays**
|
|
560
|
+
|
|
505
561
|
1. **S1 sent but ACK delayed**
|
|
506
562
|
2. **User continues editing**: S2, S3 accumulate
|
|
507
563
|
3. **Fallback timer (5s)**: Prevents permanent blocking
|
|
508
564
|
4. **When ACK arrives**: Normal flow resumes with accumulated steps
|
|
509
565
|
|
|
510
566
|
#### **Scenario: Server Backpressure**
|
|
567
|
+
|
|
511
568
|
1. **Server returns custom delay**: `response.delay = 2000ms`
|
|
512
569
|
2. **Queue respects server timing**: Waits 2000ms before next send
|
|
513
570
|
3. **Prevents server overload**: While maintaining step order
|
|
@@ -515,22 +572,25 @@ commitStepQueue({
|
|
|
515
572
|
### Error Recovery and Ordering
|
|
516
573
|
|
|
517
574
|
#### **Step Rejection Handling**
|
|
575
|
+
|
|
518
576
|
```typescript
|
|
519
577
|
// When steps are rejected due to version conflicts
|
|
520
578
|
onStepRejectedError = () => {
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
579
|
+
this.stepRejectCounter++;
|
|
580
|
+
if (this.stepRejectCounter >= maxRetries) {
|
|
581
|
+
// Trigger catchup to resync with server
|
|
582
|
+
this.throttledCatchupv2(CatchupEventReason.STEPS_REJECTED);
|
|
583
|
+
} else {
|
|
584
|
+
// Retry with accumulated steps after delay
|
|
585
|
+
setTimeout(() => this.sendStepsFromCurrentState(), 1000);
|
|
586
|
+
}
|
|
529
587
|
};
|
|
530
588
|
```
|
|
531
589
|
|
|
532
590
|
#### **Catchup and Reordering**
|
|
591
|
+
|
|
533
592
|
During catchup operations:
|
|
593
|
+
|
|
534
594
|
1. **Server provides latest state**: Document and version
|
|
535
595
|
2. **Collab plugin rebases unconfirmed steps**: Against server state
|
|
536
596
|
3. **Queue resumes**: With properly rebased and reordered steps
|
|
@@ -548,16 +608,19 @@ During catchup operations:
|
|
|
548
608
|
## Error Handling and Recovery
|
|
549
609
|
|
|
550
610
|
### 1. Step Rejection Handling
|
|
611
|
+
|
|
551
612
|
- **Counter**: `stepRejectCounter` tracks consecutive rejections
|
|
552
613
|
- **Threshold**: After `MAX_STEP_REJECTED_ERROR` rejections, triggers catchup
|
|
553
614
|
- **Recovery**: `onStepRejectedError()` manages retry logic
|
|
554
615
|
|
|
555
616
|
### 2. Catchup Mechanism
|
|
617
|
+
|
|
556
618
|
- **Trigger**: When steps are rejected or version conflicts occur
|
|
557
619
|
- **Process**: `throttledCatchupv2()` synchronizes with server state
|
|
558
620
|
- **Queue Management**: Steps are queued during catchup process
|
|
559
621
|
|
|
560
622
|
### 3. Offline Step Handling
|
|
623
|
+
|
|
561
624
|
- **Timeout**: 6-second timer for offline steps
|
|
562
625
|
- **Metadata**: Steps marked with `isOffline` metadata
|
|
563
626
|
- **Recovery**: Automatic retry when connection restored
|
|
@@ -565,16 +628,20 @@ During catchup operations:
|
|
|
565
628
|
## Performance Considerations
|
|
566
629
|
|
|
567
630
|
### 1. Throttling
|
|
631
|
+
|
|
568
632
|
- **Catchup**: Throttled to 1 second intervals to prevent spam
|
|
569
633
|
- **Analytics**: 10% sampling for step analytics to reduce overhead
|
|
570
634
|
|
|
571
635
|
### 2. Queue Management and Batching
|
|
572
|
-
|
|
636
|
+
|
|
637
|
+
- **Ready to Commit**: `readyToCommit` flag prevents concurrent sends and ensures proper step
|
|
638
|
+
ordering
|
|
573
639
|
- **Delay Management**: Server-controlled delays (minimum 680ms) for backpressure and rate limiting
|
|
574
640
|
- **Fallback Protection**: 5-second timeout prevents permanent queue blocking if ACKs are lost
|
|
575
641
|
- **Publish Optimization**: Special handling for publish operations to skip delays when possible
|
|
576
642
|
|
|
577
643
|
### 3. Memory Management
|
|
644
|
+
|
|
578
645
|
- **Step Queue**: Ordered queue for out-of-sequence steps
|
|
579
646
|
- **Cleanup**: Automatic removal of confirmed steps from unconfirmed array
|
|
580
647
|
|
|
@@ -582,24 +649,28 @@ During catchup operations:
|
|
|
582
649
|
|
|
583
650
|
The send function and commit queue behavior is controlled by several feature flags:
|
|
584
651
|
|
|
585
|
-
1. **`platform_editor_offline_editing_web`**: Enables offline editing support with step timeout
|
|
652
|
+
1. **`platform_editor_offline_editing_web`**: Enables offline editing support with step timeout
|
|
653
|
+
handling
|
|
586
654
|
2. **`platform_editor_enable_single_player_step_merging`**: Enables step merging for single users
|
|
587
|
-
3. **`skip_collab_provider_delay_on_publish`**: Allows bypassing commit delays during publish
|
|
588
|
-
|
|
655
|
+
3. **`skip_collab_provider_delay_on_publish`**: Allows bypassing commit delays during publish
|
|
656
|
+
operations
|
|
657
|
+
4. **`platform_editor_step_validation_on_connect`**: Controls step validation for first N steps
|
|
658
|
+
after connection
|
|
589
659
|
|
|
590
660
|
### Publish Operation Optimization
|
|
591
661
|
|
|
592
662
|
```typescript
|
|
593
663
|
// Special handling for publish operations
|
|
594
664
|
if (reason === 'publish' && this.lastBroadcastRequestAcked) {
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
665
|
+
if (fg('skip_collab_provider_delay_on_publish')) {
|
|
666
|
+
clearTimeout(commitWaitTimer);
|
|
667
|
+
this.readyToCommit = true;
|
|
668
|
+
}
|
|
599
669
|
}
|
|
600
670
|
```
|
|
601
671
|
|
|
602
|
-
This optimization allows publish operations to bypass the normal delay mechanism when the previous
|
|
672
|
+
This optimization allows publish operations to bypass the normal delay mechanism when the previous
|
|
673
|
+
request has been acknowledged, reducing publish latency.
|
|
603
674
|
|
|
604
675
|
## Analytics and Monitoring
|
|
605
676
|
|
|
@@ -611,9 +682,11 @@ The function emits several analytics events:
|
|
|
611
682
|
|
|
612
683
|
## Summary
|
|
613
684
|
|
|
614
|
-
The `send` function is a sophisticated orchestrator that works in tight integration with
|
|
685
|
+
The `send` function is a sophisticated orchestrator that works in tight integration with
|
|
686
|
+
`@atlaskit/prosemirror-collab`:
|
|
615
687
|
|
|
616
|
-
1. **Retrieves** unconfirmed steps from `@atlaskit/prosemirror-collab` plugin state using
|
|
688
|
+
1. **Retrieves** unconfirmed steps from `@atlaskit/prosemirror-collab` plugin state using
|
|
689
|
+
`sendableSteps()` and `getCollabState()`
|
|
617
690
|
2. **Validates** conditions for sending (online status, feature flags, connection state)
|
|
618
691
|
3. **Manages** step locking and metadata for various scenarios (offline editing, single-player mode)
|
|
619
692
|
4. **Coordinates** with the commit service for actual transmission to the collaboration server
|
|
@@ -624,18 +697,29 @@ The `send` function is a sophisticated orchestrator that works in tight integrat
|
|
|
624
697
|
|
|
625
698
|
The tight integration with `@atlaskit/prosemirror-collab` provides:
|
|
626
699
|
|
|
627
|
-
- **Automatic State Management**: The collab plugin handles the complex task of maintaining
|
|
628
|
-
|
|
629
|
-
- **
|
|
630
|
-
|
|
700
|
+
- **Automatic State Management**: The collab plugin handles the complex task of maintaining
|
|
701
|
+
unconfirmed steps, version tracking, and step rebasing
|
|
702
|
+
- **Conflict Resolution**: Built-in support for handling concurrent edits and step conflicts through
|
|
703
|
+
rebasing algorithms
|
|
704
|
+
- **Transaction Tracking**: Origins tracking allows the system to monitor step completion even after
|
|
705
|
+
rebasing operations
|
|
706
|
+
- **Robust Recovery**: The plugin state serves as the source of truth for document synchronization
|
|
707
|
+
during error recovery scenarios
|
|
631
708
|
|
|
632
709
|
### Queue Management Benefits
|
|
633
710
|
|
|
634
711
|
The `CommitStepService` queue management provides:
|
|
635
712
|
|
|
636
713
|
- **Ordered Transmission**: The `readyToCommit` flag ensures steps are sent in proper sequence
|
|
637
|
-
- **Backpressure Control**: Server-side delay management prevents overwhelming the collaboration
|
|
638
|
-
|
|
639
|
-
- **
|
|
640
|
-
|
|
641
|
-
|
|
714
|
+
- **Backpressure Control**: Server-side delay management prevents overwhelming the collaboration
|
|
715
|
+
service
|
|
716
|
+
- **Resilient Operation**: Fallback timers and error handling ensure the queue never gets
|
|
717
|
+
permanently stuck
|
|
718
|
+
- **Performance Optimization**: Special handling for publish operations and feature flag controls
|
|
719
|
+
for different scenarios
|
|
720
|
+
|
|
721
|
+
The unconfirmed steps storage leverages the battle-tested `@atlaskit/prosemirror-collab` plugin,
|
|
722
|
+
providing a robust foundation for tracking pending changes while maintaining consistency with the
|
|
723
|
+
collaborative editing protocol. This architecture ensures that the Document Service can focus on
|
|
724
|
+
orchestration and error handling while delegating the complex state management to the specialized
|
|
725
|
+
collaboration plugin.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@atlaskit/collab-provider",
|
|
3
|
-
"version": "11.0.
|
|
3
|
+
"version": "11.0.7",
|
|
4
4
|
"description": "A provider for collaborative editing.",
|
|
5
5
|
"publishConfig": {
|
|
6
6
|
"registry": "https://registry.npmjs.org/"
|
|
@@ -35,14 +35,14 @@
|
|
|
35
35
|
"@atlaskit/adf-utils": "^19.20.0",
|
|
36
36
|
"@atlaskit/analytics-gas-types": "^5.1.0",
|
|
37
37
|
"@atlaskit/analytics-listeners": "^9.0.0",
|
|
38
|
-
"@atlaskit/anonymous-assets": "^0.0.
|
|
39
|
-
"@atlaskit/editor-json-transformer": "^8.
|
|
38
|
+
"@atlaskit/anonymous-assets": "^0.0.3",
|
|
39
|
+
"@atlaskit/editor-json-transformer": "^8.25.0",
|
|
40
40
|
"@atlaskit/editor-prosemirror": "7.0.0",
|
|
41
41
|
"@atlaskit/feature-gate-js-client": "^5.5.0",
|
|
42
42
|
"@atlaskit/platform-feature-flags": "^1.1.0",
|
|
43
43
|
"@atlaskit/prosemirror-collab": "^0.17.0",
|
|
44
44
|
"@atlaskit/react-ufo": "^4.1.0",
|
|
45
|
-
"@atlaskit/tmp-editor-statsig": "^9.
|
|
45
|
+
"@atlaskit/tmp-editor-statsig": "^9.22.0",
|
|
46
46
|
"@atlaskit/ufo": "^0.4.0",
|
|
47
47
|
"@atlaskit/util-service-support": "^6.3.0",
|
|
48
48
|
"@babel/runtime": "^7.0.0",
|
|
@@ -90,6 +90,6 @@
|
|
|
90
90
|
}
|
|
91
91
|
},
|
|
92
92
|
"peerDependencies": {
|
|
93
|
-
"@atlaskit/editor-common": "^107.
|
|
93
|
+
"@atlaskit/editor-common": "^107.16.0"
|
|
94
94
|
}
|
|
95
95
|
}
|