kritzel-stencil 0.0.144 → 0.0.145

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 (290) hide show
  1. package/dist/cjs/{index-C9GjuVAx.js → default-text-tool.config-D2dP2xyB.js} +15938 -1096
  2. package/dist/cjs/default-text-tool.config-D2dP2xyB.js.map +1 -0
  3. package/dist/cjs/{index-DcTwXs_q.js → index-Cj__YTlG.js} +9 -11
  4. package/dist/cjs/index-Cj__YTlG.js.map +1 -0
  5. package/dist/cjs/index.cjs.js +1369 -12
  6. package/dist/cjs/index.cjs.js.map +1 -1
  7. package/dist/cjs/kritzel-brush-style.cjs.entry.js +1 -1
  8. package/dist/cjs/kritzel-color_22.cjs.entry.js +767 -718
  9. package/dist/cjs/loader.cjs.js +2 -2
  10. package/dist/cjs/stencil.cjs.js +3 -3
  11. package/dist/cjs/stencil.cjs.js.map +1 -1
  12. package/dist/collection/classes/core/core.class.js +263 -211
  13. package/dist/collection/classes/core/core.class.js.map +1 -1
  14. package/dist/collection/classes/core/store.class.js +21 -3
  15. package/dist/collection/classes/core/store.class.js.map +1 -1
  16. package/dist/collection/classes/core/viewport.class.js +4 -1
  17. package/dist/collection/classes/core/viewport.class.js.map +1 -1
  18. package/dist/collection/classes/core/workspace.class.js +2 -3
  19. package/dist/collection/classes/core/workspace.class.js.map +1 -1
  20. package/dist/collection/classes/handlers/context-menu.handler.js +11 -14
  21. package/dist/collection/classes/handlers/context-menu.handler.js.map +1 -1
  22. package/dist/collection/classes/handlers/key.handler.js +13 -13
  23. package/dist/collection/classes/handlers/key.handler.js.map +1 -1
  24. package/dist/collection/classes/handlers/move.handler.js +12 -9
  25. package/dist/collection/classes/handlers/move.handler.js.map +1 -1
  26. package/dist/collection/classes/handlers/resize.handler.js +20 -17
  27. package/dist/collection/classes/handlers/resize.handler.js.map +1 -1
  28. package/dist/collection/classes/handlers/rotation.handler.js +26 -23
  29. package/dist/collection/classes/handlers/rotation.handler.js.map +1 -1
  30. package/dist/collection/classes/handlers/selection.handler.js +32 -30
  31. package/dist/collection/classes/handlers/selection.handler.js.map +1 -1
  32. package/dist/collection/classes/objects/base-object.class.js +6 -15
  33. package/dist/collection/classes/objects/base-object.class.js.map +1 -1
  34. package/dist/collection/classes/objects/custom-element.class.js +2 -0
  35. package/dist/collection/classes/objects/custom-element.class.js.map +1 -1
  36. package/dist/collection/classes/objects/image.class.js +2 -0
  37. package/dist/collection/classes/objects/image.class.js.map +1 -1
  38. package/dist/collection/classes/objects/path.class.js +4 -0
  39. package/dist/collection/classes/objects/path.class.js.map +1 -1
  40. package/dist/collection/classes/objects/selection-box.class.js +3 -4
  41. package/dist/collection/classes/objects/selection-box.class.js.map +1 -1
  42. package/dist/collection/classes/objects/selection-group.class.js +109 -49
  43. package/dist/collection/classes/objects/selection-group.class.js.map +1 -1
  44. package/dist/collection/classes/objects/text.class.js +34 -40
  45. package/dist/collection/classes/objects/text.class.js.map +1 -1
  46. package/dist/collection/classes/providers/broadcast-sync-provider.class.js +93 -0
  47. package/dist/collection/classes/providers/broadcast-sync-provider.class.js.map +1 -0
  48. package/dist/collection/classes/providers/hocuspocus-sync-provider.class.js +232 -0
  49. package/dist/collection/classes/providers/hocuspocus-sync-provider.class.js.map +1 -0
  50. package/dist/collection/classes/providers/indexeddb-sync-provider.class.js +35 -0
  51. package/dist/collection/classes/providers/indexeddb-sync-provider.class.js.map +1 -0
  52. package/dist/collection/classes/providers/websocket-sync-provider.class.js +89 -0
  53. package/dist/collection/classes/providers/websocket-sync-provider.class.js.map +1 -0
  54. package/dist/collection/classes/structures/app-state-map.structure.js +189 -0
  55. package/dist/collection/classes/structures/app-state-map.structure.js.map +1 -0
  56. package/dist/collection/classes/structures/object-map.structure.js +260 -1
  57. package/dist/collection/classes/structures/object-map.structure.js.map +1 -1
  58. package/dist/collection/classes/tools/brush-tool.class.js +48 -37
  59. package/dist/collection/classes/tools/brush-tool.class.js.map +1 -1
  60. package/dist/collection/classes/tools/eraser-tool.class.js +10 -12
  61. package/dist/collection/classes/tools/eraser-tool.class.js.map +1 -1
  62. package/dist/collection/classes/tools/image-tool.class.js +2 -10
  63. package/dist/collection/classes/tools/image-tool.class.js.map +1 -1
  64. package/dist/collection/classes/tools/selection-tool.class.js +11 -8
  65. package/dist/collection/classes/tools/selection-tool.class.js.map +1 -1
  66. package/dist/collection/classes/tools/text-tool.class.js +12 -18
  67. package/dist/collection/classes/tools/text-tool.class.js.map +1 -1
  68. package/dist/collection/collection-manifest.json +1 -1
  69. package/dist/collection/components/core/kritzel-cursor-trail/kritzel-cursor-trail.js +1 -1
  70. package/dist/collection/components/core/kritzel-editor/kritzel-editor.js +36 -1
  71. package/dist/collection/components/core/kritzel-editor/kritzel-editor.js.map +1 -1
  72. package/dist/collection/components/core/kritzel-engine/kritzel-engine.js +61 -35
  73. package/dist/collection/components/core/kritzel-engine/kritzel-engine.js.map +1 -1
  74. package/dist/collection/components/shared/kritzel-color/kritzel-color.js +2 -2
  75. package/dist/collection/components/shared/kritzel-color-palette/kritzel-color-palette.js +1 -1
  76. package/dist/collection/components/shared/kritzel-font/kritzel-font.js +1 -1
  77. package/dist/collection/components/shared/kritzel-font-family/kritzel-font-family.js +1 -1
  78. package/dist/collection/components/shared/kritzel-font-size/kritzel-font-size.js +1 -1
  79. package/dist/collection/components/shared/kritzel-menu/kritzel-menu.js +1 -1
  80. package/dist/collection/components/shared/kritzel-menu-item/kritzel-menu-item.js +2 -2
  81. package/dist/collection/components/shared/kritzel-portal/kritzel-portal.js +1 -1
  82. package/dist/collection/components/shared/kritzel-split-button/kritzel-split-button.js +1 -1
  83. package/dist/collection/components/shared/kritzel-stroke-size/kritzel-stroke-size.js +1 -1
  84. package/dist/collection/components/shared/kritzel-tooltip/kritzel-tooltip.js +4 -4
  85. package/dist/collection/components/ui/kritzel-context-menu/kritzel-context-menu.js +1 -1
  86. package/dist/collection/components/ui/kritzel-control-brush-config/kritzel-control-brush-config.js +2 -2
  87. package/dist/collection/components/ui/kritzel-control-text-config/kritzel-control-text-config.js +2 -2
  88. package/dist/collection/components/ui/kritzel-controls/kritzel-controls.js +3 -3
  89. package/dist/collection/components/ui/kritzel-utility-panel/kritzel-utility-panel.js +1 -1
  90. package/dist/collection/configs/{default-engine-state.js → default-engine-config.js} +2 -8
  91. package/dist/collection/configs/default-engine-config.js.map +1 -0
  92. package/dist/collection/configs/default-sync.config.js +12 -0
  93. package/dist/collection/configs/default-sync.config.js.map +1 -0
  94. package/dist/collection/constants/core.constants.js +2 -0
  95. package/dist/collection/constants/core.constants.js.map +1 -0
  96. package/dist/collection/index.js +8 -1
  97. package/dist/collection/index.js.map +1 -1
  98. package/dist/collection/interfaces/debug-info.interface.js.map +1 -1
  99. package/dist/collection/interfaces/engine-state.interface.js.map +1 -1
  100. package/dist/collection/interfaces/object.interface.js.map +1 -1
  101. package/dist/collection/interfaces/selection-state.interface.js.map +1 -1
  102. package/dist/collection/interfaces/sync-config.interface.js +2 -0
  103. package/dist/collection/interfaces/sync-config.interface.js.map +1 -0
  104. package/dist/collection/interfaces/sync-provider.interface.js +2 -0
  105. package/dist/collection/interfaces/sync-provider.interface.js.map +1 -0
  106. package/dist/components/index.js +1361 -4
  107. package/dist/components/index.js.map +1 -1
  108. package/dist/components/kritzel-brush-style.js +4 -4
  109. package/dist/components/kritzel-color-palette.js +1 -1
  110. package/dist/components/kritzel-color.js +1 -1
  111. package/dist/components/kritzel-context-menu.js +1 -1
  112. package/dist/components/kritzel-control-brush-config.js +1 -1
  113. package/dist/components/kritzel-control-text-config.js +1 -1
  114. package/dist/components/kritzel-controls.js +1 -1
  115. package/dist/components/kritzel-cursor-trail.js +1 -1
  116. package/dist/components/kritzel-dropdown.js +1 -1
  117. package/dist/components/kritzel-editor.js +39 -27
  118. package/dist/components/kritzel-editor.js.map +1 -1
  119. package/dist/components/kritzel-engine.js +1 -1
  120. package/dist/components/kritzel-font-family.js +1 -1
  121. package/dist/components/kritzel-font-size.js +1 -1
  122. package/dist/components/kritzel-font.js +1 -1
  123. package/dist/components/kritzel-icon.js +1 -1
  124. package/dist/components/kritzel-menu-item.js +1 -1
  125. package/dist/components/kritzel-menu.js +1 -1
  126. package/dist/components/kritzel-portal.js +1 -1
  127. package/dist/components/kritzel-split-button.js +1 -1
  128. package/dist/components/kritzel-stroke-size.js +1 -1
  129. package/dist/components/kritzel-tooltip.js +1 -1
  130. package/dist/components/kritzel-utility-panel.js +1 -1
  131. package/dist/components/kritzel-workspace-manager.js +1 -1
  132. package/dist/components/{p-C_hSH2nN.js → p-8iFF5GHL.js} +6 -6
  133. package/dist/components/{p-C_hSH2nN.js.map → p-8iFF5GHL.js.map} +1 -1
  134. package/dist/components/{p-BycHaC-9.js → p-BCrMfH5n.js} +6 -6
  135. package/dist/components/{p-BycHaC-9.js.map → p-BCrMfH5n.js.map} +1 -1
  136. package/dist/components/{p-D_RcVGj0.js → p-BHDOht0m.js} +6 -6
  137. package/dist/components/{p-D_RcVGj0.js.map → p-BHDOht0m.js.map} +1 -1
  138. package/dist/components/{p-DqsgZIHC.js → p-BHT7_POQ.js} +6 -6
  139. package/dist/components/{p-DqsgZIHC.js.map → p-BHT7_POQ.js.map} +1 -1
  140. package/dist/components/{p-DzyZA2GT.js → p-BQ5cdSqE.js} +11 -11
  141. package/dist/components/{p-DzyZA2GT.js.map → p-BQ5cdSqE.js.map} +1 -1
  142. package/dist/components/{p-Co5lU_7h.js → p-BaHZYvfq.js} +13 -13
  143. package/dist/components/{p-Co5lU_7h.js.map → p-BaHZYvfq.js.map} +1 -1
  144. package/dist/components/{p-BJbN3vca.js → p-BctNMdxr.js} +8 -8
  145. package/dist/components/{p-BJbN3vca.js.map → p-BctNMdxr.js.map} +1 -1
  146. package/dist/components/{p-1bVCRi-d.js → p-BgRGxOIE.js} +20 -20
  147. package/dist/components/{p-1bVCRi-d.js.map → p-BgRGxOIE.js.map} +1 -1
  148. package/dist/components/{p-D27d2rKT.js → p-Bhtn9qay.js} +5 -5
  149. package/dist/components/{p-D27d2rKT.js.map → p-Bhtn9qay.js.map} +1 -1
  150. package/dist/components/{p-CEn1WeG3.js → p-Bit0z7Yg.js} +9 -9
  151. package/dist/components/{p-CEn1WeG3.js.map → p-Bit0z7Yg.js.map} +1 -1
  152. package/dist/components/{p-CGb-8cK4.js → p-BlI4vzRZ.js} +5 -5
  153. package/dist/components/{p-CGb-8cK4.js.map → p-BlI4vzRZ.js.map} +1 -1
  154. package/dist/components/p-ByRC-aCs.js +18262 -0
  155. package/dist/components/p-ByRC-aCs.js.map +1 -0
  156. package/dist/components/{p-fiFoOjv0.js → p-C3_LIgzd.js} +10 -10
  157. package/dist/components/{p-fiFoOjv0.js.map → p-C3_LIgzd.js.map} +1 -1
  158. package/dist/components/{p-DPxzgBs0.js → p-CIXPLjCu.js} +4 -4
  159. package/dist/components/{p-DPxzgBs0.js.map → p-CIXPLjCu.js.map} +1 -1
  160. package/dist/components/{p-UsToUu6G.js → p-COGwCbe1.js} +114 -186
  161. package/dist/components/p-COGwCbe1.js.map +1 -0
  162. package/dist/components/{p-dcR2uxM3.js → p-CURq0twf.js} +6 -6
  163. package/dist/components/{p-dcR2uxM3.js.map → p-CURq0twf.js.map} +1 -1
  164. package/dist/components/{p-C9hrbrUN.js → p-CwkUrTy1.js} +5 -7
  165. package/dist/{cjs/index-DcTwXs_q.js.map → components/p-CwkUrTy1.js.map} +1 -1
  166. package/dist/components/{p-ByAzDzS5.js → p-D13ydJjo.js} +5 -5
  167. package/dist/components/{p-ByAzDzS5.js.map → p-D13ydJjo.js.map} +1 -1
  168. package/dist/components/{p-BFNwskCY.js → p-Dbp5YJIa.js} +5 -5
  169. package/dist/components/{p-BFNwskCY.js.map → p-Dbp5YJIa.js.map} +1 -1
  170. package/dist/components/{p-BEKicPnH.js → p-Dcf7tVJW.js} +5 -5
  171. package/dist/components/{p-BEKicPnH.js.map → p-Dcf7tVJW.js.map} +1 -1
  172. package/dist/components/{p-CieOx1XL.js → p-EBtkRix7.js} +8 -8
  173. package/dist/components/{p-CieOx1XL.js.map → p-EBtkRix7.js.map} +1 -1
  174. package/dist/components/{p-gCHmJzc2.js → p-NXPGXBZ2.js} +6 -6
  175. package/dist/components/{p-gCHmJzc2.js.map → p-NXPGXBZ2.js.map} +1 -1
  176. package/dist/components/{p-YqK8ch2R.js → p-n789Y3S-.js} +4 -5
  177. package/dist/components/p-n789Y3S-.js.map +1 -0
  178. package/dist/esm/{index-YVlgItFD.js → default-text-tool.config-CsZAW1Cu.js} +15899 -1088
  179. package/dist/esm/default-text-tool.config-CsZAW1Cu.js.map +1 -0
  180. package/dist/esm/{index-Cw77zP6g.js → index-SGde3HXB.js} +9 -11
  181. package/dist/esm/index-SGde3HXB.js.map +1 -0
  182. package/dist/esm/index.js +1358 -1
  183. package/dist/esm/index.js.map +1 -1
  184. package/dist/esm/kritzel-brush-style.entry.js +1 -1
  185. package/dist/esm/kritzel-color_22.entry.js +732 -683
  186. package/dist/esm/loader.js +3 -3
  187. package/dist/esm/stencil.js +4 -4
  188. package/dist/esm/stencil.js.map +1 -1
  189. package/dist/stencil/index.esm.js +1 -1
  190. package/dist/stencil/index.esm.js.map +1 -1
  191. package/dist/stencil/p-27adbf9d.entry.js +2 -0
  192. package/dist/stencil/p-27adbf9d.entry.js.map +1 -0
  193. package/dist/stencil/p-CsZAW1Cu.js +2 -0
  194. package/dist/stencil/p-CsZAW1Cu.js.map +1 -0
  195. package/dist/stencil/{p-Cw77zP6g.js → p-SGde3HXB.js} +2 -2
  196. package/dist/stencil/p-SGde3HXB.js.map +1 -0
  197. package/dist/stencil/{p-8b831c94.entry.js → p-d702c5af.entry.js} +2 -2
  198. package/dist/stencil/stencil.esm.js +1 -1
  199. package/dist/stencil/stencil.esm.js.map +1 -1
  200. package/dist/types/classes/core/core.class.d.ts +34 -21
  201. package/dist/types/classes/core/store.class.d.ts +8 -0
  202. package/dist/types/classes/objects/base-object.class.d.ts +1 -5
  203. package/dist/types/classes/objects/path.class.d.ts +1 -0
  204. package/dist/types/classes/objects/selection-box.class.d.ts +2 -3
  205. package/dist/types/classes/objects/selection-group.class.d.ts +24 -5
  206. package/dist/types/classes/objects/text.class.d.ts +0 -3
  207. package/dist/types/classes/providers/broadcast-sync-provider.class.d.ts +18 -0
  208. package/dist/types/classes/providers/hocuspocus-sync-provider.class.d.ts +120 -0
  209. package/dist/types/classes/providers/indexeddb-sync-provider.class.d.ts +22 -0
  210. package/dist/types/classes/providers/websocket-sync-provider.class.d.ts +52 -0
  211. package/dist/types/classes/structures/app-state-map.structure.d.ts +30 -0
  212. package/dist/types/classes/structures/object-map.structure.d.ts +39 -1
  213. package/dist/types/components/core/kritzel-editor/kritzel-editor.d.ts +3 -0
  214. package/dist/types/components/core/kritzel-engine/kritzel-engine.d.ts +2 -0
  215. package/dist/types/components.d.ts +6 -0
  216. package/dist/types/configs/{default-engine-state.d.ts → default-engine-config.d.ts} +1 -1
  217. package/dist/types/configs/default-sync.config.d.ts +5 -0
  218. package/dist/types/constants/core.constants.d.ts +0 -0
  219. package/dist/types/index.d.ts +8 -1
  220. package/dist/types/interfaces/debug-info.interface.d.ts +0 -1
  221. package/dist/types/interfaces/engine-state.interface.d.ts +1 -10
  222. package/dist/types/interfaces/object.interface.d.ts +1 -1
  223. package/dist/types/interfaces/selection-state.interface.d.ts +0 -4
  224. package/dist/types/interfaces/sync-config.interface.d.ts +22 -0
  225. package/dist/types/interfaces/sync-provider.interface.d.ts +29 -0
  226. package/dist/types/stencil-public-runtime.d.ts +1 -1
  227. package/package.json +6 -2
  228. package/dist/cjs/index-C9GjuVAx.js.map +0 -1
  229. package/dist/collection/classes/commands/add-object.command.js +0 -18
  230. package/dist/collection/classes/commands/add-object.command.js.map +0 -1
  231. package/dist/collection/classes/commands/add-selection-group.command.js +0 -24
  232. package/dist/collection/classes/commands/add-selection-group.command.js.map +0 -1
  233. package/dist/collection/classes/commands/base.command.js +0 -19
  234. package/dist/collection/classes/commands/base.command.js.map +0 -1
  235. package/dist/collection/classes/commands/batch.command.js +0 -15
  236. package/dist/collection/classes/commands/batch.command.js.map +0 -1
  237. package/dist/collection/classes/commands/move-selection-group.command.js +0 -44
  238. package/dist/collection/classes/commands/move-selection-group.command.js.map +0 -1
  239. package/dist/collection/classes/commands/remove-object.command.js +0 -18
  240. package/dist/collection/classes/commands/remove-object.command.js.map +0 -1
  241. package/dist/collection/classes/commands/remove-selection-group.command.js +0 -19
  242. package/dist/collection/classes/commands/remove-selection-group.command.js.map +0 -1
  243. package/dist/collection/classes/commands/resize-selection-group.command.js +0 -29
  244. package/dist/collection/classes/commands/resize-selection-group.command.js.map +0 -1
  245. package/dist/collection/classes/commands/rotate-selection-group.command.js +0 -29
  246. package/dist/collection/classes/commands/rotate-selection-group.command.js.map +0 -1
  247. package/dist/collection/classes/commands/update-object.command.js +0 -38
  248. package/dist/collection/classes/commands/update-object.command.js.map +0 -1
  249. package/dist/collection/classes/commands/update-viewport.command.js +0 -25
  250. package/dist/collection/classes/commands/update-viewport.command.js.map +0 -1
  251. package/dist/collection/classes/core/command-manager.class.js +0 -51
  252. package/dist/collection/classes/core/command-manager.class.js.map +0 -1
  253. package/dist/collection/classes/core/database.class.js +0 -236
  254. package/dist/collection/classes/core/database.class.js.map +0 -1
  255. package/dist/collection/classes/core/history.class.js +0 -51
  256. package/dist/collection/classes/core/history.class.js.map +0 -1
  257. package/dist/collection/classes/structures/circular-buffer.structure.js +0 -48
  258. package/dist/collection/classes/structures/circular-buffer.structure.js.map +0 -1
  259. package/dist/collection/configs/default-engine-state.js.map +0 -1
  260. package/dist/collection/interfaces/command.interface.js +0 -2
  261. package/dist/collection/interfaces/command.interface.js.map +0 -1
  262. package/dist/components/p-C9hrbrUN.js.map +0 -1
  263. package/dist/components/p-UsToUu6G.js.map +0 -1
  264. package/dist/components/p-YqK8ch2R.js.map +0 -1
  265. package/dist/components/p-kn4eunyR.js +0 -3338
  266. package/dist/components/p-kn4eunyR.js.map +0 -1
  267. package/dist/esm/index-Cw77zP6g.js.map +0 -1
  268. package/dist/esm/index-YVlgItFD.js.map +0 -1
  269. package/dist/stencil/p-Cw77zP6g.js.map +0 -1
  270. package/dist/stencil/p-YVlgItFD.js +0 -2
  271. package/dist/stencil/p-YVlgItFD.js.map +0 -1
  272. package/dist/stencil/p-fe738990.entry.js +0 -2
  273. package/dist/stencil/p-fe738990.entry.js.map +0 -1
  274. package/dist/types/classes/commands/add-object.command.d.ts +0 -9
  275. package/dist/types/classes/commands/add-selection-group.command.d.ts +0 -10
  276. package/dist/types/classes/commands/base.command.d.ts +0 -11
  277. package/dist/types/classes/commands/batch.command.d.ts +0 -8
  278. package/dist/types/classes/commands/move-selection-group.command.d.ts +0 -13
  279. package/dist/types/classes/commands/remove-object.command.d.ts +0 -9
  280. package/dist/types/classes/commands/remove-selection-group.command.d.ts +0 -8
  281. package/dist/types/classes/commands/resize-selection-group.command.d.ts +0 -20
  282. package/dist/types/classes/commands/rotate-selection-group.command.d.ts +0 -10
  283. package/dist/types/classes/commands/update-object.command.d.ts +0 -11
  284. package/dist/types/classes/commands/update-viewport.command.d.ts +0 -21
  285. package/dist/types/classes/core/command-manager.class.d.ts +0 -16
  286. package/dist/types/classes/core/database.class.d.ts +0 -29
  287. package/dist/types/classes/core/history.class.d.ts +0 -12
  288. package/dist/types/classes/structures/circular-buffer.structure.d.ts +0 -13
  289. package/dist/types/interfaces/command.interface.d.ts +0 -6
  290. /package/dist/stencil/{p-8b831c94.entry.js.map → p-d702c5af.entry.js.map} +0 -0
