@datalayer/lexical-loro 0.0.7 → 0.1.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 (406) hide show
  1. package/README.md +24 -137
  2. package/lib/App.d.ts +2 -0
  3. package/lib/App.js +141 -0
  4. package/lib/Editor.d.ts +2 -0
  5. package/lib/Editor.js +111 -0
  6. package/lib/Settings.d.ts +2 -0
  7. package/lib/Settings.js +57 -0
  8. package/lib/appSettings.d.ts +36 -0
  9. package/lib/appSettings.js +44 -0
  10. package/lib/collab/loro/Bindings.d.ts +41 -0
  11. package/lib/collab/loro/Bindings.js +95 -0
  12. package/lib/collab/loro/Debug.d.ts +33 -0
  13. package/lib/collab/loro/Debug.js +448 -0
  14. package/lib/collab/loro/LexicalCollaborationContext.d.ts +19 -0
  15. package/lib/collab/loro/LexicalCollaborationContext.js +48 -0
  16. package/lib/collab/loro/LexicalCollaborationPlugin.d.ts +24 -0
  17. package/lib/collab/loro/LexicalCollaborationPlugin.js +83 -0
  18. package/lib/collab/loro/State.d.ts +53 -0
  19. package/lib/collab/loro/State.js +90 -0
  20. package/lib/collab/loro/components/LoroCollaborationUI.d.ts +13 -0
  21. package/lib/collab/loro/components/LoroCollaborationUI.js +9 -0
  22. package/lib/collab/loro/components/LoroCollaborators.d.ts +8 -0
  23. package/lib/collab/loro/components/LoroCollaborators.js +97 -0
  24. package/lib/collab/loro/components/index.d.ts +2 -0
  25. package/lib/collab/loro/components/index.js +2 -0
  26. package/lib/collab/loro/index.d.ts +6 -0
  27. package/lib/collab/loro/index.js +6 -0
  28. package/lib/collab/loro/integrators/BaseIntegrator.d.ts +14 -0
  29. package/lib/collab/loro/integrators/BaseIntegrator.js +1 -0
  30. package/lib/collab/loro/integrators/CounterIntegrator.d.ts +23 -0
  31. package/lib/collab/loro/integrators/CounterIntegrator.js +40 -0
  32. package/lib/collab/loro/integrators/ListIntegrator.d.ts +23 -0
  33. package/lib/collab/loro/integrators/ListIntegrator.js +49 -0
  34. package/lib/collab/loro/integrators/MapIntegrator.d.ts +24 -0
  35. package/lib/collab/loro/integrators/MapIntegrator.js +177 -0
  36. package/lib/collab/loro/integrators/TextIntegrator.d.ts +25 -0
  37. package/lib/collab/loro/integrators/TextIntegrator.js +51 -0
  38. package/lib/collab/loro/integrators/TreeIntegrator.d.ts +25 -0
  39. package/lib/collab/loro/integrators/TreeIntegrator.js +201 -0
  40. package/lib/collab/loro/nodes/NodeFactory.d.ts +8 -0
  41. package/lib/collab/loro/nodes/NodeFactory.js +105 -0
  42. package/lib/collab/loro/nodes/NodesMapper.d.ts +111 -0
  43. package/lib/collab/loro/nodes/NodesMapper.js +258 -0
  44. package/lib/collab/loro/propagators/DecoratorNodePropagator.d.ts +60 -0
  45. package/lib/collab/loro/propagators/DecoratorNodePropagator.js +302 -0
  46. package/lib/collab/loro/propagators/ElementNodePropagator.d.ts +62 -0
  47. package/lib/collab/loro/propagators/ElementNodePropagator.js +335 -0
  48. package/lib/collab/loro/propagators/LineBreakNodePropagator.d.ts +57 -0
  49. package/lib/collab/loro/propagators/LineBreakNodePropagator.js +196 -0
  50. package/lib/collab/loro/propagators/RootNodePropagator.d.ts +55 -0
  51. package/lib/collab/loro/propagators/RootNodePropagator.js +168 -0
  52. package/lib/collab/loro/propagators/TextNodePropagator.d.ts +60 -0
  53. package/lib/collab/loro/propagators/TextNodePropagator.js +434 -0
  54. package/lib/collab/loro/propagators/index.d.ts +49 -0
  55. package/lib/collab/loro/propagators/index.js +32 -0
  56. package/lib/collab/loro/provider/websocket.d.ts +116 -0
  57. package/lib/collab/loro/provider/websocket.js +907 -0
  58. package/lib/collab/loro/servers/index.d.ts +0 -0
  59. package/lib/collab/loro/servers/index.js +0 -0
  60. package/lib/collab/loro/servers/ws/callback.d.ts +5 -0
  61. package/lib/collab/loro/servers/ws/callback.js +85 -0
  62. package/lib/collab/loro/servers/ws/server.d.ts +2 -0
  63. package/lib/collab/loro/servers/ws/server.js +25 -0
  64. package/lib/collab/loro/servers/ws/utils.d.ts +40 -0
  65. package/lib/collab/loro/servers/ws/utils.js +513 -0
  66. package/lib/collab/loro/sync/SyncCursors.d.ts +32 -0
  67. package/lib/collab/loro/sync/SyncCursors.js +435 -0
  68. package/lib/collab/loro/sync/SyncLexicalToLoro.d.ts +4 -0
  69. package/lib/collab/loro/sync/SyncLexicalToLoro.js +80 -0
  70. package/lib/collab/loro/sync/SyncLoroToLexical.d.ts +5 -0
  71. package/lib/collab/loro/sync/SyncLoroToLexical.js +96 -0
  72. package/lib/collab/loro/types/LexicalNodeData.d.ts +32 -0
  73. package/lib/collab/loro/types/LexicalNodeData.js +71 -0
  74. package/lib/collab/loro/useCollaboration.d.ts +12 -0
  75. package/lib/collab/loro/useCollaboration.js +248 -0
  76. package/lib/collab/loro/utils/InitialContent.d.ts +64 -0
  77. package/lib/collab/loro/utils/InitialContent.js +109 -0
  78. package/lib/collab/loro/utils/LexicalToLoro.d.ts +18 -0
  79. package/lib/collab/loro/utils/LexicalToLoro.js +96 -0
  80. package/lib/collab/loro/utils/Utils.d.ts +44 -0
  81. package/lib/collab/loro/utils/Utils.js +153 -0
  82. package/lib/collab/loro/wsProvider.d.ts +8 -0
  83. package/lib/collab/loro/wsProvider.js +31 -0
  84. package/lib/collab/utils/invariant.d.ts +1 -0
  85. package/lib/collab/utils/invariant.js +11 -0
  86. package/lib/collab/utils/simpleDiffWithCursor.d.ts +5 -0
  87. package/lib/collab/utils/simpleDiffWithCursor.js +31 -0
  88. package/lib/collab/yjs/Bindings.d.ts +23 -0
  89. package/lib/collab/yjs/Bindings.js +26 -0
  90. package/lib/collab/yjs/Debug.d.ts +23 -0
  91. package/lib/collab/yjs/Debug.js +213 -0
  92. package/lib/collab/yjs/LexicalCollaborationContext.d.ts +10 -0
  93. package/lib/collab/yjs/LexicalCollaborationContext.js +37 -0
  94. package/lib/collab/yjs/LexicalCollaborationPlugin.d.ts +21 -0
  95. package/lib/collab/yjs/LexicalCollaborationPlugin.js +63 -0
  96. package/lib/collab/yjs/State.d.ts +51 -0
  97. package/lib/collab/yjs/State.js +35 -0
  98. package/lib/collab/yjs/nodes/AnyCollabNode.d.ts +5 -0
  99. package/lib/collab/yjs/nodes/AnyCollabNode.js +1 -0
  100. package/lib/collab/yjs/nodes/CollabDecoratorNode.d.ts +22 -0
  101. package/lib/collab/yjs/nodes/CollabDecoratorNode.js +64 -0
  102. package/lib/collab/yjs/nodes/CollabElementNode.d.ts +40 -0
  103. package/lib/collab/yjs/nodes/CollabElementNode.js +462 -0
  104. package/lib/collab/yjs/nodes/CollabLineBreakNode.d.ts +19 -0
  105. package/lib/collab/yjs/nodes/CollabLineBreakNode.js +44 -0
  106. package/lib/collab/yjs/nodes/CollabTextNode.d.ts +25 -0
  107. package/lib/collab/yjs/nodes/CollabTextNode.js +103 -0
  108. package/lib/collab/yjs/provider/websocket.d.ts +88 -0
  109. package/lib/collab/yjs/provider/websocket.js +415 -0
  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 +5 -0
  113. package/lib/collab/yjs/servers/ws/callback.js +72 -0
  114. package/lib/collab/yjs/servers/ws/server.d.ts +2 -0
  115. package/lib/collab/yjs/servers/ws/server.js +25 -0
  116. package/lib/collab/yjs/servers/ws/utils.d.ts +49 -0
  117. package/lib/collab/yjs/servers/ws/utils.js +284 -0
  118. package/lib/collab/yjs/sync/SyncCursors.d.ts +39 -0
  119. package/lib/collab/yjs/sync/SyncCursors.js +351 -0
  120. package/lib/collab/yjs/sync/SyncEditorStates.d.ts +10 -0
  121. package/lib/collab/yjs/sync/SyncEditorStates.js +200 -0
  122. package/lib/collab/yjs/useCollaboration.d.ts +12 -0
  123. package/lib/collab/yjs/useCollaboration.js +255 -0
  124. package/lib/collab/yjs/utils/Utils.d.ts +25 -0
  125. package/lib/collab/yjs/utils/Utils.js +402 -0
  126. package/lib/collab/yjs/wsProvider.d.ts +3 -0
  127. package/lib/collab/yjs/wsProvider.js +21 -0
  128. package/lib/commenting/index.d.ts +41 -0
  129. package/lib/commenting/index.js +324 -0
  130. package/lib/context/FlashMessageContext.d.ts +7 -0
  131. package/lib/context/FlashMessageContext.js +24 -0
  132. package/lib/context/SettingsContext.d.ts +12 -0
  133. package/lib/context/SettingsContext.js +38 -0
  134. package/lib/context/SharedHistoryContext.d.ts +11 -0
  135. package/lib/context/SharedHistoryContext.js +11 -0
  136. package/lib/context/ToolbarContext.d.ts +65 -0
  137. package/lib/context/ToolbarContext.js +84 -0
  138. package/lib/demo.d.ts +12 -0
  139. package/lib/demo.js +41 -0
  140. package/lib/hooks/useFlashMessage.d.ts +2 -0
  141. package/lib/hooks/useFlashMessage.js +4 -0
  142. package/lib/hooks/useModal.d.ts +5 -0
  143. package/lib/hooks/useModal.js +26 -0
  144. package/lib/hooks/useReport.d.ts +1 -0
  145. package/lib/hooks/useReport.js +46 -0
  146. package/lib/index.d.ts +1 -1
  147. package/lib/index.js +1 -5
  148. package/lib/nodes/AutocompleteNode.d.ts +27 -0
  149. package/lib/nodes/AutocompleteNode.js +56 -0
  150. package/lib/nodes/CounterComponent.d.ts +6 -0
  151. package/lib/nodes/CounterComponent.js +137 -0
  152. package/lib/nodes/CounterNode.d.ts +23 -0
  153. package/lib/nodes/CounterNode.js +47 -0
  154. package/lib/nodes/DateTimeNode/DateTimeComponent.d.ts +8 -0
  155. package/lib/nodes/DateTimeNode/DateTimeComponent.js +119 -0
  156. package/lib/nodes/DateTimeNode/DateTimeNode.d.ts +27 -0
  157. package/lib/nodes/DateTimeNode/DateTimeNode.js +82 -0
  158. package/lib/nodes/EmojiNode.d.ts +18 -0
  159. package/lib/nodes/EmojiNode.js +50 -0
  160. package/lib/nodes/EquationComponent.d.ts +9 -0
  161. package/lib/nodes/EquationComponent.js +75 -0
  162. package/lib/nodes/EquationNode.d.ts +26 -0
  163. package/lib/nodes/EquationNode.js +109 -0
  164. package/lib/nodes/ExcalidrawNode/ExcalidrawComponent.d.ts +8 -0
  165. package/lib/nodes/ExcalidrawNode/ExcalidrawComponent.js +110 -0
  166. package/lib/nodes/ExcalidrawNode/ExcalidrawImage.d.ts +50 -0
  167. package/lib/nodes/ExcalidrawNode/ExcalidrawImage.js +55 -0
  168. package/lib/nodes/ExcalidrawNode/index.d.ts +32 -0
  169. package/lib/nodes/ExcalidrawNode/index.js +117 -0
  170. package/lib/nodes/FigmaNode.d.ts +20 -0
  171. package/lib/nodes/FigmaNode.js +52 -0
  172. package/lib/nodes/ImageComponent.d.ts +16 -0
  173. package/lib/nodes/ImageComponent.js +272 -0
  174. package/lib/nodes/ImageNode.d.ts +50 -0
  175. package/lib/nodes/ImageNode.js +151 -0
  176. package/lib/nodes/InlineImageNode/InlineImageComponent.d.ts +26 -0
  177. package/lib/nodes/InlineImageNode/InlineImageComponent.js +161 -0
  178. package/lib/nodes/InlineImageNode/InlineImageNode.d.ts +59 -0
  179. package/lib/nodes/InlineImageNode/InlineImageNode.js +162 -0
  180. package/lib/nodes/KeywordNode.d.ts +14 -0
  181. package/lib/nodes/KeywordNode.js +33 -0
  182. package/lib/nodes/LayoutContainerNode.d.ts +24 -0
  183. package/lib/nodes/LayoutContainerNode.js +91 -0
  184. package/lib/nodes/LayoutItemNode.d.ts +16 -0
  185. package/lib/nodes/LayoutItemNode.js +65 -0
  186. package/lib/nodes/MentionNode.d.ts +20 -0
  187. package/lib/nodes/MentionNode.js +81 -0
  188. package/lib/nodes/PageBreakNode/index.d.ts +17 -0
  189. package/lib/nodes/PageBreakNode/index.js +83 -0
  190. package/lib/nodes/PlaygroundNodes.d.ts +3 -0
  191. package/lib/nodes/PlaygroundNodes.js +71 -0
  192. package/lib/nodes/PollComponent.d.ts +9 -0
  193. package/lib/nodes/PollComponent.js +85 -0
  194. package/lib/nodes/PollNode.d.ts +43 -0
  195. package/lib/nodes/PollNode.js +153 -0
  196. package/lib/nodes/SpecialTextNode.d.ts +24 -0
  197. package/lib/nodes/SpecialTextNode.js +50 -0
  198. package/lib/nodes/StickyComponent.d.ts +10 -0
  199. package/lib/nodes/StickyComponent.js +162 -0
  200. package/lib/nodes/StickyNode.d.ts +31 -0
  201. package/lib/nodes/StickyNode.js +76 -0
  202. package/lib/nodes/TweetNode.d.ts +21 -0
  203. package/lib/nodes/TweetNode.js +119 -0
  204. package/lib/nodes/YouTubeNode.d.ts +22 -0
  205. package/lib/nodes/YouTubeNode.js +84 -0
  206. package/lib/plugins/ActionsPlugin/index.d.ts +5 -0
  207. package/lib/plugins/ActionsPlugin/index.js +168 -0
  208. package/lib/plugins/AutoEmbedPlugin/index.d.ts +19 -0
  209. package/lib/plugins/AutoEmbedPlugin/index.js +158 -0
  210. package/lib/plugins/AutoLinkPlugin/index.d.ts +2 -0
  211. package/lib/plugins/AutoLinkPlugin/index.js +15 -0
  212. package/lib/plugins/AutocompletePlugin/index.d.ts +10 -0
  213. package/lib/plugins/AutocompletePlugin/index.js +2473 -0
  214. package/lib/plugins/CodeActionMenuPlugin/components/CopyButton/index.d.ts +7 -0
  215. package/lib/plugins/CodeActionMenuPlugin/components/CopyButton/index.js +42 -0
  216. package/lib/plugins/CodeActionMenuPlugin/components/PrettierButton/index.d.ts +17 -0
  217. package/lib/plugins/CodeActionMenuPlugin/components/PrettierButton/index.js +111 -0
  218. package/lib/plugins/CodeActionMenuPlugin/index.d.ts +5 -0
  219. package/lib/plugins/CodeActionMenuPlugin/index.js +104 -0
  220. package/lib/plugins/CodeActionMenuPlugin/utils.d.ts +1 -0
  221. package/lib/plugins/CodeActionMenuPlugin/utils.js +18 -0
  222. package/lib/plugins/CodeHighlightPrismPlugin/index.d.ts +2 -0
  223. package/lib/plugins/CodeHighlightPrismPlugin/index.js +10 -0
  224. package/lib/plugins/CodeHighlightShikiPlugin/index.d.ts +2 -0
  225. package/lib/plugins/CodeHighlightShikiPlugin/index.js +10 -0
  226. package/lib/plugins/CollapsiblePlugin/CollapsibleContainerNode.d.ts +25 -0
  227. package/lib/plugins/CollapsiblePlugin/CollapsibleContainerNode.js +131 -0
  228. package/lib/plugins/CollapsiblePlugin/CollapsibleContentNode.d.ts +16 -0
  229. package/lib/plugins/CollapsiblePlugin/CollapsibleContentNode.js +79 -0
  230. package/lib/plugins/CollapsiblePlugin/CollapsibleTitleNode.d.ts +16 -0
  231. package/lib/plugins/CollapsiblePlugin/CollapsibleTitleNode.js +81 -0
  232. package/lib/plugins/CollapsiblePlugin/CollapsibleUtils.d.ts +2 -0
  233. package/lib/plugins/CollapsiblePlugin/CollapsibleUtils.js +8 -0
  234. package/lib/plugins/CollapsiblePlugin/index.d.ts +3 -0
  235. package/lib/plugins/CollapsiblePlugin/index.js +128 -0
  236. package/lib/plugins/CommentPlugin/index.d.ts +9 -0
  237. package/lib/plugins/CommentPlugin/index.js +460 -0
  238. package/lib/plugins/ComponentPickerPlugin/index.d.ts +2 -0
  239. package/lib/plugins/ComponentPickerPlugin/index.js +276 -0
  240. package/lib/plugins/ContextMenuPlugin/index.d.ts +2 -0
  241. package/lib/plugins/ContextMenuPlugin/index.js +112 -0
  242. package/lib/plugins/CounterPlugin/index.d.ts +3 -0
  243. package/lib/plugins/CounterPlugin/index.js +20 -0
  244. package/lib/plugins/DatalayerPlugin/index.d.ts +2 -0
  245. package/lib/plugins/DatalayerPlugin/index.js +218 -0
  246. package/lib/plugins/DateTimePlugin/index.d.ts +8 -0
  247. package/lib/plugins/DateTimePlugin/index.js +24 -0
  248. package/lib/plugins/DocsPlugin/index.d.ts +2 -0
  249. package/lib/plugins/DocsPlugin/index.js +4 -0
  250. package/lib/plugins/DragDropPastePlugin/index.d.ts +1 -0
  251. package/lib/plugins/DragDropPastePlugin/index.js +33 -0
  252. package/lib/plugins/DraggableBlockPlugin/index.d.ts +12 -0
  253. package/lib/plugins/DraggableBlockPlugin/index.js +36 -0
  254. package/lib/plugins/EmojiPickerPlugin/index.d.ts +1 -0
  255. package/lib/plugins/EmojiPickerPlugin/index.js +80 -0
  256. package/lib/plugins/EmojisPlugin/index.d.ts +2 -0
  257. package/lib/plugins/EmojisPlugin/index.js +52 -0
  258. package/lib/plugins/EquationsPlugin/index.d.ts +14 -0
  259. package/lib/plugins/EquationsPlugin/index.js +34 -0
  260. package/lib/plugins/ExcalidrawPlugin/index.d.ts +5 -0
  261. package/lib/plugins/ExcalidrawPlugin/index.js +44 -0
  262. package/lib/plugins/FigmaPlugin/index.d.ts +4 -0
  263. package/lib/plugins/FigmaPlugin/index.js +20 -0
  264. package/lib/plugins/FloatingLinkEditorPlugin/index.d.ts +15 -0
  265. package/lib/plugins/FloatingLinkEditorPlugin/index.js +280 -0
  266. package/lib/plugins/FloatingTextFormatToolbarPlugin/index.d.ts +7 -0
  267. package/lib/plugins/FloatingTextFormatToolbarPlugin/index.js +219 -0
  268. package/lib/plugins/ImagesPlugin/index.d.ts +24 -0
  269. package/lib/plugins/ImagesPlugin/index.js +195 -0
  270. package/lib/plugins/InlineImagePlugin/index.d.ts +17 -0
  271. package/lib/plugins/InlineImagePlugin/index.js +180 -0
  272. package/lib/plugins/KeywordsPlugin/index.d.ts +2 -0
  273. package/lib/plugins/KeywordsPlugin/index.js +31 -0
  274. package/lib/plugins/LayoutPlugin/InsertLayoutDialog.d.ts +6 -0
  275. package/lib/plugins/LayoutPlugin/InsertLayoutDialog.js +21 -0
  276. package/lib/plugins/LayoutPlugin/LayoutPlugin.d.ts +7 -0
  277. package/lib/plugins/LayoutPlugin/LayoutPlugin.js +131 -0
  278. package/lib/plugins/LinkPlugin/index.d.ts +6 -0
  279. package/lib/plugins/LinkPlugin/index.js +11 -0
  280. package/lib/plugins/MarkdownShortcutPlugin/index.d.ts +2 -0
  281. package/lib/plugins/MarkdownShortcutPlugin/index.js +6 -0
  282. package/lib/plugins/MarkdownTransformers/index.d.ts +8 -0
  283. package/lib/plugins/MarkdownTransformers/index.js +234 -0
  284. package/lib/plugins/MaxLengthPlugin/index.d.ts +3 -0
  285. package/lib/plugins/MaxLengthPlugin/index.js +37 -0
  286. package/lib/plugins/MentionsPlugin/index.d.ts +2 -0
  287. package/lib/plugins/MentionsPlugin/index.js +564 -0
  288. package/lib/plugins/PageBreakPlugin/index.d.ts +4 -0
  289. package/lib/plugins/PageBreakPlugin/index.js +27 -0
  290. package/lib/plugins/PasteLogPlugin/index.d.ts +2 -0
  291. package/lib/plugins/PasteLogPlugin/index.js +27 -0
  292. package/lib/plugins/PollPlugin/index.d.ts +8 -0
  293. package/lib/plugins/PollPlugin/index.js +38 -0
  294. package/lib/plugins/ShortcutsPlugin/index.d.ts +6 -0
  295. package/lib/plugins/ShortcutsPlugin/index.js +112 -0
  296. package/lib/plugins/ShortcutsPlugin/shortcuts.d.ts +59 -0
  297. package/lib/plugins/ShortcutsPlugin/shortcuts.js +169 -0
  298. package/lib/plugins/SpecialTextPlugin/index.d.ts +2 -0
  299. package/lib/plugins/SpecialTextPlugin/index.js +46 -0
  300. package/lib/plugins/SpeechToTextPlugin/index.d.ts +5 -0
  301. package/lib/plugins/SpeechToTextPlugin/index.js +82 -0
  302. package/lib/plugins/StickyPlugin/index.d.ts +2 -0
  303. package/lib/plugins/StickyPlugin/index.js +12 -0
  304. package/lib/plugins/TabFocusPlugin/index.d.ts +1 -0
  305. package/lib/plugins/TabFocusPlugin/index.js +34 -0
  306. package/lib/plugins/TableActionMenuPlugin/index.d.ts +5 -0
  307. package/lib/plugins/TableActionMenuPlugin/index.js +492 -0
  308. package/lib/plugins/TableCellResizer/index.d.ts +3 -0
  309. package/lib/plugins/TableCellResizer/index.js +297 -0
  310. package/lib/plugins/TableHoverActionsPlugin/index.d.ts +4 -0
  311. package/lib/plugins/TableHoverActionsPlugin/index.js +188 -0
  312. package/lib/plugins/TableOfContentsPlugin/index.d.ts +2 -0
  313. package/lib/plugins/TableOfContentsPlugin/index.js +116 -0
  314. package/lib/plugins/TablePlugin.d.ts +31 -0
  315. package/lib/plugins/TablePlugin.js +63 -0
  316. package/lib/plugins/TestRecorderPlugin/index.d.ts +3 -0
  317. package/lib/plugins/TestRecorderPlugin/index.js +346 -0
  318. package/lib/plugins/ToolbarPlugin/fontSize.d.ts +9 -0
  319. package/lib/plugins/ToolbarPlugin/fontSize.js +80 -0
  320. package/lib/plugins/ToolbarPlugin/index.d.ts +9 -0
  321. package/lib/plugins/ToolbarPlugin/index.js +500 -0
  322. package/lib/plugins/ToolbarPlugin/utils.d.ts +26 -0
  323. package/lib/plugins/ToolbarPlugin/utils.js +243 -0
  324. package/lib/plugins/TreeViewPlugin/index.d.ts +2 -0
  325. package/lib/plugins/TreeViewPlugin/index.js +7 -0
  326. package/lib/plugins/TwitterPlugin/index.d.ts +4 -0
  327. package/lib/plugins/TwitterPlugin/index.js +20 -0
  328. package/lib/plugins/TypingPerfPlugin/index.d.ts +2 -0
  329. package/lib/plugins/TypingPerfPlugin/index.js +93 -0
  330. package/lib/plugins/YouTubePlugin/index.d.ts +4 -0
  331. package/lib/plugins/YouTubePlugin/index.js +20 -0
  332. package/lib/server/validation.d.ts +1 -0
  333. package/lib/server/validation.js +111 -0
  334. package/lib/setupEnv.d.ts +2 -0
  335. package/lib/setupEnv.js +25 -0
  336. package/lib/themes/CommentEditorTheme.d.ts +4 -0
  337. package/lib/themes/CommentEditorTheme.js +7 -0
  338. package/lib/themes/PlaygroundEditorTheme.d.ts +4 -0
  339. package/lib/themes/PlaygroundEditorTheme.js +120 -0
  340. package/lib/themes/StickyEditorTheme.d.ts +4 -0
  341. package/lib/themes/StickyEditorTheme.js +7 -0
  342. package/lib/tyes.dt.d.ts +12 -0
  343. package/lib/tyes.dt.js +0 -0
  344. package/lib/ui/Button.d.ts +12 -0
  345. package/lib/ui/Button.js +6 -0
  346. package/lib/ui/ColorPicker.d.ts +14 -0
  347. package/lib/ui/ColorPicker.js +219 -0
  348. package/lib/ui/ContentEditable.d.ts +9 -0
  349. package/lib/ui/ContentEditable.js +6 -0
  350. package/lib/ui/Dialog.d.ts +10 -0
  351. package/lib/ui/Dialog.js +8 -0
  352. package/lib/ui/DropDown.d.ts +18 -0
  353. package/lib/ui/DropDown.js +133 -0
  354. package/lib/ui/DropdownColorPicker.d.ts +13 -0
  355. package/lib/ui/DropdownColorPicker.js +6 -0
  356. package/lib/ui/EquationEditor.d.ts +8 -0
  357. package/lib/ui/EquationEditor.js +11 -0
  358. package/lib/ui/ExcalidrawModal.d.ts +42 -0
  359. package/lib/ui/ExcalidrawModal.js +103 -0
  360. package/lib/ui/FileInput.d.ts +10 -0
  361. package/lib/ui/FileInput.js +5 -0
  362. package/lib/ui/FlashMessage.d.ts +7 -0
  363. package/lib/ui/FlashMessage.js +6 -0
  364. package/lib/ui/ImageResizer.d.ts +17 -0
  365. package/lib/ui/ImageResizer.js +171 -0
  366. package/lib/ui/KatexEquationAlterer.d.ts +8 -0
  367. package/lib/ui/KatexEquationAlterer.js +23 -0
  368. package/lib/ui/KatexRenderer.d.ts +6 -0
  369. package/lib/ui/KatexRenderer.js +24 -0
  370. package/lib/ui/Modal.d.ts +9 -0
  371. package/lib/ui/Modal.js +48 -0
  372. package/lib/ui/Select.d.ts +8 -0
  373. package/lib/ui/Select.js +5 -0
  374. package/lib/ui/Switch.d.ts +8 -0
  375. package/lib/ui/Switch.js +6 -0
  376. package/lib/ui/TextInput.d.ts +13 -0
  377. package/lib/ui/TextInput.js +7 -0
  378. package/lib/utils/docSerialization.d.ts +3 -0
  379. package/lib/utils/docSerialization.js +56 -0
  380. package/lib/utils/emoji-list.d.ts +20 -0
  381. package/lib/utils/emoji-list.js +16605 -0
  382. package/lib/utils/getDOMRangeRect.d.ts +8 -0
  383. package/lib/utils/getDOMRangeRect.js +22 -0
  384. package/lib/utils/getSelectedNode.d.ts +2 -0
  385. package/lib/utils/getSelectedNode.js +24 -0
  386. package/lib/utils/getThemeSelector.d.ts +2 -0
  387. package/lib/utils/getThemeSelector.js +10 -0
  388. package/lib/utils/isMobileWidth.d.ts +7 -0
  389. package/lib/utils/isMobileWidth.js +7 -0
  390. package/lib/utils/joinClasses.d.ts +1 -0
  391. package/lib/utils/joinClasses.js +3 -0
  392. package/lib/utils/setFloatingElemPosition.d.ts +1 -0
  393. package/lib/utils/setFloatingElemPosition.js +55 -0
  394. package/lib/utils/setFloatingElemPositionForLinkEditor.d.ts +1 -0
  395. package/lib/utils/setFloatingElemPositionForLinkEditor.js +32 -0
  396. package/lib/utils/swipe.d.ts +4 -0
  397. package/lib/utils/swipe.js +90 -0
  398. package/lib/utils/url.d.ts +2 -0
  399. package/lib/utils/url.js +27 -0
  400. package/package.json +82 -51
  401. package/lib/DiffMerge.d.ts +0 -39
  402. package/lib/DiffMerge.js +0 -437
  403. package/lib/LoroCollaborativePlugin.d.ts +0 -62
  404. package/lib/LoroCollaborativePlugin.js +0 -2826
  405. package/lib/stableNodeState.d.ts +0 -8
  406. package/lib/stableNodeState.js +0 -15
