Skip to content

Commit 1a55fd0

Browse files
bdbchclaude
andauthored
Fix CollaborationCaret crash with undefined state during initialization (#7449)
* fix: prevent CollaborationCaret crash during editor initialization Fixes #6979, #7232 This commit addresses a critical bug where the CollaborationCaret extension would crash with "Cannot read properties of undefined (reading 'doc')" when editors were initialized with HTML content. Changes: - Updated @tiptap/y-tiptap dependency from ^3.0.0 to ^3.0.1 - Created patch for @tiptap/y-tiptap@3.0.1 to add null check in createDecorations - Added comprehensive unit tests for CollaborationCaret with HTML content - Updated test dependencies for extension-collaboration-caret The root cause was in the y-tiptap package's createDecorations function, which attempted to access ystate.doc without first checking if ystate was defined. This could occur during editor initialization when the ySyncPlugin state wasn't yet ready. The patch adds a guard to return an empty DecorationSet when ystate or ystate.doc is undefined, preventing the crash while allowing the editor to initialize properly. * chore: update to @tiptap/y-tiptap@3.0.2 and remove patch Updated all collaboration packages to use the official @tiptap/y-tiptap@3.0.2 release which includes the fix for the createDecorations crash. Changes: - Updated @tiptap/y-tiptap from ^3.0.1 to ^3.0.2 in: - @tiptap/extension-collaboration-caret - @tiptap/extension-collaboration - @tiptap/extension-drag-handle - Removed the y-tiptap patch file (no longer needed) - Updated package.json to remove patch reference - Updated changeset to reflect official release usage The upstream fix in y-tiptap@3.0.2 resolves issues #6979 and #7232. --------- Co-authored-by: Claude <noreply@anthropic.com>
1 parent b46e66a commit 1a55fd0

File tree

6 files changed

+290
-25
lines changed

6 files changed

+290
-25
lines changed
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
---
2+
'@tiptap/extension-collaboration-caret': patch
3+
'@tiptap/extension-collaboration': patch
4+
'@tiptap/extension-drag-handle': patch
5+
---
6+
7+
Fixed CollaborationCaret crash with "Cannot read properties of undefined (reading 'doc')" error by updating to @tiptap/y-tiptap@3.0.2, which includes a guard against undefined state during editor initialization. This issue affected editors initialized with HTML content, particularly when using tables.
Lines changed: 188 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,188 @@
1+
import { Editor } from '@tiptap/core'
2+
import Collaboration from '@tiptap/extension-collaboration'
3+
import CollaborationCaret from '@tiptap/extension-collaboration-caret'
4+
import Document from '@tiptap/extension-document'
5+
import Paragraph from '@tiptap/extension-paragraph'
6+
import Table from '@tiptap/extension-table'
7+
import TableCell from '@tiptap/extension-table-cell'
8+
import TableHeader from '@tiptap/extension-table-header'
9+
import TableRow from '@tiptap/extension-table-row'
10+
import Text from '@tiptap/extension-text'
11+
import { describe, expect, it } from 'vitest'
12+
import * as Y from 'yjs'
13+
14+
/**
15+
* Tests for CollaborationCaret extension, especially edge cases
16+
* with initial HTML content and complex structures like tables.
17+
*/
18+
describe('extension-collaboration-caret', () => {
19+
const editorElClass = 'tiptap'
20+
21+
const createEditorEl = () => {
22+
const editorEl = document.createElement('div')
23+
24+
editorEl.classList.add(editorElClass)
25+
document.body.appendChild(editorEl)
26+
return editorEl
27+
}
28+
29+
const getEditorEl = () => document.querySelector(`.${editorElClass}`)
30+
31+
/**
32+
* Test that the editor can be initialized with HTML content containing tables
33+
* when using Collaboration and CollaborationCaret extensions.
34+
*
35+
* This test reproduces the issue from GitHub #6979 where the editor crashes
36+
* with "Cannot read properties of undefined (reading 'doc')" when initialized
37+
* with HTML content containing tables.
38+
*/
39+
it('should not crash when initialized with HTML content containing tables', () => {
40+
const ydoc = new Y.Doc()
41+
42+
// Create a mock provider with minimal awareness API
43+
const mockProvider = {
44+
awareness: {
45+
states: new Map(),
46+
setLocalStateField: () => {},
47+
on: () => {},
48+
off: () => {},
49+
getStates: () => new Map(),
50+
},
51+
}
52+
53+
const editorEl = createEditorEl()
54+
55+
// This should not throw an error
56+
expect(() => {
57+
const editor = new Editor({
58+
element: editorEl,
59+
extensions: [
60+
Document,
61+
Paragraph,
62+
Text,
63+
Table,
64+
TableRow,
65+
TableHeader,
66+
TableCell,
67+
Collaboration.configure({
68+
document: ydoc,
69+
}),
70+
CollaborationCaret.configure({
71+
provider: mockProvider,
72+
user: {
73+
name: 'Test User',
74+
color: '#ff0000',
75+
},
76+
}),
77+
],
78+
content: `
79+
<table>
80+
<tr>
81+
<th>Header 1</th>
82+
<th>Header 2</th>
83+
</tr>
84+
<tr>
85+
<td>Cell 1</td>
86+
<td>Cell 2</td>
87+
</tr>
88+
</table>
89+
`,
90+
})
91+
92+
editor.destroy()
93+
}).not.toThrow()
94+
95+
getEditorEl()?.remove()
96+
})
97+
98+
/**
99+
* Test that the editor can be initialized with simple HTML content
100+
* when using Collaboration and CollaborationCaret extensions.
101+
*/
102+
it('should not crash when initialized with simple HTML content', () => {
103+
const ydoc = new Y.Doc()
104+
105+
const mockProvider = {
106+
awareness: {
107+
states: new Map(),
108+
setLocalStateField: () => {},
109+
on: () => {},
110+
off: () => {},
111+
getStates: () => new Map(),
112+
},
113+
}
114+
115+
const editorEl = createEditorEl()
116+
117+
expect(() => {
118+
const editor = new Editor({
119+
element: editorEl,
120+
extensions: [
121+
Document,
122+
Paragraph,
123+
Text,
124+
Collaboration.configure({
125+
document: ydoc,
126+
}),
127+
CollaborationCaret.configure({
128+
provider: mockProvider,
129+
user: {
130+
name: 'Test User',
131+
color: '#ff0000',
132+
},
133+
}),
134+
],
135+
content: '<p>Hello world</p>',
136+
})
137+
138+
editor.destroy()
139+
}).not.toThrow()
140+
141+
getEditorEl()?.remove()
142+
})
143+
144+
/**
145+
* Test that the editor can be initialized without any content
146+
* when using Collaboration and CollaborationCaret extensions.
147+
*/
148+
it('should work correctly when initialized without content', () => {
149+
const ydoc = new Y.Doc()
150+
151+
const mockProvider = {
152+
awareness: {
153+
states: new Map(),
154+
setLocalStateField: () => {},
155+
on: () => {},
156+
off: () => {},
157+
getStates: () => new Map(),
158+
},
159+
}
160+
161+
const editorEl = createEditorEl()
162+
163+
expect(() => {
164+
const editor = new Editor({
165+
element: editorEl,
166+
extensions: [
167+
Document,
168+
Paragraph,
169+
Text,
170+
Collaboration.configure({
171+
document: ydoc,
172+
}),
173+
CollaborationCaret.configure({
174+
provider: mockProvider,
175+
user: {
176+
name: 'Test User',
177+
color: '#ff0000',
178+
},
179+
}),
180+
],
181+
})
182+
183+
editor.destroy()
184+
}).not.toThrow()
185+
186+
getEditorEl()?.remove()
187+
})
188+
})

packages/extension-collaboration-caret/package.json

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,13 +32,22 @@
3232
],
3333
"devDependencies": {
3434
"@tiptap/core": "workspace:^",
35+
"@tiptap/extension-collaboration": "workspace:^",
36+
"@tiptap/extension-document": "workspace:^",
37+
"@tiptap/extension-paragraph": "workspace:^",
38+
"@tiptap/extension-table": "workspace:^",
39+
"@tiptap/extension-table-cell": "workspace:^",
40+
"@tiptap/extension-table-header": "workspace:^",
41+
"@tiptap/extension-table-row": "workspace:^",
42+
"@tiptap/extension-text": "workspace:^",
3543
"@tiptap/pm": "workspace:^",
36-
"@tiptap/y-tiptap": "^3.0.0"
44+
"@tiptap/y-tiptap": "^3.0.2",
45+
"yjs": "^13.6.23"
3746
},
3847
"peerDependencies": {
3948
"@tiptap/core": "workspace:^",
4049
"@tiptap/pm": "workspace:^",
41-
"@tiptap/y-tiptap": "^3.0.0"
50+
"@tiptap/y-tiptap": "^3.0.2"
4251
},
4352
"repository": {
4453
"type": "git",

packages/extension-collaboration/package.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,12 +33,12 @@
3333
"devDependencies": {
3434
"@tiptap/core": "workspace:^",
3535
"@tiptap/pm": "workspace:^",
36-
"@tiptap/y-tiptap": "^3.0.0"
36+
"@tiptap/y-tiptap": "^3.0.2"
3737
},
3838
"peerDependencies": {
3939
"@tiptap/core": "workspace:^",
4040
"@tiptap/pm": "workspace:^",
41-
"@tiptap/y-tiptap": "^3.0.0",
41+
"@tiptap/y-tiptap": "^3.0.2",
4242
"yjs": "^13"
4343
},
4444
"repository": {

packages/extension-drag-handle/package.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@
4040
"@tiptap/core": "workspace:^",
4141
"@tiptap/extension-collaboration": "workspace:^",
4242
"@tiptap/pm": "workspace:^",
43-
"@tiptap/y-tiptap": "^3.0.0"
43+
"@tiptap/y-tiptap": "^3.0.2"
4444
},
4545
"dependencies": {
4646
"@floating-ui/dom": "^1.6.13"
@@ -51,7 +51,7 @@
5151
"@tiptap/core": "workspace:^",
5252
"@tiptap/extension-collaboration": "workspace:^",
5353
"@tiptap/pm": "workspace:^",
54-
"@tiptap/y-tiptap": "^3.0.0"
54+
"@tiptap/y-tiptap": "^3.0.2"
5555
},
5656
"scripts": {
5757
"build": "tsup",

0 commit comments

Comments
 (0)