package/dist/esm/index.js CHANGED
@@ -1,2 +1,1359 @@
1
- export { D as DEFAULT_BRUSH_CONFIG, e as DEFAULT_TEXT_CONFIG, K as KritzelBrushTool, d as KritzelEraserTool, u as KritzelImage, f as KritzelImageTool, t as KritzelPath, c as KritzelSelectionTool, s as KritzelText, a as KritzelTextTool, g as KritzelWorkspace } from './index-YVlgItFD.js';
1
+ import { s as setIfUndefined, c as create, f as fromBase64, v as varStorage, t as toBase64, o as onChange, a as createUint8ArrayFromArrayBuffer, b as offChange, w as writeVarUint, e as encodeStateVector, d as writeVarUint8Array, g as encodeStateAsUpdate, r as readVarUint, h as applyUpdate, i as readVarUint8Array, j as readVarString, O as Observable, k as floor, l as getUnixTime, m as equalityDeep, n as writeVarString, p as toUint8Array, q as createEncoder, u as createDecoder, x as map, y as ObservableV2, z as length, A as isNode, B as min, C as pow, H as HocuspocusProvider, D as HocuspocusProviderWebsocket } from './default-text-tool.config-CsZAW1Cu.js';
2
+ export { N as BroadcastSyncProvider, S as DEFAULT_BRUSH_CONFIG, T as DEFAULT_TEXT_CONFIG, P as IndexedDBSyncProvider, Q as KritzelAppStateMap, G as KritzelBrushTool, I as KritzelEraserTool, F as KritzelImage, J as KritzelImageTool, E as KritzelPath, M as KritzelSelectionTool, K as KritzelText, L as KritzelTextTool, R as KritzelWorkspace } from './default-text-tool.config-CsZAW1Cu.js';
3
+
4
+ /* eslint-env browser */
5
+
6
+
7
+ /**
8
+ * @typedef {Object} Channel
9
+ * @property {Set<function(any, any):any>} Channel.subs
10
+ * @property {any} Channel.bc
11
+ */
12
+
13
+ /**
14
+ * @type {Map<string, Channel>}
15
+ */
16
+ const channels = new Map();
17
+
18
+ /* c8 ignore start */
19
+ class LocalStoragePolyfill {
20
+ /**
21
+ * @param {string} room
22
+ */
23
+ constructor (room) {
24
+ this.room = room;
25
+ /**
26
+ * @type {null|function({data:ArrayBuffer}):void}
27
+ */
28
+ this.onmessage = null;
29
+ /**
30
+ * @param {any} e
31
+ */
32
+ this._onChange = e => e.key === room && this.onmessage !== null && this.onmessage({ data: fromBase64(e.newValue || '') });
33
+ onChange(this._onChange);
34
+ }
35
+
36
+ /**
37
+ * @param {ArrayBuffer} buf
38
+ */
39
+ postMessage (buf) {
40
+ varStorage.setItem(this.room, toBase64(createUint8ArrayFromArrayBuffer(buf)));
41
+ }
42
+
43
+ close () {
44
+ offChange(this._onChange);
45
+ }
46
+ }
47
+ /* c8 ignore stop */
48
+
49
+ // Use BroadcastChannel or Polyfill
50
+ /* c8 ignore next */
51
+ const BC = typeof BroadcastChannel === 'undefined' ? LocalStoragePolyfill : BroadcastChannel;
52
+
53
+ /**
54
+ * @param {string} room
55
+ * @return {Channel}
56
+ */
57
+ const getChannel = room =>
58
+ setIfUndefined(channels, room, () => {
59
+ const subs = create();
60
+ const bc = new BC(room);
61
+ /**
62
+ * @param {{data:ArrayBuffer}} e
63
+ */
64
+ /* c8 ignore next */
65
+ bc.onmessage = e => subs.forEach(sub => sub(e.data, 'broadcastchannel'));
66
+ return {
67
+ bc, subs
68
+ }
69
+ });
70
+
71
+ /**
72
+ * Subscribe to global `publish` events.
73
+ *
74
+ * @function
75
+ * @param {string} room
76
+ * @param {function(any, any):any} f
77
+ */
78
+ const subscribe = (room, f) => {
79
+ getChannel(room).subs.add(f);
80
+ return f
81
+ };
82
+
83
+ /**
84
+ * Unsubscribe from `publish` global events.
85
+ *
86
+ * @function
87
+ * @param {string} room
88
+ * @param {function(any, any):any} f
89
+ */
90
+ const unsubscribe = (room, f) => {
91
+ const channel = getChannel(room);
92
+ const unsubscribed = channel.subs.delete(f);
93
+ if (unsubscribed && channel.subs.size === 0) {
94
+ channel.bc.close();
95
+ channels.delete(room);
96
+ }
97
+ return unsubscribed
98
+ };
99
+
100
+ /**
101
+ * Publish data to all subscribers (including subscribers on this tab)
102
+ *
103
+ * @function
104
+ * @param {string} room
105
+ * @param {any} data
106
+ * @param {any} [origin]
107
+ */
108
+ const publish = (room, data, origin = null) => {
109
+ const c = getChannel(room);
110
+ c.bc.postMessage(data);
111
+ c.subs.forEach(sub => sub(data, origin));
112
+ };
113
+
114
+ /**
115
+ * @module sync-protocol
116
+ */
117
+
118
+
119
+ /**
120
+ * @typedef {Map<number, number>} StateMap
121
+ */
122
+
123
+ /**
124
+ * Core Yjs defines two message types:
125
+ * • YjsSyncStep1: Includes the State Set of the sending client. When received, the client should reply with YjsSyncStep2.
126
+ * • YjsSyncStep2: Includes all missing structs and the complete delete set. When received, the client is assured that it
127
+ * received all information from the remote client.
128
+ *
129
+ * In a peer-to-peer network, you may want to introduce a SyncDone message type. Both parties should initiate the connection
130
+ * with SyncStep1. When a client received SyncStep2, it should reply with SyncDone. When the local client received both
131
+ * SyncStep2 and SyncDone, it is assured that it is synced to the remote client.
132
+ *
133
+ * In a client-server model, you want to handle this differently: The client should initiate the connection with SyncStep1.
134
+ * When the server receives SyncStep1, it should reply with SyncStep2 immediately followed by SyncStep1. The client replies
135
+ * with SyncStep2 when it receives SyncStep1. Optionally the server may send a SyncDone after it received SyncStep2, so the
136
+ * client knows that the sync is finished. There are two reasons for this more elaborated sync model: 1. This protocol can
137
+ * easily be implemented on top of http and websockets. 2. The server should only reply to requests, and not initiate them.
138
+ * Therefore it is necessary that the client initiates the sync.
139
+ *
140
+ * Construction of a message:
141
+ * [messageType : varUint, message definition..]
142
+ *
143
+ * Note: A message does not include information about the room name. This must to be handled by the upper layer protocol!
144
+ *
145
+ * stringify[messageType] stringifies a message definition (messageType is already read from the bufffer)
146
+ */
147
+
148
+ const messageYjsSyncStep1 = 0;
149
+ const messageYjsSyncStep2 = 1;
150
+ const messageYjsUpdate = 2;
151
+
152
+ /**
153
+ * Create a sync step 1 message based on the state of the current shared document.
154
+ *
155
+ * @param {encoding.Encoder} encoder
156
+ * @param {Y.Doc} doc
157
+ */
158
+ const writeSyncStep1 = (encoder, doc) => {
159
+ writeVarUint(encoder, messageYjsSyncStep1);
160
+ const sv = encodeStateVector(doc);
161
+ writeVarUint8Array(encoder, sv);
162
+ };
163
+
164
+ /**
165
+ * @param {encoding.Encoder} encoder
166
+ * @param {Y.Doc} doc
167
+ * @param {Uint8Array} [encodedStateVector]
168
+ */
169
+ const writeSyncStep2 = (encoder, doc, encodedStateVector) => {
170
+ writeVarUint(encoder, messageYjsSyncStep2);
171
+ writeVarUint8Array(encoder, encodeStateAsUpdate(doc, encodedStateVector));
172
+ };
173
+
174
+ /**
175
+ * Read SyncStep1 message and reply with SyncStep2.
176
+ *
177
+ * @param {decoding.Decoder} decoder The reply to the received message
178
+ * @param {encoding.Encoder} encoder The received message
179
+ * @param {Y.Doc} doc
180
+ */
181
+ const readSyncStep1 = (decoder, encoder, doc) =>
182
+ writeSyncStep2(encoder, doc, readVarUint8Array(decoder));
183
+
184
+ /**
185
+ * Read and apply Structs and then DeleteStore to a y instance.
186
+ *
187
+ * @param {decoding.Decoder} decoder
188
+ * @param {Y.Doc} doc
189
+ * @param {any} transactionOrigin
190
+ */
191
+ const readSyncStep2 = (decoder, doc, transactionOrigin) => {
192
+ try {
193
+ applyUpdate(doc, readVarUint8Array(decoder), transactionOrigin);
194
+ } catch (error) {
195
+ // This catches errors that are thrown by event handlers
196
+ console.error('Caught error while handling a Yjs update', error);
197
+ }
198
+ };
199
+
200
+ /**
201
+ * @param {encoding.Encoder} encoder
202
+ * @param {Uint8Array} update
203
+ */
204
+ const writeUpdate = (encoder, update) => {
205
+ writeVarUint(encoder, messageYjsUpdate);
206
+ writeVarUint8Array(encoder, update);
207
+ };
208
+
209
+ /**
210
+ * Read and apply Structs and then DeleteStore to a y instance.
211
+ *
212
+ * @param {decoding.Decoder} decoder
213
+ * @param {Y.Doc} doc
214
+ * @param {any} transactionOrigin
215
+ */
216
+ const readUpdate = readSyncStep2;
217
+
218
+ /**
219
+ * @param {decoding.Decoder} decoder A message received from another client
220
+ * @param {encoding.Encoder} encoder The reply message. Does not need to be sent if empty.
221
+ * @param {Y.Doc} doc
222
+ * @param {any} transactionOrigin
223
+ */
224
+ const readSyncMessage = (decoder, encoder, doc, transactionOrigin) => {
225
+ const messageType = readVarUint(decoder);
226
+ switch (messageType) {
227
+ case messageYjsSyncStep1:
228
+ readSyncStep1(decoder, encoder, doc);
229
+ break
230
+ case messageYjsSyncStep2:
231
+ readSyncStep2(decoder, doc, transactionOrigin);
232
+ break
233
+ case messageYjsUpdate:
234
+ readUpdate(decoder, doc, transactionOrigin);
235
+ break
236
+ default:
237
+ throw new Error('Unknown message type')
238
+ }
239
+ return messageType
240
+ };
241
+
242
+ const messagePermissionDenied = 0;
243
+
244
+ /**
245
+ * @callback PermissionDeniedHandler
246
+ * @param {any} y
247
+ * @param {string} reason
248
+ */
249
+
250
+ /**
251
+ *
252
+ * @param {decoding.Decoder} decoder
253
+ * @param {Y.Doc} y
254
+ * @param {PermissionDeniedHandler} permissionDeniedHandler
255
+ */
256
+ const readAuthMessage = (decoder, y, permissionDeniedHandler) => {
257
+ switch (readVarUint(decoder)) {
258
+ case messagePermissionDenied: permissionDeniedHandler(y, readVarString(decoder));
259
+ }
260
+ };
261
+
262
+ /**
263
+ * @module awareness-protocol
264
+ */
265
+
266
+
267
+ const outdatedTimeout = 30000;
268
+
269
+ /**
270
+ * @typedef {Object} MetaClientState
271
+ * @property {number} MetaClientState.clock
272
+ * @property {number} MetaClientState.lastUpdated unix timestamp
273
+ */
274
+
275
+ /**
276
+ * The Awareness class implements a simple shared state protocol that can be used for non-persistent data like awareness information
277
+ * (cursor, username, status, ..). Each client can update its own local state and listen to state changes of
278
+ * remote clients. Every client may set a state of a remote peer to `null` to mark the client as offline.
279
+ *
280
+ * Each client is identified by a unique client id (something we borrow from `doc.clientID`). A client can override
281
+ * its own state by propagating a message with an increasing timestamp (`clock`). If such a message is received, it is
282
+ * applied if the known state of that client is older than the new state (`clock < newClock`). If a client thinks that
283
+ * a remote client is offline, it may propagate a message with
284
+ * `{ clock: currentClientClock, state: null, client: remoteClient }`. If such a
285
+ * message is received, and the known clock of that client equals the received clock, it will override the state with `null`.
286
+ *
287
+ * Before a client disconnects, it should propagate a `null` state with an updated clock.
288
+ *
289
+ * Awareness states must be updated every 30 seconds. Otherwise the Awareness instance will delete the client state.
290
+ *
291
+ * @extends {Observable<string>}
292
+ */
293
+ class Awareness extends Observable {
294
+ /**
295
+ * @param {Y.Doc} doc
296
+ */
297
+ constructor (doc) {
298
+ super();
299
+ this.doc = doc;
300
+ /**
301
+ * @type {number}
302
+ */
303
+ this.clientID = doc.clientID;
304
+ /**
305
+ * Maps from client id to client state
306
+ * @type {Map<number, Object<string, any>>}
307
+ */
308
+ this.states = new Map();
309
+ /**
310
+ * @type {Map<number, MetaClientState>}
311
+ */
312
+ this.meta = new Map();
313
+ this._checkInterval = /** @type {any} */ (setInterval(() => {
314
+ const now = getUnixTime();
315
+ if (this.getLocalState() !== null && (outdatedTimeout / 2 <= now - /** @type {{lastUpdated:number}} */ (this.meta.get(this.clientID)).lastUpdated)) {
316
+ // renew local clock
317
+ this.setLocalState(this.getLocalState());
318
+ }
319
+ /**
320
+ * @type {Array<number>}
321
+ */
322
+ const remove = [];
323
+ this.meta.forEach((meta, clientid) => {
324
+ if (clientid !== this.clientID && outdatedTimeout <= now - meta.lastUpdated && this.states.has(clientid)) {
325
+ remove.push(clientid);
326
+ }
327
+ });
328
+ if (remove.length > 0) {
329
+ removeAwarenessStates(this, remove, 'timeout');
330
+ }
331
+ }, floor(outdatedTimeout / 10)));
332
+ doc.on('destroy', () => {
333
+ this.destroy();
334
+ });
335
+ this.setLocalState({});
336
+ }
337
+
338
+ destroy () {
339
+ this.emit('destroy', [this]);
340
+ this.setLocalState(null);
341
+ super.destroy();
342
+ clearInterval(this._checkInterval);
343
+ }
344
+
345
+ /**
346
+ * @return {Object<string,any>|null}
347
+ */
348
+ getLocalState () {
349
+ return this.states.get(this.clientID) || null
350
+ }
351
+
352
+ /**
353
+ * @param {Object<string,any>|null} state
354
+ */
355
+ setLocalState (state) {
356
+ const clientID = this.clientID;
357
+ const currLocalMeta = this.meta.get(clientID);
358
+ const clock = currLocalMeta === undefined ? 0 : currLocalMeta.clock + 1;
359
+ const prevState = this.states.get(clientID);
360
+ if (state === null) {
361
+ this.states.delete(clientID);
362
+ } else {
363
+ this.states.set(clientID, state);
364
+ }
365
+ this.meta.set(clientID, {
366
+ clock,
367
+ lastUpdated: getUnixTime()
368
+ });
369
+ const added = [];
370
+ const updated = [];
371
+ const filteredUpdated = [];
372
+ const removed = [];
373
+ if (state === null) {
374
+ removed.push(clientID);
375
+ } else if (prevState == null) {
376
+ if (state != null) {
377
+ added.push(clientID);
378
+ }
379
+ } else {
380
+ updated.push(clientID);
381
+ if (!equalityDeep(prevState, state)) {
382
+ filteredUpdated.push(clientID);
383
+ }
384
+ }
385
+ if (added.length > 0 || filteredUpdated.length > 0 || removed.length > 0) {
386
+ this.emit('change', [{ added, updated: filteredUpdated, removed }, 'local']);
387
+ }
388
+ this.emit('update', [{ added, updated, removed }, 'local']);
389
+ }
390
+
391
+ /**
392
+ * @param {string} field
393
+ * @param {any} value
394
+ */
395
+ setLocalStateField (field, value) {
396
+ const state = this.getLocalState();
397
+ if (state !== null) {
398
+ this.setLocalState({
399
+ ...state,
400
+ [field]: value
401
+ });
402
+ }
403
+ }
404
+
405
+ /**
406
+ * @return {Map<number,Object<string,any>>}
407
+ */
408
+ getStates () {
409
+ return this.states
410
+ }
411
+ }
412
+
413
+ /**
414
+ * Mark (remote) clients as inactive and remove them from the list of active peers.
415
+ * This change will be propagated to remote clients.
416
+ *
417
+ * @param {Awareness} awareness
418
+ * @param {Array<number>} clients
419
+ * @param {any} origin
420
+ */
421
+ const removeAwarenessStates = (awareness, clients, origin) => {
422
+ const removed = [];
423
+ for (let i = 0; i < clients.length; i++) {
424
+ const clientID = clients[i];
425
+ if (awareness.states.has(clientID)) {
426
+ awareness.states.delete(clientID);
427
+ if (clientID === awareness.clientID) {
428
+ const curMeta = /** @type {MetaClientState} */ (awareness.meta.get(clientID));
429
+ awareness.meta.set(clientID, {
430
+ clock: curMeta.clock + 1,
431
+ lastUpdated: getUnixTime()
432
+ });
433
+ }
434
+ removed.push(clientID);
435
+ }
436
+ }
437
+ if (removed.length > 0) {
438
+ awareness.emit('change', [{ added: [], updated: [], removed }, origin]);
439
+ awareness.emit('update', [{ added: [], updated: [], removed }, origin]);
440
+ }
441
+ };
442
+
443
+ /**
444
+ * @param {Awareness} awareness
445
+ * @param {Array<number>} clients
446
+ * @return {Uint8Array}
447
+ */
448
+ const encodeAwarenessUpdate = (awareness, clients, states = awareness.states) => {
449
+ const len = clients.length;
450
+ const encoder = createEncoder();
451
+ writeVarUint(encoder, len);
452
+ for (let i = 0; i < len; i++) {
453
+ const clientID = clients[i];
454
+ const state = states.get(clientID) || null;
455
+ const clock = /** @type {MetaClientState} */ (awareness.meta.get(clientID)).clock;
456
+ writeVarUint(encoder, clientID);
457
+ writeVarUint(encoder, clock);
458
+ writeVarString(encoder, JSON.stringify(state));
459
+ }
460
+ return toUint8Array(encoder)
461
+ };
462
+
463
+ /**
464
+ * @param {Awareness} awareness
465
+ * @param {Uint8Array} update
466
+ * @param {any} origin This will be added to the emitted change event
467
+ */
468
+ const applyAwarenessUpdate = (awareness, update, origin) => {
469
+ const decoder = createDecoder(update);
470
+ const timestamp = getUnixTime();
471
+ const added = [];
472
+ const updated = [];
473
+ const filteredUpdated = [];
474
+ const removed = [];
475
+ const len = readVarUint(decoder);
476
+ for (let i = 0; i < len; i++) {
477
+ const clientID = readVarUint(decoder);
478
+ let clock = readVarUint(decoder);
479
+ const state = JSON.parse(readVarString(decoder));
480
+ const clientMeta = awareness.meta.get(clientID);
481
+ const prevState = awareness.states.get(clientID);
482
+ const currClock = clientMeta === undefined ? 0 : clientMeta.clock;
483
+ if (currClock < clock || (currClock === clock && state === null && awareness.states.has(clientID))) {
484
+ if (state === null) {
485
+ // never let a remote client remove this local state
486
+ if (clientID === awareness.clientID && awareness.getLocalState() != null) {
487
+ // remote client removed the local state. Do not remote state. Broadcast a message indicating
488
+ // that this client still exists by increasing the clock
489
+ clock++;
490
+ } else {
491
+ awareness.states.delete(clientID);
492
+ }
493
+ } else {
494
+ awareness.states.set(clientID, state);
495
+ }
496
+ awareness.meta.set(clientID, {
497
+ clock,
498
+ lastUpdated: timestamp
499
+ });
500
+ if (clientMeta === undefined && state !== null) {
501
+ added.push(clientID);
502
+ } else if (clientMeta !== undefined && state === null) {
503
+ removed.push(clientID);
504
+ } else if (state !== null) {
505
+ if (!equalityDeep(state, prevState)) {
506
+ filteredUpdated.push(clientID);
507
+ }
508
+ updated.push(clientID);
509
+ }
510
+ }
511
+ }
512
+ if (added.length > 0 || filteredUpdated.length > 0 || removed.length > 0) {
513
+ awareness.emit('change', [{
514
+ added, updated: filteredUpdated, removed
515
+ }, origin]);
516
+ }
517
+ if (added.length > 0 || updated.length > 0 || removed.length > 0) {
518
+ awareness.emit('update', [{
519
+ added, updated, removed
520
+ }, origin]);
521
+ }
522
+ };
523
+
524
+ /**
525
+ * Utility module to work with urls.
526
+ *
527
+ * @module url
528
+ */
529
+
530
+
531
+ /**
532
+ * @param {Object<string,string>} params
533
+ * @return {string}
534
+ */
535
+ const encodeQueryParams = params =>
536
+ map(params, (val, key) => `${encodeURIComponent(key)}=${encodeURIComponent(val)}`).join('&');
537
+
538
+ /**
539
+ * @module provider/websocket
540
+ */
541
+
542
+
543
+ const messageSync = 0;
544
+ const messageQueryAwareness = 3;
545
+ const messageAwareness = 1;
546
+ const messageAuth = 2;
547
+
548
+ /**
549
+ * encoder, decoder, provider, emitSynced, messageType
550
+ * @type {Array<function(encoding.Encoder, decoding.Decoder, WebsocketProvider, boolean, number):void>}
551
+ */
552
+ const messageHandlers = [];
553
+
554
+ messageHandlers[messageSync] = (
555
+ encoder,
556
+ decoder,
557
+ provider,
558
+ emitSynced,
559
+ _messageType
560
+ ) => {
561
+ writeVarUint(encoder, messageSync);
562
+ const syncMessageType = readSyncMessage(
563
+ decoder,
564
+ encoder,
565
+ provider.doc,
566
+ provider
567
+ );
568
+ if (
569
+ emitSynced && syncMessageType === messageYjsSyncStep2 &&
570
+ !provider.synced
571
+ ) {
572
+ provider.synced = true;
573
+ }
574
+ };
575
+
576
+ messageHandlers[messageQueryAwareness] = (
577
+ encoder,
578
+ _decoder,
579
+ provider,
580
+ _emitSynced,
581
+ _messageType
582
+ ) => {
583
+ writeVarUint(encoder, messageAwareness);
584
+ writeVarUint8Array(
585
+ encoder,
586
+ encodeAwarenessUpdate(
587
+ provider.awareness,
588
+ Array.from(provider.awareness.getStates().keys())
589
+ )
590
+ );
591
+ };
592
+
593
+ messageHandlers[messageAwareness] = (
594
+ _encoder,
595
+ decoder,
596
+ provider,
597
+ _emitSynced,
598
+ _messageType
599
+ ) => {
600
+ applyAwarenessUpdate(
601
+ provider.awareness,
602
+ readVarUint8Array(decoder),
603
+ provider
604
+ );
605
+ };
606
+
607
+ messageHandlers[messageAuth] = (
608
+ _encoder,
609
+ decoder,
610
+ provider,
611
+ _emitSynced,
612
+ _messageType
613
+ ) => {
614
+ readAuthMessage(
615
+ decoder,
616
+ provider.doc,
617
+ (_ydoc, reason) => permissionDeniedHandler(provider, reason)
618
+ );
619
+ };
620
+
621
+ // @todo - this should depend on awareness.outdatedTime
622
+ const messageReconnectTimeout = 30000;
623
+
624
+ /**
625
+ * @param {WebsocketProvider} provider
626
+ * @param {string} reason
627
+ */
628
+ const permissionDeniedHandler = (provider, reason) =>
629
+ console.warn(`Permission denied to access ${provider.url}.\n${reason}`);
630
+
631
+ /**
632
+ * @param {WebsocketProvider} provider
633
+ * @param {Uint8Array} buf
634
+ * @param {boolean} emitSynced
635
+ * @return {encoding.Encoder}
636
+ */
637
+ const readMessage = (provider, buf, emitSynced) => {
638
+ const decoder = createDecoder(buf);
639
+ const encoder = createEncoder();
640
+ const messageType = readVarUint(decoder);
641
+ const messageHandler = provider.messageHandlers[messageType];
642
+ if (/** @type {any} */ (messageHandler)) {
643
+ messageHandler(encoder, decoder, provider, emitSynced, messageType);
644
+ } else {
645
+ console.error('Unable to compute message');
646
+ }
647
+ return encoder
648
+ };
649
+
650
+ /**
651
+ * Outsource this function so that a new websocket connection is created immediately.
652
+ * I suspect that the `ws.onclose` event is not always fired if there are network issues.
653
+ *
654
+ * @param {WebsocketProvider} provider
655
+ * @param {WebSocket} ws
656
+ * @param {CloseEvent | null} event
657
+ */
658
+ const closeWebsocketConnection = (provider, ws, event) => {
659
+ if (ws === provider.ws) {
660
+ provider.emit('connection-close', [event, provider]);
661
+ provider.ws = null;
662
+ ws.close();
663
+ provider.wsconnecting = false;
664
+ if (provider.wsconnected) {
665
+ provider.wsconnected = false;
666
+ provider.synced = false;
667
+ // update awareness (all users except local left)
668
+ removeAwarenessStates(
669
+ provider.awareness,
670
+ Array.from(provider.awareness.getStates().keys()).filter((client) =>
671
+ client !== provider.doc.clientID
672
+ ),
673
+ provider
674
+ );
675
+ provider.emit('status', [{
676
+ status: 'disconnected'
677
+ }]);
678
+ } else {
679
+ provider.wsUnsuccessfulReconnects++;
680
+ }
681
+ // Start with no reconnect timeout and increase timeout by
682
+ // using exponential backoff starting with 100ms
683
+ setTimeout(
684
+ setupWS,
685
+ min(
686
+ pow(2, provider.wsUnsuccessfulReconnects) * 100,
687
+ provider.maxBackoffTime
688
+ ),
689
+ provider
690
+ );
691
+ }
692
+ };
693
+
694
+ /**
695
+ * @param {WebsocketProvider} provider
696
+ */
697
+ const setupWS = (provider) => {
698
+ if (provider.shouldConnect && provider.ws === null) {
699
+ const websocket = new provider._WS(provider.url, provider.protocols);
700
+ websocket.binaryType = 'arraybuffer';
701
+ provider.ws = websocket;
702
+ provider.wsconnecting = true;
703
+ provider.wsconnected = false;
704
+ provider.synced = false;
705
+
706
+ websocket.onmessage = (event) => {
707
+ provider.wsLastMessageReceived = getUnixTime();
708
+ const encoder = readMessage(provider, new Uint8Array(event.data), true);
709
+ if (length(encoder) > 1) {
710
+ websocket.send(toUint8Array(encoder));
711
+ }
712
+ };
713
+ websocket.onerror = (event) => {
714
+ provider.emit('connection-error', [event, provider]);
715
+ };
716
+ websocket.onclose = (event) => {
717
+ closeWebsocketConnection(provider, websocket, event);
718
+ };
719
+ websocket.onopen = () => {
720
+ provider.wsLastMessageReceived = getUnixTime();
721
+ provider.wsconnecting = false;
722
+ provider.wsconnected = true;
723
+ provider.wsUnsuccessfulReconnects = 0;
724
+ provider.emit('status', [{
725
+ status: 'connected'
726
+ }]);
727
+ // always send sync step 1 when connected
728
+ const encoder = createEncoder();
729
+ writeVarUint(encoder, messageSync);
730
+ writeSyncStep1(encoder, provider.doc);
731
+ websocket.send(toUint8Array(encoder));
732
+ // broadcast local awareness state
733
+ if (provider.awareness.getLocalState() !== null) {
734
+ const encoderAwarenessState = createEncoder();
735
+ writeVarUint(encoderAwarenessState, messageAwareness);
736
+ writeVarUint8Array(
737
+ encoderAwarenessState,
738
+ encodeAwarenessUpdate(provider.awareness, [
739
+ provider.doc.clientID
740
+ ])
741
+ );
742
+ websocket.send(toUint8Array(encoderAwarenessState));
743
+ }
744
+ };
745
+ provider.emit('status', [{
746
+ status: 'connecting'
747
+ }]);
748
+ }
749
+ };
750
+
751
+ /**
752
+ * @param {WebsocketProvider} provider
753
+ * @param {ArrayBuffer} buf
754
+ */
755
+ const broadcastMessage = (provider, buf) => {
756
+ const ws = provider.ws;
757
+ if (provider.wsconnected && ws && ws.readyState === ws.OPEN) {
758
+ ws.send(buf);
759
+ }
760
+ if (provider.bcconnected) {
761
+ publish(provider.bcChannel, buf, provider);
762
+ }
763
+ };
764
+
765
+ /**
766
+ * Websocket Provider for Yjs. Creates a websocket connection to sync the shared document.
767
+ * The document name is attached to the provided url. I.e. the following example
768
+ * creates a websocket connection to http://localhost:1234/my-document-name
769
+ *
770
+ * @example
771
+ * import * as Y from 'yjs'
772
+ * import { WebsocketProvider } from 'y-websocket'
773
+ * const doc = new Y.Doc()
774
+ * const provider = new WebsocketProvider('http://localhost:1234', 'my-document-name', doc)
775
+ *
776
+ * @extends {ObservableV2<{ 'connection-close': (event: CloseEvent | null, provider: WebsocketProvider) => any, 'status': (event: { status: 'connected' | 'disconnected' | 'connecting' }) => any, 'connection-error': (event: Event, provider: WebsocketProvider) => any, 'sync': (state: boolean) => any }>}
777
+ */
778
+ class WebsocketProvider extends ObservableV2 {
779
+ /**
780
+ * @param {string} serverUrl
781
+ * @param {string} roomname
782
+ * @param {Y.Doc} doc
783
+ * @param {object} opts
784
+ * @param {boolean} [opts.connect]
785
+ * @param {awarenessProtocol.Awareness} [opts.awareness]
786
+ * @param {Object<string,string>} [opts.params] specify url parameters
787
+ * @param {Array<string>} [opts.protocols] specify websocket protocols
788
+ * @param {typeof WebSocket} [opts.WebSocketPolyfill] Optionall provide a WebSocket polyfill
789
+ * @param {number} [opts.resyncInterval] Request server state every `resyncInterval` milliseconds
790
+ * @param {number} [opts.maxBackoffTime] Maximum amount of time to wait before trying to reconnect (we try to reconnect using exponential backoff)
791
+ * @param {boolean} [opts.disableBc] Disable cross-tab BroadcastChannel communication
792
+ */
793
+ constructor (serverUrl, roomname, doc, {
794
+ connect = true,
795
+ awareness = new Awareness(doc),
796
+ params = {},
797
+ protocols = [],
798
+ WebSocketPolyfill = WebSocket,
799
+ resyncInterval = -1,
800
+ maxBackoffTime = 2500,
801
+ disableBc = false
802
+ } = {}) {
803
+ super();
804
+ // ensure that serverUrl does not end with /
805
+ while (serverUrl[serverUrl.length - 1] === '/') {
806
+ serverUrl = serverUrl.slice(0, serverUrl.length - 1);
807
+ }
808
+ this.serverUrl = serverUrl;
809
+ this.bcChannel = serverUrl + '/' + roomname;
810
+ this.maxBackoffTime = maxBackoffTime;
811
+ /**
812
+ * The specified url parameters. This can be safely updated. The changed parameters will be used
813
+ * when a new connection is established.
814
+ * @type {Object<string,string>}
815
+ */
816
+ this.params = params;
817
+ this.protocols = protocols;
818
+ this.roomname = roomname;
819
+ this.doc = doc;
820
+ this._WS = WebSocketPolyfill;
821
+ this.awareness = awareness;
822
+ this.wsconnected = false;
823
+ this.wsconnecting = false;
824
+ this.bcconnected = false;
825
+ this.disableBc = disableBc;
826
+ this.wsUnsuccessfulReconnects = 0;
827
+ this.messageHandlers = messageHandlers.slice();
828
+ /**
829
+ * @type {boolean}
830
+ */
831
+ this._synced = false;
832
+ /**
833
+ * @type {WebSocket?}
834
+ */
835
+ this.ws = null;
836
+ this.wsLastMessageReceived = 0;
837
+ /**
838
+ * Whether to connect to other peers or not
839
+ * @type {boolean}
840
+ */
841
+ this.shouldConnect = connect;
842
+
843
+ /**
844
+ * @type {number}
845
+ */
846
+ this._resyncInterval = 0;
847
+ if (resyncInterval > 0) {
848
+ this._resyncInterval = /** @type {any} */ (setInterval(() => {
849
+ if (this.ws && this.ws.readyState === WebSocket.OPEN) {
850
+ // resend sync step 1
851
+ const encoder = createEncoder();
852
+ writeVarUint(encoder, messageSync);
853
+ writeSyncStep1(encoder, doc);
854
+ this.ws.send(toUint8Array(encoder));
855
+ }
856
+ }, resyncInterval));
857
+ }
858
+
859
+ /**
860
+ * @param {ArrayBuffer} data
861
+ * @param {any} origin
862
+ */
863
+ this._bcSubscriber = (data, origin) => {
864
+ if (origin !== this) {
865
+ const encoder = readMessage(this, new Uint8Array(data), false);
866
+ if (length(encoder) > 1) {
867
+ publish(this.bcChannel, toUint8Array(encoder), this);
868
+ }
869
+ }
870
+ };
871
+ /**
872
+ * Listens to Yjs updates and sends them to remote peers (ws and broadcastchannel)
873
+ * @param {Uint8Array} update
874
+ * @param {any} origin
875
+ */
876
+ this._updateHandler = (update, origin) => {
877
+ if (origin !== this) {
878
+ const encoder = createEncoder();
879
+ writeVarUint(encoder, messageSync);
880
+ writeUpdate(encoder, update);
881
+ broadcastMessage(this, toUint8Array(encoder));
882
+ }
883
+ };
884
+ this.doc.on('update', this._updateHandler);
885
+ /**
886
+ * @param {any} changed
887
+ * @param {any} _origin
888
+ */
889
+ this._awarenessUpdateHandler = ({ added, updated, removed }, _origin) => {
890
+ const changedClients = added.concat(updated).concat(removed);
891
+ const encoder = createEncoder();
892
+ writeVarUint(encoder, messageAwareness);
893
+ writeVarUint8Array(
894
+ encoder,
895
+ encodeAwarenessUpdate(awareness, changedClients)
896
+ );
897
+ broadcastMessage(this, toUint8Array(encoder));
898
+ };
899
+ this._exitHandler = () => {
900
+ removeAwarenessStates(
901
+ this.awareness,
902
+ [doc.clientID],
903
+ 'app closed'
904
+ );
905
+ };
906
+ if (isNode && typeof process !== 'undefined') {
907
+ process.on('exit', this._exitHandler);
908
+ }
909
+ awareness.on('update', this._awarenessUpdateHandler);
910
+ this._checkInterval = /** @type {any} */ (setInterval(() => {
911
+ if (
912
+ this.wsconnected &&
913
+ messageReconnectTimeout <
914
+ getUnixTime() - this.wsLastMessageReceived
915
+ ) {
916
+ // no message received in a long time - not even your own awareness
917
+ // updates (which are updated every 15 seconds)
918
+ closeWebsocketConnection(this, /** @type {WebSocket} */ (this.ws), null);
919
+ }
920
+ }, messageReconnectTimeout / 10));
921
+ if (connect) {
922
+ this.connect();
923
+ }
924
+ }
925
+
926
+ get url () {
927
+ const encodedParams = encodeQueryParams(this.params);
928
+ return this.serverUrl + '/' + this.roomname +
929
+ (encodedParams.length === 0 ? '' : '?' + encodedParams)
930
+ }
931
+
932
+ /**
933
+ * @type {boolean}
934
+ */
935
+ get synced () {
936
+ return this._synced
937
+ }
938
+
939
+ set synced (state) {
940
+ if (this._synced !== state) {
941
+ this._synced = state;
942
+ // @ts-ignore
943
+ this.emit('synced', [state]);
944
+ this.emit('sync', [state]);
945
+ }
946
+ }
947
+
948
+ destroy () {
949
+ if (this._resyncInterval !== 0) {
950
+ clearInterval(this._resyncInterval);
951
+ }
952
+ clearInterval(this._checkInterval);
953
+ this.disconnect();
954
+ if (isNode && typeof process !== 'undefined') {
955
+ process.off('exit', this._exitHandler);
956
+ }
957
+ this.awareness.off('update', this._awarenessUpdateHandler);
958
+ this.doc.off('update', this._updateHandler);
959
+ super.destroy();
960
+ }
961
+
962
+ connectBc () {
963
+ if (this.disableBc) {
964
+ return
965
+ }
966
+ if (!this.bcconnected) {
967
+ subscribe(this.bcChannel, this._bcSubscriber);
968
+ this.bcconnected = true;
969
+ }
970
+ // send sync step1 to bc
971
+ // write sync step 1
972
+ const encoderSync = createEncoder();
973
+ writeVarUint(encoderSync, messageSync);
974
+ writeSyncStep1(encoderSync, this.doc);
975
+ publish(this.bcChannel, toUint8Array(encoderSync), this);
976
+ // broadcast local state
977
+ const encoderState = createEncoder();
978
+ writeVarUint(encoderState, messageSync);
979
+ writeSyncStep2(encoderState, this.doc);
980
+ publish(this.bcChannel, toUint8Array(encoderState), this);
981
+ // write queryAwareness
982
+ const encoderAwarenessQuery = createEncoder();
983
+ writeVarUint(encoderAwarenessQuery, messageQueryAwareness);
984
+ publish(
985
+ this.bcChannel,
986
+ toUint8Array(encoderAwarenessQuery),
987
+ this
988
+ );
989
+ // broadcast local awareness state
990
+ const encoderAwarenessState = createEncoder();
991
+ writeVarUint(encoderAwarenessState, messageAwareness);
992
+ writeVarUint8Array(
993
+ encoderAwarenessState,
994
+ encodeAwarenessUpdate(this.awareness, [
995
+ this.doc.clientID
996
+ ])
997
+ );
998
+ publish(
999
+ this.bcChannel,
1000
+ toUint8Array(encoderAwarenessState),
1001
+ this
1002
+ );
1003
+ }
1004
+
1005
+ disconnectBc () {
1006
+ // broadcast message with local awareness state set to null (indicating disconnect)
1007
+ const encoder = createEncoder();
1008
+ writeVarUint(encoder, messageAwareness);
1009
+ writeVarUint8Array(
1010
+ encoder,
1011
+ encodeAwarenessUpdate(this.awareness, [
1012
+ this.doc.clientID
1013
+ ], new Map())
1014
+ );
1015
+ broadcastMessage(this, toUint8Array(encoder));
1016
+ if (this.bcconnected) {
1017
+ unsubscribe(this.bcChannel, this._bcSubscriber);
1018
+ this.bcconnected = false;
1019
+ }
1020
+ }
1021
+
1022
+ disconnect () {
1023
+ this.shouldConnect = false;
1024
+ this.disconnectBc();
1025
+ if (this.ws !== null) {
1026
+ closeWebsocketConnection(this, this.ws, null);
1027
+ }
1028
+ }
1029
+
1030
+ connect () {
1031
+ this.shouldConnect = true;
1032
+ if (!this.wsconnected && this.ws === null) {
1033
+ setupWS(this);
1034
+ this.connectBc();
1035
+ }
1036
+ }
1037
+ }
1038
+
1039
+ /**
1040
+ * WebSocket sync provider for real-time collaboration
1041
+ */
1042
+ class WebSocketSyncProvider {
1043
+ provider;
1044
+ isConnected = false;
1045
+ constructor(docName, doc, options) {
1046
+ const url = options?.url || 'ws://localhost:1234';
1047
+ const roomName = options?.roomName || docName;
1048
+ this.provider = new WebsocketProvider(url, roomName, doc, {
1049
+ params: options?.params,
1050
+ protocols: options?.protocols,
1051
+ WebSocketPolyfill: options?.WebSocketPolyfill,
1052
+ awareness: options?.awareness,
1053
+ maxBackoffTime: options?.maxBackoffTime,
1054
+ disableBc: true,
1055
+ });
1056
+ this.setupEventListeners();
1057
+ console.info(`WebSocket Provider initialized: ${url}/${roomName}`);
1058
+ }
1059
+ /**
1060
+ * Static factory method for creating WebSocketSyncProvider with configuration options
1061
+ * Returns a ProviderFactory that can be used in sync configuration
1062
+ */
1063
+ static with(options) {
1064
+ return {
1065
+ create: (docName, doc) => {
1066
+ return new WebSocketSyncProvider(docName, doc, options);
1067
+ },
1068
+ };
1069
+ }
1070
+ setupEventListeners() {
1071
+ this.provider.on('status', ({ status }) => {
1072
+ if (status === 'connected') {
1073
+ this.isConnected = true;
1074
+ console.info('WebSocket connected');
1075
+ }
1076
+ else if (status === 'disconnected') {
1077
+ this.isConnected = false;
1078
+ console.info('WebSocket disconnected');
1079
+ }
1080
+ });
1081
+ this.provider.on('sync', (synced) => {
1082
+ if (synced) {
1083
+ console.info('WebSocket synced');
1084
+ }
1085
+ });
1086
+ }
1087
+ async connect() {
1088
+ if (this.isConnected) {
1089
+ return;
1090
+ }
1091
+ return new Promise((resolve, reject) => {
1092
+ const timeout = setTimeout(() => {
1093
+ reject(new Error('WebSocket connection timeout'));
1094
+ }, 10000); // 10 second timeout
1095
+ const statusHandler = ({ status }) => {
1096
+ if (status === 'connected') {
1097
+ clearTimeout(timeout);
1098
+ this.provider.off('status', statusHandler);
1099
+ this.isConnected = true;
1100
+ resolve();
1101
+ }
1102
+ };
1103
+ this.provider.on('status', statusHandler);
1104
+ // If already connected, resolve immediately
1105
+ if (this.provider.wsconnected) {
1106
+ clearTimeout(timeout);
1107
+ this.provider.off('status', statusHandler);
1108
+ this.isConnected = true;
1109
+ resolve();
1110
+ }
1111
+ });
1112
+ }
1113
+ disconnect() {
1114
+ if (this.provider) {
1115
+ this.provider.disconnect();
1116
+ }
1117
+ this.isConnected = false;
1118
+ }
1119
+ destroy() {
1120
+ if (this.provider) {
1121
+ this.provider.destroy();
1122
+ }
1123
+ this.isConnected = false;
1124
+ }
1125
+ }
1126
+
1127
+ /**
1128
+ * Hocuspocus sync provider for real-time collaboration
1129
+ * Supports multiplexing - multiple documents can share the same WebSocket connection
1130
+ */
1131
+ class HocuspocusSyncProvider {
1132
+ provider;
1133
+ isConnected = false;
1134
+ isSynced = false;
1135
+ usesSharedSocket = false;
1136
+ // Static shared WebSocket instance for multiplexing
1137
+ static sharedWebSocketProvider = null;
1138
+ constructor(docName, doc, options) {
1139
+ const name = options?.name || docName;
1140
+ const url = options?.url || 'ws://localhost:1234';
1141
+ // Use provided websocketProvider or the static shared one
1142
+ const websocketProvider = options?.websocketProvider || HocuspocusSyncProvider.sharedWebSocketProvider;
1143
+ if (websocketProvider) {
1144
+ // Multiplexing mode - use shared WebSocket connection
1145
+ this.usesSharedSocket = true;
1146
+ const config = {
1147
+ websocketProvider,
1148
+ name,
1149
+ document: doc,
1150
+ token: options?.token || null,
1151
+ onConnect: () => {
1152
+ this.isConnected = true;
1153
+ if (!options?.quiet) {
1154
+ console.info(`Hocuspocus connected: ${name}`);
1155
+ }
1156
+ if (options?.onConnect) {
1157
+ options.onConnect();
1158
+ }
1159
+ },
1160
+ onDisconnect: () => {
1161
+ this.isConnected = false;
1162
+ this.isSynced = false;
1163
+ if (!options?.quiet) {
1164
+ console.info(`Hocuspocus disconnected: ${name}`);
1165
+ }
1166
+ if (options?.onDisconnect) {
1167
+ options.onDisconnect();
1168
+ }
1169
+ },
1170
+ onSynced: () => {
1171
+ this.isSynced = true;
1172
+ if (!options?.quiet) {
1173
+ console.info(`Hocuspocus synced: ${name}`);
1174
+ }
1175
+ if (options?.onSynced) {
1176
+ options.onSynced();
1177
+ }
1178
+ },
1179
+ };
1180
+ // Add optional settings
1181
+ if (options?.forceSyncInterval !== undefined) {
1182
+ config.forceSyncInterval = options.forceSyncInterval;
1183
+ }
1184
+ if (options?.onAuthenticationFailed) {
1185
+ config.onAuthenticationFailed = options.onAuthenticationFailed;
1186
+ }
1187
+ if (options?.onStatus) {
1188
+ config.onStatus = options.onStatus;
1189
+ }
1190
+ this.provider = new HocuspocusProvider(config);
1191
+ // Must call attach() explicitly when using shared socket
1192
+ this.provider.attach();
1193
+ if (!options?.quiet) {
1194
+ console.info(`Hocuspocus Provider initialized (multiplexed): ${name}`);
1195
+ }
1196
+ }
1197
+ else {
1198
+ // Standalone mode - create own WebSocket connection
1199
+ this.usesSharedSocket = false;
1200
+ const config = {
1201
+ url,
1202
+ name,
1203
+ document: doc,
1204
+ token: options?.token || null,
1205
+ onConnect: () => {
1206
+ this.isConnected = true;
1207
+ if (!options?.quiet) {
1208
+ console.info(`Hocuspocus connected: ${name}`);
1209
+ }
1210
+ if (options?.onConnect) {
1211
+ options.onConnect();
1212
+ }
1213
+ },
1214
+ onDisconnect: () => {
1215
+ this.isConnected = false;
1216
+ this.isSynced = false;
1217
+ if (!options?.quiet) {
1218
+ console.info(`Hocuspocus disconnected: ${name}`);
1219
+ }
1220
+ if (options?.onDisconnect) {
1221
+ options.onDisconnect();
1222
+ }
1223
+ },
1224
+ onSynced: () => {
1225
+ this.isSynced = true;
1226
+ if (!options?.quiet) {
1227
+ console.info(`Hocuspocus synced: ${name}`);
1228
+ }
1229
+ if (options?.onSynced) {
1230
+ options.onSynced();
1231
+ }
1232
+ },
1233
+ };
1234
+ // Add optional settings
1235
+ if (options?.forceSyncInterval !== undefined) {
1236
+ config.forceSyncInterval = options.forceSyncInterval;
1237
+ }
1238
+ if (options?.onAuthenticationFailed) {
1239
+ config.onAuthenticationFailed = options.onAuthenticationFailed;
1240
+ }
1241
+ if (options?.onStatus) {
1242
+ config.onStatus = options.onStatus;
1243
+ }
1244
+ if (options?.WebSocketPolyfill) {
1245
+ config.WebSocketPolyfill = options.WebSocketPolyfill;
1246
+ }
1247
+ this.provider = new HocuspocusProvider(config);
1248
+ if (!options?.quiet) {
1249
+ console.info(`Hocuspocus Provider initialized: ${url}/${name}`);
1250
+ }
1251
+ }
1252
+ }
1253
+ /**
1254
+ * Create a shared WebSocket connection for multiplexing
1255
+ * Call this once to create a shared connection that multiple providers can use
1256
+ */
1257
+ static createSharedWebSocket(options) {
1258
+ if (HocuspocusSyncProvider.sharedWebSocketProvider) {
1259
+ console.warn('Shared WebSocket already exists. Returning existing instance.');
1260
+ return HocuspocusSyncProvider.sharedWebSocketProvider;
1261
+ }
1262
+ const config = {
1263
+ url: options.url,
1264
+ };
1265
+ if (options.WebSocketPolyfill) {
1266
+ config.WebSocketPolyfill = options.WebSocketPolyfill;
1267
+ }
1268
+ if (options.onConnect) {
1269
+ config.onConnect = options.onConnect;
1270
+ }
1271
+ if (options.onDisconnect) {
1272
+ config.onDisconnect = options.onDisconnect;
1273
+ }
1274
+ if (options.onStatus) {
1275
+ config.onStatus = options.onStatus;
1276
+ }
1277
+ HocuspocusSyncProvider.sharedWebSocketProvider = new HocuspocusProviderWebsocket(config);
1278
+ console.info(`Shared Hocuspocus WebSocket created: ${options.url}`);
1279
+ return HocuspocusSyncProvider.sharedWebSocketProvider;
1280
+ }
1281
+ /**
1282
+ * Destroy the shared WebSocket connection
1283
+ * Call this when you're done with all multiplexed providers
1284
+ */
1285
+ static destroySharedWebSocket() {
1286
+ if (HocuspocusSyncProvider.sharedWebSocketProvider) {
1287
+ HocuspocusSyncProvider.sharedWebSocketProvider.destroy();
1288
+ HocuspocusSyncProvider.sharedWebSocketProvider = null;
1289
+ console.info('Shared Hocuspocus WebSocket destroyed');
1290
+ }
1291
+ }
1292
+ /**
1293
+ * Get the shared WebSocket provider instance (if it exists)
1294
+ */
1295
+ static getSharedWebSocket() {
1296
+ return HocuspocusSyncProvider.sharedWebSocketProvider;
1297
+ }
1298
+ /**
1299
+ * Static factory method for creating HocuspocusSyncProvider with configuration options
1300
+ * Returns a ProviderFactory that can be used in sync configuration
1301
+ */
1302
+ static with(options) {
1303
+ return {
1304
+ create: (docName, doc) => {
1305
+ return new HocuspocusSyncProvider(docName, doc, options);
1306
+ },
1307
+ };
1308
+ }
1309
+ async connect() {
1310
+ if (this.isSynced) {
1311
+ return;
1312
+ }
1313
+ return new Promise((resolve, reject) => {
1314
+ const timeout = setTimeout(() => {
1315
+ reject(new Error('Hocuspocus connection timeout'));
1316
+ }, 10000); // 10 second timeout
1317
+ const syncHandler = () => {
1318
+ clearTimeout(timeout);
1319
+ this.provider.off('synced', syncHandler);
1320
+ resolve();
1321
+ };
1322
+ this.provider.on('synced', syncHandler);
1323
+ // If already synced, resolve immediately
1324
+ if (this.provider.isSynced) {
1325
+ clearTimeout(timeout);
1326
+ this.provider.off('synced', syncHandler);
1327
+ resolve();
1328
+ return;
1329
+ }
1330
+ // Connect if not already connected (standalone mode only)
1331
+ if (!this.isConnected && !this.usesSharedSocket) {
1332
+ this.provider.connect();
1333
+ }
1334
+ });
1335
+ }
1336
+ disconnect() {
1337
+ if (this.provider) {
1338
+ if (this.usesSharedSocket) {
1339
+ // Detach from shared socket instead of disconnecting
1340
+ this.provider.detach();
1341
+ }
1342
+ else {
1343
+ this.provider.disconnect();
1344
+ }
1345
+ }
1346
+ this.isConnected = false;
1347
+ this.isSynced = false;
1348
+ }
1349
+ destroy() {
1350
+ if (this.provider) {
1351
+ this.provider.destroy();
1352
+ }
1353
+ this.isConnected = false;
1354
+ this.isSynced = false;
1355
+ }
1356
+ }
1357
+
1358
+ export { HocuspocusSyncProvider, WebSocketSyncProvider };
2
1359
  //# sourceMappingURL=index.js.map