File without changes
File without changes
@@ -0,0 +1,5 @@
1
+ export declare const isCallbackSet: boolean;
2
+ /**
3
+ * @param {import('./utils.ts').WSSharedDoc} doc
4
+ */
5
+ export declare const callbackIntegrator: (doc: any) => void;
@@ -0,0 +1,85 @@
1
+ import http from 'http';
2
+ import * as number from 'lib0/number';
3
+ const CALLBACK_URL = process.env.CALLBACK_URL ? new URL(process.env.CALLBACK_URL) : null;
4
+ const CALLBACK_TIMEOUT = number.parseInt(process.env.CALLBACK_TIMEOUT || '5000');
5
+ const CALLBACK_OBJECTS = process.env.CALLBACK_OBJECTS ? JSON.parse(process.env.CALLBACK_OBJECTS) : {};
6
+ export const isCallbackSet = !!CALLBACK_URL;
7
+ /**
8
+ * @param {import('./utils.ts').WSSharedDoc} doc
9
+ */
10
+ export const callbackIntegrator = (doc) => {
11
+ const room = doc.name;
12
+ const dataToSend = {
13
+ room,
14
+ data: {}
15
+ };
16
+ const sharedObjectList = Object.keys(CALLBACK_OBJECTS);
17
+ sharedObjectList.forEach(sharedObjectName => {
18
+ const sharedObjectType = CALLBACK_OBJECTS[sharedObjectName];
19
+ const content = getContent(sharedObjectName, sharedObjectType, doc);
20
+ dataToSend.data[sharedObjectName] = {
21
+ type: sharedObjectType,
22
+ content: content && typeof content.toJSON === 'function' ? content.toJSON() : content
23
+ };
24
+ });
25
+ };
26
+ /**
27
+ * @param {URL} url
28
+ * @param {number} timeout
29
+ * @param {Object} data
30
+ */
31
+ const callbackRequest = (url, timeout, data) => {
32
+ data = JSON.stringify(data);
33
+ const options = {
34
+ hostname: url.hostname,
35
+ port: url.port,
36
+ path: url.pathname,
37
+ timeout,
38
+ method: 'POST',
39
+ headers: {
40
+ 'Content-Type': 'application/json',
41
+ 'Content-Length': Buffer.byteLength(data)
42
+ }
43
+ };
44
+ const req = http.request(options);
45
+ req.on('timeout', () => {
46
+ console.warn('Callback request timed out.');
47
+ req.abort();
48
+ });
49
+ req.on('error', (e) => {
50
+ console.warn('Callback request error.', e);
51
+ req.abort();
52
+ });
53
+ req.write(data);
54
+ req.end();
55
+ };
56
+ /**
57
+ * @param {string} objName
58
+ * @param {string} objType
59
+ * @param {import('./utils.ts').WSSharedDoc} doc
60
+ */
61
+ const getContent = (objName, objType, doc) => {
62
+ // Loro uses a different API than YJS
63
+ // Get the container by name and type
64
+ try {
65
+ switch (objType) {
66
+ case 'List':
67
+ case 'Array':
68
+ return doc.doc.getList(objName);
69
+ case 'Map':
70
+ return doc.doc.getMap(objName);
71
+ case 'Text':
72
+ return doc.doc.getText(objName);
73
+ case 'Tree':
74
+ return doc.doc.getTree(objName);
75
+ default: {
76
+ // For unknown types, return the entire document state
77
+ return doc.doc.toJSON();
78
+ }
79
+ }
80
+ }
81
+ catch (error) {
82
+ console.warn(`Failed to get content for ${objName} of type ${objType}:`, error);
83
+ return {};
84
+ }
85
+ };
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ export {};
@@ -0,0 +1,25 @@
1
+ #!/usr/bin/env node
2
+ import { WebSocketServer } from 'ws';
3
+ import http from 'http';
4
+ import * as number from 'lib0/number';
5
+ import { setupWSConnection } from './utils';
6
+ const wss = new WebSocketServer({ noServer: true });
7
+ const host = process.env.HOST || 'localhost';
8
+ const port = number.parseInt(process.env.PORT || '1235');
9
+ const server = http.createServer((_request, response) => {
10
+ response.writeHead(200, { 'Content-Type': 'text/plain' });
11
+ response.end('okay');
12
+ });
13
+ wss.on('connection', setupWSConnection);
14
+ server.on('upgrade', (request, socket, head) => {
15
+ // You may check auth of request here..
16
+ // Call `wss.HandleUpgrade` *after* you checked whether the client has access
17
+ // (e.g. by checking cookies, or url parameters).
18
+ // See https://github.com/websockets/ws#client-authentication
19
+ wss.handleUpgrade(request, socket, head, /** @param {any} ws */ /** @param {any} ws */ ws => {
20
+ wss.emit('connection', ws, request);
21
+ });
22
+ });
23
+ server.listen(port, host, () => {
24
+ console.log(`running at '${host}' on port ${port}`);
25
+ });
@@ -0,0 +1,40 @@
1
+ import { LoroDoc, EphemeralStore } from 'loro-crdt';
2
+ /**
3
+ *
4
+ */
5
+ export declare const setPersistence: (persistence_: any) => void;
6
+ /**
7
+ *
8
+ */
9
+ export declare const getPersistence: () => any;
10
+ /**
11
+ *
12
+ */
13
+ export declare const docs: Map<string, WSSharedDoc>;
14
+ /**
15
+ * This function is called once every time a Loro document is created. You can
16
+ * use it to pull data from an external source or initialize content.
17
+ */
18
+ export declare const setContentInitializor: (f: any) => void;
19
+ /**
20
+ * Gets a Doc by name, whether in memory or on disk
21
+ */
22
+ export declare const getDoc: (docname: any) => WSSharedDoc;
23
+ /**
24
+ * @param {import('ws').WebSocket} conn
25
+ * @param {import('http').IncomingMessage} req
26
+ * @param {any} opts
27
+ */
28
+ export declare const setupWSConnection: (conn: any, req: any, { docName }?: {
29
+ docName?: any;
30
+ }) => void;
31
+ export declare class WSSharedDoc {
32
+ name: string;
33
+ doc: LoroDoc;
34
+ connections: Map<any, Set<string>>;
35
+ ephemeralStore: EphemeralStore;
36
+ conns: Map<any, Set<any>>;
37
+ private _conns;
38
+ lastEphemeralSender: any;
39
+ constructor(name: any);
40
+ }
@@ -0,0 +1,513 @@
1
+ import { LoroDoc, EphemeralStore } from 'loro-crdt';
2
+ import * as map from 'lib0/map';
3
+ import * as eventloop from 'lib0/eventloop';
4
+ import { messageEphemeral, messageQueryEphemeral, messageQuerySnapshot, messageUpdate, } from '../../provider/websocket';
5
+ import { callbackIntegrator, isCallbackSet } from './callback';
6
+ import { initializeLoroDocWithLexicalContent } from '../../utils/InitialContent';
7
+ const pingTimeout = 30000;
8
+ const CALLBACK_DEBOUNCE_WAIT = parseInt(process.env.CALLBACK_DEBOUNCE_WAIT || '2000');
9
+ const CALLBACK_DEBOUNCE_MAXWAIT = parseInt(process.env.CALLBACK_DEBOUNCE_MAXWAIT || '10000');
10
+ const debouncer = eventloop.createDebouncer(CALLBACK_DEBOUNCE_WAIT, CALLBACK_DEBOUNCE_MAXWAIT);
11
+ const wsReadyStateConnecting = 0;
12
+ const wsReadyStateOpen = 1;
13
+ const wsReadyStateClosing = 2;
14
+ const wsReadyStateClosed = 3;
15
+ const persistenceDir = process.env.YPERSISTENCE;
16
+ // Helper function to log tree structure for debugging
17
+ const logTreeStructure = (doc, context) => {
18
+ try {
19
+ console.log(`[Server] ${context} - Tree Structure Debug:`);
20
+ // Try to get the tree container using getTree method
21
+ try {
22
+ const tree = doc.getTree('tree');
23
+ if (tree) {
24
+ const nodes = tree.nodes();
25
+ console.log(`[Server] Total nodes in tree: ${nodes.length}`);
26
+ // Helper function to recursively log tree structure
27
+ const logTreeStructureRecursive = (node, prefix = '', isLast = true, depth = 0) => {
28
+ const data = Object.fromEntries(node.data.entries());
29
+ const treeId = node.id;
30
+ const elementType = data.elementType || 'no-type';
31
+ const connector = depth === 0 ? '' : (isLast ? '└── ' : '├── ');
32
+ const nodeInfo = `TreeID(${treeId.slice(0, 8)}...) [${elementType}]`;
33
+ console.log(`[Server] ${prefix}${connector}${nodeInfo}`);
34
+ const children = node.children();
35
+ if (children && children.length > 0) {
36
+ children.forEach((child, index) => {
37
+ const isLastChild = index === children.length - 1;
38
+ const childPrefix = prefix + (depth === 0 ? '' : (isLast ? ' ' : '│ '));
39
+ logTreeStructureRecursive(child, childPrefix, isLastChild, depth + 1);
40
+ });
41
+ }
42
+ };
43
+ // Find and display all root nodes
44
+ const rootNodes = nodes.filter((node) => {
45
+ const parent = node.parent();
46
+ const data = Object.fromEntries(node.data.entries());
47
+ return !parent || data.isRoot;
48
+ });
49
+ console.log(`[Server] Root nodes: ${rootNodes.length}`);
50
+ console.log('');
51
+ if (rootNodes.length === 0) {
52
+ console.log('[Server] ⚠️ No root nodes found!');
53
+ }
54
+ else {
55
+ rootNodes.forEach((root, index) => {
56
+ const isLastRoot = index === rootNodes.length - 1;
57
+ logTreeStructureRecursive(root, '', isLastRoot, 0);
58
+ });
59
+ }
60
+ }
61
+ else {
62
+ console.log(`[Server] No tree container found`);
63
+ }
64
+ }
65
+ catch (treeError) {
66
+ console.log(`[Server] Error accessing tree container:`, treeError.message);
67
+ }
68
+ // Try export with different modes for debugging
69
+ try {
70
+ const snapshot = doc.export({ mode: 'snapshot' });
71
+ console.log(`[Server] Snapshot size: ${snapshot.length} bytes`);
72
+ const update = doc.export({ mode: 'update' });
73
+ console.log(`[Server] Update size: ${update.length} bytes`);
74
+ }
75
+ catch (exportError) {
76
+ console.log(`[Server] Error exporting:`, exportError.message);
77
+ }
78
+ }
79
+ catch (error) {
80
+ console.warn(`[Server] Error logging tree structure:`, error);
81
+ }
82
+ };
83
+ /**
84
+ *
85
+ */
86
+ let persistence = null;
87
+ if (typeof persistenceDir === 'string') {
88
+ console.log('Persistence directory configured: "' + persistenceDir + '" but Loro persistence not yet implemented');
89
+ // TODO: Implement actual Loro document persistence
90
+ // For now, disable persistence to keep documents in memory
91
+ console.log('Disabling persistence - documents will be kept in memory only');
92
+ persistence = null;
93
+ // Uncomment and implement when ready for actual persistence:
94
+ // persistence = {
95
+ // provider: null, // Replace with Loro-compatible persistence provider
96
+ // bindState: async (docName, doc: WSSharedDoc) => {
97
+ // // TODO: Implement Loro document persistence loading
98
+ // console.warn(`Loading state for document: ${docName}`)
99
+ // },
100
+ // writeState: async (docName, doc: WSSharedDoc) => {
101
+ // // TODO: Implement Loro document state writing
102
+ // console.warn(`Saving state for document: ${docName}`)
103
+ // }
104
+ // }
105
+ }
106
+ /**
107
+ *
108
+ */
109
+ export const setPersistence = persistence_ => {
110
+ persistence = persistence_;
111
+ };
112
+ /**
113
+ *
114
+ */
115
+ export const getPersistence = () => persistence;
116
+ /**
117
+ *
118
+ */
119
+ export const docs = new Map();
120
+ /**
121
+ * @type {(ydoc: Y.Doc) => Promise<void>}
122
+ */
123
+ let contentInitializor = _ydoc => Promise.resolve();
124
+ /**
125
+ * This function is called once every time a Loro document is created. You can
126
+ * use it to pull data from an external source or initialize content.
127
+ */
128
+ export const setContentInitializor = (f) => {
129
+ contentInitializor = f;
130
+ };
131
+ /**
132
+ * Gets a Doc by name, whether in memory or on disk
133
+ */
134
+ export const getDoc = (docname) => map.setIfUndefined(docs, docname, () => {
135
+ const doc = new WSSharedDoc(docname);
136
+ if (persistence !== null) {
137
+ persistence.bindState(docname, doc);
138
+ }
139
+ docs.set(docname, doc);
140
+ return doc;
141
+ });
142
+ /**
143
+ *
144
+ */
145
+ const sendMessage = (doc, conn, message) => {
146
+ if (conn.readyState === wsReadyStateClosing || conn.readyState === wsReadyStateClosed) {
147
+ closeConn(doc, conn);
148
+ }
149
+ try {
150
+ // console.log(`Sending message to ${conn.id || 'unknown'}`)
151
+ conn.send(JSON.stringify(message), {}, err => { if (err != null)
152
+ closeConn(doc, conn); });
153
+ }
154
+ catch (e) {
155
+ console.warn(e);
156
+ closeConn(doc, conn);
157
+ }
158
+ };
159
+ /**
160
+ *
161
+ */
162
+ const sendMessageBinary = (doc, conn, message) => {
163
+ if (conn.readyState === wsReadyStateClosing || conn.readyState === wsReadyStateClosed) {
164
+ closeConn(doc, conn);
165
+ }
166
+ try {
167
+ // console.log(`Sending message to ${conn.id || 'unknown'}`)
168
+ conn.send(message, {}, err => { if (err != null)
169
+ closeConn(doc, conn); });
170
+ }
171
+ catch (e) {
172
+ console.warn(e);
173
+ closeConn(doc, conn);
174
+ }
175
+ };
176
+ /**
177
+ *
178
+ */
179
+ const messageListener = (conn, doc, message) => {
180
+ try {
181
+ let messageData = null;
182
+ let messageStr = '';
183
+ if (typeof message === 'string') {
184
+ messageStr = message;
185
+ }
186
+ else if (message instanceof ArrayBuffer) {
187
+ try {
188
+ const decoder = new TextDecoder();
189
+ messageStr = decoder.decode(message);
190
+ }
191
+ catch (decodeError) {
192
+ console.warn(`[Server] messageListener - Failed to decode ArrayBuffer as string, treating as binary Loro update`);
193
+ // If decoding fails, treat as raw binary Loro update
194
+ const updateBytes = new Uint8Array(message);
195
+ doc.doc.import(updateBytes);
196
+ // Broadcast the update to other connections
197
+ doc.conns.forEach((_, c) => {
198
+ if (c !== conn) {
199
+ sendMessageBinary(doc, c, new Uint8Array(message));
200
+ }
201
+ });
202
+ return;
203
+ }
204
+ }
205
+ else if (message instanceof Uint8Array) {
206
+ try {
207
+ const decoder = new TextDecoder();
208
+ messageStr = decoder.decode(message);
209
+ }
210
+ catch (decodeError) {
211
+ console.warn(`[Server] messageListener - Failed to decode Uint8Array as string, treating as binary Loro update`);
212
+ // If decoding fails, treat as raw binary Loro update
213
+ doc.doc.import(message);
214
+ // Broadcast the update to other connections
215
+ doc.conns.forEach((_, c) => {
216
+ if (c !== conn) {
217
+ sendMessageBinary(doc, c, message);
218
+ }
219
+ });
220
+ return;
221
+ }
222
+ }
223
+ else {
224
+ console.warn(`[Server] messageListener - Unknown message type:`, typeof message);
225
+ return;
226
+ }
227
+ if (!messageStr || messageStr.length === 0) {
228
+ return;
229
+ }
230
+ try {
231
+ messageData = JSON.parse(messageStr);
232
+ }
233
+ catch (parseError) {
234
+ console.warn(`[Server] messageListener - JSON parse error:`, parseError.message);
235
+ console.warn(`[Server] messageListener - Raw message:`, messageStr.substring(0, 500));
236
+ return;
237
+ }
238
+ console.log(`[Server] Received message type: ${messageData.type} for doc: ${doc.name}`);
239
+ switch (messageData.type) {
240
+ case messageQuerySnapshot:
241
+ // Client is requesting a snapshot - send current document state
242
+ const requestId = Math.random().toString(36).substr(2, 9);
243
+ console.log(`[Server] Client requesting snapshot for doc: ${doc.name} (Request ID: ${requestId})`);
244
+ // Log tree structure before creating snapshot
245
+ // logTreeStructure(doc.doc, `Before creating snapshot (Request ID: ${requestId})`)
246
+ try {
247
+ const snapshot = doc.doc.export({ mode: 'snapshot' });
248
+ console.log(`[Server] Sending snapshot response: ${snapshot.length} bytes (Request ID: ${requestId})`);
249
+ // Verify the snapshot contains expected content
250
+ const tree = doc.doc.getTree('tree');
251
+ const nodes = tree.nodes();
252
+ console.log(`[Server] Snapshot contains ${nodes.length} nodes from server document`);
253
+ // Send binary snapshot data directly instead of wrapped message
254
+ conn.send(snapshot);
255
+ }
256
+ catch (snapshotError) {
257
+ console.error(`[Server] ERROR creating/sending snapshot:`, snapshotError.message);
258
+ console.error(`[Server] Stack:`, snapshotError.stack);
259
+ }
260
+ break;
261
+ case messageEphemeral:
262
+ try {
263
+ const ephemeralBytes = new Uint8Array(messageData.ephemeral);
264
+ // Mark this connection as the sender to avoid echo
265
+ doc.lastEphemeralSender = conn;
266
+ // Debug: Check ephemeral store state before and after
267
+ const beforeStates = doc.ephemeralStore.getAllStates();
268
+ const beforeKeys = Object.keys(beforeStates);
269
+ doc.ephemeralStore.apply(ephemeralBytes);
270
+ const afterStates = doc.ephemeralStore.getAllStates();
271
+ const afterKeys = Object.keys(afterStates);
272
+ console.log(`📡 SERVER DEBUG - Applied ephemeral update from ${conn.id}:`, {
273
+ bytesLength: ephemeralBytes.length,
274
+ beforeKeys,
275
+ afterKeys,
276
+ newKeys: afterKeys.filter(k => !beforeKeys.includes(k)),
277
+ totalConnections: doc.conns.size
278
+ });
279
+ }
280
+ catch (ephemeralError) {
281
+ console.warn('messageEphemeral - ERROR applying ephemeral update');
282
+ // Clear sender reference on error
283
+ doc.lastEphemeralSender = null;
284
+ }
285
+ break;
286
+ case messageQueryEphemeral:
287
+ // Send current ephemeral state to requesting client
288
+ try {
289
+ const allStates = doc.ephemeralStore.getAllStates();
290
+ const allKeys = Object.keys(allStates);
291
+ const ephemeralUpdate = doc.ephemeralStore.encodeAll();
292
+ console.log(`📡 SERVER DEBUG - Client ${conn.id} requesting ephemeral state:`, {
293
+ allKeysAvailable: allKeys,
294
+ encodedLength: ephemeralUpdate.length,
295
+ totalConnections: doc.conns.size
296
+ });
297
+ const ephemeralResponse = {
298
+ type: 'ephemeral',
299
+ ephemeral: Array.from(ephemeralUpdate),
300
+ docId: doc.name
301
+ };
302
+ sendMessage(doc, conn, ephemeralResponse);
303
+ }
304
+ catch (error) {
305
+ console.warn('[Server] messageQueryEphemeral - ERROR encoding/sending ephemeral state:', error);
306
+ }
307
+ break;
308
+ case messageUpdate:
309
+ // Apply the Loro update to the document.
310
+ const updateBytes = new Uint8Array(messageData.update);
311
+ const i = doc.doc.import(updateBytes);
312
+ // logTreeStructure(doc.doc, `After applying update from client ${conn.id || 'unknown'}`)
313
+ // Create properly formatted message for broadcasting
314
+ // Send the update to all other connections
315
+ let broadcastCount = 0;
316
+ doc.conns.forEach((_, c) => {
317
+ if (c !== conn) {
318
+ console.log(`Broadcasting Update to connection: ${c.id}`);
319
+ sendMessage(doc, c, messageData);
320
+ broadcastCount++;
321
+ }
322
+ });
323
+ // Trigger callback if configured
324
+ if (isCallbackSet) {
325
+ debouncer(() => callbackIntegrator(doc));
326
+ }
327
+ break;
328
+ }
329
+ }
330
+ catch (err) {
331
+ console.warn(err);
332
+ // Note: LoroDoc doesn't have emit method, using console.warn instead
333
+ console.warn('Message handling error:', err);
334
+ }
335
+ };
336
+ /**
337
+ * @param {WSSharedDoc} doc
338
+ * @param {any} conn
339
+ */
340
+ const closeConn = (doc, conn) => {
341
+ if (doc.conns.has(conn)) {
342
+ console.log(`Closing connection: ${conn.id || 'unknown'} for document: ${doc.name}`);
343
+ /**
344
+ * @type {Set<string>}
345
+ */
346
+ const controlledKeys = doc.conns.get(conn);
347
+ doc.conns.delete(conn);
348
+ // Remove ephemeral state controlled by this connection
349
+ if (controlledKeys) {
350
+ controlledKeys.forEach(key => {
351
+ doc.ephemeralStore.delete(key);
352
+ });
353
+ }
354
+ console.log(`Remaining connections for document ${doc.name}: ${doc.conns.size}`);
355
+ if (doc.conns.size === 0) {
356
+ if (persistence !== null) {
357
+ // if persisted, we store state and cleanup document
358
+ console.log(`[Server] Persisting document ${doc.name} before cleanup`);
359
+ persistence.writeState(doc.name, doc).then(() => {
360
+ // Cleanup WSSharedDoc resources (no destroy method needed for Loro)
361
+ console.log(`[Server] Document ${doc.name} persisted and cleaned up`);
362
+ });
363
+ docs.delete(doc.name);
364
+ }
365
+ else {
366
+ // No persistence configured - keep document in memory for reconnections
367
+ console.log(`[Server] No persistence configured - keeping document ${doc.name} in memory for future connections`);
368
+ // logTreeStructure(doc.doc, `Document ${doc.name} structure before keeping in memory`)
369
+ }
370
+ }
371
+ }
372
+ conn.close();
373
+ };
374
+ /**
375
+ * @param {import('ws').WebSocket} conn
376
+ * @param {import('http').IncomingMessage} req
377
+ * @param {any} opts
378
+ */
379
+ export const setupWSConnection = (conn, req, { docName = (req.url || '').slice(1).split('?')[0] } = {}) => {
380
+ conn.binaryType = 'arraybuffer';
381
+ // get doc, initialize if it does not exist yet
382
+ const doc = getDoc(docName);
383
+ // Assign a unique ID to the connection for logging
384
+ conn.id = `conn-${conn._socket?.remoteAddress || 'unknown'}:${conn._socket?.remotePort || Math.random()}`;
385
+ console.log(`New connection established: ${conn.id} for document: ${docName} (LoroDoc peerId: ${doc.doc.peerId})`);
386
+ doc.conns.set(conn, new Set());
387
+ // listen and reply to events
388
+ conn.on('message', message => messageListener(conn, doc, message));
389
+ // Check if connection is still alive
390
+ let pongReceived = true;
391
+ const pingInterval = setInterval(() => {
392
+ if (!pongReceived) {
393
+ if (doc.conns.has(conn)) {
394
+ closeConn(doc, conn);
395
+ }
396
+ clearInterval(pingInterval);
397
+ }
398
+ else if (doc.conns.has(conn)) {
399
+ pongReceived = false;
400
+ try {
401
+ conn.ping();
402
+ }
403
+ catch (e) {
404
+ closeConn(doc, conn);
405
+ clearInterval(pingInterval);
406
+ }
407
+ }
408
+ }, pingTimeout);
409
+ conn.on('close', () => {
410
+ closeConn(doc, conn);
411
+ clearInterval(pingInterval);
412
+ });
413
+ conn.on('pong', () => {
414
+ pongReceived = true;
415
+ });
416
+ // put the following in a variables in a block so the interval integrators don't keep in in
417
+ // scope
418
+ {
419
+ // Send initial snapshot to new client
420
+ // Log tree structure before creating initial snapshot
421
+ // logTreeStructure(doc.doc, `Before creating initial snapshot for new client ${conn.id}`)
422
+ try {
423
+ const snapshot = doc.doc.export({ mode: 'snapshot' });
424
+ console.log(`[Server] Sending initial snapshot to new client: ${snapshot.length} bytes`);
425
+ // Verify the snapshot contains expected content
426
+ const tree = doc.doc.getTree('tree');
427
+ const nodes = tree.nodes();
428
+ console.log(`[Server] Initial snapshot contains ${nodes.length} nodes from server document`);
429
+ // Send binary snapshot data directly instead of wrapped message
430
+ conn.send(snapshot);
431
+ }
432
+ catch (snapshotError) {
433
+ console.error(`[Server] ERROR creating/sending initial snapshot:`, snapshotError.message);
434
+ console.error(`[Server] Stack:`, snapshotError.stack);
435
+ }
436
+ // Send current ephemeral state if any
437
+ const ephemeralUpdate = doc.ephemeralStore.encodeAll();
438
+ if (ephemeralUpdate.length > 0) {
439
+ const ephemeralMessage = {
440
+ type: 'ephemeral',
441
+ ephemeral: Array.from(ephemeralUpdate),
442
+ docId: doc.name
443
+ };
444
+ sendMessage(doc, conn, ephemeralMessage);
445
+ }
446
+ }
447
+ };
448
+ export class WSSharedDoc {
449
+ name;
450
+ doc;
451
+ connections;
452
+ ephemeralStore;
453
+ conns;
454
+ _conns;
455
+ lastEphemeralSender;
456
+ constructor(name) {
457
+ this.name = name;
458
+ this.doc = new LoroDoc();
459
+ // Initialize the document with default Lexical content
460
+ console.log(`[Server] Initializing document '${name}' with default content`);
461
+ initializeLoroDocWithLexicalContent(this.doc);
462
+ /**
463
+ * Maps from conn to set of controlled ephemeral keys. Delete all keys when this conn is closed
464
+ * @type {Map<Object, Set<string>>}
465
+ */
466
+ this.connections = new Map();
467
+ this.conns = new Map();
468
+ this._conns = new Set();
469
+ /**
470
+ * @type {EphemeralStore}
471
+ */
472
+ this.ephemeralStore = new EphemeralStore(30000); // 30 second timeout
473
+ // Store the last sender to avoid echo loops
474
+ this.lastEphemeralSender = null;
475
+ /**
476
+ * @type {Array<function>}
477
+ */
478
+ const ephemeralChangeIntegrator = (event) => {
479
+ // Only broadcast if there are actual changes
480
+ if (event.added.length > 0 || event.updated.length > 0 || event.removed.length > 0) {
481
+ try {
482
+ const encodedData = this.ephemeralStore.encodeAll();
483
+ // Skip broadcast if no actual data to send
484
+ if (encodedData.length === 0) {
485
+ return;
486
+ }
487
+ const message = {
488
+ type: 'ephemeral',
489
+ ephemeral: Array.from(encodedData),
490
+ docId: this.name
491
+ };
492
+ // Broadcast to all connections EXCEPT the one that sent the last ephemeral update
493
+ this.conns.forEach((_, conn) => {
494
+ if (conn !== this.lastEphemeralSender) {
495
+ sendMessage(this, conn, message);
496
+ }
497
+ });
498
+ // Clear the sender reference after broadcast
499
+ this.lastEphemeralSender = null;
500
+ }
501
+ catch (broadcastError) {
502
+ console.warn(`[Server] ephemeralChangeIntegrator - ERROR broadcasting:`, {
503
+ error: broadcastError.message,
504
+ stack: broadcastError.stack
505
+ });
506
+ }
507
+ }
508
+ };
509
+ this.ephemeralStore.subscribe(ephemeralChangeIntegrator);
510
+ // Note: LoroDoc doesn't have 'on' method like Y.Doc
511
+ // Update handling will be done through message processing
512
+ }
513
+ }