@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 CHANGED
@@ -1,5 +1,11 @@
1
1
  # @atlaskit/collab-provider
2
2
 
3
+ ## 11.0.7
4
+
5
+ ### Patch Changes
6
+
7
+ - Updated dependencies
8
+
3
9
  ## 11.0.6
4
10
 
5
11
  ### Patch Changes
@@ -1,60 +1,66 @@
1
1
  {
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
- ],
19
- "references": [
20
- {
21
- "path": "../../adf-utils/afm-cc/tsconfig.json"
22
- },
23
- {
24
- "path": "../../../analytics/analytics-gas-types/afm-cc/tsconfig.json"
25
- },
26
- {
27
- "path": "../../../analytics/analytics-listeners/afm-cc/tsconfig.json"
28
- },
29
- {
30
- "path": "../../../elements/anonymous-assets/afm-cc/tsconfig.json"
31
- },
32
- {
33
- "path": "../../editor-json-transformer/afm-cc/tsconfig.json"
34
- },
35
- {
36
- "path": "../../../measurement/feature-gate-js-client/afm-cc/tsconfig.json"
37
- },
38
- {
39
- "path": "../../../platform/feature-flags/afm-cc/tsconfig.json"
40
- },
41
- {
42
- "path": "../../prosemirror-collab/afm-cc/tsconfig.json"
43
- },
44
- {
45
- "path": "../../../react-ufo/atlaskit/afm-cc/tsconfig.json"
46
- },
47
- {
48
- "path": "../../tmp-editor-statsig/afm-cc/tsconfig.json"
49
- },
50
- {
51
- "path": "../../../data/ufo-external/afm-cc/tsconfig.json"
52
- },
53
- {
54
- "path": "../../../elements/util-service-support/afm-cc/tsconfig.json"
55
- },
56
- {
57
- "path": "../../editor-common/afm-cc/tsconfig.json"
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
  }
@@ -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
  {
@@ -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.5";
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
  };
@@ -1,5 +1,5 @@
1
1
  export const name = "@atlaskit/collab-provider";
2
- export const version = "11.0.5";
2
+ export const version = "11.0.6";
3
3
  export const nextMajorVersion = () => {
4
4
  return [Number(version.split('.')[0]) + 1, 0, 0].join('.');
5
5
  };
@@ -1,5 +1,5 @@
1
1
  export var name = "@atlaskit/collab-provider";
2
- export var version = "11.0.5";
2
+ export var version = "11.0.6";
3
3
  export var nextMajorVersion = function nextMajorVersion() {
4
4
  return [Number(version.split('.')[0]) + 1, 0, 0].join('.');
5
5
  };
@@ -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 collaborative editing system that handles the transmission of document changes (steps) from the local editor to other participants. This document provides a deep dive into how the function works, with particular focus on how unconfirmed steps are stored and retrieved, and how the commit queue manages step transmission.
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 service. Here's the detailed workflow showing the integration with `@atlaskit/prosemirror-collab`:
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 state within ProseMirror. It provides the fundamental infrastructure for tracking unconfirmed steps and handling collaborative operations.
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 transactions
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 metadata
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 operations:
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
- // Handle potential conflicts
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 array
137
+ - `@atlaskit/prosemirror-collab` processes the event and removes confirmed steps from unconfirmed
138
+ array
119
139
 
120
140
  #### 5. **Error Recovery Integration**
121
- - **Catchup Operations**: When version conflicts occur, the collab plugin state is updated with new document versions
122
- - **Step Rebasing**: The plugin handles automatic rebasing of unconfirmed steps against incoming remote steps
123
- - **State Restoration**: During document recovery, the plugin state is reset while preserving unconfirmed steps for reconciliation
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 `@atlaskit/prosemirror-collab` plugin. The storage mechanism works as follows:
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 plugin state, which is embedded within the main editor state as a plugin-specific state slice.
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 (`origins`) to track the source of changes even after rebasing operations.
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
- - **Adding Steps**: When transactions are applied locally, new steps are added to the unconfirmed array
137
- - **Removing Steps**: When acknowledgments are received from the server, corresponding steps are removed
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 conflict resolution.
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
- const state = this.getState?.();
158
- if (!state) {
159
- // Error handling
160
- return;
161
- }
162
- return sendableSteps(state)?.steps;
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
- const state = this.getState?.();
172
- if (!state) {
173
- // Error handling
174
- return;
175
- }
176
- return sendableSteps(state)?.origins;
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
- origins?.forEach((origin) => {
236
- if (origin instanceof Transaction) {
237
- return origin.setMeta('mergeIsLocked', true);
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 management to prevent overwhelming the server and ensure proper step ordering.
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
- logger('Not ready to commit, skip');
293
- return;
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
- delay = response.delay; // Server-provided backpressure delay
353
+ delay = response.delay; // Server-provided backpressure delay
311
354
  }
312
355
 
313
356
  commitWaitTimer = setTimeout(() => {
314
- this.readyToCommit = true;
315
- logger('reset readyToCommit');
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
- this.readyToCommit = true;
330
- this.lastBroadcastRequestAcked = true;
331
- logger('reset readyToCommit by timer');
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
- userId: this.getUserId()!,
348
- clientId: this.clientId!,
349
- steps: unconfirmedSteps,
350
- version,
351
- onStepsAdded: this.onStepsAdded,
352
- __livePage: this.options.__livePage,
353
- hasRecovered: this.hasRecovered,
354
- collabMode: this.participantsService.getCollabMode(),
355
- reason,
356
- numberOfStepCommitsSent: this.numberOfStepCommitsSent,
357
- setNumberOfCommitsSent: this.setNumberOfCommitsSent,
358
- lockSteps: this.lockSteps,
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 ensure steps are sent to the server in the correct order, preventing race conditions and maintaining document consistency.
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
- logger('Not ready to commit, skip');
474
- return; // Prevents out-of-order transmission
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
- steps: [S2, S3], // Accumulated unconfirmed steps
489
- version: 13, // Expected final version after these steps
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
- this.stepRejectCounter++;
522
- if (this.stepRejectCounter >= maxRetries) {
523
- // Trigger catchup to resync with server
524
- this.throttledCatchupv2(CatchupEventReason.STEPS_REJECTED);
525
- } else {
526
- // Retry with accumulated steps after delay
527
- setTimeout(() => this.sendStepsFromCurrentState(), 1000);
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
- - **Ready to Commit**: `readyToCommit` flag prevents concurrent sends and ensures proper step ordering
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 handling
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 operations
588
- 4. **`platform_editor_step_validation_on_connect`**: Controls step validation for first N steps after connection
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
- if (fg('skip_collab_provider_delay_on_publish')) {
596
- clearTimeout(commitWaitTimer);
597
- this.readyToCommit = true;
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 request has been acknowledged, reducing publish latency.
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 `@atlaskit/prosemirror-collab`:
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 `sendableSteps()` and `getCollabState()`
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 unconfirmed steps, version tracking, and step rebasing
628
- - **Conflict Resolution**: Built-in support for handling concurrent edits and step conflicts through rebasing algorithms
629
- - **Transaction Tracking**: Origins tracking allows the system to monitor step completion even after rebasing operations
630
- - **Robust Recovery**: The plugin state serves as the source of truth for document synchronization during error recovery scenarios
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 service
638
- - **Resilient Operation**: Fallback timers and error handling ensure the queue never gets permanently stuck
639
- - **Performance Optimization**: Special handling for publish operations and feature flag controls for different scenarios
640
-
641
- The unconfirmed steps storage leverages the battle-tested `@atlaskit/prosemirror-collab` plugin, providing a robust foundation for tracking pending changes while maintaining consistency with the collaborative editing protocol. This architecture ensures that the Document Service can focus on orchestration and error handling while delegating the complex state management to the specialized collaboration plugin.
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.6",
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.2",
39
- "@atlaskit/editor-json-transformer": "^8.24.0",
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.17.0",
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.12.0"
93
+ "@atlaskit/editor-common": "^107.16.0"
94
94
  }
95
95
  }