prosemirror-changeset 2.3.1 → 2.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,3 +1,9 @@
1
+ ## 2.4.0 (2026-02-14)
2
+
3
+ ### New features
4
+
5
+ `Change` objects can now be serialized to and deserialized from JSON, and `ChangeSet.create` allows you to pass in a set of changes.
6
+
1
7
  ## 2.3.1 (2025-05-28)
2
8
 
3
9
  ### Bug fixes
package/README.md CHANGED
@@ -43,11 +43,17 @@ A replaced range with metadata associated with it.
43
43
  Data associated with the inserted content. Length adds up to
44
44
  `this.toB - this.fromB`.
45
45
 
46
+ * **`toJSON`**`() → ChangeJSON`\
47
+ Returns a JSON-serializeable object to represent this change.
48
+
46
49
  * `static `**`merge`**`<Data>(x: readonly Change[], y: readonly Change[], combine: fn(dataA: Data, dataB: Data) → Data) → readonly Change[]`\
47
50
  This merges two changesets (the end document of x should be the
48
51
  start document of y) into a single one spanning the start of x to
49
52
  the end of y.
50
53
 
54
+ * `static `**`fromJSON`**`<Data>(json: ChangeJSON) → Change`\
55
+ Deserialize a change from JSON format.
56
+
51
57
 
52
58
  ### class Span`<Data = any>`
53
59
 
@@ -96,7 +102,7 @@ partially undo themselves by comparing their content.
96
102
  make sure the method is called on the old set and passed the new
97
103
  set. The returned positions will be in new document coordinates.
98
104
 
99
- * `static `**`create`**`<Data = any>(doc: Node, combine?: fn(dataA: Data, dataB: Data) → Data = (a, b) => a === b ? a : null as any, tokenEncoder?: TokenEncoder = DefaultEncoder) → ChangeSet`\
105
+ * `static `**`create`**`<Data = any>(doc: Node, combine?: fn(dataA: Data, dataB: Data) → Data = (a, b) => a === b ? a : null as any, tokenEncoder?: TokenEncoder = DefaultEncoder, changes?: readonly Change[] = []) → ChangeSet`\
100
106
  Create a changeset with the given base object and configuration.
101
107
 
102
108
  The `combine` function is used to compare and combine metadata—it
@@ -108,6 +114,11 @@ partially undo themselves by comparing their content.
108
114
  changes. The default is to just compare nodes by name and text
109
115
  by character, ignoring marks and attributes.
110
116
 
117
+ To serialize a change set, you can store its document and
118
+ change array as JSON, and then pass the deserialized (via
119
+ [`Change.fromJSON`](#changes.Change^fromJSON)) set of changes
120
+ as fourth argument to `create` to recreate the set.
121
+
111
122
 
112
123
  * **`simplifyChanges`**`(changes: readonly Change[], doc: Node) → Change[]`\
113
124
  Simplifies a set of changes for presentation. This makes the
@@ -144,3 +155,21 @@ performance.
144
155
  Compare the given tokens. Should return true when they count as
145
156
  equal.
146
157
 
158
+
159
+ ### type ChangeJSON`<Data>`
160
+
161
+ JSON-serialized form of a change.
162
+
163
+ * **`fromA`**`: number`
164
+
165
+ * **`toA`**`: number`
166
+
167
+ * **`fromB`**`: number`
168
+
169
+ * **`toB`**`: number`
170
+
171
+ * **`deleted`**`: readonly {length: number, data: Data}[]`
172
+
173
+ * **`inserted`**`: readonly {length: number, data: Data}[]`
174
+
175
+
package/dist/index.cjs CHANGED
@@ -210,6 +210,11 @@ var Change = function () {
210
210
  if (startA == 0 && startB == 0 && endA == this.toA - this.fromA && endB == this.toB - this.fromB) return this;
211
211
  return new Change(this.fromA + startA, this.fromA + endA, this.fromB + startB, this.fromB + endB, Span.slice(this.deleted, startA, endA), Span.slice(this.inserted, startB, endB));
212
212
  }
213
+ }, {
214
+ key: "toJSON",
215
+ value: function toJSON() {
216
+ return this;
217
+ }
213
218
  }], [{
214
219
  key: "merge",
215
220
  value: function merge(x, y, combine) {
@@ -276,6 +281,15 @@ var Change = function () {
276
281
  }
277
282
  }
278
283
  }
284
+ }, {
285
+ key: "fromJSON",
286
+ value: function fromJSON(json) {
287
+ return new Change(json.fromA, json.toA, json.fromB, json.toB, json.deleted.map(function (d) {
288
+ return new Span(d.length, d.data);
289
+ }), json.inserted.map(function (d) {
290
+ return new Span(d.length, d.data);
291
+ }));
292
+ }
279
293
  }]);
280
294
  return Change;
281
295
  }();
@@ -507,11 +521,12 @@ var ChangeSet = function () {
507
521
  return a === b ? a : null;
508
522
  };
509
523
  var tokenEncoder = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : DefaultEncoder;
