kritzel-stencil 0.1.36 → 0.1.38

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 (145) hide show
  1. package/dist/cjs/{alignment.enum-CrGpUOOc.js → alignment.enum-CDKwHF7c.js} +211 -2940
  2. package/dist/cjs/index-DIJLnoqQ.js +2 -2
  3. package/dist/cjs/index.cjs.js +2863 -58
  4. package/dist/cjs/{kritzel-back-to-content_32.cjs.entry.js → kritzel-back-to-content_33.cjs.entry.js} +1170 -226
  5. package/dist/cjs/loader.cjs.js +1 -1
  6. package/dist/cjs/stencil.cjs.js +1 -1
  7. package/dist/collection/classes/core/core.class.js +10 -10
  8. package/dist/collection/classes/core/viewport.class.js +39 -0
  9. package/dist/collection/classes/core/workspace.class.js +12 -2
  10. package/dist/collection/classes/objects/group.class.js +38 -3
  11. package/dist/collection/classes/objects/selection-group.class.js +2 -2
  12. package/dist/collection/classes/objects/shape.class.js +2 -2
  13. package/dist/collection/classes/objects/text.class.js +31 -6
  14. package/dist/collection/classes/registries/icon-registry.class.js +1 -0
  15. package/dist/collection/classes/structures/object-map.structure.js +24 -3
  16. package/dist/collection/classes/tools/brush-tool.class.js +4 -8
  17. package/dist/collection/classes/tools/line-tool.class.js +30 -20
  18. package/dist/collection/classes/tools/selection-tool.class.js +2 -2
  19. package/dist/collection/collection-manifest.json +1 -0
  20. package/dist/collection/components/core/kritzel-cursor-trail/kritzel-cursor-trail.js +1 -1
  21. package/dist/collection/components/core/kritzel-editor/kritzel-editor.js +132 -16
  22. package/dist/collection/components/core/kritzel-engine/kritzel-engine.js +695 -8
  23. package/dist/collection/components/shared/kritzel-color/kritzel-color.js +2 -2
  24. package/dist/collection/components/shared/kritzel-color-palette/kritzel-color-palette.js +1 -1
  25. package/dist/collection/components/shared/kritzel-font/kritzel-font.js +1 -1
  26. package/dist/collection/components/shared/kritzel-font-size/kritzel-font-size.js +1 -1
  27. package/dist/collection/components/shared/kritzel-master-detail/kritzel-master-detail.js +3 -3
  28. package/dist/collection/components/shared/kritzel-menu/kritzel-menu.js +1 -1
  29. package/dist/collection/components/shared/kritzel-menu-item/kritzel-menu-item.js +2 -2
  30. package/dist/collection/components/shared/kritzel-numeric-input/kritzel-numeric-input.js +1 -1
  31. package/dist/collection/components/shared/kritzel-opacity-slider/kritzel-opacity-slider.js +1 -1
  32. package/dist/collection/components/shared/kritzel-pill-tabs/kritzel-pill-tabs.css +58 -0
  33. package/dist/collection/components/shared/kritzel-pill-tabs/kritzel-pill-tabs.js +135 -0
  34. package/dist/collection/components/shared/kritzel-portal/kritzel-portal.js +1 -1
  35. package/dist/collection/components/shared/kritzel-shape-fill/kritzel-shape-fill.js +2 -2
  36. package/dist/collection/components/shared/kritzel-slide-toggle/kritzel-slide-toggle.js +1 -1
  37. package/dist/collection/components/shared/kritzel-split-button/kritzel-split-button.js +1 -1
  38. package/dist/collection/components/shared/kritzel-stroke-size/kritzel-stroke-size.js +1 -1
  39. package/dist/collection/components/shared/kritzel-tooltip/kritzel-tooltip.js +3 -3
  40. package/dist/collection/components/ui/kritzel-back-to-content/kritzel-back-to-content.js +1 -1
  41. package/dist/collection/components/ui/kritzel-controls/kritzel-controls.js +10 -10
  42. package/dist/collection/components/ui/kritzel-export/kritzel-export.css +79 -31
  43. package/dist/collection/components/ui/kritzel-export/kritzel-export.js +94 -7
  44. package/dist/collection/components/ui/kritzel-more-menu/kritzel-more-menu.js +7 -2
  45. package/dist/collection/components/ui/kritzel-settings/kritzel-settings.js +1 -1
  46. package/dist/collection/components/ui/kritzel-utility-panel/kritzel-utility-panel.js +1 -1
  47. package/dist/collection/constants/version.js +1 -1
  48. package/dist/collection/helpers/svg-export.helper.js +403 -0
  49. package/dist/components/index.d.ts +2 -0
  50. package/dist/components/index.js +1 -1
  51. package/dist/components/kritzel-back-to-content.js +1 -1
  52. package/dist/components/kritzel-brush-style.js +1 -1
  53. package/dist/components/kritzel-color-palette.js +1 -1
  54. package/dist/components/kritzel-color.js +1 -1
  55. package/dist/components/kritzel-context-menu.js +1 -1
  56. package/dist/components/kritzel-controls.js +1 -1
  57. package/dist/components/kritzel-cursor-trail.js +1 -1
  58. package/dist/components/kritzel-editor.js +1 -1
  59. package/dist/components/kritzel-engine.js +1 -1
  60. package/dist/components/kritzel-export.js +1 -1
  61. package/dist/components/kritzel-font-size.js +1 -1
  62. package/dist/components/kritzel-font.js +1 -1
  63. package/dist/components/kritzel-icon.js +1 -1
  64. package/dist/components/kritzel-master-detail.js +1 -1
  65. package/dist/components/kritzel-menu-item.js +1 -1
  66. package/dist/components/kritzel-menu.js +1 -1
  67. package/dist/components/kritzel-more-menu.js +1 -1
  68. package/dist/components/kritzel-numeric-input.js +1 -1
  69. package/dist/components/kritzel-opacity-slider.js +1 -1
  70. package/dist/components/kritzel-pill-tabs.d.ts +11 -0
  71. package/dist/components/kritzel-pill-tabs.js +1 -0
  72. package/dist/components/kritzel-portal.js +1 -1
  73. package/dist/components/kritzel-settings.js +1 -1
  74. package/dist/components/kritzel-shape-fill.js +1 -1
  75. package/dist/components/kritzel-slide-toggle.js +1 -1
  76. package/dist/components/kritzel-split-button.js +1 -1
  77. package/dist/components/kritzel-stroke-size.js +1 -1
  78. package/dist/components/kritzel-tool-config.js +1 -1
  79. package/dist/components/kritzel-tooltip.js +1 -1
  80. package/dist/components/kritzel-utility-panel.js +1 -1
  81. package/dist/components/kritzel-workspace-manager.js +1 -1
  82. package/dist/components/{p-CCkRb3eU.js → p-7sLJBaSO.js} +1 -1
  83. package/dist/components/{p-CHWFmCfa.js → p-B0hPXBSL.js} +1 -1
  84. package/dist/components/p-BKGzcc4f.js +1 -0
  85. package/dist/components/{p-C3IWgklZ.js → p-BOwGipF7.js} +1 -1
  86. package/dist/components/{p-DZStVW3N.js → p-BZDomNAb.js} +1 -1
  87. package/dist/components/{p-C5dvI2GL.js → p-BlhchRQG.js} +1 -1
  88. package/dist/components/p-BuW--ssq.js +1 -0
  89. package/dist/components/{p-BcewUES6.js → p-ByNLD9rO.js} +1 -1
  90. package/dist/components/{p-nmsfI-Ml.js → p-ByOUWWeg.js} +1 -1
  91. package/dist/components/{p-BSEpekaB.js → p-CGPCnrW0.js} +1 -1
  92. package/dist/components/p-CJgOhXpF.js +1 -0
  93. package/dist/components/p-CTJdmqGT.js +1 -0
  94. package/dist/components/{p-BkqfzvGW.js → p-C_SElym1.js} +1 -1
  95. package/dist/components/p-CixkzBoE.js +1 -0
  96. package/dist/components/p-Cveey2Qi.js +9 -0
  97. package/dist/components/{p-CQIAQIxh.js → p-CzBSVjV9.js} +1 -1
  98. package/dist/components/{p-BtRuIMOs.js → p-D4Qo20ct.js} +1 -1
  99. package/dist/components/{p-C-vDRJeV.js → p-DOt_rIxS.js} +1 -1
  100. package/dist/components/p-DRMo3iHo.js +1 -0
  101. package/dist/components/p-Db95HMsD.js +1 -0
  102. package/dist/components/{p-B_nV7GBe.js → p-Dh4H1HH2.js} +1 -1
  103. package/dist/components/{p-Ciqstenj.js → p-Dpe6nRQf.js} +1 -1
  104. package/dist/components/{p-CNHUUPnK.js → p-KSZ60yWF.js} +1 -1
  105. package/dist/components/{p-CwzXB6DI.js → p-MbXFAtTC.js} +1 -1
  106. package/dist/components/{p-BjxcLWBm.js → p-Y4oj5ghv.js} +1 -1
  107. package/dist/components/{p-CgxnZAk7.js → p-_TQBZuO2.js} +1 -1
  108. package/dist/components/{p-Bd_-DUbl.js → p-mS3xn_f8.js} +1 -1
  109. package/dist/components/{p-CwieoFh-.js → p-oB28KY6p.js} +1 -1
  110. package/dist/components/{p-CxHPqkP7.js → p-toJ5mHHq.js} +1 -1
  111. package/dist/esm/{alignment.enum-CO0RVpkN.js → alignment.enum-BlHtjITm.js} +211 -2939
  112. package/dist/esm/index-CS1qgEOS.js +2 -2
  113. package/dist/esm/index.js +2859 -56
  114. package/dist/esm/{kritzel-back-to-content_32.entry.js → kritzel-back-to-content_33.entry.js} +1170 -227
  115. package/dist/esm/loader.js +1 -1
  116. package/dist/esm/stencil.js +1 -1
  117. package/dist/stencil/index.esm.js +1 -1
  118. package/dist/stencil/p-7e418c32.entry.js +9 -0
  119. package/dist/stencil/p-BlHtjITm.js +1 -0
  120. package/dist/stencil/stencil.esm.js +1 -1
  121. package/dist/types/classes/core/viewport.class.d.ts +7 -0
  122. package/dist/types/classes/core/workspace.class.d.ts +7 -1
  123. package/dist/types/classes/objects/group.class.d.ts +11 -0
  124. package/dist/types/classes/objects/text.class.d.ts +8 -1
  125. package/dist/types/classes/structures/object-map.structure.d.ts +7 -0
  126. package/dist/types/classes/tools/line-tool.class.d.ts +2 -0
  127. package/dist/types/components/core/kritzel-editor/kritzel-editor.d.ts +6 -2
  128. package/dist/types/components/core/kritzel-engine/kritzel-engine.d.ts +84 -0
  129. package/dist/types/components/shared/kritzel-pill-tabs/kritzel-pill-tabs.d.ts +26 -0
  130. package/dist/types/components/ui/kritzel-export/kritzel-export.d.ts +20 -2
  131. package/dist/types/components.d.ts +168 -7
  132. package/dist/types/constants/version.d.ts +1 -1
  133. package/dist/types/helpers/svg-export.helper.d.ts +139 -0
  134. package/package.json +1 -4
  135. package/dist/collection/e2e/pages/editor.page.js +0 -794
  136. package/dist/components/p-4sM1hDrm.js +0 -1
  137. package/dist/components/p-C6XYpnTo.js +0 -1
  138. package/dist/components/p-Cn1Fa1dI.js +0 -1
  139. package/dist/components/p-DBch4fvu.js +0 -1
  140. package/dist/components/p-DNPNsX4H.js +0 -1
  141. package/dist/components/p-DSWCnSDK.js +0 -1
  142. package/dist/components/p-Dp49yshY.js +0 -9
  143. package/dist/stencil/p-CO0RVpkN.js +0 -1
  144. package/dist/stencil/p-a28b6b28.entry.js +0 -9
  145. package/dist/types/e2e/pages/editor.page.d.ts +0 -373
@@ -7415,11 +7415,11 @@ class Plugin {
7415
7415
  */
7416
7416
  getState(state) { return state[this.key]; }
7417
7417
  }
7418
- const keys$1 = Object.create(null);
7418
+ const keys = Object.create(null);
7419
7419
  function createKey(name) {
7420
- if (name in keys$1)
7421
- return name + "$" + ++keys$1[name];
7422
- keys$1[name] = 0;
7420
+ if (name in keys)
7421
+ return name + "$" + ++keys[name];
7422
+ keys[name] = 0;
7423
7423
  return name + "$";
7424
7424
  }
7425
7425
 
@@ -15100,7 +15100,13 @@ class KritzelText extends KritzelBaseObject {
15100
15100
  }
15101
15101
  /**
15102
15102
  * Constructs a new KritzelText instance with optional configuration.
15103
+ * Can be used by consumers to create text objects programmatically:
15104
+ * ```ts
15105
+ * const text = new KritzelText({ text: 'Hello World', translateX: 100, translateY: 100 });
15106
+ * await editor.addObject(text);
15107
+ * ```
15103
15108
  * @param config - Optional configuration object.
15109
+ * @param config.text - Initial plain text content.
15104
15110
  * @param config.translateX - Initial X translation position.
15105
15111
  * @param config.translateY - Initial Y translation position.
15106
15112
  * @param config.fontSize - Font size in points (default: 8).
@@ -15116,16 +15122,33 @@ class KritzelText extends KritzelBaseObject {
15116
15122
  */
15117
15123
  constructor(config) {
15118
15124
  super();
15125
+ // Always create the editor so setContent() works immediately
15126
+ this.editor = this.createEditor();
15119
15127
  if (config) {
15120
15128
  this.translateX = config.translateX || 0;
15121
15129
  this.translateY = config.translateY || 0;
15122
15130
  this.fontSize = config.fontSize || 8;
15123
15131
  this.fontFamily = config.fontFamily || 'Arial';
15124
15132
  this.fontColor = config.fontColor || { light: '#000000', dark: '#ffffff' };
15125
- this.width = this.initialWidth / (this._core.store.state.scale < 0 ? this._core.store.state.scale : 1);
15126
- this.height = (this.fontSize * 1.2) / (this._core.store.state.scale < 0 ? this._core.store.state.scale : 1);
15133
+ // Guard _core access: scale may not be available when created by consumers
15134
+ const coreScale = this._core?.store?.state?.scale;
15135
+ const effectiveScale = coreScale != null && coreScale < 0 ? coreScale : 1;
15136
+ this.width = this.initialWidth / effectiveScale;
15137
+ this.height = (this.fontSize * 1.2) / effectiveScale;
15127
15138
  this.scale = config.scale || 1;
15128
15139
  this.scaleFactor = config.scaleX ?? 1;
15140
+ // Set initial text content if provided
15141
+ if (config.text) {
15142
+ this.setContent({
15143
+ type: 'doc',
15144
+ content: [
15145
+ {
15146
+ type: 'paragraph',
15147
+ content: [{ type: 'text', text: config.text }],
15148
+ },
15149
+ ],
15150
+ });
15151
+ }
15129
15152
  }
15130
15153
  }
15131
15154
  /**
@@ -15146,15 +15169,17 @@ class KritzelText extends KritzelBaseObject {
15146
15169
  object.fontFamily = fontFamily;
15147
15170
  object.translateX = 0;
15148
15171
  object.translateY = 0;
15149
- object.width = object.initialWidth / (object._core.store.state.scale < 0 ? object._core.store.state.scale : 1);
15150
- object.height = (object.fontSize * 1.2) / (object._core.store.state.scale < 0 ? object._core.store.state.scale : 1);
15172
+ const coreScale = core.store.state.scale;
15173
+ const effectiveScale = coreScale < 0 ? coreScale : 1;
15174
+ object.width = object.initialWidth / effectiveScale;
15175
+ object.height = (object.fontSize * 1.2) / effectiveScale;
15151
15176
  object.backgroundColor = { light: 'transparent', dark: 'transparent' };
15152
15177
  object.initialWidth = object.width;
15153
15178
  object.initialHeight = object.height;
15154
15179
  object.scaleFactor = 1;
15155
- object.scale = scale ?? object._core.store.state.scale;
15180
+ object.scale = scale ?? coreScale;
15156
15181
  object.zIndex = core.store.currentZIndex;
15157
- object.editor = object.createEditor();
15182
+ // Editor is already created in the constructor
15158
15183
  return object;
15159
15184
  }
15160
15185
  /**
@@ -16863,6 +16888,12 @@ class KritzelLine extends KritzelBaseObject {
16863
16888
  }
16864
16889
  }
16865
16890
 
16891
+ class KritzelClassHelper {
16892
+ static isInstanceOf(object, className) {
16893
+ return !!object && object.__class__ === className;
16894
+ }
16895
+ }
16896
+
16866
16897
  /**
16867
16898
  * KritzelGroup represents a permanent grouping of objects that act as a single unit.
16868
16899
  *
@@ -16882,6 +16913,12 @@ class KritzelGroup extends KritzelBaseObject {
16882
16913
  * Children can be any KritzelBaseObject, including other KritzelGroups for nesting.
16883
16914
  */
16884
16915
  childIds = [];
16916
+ /**
16917
+ * Holds child objects that have been added via addChild() before the group
16918
+ * is added to the engine. Once the group is added (and _core is set),
16919
+ * these are flushed into the store and this array is cleared.
16920
+ */
16921
+ _pendingChildren = [];
16885
16922
  /**
16886
16923
  * Snapshots of child states for transformation operations (resize, rotate).
16887
16924
  * Similar pattern to KritzelSelectionGroup.
@@ -16899,6 +16936,9 @@ class KritzelGroup extends KritzelBaseObject {
16899
16936
  * @returns An array of KritzelBaseObject instances representing the group's children.
16900
16937
  */
