@datalayer/lexical-loro 0.2.5 → 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (399) hide show
  1. package/package.json +1 -1
  2. package/lib/App.d.ts +0 -2
  3. package/lib/App.js +0 -141
  4. package/lib/Editor.d.ts +0 -2
  5. package/lib/Editor.js +0 -115
  6. package/lib/Settings.d.ts +0 -2
  7. package/lib/Settings.js +0 -57
  8. package/lib/appSettings.d.ts +0 -36
  9. package/lib/appSettings.js +0 -48
  10. package/lib/collab/loro/Bindings.d.ts +0 -41
  11. package/lib/collab/loro/Bindings.js +0 -99
  12. package/lib/collab/loro/Debug.d.ts +0 -33
  13. package/lib/collab/loro/Debug.js +0 -452
  14. package/lib/collab/loro/LexicalCollaborationContext.d.ts +0 -19
  15. package/lib/collab/loro/LexicalCollaborationContext.js +0 -52
  16. package/lib/collab/loro/LexicalCollaborationPlugin.d.ts +0 -24
  17. package/lib/collab/loro/LexicalCollaborationPlugin.js +0 -83
  18. package/lib/collab/loro/State.d.ts +0 -53
  19. package/lib/collab/loro/State.js +0 -94
  20. package/lib/collab/loro/components/LoroCollaborationUI.d.ts +0 -13
  21. package/lib/collab/loro/components/LoroCollaborationUI.js +0 -9
  22. package/lib/collab/loro/components/LoroCollaborators.d.ts +0 -8
  23. package/lib/collab/loro/components/LoroCollaborators.js +0 -102
  24. package/lib/collab/loro/components/index.d.ts +0 -2
  25. package/lib/collab/loro/components/index.js +0 -6
  26. package/lib/collab/loro/index.d.ts +0 -6
  27. package/lib/collab/loro/index.js +0 -10
  28. package/lib/collab/loro/integrators/BaseIntegrator.d.ts +0 -14
  29. package/lib/collab/loro/integrators/BaseIntegrator.js +0 -5
  30. package/lib/collab/loro/integrators/CounterIntegrator.d.ts +0 -23
  31. package/lib/collab/loro/integrators/CounterIntegrator.js +0 -44
  32. package/lib/collab/loro/integrators/ListIntegrator.d.ts +0 -23
  33. package/lib/collab/loro/integrators/ListIntegrator.js +0 -53
  34. package/lib/collab/loro/integrators/MapIntegrator.d.ts +0 -24
  35. package/lib/collab/loro/integrators/MapIntegrator.js +0 -235
  36. package/lib/collab/loro/integrators/TextIntegrator.d.ts +0 -25
  37. package/lib/collab/loro/integrators/TextIntegrator.js +0 -55
  38. package/lib/collab/loro/integrators/TreeIntegrator.d.ts +0 -36
  39. package/lib/collab/loro/integrators/TreeIntegrator.js +0 -251
  40. package/lib/collab/loro/nodes/NodeFactory.d.ts +0 -15
  41. package/lib/collab/loro/nodes/NodeFactory.js +0 -101
  42. package/lib/collab/loro/nodes/NodesMapper.d.ts +0 -120
  43. package/lib/collab/loro/nodes/NodesMapper.js +0 -277
  44. package/lib/collab/loro/propagators/DecoratorNodePropagator.d.ts +0 -60
  45. package/lib/collab/loro/propagators/DecoratorNodePropagator.js +0 -306
  46. package/lib/collab/loro/propagators/ElementNodePropagator.d.ts +0 -62
  47. package/lib/collab/loro/propagators/ElementNodePropagator.js +0 -326
  48. package/lib/collab/loro/propagators/LineBreakNodePropagator.d.ts +0 -57
  49. package/lib/collab/loro/propagators/LineBreakNodePropagator.js +0 -200
  50. package/lib/collab/loro/propagators/RootNodePropagator.d.ts +0 -55
  51. package/lib/collab/loro/propagators/RootNodePropagator.js +0 -174
  52. package/lib/collab/loro/propagators/TextNodePropagator.d.ts +0 -60
  53. package/lib/collab/loro/propagators/TextNodePropagator.js +0 -440
  54. package/lib/collab/loro/propagators/index.d.ts +0 -49
  55. package/lib/collab/loro/propagators/index.js +0 -30
  56. package/lib/collab/loro/provider/websocket.d.ts +0 -116
  57. package/lib/collab/loro/provider/websocket.js +0 -911
  58. package/lib/collab/loro/servers/index.d.ts +0 -0
  59. package/lib/collab/loro/servers/index.js +0 -4
  60. package/lib/collab/loro/servers/ws/callback.d.ts +0 -5
  61. package/lib/collab/loro/servers/ws/callback.js +0 -89
  62. package/lib/collab/loro/servers/ws/server.d.ts +0 -2
  63. package/lib/collab/loro/servers/ws/server.js +0 -29
  64. package/lib/collab/loro/servers/ws/utils.d.ts +0 -40
  65. package/lib/collab/loro/servers/ws/utils.js +0 -517
  66. package/lib/collab/loro/sync/SyncCursors.d.ts +0 -32
  67. package/lib/collab/loro/sync/SyncCursors.js +0 -475
  68. package/lib/collab/loro/sync/SyncLexicalToLoro.d.ts +0 -4
  69. package/lib/collab/loro/sync/SyncLexicalToLoro.js +0 -113
  70. package/lib/collab/loro/sync/SyncLoroToLexical.d.ts +0 -5
  71. package/lib/collab/loro/sync/SyncLoroToLexical.js +0 -100
  72. package/lib/collab/loro/types/LexicalNodeData.d.ts +0 -32
  73. package/lib/collab/loro/types/LexicalNodeData.js +0 -75
  74. package/lib/collab/loro/useCollaboration.d.ts +0 -12
  75. package/lib/collab/loro/useCollaboration.js +0 -260
  76. package/lib/collab/loro/utils/InitialContent.d.ts +0 -64
  77. package/lib/collab/loro/utils/InitialContent.js +0 -113
  78. package/lib/collab/loro/utils/LexicalToLoro.d.ts +0 -18
  79. package/lib/collab/loro/utils/LexicalToLoro.js +0 -100
  80. package/lib/collab/loro/utils/Utils.d.ts +0 -44
  81. package/lib/collab/loro/utils/Utils.js +0 -157
  82. package/lib/collab/loro/wsProvider.d.ts +0 -8
  83. package/lib/collab/loro/wsProvider.js +0 -35
  84. package/lib/collab/utils/invariant.d.ts +0 -1
  85. package/lib/collab/utils/invariant.js +0 -15
  86. package/lib/collab/utils/simpleDiffWithCursor.d.ts +0 -5
  87. package/lib/collab/utils/simpleDiffWithCursor.js +0 -35
  88. package/lib/collab/yjs/Bindings.d.ts +0 -23
  89. package/lib/collab/yjs/Bindings.js +0 -26
  90. package/lib/collab/yjs/Debug.d.ts +0 -23
  91. package/lib/collab/yjs/Debug.js +0 -213
  92. package/lib/collab/yjs/LexicalCollaborationContext.d.ts +0 -10
  93. package/lib/collab/yjs/LexicalCollaborationContext.js +0 -37
  94. package/lib/collab/yjs/LexicalCollaborationPlugin.d.ts +0 -21
  95. package/lib/collab/yjs/LexicalCollaborationPlugin.js +0 -63
  96. package/lib/collab/yjs/State.d.ts +0 -51
  97. package/lib/collab/yjs/State.js +0 -35
  98. package/lib/collab/yjs/nodes/AnyCollabNode.d.ts +0 -5
  99. package/lib/collab/yjs/nodes/AnyCollabNode.js +0 -1
  100. package/lib/collab/yjs/nodes/CollabDecoratorNode.d.ts +0 -22
  101. package/lib/collab/yjs/nodes/CollabDecoratorNode.js +0 -64
  102. package/lib/collab/yjs/nodes/CollabElementNode.d.ts +0 -40
  103. package/lib/collab/yjs/nodes/CollabElementNode.js +0 -462
  104. package/lib/collab/yjs/nodes/CollabLineBreakNode.d.ts +0 -19
  105. package/lib/collab/yjs/nodes/CollabLineBreakNode.js +0 -44
  106. package/lib/collab/yjs/nodes/CollabTextNode.d.ts +0 -25
  107. package/lib/collab/yjs/nodes/CollabTextNode.js +0 -103
  108. package/lib/collab/yjs/provider/websocket.d.ts +0 -88
  109. package/lib/collab/yjs/provider/websocket.js +0 -415
  110. package/lib/collab/yjs/servers/index.d.ts +0 -0
  111. package/lib/collab/yjs/servers/index.js +0 -0
  112. package/lib/collab/yjs/servers/ws/callback.d.ts +0 -5
  113. package/lib/collab/yjs/servers/ws/callback.js +0 -72
  114. package/lib/collab/yjs/servers/ws/server.d.ts +0 -2
  115. package/lib/collab/yjs/servers/ws/server.js +0 -25
  116. package/lib/collab/yjs/servers/ws/utils.d.ts +0 -49
  117. package/lib/collab/yjs/servers/ws/utils.js +0 -284
  118. package/lib/collab/yjs/sync/SyncCursors.d.ts +0 -39
  119. package/lib/collab/yjs/sync/SyncCursors.js +0 -351
  120. package/lib/collab/yjs/sync/SyncEditorStates.d.ts +0 -10
  121. package/lib/collab/yjs/sync/SyncEditorStates.js +0 -200
  122. package/lib/collab/yjs/useCollaboration.d.ts +0 -12
  123. package/lib/collab/yjs/useCollaboration.js +0 -255
  124. package/lib/collab/yjs/utils/Utils.d.ts +0 -25
  125. package/lib/collab/yjs/utils/Utils.js +0 -402
  126. package/lib/collab/yjs/wsProvider.d.ts +0 -3
  127. package/lib/collab/yjs/wsProvider.js +0 -21
  128. package/lib/commenting/index.d.ts +0 -41
  129. package/lib/commenting/index.js +0 -328
  130. package/lib/context/FlashMessageContext.d.ts +0 -7
  131. package/lib/context/FlashMessageContext.js +0 -24
  132. package/lib/context/SettingsContext.d.ts +0 -12
  133. package/lib/context/SettingsContext.js +0 -38
  134. package/lib/context/SharedHistoryContext.d.ts +0 -11
  135. package/lib/context/SharedHistoryContext.js +0 -11
  136. package/lib/context/ToolbarContext.d.ts +0 -65
  137. package/lib/context/ToolbarContext.js +0 -84
  138. package/lib/demo.d.ts +0 -12
  139. package/lib/demo.js +0 -45
  140. package/lib/hooks/useFlashMessage.d.ts +0 -2
  141. package/lib/hooks/useFlashMessage.js +0 -8
  142. package/lib/hooks/useModal.d.ts +0 -5
  143. package/lib/hooks/useModal.js +0 -26
  144. package/lib/hooks/useReport.d.ts +0 -1
  145. package/lib/hooks/useReport.js +0 -50
  146. package/lib/index.d.ts +0 -1
  147. package/lib/index.js +0 -5
  148. package/lib/nodes/AutocompleteNode.d.ts +0 -27
  149. package/lib/nodes/AutocompleteNode.js +0 -60
  150. package/lib/nodes/CounterComponent.d.ts +0 -6
  151. package/lib/nodes/CounterComponent.js +0 -137
  152. package/lib/nodes/CounterNode.d.ts +0 -23
  153. package/lib/nodes/CounterNode.js +0 -47
  154. package/lib/nodes/DateTimeNode/DateTimeComponent.d.ts +0 -8
  155. package/lib/nodes/DateTimeNode/DateTimeComponent.js +0 -119
  156. package/lib/nodes/DateTimeNode/DateTimeNode.d.ts +0 -27
  157. package/lib/nodes/DateTimeNode/DateTimeNode.js +0 -82
  158. package/lib/nodes/EmojiNode.d.ts +0 -18
  159. package/lib/nodes/EmojiNode.js +0 -54
  160. package/lib/nodes/EquationComponent.d.ts +0 -9
  161. package/lib/nodes/EquationComponent.js +0 -75
  162. package/lib/nodes/EquationNode.d.ts +0 -26
  163. package/lib/nodes/EquationNode.js +0 -109
  164. package/lib/nodes/ExcalidrawNode/ExcalidrawComponent.d.ts +0 -8
  165. package/lib/nodes/ExcalidrawNode/ExcalidrawComponent.js +0 -110
  166. package/lib/nodes/ExcalidrawNode/ExcalidrawImage.d.ts +0 -50
  167. package/lib/nodes/ExcalidrawNode/ExcalidrawImage.js +0 -55
  168. package/lib/nodes/ExcalidrawNode/index.d.ts +0 -32
  169. package/lib/nodes/ExcalidrawNode/index.js +0 -117
  170. package/lib/nodes/FigmaNode.d.ts +0 -20
  171. package/lib/nodes/FigmaNode.js +0 -52
  172. package/lib/nodes/ImageComponent.d.ts +0 -16
  173. package/lib/nodes/ImageComponent.js +0 -272
  174. package/lib/nodes/ImageNode.d.ts +0 -50
  175. package/lib/nodes/ImageNode.js +0 -151
  176. package/lib/nodes/InlineImageNode/InlineImageComponent.d.ts +0 -26
  177. package/lib/nodes/InlineImageNode/InlineImageComponent.js +0 -161
  178. package/lib/nodes/InlineImageNode/InlineImageNode.d.ts +0 -59
  179. package/lib/nodes/InlineImageNode/InlineImageNode.js +0 -162
  180. package/lib/nodes/KeywordNode.d.ts +0 -14
  181. package/lib/nodes/KeywordNode.js +0 -37
  182. package/lib/nodes/LayoutContainerNode.d.ts +0 -24
  183. package/lib/nodes/LayoutContainerNode.js +0 -95
  184. package/lib/nodes/LayoutItemNode.d.ts +0 -16
  185. package/lib/nodes/LayoutItemNode.js +0 -69
  186. package/lib/nodes/MentionNode.d.ts +0 -20
  187. package/lib/nodes/MentionNode.js +0 -85
  188. package/lib/nodes/PageBreakNode/index.d.ts +0 -17
  189. package/lib/nodes/PageBreakNode/index.js +0 -83
  190. package/lib/nodes/PlaygroundNodes.d.ts +0 -3
  191. package/lib/nodes/PlaygroundNodes.js +0 -75
  192. package/lib/nodes/PollComponent.d.ts +0 -9
  193. package/lib/nodes/PollComponent.js +0 -85
  194. package/lib/nodes/PollNode.d.ts +0 -43
  195. package/lib/nodes/PollNode.js +0 -153
  196. package/lib/nodes/SpecialTextNode.d.ts +0 -24
  197. package/lib/nodes/SpecialTextNode.js +0 -54
  198. package/lib/nodes/StickyComponent.d.ts +0 -10
  199. package/lib/nodes/StickyComponent.js +0 -162
  200. package/lib/nodes/StickyNode.d.ts +0 -31
  201. package/lib/nodes/StickyNode.js +0 -76
  202. package/lib/nodes/TweetNode.d.ts +0 -21
  203. package/lib/nodes/TweetNode.js +0 -119
  204. package/lib/nodes/YouTubeNode.d.ts +0 -22
  205. package/lib/nodes/YouTubeNode.js +0 -84
  206. package/lib/plugins/ActionsPlugin/index.d.ts +0 -5
  207. package/lib/plugins/ActionsPlugin/index.js +0 -168
  208. package/lib/plugins/AutoEmbedPlugin/index.d.ts +0 -19
  209. package/lib/plugins/AutoEmbedPlugin/index.js +0 -158
  210. package/lib/plugins/AutoLinkPlugin/index.d.ts +0 -2
  211. package/lib/plugins/AutoLinkPlugin/index.js +0 -15
  212. package/lib/plugins/AutocompletePlugin/index.d.ts +0 -10
  213. package/lib/plugins/AutocompletePlugin/index.js +0 -2477
  214. package/lib/plugins/CodeActionMenuPlugin/components/CopyButton/index.d.ts +0 -7
  215. package/lib/plugins/CodeActionMenuPlugin/components/CopyButton/index.js +0 -46
  216. package/lib/plugins/CodeActionMenuPlugin/components/PrettierButton/index.d.ts +0 -17
  217. package/lib/plugins/CodeActionMenuPlugin/components/PrettierButton/index.js +0 -115
  218. package/lib/plugins/CodeActionMenuPlugin/index.d.ts +0 -5
  219. package/lib/plugins/CodeActionMenuPlugin/index.js +0 -104
  220. package/lib/plugins/CodeActionMenuPlugin/utils.d.ts +0 -1
  221. package/lib/plugins/CodeActionMenuPlugin/utils.js +0 -22
  222. package/lib/plugins/CodeHighlightPrismPlugin/index.d.ts +0 -2
  223. package/lib/plugins/CodeHighlightPrismPlugin/index.js +0 -14
  224. package/lib/plugins/CodeHighlightShikiPlugin/index.d.ts +0 -2
  225. package/lib/plugins/CodeHighlightShikiPlugin/index.js +0 -14
  226. package/lib/plugins/CollapsiblePlugin/CollapsibleContainerNode.d.ts +0 -25
  227. package/lib/plugins/CollapsiblePlugin/CollapsibleContainerNode.js +0 -135
  228. package/lib/plugins/CollapsiblePlugin/CollapsibleContentNode.d.ts +0 -16
  229. package/lib/plugins/CollapsiblePlugin/CollapsibleContentNode.js +0 -83
  230. package/lib/plugins/CollapsiblePlugin/CollapsibleTitleNode.d.ts +0 -16
  231. package/lib/plugins/CollapsiblePlugin/CollapsibleTitleNode.js +0 -85
  232. package/lib/plugins/CollapsiblePlugin/CollapsibleUtils.d.ts +0 -2
  233. package/lib/plugins/CollapsiblePlugin/CollapsibleUtils.js +0 -12
  234. package/lib/plugins/CollapsiblePlugin/index.d.ts +0 -3
  235. package/lib/plugins/CollapsiblePlugin/index.js +0 -132
  236. package/lib/plugins/CommentPlugin/index.d.ts +0 -9
  237. package/lib/plugins/CommentPlugin/index.js +0 -460
  238. package/lib/plugins/ComponentPickerPlugin/index.d.ts +0 -2
  239. package/lib/plugins/ComponentPickerPlugin/index.js +0 -276
  240. package/lib/plugins/ContextMenuPlugin/index.d.ts +0 -2
  241. package/lib/plugins/ContextMenuPlugin/index.js +0 -112
  242. package/lib/plugins/CounterPlugin/index.d.ts +0 -3
  243. package/lib/plugins/CounterPlugin/index.js +0 -24
  244. package/lib/plugins/DateTimePlugin/index.d.ts +0 -8
  245. package/lib/plugins/DateTimePlugin/index.js +0 -28
  246. package/lib/plugins/DebugPlugin/index.d.ts +0 -3
  247. package/lib/plugins/DebugPlugin/index.js +0 -219
  248. package/lib/plugins/DocsPlugin/index.d.ts +0 -2
  249. package/lib/plugins/DocsPlugin/index.js +0 -4
  250. package/lib/plugins/DragDropPastePlugin/index.d.ts +0 -1
  251. package/lib/plugins/DragDropPastePlugin/index.js +0 -37
  252. package/lib/plugins/DraggableBlockPlugin/index.d.ts +0 -12
  253. package/lib/plugins/DraggableBlockPlugin/index.js +0 -36
  254. package/lib/plugins/EmojiPickerPlugin/index.d.ts +0 -1
  255. package/lib/plugins/EmojiPickerPlugin/index.js +0 -84
  256. package/lib/plugins/EmojisPlugin/index.d.ts +0 -2
  257. package/lib/plugins/EmojisPlugin/index.js +0 -56
  258. package/lib/plugins/EquationsPlugin/index.d.ts +0 -14
  259. package/lib/plugins/EquationsPlugin/index.js +0 -34
  260. package/lib/plugins/ExcalidrawPlugin/index.d.ts +0 -5
  261. package/lib/plugins/ExcalidrawPlugin/index.js +0 -44
  262. package/lib/plugins/FigmaPlugin/index.d.ts +0 -4
  263. package/lib/plugins/FigmaPlugin/index.js +0 -24
  264. package/lib/plugins/FloatingLinkEditorPlugin/index.d.ts +0 -15
  265. package/lib/plugins/FloatingLinkEditorPlugin/index.js +0 -280
  266. package/lib/plugins/FloatingTextFormatToolbarPlugin/index.d.ts +0 -7
  267. package/lib/plugins/FloatingTextFormatToolbarPlugin/index.js +0 -219
  268. package/lib/plugins/ImagesPlugin/index.d.ts +0 -24
  269. package/lib/plugins/ImagesPlugin/index.js +0 -195
  270. package/lib/plugins/InlineImagePlugin/index.d.ts +0 -17
  271. package/lib/plugins/InlineImagePlugin/index.js +0 -180
  272. package/lib/plugins/KeywordsPlugin/index.d.ts +0 -2
  273. package/lib/plugins/KeywordsPlugin/index.js +0 -35
  274. package/lib/plugins/LayoutPlugin/InsertLayoutDialog.d.ts +0 -6
  275. package/lib/plugins/LayoutPlugin/InsertLayoutDialog.js +0 -21
  276. package/lib/plugins/LayoutPlugin/LayoutPlugin.d.ts +0 -7
  277. package/lib/plugins/LayoutPlugin/LayoutPlugin.js +0 -135
  278. package/lib/plugins/LinkPlugin/index.d.ts +0 -6
  279. package/lib/plugins/LinkPlugin/index.js +0 -11
  280. package/lib/plugins/MarkdownShortcutPlugin/index.d.ts +0 -2
  281. package/lib/plugins/MarkdownShortcutPlugin/index.js +0 -6
  282. package/lib/plugins/MarkdownTransformers/index.d.ts +0 -8
  283. package/lib/plugins/MarkdownTransformers/index.js +0 -238
  284. package/lib/plugins/MaxLengthPlugin/index.d.ts +0 -3
  285. package/lib/plugins/MaxLengthPlugin/index.js +0 -41
  286. package/lib/plugins/MentionsPlugin/index.d.ts +0 -2
  287. package/lib/plugins/MentionsPlugin/index.js +0 -564
  288. package/lib/plugins/PageBreakPlugin/index.d.ts +0 -4
  289. package/lib/plugins/PageBreakPlugin/index.js +0 -31
  290. package/lib/plugins/PasteLogPlugin/index.d.ts +0 -2
  291. package/lib/plugins/PasteLogPlugin/index.js +0 -27
  292. package/lib/plugins/PollPlugin/index.d.ts +0 -8
  293. package/lib/plugins/PollPlugin/index.js +0 -38
  294. package/lib/plugins/ShortcutsPlugin/index.d.ts +0 -6
  295. package/lib/plugins/ShortcutsPlugin/index.js +0 -116
  296. package/lib/plugins/ShortcutsPlugin/shortcuts.d.ts +0 -59
  297. package/lib/plugins/ShortcutsPlugin/shortcuts.js +0 -173
  298. package/lib/plugins/SpecialTextPlugin/index.d.ts +0 -2
  299. package/lib/plugins/SpecialTextPlugin/index.js +0 -50
  300. package/lib/plugins/SpeechToTextPlugin/index.d.ts +0 -5
  301. package/lib/plugins/SpeechToTextPlugin/index.js +0 -86
  302. package/lib/plugins/StickyPlugin/index.d.ts +0 -2
  303. package/lib/plugins/StickyPlugin/index.js +0 -16
  304. package/lib/plugins/TabFocusPlugin/index.d.ts +0 -1
  305. package/lib/plugins/TabFocusPlugin/index.js +0 -38
  306. package/lib/plugins/TableActionMenuPlugin/index.d.ts +0 -5
  307. package/lib/plugins/TableActionMenuPlugin/index.js +0 -492
  308. package/lib/plugins/TableCellResizer/index.d.ts +0 -3
  309. package/lib/plugins/TableCellResizer/index.js +0 -297
  310. package/lib/plugins/TableHoverActionsPlugin/index.d.ts +0 -4
  311. package/lib/plugins/TableHoverActionsPlugin/index.js +0 -188
  312. package/lib/plugins/TableOfContentsPlugin/index.d.ts +0 -2
  313. package/lib/plugins/TableOfContentsPlugin/index.js +0 -116
  314. package/lib/plugins/TablePlugin.d.ts +0 -31
  315. package/lib/plugins/TablePlugin.js +0 -63
  316. package/lib/plugins/TestRecorderPlugin/index.d.ts +0 -3
  317. package/lib/plugins/TestRecorderPlugin/index.js +0 -346
  318. package/lib/plugins/ToolbarPlugin/fontSize.d.ts +0 -9
  319. package/lib/plugins/ToolbarPlugin/fontSize.js +0 -84
  320. package/lib/plugins/ToolbarPlugin/index.d.ts +0 -9
  321. package/lib/plugins/ToolbarPlugin/index.js +0 -500
  322. package/lib/plugins/ToolbarPlugin/utils.d.ts +0 -26
  323. package/lib/plugins/ToolbarPlugin/utils.js +0 -247
  324. package/lib/plugins/TreeViewPlugin/index.d.ts +0 -2
  325. package/lib/plugins/TreeViewPlugin/index.js +0 -7
  326. package/lib/plugins/TwitterPlugin/index.d.ts +0 -4
  327. package/lib/plugins/TwitterPlugin/index.js +0 -24
  328. package/lib/plugins/TypingPerfPlugin/index.d.ts +0 -2
  329. package/lib/plugins/TypingPerfPlugin/index.js +0 -97
  330. package/lib/plugins/YouTubePlugin/index.d.ts +0 -4
  331. package/lib/plugins/YouTubePlugin/index.js +0 -24
  332. package/lib/server/validation.d.ts +0 -1
  333. package/lib/server/validation.js +0 -115
  334. package/lib/setupEnv.d.ts +0 -2
  335. package/lib/setupEnv.js +0 -29
  336. package/lib/themes/CommentEditorTheme.d.ts +0 -4
  337. package/lib/themes/CommentEditorTheme.js +0 -11
  338. package/lib/themes/PlaygroundEditorTheme.d.ts +0 -4
  339. package/lib/themes/PlaygroundEditorTheme.js +0 -124
  340. package/lib/themes/StickyEditorTheme.d.ts +0 -4
  341. package/lib/themes/StickyEditorTheme.js +0 -11
  342. package/lib/tyes.dt.d.ts +0 -12
  343. package/lib/tyes.dt.js +0 -4
  344. package/lib/ui/Button.d.ts +0 -12
  345. package/lib/ui/Button.js +0 -6
  346. package/lib/ui/ColorPicker.d.ts +0 -14
  347. package/lib/ui/ColorPicker.js +0 -219
  348. package/lib/ui/ContentEditable.d.ts +0 -9
  349. package/lib/ui/ContentEditable.js +0 -6
  350. package/lib/ui/Dialog.d.ts +0 -10
  351. package/lib/ui/Dialog.js +0 -8
  352. package/lib/ui/DropDown.d.ts +0 -18
  353. package/lib/ui/DropDown.js +0 -133
  354. package/lib/ui/DropdownColorPicker.d.ts +0 -13
  355. package/lib/ui/DropdownColorPicker.js +0 -6
  356. package/lib/ui/EquationEditor.d.ts +0 -8
  357. package/lib/ui/EquationEditor.js +0 -11
  358. package/lib/ui/ExcalidrawModal.d.ts +0 -42
  359. package/lib/ui/ExcalidrawModal.js +0 -103
  360. package/lib/ui/FileInput.d.ts +0 -10
  361. package/lib/ui/FileInput.js +0 -5
  362. package/lib/ui/FlashMessage.d.ts +0 -7
  363. package/lib/ui/FlashMessage.js +0 -6
  364. package/lib/ui/ImageResizer.d.ts +0 -17
  365. package/lib/ui/ImageResizer.js +0 -171
  366. package/lib/ui/KatexEquationAlterer.d.ts +0 -8
  367. package/lib/ui/KatexEquationAlterer.js +0 -23
  368. package/lib/ui/KatexRenderer.d.ts +0 -6
  369. package/lib/ui/KatexRenderer.js +0 -24
  370. package/lib/ui/Modal.d.ts +0 -9
  371. package/lib/ui/Modal.js +0 -48
  372. package/lib/ui/Select.d.ts +0 -8
  373. package/lib/ui/Select.js +0 -5
  374. package/lib/ui/Switch.d.ts +0 -8
  375. package/lib/ui/Switch.js +0 -6
  376. package/lib/ui/TextInput.d.ts +0 -13
  377. package/lib/ui/TextInput.js +0 -7
  378. package/lib/utils/docSerialization.d.ts +0 -3
  379. package/lib/utils/docSerialization.js +0 -60
  380. package/lib/utils/emoji-list.d.ts +0 -20
  381. package/lib/utils/emoji-list.js +0 -16609
  382. package/lib/utils/getDOMRangeRect.d.ts +0 -8
  383. package/lib/utils/getDOMRangeRect.js +0 -26
  384. package/lib/utils/getSelectedNode.d.ts +0 -2
  385. package/lib/utils/getSelectedNode.js +0 -28
  386. package/lib/utils/getThemeSelector.d.ts +0 -2
  387. package/lib/utils/getThemeSelector.js +0 -14
  388. package/lib/utils/isMobileWidth.d.ts +0 -7
  389. package/lib/utils/isMobileWidth.js +0 -11
  390. package/lib/utils/joinClasses.d.ts +0 -1
  391. package/lib/utils/joinClasses.js +0 -7
  392. package/lib/utils/setFloatingElemPosition.d.ts +0 -1
  393. package/lib/utils/setFloatingElemPosition.js +0 -59
  394. package/lib/utils/setFloatingElemPositionForLinkEditor.d.ts +0 -1
  395. package/lib/utils/setFloatingElemPositionForLinkEditor.js +0 -36
  396. package/lib/utils/swipe.d.ts +0 -4
  397. package/lib/utils/swipe.js +0 -94
  398. package/lib/utils/url.d.ts +0 -2
  399. package/lib/utils/url.js +0 -31
