@datalayer/lexical-loro 0.1.0 → 0.2.2

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