16901
16938
  get children() {
16939
+ if (!this._core) {
16940
+ return this._pendingChildren;
16941
+ }
16902
16942
  return this.childIds
16903
16943
  .map(id => {
16904
16944
  const found = this._core.store.state.objects.filter(obj => obj.id === id);
@@ -16973,6 +17013,10 @@ class KritzelGroup extends KritzelBaseObject {
16973
17013
  addChild(object) {
16974
17014
  if (!this.childIds.includes(object.id)) {
16975
17015
  this.childIds.push(object.id);
17016
+ if (!this._core) {
17017
+ this._pendingChildren.push(object);
17018
+ return;
17019
+ }
16976
17020
  this.refreshBoundingBox();
16977
17021
  this.captureChildSnapshots();
16978
17022
  }
@@ -17008,6 +17052,27 @@ class KritzelGroup extends KritzelBaseObject {
17008
17052
  this.refreshBoundingBox();
17009
17053
  this.captureChildSnapshots();
17010
17054
  }
17055
+ /**
17056
+ * Moves the group and all its children to the center of the viewport.
17057
+ * Overrides the base implementation to ensure children move with the group.
17058
+ */
17059
+ centerInViewport() {
17060
+ const { viewportWidth, viewportHeight, translateX: viewportTranslateX, translateY: viewportTranslateY, scale: viewportScale } = this._core.store.state;
17061
+ const { x, y, width, height } = this.rotatedBoundingBox;
17062
+ const objectCenterX = x + width / 2;
17063
+ const objectCenterY = y + height / 2;
17064
+ const targetCenterX = (viewportWidth / 2 - viewportTranslateX) / viewportScale;
17065
+ const targetCenterY = (viewportHeight / 2 - viewportTranslateY) / viewportScale;
17066
+ const deltaX = targetCenterX - objectCenterX;
17067
+ const deltaY = targetCenterY - objectCenterY;
17068
+ this.translateX += deltaX;
17069
+ this.translateY += deltaY;
17070
+ this.children.forEach(child => {
17071
+ child.translateX += deltaX;
17072
+ child.translateY += deltaY;
17073
+ });
17074
+ this.captureChildSnapshots();
17075
+ }
17011
17076
  /**
17012
17077
  * Recalculates the group's bounding box based on its children.
17013
17078
  * Handles three cases: empty group (zero dimensions), single child (inherit its bounds),
@@ -17294,7 +17359,7 @@ class KritzelGroup extends KritzelBaseObject {
17294
17359
  copiedChildren.push(copiedChild);
17295
17360
  copiedGroup.childIds.push(copiedChild.id);
17296
17361
  });
17297
- // Store copied children on the group for paste() to access
17362
+ // Store copied children on the group for paste() to handle
17298
17363
  copiedGroup._pendingChildren = copiedChildren;
17299
17364
  // Copy group properties
17300
17365
  copiedGroup.rotation = this.rotation;
@@ -17311,7 +17376,7 @@ class KritzelGroup extends KritzelBaseObject {
17311
17376
  * @returns A serializable plain object representation of this group.
17312
17377
  */
17313
17378
  serialize() {
17314
- const { _core, _elementRef, element, totalWidth, totalHeight, unchangedChildSnapshots, ...remainingProps } = this;
17379
+ const { _core, _elementRef, element, totalWidth, totalHeight, unchangedChildSnapshots, _pendingChildren, ...remainingProps } = this;
17315
17380
  const clonedProps = structuredClone(remainingProps);
17316
17381
  // Convert Map to plain object for serialization
17317
17382
  clonedProps.unchangedChildSnapshots = Object.fromEntries(this.unchangedChildSnapshots);
@@ -17348,7 +17413,7 @@ class KritzelGroup extends KritzelBaseObject {
17348
17413
  this.children.forEach(child => {
17349
17414
  child.workspaceId = workspaceId;
17350
17415
  // Recursively update nested groups
17351
- if (child instanceof KritzelGroup) {
17416
+ if (KritzelClassHelper.isInstanceOf(child, 'KritzelGroup')) {
17352
17417
  child.updateWorkspaceId(workspaceId);
17353
17418
  }
17354
17419
  });
@@ -17428,7 +17493,7 @@ class KritzelShape extends KritzelBaseObject {
17428
17493
  this.height = config.height ?? 100;
17429
17494
  this.shapeType = config.shapeType ?? exports.ShapeType.Rectangle;
17430
17495
  this.fillColor = config.fillColor ?? { light: 'transparent', dark: 'transparent' };
17431
- this.strokeColor = config.strokeColor ?? { light: '#000000', dark: '#ffffff' };
17496
+ this.strokeColor = config.strokeColor ?? config.fillColor ?? { light: '#000000', dark: '#ffffff' };
17432
17497
  this.strokeWidth = config.strokeWidth ?? 4;
17433
17498
  this.fontSize = config.fontSize ?? 16;
17434
17499
  this.fontFamily = config.fontFamily ?? 'Arial';
@@ -17458,7 +17523,7 @@ class KritzelShape extends KritzelBaseObject {
17458
17523
  object.height = config?.height ?? 100;
17459
17524
  object.shapeType = config?.shapeType ?? exports.ShapeType.Rectangle;
17460
17525
  object.fillColor = config?.fillColor ?? { light: 'transparent', dark: 'transparent' };
17461
- object.strokeColor = config?.strokeColor ?? { light: '#000000', dark: '#ffffff' };
17526
+ object.strokeColor = config?.strokeColor ?? config?.fillColor ?? { light: '#000000', dark: '#ffffff' };
17462
17527
  object.strokeWidth = config?.strokeWidth ?? 4;
17463
17528
  object.opacity = config?.opacity ?? 1;
17464
17529
  object.fontSize = config?.fontSize ?? 16;
@@ -18168,8 +18233,7 @@ class KritzelBrushTool extends KritzelBaseTool {
18168
18233
  }
18169
18234
  if (event.pointerType === 'mouse') {
18170
18235
  if (this._core.store.state.isDrawing && this._currentPathId) {
18171
- const matchingObjects = this._core.store.state.objects.filter(o => o.id === this._currentPathId);
18172
- const currentPath = matchingObjects.length > 0 ? matchingObjects[0] : null;
18236
+ const currentPath = this._core.store.state.objects.findById(this._currentPathId);
18173
18237
  if (currentPath) {
18174
18238
  const viewportScale = this._core.store.state.scale;
18175
18239
  const lockScale = this._core.store.state.lockDrawingScale;
@@ -18197,8 +18261,7 @@ class KritzelBrushTool extends KritzelBaseTool {
18197
18261
  const activePointers = Array.from(this._core.store.state.pointers.values());
18198
18262
  if (activePointers.length === 1) {
18199
18263
  if (this._currentPathId) {
18200
- const matchingObjects = this._core.store.state.objects.filter(o => o.id === this._currentPathId);
18201
- const currentPath = matchingObjects.length > 0 ? matchingObjects[0] : null;
18264
+ const currentPath = this._core.store.state.objects.findById(this._currentPathId);
18202
18265
  if (currentPath) {
18203
18266
  const viewportScale = this._core.store.state.scale;
18204
18267
  const lockScale = this._core.store.state.lockDrawingScale;
@@ -18238,8 +18301,7 @@ class KritzelBrushTool extends KritzelBaseTool {
18238
18301
  if (this._core.store.state.isDrawing) {
18239
18302
  this._core.store.state.isDrawing = false;
18240
18303
  if (this._currentPathId) {
18241
- const matchingObjects = this._core.store.state.objects.filter(o => o.id === this._currentPathId);
18242
- const currentPath = matchingObjects.length > 0 ? matchingObjects[0] : null;
18304
+ const currentPath = this._core.store.state.objects.findById(this._currentPathId);
18243
18305
  if (currentPath) {
18244
18306
  currentPath.isCompleted = true;
18245
18307
  this._core.store.state.objects.update(currentPath);
@@ -18254,8 +18316,7 @@ class KritzelBrushTool extends KritzelBaseTool {
18254
18316
  if (this._core.store.state.isDrawing) {
18255
18317
  this._core.store.state.isDrawing = false;
18256
18318
  if (this._currentPathId) {
18257
- const matchingObjects = this._core.store.state.objects.filter(o => o.id === this._currentPathId);
18258
- const currentPath = matchingObjects.length > 0 ? matchingObjects[0] : null;
18319
+ const currentPath = this._core.store.state.objects.findById(this._currentPathId);
18259
18320
  if (currentPath) {
18260
18321
  currentPath.isCompleted = true;
18261
18322
  this._core.store.state.objects.update(currentPath);
@@ -18425,7 +18486,7 @@ class KritzelSelectionGroup extends KritzelBaseObject {
18425
18486
  this.captureUnchangedSnapshots(children);
18426
18487
  // Groups need their own child snapshots aligned with the current transform.
18427
18488
  for (const obj of children) {
18428
- if (obj instanceof KritzelGroup) {
18489
+ if (KritzelClassHelper.isInstanceOf(obj, 'KritzelGroup')) {
18429
18490
  obj.finalize();
18430
18491
  }
18431
18492
  }
@@ -18866,6 +18927,8 @@ class KritzelLineTool extends KritzelBaseTool {
18866
18927
  _startX = 0;
18867
18928
  /** Y coordinate of the line's starting point */
18868
18929
  _startY = 0;
18930
+ /** Tracks the ID of the line currently being drawn */
18931
+ _currentLineId = null;
18869
18932
  /**
18870
18933
  * Creates a new instance of KritzelLineTool.
18871
18934
  * @param core - The KritzelCore instance that provides access to application state and functionality
@@ -18907,6 +18970,7 @@ class KritzelLineTool extends KritzelBaseTool {
18907
18970
  arrows: this.arrows,
18908
18971
  });
18909
18972
  line.isCompleted = false;
18973
+ this._currentLineId = line.id;
18910
18974
  this._core.store.state.objects.insert(line);
18911
18975
  }
18912
18976
  }
@@ -18935,6 +18999,7 @@ class KritzelLineTool extends KritzelBaseTool {
18935
18999
  arrows: this.arrows,
18936
19000
  });
18937
19001
  line.isCompleted = false;
19002
+ this._currentLineId = line.id;
18938
19003
  this._core.store.state.objects.insert(line);
18939
19004
  }
18940
19005
  }
@@ -18950,8 +19015,8 @@ class KritzelLineTool extends KritzelBaseTool {
18950
19015
  event.preventDefault();
18951
19016
  }
18952
19017
  if (event.pointerType === 'mouse') {
18953
- if (this._core.store.state.isDrawing) {
18954
- const currentLine = this._core.store.currentLine;
19018
+ if (this._core.store.state.isDrawing && this._currentLineId) {
19019
+ const currentLine = this._core.store.state.objects.findById(this._currentLineId);
18955
19020
  if (currentLine) {
18956
19021
  const viewportScale = this._core.store.state.scale;
18957
19022
  const lockScale = this._core.store.state.lockDrawingScale;
@@ -18981,8 +19046,8 @@ class KritzelLineTool extends KritzelBaseTool {
18981
19046
  }
18982
19047
  if (event.pointerType === 'touch' || event.pointerType === 'pen') {
18983
19048
  const activePointers = Array.from(this._core.store.state.pointers.values());
18984
- if (activePointers.length === 1) {
18985
- const currentLine = this._core.store.currentLine;
19049
+ if (activePointers.length === 1 && this._currentLineId) {
19050
+ const currentLine = this._core.store.state.objects.findById(this._currentLineId);
18986
19051
  if (currentLine) {
18987
19052
  const viewportScale = this._core.store.state.scale;
18988
19053
  const lockScale = this._core.store.state.lockDrawingScale;
@@ -19024,28 +19089,34 @@ class KritzelLineTool extends KritzelBaseTool {
19024
19089
  if (event.pointerType === 'mouse') {
19025
19090
  if (this._core.store.state.isDrawing) {
19026
19091
  this._core.store.state.isDrawing = false;
19027
- const currentLine = this._core.store.currentLine;
19028
- if (currentLine) {
19029
- currentLine.isCompleted = true;
19030
- this._core.store.state.objects.update(currentLine);
19031
- this._core.engine.emitObjectsChange();
19032
- this._core.engine.emitObjectsAdded([currentLine]);
19033
- // Switch to selection tool and select the drawn line
19034
- this.selectLineAndSwitchTool(currentLine);
19092
+ if (this._currentLineId) {
19093
+ const currentLine = this._core.store.state.objects.findById(this._currentLineId);
19094
+ if (currentLine) {
19095
+ currentLine.isCompleted = true;
19096
+ this._core.store.state.objects.update(currentLine);
19097
+ this._core.engine.emitObjectsChange();
19098
+ this._core.engine.emitObjectsAdded([currentLine]);
19099
+ // Switch to selection tool and select the drawn line
19100
+ this.selectLineAndSwitchTool(currentLine);
19101
+ }
19102
+ this._currentLineId = null;
19035
19103
  }
19036
19104
  }
19037
19105
  }
19038
19106
  if (event.pointerType === 'touch' || event.pointerType === 'pen') {
19039
19107
  if (this._core.store.state.isDrawing) {
19040
19108
  this._core.store.state.isDrawing = false;
19041
- const currentLine = this._core.store.currentLine;
19042
- if (currentLine) {
19043
- currentLine.isCompleted = true;
19044
- this._core.store.state.objects.update(currentLine);
19045
- this._core.engine.emitObjectsChange();
19046
- this._core.engine.emitObjectsAdded([currentLine]);
19047
- // Switch to selection tool and select the drawn line
19048
- this.selectLineAndSwitchTool(currentLine);
19109
+ if (this._currentLineId) {
19110
+ const currentLine = this._core.store.state.objects.findById(this._currentLineId);
19111
+ if (currentLine) {
19112
+ currentLine.isCompleted = true;
19113
+ this._core.store.state.objects.update(currentLine);
19114
+ this._core.engine.emitObjectsChange();
19115
+ this._core.engine.emitObjectsAdded([currentLine]);
19116
+ // Switch to selection tool and select the drawn line
19117
+ this.selectLineAndSwitchTool(currentLine);
19118
+ }
19119
+ this._currentLineId = null;
19049
19120
  }
19050
19121
  }
19051
19122
  }
@@ -19770,6 +19841,7 @@ KritzelIconRegistry.registerIcons({
19770
19841
  'send-to-back': '<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-arrow-down-to-line-icon lucide-arrow-down-to-line"><path d="M12 17V3"/><path d="m6 11 6 6 6-6"/><path d="M19 21H5"/></svg>',
19771
19842
  'select-all': '<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-square-mouse-pointer-icon lucide-square-mouse-pointer"><path d="M12.034 12.681a.498.498 0 0 1 .647-.647l9 3.5a.5.5 0 0 1-.033.943l-3.444 1.068a1 1 0 0 0-.66.66l-1.067 3.443a.5.5 0 0 1-.943.033z"/><path d="M21 11V5a2 2 0 0 0-2-2H5a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h6"/></svg>',
19772
19843
  'download': '<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-download-icon lucide-download"><path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"/><polyline points="7 10 12 15 17 10"/><line x1="12" x2="12" y1="15" y2="3"/></svg>',
19844
+ 'upload': '<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-upload-icon lucide-upload"><path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"/><polyline points="17 8 12 3 7 8"/><line x1="12" x2="12" y1="3" y2="15"/></svg>',
19773
19845
  'undo': '<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-undo-icon lucide-undo"><path d="M3 7v6h6"/><path d="M21 17a9 9 0 0 0-9-9 9 9 0 0 0-6 2.3L3 13"/></svg>',
19774
19846
  'redo': '<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-redo-icon lucide-redo"><path d="M21 7v6h-6"/><path d="M3 17a9 9 0 0 1 9-9 9 9 0 0 1 6 2.3l3 2.7"/></svg>',
19775
19847
  'plus': '<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-plus-icon lucide-plus"><path d="M5 12h14"/><path d="M12 5v14"/></svg>',
@@ -21630,7 +21702,7 @@ class KritzelSelectionTool extends KritzelBaseTool {
21630
21702
  flattenObjects(objects) {
21631
21703
  let result = [];
21632
21704
  for (const obj of objects) {
21633
- if (obj instanceof KritzelGroup) {
21705
+ if (KritzelClassHelper.isInstanceOf(obj, 'KritzelGroup')) {
21634
21706
  result = result.concat(this.flattenObjects(obj.children));
21635
21707
  }
21636
21708
  else {
@@ -22164,2902 +22236,108 @@ class IndexedDBSyncProvider {
22164
22236
  }
22165
22237
  }
22166
22238
 
22239
+ /** Current version of the workspace export format */
22240
+ const WORKSPACE_EXPORT_VERSION = '1.0.0';
22167
22241
  /**
22168
- * Common Math expressions.
22169
- *
22170
- * @module math
22171
- */
22172
-
22173
- const floor$1 = Math.floor;
22174
-
22175
- /**
22176
- * @function
22177
- * @param {number} a
22178
- * @param {number} b
22179
- * @return {number} The smaller element of a and b
22180
- */
22181
- const min$1 = (a, b) => a < b ? a : b;
22182
-
22183
- /**
22184
- * @function
22185
- * @param {number} a
22186
- * @param {number} b
22187
- * @return {number} The bigger element of a and b
22188
- */
22189
- const max$1 = (a, b) => a > b ? a : b;
22190
-
22191
- /* eslint-env browser */
22192
-
22193
- const BIT8$1 = 128;
22194
- const BITS7$1 = 127;
22195
-
22196
- /**
22197
- * Utility helpers for working with numbers.
22198
- *
22199
- * @module number
22200
- */
22201
-
22202
-
22203
- const MAX_SAFE_INTEGER$1 = Number.MAX_SAFE_INTEGER;
22204
-
22205
- /**
22206
- * @param {string} str
22207
- * @return {Uint8Array}
22208
- */
22209
- const _encodeUtf8Polyfill$1 = str => {
22210
- const encodedString = unescape(encodeURIComponent(str));
22211
- const len = encodedString.length;
22212
- const buf = new Uint8Array(len);
22213
- for (let i = 0; i < len; i++) {
22214
- buf[i] = /** @type {number} */ (encodedString.codePointAt(i));
22215
- }
22216
- return buf
22217
- };
22218
-
22219
- /* c8 ignore next */
22220
- const utf8TextEncoder$1 = /** @type {TextEncoder} */ (typeof TextEncoder !== 'undefined' ? new TextEncoder() : null);
22221
-
22222
- /**
22223
- * @param {string} str
22224
- * @return {Uint8Array}
22225
- */
22226
- const _encodeUtf8Native$1 = str => utf8TextEncoder$1.encode(str);
22227
-
22228
- /**
22229
- * @param {string} str
22230
- * @return {Uint8Array}
22242
+ * Represents a workspace in the Kritzel canvas.
22243
+ * A workspace is an isolated canvas area with its own set of objects and viewport state.
22244
+ * It can be serialized and deserialized for persistence and collaborative editing.
22231
22245
  */
22232
- /* c8 ignore next */
22233
- const encodeUtf8$1 = utf8TextEncoder$1 ? _encodeUtf8Native$1 : _encodeUtf8Polyfill$1;
22234
-
22235
- /* c8 ignore next */
22236
- let utf8TextDecoder$1 = typeof TextDecoder === 'undefined' ? null : new TextDecoder('utf-8', { fatal: true, ignoreBOM: true });
22237
-
22238
- /* c8 ignore start */
22239
- if (utf8TextDecoder$1 && utf8TextDecoder$1.decode(new Uint8Array()).length === 1) {
22240
- // Safari doesn't handle BOM correctly.
22241
- // This fixes a bug in Safari 13.0.5 where it produces a BOM the first time it is called.
22242
- // utf8TextDecoder.decode(new Uint8Array()).length === 1 on the first call and
22243
- // utf8TextDecoder.decode(new Uint8Array()).length === 1 on the second call
22244
- // Another issue is that from then on no BOM chars are recognized anymore
22245
- /* c8 ignore next */
22246
- utf8TextDecoder$1 = null;
22246
+ class KritzelWorkspace {
22247
+ __class__ = 'KritzelWorkspace';
22248
+ /** Unique identifier for the workspace */
22249
+ id;
22250
+ /** Display name of the workspace */
22251
+ name;
22252
+ /** Timestamp when the workspace was created */
22253
+ createdAt;
22254
+ /** Timestamp when the workspace was last updated */
22255
+ updatedAt;
22256
+ /** Viewport state containing pan and zoom information */
22257
+ viewport;
22258
+ /** Reference to the KritzelCore instance managing this workspace */
22259
+ _core;
22260
+ /**
22261
+ * Creates a new KritzelWorkspace instance.
22262
+ * @param id - Unique identifier for the workspace
22263
+ * @param name - Display name of the workspace
22264
+ * @param viewport - Initial viewport state with translateX, translateY, and scale values
22265
+ */
22266
+ constructor(id, name, viewport = { translateX: 0, translateY: 0, scale: 1 }) {
22267
+ this.id = id;
22268
+ this.name = name;
22269
+ this.createdAt = new Date();
22270
+ this.updatedAt = new Date();
22271
+ this.viewport = viewport;
22272
+ }
22273
+ /**
22274
+ * Factory method to create a workspace with a core reference.
22275
+ * @param core - The KritzelCore instance to associate with this workspace
22276
+ * @param obj - Object containing workspace properties (id, name, viewport)
22277
+ * @returns A new KritzelWorkspace instance with the core reference set
22278
+ */
22279
+ static create(core, obj) {
22280
+ const workspace = new KritzelWorkspace(obj.id, obj.name, obj.viewport);
22281
+ workspace._core = core;
22282
+ return workspace;
22283
+ }
22284
+ /**
22285
+ * Adds an object to this workspace via the core engine.
22286
+ * @param obj - The object to add to the workspace
22287
+ */
22288
+ addObject(obj) {
22289
+ this._core.engine.addObject(obj);
22290
+ }
22291
+ /**
22292
+ * Serializes the workspace to a plain object for storage or transmission.
22293
+ * Converts dates to ISO strings and includes the class identifier.
22294
+ * @param options - Optional settings for serialization
22295
+ * @param options.includeObjects - If true, includes all canvas objects in the serialized output
22296
+ * @returns A plain object representation of the workspace
22297
+ */
22298
+ serialize(options) {
22299
+ const serialized = {
22300
+ __class__: this.__class__,
22301
+ version: WORKSPACE_EXPORT_VERSION,
22302
+ id: this.id,
22303
+ name: this.name,
22304
+ createdAt: this.createdAt.toISOString(),
22305
+ updatedAt: this.updatedAt.toISOString(),
22306
+ viewport: this.viewport,
22307
+ };
22308
+ if (options?.includeObjects && this._core) {
22309
+ const allObjects = this._core.store.allNonSelectionObjects;
22310
+ serialized.objects = allObjects.map(obj => obj.serialize());
22311
+ }
22312
+ return serialized;
22313
+ }
22314
+ /**
22315
+ * Deserializes a plain object into this workspace instance.
22316
+ * Converts ISO string dates back to Date objects.
22317
+ * @param object - The plain object to deserialize from
22318
+ * @returns This workspace instance with updated properties
22319
+ */
22320
+ deserialize(object) {
22321
+ Object.assign(this, object);
22322
+ this.createdAt = new Date(object.createdAt);
22323
+ this.updatedAt = new Date(object.updatedAt);
22324
+ return this;
22325
+ }
22247
22326
  }
22248
22327
 
22249
22328
  /**
22250
- * Efficient schema-less binary encoding with support for variable length encoding.
22251
- *
22252
- * Use [lib0/encoding] with [lib0/decoding]. Every encoding function has a corresponding decoding function.
22253
- *
22254
- * Encodes numbers in little-endian order (least to most significant byte order)
22255
- * and is compatible with Golang's binary encoding (https://golang.org/pkg/encoding/binary/)
22256
- * which is also used in Protocol Buffers.
22257
- *
22258
- * ```js
22259
- * // encoding step
22260
- * const encoder = encoding.createEncoder()
22261
- * encoding.writeVarUint(encoder, 256)
22262
- * encoding.writeVarString(encoder, 'Hello world!')
22263
- * const buf = encoding.toUint8Array(encoder)
22264
- * ```
22265
- *
22266
- * ```js
22267
- * // decoding step
22268
- * const decoder = decoding.createDecoder(buf)
22269
- * decoding.readVarUint(decoder) // => 256
22270
- * decoding.readVarString(decoder) // => 'Hello world!'
22271
- * decoding.hasContent(decoder) // => false - all data is read
22272
- * ```
22273
- *
22274
- * @module encoding
22275
- */
22276
-
22277
-
22278
- /**
22279
- * Write one byte to the encoder.
22280
- *
22281
- * @function
22282
- * @param {Encoder} encoder
22283
- * @param {number} num The byte that is to be encoded.
22284
- */
22285
- const write$1 = (encoder, num) => {
22286
- const bufferLen = encoder.cbuf.length;
22287
- if (encoder.cpos === bufferLen) {
22288
- encoder.bufs.push(encoder.cbuf);
22289
- encoder.cbuf = new Uint8Array(bufferLen * 2);
22290
- encoder.cpos = 0;
22291
- }
22292
- encoder.cbuf[encoder.cpos++] = num;
22293
- };
22294
-
22295
- /**
22296
- * Write a variable length unsigned integer. Max encodable integer is 2^53.
22297
- *
22298
- * @function
22299
- * @param {Encoder} encoder
22300
- * @param {number} num The number that is to be encoded.
22301
- */
22302
- const writeVarUint$1 = (encoder, num) => {
22303
- while (num > BITS7$1) {
22304
- write$1(encoder, BIT8$1 | (BITS7$1 & num));
22305
- num = floor$1(num / 128); // shift >>> 7
22306
- }
22307
- write$1(encoder, BITS7$1 & num);
22308
- };
22309
-
22310
- /**
22311
- * A cache to store strings temporarily
22312
- */
22313
- const _strBuffer$1 = new Uint8Array(30000);
22314
- const _maxStrBSize$1 = _strBuffer$1.length / 3;
22315
-
22316
- /**
22317
- * Write a variable length string.
22318
- *
22319
- * @function
22320
- * @param {Encoder} encoder
22321
- * @param {String} str The string that is to be encoded.
22322
- */
22323
- const _writeVarStringNative$1 = (encoder, str) => {
22324
- if (str.length < _maxStrBSize$1) {
22325
- // We can encode the string into the existing buffer
22326
- /* c8 ignore next */
22327
- const written = utf8TextEncoder$1.encodeInto(str, _strBuffer$1).written || 0;
22328
- writeVarUint$1(encoder, written);
22329
- for (let i = 0; i < written; i++) {
22330
- write$1(encoder, _strBuffer$1[i]);
22331
- }
22332
- } else {
22333
- writeVarUint8Array$1(encoder, encodeUtf8$1(str));
22334
- }
22335
- };
22336
-
22337
- /**
22338
- * Write a variable length string.
22339
- *
22340
- * @function
22341
- * @param {Encoder} encoder
22342
- * @param {String} str The string that is to be encoded.
22343
- */
22344
- const _writeVarStringPolyfill$1 = (encoder, str) => {
22345
- const encodedString = unescape(encodeURIComponent(str));
22346
- const len = encodedString.length;
22347
- writeVarUint$1(encoder, len);
22348
- for (let i = 0; i < len; i++) {
22349
- write$1(encoder, /** @type {number} */ (encodedString.codePointAt(i)));
22350
- }
22351
- };
22352
-
22353
- /**
22354
- * Write a variable length string.
22355
- *
22356
- * @function
22357
- * @param {Encoder} encoder
22358
- * @param {String} str The string that is to be encoded.
22359
- */
22360
- /* c8 ignore next */
22361
- const writeVarString$1 = (utf8TextEncoder$1 && /** @type {any} */ (utf8TextEncoder$1).encodeInto) ? _writeVarStringNative$1 : _writeVarStringPolyfill$1;
22362
-
22363
- /**
22364
- * Append fixed-length Uint8Array to the encoder.
22365
- *
22366
- * @function
22367
- * @param {Encoder} encoder
22368
- * @param {Uint8Array} uint8Array
22369
- */
22370
- const writeUint8Array$1 = (encoder, uint8Array) => {
22371
- const bufferLen = encoder.cbuf.length;
22372
- const cpos = encoder.cpos;
22373
- const leftCopyLen = min$1(bufferLen - cpos, uint8Array.length);
22374
- const rightCopyLen = uint8Array.length - leftCopyLen;
22375
- encoder.cbuf.set(uint8Array.subarray(0, leftCopyLen), cpos);
22376
- encoder.cpos += leftCopyLen;
22377
- if (rightCopyLen > 0) {
22378
- // Still something to write, write right half..
22379
- // Append new buffer
22380
- encoder.bufs.push(encoder.cbuf);
22381
- // must have at least size of remaining buffer
22382
- encoder.cbuf = new Uint8Array(max$1(bufferLen * 2, rightCopyLen));
22383
- // copy array
22384
- encoder.cbuf.set(uint8Array.subarray(leftCopyLen));
22385
- encoder.cpos = rightCopyLen;
22386
- }
22387
- };
22388
-
22389
- /**
22390
- * Append an Uint8Array to Encoder.
22391
- *
22392
- * @function
22393
- * @param {Encoder} encoder
22394
- * @param {Uint8Array} uint8Array
22395
- */
22396
- const writeVarUint8Array$1 = (encoder, uint8Array) => {
22397
- writeVarUint$1(encoder, uint8Array.byteLength);
22398
- writeUint8Array$1(encoder, uint8Array);
22399
- };
22400
-
22401
- /**
22402
- * Error helpers.
22403
- *
22404
- * @module error
22405
- */
22406
-
22407
- /**
22408
- * @param {string} s
22409
- * @return {Error}
22410
- */
22411
- /* c8 ignore next */
22412
- const create$3 = s => new Error(s);
22413
-
22414
- /**
22415
- * Efficient schema-less binary decoding with support for variable length encoding.
22416
- *
22417
- * Use [lib0/decoding] with [lib0/encoding]. Every encoding function has a corresponding decoding function.
22418
- *
22419
- * Encodes numbers in little-endian order (least to most significant byte order)
22420
- * and is compatible with Golang's binary encoding (https://golang.org/pkg/encoding/binary/)
22421
- * which is also used in Protocol Buffers.
22422
- *
22423
- * ```js
22424
- * // encoding step
22425
- * const encoder = encoding.createEncoder()
22426
- * encoding.writeVarUint(encoder, 256)
22427
- * encoding.writeVarString(encoder, 'Hello world!')
22428
- * const buf = encoding.toUint8Array(encoder)
22429
- * ```
22430
- *
22431
- * ```js
22432
- * // decoding step
22433
- * const decoder = decoding.createDecoder(buf)
22434
- * decoding.readVarUint(decoder) // => 256
22435
- * decoding.readVarString(decoder) // => 'Hello world!'
22436
- * decoding.hasContent(decoder) // => false - all data is read
22437
- * ```
22438
- *
22439
- * @module decoding
22440
- */
22441
-
22442
-
22443
- const errorUnexpectedEndOfArray$1 = create$3('Unexpected end of array');
22444
- const errorIntegerOutOfRange$1 = create$3('Integer out of Range');
22445
-
22446
- /**
22447
- * Create an Uint8Array view of the next `len` bytes and advance the position by `len`.
22448
- *
22449
- * Important: The Uint8Array still points to the underlying ArrayBuffer. Make sure to discard the result as soon as possible to prevent any memory leaks.
22450
- * Use `buffer.copyUint8Array` to copy the result into a new Uint8Array.
22451
- *
22452
- * @function
22453
- * @param {Decoder} decoder The decoder instance
22454
- * @param {number} len The length of bytes to read
22455
- * @return {Uint8Array}
22456
- */
22457
- const readUint8Array$1 = (decoder, len) => {
22458
- const view = new Uint8Array(decoder.arr.buffer, decoder.pos + decoder.arr.byteOffset, len);
22459
- decoder.pos += len;
22460
- return view
22461
- };
22462
-
22463
- /**
22464
- * Read variable length Uint8Array.
22465
- *
22466
- * Important: The Uint8Array still points to the underlying ArrayBuffer. Make sure to discard the result as soon as possible to prevent any memory leaks.
22467
- * Use `buffer.copyUint8Array` to copy the result into a new Uint8Array.
22468
- *
22469
- * @function
22470
- * @param {Decoder} decoder
22471
- * @return {Uint8Array}
22472
- */
22473
- const readVarUint8Array$1 = decoder => readUint8Array$1(decoder, readVarUint$1(decoder));
22474
-
22475
- /**
22476
- * Read one byte as unsigned integer.
22477
- * @function
22478
- * @param {Decoder} decoder The decoder instance
22479
- * @return {number} Unsigned 8-bit integer
22480
- */
22481
- const readUint8$1 = decoder => decoder.arr[decoder.pos++];
22482
-
22483
- /**
22484
- * Read unsigned integer (32bit) with variable length.
22485
- * 1/8th of the storage is used as encoding overhead.
22486
- * * numbers < 2^7 is stored in one bytlength
22487
- * * numbers < 2^14 is stored in two bylength
22488
- *
22489
- * @function
22490
- * @param {Decoder} decoder
22491
- * @return {number} An unsigned integer.length
22492
- */
22493
- const readVarUint$1 = decoder => {
22494
- let num = 0;
22495
- let mult = 1;
22496
- const len = decoder.arr.length;
22497
- while (decoder.pos < len) {
22498
- const r = decoder.arr[decoder.pos++];
22499
- // num = num | ((r & binary.BITS7) << len)
22500
- num = num + (r & BITS7$1) * mult; // shift $r << (7*#iterations) and add it to num
22501
- mult *= 128; // next iteration, shift 7 "more" to the left
22502
- if (r < BIT8$1) {
22503
- return num
22504
- }
22505
- /* c8 ignore start */
22506
- if (num > MAX_SAFE_INTEGER$1) {
22507
- throw errorIntegerOutOfRange$1
22508
- }
22509
- /* c8 ignore stop */
22510
- }
22511
- throw errorUnexpectedEndOfArray$1
22512
- };
22513
-
22514
- /**
22515
- * We don't test this function anymore as we use native decoding/encoding by default now.
22516
- * Better not modify this anymore..
22517
- *
22518
- * Transforming utf8 to a string is pretty expensive. The code performs 10x better
22519
- * when String.fromCodePoint is fed with all characters as arguments.
22520
- * But most environments have a maximum number of arguments per functions.
22521
- * For effiency reasons we apply a maximum of 10000 characters at once.
22522
- *
22523
- * @function
22524
- * @param {Decoder} decoder
22525
- * @return {String} The read String.
22329
+ * Default sync configuration - IndexedDB + BroadcastChannel
22526
22330
  */
22527
- /* c8 ignore start */
22528
- const _readVarStringPolyfill$1 = decoder => {
22529
- let remainingLen = readVarUint$1(decoder);
22530
- if (remainingLen === 0) {
22531
- return ''
22532
- } else {
22533
- let encodedString = String.fromCodePoint(readUint8$1(decoder)); // remember to decrease remainingLen
22534
- if (--remainingLen < 100) { // do not create a Uint8Array for small strings
22535
- while (remainingLen--) {
22536
- encodedString += String.fromCodePoint(readUint8$1(decoder));
22537
- }
22538
- } else {
22539
- while (remainingLen > 0) {
22540
- const nextLen = remainingLen < 10000 ? remainingLen : 10000;
22541
- // this is dangerous, we create a fresh array view from the existing buffer
22542
- const bytes = decoder.arr.subarray(decoder.pos, decoder.pos + nextLen);
22543
- decoder.pos += nextLen;
22544
- // Starting with ES5.1 we can supply a generic array-like object as arguments
22545
- encodedString += String.fromCodePoint.apply(null, /** @type {any} */ (bytes));
22546
- remainingLen -= nextLen;
22547
- }
22548
- }
22549
- return decodeURIComponent(escape(encodedString))
22550
- }
22331
+ const DEFAULT_SYNC_CONFIG = {
22332
+ providers: [
22333
+ IndexedDBSyncProvider
22334
+ ],
22551
22335
  };
22552
- /* c8 ignore stop */
22553
-
22554
- /**
22555
- * @function
22556
- * @param {Decoder} decoder
22557
- * @return {String} The read String
22558
- */
22559
- const _readVarStringNative$1 = decoder =>
22560
- /** @type any */ (utf8TextDecoder$1).decode(readVarUint8Array$1(decoder));
22561
22336
 
22562
22337
  /**
22563
- * Read string of variable length
22564
- * * varUint is used to store the length of the string
22565
- *
22566
- * @function
22567
- * @param {Decoder} decoder
22568
- * @return {String} The read String
22569
- *
22570
- */
22571
- /* c8 ignore next */
22572
- const readVarString$1 = utf8TextDecoder$1 ? _readVarStringNative$1 : _readVarStringPolyfill$1;
22573
-
22574
- var AuthMessageType;
22575
- (function (AuthMessageType) {
22576
- AuthMessageType[AuthMessageType["Token"] = 0] = "Token";
22577
- AuthMessageType[AuthMessageType["PermissionDenied"] = 1] = "PermissionDenied";
22578
- AuthMessageType[AuthMessageType["Authenticated"] = 2] = "Authenticated";
22579
- })(AuthMessageType || (AuthMessageType = {}));
22580
- const writeAuthentication = (encoder, auth) => {
22581
- writeVarUint$1(encoder, AuthMessageType.Token);
22582
- writeVarString$1(encoder, auth);
22583
- };
22584
- const readAuthMessage = (decoder, sendToken, permissionDeniedHandler, authenticatedHandler) => {
22585
- switch (readVarUint$1(decoder)) {
22586
- case AuthMessageType.Token: {
22587
- sendToken();
22588
- break;
22589
- }
22590
- case AuthMessageType.PermissionDenied: {
22591
- permissionDeniedHandler(readVarString$1(decoder));
22592
- break;
22593
- }
22594
- case AuthMessageType.Authenticated: {
22595
- authenticatedHandler(readVarString$1(decoder));
22596
- break;
22597
- }
22598
- }
22599
- };
22600
-
22601
- const awarenessStatesToArray = (states) => {
22602
- return Array.from(states.entries()).map(([key, value]) => {
22603
- return {
22604
- clientId: key,
22605
- ...value,
22606
- };
22607
- });
22608
- };
22609
-
22610
- /**
22611
- * State of the WebSocket connection.
22612
- * https://developer.mozilla.org/de/docs/Web/API/WebSocket/readyState
22613
- */
22614
- var WsReadyStates;
22615
- (function (WsReadyStates) {
22616
- WsReadyStates[WsReadyStates["Connecting"] = 0] = "Connecting";
22617
- WsReadyStates[WsReadyStates["Open"] = 1] = "Open";
22618
- WsReadyStates[WsReadyStates["Closing"] = 2] = "Closing";
22619
- WsReadyStates[WsReadyStates["Closed"] = 3] = "Closed";
22620
- })(WsReadyStates || (WsReadyStates = {}));
22621
-
22622
- function applyDefaults(options) {
22623
- if (!options) {
22624
- options = {};
22625
- }
22626
- return {
22627
- delay: (options.delay === undefined) ? 200 : options.delay,
22628
- initialDelay: (options.initialDelay === undefined) ? 0 : options.initialDelay,
22629
- minDelay: (options.minDelay === undefined) ? 0 : options.minDelay,
22630
- maxDelay: (options.maxDelay === undefined) ? 0 : options.maxDelay,
22631
- factor: (options.factor === undefined) ? 0 : options.factor,
22632
- maxAttempts: (options.maxAttempts === undefined) ? 3 : options.maxAttempts,
22633
- timeout: (options.timeout === undefined) ? 0 : options.timeout,
22634
- jitter: (options.jitter === true),
22635
- initialJitter: (options.initialJitter === true),
22636
- handleError: (options.handleError === undefined) ? null : options.handleError,
22637
- handleTimeout: (options.handleTimeout === undefined) ? null : options.handleTimeout,
22638
- beforeAttempt: (options.beforeAttempt === undefined) ? null : options.beforeAttempt,
22639
- calculateDelay: (options.calculateDelay === undefined) ? null : options.calculateDelay
22640
- };
22641
- }
22642
- async function sleep(delay) {
22643
- return new Promise((resolve) => setTimeout(resolve, delay));
22644
- }
22645
- function defaultCalculateDelay(context, options) {
22646
- let delay = options.delay;
22647
- if (delay === 0) {
22648
- // no delay between attempts
22649
- return 0;
22650
- }
22651
- if (options.factor) {
22652
- delay *= Math.pow(options.factor, context.attemptNum - 1);
22653
- if (options.maxDelay !== 0) {
22654
- delay = Math.min(delay, options.maxDelay);
22655
- }
22656
- }
22657
- if (options.jitter) {
22658
- // Jitter will result in a random value between `minDelay` and
22659
- // calculated delay for a given attempt.
22660
- // See https://www.awsarchitectureblog.com/2015/03/backoff.html
22661
- // We're using the "full jitter" strategy.
22662
- const min = Math.ceil(options.minDelay);
22663
- const max = Math.floor(delay);
22664
- delay = Math.floor(Math.random() * (max - min + 1)) + min;
22665
- }
22666
- return Math.round(delay);
22667
- }
22668
- async function retry(attemptFunc, attemptOptions) {
22669
- const options = applyDefaults(attemptOptions);
22670
- for (const prop of [
22671
- 'delay',
22672
- 'initialDelay',
22673
- 'minDelay',
22674
- 'maxDelay',
22675
- 'maxAttempts',
22676
- 'timeout'
22677
- ]) {
22678
- const value = options[prop];
22679
- if (!Number.isInteger(value) || (value < 0)) {
22680
- throw new Error(`Value for ${prop} must be an integer greater than or equal to 0`);
22681
- }
22682
- }
22683
- if ((options.factor.constructor !== Number) || (options.factor < 0)) {
22684
- throw new Error(`Value for factor must be a number greater than or equal to 0`);
22685
- }
22686
- if (options.delay < options.minDelay) {
22687
- throw new Error(`delay cannot be less than minDelay (delay: ${options.delay}, minDelay: ${options.minDelay}`);
22688
- }
22689
- const context = {
22690
- attemptNum: 0,
22691
- attemptsRemaining: options.maxAttempts ? options.maxAttempts : -1,
22692
- aborted: false,
22693
- abort() {
22694
- context.aborted = true;
22695
- }
22696
- };
22697
- const calculateDelay = options.calculateDelay || defaultCalculateDelay;
22698
- async function makeAttempt() {
22699
- if (options.beforeAttempt) {
22700
- options.beforeAttempt(context, options);
22701
- }
22702
- if (context.aborted) {
22703
- const err = new Error(`Attempt aborted`);
22704
- err.code = 'ATTEMPT_ABORTED';
22705
- throw err;
22706
- }
22707
- const onError = async (err) => {
22708
- if (options.handleError) {
22709
- await options.handleError(err, context, options);
22710
- }
22711
- if (context.aborted || (context.attemptsRemaining === 0)) {
22712
- throw err;
22713
- }
22714
- // We are about to try again so increment attempt number
22715
- context.attemptNum++;
22716
- const delay = calculateDelay(context, options);
22717
- if (delay) {
22718
- await sleep(delay);
22719
- }
22720
- return makeAttempt();
22721
- };
22722
- if (context.attemptsRemaining > 0) {
22723
- context.attemptsRemaining--;
22724
- }
22725
- if (options.timeout) {
22726
- return new Promise((resolve, reject) => {
22727
- const timer = setTimeout(() => {
22728
- if (options.handleTimeout) {
22729
- // If calling handleTimeout throws an error that is not wrapped in a promise
22730
- // we want to catch the error and reject.
22731
- try {
22732
- resolve(options.handleTimeout(context, options));
22733
- }
22734
- catch (e) {
22735
- reject(e);
22736
- }
22737
- }
22738
- else {
22739
- const err = new Error(`Retry timeout (attemptNum: ${context.attemptNum}, timeout: ${options.timeout})`);
22740
- err.code = 'ATTEMPT_TIMEOUT';
22741
- reject(err);
22742
- }
22743
- }, options.timeout);
22744
- attemptFunc(context, options).then((result) => {
22745
- clearTimeout(timer);
22746
- resolve(result);
22747
- }).catch((err) => {
22748
- clearTimeout(timer);
22749
- // Calling resolve with a Promise that rejects here will result
22750
- // in an unhandled rejection. Calling `reject` with errors
22751
- // does not result in an unhandled rejection
22752
- onError(err).then(resolve).catch(reject);
22753
- });
22754
- });
22755
- }
22756
- else {
22757
- // No timeout provided so wait indefinitely for the returned promise
22758
- // to be resolved.
22759
- return attemptFunc(context, options).catch(onError);
22760
- }
22761
- }
22762
- const initialDelay = options.calculateDelay
22763
- ? options.calculateDelay(context, options)
22764
- : options.initialDelay;
22765
- if (initialDelay) {
22766
- await sleep(initialDelay);
22767
- }
22768
- if (context.attemptNum < 1 && options.initialJitter) {
22769
- const delay = calculateDelay(context, options);
22770
- if (delay) {
22771
- await sleep(delay);
22772
- }
22773
- }
22774
- return makeAttempt();
22775
- }
22776
-
22777
- /**
22778
- * Common Math expressions.
22779
- *
22780
- * @module math
22781
- */
22782
-
22783
- const floor = Math.floor;
22784
-
22785
- /**
22786
- * @function
22787
- * @param {number} a
22788
- * @param {number} b
22789
- * @return {number} The smaller element of a and b
22790
- */
22791
- const min = (a, b) => a < b ? a : b;
22792
-
22793
- /**
22794
- * @function
22795
- * @param {number} a
22796
- * @param {number} b
22797
- * @return {number} The bigger element of a and b
22798
- */
22799
- const max = (a, b) => a > b ? a : b;
22800
-
22801
- /* eslint-env browser */
22802
-
22803
- const BIT7 = 64;
22804
- const BIT8 = 128;
22805
- const BITS6 = 63;
22806
- const BITS7 = 127;
22807
-
22808
- /**
22809
- * Utility helpers for working with numbers.
22810
- *
22811
- * @module number
22812
- */
22813
-
22814
-
22815
- const MAX_SAFE_INTEGER = Number.MAX_SAFE_INTEGER;
22816
-
22817
- /**
22818
- * Utility module to work with sets.
22819
- *
22820
- * @module set
22821
- */
22822
-
22823
- const create$2 = () => new Set();
22824
-
22825
- /**
22826
- * Utility module to work with Arrays.
22827
- *
22828
- * @module array
22829
- */
22830
-
22831
-
22832
- /**
22833
- * Transforms something array-like to an actual Array.
22834
- *
22835
- * @function
22836
- * @template T
22837
- * @param {ArrayLike<T>|Iterable<T>} arraylike
22838
- * @return {T}
22839
- */
22840
- const from = Array.from;
22841
-
22842
- /**
22843
- * @param {string} str
22844
- * @return {Uint8Array}
22845
- */
22846
- const _encodeUtf8Polyfill = str => {
22847
- const encodedString = unescape(encodeURIComponent(str));
22848
- const len = encodedString.length;
22849
- const buf = new Uint8Array(len);
22850
- for (let i = 0; i < len; i++) {
22851
- buf[i] = /** @type {number} */ (encodedString.codePointAt(i));
22852
- }
22853
- return buf
22854
- };
22855
-
22856
- /* c8 ignore next */
22857
- const utf8TextEncoder = /** @type {TextEncoder} */ (typeof TextEncoder !== 'undefined' ? new TextEncoder() : null);
22858
-
22859
- /**
22860
- * @param {string} str
22861
- * @return {Uint8Array}
22862
- */
22863
- const _encodeUtf8Native = str => utf8TextEncoder.encode(str);
22864
-
22865
- /**
22866
- * @param {string} str
22867
- * @return {Uint8Array}
22868
- */
22869
- /* c8 ignore next */
22870
- const encodeUtf8 = utf8TextEncoder ? _encodeUtf8Native : _encodeUtf8Polyfill;
22871
-
22872
- /* c8 ignore next */
22873
- let utf8TextDecoder = typeof TextDecoder === 'undefined' ? null : new TextDecoder('utf-8', { fatal: true, ignoreBOM: true });
22874
-
22875
- /* c8 ignore start */
22876
- if (utf8TextDecoder && utf8TextDecoder.decode(new Uint8Array()).length === 1) {
22877
- // Safari doesn't handle BOM correctly.
22878
- // This fixes a bug in Safari 13.0.5 where it produces a BOM the first time it is called.
22879
- // utf8TextDecoder.decode(new Uint8Array()).length === 1 on the first call and
22880
- // utf8TextDecoder.decode(new Uint8Array()).length === 1 on the second call
22881
- // Another issue is that from then on no BOM chars are recognized anymore
22882
- /* c8 ignore next */
22883
- utf8TextDecoder = null;
22884
- }
22885
-
22886
- /**
22887
- * Efficient schema-less binary encoding with support for variable length encoding.
22888
- *
22889
- * Use [lib0/encoding] with [lib0/decoding]. Every encoding function has a corresponding decoding function.
22890
- *
22891
- * Encodes numbers in little-endian order (least to most significant byte order)
22892
- * and is compatible with Golang's binary encoding (https://golang.org/pkg/encoding/binary/)
22893
- * which is also used in Protocol Buffers.
22894
- *
22895
- * ```js
22896
- * // encoding step
22897
- * const encoder = encoding.createEncoder()
22898
- * encoding.writeVarUint(encoder, 256)
22899
- * encoding.writeVarString(encoder, 'Hello world!')
22900
- * const buf = encoding.toUint8Array(encoder)
22901
- * ```
22902
- *
22903
- * ```js
22904
- * // decoding step
22905
- * const decoder = decoding.createDecoder(buf)
22906
- * decoding.readVarUint(decoder) // => 256
22907
- * decoding.readVarString(decoder) // => 'Hello world!'
22908
- * decoding.hasContent(decoder) // => false - all data is read
22909
- * ```
22910
- *
22911
- * @module encoding
22912
- */
22913
-
22914
-
22915
- /**
22916
- * A BinaryEncoder handles the encoding to an Uint8Array.
22917
- */
22918
- class Encoder {
22919
- constructor () {
22920
- this.cpos = 0;
22921
- this.cbuf = new Uint8Array(100);
22922
- /**
22923
- * @type {Array<Uint8Array>}
22924
- */
22925
- this.bufs = [];
22926
- }
22927
- }
22928
-
22929
- /**
22930
- * @function
22931
- * @return {Encoder}
22932
- */
22933
- const createEncoder = () => new Encoder();
22934
-
22935
- /**
22936
- * The current length of the encoded data.
22937
- *
22938
- * @function
22939
- * @param {Encoder} encoder
22940
- * @return {number}
22941
- */
22942
- const length$1 = encoder => {
22943
- let len = encoder.cpos;
22944
- for (let i = 0; i < encoder.bufs.length; i++) {
22945
- len += encoder.bufs[i].length;
22946
- }
22947
- return len
22948
- };
22949
-
22950
- /**
22951
- * Transform to Uint8Array.
22952
- *
22953
- * @function
22954
- * @param {Encoder} encoder
22955
- * @return {Uint8Array} The created ArrayBuffer.
22956
- */
22957
- const toUint8Array = encoder => {
22958
- const uint8arr = new Uint8Array(length$1(encoder));
22959
- let curPos = 0;
22960
- for (let i = 0; i < encoder.bufs.length; i++) {
22961
- const d = encoder.bufs[i];
22962
- uint8arr.set(d, curPos);
22963
- curPos += d.length;
22964
- }
22965
- uint8arr.set(new Uint8Array(encoder.cbuf.buffer, 0, encoder.cpos), curPos);
22966
- return uint8arr
22967
- };
22968
-
22969
- /**
22970
- * Write one byte to the encoder.
22971
- *
22972
- * @function
22973
- * @param {Encoder} encoder
22974
- * @param {number} num The byte that is to be encoded.
22975
- */
22976
- const write = (encoder, num) => {
22977
- const bufferLen = encoder.cbuf.length;
22978
- if (encoder.cpos === bufferLen) {
22979
- encoder.bufs.push(encoder.cbuf);
22980
- encoder.cbuf = new Uint8Array(bufferLen * 2);
22981
- encoder.cpos = 0;
22982
- }
22983
- encoder.cbuf[encoder.cpos++] = num;
22984
- };
22985
-
22986
- /**
22987
- * Write a variable length unsigned integer. Max encodable integer is 2^53.
22988
- *
22989
- * @function
22990
- * @param {Encoder} encoder
22991
- * @param {number} num The number that is to be encoded.
22992
- */
22993
- const writeVarUint = (encoder, num) => {
22994
- while (num > BITS7) {
22995
- write(encoder, BIT8 | (BITS7 & num));
22996
- num = floor(num / 128); // shift >>> 7
22997
- }
22998
- write(encoder, BITS7 & num);
22999
- };
23000
-
23001
- /**
23002
- * A cache to store strings temporarily
23003
- */
23004
- const _strBuffer = new Uint8Array(30000);
23005
- const _maxStrBSize = _strBuffer.length / 3;
23006
-
23007
- /**
23008
- * Write a variable length string.
23009
- *
23010
- * @function
23011
- * @param {Encoder} encoder
23012
- * @param {String} str The string that is to be encoded.
23013
- */
23014
- const _writeVarStringNative = (encoder, str) => {
23015
- if (str.length < _maxStrBSize) {
23016
- // We can encode the string into the existing buffer
23017
- /* c8 ignore next */
23018
- const written = utf8TextEncoder.encodeInto(str, _strBuffer).written || 0;
23019
- writeVarUint(encoder, written);
23020
- for (let i = 0; i < written; i++) {
23021
- write(encoder, _strBuffer[i]);
23022
- }
23023
- } else {
23024
- writeVarUint8Array(encoder, encodeUtf8(str));
23025
- }
23026
- };
23027
-
23028
- /**
23029
- * Write a variable length string.
23030
- *
23031
- * @function
23032
- * @param {Encoder} encoder
23033
- * @param {String} str The string that is to be encoded.
23034
- */
23035
- const _writeVarStringPolyfill = (encoder, str) => {
23036
- const encodedString = unescape(encodeURIComponent(str));
23037
- const len = encodedString.length;
23038
- writeVarUint(encoder, len);
23039
- for (let i = 0; i < len; i++) {
23040
- write(encoder, /** @type {number} */ (encodedString.codePointAt(i)));
23041
- }
23042
- };
23043
-
23044
- /**
23045
- * Write a variable length string.
23046
- *
23047
- * @function
23048
- * @param {Encoder} encoder
23049
- * @param {String} str The string that is to be encoded.
23050
- */
23051
- /* c8 ignore next */
23052
- const writeVarString = (utf8TextEncoder && /** @type {any} */ (utf8TextEncoder).encodeInto) ? _writeVarStringNative : _writeVarStringPolyfill;
23053
-
23054
- /**
23055
- * Append fixed-length Uint8Array to the encoder.
23056
- *
23057
- * @function
23058
- * @param {Encoder} encoder
23059
- * @param {Uint8Array} uint8Array
23060
- */
23061
- const writeUint8Array = (encoder, uint8Array) => {
23062
- const bufferLen = encoder.cbuf.length;
23063
- const cpos = encoder.cpos;
23064
- const leftCopyLen = min(bufferLen - cpos, uint8Array.length);
23065
- const rightCopyLen = uint8Array.length - leftCopyLen;
23066
- encoder.cbuf.set(uint8Array.subarray(0, leftCopyLen), cpos);
23067
- encoder.cpos += leftCopyLen;
23068
- if (rightCopyLen > 0) {
23069
- // Still something to write, write right half..
23070
- // Append new buffer
23071
- encoder.bufs.push(encoder.cbuf);
23072
- // must have at least size of remaining buffer
23073
- encoder.cbuf = new Uint8Array(max(bufferLen * 2, rightCopyLen));
23074
- // copy array
23075
- encoder.cbuf.set(uint8Array.subarray(leftCopyLen));
23076
- encoder.cpos = rightCopyLen;
23077
- }
23078
- };
23079
-
23080
- /**
23081
- * Append an Uint8Array to Encoder.
23082
- *
23083
- * @function
23084
- * @param {Encoder} encoder
23085
- * @param {Uint8Array} uint8Array
23086
- */
23087
- const writeVarUint8Array = (encoder, uint8Array) => {
23088
- writeVarUint(encoder, uint8Array.byteLength);
23089
- writeUint8Array(encoder, uint8Array);
23090
- };
23091
-
23092
- /**
23093
- * Error helpers.
23094
- *
23095
- * @module error
23096
- */
23097
-
23098
- /**
23099
- * @param {string} s
23100
- * @return {Error}
23101
- */
23102
- /* c8 ignore next */
23103
- const create$1 = s => new Error(s);
23104
-
23105
- /**
23106
- * Efficient schema-less binary decoding with support for variable length encoding.
23107
- *
23108
- * Use [lib0/decoding] with [lib0/encoding]. Every encoding function has a corresponding decoding function.
23109
- *
23110
- * Encodes numbers in little-endian order (least to most significant byte order)
23111
- * and is compatible with Golang's binary encoding (https://golang.org/pkg/encoding/binary/)
23112
- * which is also used in Protocol Buffers.
23113
- *
23114
- * ```js
23115
- * // encoding step
23116
- * const encoder = encoding.createEncoder()
23117
- * encoding.writeVarUint(encoder, 256)
23118
- * encoding.writeVarString(encoder, 'Hello world!')
23119
- * const buf = encoding.toUint8Array(encoder)
23120
- * ```
23121
- *
23122
- * ```js
23123
- * // decoding step
23124
- * const decoder = decoding.createDecoder(buf)
23125
- * decoding.readVarUint(decoder) // => 256
23126
- * decoding.readVarString(decoder) // => 'Hello world!'
23127
- * decoding.hasContent(decoder) // => false - all data is read
23128
- * ```
23129
- *
23130
- * @module decoding
23131
- */
23132
-
23133
-
23134
- const errorUnexpectedEndOfArray = create$1('Unexpected end of array');
23135
- const errorIntegerOutOfRange = create$1('Integer out of Range');
23136
-
23137
- /**
23138
- * A Decoder handles the decoding of an Uint8Array.
23139
- */
23140
- class Decoder {
23141
- /**
23142
- * @param {Uint8Array} uint8Array Binary data to decode
23143
- */
23144
- constructor (uint8Array) {
23145
- /**
23146
- * Decoding target.
23147
- *
23148
- * @type {Uint8Array}
23149
- */
23150
- this.arr = uint8Array;
23151
- /**
23152
- * Current decoding position.
23153
- *
23154
- * @type {number}
23155
- */
23156
- this.pos = 0;
23157
- }
23158
- }
23159
-
23160
- /**
23161
- * @function
23162
- * @param {Uint8Array} uint8Array
23163
- * @return {Decoder}
23164
- */
23165
- const createDecoder = uint8Array => new Decoder(uint8Array);
23166
-
23167
- /**
23168
- * Create an Uint8Array view of the next `len` bytes and advance the position by `len`.
23169
- *
23170
- * Important: The Uint8Array still points to the underlying ArrayBuffer. Make sure to discard the result as soon as possible to prevent any memory leaks.
23171
- * Use `buffer.copyUint8Array` to copy the result into a new Uint8Array.
23172
- *
23173
- * @function
23174
- * @param {Decoder} decoder The decoder instance
23175
- * @param {number} len The length of bytes to read
23176
- * @return {Uint8Array}
23177
- */
23178
- const readUint8Array = (decoder, len) => {
23179
- const view = new Uint8Array(decoder.arr.buffer, decoder.pos + decoder.arr.byteOffset, len);
23180
- decoder.pos += len;
23181
- return view
23182
- };
23183
-
23184
- /**
23185
- * Read variable length Uint8Array.
23186
- *
23187
- * Important: The Uint8Array still points to the underlying ArrayBuffer. Make sure to discard the result as soon as possible to prevent any memory leaks.
23188
- * Use `buffer.copyUint8Array` to copy the result into a new Uint8Array.
23189
- *
23190
- * @function
23191
- * @param {Decoder} decoder
23192
- * @return {Uint8Array}
23193
- */
23194
- const readVarUint8Array = decoder => readUint8Array(decoder, readVarUint(decoder));
23195
-
23196
- /**
23197
- * Read one byte as unsigned integer.
23198
- * @function
23199
- * @param {Decoder} decoder The decoder instance
23200
- * @return {number} Unsigned 8-bit integer
23201
- */
23202
- const readUint8 = decoder => decoder.arr[decoder.pos++];
23203
-
23204
- /**
23205
- * Read unsigned integer (32bit) with variable length.
23206
- * 1/8th of the storage is used as encoding overhead.
23207
- * * numbers < 2^7 is stored in one bytlength
23208
- * * numbers < 2^14 is stored in two bylength
23209
- *
23210
- * @function
23211
- * @param {Decoder} decoder
23212
- * @return {number} An unsigned integer.length
23213
- */
23214
- const readVarUint = decoder => {
23215
- let num = 0;
23216
- let mult = 1;
23217
- const len = decoder.arr.length;
23218
- while (decoder.pos < len) {
23219
- const r = decoder.arr[decoder.pos++];
23220
- // num = num | ((r & binary.BITS7) << len)
23221
- num = num + (r & BITS7) * mult; // shift $r << (7*#iterations) and add it to num
23222
- mult *= 128; // next iteration, shift 7 "more" to the left
23223
- if (r < BIT8) {
23224
- return num
23225
- }
23226
- /* c8 ignore start */
23227
- if (num > MAX_SAFE_INTEGER) {
23228
- throw errorIntegerOutOfRange
23229
- }
23230
- /* c8 ignore stop */
23231
- }
23232
- throw errorUnexpectedEndOfArray
23233
- };
23234
-
23235
- /**
23236
- * Read signed integer (32bit) with variable length.
23237
- * 1/8th of the storage is used as encoding overhead.
23238
- * * numbers < 2^7 is stored in one bytlength
23239
- * * numbers < 2^14 is stored in two bylength
23240
- * @todo This should probably create the inverse ~num if number is negative - but this would be a breaking change.
23241
- *
23242
- * @function
23243
- * @param {Decoder} decoder
23244
- * @return {number} An unsigned integer.length
23245
- */
23246
- const readVarInt = decoder => {
23247
- let r = decoder.arr[decoder.pos++];
23248
- let num = r & BITS6;
23249
- let mult = 64;
23250
- const sign = (r & BIT7) > 0 ? -1 : 1;
23251
- if ((r & BIT8) === 0) {
23252
- // don't continue reading
23253
- return sign * num
23254
- }
23255
- const len = decoder.arr.length;
23256
- while (decoder.pos < len) {
23257
- r = decoder.arr[decoder.pos++];
23258
- // num = num | ((r & binary.BITS7) << len)
23259
- num = num + (r & BITS7) * mult;
23260
- mult *= 128;
23261
- if (r < BIT8) {
23262
- return sign * num
23263
- }
23264
- /* c8 ignore start */
23265
- if (num > MAX_SAFE_INTEGER) {
23266
- throw errorIntegerOutOfRange
23267
- }
23268
- /* c8 ignore stop */
23269
- }
23270
- throw errorUnexpectedEndOfArray
23271
- };
23272
-
23273
- /**
23274
- * We don't test this function anymore as we use native decoding/encoding by default now.
23275
- * Better not modify this anymore..
23276
- *
23277
- * Transforming utf8 to a string is pretty expensive. The code performs 10x better
23278
- * when String.fromCodePoint is fed with all characters as arguments.
23279
- * But most environments have a maximum number of arguments per functions.
23280
- * For effiency reasons we apply a maximum of 10000 characters at once.
23281
- *
23282
- * @function
23283
- * @param {Decoder} decoder
23284
- * @return {String} The read String.
23285
- */
23286
- /* c8 ignore start */
23287
- const _readVarStringPolyfill = decoder => {
23288
- let remainingLen = readVarUint(decoder);
23289
- if (remainingLen === 0) {
23290
- return ''
23291
- } else {
23292
- let encodedString = String.fromCodePoint(readUint8(decoder)); // remember to decrease remainingLen
23293
- if (--remainingLen < 100) { // do not create a Uint8Array for small strings
23294
- while (remainingLen--) {
23295
- encodedString += String.fromCodePoint(readUint8(decoder));
23296
- }
23297
- } else {
23298
- while (remainingLen > 0) {
23299
- const nextLen = remainingLen < 10000 ? remainingLen : 10000;
23300
- // this is dangerous, we create a fresh array view from the existing buffer
23301
- const bytes = decoder.arr.subarray(decoder.pos, decoder.pos + nextLen);
23302
- decoder.pos += nextLen;
23303
- // Starting with ES5.1 we can supply a generic array-like object as arguments
23304
- encodedString += String.fromCodePoint.apply(null, /** @type {any} */ (bytes));
23305
- remainingLen -= nextLen;
23306
- }
23307
- }
23308
- return decodeURIComponent(escape(encodedString))
23309
- }
23310
- };
23311
- /* c8 ignore stop */
23312
-
23313
- /**
23314
- * @function
23315
- * @param {Decoder} decoder
23316
- * @return {String} The read String
23317
- */
23318
- const _readVarStringNative = decoder =>
23319
- /** @type any */ (utf8TextDecoder).decode(readVarUint8Array(decoder));
23320
-
23321
- /**
23322
- * Read string of variable length
23323
- * * varUint is used to store the length of the string
23324
- *
23325
- * @function
23326
- * @param {Decoder} decoder
23327
- * @return {String} The read String
23328
- *
23329
- */
23330
- /* c8 ignore next */
23331
- const readVarString = utf8TextDecoder ? _readVarStringNative : _readVarStringPolyfill;
23332
-
23333
- /**
23334
- * Look ahead and read varString without incrementing position
23335
- *
23336
- * @function
23337
- * @param {Decoder} decoder
23338
- * @return {string}
23339
- */
23340
- const peekVarString = decoder => {
23341
- const pos = decoder.pos;
23342
- const s = readVarString(decoder);
23343
- decoder.pos = pos;
23344
- return s
23345
- };
23346
-
23347
- /**
23348
- * Utility module to work with time.
23349
- *
23350
- * @module time
23351
- */
23352
-
23353
-
23354
- /**
23355
- * Return current unix time.
23356
- *
23357
- * @return {number}
23358
- */
23359
- const getUnixTime = Date.now;
23360
-
23361
- /**
23362
- * Utility module to work with key-value stores.
23363
- *
23364
- * @module map
23365
- */
23366
-
23367
- /**
23368
- * Creates a new Map instance.
23369
- *
23370
- * @function
23371
- * @return {Map<any, any>}
23372
- *
23373
- * @function
23374
- */
23375
- const create = () => new Map();
23376
-
23377
- /**
23378
- * Get map property. Create T if property is undefined and set T on map.
23379
- *
23380
- * ```js
23381
- * const listeners = map.setIfUndefined(events, 'eventName', set.create)
23382
- * listeners.add(listener)
23383
- * ```
23384
- *
23385
- * @function
23386
- * @template {Map<any, any>} MAP
23387
- * @template {MAP extends Map<any,infer V> ? function():V : unknown} CF
23388
- * @param {MAP} map
23389
- * @param {MAP extends Map<infer K,any> ? K : unknown} key
23390
- * @param {CF} createT
23391
- * @return {ReturnType<CF>}
23392
- */
23393
- const setIfUndefined = (map, key, createT) => {
23394
- let set = map.get(key);
23395
- if (set === undefined) {
23396
- map.set(key, set = createT());
23397
- }
23398
- return set
23399
- };
23400
-
23401
- /**
23402
- * Observable class prototype.
23403
- *
23404
- * @module observable
23405
- */
23406
-
23407
-
23408
- /* c8 ignore start */
23409
- /**
23410
- * Handles named events.
23411
- *
23412
- * @deprecated
23413
- * @template N
23414
- */
23415
- class Observable {
23416
- constructor () {
23417
- /**
23418
- * Some desc.
23419
- * @type {Map<N, any>}
23420
- */
23421
- this._observers = create();
23422
- }
23423
-
23424
- /**
23425
- * @param {N} name
23426
- * @param {function} f
23427
- */
23428
- on (name, f) {
23429
- setIfUndefined(this._observers, name, create$2).add(f);
23430
- }
23431
-
23432
- /**
23433
- * @param {N} name
23434
- * @param {function} f
23435
- */
23436
- once (name, f) {
23437
- /**
23438
- * @param {...any} args
23439
- */
23440
- const _f = (...args) => {
23441
- this.off(name, _f);
23442
- f(...args);
23443
- };
23444
- this.on(name, _f);
23445
- }
23446
-
23447
- /**
23448
- * @param {N} name
23449
- * @param {function} f
23450
- */
23451
- off (name, f) {
23452
- const observers = this._observers.get(name);
23453
- if (observers !== undefined) {
23454
- observers.delete(f);
23455
- if (observers.size === 0) {
23456
- this._observers.delete(name);
23457
- }
23458
- }
23459
- }
23460
-
23461
- /**
23462
- * Emit a named event. All registered event listeners that listen to the
23463
- * specified name will receive the event.
23464
- *
23465
- * @todo This should catch exceptions
23466
- *
23467
- * @param {N} name The event name.
23468
- * @param {Array<any>} args The arguments that are applied to the event listener.
23469
- */
23470
- emit (name, args) {
23471
- // copy all listeners to an array first to make sure that no event is emitted to listeners that are subscribed while the event handler is called.
23472
- return from((this._observers.get(name) || create()).values()).forEach(f => f(...args))
23473
- }
23474
-
23475
- destroy () {
23476
- this._observers = create();
23477
- }
23478
- }
23479
- /* c8 ignore end */
23480
-
23481
- /**
23482
- * Utility functions for working with EcmaScript objects.
23483
- *
23484
- * @module object
23485
- */
23486
-
23487
-
23488
- /**
23489
- * @param {Object<string,any>} obj
23490
- */
23491
- const keys = Object.keys;
23492
-
23493
- /**
23494
- * @deprecated use object.size instead
23495
- * @param {Object<string,any>} obj
23496
- * @return {number}
23497
- */
23498
- const length = obj => keys(obj).length;
23499
-
23500
- /**
23501
- * Calls `Object.prototype.hasOwnProperty`.
23502
- *
23503
- * @param {any} obj
23504
- * @param {string|symbol} key
23505
- * @return {boolean}
23506
- */
23507
- const hasProperty = (obj, key) => Object.prototype.hasOwnProperty.call(obj, key);
23508
-
23509
- /**
23510
- * Common functions and function call helpers.
23511
- *
23512
- * @module function
23513
- */
23514
-
23515
-
23516
- /**
23517
- * @template T
23518
- *
23519
- * @param {T} a
23520
- * @param {T} b
23521
- * @return {boolean}
23522
- */
23523
- const equalityStrict = (a, b) => a === b;
23524
-
23525
- /* c8 ignore start */
23526
-
23527
- /**
23528
- * @param {any} a
23529
- * @param {any} b
23530
- * @return {boolean}
23531
- */
23532
- const equalityDeep = (a, b) => {
23533
- if (a == null || b == null) {
23534
- return equalityStrict(a, b)
23535
- }
23536
- if (a.constructor !== b.constructor) {
23537
- return false
23538
- }
23539
- if (a === b) {
23540
- return true
23541
- }
23542
- switch (a.constructor) {
23543
- case ArrayBuffer:
23544
- a = new Uint8Array(a);
23545
- b = new Uint8Array(b);
23546
- // eslint-disable-next-line no-fallthrough
23547
- case Uint8Array: {
23548
- if (a.byteLength !== b.byteLength) {
23549
- return false
23550
- }
23551
- for (let i = 0; i < a.length; i++) {
23552
- if (a[i] !== b[i]) {
23553
- return false
23554
- }
23555
- }
23556
- break
23557
- }
23558
- case Set: {
23559
- if (a.size !== b.size) {
23560
- return false
23561
- }
23562
- for (const value of a) {
23563
- if (!b.has(value)) {
23564
- return false
23565
- }
23566
- }
23567
- break
23568
- }
23569
- case Map: {
23570
- if (a.size !== b.size) {
23571
- return false
23572
- }
23573
- for (const key of a.keys()) {
23574
- if (!b.has(key) || !equalityDeep(a.get(key), b.get(key))) {
23575
- return false
23576
- }
23577
- }
23578
- break
23579
- }
23580
- case Object:
23581
- if (length(a) !== length(b)) {
23582
- return false
23583
- }
23584
- for (const key in a) {
23585
- if (!hasProperty(a, key) || !equalityDeep(a[key], b[key])) {
23586
- return false
23587
- }
23588
- }
23589
- break
23590
- case Array:
23591
- if (a.length !== b.length) {
23592
- return false
23593
- }
23594
- for (let i = 0; i < a.length; i++) {
23595
- if (!equalityDeep(a[i], b[i])) {
23596
- return false
23597
- }
23598
- }
23599
- break
23600
- default:
23601
- return false
23602
- }
23603
- return true
23604
- };
23605
-
23606
- /**
23607
- * @module awareness-protocol
23608
- */
23609
-
23610
-
23611
- const outdatedTimeout = 30000;
23612
-
23613
- /**
23614
- * @typedef {Object} MetaClientState
23615
- * @property {number} MetaClientState.clock
23616
- * @property {number} MetaClientState.lastUpdated unix timestamp
23617
- */
23618
-
23619
- /**
23620
- * The Awareness class implements a simple shared state protocol that can be used for non-persistent data like awareness information
23621
- * (cursor, username, status, ..). Each client can update its own local state and listen to state changes of
23622
- * remote clients. Every client may set a state of a remote peer to `null` to mark the client as offline.
23623
- *
23624
- * Each client is identified by a unique client id (something we borrow from `doc.clientID`). A client can override
23625
- * its own state by propagating a message with an increasing timestamp (`clock`). If such a message is received, it is
23626
- * applied if the known state of that client is older than the new state (`clock < newClock`). If a client thinks that
23627
- * a remote client is offline, it may propagate a message with
23628
- * `{ clock: currentClientClock, state: null, client: remoteClient }`. If such a
23629
- * message is received, and the known clock of that client equals the received clock, it will override the state with `null`.
23630
- *
23631
- * Before a client disconnects, it should propagate a `null` state with an updated clock.
23632
- *
23633
- * Awareness states must be updated every 30 seconds. Otherwise the Awareness instance will delete the client state.
23634
- *
23635
- * @extends {Observable<string>}
23636
- */
23637
- class Awareness extends Observable {
23638
- /**
23639
- * @param {Y.Doc} doc
23640
- */
23641
- constructor (doc) {
23642
- super();
23643
- this.doc = doc;
23644
- /**
23645
- * @type {number}
23646
- */
23647
- this.clientID = doc.clientID;
23648
- /**
23649
- * Maps from client id to client state
23650
- * @type {Map<number, Object<string, any>>}
23651
- */
23652
- this.states = new Map();
23653
- /**
23654
- * @type {Map<number, MetaClientState>}
23655
- */
23656
- this.meta = new Map();
23657
- this._checkInterval = /** @type {any} */ (setInterval(() => {
23658
- const now = getUnixTime();
23659
- if (this.getLocalState() !== null && (outdatedTimeout / 2 <= now - /** @type {{lastUpdated:number}} */ (this.meta.get(this.clientID)).lastUpdated)) {
23660
- // renew local clock
23661
- this.setLocalState(this.getLocalState());
23662
- }
23663
- /**
23664
- * @type {Array<number>}
23665
- */
23666
- const remove = [];
23667
- this.meta.forEach((meta, clientid) => {
23668
- if (clientid !== this.clientID && outdatedTimeout <= now - meta.lastUpdated && this.states.has(clientid)) {
23669
- remove.push(clientid);
23670
- }
23671
- });
23672
- if (remove.length > 0) {
23673
- removeAwarenessStates(this, remove, 'timeout');
23674
- }
23675
- }, floor(outdatedTimeout / 10)));
23676
- doc.on('destroy', () => {
23677
- this.destroy();
23678
- });
23679
- this.setLocalState({});
23680
- }
23681
-
23682
- destroy () {
23683
- this.emit('destroy', [this]);
23684
- this.setLocalState(null);
23685
- super.destroy();
23686
- clearInterval(this._checkInterval);
23687
- }
23688
-
23689
- /**
23690
- * @return {Object<string,any>|null}
23691
- */
23692
- getLocalState () {
23693
- return this.states.get(this.clientID) || null
23694
- }
23695
-
23696
- /**
23697
- * @param {Object<string,any>|null} state
23698
- */
23699
- setLocalState (state) {
23700
- const clientID = this.clientID;
23701
- const currLocalMeta = this.meta.get(clientID);
23702
- const clock = currLocalMeta === undefined ? 0 : currLocalMeta.clock + 1;
23703
- const prevState = this.states.get(clientID);
23704
- if (state === null) {
23705
- this.states.delete(clientID);
23706
- } else {
23707
- this.states.set(clientID, state);
23708
- }
23709
- this.meta.set(clientID, {
23710
- clock,
23711
- lastUpdated: getUnixTime()
23712
- });
23713
- const added = [];
23714
- const updated = [];
23715
- const filteredUpdated = [];
23716
- const removed = [];
23717
- if (state === null) {
23718
- removed.push(clientID);
23719
- } else if (prevState == null) {
23720
- if (state != null) {
23721
- added.push(clientID);
23722
- }
23723
- } else {
23724
- updated.push(clientID);
23725
- if (!equalityDeep(prevState, state)) {
23726
- filteredUpdated.push(clientID);
23727
- }
23728
- }
23729
- if (added.length > 0 || filteredUpdated.length > 0 || removed.length > 0) {
23730
- this.emit('change', [{ added, updated: filteredUpdated, removed }, 'local']);
23731
- }
23732
- this.emit('update', [{ added, updated, removed }, 'local']);
23733
- }
23734
-
23735
- /**
23736
- * @param {string} field
23737
- * @param {any} value
23738
- */
23739
- setLocalStateField (field, value) {
23740
- const state = this.getLocalState();
23741
- if (state !== null) {
23742
- this.setLocalState({
23743
- ...state,
23744
- [field]: value
23745
- });
23746
- }
23747
- }
23748
-
23749
- /**
23750
- * @return {Map<number,Object<string,any>>}
23751
- */
23752
- getStates () {
23753
- return this.states
23754
- }
23755
- }
23756
-
23757
- /**
23758
- * Mark (remote) clients as inactive and remove them from the list of active peers.
23759
- * This change will be propagated to remote clients.
23760
- *
23761
- * @param {Awareness} awareness
23762
- * @param {Array<number>} clients
23763
- * @param {any} origin
23764
- */
23765
- const removeAwarenessStates = (awareness, clients, origin) => {
23766
- const removed = [];
23767
- for (let i = 0; i < clients.length; i++) {
23768
- const clientID = clients[i];
23769
- if (awareness.states.has(clientID)) {
23770
- awareness.states.delete(clientID);
23771
- if (clientID === awareness.clientID) {
23772
- const curMeta = /** @type {MetaClientState} */ (awareness.meta.get(clientID));
23773
- awareness.meta.set(clientID, {
23774
- clock: curMeta.clock + 1,
23775
- lastUpdated: getUnixTime()
23776
- });
23777
- }
23778
- removed.push(clientID);
23779
- }
23780
- }
23781
- if (removed.length > 0) {
23782
- awareness.emit('change', [{ added: [], updated: [], removed }, origin]);
23783
- awareness.emit('update', [{ added: [], updated: [], removed }, origin]);
23784
- }
23785
- };
23786
-
23787
- /**
23788
- * @param {Awareness} awareness
23789
- * @param {Array<number>} clients
23790
- * @return {Uint8Array}
23791
- */
23792
- const encodeAwarenessUpdate = (awareness, clients, states = awareness.states) => {
23793
- const len = clients.length;
23794
- const encoder = createEncoder();
23795
- writeVarUint(encoder, len);
23796
- for (let i = 0; i < len; i++) {
23797
- const clientID = clients[i];
23798
- const state = states.get(clientID) || null;
23799
- const clock = /** @type {MetaClientState} */ (awareness.meta.get(clientID)).clock;
23800
- writeVarUint(encoder, clientID);
23801
- writeVarUint(encoder, clock);
23802
- writeVarString(encoder, JSON.stringify(state));
23803
- }
23804
- return toUint8Array(encoder)
23805
- };
23806
-
23807
- /**
23808
- * @param {Awareness} awareness
23809
- * @param {Uint8Array} update
23810
- * @param {any} origin This will be added to the emitted change event
23811
- */
23812
- const applyAwarenessUpdate = (awareness, update, origin) => {
23813
- const decoder = createDecoder(update);
23814
- const timestamp = getUnixTime();
23815
- const added = [];
23816
- const updated = [];
23817
- const filteredUpdated = [];
23818
- const removed = [];
23819
- const len = readVarUint(decoder);
23820
- for (let i = 0; i < len; i++) {
23821
- const clientID = readVarUint(decoder);
23822
- let clock = readVarUint(decoder);
23823
- const state = JSON.parse(readVarString(decoder));
23824
- const clientMeta = awareness.meta.get(clientID);
23825
- const prevState = awareness.states.get(clientID);
23826
- const currClock = clientMeta === undefined ? 0 : clientMeta.clock;
23827
- if (currClock < clock || (currClock === clock && state === null && awareness.states.has(clientID))) {
23828
- if (state === null) {
23829
- // never let a remote client remove this local state
23830
- if (clientID === awareness.clientID && awareness.getLocalState() != null) {
23831
- // remote client removed the local state. Do not remote state. Broadcast a message indicating
23832
- // that this client still exists by increasing the clock
23833
- clock++;
23834
- } else {
23835
- awareness.states.delete(clientID);
23836
- }
23837
- } else {
23838
- awareness.states.set(clientID, state);
23839
- }
23840
- awareness.meta.set(clientID, {
23841
- clock,
23842
- lastUpdated: timestamp
23843
- });
23844
- if (clientMeta === undefined && state !== null) {
23845
- added.push(clientID);
23846
- } else if (clientMeta !== undefined && state === null) {
23847
- removed.push(clientID);
23848
- } else if (state !== null) {
23849
- if (!equalityDeep(state, prevState)) {
23850
- filteredUpdated.push(clientID);
23851
- }
23852
- updated.push(clientID);
23853
- }
23854
- }
23855
- }
23856
- if (added.length > 0 || filteredUpdated.length > 0 || removed.length > 0) {
23857
- awareness.emit('change', [{
23858
- added, updated: filteredUpdated, removed
23859
- }, origin]);
23860
- }
23861
- if (added.length > 0 || updated.length > 0 || removed.length > 0) {
23862
- awareness.emit('update', [{
23863
- added, updated, removed
23864
- }, origin]);
23865
- }
23866
- };
23867
-
23868
- class EventEmitter {
23869
- constructor() {
23870
- // eslint-disable-next-line @typescript-eslint/no-unsafe-function-type
23871
- this.callbacks = {};
23872
- }
23873
- // eslint-disable-next-line @typescript-eslint/no-unsafe-function-type
23874
- on(event, fn) {
23875
- if (!this.callbacks[event]) {
23876
- this.callbacks[event] = [];
23877
- }
23878
- this.callbacks[event].push(fn);
23879
- return this;
23880
- }
23881
- emit(event, ...args) {
23882
- const callbacks = this.callbacks[event];
23883
- if (callbacks) {
23884
- callbacks.forEach((callback) => callback.apply(this, args));
23885
- }
23886
- return this;
23887
- }
23888
- // eslint-disable-next-line @typescript-eslint/no-unsafe-function-type
23889
- off(event, fn) {
23890
- const callbacks = this.callbacks[event];
23891
- if (callbacks) {
23892
- if (fn) {
23893
- this.callbacks[event] = callbacks.filter((callback) => callback !== fn);
23894
- }
23895
- else {
23896
- delete this.callbacks[event];
23897
- }
23898
- }
23899
- return this;
23900
- }
23901
- removeAllListeners() {
23902
- this.callbacks = {};
23903
- }
23904
- }
23905
-
23906
- class IncomingMessage {
23907
- constructor(data) {
23908
- this.data = data;
23909
- this.encoder = createEncoder();
23910
- this.decoder = createDecoder(new Uint8Array(this.data));
23911
- }
23912
- peekVarString() {
23913
- return peekVarString(this.decoder);
23914
- }
23915
- readVarUint() {
23916
- return readVarUint(this.decoder);
23917
- }
23918
- readVarString() {
23919
- return readVarString(this.decoder);
23920
- }
23921
- readVarUint8Array() {
23922
- return readVarUint8Array(this.decoder);
23923
- }
23924
- writeVarUint(type) {
23925
- return writeVarUint(this.encoder, type);
23926
- }
23927
- writeVarString(string) {
23928
- return writeVarString(this.encoder, string);
23929
- }
23930
- writeVarUint8Array(data) {
23931
- return writeVarUint8Array(this.encoder, data);
23932
- }
23933
- length() {
23934
- return length$1(this.encoder);
23935
- }
23936
- }
23937
-
23938
- var MessageType;
23939
- (function (MessageType) {
23940
- MessageType[MessageType["Sync"] = 0] = "Sync";
23941
- MessageType[MessageType["Awareness"] = 1] = "Awareness";
23942
- MessageType[MessageType["Auth"] = 2] = "Auth";
23943
- MessageType[MessageType["QueryAwareness"] = 3] = "QueryAwareness";
23944
- MessageType[MessageType["Stateless"] = 5] = "Stateless";
23945
- MessageType[MessageType["CLOSE"] = 7] = "CLOSE";
23946
- MessageType[MessageType["SyncStatus"] = 8] = "SyncStatus";
23947
- })(MessageType || (MessageType = {}));
23948
- var WebSocketStatus;
23949
- (function (WebSocketStatus) {
23950
- WebSocketStatus["Connecting"] = "connecting";
23951
- WebSocketStatus["Connected"] = "connected";
23952
- WebSocketStatus["Disconnected"] = "disconnected";
23953
- })(WebSocketStatus || (WebSocketStatus = {}));
23954
-
23955
- class OutgoingMessage {
23956
- constructor() {
23957
- this.encoder = createEncoder();
23958
- }
23959
- get(args) {
23960
- return args.encoder;
23961
- }
23962
- toUint8Array() {
23963
- return toUint8Array(this.encoder);
23964
- }
23965
- }
23966
-
23967
- class CloseMessage extends OutgoingMessage {
23968
- constructor() {
23969
- super(...arguments);
23970
- this.type = MessageType.CLOSE;
23971
- this.description = "Ask the server to close the connection";
23972
- }
23973
- get(args) {
23974
- writeVarString(this.encoder, args.documentName);
23975
- writeVarUint(this.encoder, this.type);
23976
- return this.encoder;
23977
- }
23978
- }
23979
-
23980
- class HocuspocusProviderWebsocket extends EventEmitter {
23981
- constructor(configuration) {
23982
- super();
23983
- this.messageQueue = [];
23984
- this.configuration = {
23985
- url: "",
23986
- autoConnect: true,
23987
- preserveTrailingSlash: false,
23988
- // @ts-ignore
23989
- document: undefined,
23990
- WebSocketPolyfill: undefined,
23991
- // TODO: this should depend on awareness.outdatedTime
23992
- messageReconnectTimeout: 30000,
23993
- // 1 second
23994
- delay: 1000,
23995
- // instant
23996
- initialDelay: 0,
23997
- // double the delay each time
23998
- factor: 2,
23999
- // unlimited retries
24000
- maxAttempts: 0,
24001
- // wait at least 1 second
24002
- minDelay: 1000,
24003
- // at least every 30 seconds
24004
- maxDelay: 30000,
24005
- // randomize
24006
- jitter: true,
24007
- // retry forever
24008
- timeout: 0,
24009
- onOpen: () => null,
24010
- onConnect: () => null,
24011
- onMessage: () => null,
24012
- onOutgoingMessage: () => null,
24013
- onStatus: () => null,
24014
- onDisconnect: () => null,
24015
- onClose: () => null,
24016
- onDestroy: () => null,
24017
- onAwarenessUpdate: () => null,
24018
- onAwarenessChange: () => null,
24019
- handleTimeout: null,
24020
- providerMap: new Map(),
24021
- };
24022
- this.webSocket = null;
24023
- this.webSocketHandlers = {};
24024
- this.shouldConnect = true;
24025
- this.status = WebSocketStatus.Disconnected;
24026
- this.lastMessageReceived = 0;
24027
- this.identifier = 0;
24028
- this.intervals = {
24029
- connectionChecker: null,
24030
- };
24031
- this.connectionAttempt = null;
24032
- this.receivedOnOpenPayload = undefined;
24033
- this.closeTries = 0;
24034
- this.setConfiguration(configuration);
24035
- this.configuration.WebSocketPolyfill = configuration.WebSocketPolyfill
24036
- ? configuration.WebSocketPolyfill
24037
- : WebSocket;
24038
- this.on("open", this.configuration.onOpen);
24039
- this.on("open", this.onOpen.bind(this));
24040
- this.on("connect", this.configuration.onConnect);
24041
- this.on("message", this.configuration.onMessage);
24042
- this.on("outgoingMessage", this.configuration.onOutgoingMessage);
24043
- this.on("status", this.configuration.onStatus);
24044
- this.on("disconnect", this.configuration.onDisconnect);
24045
- this.on("close", this.configuration.onClose);
24046
- this.on("destroy", this.configuration.onDestroy);
24047
- this.on("awarenessUpdate", this.configuration.onAwarenessUpdate);
24048
- this.on("awarenessChange", this.configuration.onAwarenessChange);
24049
- this.on("close", this.onClose.bind(this));
24050
- this.on("message", this.onMessage.bind(this));
24051
- this.intervals.connectionChecker = setInterval(this.checkConnection.bind(this), this.configuration.messageReconnectTimeout / 10);
24052
- if (this.shouldConnect) {
24053
- this.connect();
24054
- }
24055
- }
24056
- async onOpen(event) {
24057
- this.status = WebSocketStatus.Connected;
24058
- this.emit("status", { status: WebSocketStatus.Connected });
24059
- this.cancelWebsocketRetry = undefined;
24060
- this.receivedOnOpenPayload = event;
24061
- }
24062
- attach(provider) {
24063
- this.configuration.providerMap.set(provider.configuration.name, provider);
24064
- if (this.status === WebSocketStatus.Disconnected && this.shouldConnect) {
24065
- this.connect();
24066
- }
24067
- if (this.receivedOnOpenPayload &&
24068
- this.status === WebSocketStatus.Connected) {
24069
- provider.onOpen(this.receivedOnOpenPayload);
24070
- }
24071
- }
24072
- detach(provider) {
24073
- if (this.configuration.providerMap.has(provider.configuration.name)) {
24074
- provider.send(CloseMessage, {
24075
- documentName: provider.configuration.name,
24076
- });
24077
- this.configuration.providerMap.delete(provider.configuration.name);
24078
- }
24079
- }
24080
- setConfiguration(configuration = {}) {
24081
- this.configuration = { ...this.configuration, ...configuration };
24082
- if (!this.configuration.autoConnect) {
24083
- this.shouldConnect = false;
24084
- }
24085
- }
24086
- async connect() {
24087
- if (this.status === WebSocketStatus.Connected) {
24088
- return;
24089
- }
24090
- // Always cancel any previously initiated connection retryer instances
24091
- if (this.cancelWebsocketRetry) {
24092
- this.cancelWebsocketRetry();
24093
- this.cancelWebsocketRetry = undefined;
24094
- }
24095
- this.receivedOnOpenPayload = undefined;
24096
- this.shouldConnect = true;
24097
- const abortableRetry = () => {
24098
- let cancelAttempt = false;
24099
- const retryPromise = retry(this.createWebSocketConnection.bind(this), {
24100
- delay: this.configuration.delay,
24101
- initialDelay: this.configuration.initialDelay,
24102
- factor: this.configuration.factor,
24103
- maxAttempts: this.configuration.maxAttempts,
24104
- minDelay: this.configuration.minDelay,
24105
- maxDelay: this.configuration.maxDelay,
24106
- jitter: this.configuration.jitter,
24107
- timeout: this.configuration.timeout,
24108
- handleTimeout: this.configuration.handleTimeout,
24109
- beforeAttempt: (context) => {
24110
- if (!this.shouldConnect || cancelAttempt) {
24111
- context.abort();
24112
- }
24113
- },
24114
- }).catch((error) => {
24115
- // If we aborted the connection attempt then don’t throw an error
24116
- // ref: https://github.com/lifeomic/attempt/blob/master/src/index.ts#L136
24117
- if (error && error.code !== "ATTEMPT_ABORTED") {
24118
- throw error;
24119
- }
24120
- });
24121
- return {
24122
- retryPromise,
24123
- cancelFunc: () => {
24124
- cancelAttempt = true;
24125
- },
24126
- };
24127
- };
24128
- const { retryPromise, cancelFunc } = abortableRetry();
24129
- this.cancelWebsocketRetry = cancelFunc;
24130
- return retryPromise;
24131
- }
24132
- // eslint-disable-next-line @typescript-eslint/no-unsafe-function-type
24133
- attachWebSocketListeners(ws, reject) {
24134
- const { identifier } = ws;
24135
- const onMessageHandler = (payload) => this.emit("message", payload);
24136
- const onCloseHandler = (payload) => this.emit("close", { event: payload });
24137
- const onOpenHandler = (payload) => this.emit("open", payload);
24138
- const onErrorHandler = (err) => {
24139
- reject(err);
24140
- };
24141
- this.webSocketHandlers[identifier] = {
24142
- message: onMessageHandler,
24143
- close: onCloseHandler,
24144
- open: onOpenHandler,
24145
- error: onErrorHandler,
24146
- };
24147
- const handlers = this.webSocketHandlers[ws.identifier];
24148
- Object.keys(handlers).forEach((name) => {
24149
- ws.addEventListener(name, handlers[name]);
24150
- });
24151
- }
24152
- cleanupWebSocket() {
24153
- if (!this.webSocket) {
24154
- return;
24155
- }
24156
- const { identifier } = this.webSocket;
24157
- const handlers = this.webSocketHandlers[identifier];
24158
- Object.keys(handlers).forEach((name) => {
24159
- var _a;
24160
- (_a = this.webSocket) === null || _a === void 0 ? void 0 : _a.removeEventListener(name, handlers[name]);
24161
- delete this.webSocketHandlers[identifier];
24162
- });
24163
- this.webSocket.close();
24164
- this.webSocket = null;
24165
- }
24166
- createWebSocketConnection() {
24167
- return new Promise((resolve, reject) => {
24168
- if (this.webSocket) {
24169
- this.messageQueue = [];
24170
- this.cleanupWebSocket();
24171
- }
24172
- this.lastMessageReceived = 0;
24173
- this.identifier += 1;
24174
- // Init the WebSocket connection
24175
- const ws = new this.configuration.WebSocketPolyfill(this.url);
24176
- ws.binaryType = "arraybuffer";
24177
- ws.identifier = this.identifier;
24178
- this.attachWebSocketListeners(ws, reject);
24179
- this.webSocket = ws;
24180
- // Reset the status
24181
- this.status = WebSocketStatus.Connecting;
24182
- this.emit("status", { status: WebSocketStatus.Connecting });
24183
- // Store resolve/reject for later use
24184
- this.connectionAttempt = {
24185
- resolve,
24186
- reject,
24187
- };
24188
- });
24189
- }
24190
- onMessage(event) {
24191
- var _a;
24192
- this.resolveConnectionAttempt();
24193
- this.lastMessageReceived = getUnixTime();
24194
- const message = new IncomingMessage(event.data);
24195
- const documentName = message.peekVarString();
24196
- (_a = this.configuration.providerMap.get(documentName)) === null || _a === void 0 ? void 0 : _a.onMessage(event);
24197
- }
24198
- resolveConnectionAttempt() {
24199
- if (this.connectionAttempt) {
24200
- this.connectionAttempt.resolve();
24201
- this.connectionAttempt = null;
24202
- this.status = WebSocketStatus.Connected;
24203
- this.emit("status", { status: WebSocketStatus.Connected });
24204
- this.emit("connect");
24205
- this.messageQueue.forEach((message) => this.send(message));
24206
- this.messageQueue = [];
24207
- }
24208
- }
24209
- stopConnectionAttempt() {
24210
- this.connectionAttempt = null;
24211
- }
24212
- rejectConnectionAttempt() {
24213
- var _a;
24214
- (_a = this.connectionAttempt) === null || _a === void 0 ? void 0 : _a.reject();
24215
- this.connectionAttempt = null;
24216
- }
24217
- checkConnection() {
24218
- var _a;
24219
- // Don’t check the connection when it’s not even established
24220
- if (this.status !== WebSocketStatus.Connected) {
24221
- return;
24222
- }
24223
- // Don’t close the connection while waiting for the first message
24224
- if (!this.lastMessageReceived) {
24225
- return;
24226
- }
24227
- // Don’t close the connection when a message was received recently
24228
- if (this.configuration.messageReconnectTimeout >=
24229
- getUnixTime() - this.lastMessageReceived) {
24230
- return;
24231
- }
24232
- // No message received in a long time, not even your own
24233
- // Awareness updates, which are updated every 15 seconds
24234
- // if awareness is enabled.
24235
- this.closeTries += 1;
24236
- // https://bugs.webkit.org/show_bug.cgi?id=247943
24237
- if (this.closeTries > 2) {
24238
- this.onClose({
24239
- event: {
24240
- code: 4408,
24241
- reason: "forced",
24242
- },
24243
- });
24244
- this.closeTries = 0;
24245
- }
24246
- else {
24247
- (_a = this.webSocket) === null || _a === void 0 ? void 0 : _a.close();
24248
- this.messageQueue = [];
24249
- }
24250
- }
24251
- get serverUrl() {
24252
- if (this.configuration.preserveTrailingSlash) {
24253
- return this.configuration.url;
24254
- }
24255
- // By default, ensure that the URL never ends with /
24256
- let url = this.configuration.url;
24257
- while (url[url.length - 1] === "/") {
24258
- url = url.slice(0, url.length - 1);
24259
- }
24260
- return url;
24261
- }
24262
- get url() {
24263
- return this.serverUrl;
24264
- }
24265
- disconnect() {
24266
- this.shouldConnect = false;
24267
- if (this.webSocket === null) {
24268
- return;
24269
- }
24270
- try {
24271
- this.webSocket.close();
24272
- this.messageQueue = [];
24273
- }
24274
- catch (e) {
24275
- console.error(e);
24276
- }
24277
- }
24278
- send(message) {
24279
- var _a;
24280
- if (((_a = this.webSocket) === null || _a === void 0 ? void 0 : _a.readyState) === WsReadyStates.Open) {
24281
- this.webSocket.send(message);
24282
- }
24283
- else {
24284
- this.messageQueue.push(message);
24285
- }
24286
- }
24287
- onClose({ event }) {
24288
- this.closeTries = 0;
24289
- this.cleanupWebSocket();
24290
- if (this.connectionAttempt) {
24291
- // That connection attempt failed.
24292
- this.rejectConnectionAttempt();
24293
- }
24294
- // Let’s update the connection status.
24295
- this.status = WebSocketStatus.Disconnected;
24296
- this.emit("status", { status: WebSocketStatus.Disconnected });
24297
- this.emit("disconnect", { event });
24298
- // trigger connect if no retry is running and we want to have a connection
24299
- if (!this.cancelWebsocketRetry && this.shouldConnect) {
24300
- setTimeout(() => {
24301
- this.connect();
24302
- }, this.configuration.delay);
24303
- }
24304
- }
24305
- destroy() {
24306
- this.emit("destroy");
24307
- clearInterval(this.intervals.connectionChecker);
24308
- // If there is still a connection attempt outstanding then we should stop
24309
- // it before calling disconnect, otherwise it will be rejected in the onClose
24310
- // handler and trigger a retry
24311
- this.stopConnectionAttempt();
24312
- this.disconnect();
24313
- this.removeAllListeners();
24314
- this.cleanupWebSocket();
24315
- }
24316
- }
24317
-
24318
- /**
24319
- * @module sync-protocol
24320
- */
24321
-
24322
-
24323
- /**
24324
- * @typedef {Map<number, number>} StateMap
24325
- */
24326
-
24327
- /**
24328
- * Core Yjs defines two message types:
24329
- * • YjsSyncStep1: Includes the State Set of the sending client. When received, the client should reply with YjsSyncStep2.
24330
- * • YjsSyncStep2: Includes all missing structs and the complete delete set. When received, the client is assured that it
24331
- * received all information from the remote client.
24332
- *
24333
- * In a peer-to-peer network, you may want to introduce a SyncDone message type. Both parties should initiate the connection
24334
- * with SyncStep1. When a client received SyncStep2, it should reply with SyncDone. When the local client received both
24335
- * SyncStep2 and SyncDone, it is assured that it is synced to the remote client.
24336
- *
24337
- * In a client-server model, you want to handle this differently: The client should initiate the connection with SyncStep1.
24338
- * When the server receives SyncStep1, it should reply with SyncStep2 immediately followed by SyncStep1. The client replies
24339
- * with SyncStep2 when it receives SyncStep1. Optionally the server may send a SyncDone after it received SyncStep2, so the
24340
- * client knows that the sync is finished. There are two reasons for this more elaborated sync model: 1. This protocol can
24341
- * easily be implemented on top of http and websockets. 2. The server should only reply to requests, and not initiate them.
24342
- * Therefore it is necessary that the client initiates the sync.
24343
- *
24344
- * Construction of a message:
24345
- * [messageType : varUint, message definition..]
24346
- *
24347
- * Note: A message does not include information about the room name. This must to be handled by the upper layer protocol!
24348
- *
24349
- * stringify[messageType] stringifies a message definition (messageType is already read from the bufffer)
24350
- */
24351
-
24352
- const messageYjsSyncStep1 = 0;
24353
- const messageYjsSyncStep2 = 1;
24354
- const messageYjsUpdate = 2;
24355
-
24356
- /**
24357
- * Create a sync step 1 message based on the state of the current shared document.
24358
- *
24359
- * @param {encoding.Encoder} encoder
24360
- * @param {Y.Doc} doc
24361
- */
24362
- const writeSyncStep1 = (encoder, doc) => {
24363
- writeVarUint(encoder, messageYjsSyncStep1);
24364
- const sv = Y__namespace.encodeStateVector(doc);
24365
- writeVarUint8Array(encoder, sv);
24366
- };
24367
-
24368
- /**
24369
- * @param {encoding.Encoder} encoder
24370
- * @param {Y.Doc} doc
24371
- * @param {Uint8Array} [encodedStateVector]
24372
- */
24373
- const writeSyncStep2 = (encoder, doc, encodedStateVector) => {
24374
- writeVarUint(encoder, messageYjsSyncStep2);
24375
- writeVarUint8Array(encoder, Y__namespace.encodeStateAsUpdate(doc, encodedStateVector));
24376
- };
24377
-
24378
- /**
24379
- * Read SyncStep1 message and reply with SyncStep2.
24380
- *
24381
- * @param {decoding.Decoder} decoder The reply to the received message
24382
- * @param {encoding.Encoder} encoder The received message
24383
- * @param {Y.Doc} doc
24384
- */
24385
- const readSyncStep1 = (decoder, encoder, doc) =>
24386
- writeSyncStep2(encoder, doc, readVarUint8Array(decoder));
24387
-
24388
- /**
24389
- * Read and apply Structs and then DeleteStore to a y instance.
24390
- *
24391
- * @param {decoding.Decoder} decoder
24392
- * @param {Y.Doc} doc
24393
- * @param {any} transactionOrigin
24394
- */
24395
- const readSyncStep2 = (decoder, doc, transactionOrigin) => {
24396
- try {
24397
- Y__namespace.applyUpdate(doc, readVarUint8Array(decoder), transactionOrigin);
24398
- } catch (error) {
24399
- // This catches errors that are thrown by event handlers
24400
- console.error('Caught error while handling a Yjs update', error);
24401
- }
24402
- };
24403
-
24404
- /**
24405
- * @param {encoding.Encoder} encoder
24406
- * @param {Uint8Array} update
24407
- */
24408
- const writeUpdate = (encoder, update) => {
24409
- writeVarUint(encoder, messageYjsUpdate);
24410
- writeVarUint8Array(encoder, update);
24411
- };
24412
-
24413
- /**
24414
- * Read and apply Structs and then DeleteStore to a y instance.
24415
- *
24416
- * @param {decoding.Decoder} decoder
24417
- * @param {Y.Doc} doc
24418
- * @param {any} transactionOrigin
24419
- */
24420
- const readUpdate = readSyncStep2;
24421
-
24422
- /**
24423
- * @param {decoding.Decoder} decoder A message received from another client
24424
- * @param {encoding.Encoder} encoder The reply message. Does not need to be sent if empty.
24425
- * @param {Y.Doc} doc
24426
- * @param {any} transactionOrigin
24427
- */
24428
- const readSyncMessage = (decoder, encoder, doc, transactionOrigin) => {
24429
- const messageType = readVarUint(decoder);
24430
- switch (messageType) {
24431
- case messageYjsSyncStep1:
24432
- readSyncStep1(decoder, encoder, doc);
24433
- break
24434
- case messageYjsSyncStep2:
24435
- readSyncStep2(decoder, doc, transactionOrigin);
24436
- break
24437
- case messageYjsUpdate:
24438
- readUpdate(decoder, doc, transactionOrigin);
24439
- break
24440
- default:
24441
- throw new Error('Unknown message type')
24442
- }
24443
- return messageType
24444
- };
24445
-
24446
- class MessageReceiver {
24447
- constructor(message) {
24448
- this.message = message;
24449
- }
24450
- apply(provider, emitSynced) {
24451
- const { message } = this;
24452
- const type = message.readVarUint();
24453
- const emptyMessageLength = message.length();
24454
- switch (type) {
24455
- case MessageType.Sync:
24456
- this.applySyncMessage(provider, emitSynced);
24457
- break;
24458
- case MessageType.Awareness:
24459
- this.applyAwarenessMessage(provider);
24460
- break;
24461
- case MessageType.Auth:
24462
- this.applyAuthMessage(provider);
24463
- break;
24464
- case MessageType.QueryAwareness:
24465
- this.applyQueryAwarenessMessage(provider);
24466
- break;
24467
- case MessageType.Stateless:
24468
- provider.receiveStateless(readVarString(message.decoder));
24469
- break;
24470
- case MessageType.SyncStatus:
24471
- this.applySyncStatusMessage(provider, readVarInt(message.decoder) === 1);
24472
- break;
24473
- case MessageType.CLOSE:
24474
- // eslint-disable-next-line no-case-declarations
24475
- const event = {
24476
- code: 1000,
24477
- reason: readVarString(message.decoder),
24478
- // @ts-ignore
24479
- target: provider.configuration.websocketProvider.webSocket,
24480
- type: "close",
24481
- };
24482
- provider.onClose();
24483
- provider.configuration.onClose({ event });
24484
- provider.forwardClose({ event });
24485
- break;
24486
- default:
24487
- throw new Error(`Can’t apply message of unknown type: ${type}`);
24488
- }
24489
- // Reply
24490
- if (message.length() > emptyMessageLength + 1) {
24491
- // length of documentName (considered in emptyMessageLength plus length of yjs sync type, set in applySyncMessage)
24492
- // @ts-ignore
24493
- provider.send(OutgoingMessage, { encoder: message.encoder });
24494
- }
24495
- }
24496
- applySyncMessage(provider, emitSynced) {
24497
- const { message } = this;
24498
- message.writeVarUint(MessageType.Sync);
24499
- // Apply update
24500
- const syncMessageType = readSyncMessage(message.decoder, message.encoder, provider.document, provider);
24501
- // Synced once we receive Step2
24502
- if (emitSynced && syncMessageType === messageYjsSyncStep2) {
24503
- provider.synced = true;
24504
- }
24505
- }
24506
- applySyncStatusMessage(provider, applied) {
24507
- if (applied) {
24508
- provider.decrementUnsyncedChanges();
24509
- }
24510
- }
24511
- applyAwarenessMessage(provider) {
24512
- if (!provider.awareness)
24513
- return;
24514
- const { message } = this;
24515
- applyAwarenessUpdate(provider.awareness, message.readVarUint8Array(), provider);
24516
- }
24517
- applyAuthMessage(provider) {
24518
- const { message } = this;
24519
- readAuthMessage(message.decoder, provider.sendToken.bind(provider), provider.permissionDeniedHandler.bind(provider), provider.authenticatedHandler.bind(provider));
24520
- }
24521
- applyQueryAwarenessMessage(provider) {
24522
- if (!provider.awareness)
24523
- return;
24524
- const { message } = this;
24525
- message.writeVarUint(MessageType.Awareness);
24526
- message.writeVarUint8Array(encodeAwarenessUpdate(provider.awareness, Array.from(provider.awareness.getStates().keys())));
24527
- }
24528
- }
24529
-
24530
- class MessageSender {
24531
- constructor(Message, args = {}) {
24532
- this.message = new Message();
24533
- this.encoder = this.message.get(args);
24534
- }
24535
- create() {
24536
- return toUint8Array(this.encoder);
24537
- }
24538
- send(webSocket) {
24539
- webSocket === null || webSocket === void 0 ? void 0 : webSocket.send(this.create());
24540
- }
24541
- }
24542
-
24543
- class AuthenticationMessage extends OutgoingMessage {
24544
- constructor() {
24545
- super(...arguments);
24546
- this.type = MessageType.Auth;
24547
- this.description = "Authentication";
24548
- }
24549
- get(args) {
24550
- if (typeof args.token === "undefined") {
24551
- throw new Error("The authentication message requires `token` as an argument.");
24552
- }
24553
- writeVarString(this.encoder, args.documentName);
24554
- writeVarUint(this.encoder, this.type);
24555
- writeAuthentication(this.encoder, args.token);
24556
- return this.encoder;
24557
- }
24558
- }
24559
-
24560
- class AwarenessMessage extends OutgoingMessage {
24561
- constructor() {
24562
- super(...arguments);
24563
- this.type = MessageType.Awareness;
24564
- this.description = "Awareness states update";
24565
- }
24566
- get(args) {
24567
- if (typeof args.awareness === "undefined") {
24568
- throw new Error("The awareness message requires awareness as an argument");
24569
- }
24570
- if (typeof args.clients === "undefined") {
24571
- throw new Error("The awareness message requires clients as an argument");
24572
- }
24573
- writeVarString(this.encoder, args.documentName);
24574
- writeVarUint(this.encoder, this.type);
24575
- let awarenessUpdate;
24576
- if (args.states === undefined) {
24577
- awarenessUpdate = encodeAwarenessUpdate(args.awareness, args.clients);
24578
- }
24579
- else {
24580
- awarenessUpdate = encodeAwarenessUpdate(args.awareness, args.clients, args.states);
24581
- }
24582
- writeVarUint8Array(this.encoder, awarenessUpdate);
24583
- return this.encoder;
24584
- }
24585
- }
24586
-
24587
- class StatelessMessage extends OutgoingMessage {
24588
- constructor() {
24589
- super(...arguments);
24590
- this.type = MessageType.Stateless;
24591
- this.description = "A stateless message";
24592
- }
24593
- get(args) {
24594
- var _a;
24595
- writeVarString(this.encoder, args.documentName);
24596
- writeVarUint(this.encoder, this.type);
24597
- writeVarString(this.encoder, (_a = args.payload) !== null && _a !== void 0 ? _a : "");
24598
- return this.encoder;
24599
- }
24600
- }
24601
-
24602
- class SyncStepOneMessage extends OutgoingMessage {
24603
- constructor() {
24604
- super(...arguments);
24605
- this.type = MessageType.Sync;
24606
- this.description = "First sync step";
24607
- }
24608
- get(args) {
24609
- if (typeof args.document === "undefined") {
24610
- throw new Error("The sync step one message requires document as an argument");
24611
- }
24612
- writeVarString(this.encoder, args.documentName);
24613
- writeVarUint(this.encoder, this.type);
24614
- writeSyncStep1(this.encoder, args.document);
24615
- return this.encoder;
24616
- }
24617
- }
24618
-
24619
- class UpdateMessage extends OutgoingMessage {
24620
- constructor() {
24621
- super(...arguments);
24622
- this.type = MessageType.Sync;
24623
- this.description = "A document update";
24624
- }
24625
- get(args) {
24626
- writeVarString(this.encoder, args.documentName);
24627
- writeVarUint(this.encoder, this.type);
24628
- writeUpdate(this.encoder, args.update);
24629
- return this.encoder;
24630
- }
24631
- }
24632
-
24633
- class AwarenessError extends Error {
24634
- constructor() {
24635
- super(...arguments);
24636
- this.code = 1001;
24637
- }
24638
- }
24639
- class HocuspocusProvider extends EventEmitter {
24640
- constructor(configuration) {
24641
- var _a, _b, _c;
24642
- super();
24643
- this.configuration = {
24644
- name: "",
24645
- // @ts-ignore
24646
- document: undefined,
24647
- // @ts-ignore
24648
- awareness: undefined,
24649
- token: null,
24650
- forceSyncInterval: false,
24651
- onAuthenticated: () => null,
24652
- onAuthenticationFailed: () => null,
24653
- onOpen: () => null,
24654
- onConnect: () => null,
24655
- onMessage: () => null,
24656
- onOutgoingMessage: () => null,
24657
- onSynced: () => null,
24658
- onStatus: () => null,
24659
- onDisconnect: () => null,
24660
- onClose: () => null,
24661
- onDestroy: () => null,
24662
- onAwarenessUpdate: () => null,
24663
- onAwarenessChange: () => null,
24664
- onStateless: () => null,
24665
- onUnsyncedChanges: () => null,
24666
- };
24667
- this.isSynced = false;
24668
- this.unsyncedChanges = 0;
24669
- this.isAuthenticated = false;
24670
- this.authorizedScope = undefined;
24671
- // @internal
24672
- this.manageSocket = false;
24673
- this._isAttached = false;
24674
- this.intervals = {
24675
- forceSync: null,
24676
- };
24677
- this.boundDocumentUpdateHandler = this.documentUpdateHandler.bind(this);
24678
- this.boundAwarenessUpdateHandler = this.awarenessUpdateHandler.bind(this);
24679
- this.boundPageHide = this.pageHide.bind(this);
24680
- this.boundOnOpen = this.onOpen.bind(this);
24681
- this.boundOnClose = this.onClose.bind(this);
24682
- this.forwardConnect = () => this.emit("connect");
24683
- this.forwardStatus = (e) => this.emit("status", e);
24684
- this.forwardClose = (e) => this.emit("close", e);
24685
- this.forwardDisconnect = (e) => this.emit("disconnect", e);
24686
- this.forwardDestroy = () => this.emit("destroy");
24687
- this.setConfiguration(configuration);
24688
- this.configuration.document = configuration.document
24689
- ? configuration.document
24690
- : new Y__namespace.Doc();
24691
- this.configuration.awareness =
24692
- configuration.awareness !== undefined
24693
- ? configuration.awareness
24694
- : new Awareness(this.document);
24695
- this.on("open", this.configuration.onOpen);
24696
- this.on("message", this.configuration.onMessage);
24697
- this.on("outgoingMessage", this.configuration.onOutgoingMessage);
24698
- this.on("synced", this.configuration.onSynced);
24699
- this.on("destroy", this.configuration.onDestroy);
24700
- this.on("awarenessUpdate", this.configuration.onAwarenessUpdate);
24701
- this.on("awarenessChange", this.configuration.onAwarenessChange);
24702
- this.on("stateless", this.configuration.onStateless);
24703
- this.on("unsyncedChanges", this.configuration.onUnsyncedChanges);
24704
- this.on("authenticated", this.configuration.onAuthenticated);
24705
- this.on("authenticationFailed", this.configuration.onAuthenticationFailed);
24706
- (_a = this.awareness) === null || _a === void 0 ? void 0 : _a.on("update", () => {
24707
- this.emit("awarenessUpdate", {
24708
- states: awarenessStatesToArray(this.awareness.getStates()),
24709
- });
24710
- });
24711
- (_b = this.awareness) === null || _b === void 0 ? void 0 : _b.on("change", () => {
24712
- this.emit("awarenessChange", {
24713
- states: awarenessStatesToArray(this.awareness.getStates()),
24714
- });
24715
- });
24716
- this.document.on("update", this.boundDocumentUpdateHandler);
24717
- (_c = this.awareness) === null || _c === void 0 ? void 0 : _c.on("update", this.boundAwarenessUpdateHandler);
24718
- this.registerEventListeners();
24719
- if (this.configuration.forceSyncInterval &&
24720
- typeof this.configuration.forceSyncInterval === "number") {
24721
- this.intervals.forceSync = setInterval(this.forceSync.bind(this), this.configuration.forceSyncInterval);
24722
- }
24723
- if (this.manageSocket) {
24724
- this.attach();
24725
- }
24726
- }
24727
- setConfiguration(configuration = {}) {
24728
- if (!configuration.websocketProvider) {
24729
- this.manageSocket = true;
24730
- this.configuration.websocketProvider = new HocuspocusProviderWebsocket(configuration);
24731
- }
24732
- this.configuration = { ...this.configuration, ...configuration };
24733
- }
24734
- get document() {
24735
- return this.configuration.document;
24736
- }
24737
- get isAttached() {
24738
- return this._isAttached;
24739
- }
24740
- get awareness() {
24741
- return this.configuration.awareness;
24742
- }
24743
- get hasUnsyncedChanges() {
24744
- return this.unsyncedChanges > 0;
24745
- }
24746
- resetUnsyncedChanges() {
24747
- this.unsyncedChanges = 1;
24748
- this.emit("unsyncedChanges", { number: this.unsyncedChanges });
24749
- }
24750
- incrementUnsyncedChanges() {
24751
- this.unsyncedChanges += 1;
24752
- this.emit("unsyncedChanges", { number: this.unsyncedChanges });
24753
- }
24754
- decrementUnsyncedChanges() {
24755
- if (this.unsyncedChanges > 0) {
24756
- this.unsyncedChanges -= 1;
24757
- }
24758
- if (this.unsyncedChanges === 0) {
24759
- this.synced = true;
24760
- }
24761
- this.emit("unsyncedChanges", { number: this.unsyncedChanges });
24762
- }
24763
- forceSync() {
24764
- this.resetUnsyncedChanges();
24765
- this.send(SyncStepOneMessage, {
24766
- document: this.document,
24767
- documentName: this.configuration.name,
24768
- });
24769
- }
24770
- pageHide() {
24771
- if (this.awareness) {
24772
- removeAwarenessStates(this.awareness, [this.document.clientID], "page hide");
24773
- }
24774
- }
24775
- registerEventListeners() {
24776
- if (typeof window === "undefined" || !("addEventListener" in window)) {
24777
- return;
24778
- }
24779
- window.addEventListener("pagehide", this.boundPageHide);
24780
- }
24781
- sendStateless(payload) {
24782
- this.send(StatelessMessage, {
24783
- documentName: this.configuration.name,
24784
- payload,
24785
- });
24786
- }
24787
- async sendToken() {
24788
- let token;
24789
- try {
24790
- token = await this.getToken();
24791
- }
24792
- catch (error) {
24793
- this.permissionDeniedHandler(`Failed to get token during sendToken(): ${error}`);
24794
- return;
24795
- }
24796
- this.send(AuthenticationMessage, {
24797
- token: token !== null && token !== void 0 ? token : "",
24798
- documentName: this.configuration.name,
24799
- });
24800
- }
24801
- documentUpdateHandler(update, origin) {
24802
- if (origin === this) {
24803
- return;
24804
- }
24805
- this.incrementUnsyncedChanges();
24806
- this.send(UpdateMessage, { update, documentName: this.configuration.name });
24807
- }
24808
- awarenessUpdateHandler({ added, updated, removed }, origin) {
24809
- const changedClients = added.concat(updated).concat(removed);
24810
- this.send(AwarenessMessage, {
24811
- awareness: this.awareness,
24812
- clients: changedClients,
24813
- documentName: this.configuration.name,
24814
- });
24815
- }
24816
- /**
24817
- * Indicates whether a first handshake with the server has been established
24818
- *
24819
- * Note: this does not mean all updates from the client have been persisted to the backend. For this,
24820
- * use `hasUnsyncedChanges`.
24821
- */
24822
- get synced() {
24823
- return this.isSynced;
24824
- }
24825
- set synced(state) {
24826
- if (this.isSynced === state) {
24827
- return;
24828
- }
24829
- this.isSynced = state;
24830
- if (state) {
24831
- this.emit("synced", { state });
24832
- }
24833
- }
24834
- receiveStateless(payload) {
24835
- this.emit("stateless", { payload });
24836
- }
24837
- // not needed, but provides backward compatibility with e.g. lexical/yjs
24838
- async connect() {
24839
- if (this.manageSocket) {
24840
- return this.configuration.websocketProvider.connect();
24841
- }
24842
- console.warn("HocuspocusProvider::connect() is deprecated and does not do anything. Please connect/disconnect on the websocketProvider, or attach/deattach providers.");
24843
- }
24844
- disconnect() {
24845
- if (this.manageSocket) {
24846
- return this.configuration.websocketProvider.disconnect();
24847
- }
24848
- console.warn("HocuspocusProvider::disconnect() is deprecated and does not do anything. Please connect/disconnect on the websocketProvider, or attach/deattach providers.");
24849
- }
24850
- async onOpen(event) {
24851
- this.isAuthenticated = false;
24852
- this.emit("open", { event });
24853
- await this.sendToken();
24854
- this.startSync();
24855
- }
24856
- async getToken() {
24857
- if (typeof this.configuration.token === "function") {
24858
- const token = await this.configuration.token();
24859
- return token;
24860
- }
24861
- return this.configuration.token;
24862
- }
24863
- startSync() {
24864
- this.resetUnsyncedChanges();
24865
- this.send(SyncStepOneMessage, {
24866
- document: this.document,
24867
- documentName: this.configuration.name,
24868
- });
24869
- if (this.awareness && this.awareness.getLocalState() !== null) {
24870
- this.send(AwarenessMessage, {
24871
- awareness: this.awareness,
24872
- clients: [this.document.clientID],
24873
- documentName: this.configuration.name,
24874
- });
24875
- }
24876
- }
24877
- send(message, args) {
24878
- if (!this._isAttached)
24879
- return;
24880
- const messageSender = new MessageSender(message, args);
24881
- this.emit("outgoingMessage", { message: messageSender.message });
24882
- messageSender.send(this.configuration.websocketProvider);
24883
- }
24884
- onMessage(event) {
24885
- const message = new IncomingMessage(event.data);
24886
- const documentName = message.readVarString();
24887
- message.writeVarString(documentName);
24888
- this.emit("message", { event, message: new IncomingMessage(event.data) });
24889
- new MessageReceiver(message).apply(this, true);
24890
- }
24891
- onClose() {
24892
- this.isAuthenticated = false;
24893
- this.synced = false;
24894
- // update awareness (all users except local left)
24895
- if (this.awareness) {
24896
- removeAwarenessStates(this.awareness, Array.from(this.awareness.getStates().keys()).filter((client) => client !== this.document.clientID), this);
24897
- }
24898
- }
24899
- destroy() {
24900
- this.emit("destroy");
24901
- if (this.intervals.forceSync) {
24902
- clearInterval(this.intervals.forceSync);
24903
- }
24904
- if (this.awareness) {
24905
- removeAwarenessStates(this.awareness, [this.document.clientID], "provider destroy");
24906
- this.awareness.off("update", this.boundAwarenessUpdateHandler);
24907
- this.awareness.destroy();
24908
- }
24909
- this.document.off("update", this.boundDocumentUpdateHandler);
24910
- this.removeAllListeners();
24911
- this.detach();
24912
- if (this.manageSocket) {
24913
- this.configuration.websocketProvider.destroy();
24914
- }
24915
- if (typeof window === "undefined" || !("removeEventListener" in window)) {
24916
- return;
24917
- }
24918
- window.removeEventListener("pagehide", this.boundPageHide);
24919
- }
24920
- detach() {
24921
- this.configuration.websocketProvider.off("connect", this.configuration.onConnect);
24922
- this.configuration.websocketProvider.off("connect", this.forwardConnect);
24923
- this.configuration.websocketProvider.off("status", this.forwardStatus);
24924
- this.configuration.websocketProvider.off("status", this.configuration.onStatus);
24925
- this.configuration.websocketProvider.off("open", this.boundOnOpen);
24926
- this.configuration.websocketProvider.off("close", this.boundOnClose);
24927
- this.configuration.websocketProvider.off("close", this.configuration.onClose);
24928
- this.configuration.websocketProvider.off("close", this.forwardClose);
24929
- this.configuration.websocketProvider.off("disconnect", this.configuration.onDisconnect);
24930
- this.configuration.websocketProvider.off("disconnect", this.forwardDisconnect);
24931
- this.configuration.websocketProvider.off("destroy", this.configuration.onDestroy);
24932
- this.configuration.websocketProvider.off("destroy", this.forwardDestroy);
24933
- this.configuration.websocketProvider.detach(this);
24934
- this._isAttached = false;
24935
- }
24936
- attach() {
24937
- if (this._isAttached)
24938
- return;
24939
- this.configuration.websocketProvider.on("connect", this.configuration.onConnect);
24940
- this.configuration.websocketProvider.on("connect", this.forwardConnect);
24941
- this.configuration.websocketProvider.on("status", this.configuration.onStatus);
24942
- this.configuration.websocketProvider.on("status", this.forwardStatus);
24943
- this.configuration.websocketProvider.on("open", this.boundOnOpen);
24944
- this.configuration.websocketProvider.on("close", this.boundOnClose);
24945
- this.configuration.websocketProvider.on("close", this.configuration.onClose);
24946
- this.configuration.websocketProvider.on("close", this.forwardClose);
24947
- this.configuration.websocketProvider.on("disconnect", this.configuration.onDisconnect);
24948
- this.configuration.websocketProvider.on("disconnect", this.forwardDisconnect);
24949
- this.configuration.websocketProvider.on("destroy", this.configuration.onDestroy);
24950
- this.configuration.websocketProvider.on("destroy", this.forwardDestroy);
24951
- this.configuration.websocketProvider.attach(this);
24952
- this._isAttached = true;
24953
- }
24954
- permissionDeniedHandler(reason) {
24955
- this.emit("authenticationFailed", { reason });
24956
- this.isAuthenticated = false;
24957
- }
24958
- authenticatedHandler(scope) {
24959
- this.isAuthenticated = true;
24960
- this.authorizedScope = scope;
24961
- this.emit("authenticated", { scope });
24962
- }
24963
- setAwarenessField(key, value) {
24964
- if (!this.awareness) {
24965
- throw new AwarenessError(`Cannot set awareness field "${key}" to ${JSON.stringify(value)}. You have disabled Awareness for this provider by explicitly passing awareness: null in the provider configuration.`);
24966
- }
24967
- this.awareness.setLocalStateField(key, value);
24968
- }
24969
- }
24970
-
24971
- /**
24972
- * Represents a workspace in the Kritzel canvas.
24973
- * A workspace is an isolated canvas area with its own set of objects and viewport state.
24974
- * It can be serialized and deserialized for persistence and collaborative editing.
24975
- */
24976
- class KritzelWorkspace {
24977
- __class__ = 'KritzelWorkspace';
24978
- /** Unique identifier for the workspace */
24979
- id;
24980
- /** Display name of the workspace */
24981
- name;
24982
- /** Timestamp when the workspace was created */
24983
- createdAt;
24984
- /** Timestamp when the workspace was last updated */
24985
- updatedAt;
24986
- /** Viewport state containing pan and zoom information */
24987
- viewport;
24988
- /** Reference to the KritzelCore instance managing this workspace */
24989
- _core;
24990
- /**
24991
- * Creates a new KritzelWorkspace instance.
24992
- * @param id - Unique identifier for the workspace
24993
- * @param name - Display name of the workspace
24994
- * @param viewport - Initial viewport state with translateX, translateY, and scale values
24995
- */
24996
- constructor(id, name, viewport = { translateX: 0, translateY: 0, scale: 1 }) {
24997
- this.id = id;
24998
- this.name = name;
24999
- this.createdAt = new Date();
25000
- this.updatedAt = new Date();
25001
- this.viewport = viewport;
25002
- }
25003
- /**
25004
- * Factory method to create a workspace with a core reference.
25005
- * @param core - The KritzelCore instance to associate with this workspace
25006
- * @param obj - Object containing workspace properties (id, name, viewport)
25007
- * @returns A new KritzelWorkspace instance with the core reference set
25008
- */
25009
- static create(core, obj) {
25010
- const workspace = new KritzelWorkspace(obj.id, obj.name, obj.viewport);
25011
- workspace._core = core;
25012
- return workspace;
25013
- }
25014
- /**
25015
- * Adds an object to this workspace via the core engine.
25016
- * @param obj - The object to add to the workspace
25017
- */
25018
- addObject(obj) {
25019
- this._core.engine.addObject(obj);
25020
- }
25021
- /**
25022
- * Serializes the workspace to a plain object for storage or transmission.
25023
- * Converts dates to ISO strings and includes the class identifier.
25024
- * @returns A plain object representation of the workspace
25025
- */
25026
- serialize() {
25027
- return {
25028
- __class__: this.__class__,
25029
- id: this.id,
25030
- name: this.name,
25031
- createdAt: this.createdAt.toISOString(),
25032
- updatedAt: this.updatedAt.toISOString(),
25033
- viewport: this.viewport,
25034
- };
25035
- }
25036
- /**
25037
- * Deserializes a plain object into this workspace instance.
25038
- * Converts ISO string dates back to Date objects.
25039
- * @param object - The plain object to deserialize from
25040
- * @returns This workspace instance with updated properties
25041
- */
25042
- deserialize(object) {
25043
- Object.assign(this, object);
25044
- this.createdAt = new Date(object.createdAt);
25045
- this.updatedAt = new Date(object.updatedAt);
25046
- return this;
25047
- }
25048
- }
25049
-
25050
- /**
25051
- * Default sync configuration - IndexedDB + BroadcastChannel
25052
- */
25053
- const DEFAULT_SYNC_CONFIG = {
25054
- providers: [
25055
- IndexedDBSyncProvider
25056
- ],
25057
- };
25058
-
25059
- /**
25060
- * Manages the application state for workspaces with Yjs-based synchronization support.
25061
- * This class provides a synchronized map of workspaces that can be shared across
25062
- * multiple clients using configurable sync providers (e.g., WebSocket, WebRTC).
22338
+ * Manages the application state for workspaces with Yjs-based synchronization support.
22339
+ * This class provides a synchronized map of workspaces that can be shared across
22340
+ * multiple clients using configurable sync providers (e.g., WebSocket, WebRTC).
25063
22341
  */
25064
22342
  class KritzelAppStateMap {
25065
22343
  map;
@@ -25354,12 +22632,6 @@ class KritzelAppStateMap {
25354
22632
  }
25355
22633
  }
25356
22634
 
25357
- class KritzelClassHelper {
25358
- static isInstanceOf(object, className) {
25359
- return !!object && object.__class__ === className;
25360
- }
25361
- }
25362
-
25363
22635
  /**
25364
22636
  * Manages anchor connections between line endpoints and other objects.
25365
22637
  * Maintains a runtime index for efficient reverse lookups and handles
@@ -26740,8 +24012,6 @@ exports.DEFAULT_COLOR_PALETTE = DEFAULT_COLOR_PALETTE;
26740
24012
  exports.DEFAULT_LINE_TOOL_CONFIG = DEFAULT_LINE_TOOL_CONFIG;
26741
24013
  exports.DEFAULT_SYNC_CONFIG = DEFAULT_SYNC_CONFIG;
26742
24014
  exports.DEFAULT_TEXT_CONFIG = DEFAULT_TEXT_CONFIG;
26743
- exports.HocuspocusProvider = HocuspocusProvider;
26744
- exports.HocuspocusProviderWebsocket = HocuspocusProviderWebsocket;
26745
24015
  exports.IndexedDBSyncProvider = IndexedDBSyncProvider;
26746
24016
  exports.KritzelAnchorManager = KritzelAnchorManager;
26747
24017
  exports.KritzelAppStateMap = KritzelAppStateMap;
@@ -26774,5 +24044,6 @@ exports.KritzelThemeManager = KritzelThemeManager;
26774
24044
  exports.KritzelToolRegistry = KritzelToolRegistry;
26775
24045
  exports.KritzelWorkspace = KritzelWorkspace;
26776
24046
  exports.ObjectHelper = ObjectHelper;
24047
+ exports.WORKSPACE_EXPORT_VERSION = WORKSPACE_EXPORT_VERSION;
26777
24048
  exports.darkTheme = darkTheme;
26778
24049
  exports.lightTheme = lightTheme;