524
+ var changes = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : [];
510
525
  return new ChangeSet({
511
526
  combine: combine,
512
527
  doc: doc,
513
528
  encoder: tokenEncoder
514
- }, []);
529
+ }, changes);
515
530
  }
516
531
  }]);
517
532
  return ChangeSet;
package/dist/index.d.cts CHANGED
@@ -50,7 +50,32 @@ declare class Change<Data = any> {
50
50
  the end of y.
51
51
  */
52
52
  static merge<Data>(x: readonly Change<Data>[], y: readonly Change<Data>[], combine: (dataA: Data, dataB: Data) => Data): readonly Change<Data>[];
53
+ /**
54
+ Deserialize a change from JSON format.
55
+ */
56
+ static fromJSON<Data>(json: ChangeJSON<Data>): Change<Data>;
57
+ /**
58
+ Returns a JSON-serializeable object to represent this change.
59
+ */
60
+ toJSON(): ChangeJSON<Data>;
53
61
  }
62
+ /**
63
+ JSON-serialized form of a change.
64
+ */
65
+ type ChangeJSON<Data> = {
66
+ fromA: number;
67
+ toA: number;
68
+ fromB: number;
69
+ toB: number;
70
+ deleted: readonly {
71
+ length: number;
72
+ data: Data;
73
+ }[];
74
+ inserted: readonly {
75
+ length: number;
76
+ data: Data;
77
+ }[];
78
+ };
54
79
 
55
80
  /**
56
81
  A token encoder can be passed when creating a `ChangeSet` in order
@@ -149,8 +174,13 @@ declare class ChangeSet<Data = any> {
149
174
  serialized and compared when diffing the content produced by
150
175
  changes. The default is to just compare nodes by name and text
151
176
  by character, ignoring marks and attributes.
177
+
178
+ To serialize a change set, you can store its document and
179
+ change array as JSON, and then pass the deserialized (via
180
+ [`Change.fromJSON`](https://prosemirror.net/docs/ref/#changes.Change^fromJSON)) set of changes
181
+ as fourth argument to `create` to recreate the set.
152
182
  */
153
- static create<Data = any>(doc: Node, combine?: (dataA: Data, dataB: Data) => Data, tokenEncoder?: TokenEncoder<any>): ChangeSet<Data>;
183
+ static create<Data = any>(doc: Node, combine?: (dataA: Data, dataB: Data) => Data, tokenEncoder?: TokenEncoder<any>, changes?: readonly Change<Data>[]): ChangeSet<Data>;
154
184
  }
155
185
 
156
- export { Change, ChangeSet, Span, type TokenEncoder, simplifyChanges };
186
+ export { Change, type ChangeJSON, ChangeSet, Span, type TokenEncoder, simplifyChanges };
package/dist/index.d.ts CHANGED
@@ -50,7 +50,32 @@ declare class Change<Data = any> {
50
50
  the end of y.
51
51
  */
52
52
  static merge<Data>(x: readonly Change<Data>[], y: readonly Change<Data>[], combine: (dataA: Data, dataB: Data) => Data): readonly Change<Data>[];
53
+ /**
54
+ Deserialize a change from JSON format.
55
+ */
56
+ static fromJSON<Data>(json: ChangeJSON<Data>): Change<Data>;
57
+ /**
58
+ Returns a JSON-serializeable object to represent this change.
59
+ */
60
+ toJSON(): ChangeJSON<Data>;
53
61
  }
62
+ /**
63
+ JSON-serialized form of a change.
64
+ */
65
+ type ChangeJSON<Data> = {
66
+ fromA: number;
67
+ toA: number;
68
+ fromB: number;
69
+ toB: number;
70
+ deleted: readonly {
71
+ length: number;
72
+ data: Data;
73
+ }[];
74
+ inserted: readonly {
75
+ length: number;
76
+ data: Data;
77
+ }[];
78
+ };
54
79
 
55
80
  /**
56
81
  A token encoder can be passed when creating a `ChangeSet` in order
@@ -149,8 +174,13 @@ declare class ChangeSet<Data = any> {
149
174
  serialized and compared when diffing the content produced by
150
175
  changes. The default is to just compare nodes by name and text
151
176
  by character, ignoring marks and attributes.
177
+
178
+ To serialize a change set, you can store its document and
179
+ change array as JSON, and then pass the deserialized (via
180
+ [`Change.fromJSON`](https://prosemirror.net/docs/ref/#changes.Change^fromJSON)) set of changes
181
+ as fourth argument to `create` to recreate the set.
152
182
  */
153
- static create<Data = any>(doc: Node, combine?: (dataA: Data, dataB: Data) => Data, tokenEncoder?: TokenEncoder<any>): ChangeSet<Data>;
183
+ static create<Data = any>(doc: Node, combine?: (dataA: Data, dataB: Data) => Data, tokenEncoder?: TokenEncoder<any>, changes?: readonly Change<Data>[]): ChangeSet<Data>;
154
184
  }
155
185
 
156
- export { Change, ChangeSet, Span, type TokenEncoder, simplifyChanges };
186
+ export { Change, type ChangeJSON, ChangeSet, Span, type TokenEncoder, simplifyChanges };
package/dist/index.js CHANGED
@@ -350,6 +350,16 @@ class Change {
350
350
  }