@@ -1,911 +0,0 @@
1
- /*
2
- * Copyright (c) 2023-2025 Datalayer, Inc.
3
- * Distributed under the terms of the MIT License.
4
- */
5
- import { EphemeralStore } from 'loro-crdt';
6
- import { ObservableV2 } from 'lib0/observable';
7
- import * as bc from 'lib0/broadcastchannel';
8
- import * as time from 'lib0/time';
9
- import * as math from 'lib0/math';
10
- import * as url from 'lib0/url';
11
- import * as env from 'lib0/environment';
12
- import { generateClientID, generateRandomClientID } from '../utils/Utils';
13
- // @todo - this should depend on ephemeral timeout
14
- const messageReconnectTimeoutMs = 30 * 1000; // 30 seconds
15
- // Loro message types
16
- export const messageUpdate = 'update';
17
- export const messageQuerySnapshot = 'query-snapshot';
18
- export const messageEphemeral = 'ephemeral';
19
- export const messageQueryEphemeral = 'query-ephemeral';
20
- /**
21
- * Awareness adapter that wraps EphemeralStore to provide awareness-like API
22
- */
23
- class AwarenessAdapter {
24
- ephemeralStore;
25
- localClientId;
26
- eventHandlers = new Map();
27
- constructor(ephemeralStore, doc) {
28
- this.ephemeralStore = ephemeralStore;
29
- // Use the same client ID as the binding for consistency
30
- this.localClientId = doc ? generateClientID(doc) : generateRandomClientID();
31
- console.log('🔄 AwarenessAdapter created:', {
32
- localClientId: this.localClientId,
33
- docPeerId: doc ? doc.peerId : 'no-doc',
34
- existingStatesCount: Object.keys(ephemeralStore.getAllStates()).length
35
- });
36
- // Subscribe to ephemeral store changes and emit awareness updates
37
- this.ephemeralStore.subscribe((event) => {
38
- // Emit update events when ephemeral state changes
39
- const updateHandlers = this.eventHandlers.get('update') || [];
40
- updateHandlers.forEach(integrater => integrater());
41
- });
42
- }
43
- getLocalState() {
44
- const localKey = this.localClientId.toString();
45
- try {
46
- const state = this.ephemeralStore.get(localKey);
47
- return state ? state : null;
48
- }
49
- catch (error) {
50
- console.warn(`[Client] AwarenessAdapter.getLocalState() - ephemeralStore.get() FAILED:`, {
51
- error: error.message,
52
- stack: error.stack,
53
- localKey,
54
- peerId: this.localClientId,
55
- storeExists: !!this.ephemeralStore
56
- });
57
- throw error;
58
- }
59
- }
60
- getStates() {
61
- const states = new Map();
62
- try {
63
- // Get all states from ephemeral store
64
- const allStates = this.ephemeralStore.getAllStates();
65
- // Clean up stale states periodically (very rarely to avoid performance issues)
66
- if (Math.random() < 0.001) { // ~0.1% chance per call
67
- this.cleanupStaleStates(allStates);
68
- }
69
- // Iterate through all keys and extract user states
70
- for (const [key, value] of Object.entries(allStates)) {
71
- // Keys are now direct client ID strings
72
- const clientId = parseInt(key, 10);
73
- if (!isNaN(clientId) && value) {
74
- states.set(clientId, value);
75
- }
76
- }
77
- }
78
- catch (error) {
79
- console.warn(`[Client] AwarenessAdapter.getStates() - ERROR:`, error.message);
80
- // Fallback to just local state
81
- const localState = this.getLocalState();
82
- if (localState) {
83
- states.set(this.localClientId, localState);
84
- }
85
- }
86
- return states;
87
- }
88
- cleanupStaleStates(allStates) {
89
- const currentTime = Date.now();
90
- const staleThreshold = 5 * 60 * 1000; // 5 minutes
91
- try {
92
- for (const [key, value] of Object.entries(allStates)) {
93
- // Keys are now direct client ID strings
94
- const clientId = parseInt(key, 10);
95
- if (!isNaN(clientId) && value && typeof value === 'object') {
96
- const state = value;
97
- const lastActivity = typeof state.lastActivity === 'number' ? state.lastActivity : 0;
98
- // Remove states that haven't been active for more than the threshold
99
- if (typeof lastActivity === 'number' && currentTime - lastActivity > staleThreshold) {
100
- console.log('Cleaning up stale user state:', key, 'last activity:', new Date(lastActivity).toISOString());
101
- this.ephemeralStore.delete(key);
102
- }
103
- }
104
- }
105
- }
106
- catch (error) {
107
- console.warn('Error during stale state cleanup:', error.message);
108
- }
109
- }
110
- setLocalState(state) {
111
- const localKey = this.localClientId.toString();
112
- // Add lastActivity timestamp for stale state cleanup
113
- const stateWithActivity = {
114
- ...state,
115
- lastActivity: Date.now()
116
- };
117
- try {
118
- this.ephemeralStore.set(localKey, stateWithActivity);
119
- }
120
- catch (error) {
121
- console.warn(`[Client] AwarenessAdapter.setLocalState() - ephemeralStore.set() FAILED:`, {
122
- error: error.message,
123
- stack: error.stack,
124
- localKey,
125
- peerId: this.localClientId,
126
- stateKeys: Object.keys(state || {}),
127
- storeExists: !!this.ephemeralStore
128
- });
129
- throw error;
130
- }
131
- }
132
- setLocalStateField(field, value) {
133
- const localState = this.getLocalState() || {
134
- anchorPos: null,
135
- awarenessData: {},
136
- color: '#000000',
137
- focusPos: null,
138
- focusing: false,
139
- name: 'Anonymous',
140
- };
141
- localState[field] = value;
142
- this.setLocalState(localState);
143
- }
144
- on(type, cb) {
145
- if (!this.eventHandlers.has(type)) {
146
- this.eventHandlers.set(type, []);
147
- }
148
- this.eventHandlers.get(type).push(cb);
149
- }
150
- off(type, cb) {
151
- const integrators = this.eventHandlers.get(type);
152
- if (integrators) {
153
- const index = integrators.indexOf(cb);
154
- if (index !== -1) {
155
- integrators.splice(index, 1);
156
- }
157
- }
158
- }
159
- // Manual cleanup method for debugging
160
- forceCleanupStaleStates() {
161
- try {
162
- const allStates = this.ephemeralStore.getAllStates();
163
- console.log('Force cleanup - total keys before:', Object.keys(allStates).length);
164
- this.cleanupStaleStates(allStates);
165
- const newStates = this.ephemeralStore.getAllStates();
166
- console.log('Force cleanup - total keys after:', Object.keys(newStates).length);
167
- }
168
- catch (error) {
169
- console.warn('Force cleanup failed:', error.message);
170
- }
171
- }
172
- }
173
- /**
174
- * Message integrators for different Loro message types
175
- */
176
- const messageHandlers = {};
177
- messageHandlers[messageQueryEphemeral] = (provider, message, _emitSynced) => {
178
- try {
179
- // Use encodeAll() to encode all ephemeral store data
180
- const encodedData = provider.ephemeralStore.encodeAll();
181
- const response = {
182
- type: 'ephemeral',
183
- ephemeral: Array.from(encodedData),
184
- docId: message.docId
185
- };
186
- return JSON.stringify(response);
187
- }
188
- catch (error) {
189
- console.warn('Error in messageQueryEphemeral integrater:', error.message);
190
- return null;
191
- }
192
- };
193
- messageHandlers[messageEphemeral] = (provider, message, _emitSynced) => {
194
- try {
195
- // Validate message data before processing
196
- if (!message.ephemeral || message.ephemeral.length === 0) {
197
- console.warn(`[Client] messageHandlers[messageEphemeral] - Skipping empty ephemeral data`);
198
- return null;
199
- }
200
- // Reject obviously corrupted data (too small, common corrupt patterns)
201
- if (message.ephemeral.length < 8) {
202
- console.warn(`[Client] messageHandlers[messageEphemeral] - Rejecting suspiciously small ephemeral data:`, message.ephemeral.length);
203
- return null;
204
- }
205
- // Additional validation - check for specific known bad patterns
206
- if (message.ephemeral.length === 1) {
207
- console.warn(`[Client] messageHandlers[messageEphemeral] - Rejecting single-byte ephemeral data (likely corrupted):`, message.ephemeral);
208
- return null;
209
- }
210
- // Validate ephemeral data format by checking if it starts with reasonable values
211
- // Ephemeral data should have a structured format - single random bytes are invalid
212
- const firstBytes = message.ephemeral.slice(0, 4);
213
- if (firstBytes.every(b => b === 0) || firstBytes.every(b => b === 255)) {
214
- console.warn(`[Client] messageHandlers[messageEphemeral] - Rejecting ephemeral data with suspicious pattern:`, firstBytes);
215
- return null;
216
- }
217
- // Apply ephemeral update
218
- const ephemeralBytes = new Uint8Array(message.ephemeral);
219
- // Use a try-catch specifically for the apply operation to isolate WASM errors
220
- try {
221
- provider.ephemeralStore.apply(ephemeralBytes);
222
- }
223
- catch (applyError) {
224
- console.warn(`[Client] messageHandlers[messageEphemeral] - WASM apply() failed:`, {
225
- error: applyError.message,
226
- ephemeralLength: ephemeralBytes.length,
227
- ephemeralSample: Array.from(ephemeralBytes.slice(0, 20))
228
- });
229
- // If this is a WASM memory error, don't attempt any more ephemeral operations
230
- if (applyError.message && applyError.message.includes('memory access out of bounds')) {
231
- console.warn(`[Client] messageHandlers[messageEphemeral] - CRITICAL: WASM memory corruption in apply(). Stopping ephemeral processing.`);
232
- return null;
233
- }
234
- throw applyError; // Re-throw if not a WASM error
235
- }
236
- return null;
237
- }
238
- catch (error) {
239
- console.warn(`[Client] messageHandlers[messageEphemeral] - ERROR in ephemeral message integrater:`, {
240
- error: error.message,
241
- stack: error.stack,
242
- messageLength: message.ephemeral?.length,
243
- ephemeralSample: message.ephemeral?.slice(0, 10)
244
- });
245
- return null;
246
- }
247
- };
248
- messageHandlers[messageUpdate] = (provider, message, emitSynced) => {
249
- try {
250
- // Apply the update to the local document
251
- const updateBytes = new Uint8Array(message.update);
252
- // Get document state before applying update for comparison
253
- // const beforeVersion = provider.doc.version()
254
- // Import with sender's peerId as origin to mark as remote update
255
- // We don't know the actual sender's peerId, so use a generic remote identifier
256
- // The key point is that it's NOT our local peerId
257
- const importStatus = provider.doc.import(updateBytes);
258
- const afterVersion = provider.doc.version();
259
- // Update our last exported version to include the remote changes
260
- // This ensures we don't re-export remote changes
261
- provider._lastExportedVersion = afterVersion;
262
- if (emitSynced && !provider._synced) {
263
- provider.synced = true;
264
- }
265
- return null; // No response needed
266
- }
267
- catch (error) {
268
- console.warn(`❌ [LORO-UPDATE-ERROR] Failed to apply Loro update:`, error);
269
- return null;
270
- }
271
- };
272
- /**
273
- * @param {WebsocketProvider} provider
274
- * @param {string} reason
275
- */
276
- const permissionDeniedIntegrator = (provider, reason) => console.warn(`Permission denied to access ${provider.url}.\n${reason}`);
277
- /**
278
- * Process incoming message (JSON or binary) and return optional response
279
- */
280
- const processMessage = (provider, data, emitSynced) => {
281
- if (data instanceof ArrayBuffer) {
282
- try {
283
- /*
284
- // Try to decode as UTF-8 string (JSON messages sent as binary)
285
- const decoder = new TextDecoder()
286
- const jsonString = decoder.decode(data)
287
- const message = JSON.parse(jsonString) as LoroWebSocketMessage
288
- const messageHandler = messageHandlers[message.type]
289
- if (messageHandler) {
290
- return messageHandler(provider, message, emitSynced)
291
- } else {
292
- console.warn('Unknown message type:', message.type)
293
- return null
294
- }
295
- */
296
- // If JSON parsing fails, treat as raw binary Loro update
297
- const updateBytes = new Uint8Array(data);
298
- provider.doc.import(updateBytes);
299
- if (emitSynced && !provider._synced) {
300
- provider.synced = true;
301
- }
302
- return null; // No response needed for binary updates
303
- }
304
- catch (error) {
305
- console.warn('Failed to process binary Loro update:', error);
306
- return null;
307
- }
308
- }
309
- else if (typeof data === 'string') {
310
- try {
311
- const message = JSON.parse(data);
312
- const messageHandler = messageHandlers[message.type];
313
- if (messageHandler) {
314
- return messageHandler(provider, message, emitSynced);
315
- }
316
- else {
317
- console.warn('Unknown message type:', message.type);
318
- return null;
319
- }
320
- }
321
- catch (error) {
322
- console.warn('Failed to process JSON message:', error);
323
- return null;
324
- }
325
- }
326
- else if (typeof data === 'object' && data !== null) {
327
- try {
328
- const message = data;
329
- const messageHandler = messageHandlers[message.type];
330
- if (messageHandler) {
331
- return messageHandler(provider, message, emitSynced);
332
- }
333
- else {
334
- console.warn('Unknown message type:', message.type);
335
- return null;
336
- }
337
- }
338
- catch (error) {
339
- console.warn('Failed to process object message:', error);
340
- return null;
341
- }
342
- }
343
- console.warn('Unknown message format:', typeof data);
344
- return null;
345
- };
346
- /**
347
- * Outsource this function so that a new websocket connection is created immediately.
348
- * I suspect that the `ws.onclose` event is not always fired if there are network issues.
349
- *
350
- * @param {WebsocketProvider} provider
351
- * @param {WebSocket} ws
352
- * @param {CloseEvent | null} event
353
- */
354
- const closeWebsocketConnection = (provider, ws, event) => {
355
- if (ws === provider.ws) {
356
- provider.emit('connection-close', [event, provider]);
357
- provider.ws = null;
358
- ws.close();
359
- provider.wsconnecting = false;
360
- if (provider.wsconnected) {
361
- provider.wsconnected = false;
362
- provider.synced = false;
363
- // Clear local ephemeral state on disconnect
364
- provider.ephemeralStore.delete('presence');
365
- provider.ephemeralStore.delete('cursor');
366
- // Clear user-specific state
367
- try {
368
- const peerId = generateClientID(provider.doc);
369
- const userKey = peerId.toString();
370
- provider.ephemeralStore.delete(userKey);
371
- console.log('Disconnect cleanup: removed user key:', userKey);
372
- }
373
- catch (error) {
374
- console.warn('Disconnect cleanup failed:', error.message);
375
- }
376
- provider.emit('status', [{
377
- status: 'disconnected'
378
- }]);
379
- }
380
- else {
381
- provider.wsUnsuccessfulReconnects++;
382
- }
383
- // Start with no reconnect timeout and increase timeout by
384
- // using exponential backoff starting with 100ms
385
- setTimeout(setupWS, math.min(math.pow(2, provider.wsUnsuccessfulReconnects) * 100, provider.maxBackoffTime), provider);
386
- }
387
- };
388
- const sendMessage = (ws, message) => {
389
- if (ws && ws.readyState === ws.OPEN) {
390
- try {
391
- const m = JSON.stringify(message);
392
- ws.send(m);
393
- }
394
- catch (error) {
395
- console.warn('Failed to send message over WebSocket:', error);
396
- }
397
- }
398
- else {
399
- console.warn('WebSocket not open, cannot send message');
400
- }
401
- };
402
- /**
403
- * @param {WebsocketProvider} provider
404
- */
405
- const setupWS = (provider) => {
406
- if (provider.shouldConnect && provider.ws === null) {
407
- const ws = new provider._WS(provider.url, provider.protocols);
408
- ws.binaryType = 'arraybuffer';
409
- provider.ws = ws;
410
- provider.wsconnecting = true;
411
- provider.wsconnected = false;
412
- provider.synced = false;
413
- ws.onmessage = (event) => {
414
- provider.wsLastMessageReceived = time.getUnixTime();
415
- const response = processMessage(provider, event.data, true);
416
- if (response) {
417
- // TODO
418
- // sendMessage(ws, response)
419
- }
420
- };
421
- ws.onerror = (event) => {
422
- provider.emit('connection-error', [event, provider]);
423
- };
424
- ws.onclose = (event) => {
425
- closeWebsocketConnection(provider, ws, event);
426
- };
427
- ws.onopen = () => {
428
- provider.wsLastMessageReceived = time.getUnixTime();
429
- provider.wsconnecting = false;
430
- provider.wsconnected = true;
431
- provider.wsUnsuccessfulReconnects = 0;
432
- provider.emit('status', [{
433
- status: 'connected'
434
- }]);
435
- console.log('✅ WebSocket connection established, requesting initial data');
436
- // Since we're in onopen, we know the WebSocket is ready
437
- // Use sendMessage directly to avoid any race conditions
438
- // Only request snapshot if we haven't already loaded it
439
- if (!provider.snapshotLoaded) {
440
- // First request a snapshot to get the initial document state
441
- const requestId = Math.random().toString(36).substr(2, 9);
442
- const clientId = generateClientID(provider.doc).toString();
443
- const snapshotRequest = {
444
- type: 'query-snapshot',
445
- docId: provider.docId,
446
- clientId: clientId
447
- };
448
- console.log(`🔄 Requesting initial snapshot from server (ID: ${requestId}, clientId: ${clientId}):`, snapshotRequest);
449
- console.log(`🔄 Provider instance ID: ${provider.wsServerUrl}/${provider.docId}, snapshotLoaded: ${provider.snapshotLoaded}`);
450
- sendMessage(ws, snapshotRequest);
451
- }
452
- else {
453
- console.log('📸 Snapshot already loaded, skipping request');
454
- }
455
- // Then request initial ephemeral state from server
456
- const clientId = generateClientID(provider.doc).toString();
457
- const ephemeralRequest = {
458
- type: 'query-ephemeral',
459
- docId: provider.docId,
460
- clientId: clientId
461
- };
462
- sendMessage(ws, ephemeralRequest);
463
- // broadcast local ephemeral state if any
464
- const localState = provider.ephemeralStore.getAllStates();
465
- if (Object.keys(localState).length > 0) {
466
- try {
467
- // Use encodeAll() to encode all ephemeral store data
468
- const encodedData = provider.ephemeralStore.encodeAll();
469
- const ephemeralMessage = {
470
- type: 'ephemeral',
471
- ephemeral: Array.from(encodedData),
472
- docId: provider.docId
473
- };
474
- sendMessage(ws, ephemeralMessage);
475
- }
476
- catch (error) {
477
- console.warn(`[Client] setupWS - MAJOR ERROR in ephemeral process:`, {
478
- error: error.message,
479
- stack: error.stack,
480
- localStateKeys: Object.keys(localState),
481
- storeExists: !!provider.ephemeralStore
482
- });
483
- }
484
- }
485
- };
486
- provider.emit('status', [{
487
- status: 'connecting'
488
- }]);
489
- }
490
- };
491
- /**
492
- * Broadcast JSON message to WebSocket and BroadcastChannel
493
- */
494
- const broadcastMessage = (provider, message) => {
495
- const ws = provider.ws;
496
- if (provider.wsconnected && ws && ws.readyState === ws.OPEN) {
497
- sendMessage(ws, message);
498
- }
499
- else {
500
- console.log('❌ [BROADCAST] WebSocket not ready for sending');
501
- }
502
- if (provider.bcconnected) {
503
- bc.publish(provider.bcChannel, JSON.stringify(message), provider);
504
- }
505
- else {
506
- console.log('📻 [BROADCAST] BroadcastChannel not connected');
507
- }
508
- };
509
- /**
510
- * Websocket Provider for Loro. Creates a websocket connection to sync the shared document.
511
- * The document name is attached to the provided url. I.e. the following example
512
- * creates a websocket connection to http://localhost:1235/my-document-name
513
- */
514
- export class WebsocketProvider extends ObservableV2 {
515
- static globalEphemeralStore = null;
516
- wsServerUrl = '';
517
- docId = '';
518
- doc = null;
519
- _WS = null;
520
- protocols = [];
521
- params = {};
522
- ephemeralStore = null;
523
- awareness = null;
524
- ws = null;
525
- wsconnected = false;
526
- wsconnecting = false;
527
- bcconnected = false;
528
- disableBc = false;
529
- bcChannel = '';
530
- maxBackoffTime = 2500;
531
- wsUnsuccessfulReconnects = 0;
532
- messageHandlers = [];
533
- _synced = false;
534
- wsLastMessageReceived = 0;
535
- shouldConnect = false;
536
- snapshotLoaded = false;
537
- _checkInterval = null;
538
- _resyncInterval = null;
539
- _updateHandler = null;
540
- _ephemeralUpdateIntegrator = null;
541
- _exitIntegrator = null;
542
- _bcSubscriber = null;
543
- _lastExportedVersion = null; // Track last exported version for incremental updates
544
- /**
545
- * @param {string} wsServerUrl
546
- * @param {string} docId
547
- * @param {LoroDoc} doc
548
- * @param {object} opts
549
- * @param {boolean} [opts.connect]
550
- * @param {EphemeralStore} [opts.ephemeralStore]
551
- * @param {Object<string,string>} [opts.params] specify url parameters
552
- * @param {Array<string>} [opts.protocols] specify websocket protocols
553
- * @param {typeof WebSocket} [opts.WebSocketPolyfill] Optionally provide a WebSocket polyfill
554
- * @param {number} [opts.resyncInterval] Request server state every `resyncInterval` milliseconds
555
- * @param {number} [opts.maxBackoffTime] Maximum amount of time to wait before trying to reconnect (we try to reconnect using exponential backoff)
556
- * @param {boolean} [opts.disableBc] Disable cross-tab BroadcastChannel communication
557
- */
558
- constructor(wsServerUrl, docId, doc, { connect = true, ephemeralStore = undefined, params = {}, protocols = [], WebSocketPolyfill = WebSocket, resyncInterval = -1, maxBackoffTime = 2500, disableBc = false } = {}) {
559
- super();
560
- // ensure that serverUrl does not end with /
561
- while (wsServerUrl[wsServerUrl.length - 1] === '/') {
562
- wsServerUrl = wsServerUrl.slice(0, wsServerUrl.length - 1);
563
- }
564
- this.wsServerUrl = wsServerUrl;
565
- this.bcChannel = wsServerUrl + '/' + docId;
566
- this.maxBackoffTime = maxBackoffTime;
567
- /**
568
- * The specified url parameters. This can be safely updated. The changed parameters will be used
569
- * when a new connection is established.
570
- * @type {Object<string,string>}
571
- */
572
- this.params = params;
573
- this.protocols = protocols;
574
- this.docId = docId;
575
- this.doc = doc;
576
- this._WS = WebSocketPolyfill;
577
- // Create or reuse persistent ephemeral store for the entire user session
578
- try {
579
- if (ephemeralStore) {
580
- // Use provided ephemeral store (already persistent)
581
- this.ephemeralStore = ephemeralStore;
582
- }
583
- else {
584
- // Create or reuse global ephemeral store for session persistence
585
- if (!WebsocketProvider.globalEphemeralStore) {
586
- WebsocketProvider.globalEphemeralStore = new EphemeralStore(300000); // 5 minute timeout
587
- console.log('🆕 Created new global EphemeralStore');
588
- }
589
- else {
590
- console.log('♻️ Reusing existing global EphemeralStore - cleaning up stale user states');
591
- // Clean up all existing user states when reusing store to prevent accumulation
592
- const allStates = WebsocketProvider.globalEphemeralStore.getAllStates();
593
- Object.keys(allStates).forEach(key => {
594
- // Keys are now direct client ID strings, check if it's a valid client ID
595
- const clientId = parseInt(key, 10);
596
- if (!isNaN(clientId)) {
597
- WebsocketProvider.globalEphemeralStore.delete(key);
598
- console.log('🧹 Cleaned up stale user state:', key);
599
- }
600
- });
601
- }
602
- this.ephemeralStore = WebsocketProvider.globalEphemeralStore;
603
- }
604
- }
605
- catch (error) {
606
- console.warn(`[Client] WebsocketProvider constructor - ERROR setting up EphemeralStore:`, {
607
- error: error.message,
608
- stack: error.stack,
609
- docId
610
- });
611
- throw error;
612
- }
613
- // Create awareness adapter that wraps ephemeral store
614
- this.awareness = new AwarenessAdapter(this.ephemeralStore, this.doc);
615
- this.wsconnected = false;
616
- this.wsconnecting = false;
617
- this.bcconnected = false;
618
- this.disableBc = disableBc;
619
- this.wsUnsuccessfulReconnects = 0;
620
- /**
621
- * @type {boolean}
622
- */
623
- this._synced = false;
624
- /**
625
- * @type {WebSocket?}
626
- */
627
- this.ws = null;
628
- this.wsLastMessageReceived = 0;
629
- /**
630
- * Whether to connect to other peers or not
631
- * @type {boolean}
632
- */
633
- this.shouldConnect = connect;
634
- /**
635
- * @type {number}
636
- */
637
- this._resyncInterval = 0;
638
- if (resyncInterval > 0) {
639
- this._resyncInterval = /** @type {any} */ (setInterval(() => {
640
- if (this.ws && this.ws.readyState === WebSocket.OPEN) {
641
- // Request fresh ephemeral state from server with client ID
642
- const clientId = generateClientID(this.doc).toString();
643
- const queryMessage = {
644
- type: 'query-ephemeral',
645
- docId: this.docId,
646
- clientId: clientId
647
- };
648
- sendMessage(this.ws, queryMessage);
649
- }
650
- }, resyncInterval));
651
- }
652
- /**
653
- * @param {string | object} data
654
- * @param {any} origin
655
- */
656
- this._bcSubscriber = (data, origin) => {
657
- if (origin !== this) {
658
- const response = processMessage(this, data, false);
659
- if (response) {
660
- bc.publish(this.bcChannel, response, this);
661
- }
662
- }
663
- };
664
- /**
665
- * Listens to Loro Loro updates and sends them to remote peers (ws and broadcastchannel)
666
- * @param {Uint8Array} update
667
- * @param {any} origin
668
- */
669
- this._updateHandler = (update) => {
670
- // This integrater is only called for local changes that need to be broadcast
671
- const updateMessage = {
672
- type: 'update',
673
- update: Array.from(update),
674
- docId: this.docId
675
- };
676
- broadcastMessage(this, updateMessage);
677
- };
678
- // Document update integrater - called when Loro emits document change events
679
- /**
680
- * @param {EphemeralStoreEvent} event - EphemeralStoreEvent with added, updated, removed arrays
681
- */
682
- this._ephemeralUpdateIntegrator = (event) => {
683
- // Only broadcast if there are actual changes
684
- if (event.added.length > 0 || event.updated.length > 0 || event.removed.length > 0) {
685
- try {
686
- // Use encodeAll() to encode all ephemeral store data
687
- const encodedData = this.ephemeralStore.encodeAll();
688
- const ephemeralMessage = {
689
- type: 'ephemeral',
690
- ephemeral: Array.from(encodedData),
691
- docId: this.docId
692
- };
693
- broadcastMessage(this, ephemeralMessage);
694
- }
695
- catch (error) {
696
- console.warn(`[Client] _ephemeralUpdateIntegrator - ERROR:`, error.message);
697
- // Fallback: skip this update rather than crash
698
- }
699
- }
700
- };
701
- this._exitIntegrator = () => {
702
- // Clear only our local ephemeral state on exit, don't destroy the global store
703
- if (this.ephemeralStore && this.awareness) {
704
- try {
705
- const peerId = generateClientID(this.doc);
706
- const userKey = peerId.toString();
707
- this.ephemeralStore.delete(userKey);
708
- }
709
- catch (error) {
710
- console.warn(`[Client] Process exit - Could not clear user state:`, error.message);
711
- }
712
- }
713
- };
714
- if (env.isNode && typeof process !== 'undefined') {
715
- process.on('exit', this._exitIntegrator);
716
- }
717
- this.ephemeralStore.subscribe(this._ephemeralUpdateIntegrator);
718
- // Initialize the last exported version to current document version
719
- this._lastExportedVersion = this.doc.version();
720
- // Use Loro's native event system to listen for document changes
721
- try {
722
- /*
723
- this.doc.subscribe((event: LoroEventBatch) => {
724
- try {
725
- const afterCommitVersion = this.doc.version()
726
- const update = this.doc.export({
727
- mode: 'update',
728
- from: this._lastExportedVersion
729
- });
730
-
731
- if (update.length > 0) {
732
- this._updateHandler(update);
733
- // Update the last exported version to current version
734
- this._lastExportedVersion = afterCommitVersion
735
- } else {
736
- console.warn(`[WEBSOCKET-PROVIDER] No incremental update available - versions might be the same`);
737
- }
738
- } catch (error) {
739
- console.warn(`[WEBSOCKET-PROVIDER] Error exporting incremental update:`, error);
740
- }
741
- });
742
- */
743
- this.doc.subscribeLocalUpdates((update) => {
744
- try {
745
- this._updateHandler(update);
746
- }
747
- catch (error) {
748
- console.warn(`[WEBSOCKET-PROVIDER] Error exporting incremental update:`, error);
749
- }
750
- });
751
- }
752
- catch (error) {
753
- console.warn(`[Client] ERROR setting up Loro document subscription:`, error);
754
- }
755
- this._checkInterval = (setInterval(() => {
756
- if (this.wsconnected &&
757
- messageReconnectTimeoutMs <
758
- time.getUnixTime() - this.wsLastMessageReceived) {
759
- // no message received in a long time - not even your own ephemeral
760
- // updates (which are updated every 15 seconds)
761
- closeWebsocketConnection(this, this.ws, null);
762
- }
763
- }, messageReconnectTimeoutMs / 10));
764
- if (connect) {
765
- this.connect();
766
- }
767
- }
768
- get url() {
769
- const encodedParams = url.encodeQueryParams(this.params);
770
- return this.wsServerUrl + '/' + this.docId +
771
- (encodedParams.length === 0 ? '' : '?' + encodedParams);
772
- }
773
- /**
774
- * @type {boolean}
775
- */
776
- get synced() {
777
- return this._synced;
778
- }
779
- set synced(state) {
780
- if (this._synced !== state) {
781
- this._synced = state;
782
- super.emit('synced', [state]);
783
- super.emit('sync', [state]);
784
- }
785
- }
786
- destroy() {
787
- if (this._resyncInterval !== 0) {
788
- clearInterval(this._resyncInterval);
789
- }
790
- clearInterval(this._checkInterval);
791
- this.disconnect();
792
- if (env.isNode && typeof process !== 'undefined') {
793
- process.off('exit', this._exitIntegrator);
794
- }
795
- // DON'T destroy the ephemeral store - it's shared across the session
796
- // Only clear our local state from it
797
- if (this.ephemeralStore && this.awareness) {
798
- try {
799
- const peerId = generateClientID(this.doc);
800
- const userKey = peerId.toString();
801
- this.ephemeralStore.delete(userKey);
802
- }
803
- catch (error) {
804
- console.warn(`[Client] WebsocketProvider.destroy - Could not clear user state:`, error.message);
805
- }
806
- }
807
- // Note: LoroDoc doesn't have event listeners to remove
808
- super.destroy();
809
- }
810
- connectBc() {
811
- if (this.disableBc) {
812
- return;
813
- }
814
- if (!this.bcconnected) {
815
- bc.subscribe(this.bcChannel, this._bcSubscriber);
816
- this.bcconnected = true;
817
- }
818
- // Note: BroadcastChannel snapshot sharing removed - only WebSocket queries supported
819
- // Query ephemeral state from other tabs
820
- const clientId = generateClientID(this.doc).toString();
821
- const queryMessage = {
822
- type: 'query-ephemeral',
823
- docId: this.docId,
824
- clientId: clientId
825
- };
826
- bc.publish(this.bcChannel, JSON.stringify(queryMessage), this);
827
- // Broadcast local ephemeral state using container approach
828
- const localState = this.ephemeralStore.getAllStates();
829
- if (Object.keys(localState).length > 0) {
830
- try {
831
- // Use encodeAll() to encode all ephemeral store data
832
- const encodedData = this.ephemeralStore.encodeAll();
833
- const ephemeralMessage = {
834
- type: 'ephemeral',
835
- ephemeral: Array.from(encodedData),
836
- docId: this.docId
837
- };
838
- bc.publish(this.bcChannel, JSON.stringify(ephemeralMessage), this);
839
- }
840
- catch (error) {
841
- console.warn('Error broadcasting ephemeral state in connectBc:', error.message);
842
- }
843
- }
844
- }
845
- disconnectBc() {
846
- // broadcast message with local ephemeral state cleared (indicating disconnect)
847
- this.ephemeralStore.delete('presence');
848
- this.ephemeralStore.delete('cursor');
849
- // Clear user-specific state
850
- try {
851
- const peerId = generateClientID(this.doc);
852
- const userKey = peerId.toString();
853
- this.ephemeralStore.delete(userKey);
854
- console.log('Broadcast disconnect cleanup: removed user key:', userKey);
855
- }
856
- catch (error) {
857
- console.warn('Broadcast disconnect cleanup failed:', error.message);
858
- }
859
- try {
860
- // Use encodeAll() to encode ephemeral store data for disconnect broadcast
861
- const encodedData = this.ephemeralStore.encodeAll();
862
- const ephemeralMessage = {
863
- type: 'ephemeral',
864
- ephemeral: Array.from(encodedData),
865
- docId: this.docId
866
- };
867
- broadcastMessage(this, ephemeralMessage);
868
- }
869
- catch (error) {
870
- console.warn('Error broadcasting disconnect in disconnectBc:', error.message);
871
- }
872
- if (this.bcconnected) {
873
- bc.unsubscribe(this.bcChannel, this._bcSubscriber);
874
- this.bcconnected = false;
875
- }
876
- }
877
- disconnect() {
878
- this.shouldConnect = false;
879
- this.disconnectBc();
880
- if (this.ws !== null) {
881
- closeWebsocketConnection(this, this.ws, null);
882
- }
883
- }
884
- connect() {
885
- this.shouldConnect = true;
886
- if (!this.wsconnected && this.ws === null) {
887
- setupWS(this);
888
- this.connectBc();
889
- }
890
- }
891
- /**
892
- * Manually send a Loro document update to connected peers
893
- * Call this method after making changes to the LoroDoc
894
- * @param {Uint8Array} update The update bytes from LoroDoc
895
- */
896
- sendUpdate(update) {
897
- this._updateHandler(update, null);
898
- }
899
- /**
900
- * Force cleanup of stale ephemeral states (for debugging)
901
- * Removes user states that haven't been active for more than 5 minutes
902
- */
903
- cleanupStaleStates() {
904
- if (this.awareness && typeof this.awareness.forceCleanupStaleStates === 'function') {
905
- this.awareness.forceCleanupStaleStates();
906
- }
907
- else {
908
- console.warn('Cleanup method not available on awareness provider');
909
- }
910
- }
911
- }