351
351
  }
352
352
  }
353
+ /**
354
+ Deserialize a change from JSON format.
355
+ */
356
+ static fromJSON(json) {
357
+ return new Change(json.fromA, json.toA, json.fromB, json.toB, json.deleted.map(d => new Span(d.length, d.data)), json.inserted.map(d => new Span(d.length, d.data)));
358
+ }
359
+ /**
360
+ Returns a JSON-serializeable object to represent this change.
361
+ */
362
+ toJSON() { return this; }
353
363
  }
354
364
 
355
365
  let letter;
@@ -646,9 +656,14 @@ class ChangeSet {
646
656
  serialized and compared when diffing the content produced by
647
657
  changes. The default is to just compare nodes by name and text
648
658
  by character, ignoring marks and attributes.
659
+
660
+ To serialize a change set, you can store its document and
661
+ change array as JSON, and then pass the deserialized (via
662
+ [`Change.fromJSON`](https://prosemirror.net/docs/ref/#changes.Change^fromJSON)) set of changes
663
+ as fourth argument to `create` to recreate the set.
649
664
  */
650
- static create(doc, combine = (a, b) => a === b ? a : null, tokenEncoder = DefaultEncoder) {
651
- return new ChangeSet({ combine, doc, encoder: tokenEncoder }, []);
665
+ static create(doc, combine = (a, b) => a === b ? a : null, tokenEncoder = DefaultEncoder, changes = []) {
666
+ return new ChangeSet({ combine, doc, encoder: tokenEncoder }, changes);
652
667
  }
653
668
  }
654
669
  /**
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "prosemirror-changeset",
3
- "version": "2.3.1",
3
+ "version": "2.4.0",
4
4
  "description": "Distills a series of editing steps into deleted and added ranges",
5
5
  "type": "module",
6
6
  "main": "dist/index.cjs",
package/src/README.md CHANGED
@@ -27,4 +27,6 @@ it was made, or the step data necessary to invert it again.
27
27
 
28
28
  @simplifyChanges
29
29
 
30
- @TokenEncoder
30
+ @TokenEncoder
31
+
32
+ @ChangeJSON
package/src/change.ts CHANGED
@@ -168,4 +168,22 @@ export class Change<Data = any> {
168
168
  }
169
169
  }
170
170
  }
171
+
172
+ /// Deserialize a change from JSON format.
173
+ static fromJSON<Data>(json: ChangeJSON<Data>) {
174
+ return new Change(json.fromA, json.toA, json.fromB, json.toB,
175
+ json.deleted.map(d => new Span(d.length, d.data)),
176
+ json.inserted.map(d => new Span(d.length, d.data)))
177
+ }
178
+
179
+ /// Returns a JSON-serializeable object to represent this change.
180
+ toJSON(): ChangeJSON<Data> { return this }
181
+ }
182
+
183
+ /// JSON-serialized form of a change.
184
+ export type ChangeJSON<Data> = {
185
+ fromA: number, toA: number,
186
+ fromB: number, toB: number,
187
+ deleted: readonly {length: number, data: Data}[],
188
+ inserted: readonly {length: number, data: Data}[]
171
189
  }
package/src/changeset.ts CHANGED
@@ -1,8 +1,8 @@
1
1
  import {Node} from "prosemirror-model"
2
2
  import {StepMap} from "prosemirror-transform"
3
3
  import {computeDiff, TokenEncoder, DefaultEncoder} from "./diff"
4
- import {Change, Span} from "./change"
5
- export {Change, Span}
4
+ import {Change, Span, ChangeJSON} from "./change"
5
+ export {Change, Span, ChangeJSON}
6
6
  export {simplifyChanges} from "./simplify"
7
7
  export {TokenEncoder}
8
8
 
@@ -146,12 +146,18 @@ export class ChangeSet<Data = any> {
146
146
  /// serialized and compared when diffing the content produced by
147
147
  /// changes. The default is to just compare nodes by name and text
148
148
  /// by character, ignoring marks and attributes.
149
+ ///
150
+ /// To serialize a change set, you can store its document and
151
+ /// change array as JSON, and then pass the deserialized (via
152
+ /// [`Change.fromJSON`](#changes.Change^fromJSON)) set of changes
153
+ /// as fourth argument to `create` to recreate the set.
149
154
  static create<Data = any>(
150
155
  doc: Node,
151
156
  combine: (dataA: Data, dataB: Data) => Data = (a, b) => a === b ? a : null as any,
152
- tokenEncoder: TokenEncoder<any> = DefaultEncoder
157
+ tokenEncoder: TokenEncoder<any> = DefaultEncoder,
158
+ changes: readonly Change<Data>[] = []
153
159
  ) {
154
- return new ChangeSet({combine, doc, encoder: tokenEncoder}, [])
160
+ return new ChangeSet({combine, doc, encoder: tokenEncoder}, changes)
155
161
  }
156
162
 
157
163
  /// Exported for testing @internal