@automerge/automerge-repo 2.0.5 → 2.0.6

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.
@@ -183,6 +183,17 @@ export declare class DocHandle<T> extends EventEmitter<DocHandleEvents<T>> {
183
183
  * @returns A set of heads representing the concurrent change that was made.
184
184
  */
185
185
  changeAt(heads: UrlHeads, callback: A.ChangeFn<T>, options?: A.ChangeOptions<T>): UrlHeads | undefined;
186
+ /**
187
+ * Check if the document can be change()ed. Currently, documents can be
188
+ * edited unless we are viewing a particular point in time.
189
+ *
190
+ * @remarks It is technically possible to back-date changes using changeAt(),
191
+ * but we block it for usability reasons when viewing a particular point in time.
192
+ * To make changes in the past, use the primary document handle with no heads set.
193
+ *
194
+ * @returns boolean indicating whether changes are possible
195
+ */
196
+ isReadOnly(): boolean;
186
197
  /**
187
198
  * Merges another document into this document. Any peers we are sharing changes with will be
188
199
  * notified of the changes resulting from the merge.
@@ -1 +1 @@
1
- {"version":3,"file":"DocHandle.d.ts","sourceRoot":"","sources":["../src/DocHandle.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"DocHandle.d.ts","sourceRoot":"","sources":["../src/DocHandle.ts"],"names":[],"mappings":"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"}
package/dist/DocHandle.js CHANGED
@@ -150,6 +150,39 @@ export class DocHandle extends EventEmitter {
150
150
  // use a longer delay here so as not to race with other delays
151
151
  { timeout: this.#timeoutDelay * 2 });
152
152
  }
153
+ /**
154
+ * Update the document with whatever the result of callback is
155
+ *
156
+ * This is necessary instead of directly calling
157
+ * `this.#machine.send({ type: UPDATE, payload: { callback } })` because we
158
+ * want to catch any exceptions that the callback might throw, then rethrow
159
+ * them after the state machine has processed the update.
160
+ */
161
+ #sendUpdate(callback) {
162
+ // This is kind of awkward. we have to pass the callback to xstate and wait for it to run it.
163
+ // We're relying here on the fact that xstate runs everything synchronously, so by the time
164
+ // `send` returns we know that the callback will have been run and so `thrownException` will
165
+ // be set if the callback threw an error.
166
+ let thrownException = null;
167
+ this.#machine.send({
168
+ type: UPDATE,
169
+ payload: {
170
+ callback: doc => {
171
+ try {
172
+ return callback(doc);
173
+ }
174
+ catch (e) {
175
+ thrownException = e;
176
+ return doc;
177
+ }
178
+ },
179
+ },
180
+ });
181
+ if (thrownException) {
182
+ // If the callback threw an error, we throw it here so the caller can handle it
183
+ throw thrownException;
184
+ }
185
+ }
153
186
  /**
154
187
  * Called after state transitions. If the document has changed, emits a change event. If we just
155
188
  * received the document for the first time, signal that our request has been completed.
@@ -389,7 +422,7 @@ export class DocHandle extends EventEmitter {
389
422
  * @hidden
390
423
  */
391
424
  update(callback) {
392
- this.#machine.send({ type: UPDATE, payload: { callback } });
425
+ this.#sendUpdate(callback);
393
426
  }
394
427
  /**
395
428
  * `doneLoading` is called by the repo after it decides it has all the changes
@@ -444,10 +477,7 @@ export class DocHandle extends EventEmitter {
444
477
  if (this.#fixedHeads) {
445
478
  throw new Error(`DocHandle#${this.documentId} is in view-only mode at specific heads. Use clone() to create a new document from this state.`);
446
479
  }
447
- this.#machine.send({
448
- type: UPDATE,
449
- payload: { callback: doc => A.change(doc, options, callback) },
450
- });
480
+ this.#sendUpdate(doc => A.change(doc, options, callback));
451
481
  }
452
482
  /**
453
483
  * Makes a change as if the document were at `heads`.
@@ -462,21 +492,27 @@ export class DocHandle extends EventEmitter {
462
492
  throw new Error(`DocHandle#${this.documentId} is in view-only mode at specific heads. Use clone() to create a new document from this state.`);
463
493
  }
464
494
  let resultHeads = undefined;
465
- this.#machine.send({
466
- type: UPDATE,
467
- payload: {
468
- callback: doc => {
469
- const result = A.changeAt(doc, decodeHeads(heads), options, callback);
470
- resultHeads = result.newHeads
471
- ? encodeHeads(result.newHeads)
472
- : undefined;
473
- return result.newDoc;
474
- },
475
- },
495
+ this.#sendUpdate(doc => {
496
+ const result = A.changeAt(doc, decodeHeads(heads), options, callback);
497
+ resultHeads = result.newHeads ? encodeHeads(result.newHeads) : undefined;
498
+ return result.newDoc;
476
499
  });
477
500
  // the callback above will always run before we get here, so this should always contain the new heads
478
501
  return resultHeads;
479
502
  }
503
+ /**
504
+ * Check if the document can be change()ed. Currently, documents can be
505
+ * edited unless we are viewing a particular point in time.
506
+ *
507
+ * @remarks It is technically possible to back-date changes using changeAt(),
508
+ * but we block it for usability reasons when viewing a particular point in time.
509
+ * To make changes in the past, use the primary document handle with no heads set.
510
+ *
511
+ * @returns boolean indicating whether changes are possible
512
+ */
513
+ isReadOnly() {
514
+ return !!this.#fixedHeads;
515
+ }
480
516
  /**
481
517
  * Merges another document into this document. Any peers we are sharing changes with will be
482
518
  * notified of the changes resulting from the merge.
@@ -1 +1 @@
1
- {"version":3,"file":"NetworkSubsystem.d.ts","sourceRoot":"","sources":["../../src/network/NetworkSubsystem.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,YAAY,EAAE,MAAM,eAAe,CAAA;AAC5C,OAAO,EAAE,MAAM,EAAa,MAAM,aAAa,CAAA;AAC/C,OAAO,KAAK,EACV,uBAAuB,EACvB,uBAAuB,EACvB,YAAY,EACb,MAAM,8BAA8B,CAAA;AACrC,OAAO,EAEL,eAAe,EACf,WAAW,EAGZ,MAAM,eAAe,CAAA;AAOtB,qBAAa,gBAAiB,SAAQ,YAAY,CAAC,sBAAsB,CAAC;;IAW/D,MAAM,EAAE,MAAM;IACrB,OAAO,CAAC,YAAY;IALtB,QAAQ,EAAE,uBAAuB,EAAE,CAAK;gBAGtC,QAAQ,EAAE,uBAAuB,EAAE,EAC5B,MAAM,EAAE,MAAM,EACb,YAAY,EAAE,OAAO,CAAC,YAAY,CAAC;IAO7C,UAAU;IAIV,SAAS;IAIT,iBAAiB,CAAC,cAAc,EAAE,uBAAuB;IAqEzD,oBAAoB,CAAC,cAAc,EAAE,uBAAuB;IAK5D,IAAI,CAAC,OAAO,EAAE,eAAe;IAsC7B,OAAO,gBAEN;IAED,SAAS,wBAER;CACF;AAID,MAAM,WAAW,sBAAsB;IACrC,IAAI,EAAE,CAAC,OAAO,EAAE,WAAW,KAAK,IAAI,CAAA;IACpC,mBAAmB,EAAE,CAAC,OAAO,EAAE,uBAAuB,KAAK,IAAI,CAAA;IAC/D,OAAO,EAAE,CAAC,OAAO,EAAE,WAAW,KAAK,IAAI,CAAA;CACxC;AAED,MAAM,WAAW,WAAW;IAC1B,MAAM,EAAE,MAAM,CAAA;IACd,YAAY,EAAE,YAAY,CAAA;CAC3B"}
1
+ {"version":3,"file":"NetworkSubsystem.d.ts","sourceRoot":"","sources":["../../src/network/NetworkSubsystem.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,YAAY,EAAE,MAAM,eAAe,CAAA;AAC5C,OAAO,EAAE,MAAM,EAAa,MAAM,aAAa,CAAA;AAC/C,OAAO,KAAK,EACV,uBAAuB,EACvB,uBAAuB,EACvB,YAAY,EACb,MAAM,8BAA8B,CAAA;AACrC,OAAO,EAEL,eAAe,EACf,WAAW,EAGZ,MAAM,eAAe,CAAA;AAOtB,qBAAa,gBAAiB,SAAQ,YAAY,CAAC,sBAAsB,CAAC;;IAW/D,MAAM,EAAE,MAAM;IACrB,OAAO,CAAC,YAAY;IALtB,QAAQ,EAAE,uBAAuB,EAAE,CAAK;gBAGtC,QAAQ,EAAE,uBAAuB,EAAE,EAC5B,MAAM,EAAE,MAAM,EACb,YAAY,EAAE,OAAO,CAAC,YAAY,CAAC;IAO7C,UAAU;IAIV,SAAS;IAIT,iBAAiB,CAAC,cAAc,EAAE,uBAAuB;IAsEzD,oBAAoB,CAAC,cAAc,EAAE,uBAAuB;IAK5D,IAAI,CAAC,OAAO,EAAE,eAAe;IAsC7B,OAAO,gBAEN;IAED,SAAS,wBAER;CACF;AAID,MAAM,WAAW,sBAAsB;IACrC,IAAI,EAAE,CAAC,OAAO,EAAE,WAAW,KAAK,IAAI,CAAA;IACpC,mBAAmB,EAAE,CAAC,OAAO,EAAE,uBAAuB,KAAK,IAAI,CAAA;IAC/D,OAAO,EAAE,CAAC,OAAO,EAAE,WAAW,KAAK,IAAI,CAAA;CACxC;AAED,MAAM,WAAW,WAAW;IAC1B,MAAM,EAAE,MAAM,CAAA;IACd,YAAY,EAAE,YAAY,CAAA;CAC3B"}
@@ -68,6 +68,7 @@ export class NetworkSubsystem extends EventEmitter {
68
68
  delete this.#adaptersByPeer[peerId];
69
69
  }
70
70
  });
71
+ this.adapters = this.adapters.filter(a => a !== networkAdapter);
71
72
  });
72
73
  this.peerMetadata
73
74
  .then(peerMetadata => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@automerge/automerge-repo",
3
- "version": "2.0.5",
3
+ "version": "2.0.6",
4
4
  "description": "A repository object to manage a collection of automerge documents",
5
5
  "repository": "https://github.com/automerge/automerge-repo/tree/master/packages/automerge-repo",
6
6
  "author": "Peter van Hardenberg <pvh@pvh.ca>",
@@ -59,5 +59,5 @@
59
59
  "publishConfig": {
60
60
  "access": "public"
61
61
  },
62
- "gitHead": "9d24ea3db7c1f459fc66ce43b794a9ed4bef1341"
62
+ "gitHead": "d90f3e51d9ed357fd6f3098ac11190996ae0c05a"
63
63
  }
package/src/DocHandle.ts CHANGED
@@ -183,6 +183,39 @@ export class DocHandle<T> extends EventEmitter<DocHandleEvents<T>> {
183
183
  )
184
184
  }
185
185
 
186
+ /**
187
+ * Update the document with whatever the result of callback is
188
+ *
189
+ * This is necessary instead of directly calling
190
+ * `this.#machine.send({ type: UPDATE, payload: { callback } })` because we
191
+ * want to catch any exceptions that the callback might throw, then rethrow
192
+ * them after the state machine has processed the update.
193
+ */
194
+ #sendUpdate(callback: (doc: A.Doc<T>) => A.Doc<T>) {
195
+ // This is kind of awkward. we have to pass the callback to xstate and wait for it to run it.
196
+ // We're relying here on the fact that xstate runs everything synchronously, so by the time
197
+ // `send` returns we know that the callback will have been run and so `thrownException` will
198
+ // be set if the callback threw an error.
199
+ let thrownException: null | Error = null
200
+ this.#machine.send({
201
+ type: UPDATE,
202
+ payload: {
203
+ callback: doc => {
204
+ try {
205
+ return callback(doc)
206
+ } catch (e) {
207
+ thrownException = e as Error
208
+ return doc
209
+ }
210
+ },
211
+ },
212
+ })
213
+ if (thrownException) {
214
+ // If the callback threw an error, we throw it here so the caller can handle it
215
+ throw thrownException
216
+ }
217
+ }
218
+
186
219
  /**
187
220
  * Called after state transitions. If the document has changed, emits a change event. If we just
188
221
  * received the document for the first time, signal that our request has been completed.
@@ -466,7 +499,7 @@ export class DocHandle<T> extends EventEmitter<DocHandleEvents<T>> {
466
499
  * @hidden
467
500
  */
468
501
  update(callback: (doc: A.Doc<T>) => A.Doc<T>) {
469
- this.#machine.send({ type: UPDATE, payload: { callback } })
502
+ this.#sendUpdate(callback)
470
503
  }
471
504
 
472
505
  /**
@@ -532,10 +565,7 @@ export class DocHandle<T> extends EventEmitter<DocHandleEvents<T>> {
532
565
  )
533
566
  }
534
567
 
535
- this.#machine.send({
536
- type: UPDATE,
537
- payload: { callback: doc => A.change(doc, options, callback) },
538
- })
568
+ this.#sendUpdate(doc => A.change(doc, options, callback))
539
569
  }
540
570
  /**
541
571
  * Makes a change as if the document were at `heads`.
@@ -557,24 +587,32 @@ export class DocHandle<T> extends EventEmitter<DocHandleEvents<T>> {
557
587
  `DocHandle#${this.documentId} is in view-only mode at specific heads. Use clone() to create a new document from this state.`
558
588
  )
559
589
  }
590
+
560
591
  let resultHeads: UrlHeads | undefined = undefined
561
- this.#machine.send({
562
- type: UPDATE,
563
- payload: {
564
- callback: doc => {
565
- const result = A.changeAt(doc, decodeHeads(heads), options, callback)
566
- resultHeads = result.newHeads
567
- ? encodeHeads(result.newHeads)
568
- : undefined
569
- return result.newDoc
570
- },
571
- },
592
+ this.#sendUpdate(doc => {
593
+ const result = A.changeAt(doc, decodeHeads(heads), options, callback)
594
+ resultHeads = result.newHeads ? encodeHeads(result.newHeads) : undefined
595
+ return result.newDoc
572
596
  })
573
597
 
574
598
  // the callback above will always run before we get here, so this should always contain the new heads
575
599
  return resultHeads
576
600
  }
577
601
 
602
+ /**
603
+ * Check if the document can be change()ed. Currently, documents can be
604
+ * edited unless we are viewing a particular point in time.
605
+ *
606
+ * @remarks It is technically possible to back-date changes using changeAt(),
607
+ * but we block it for usability reasons when viewing a particular point in time.
608
+ * To make changes in the past, use the primary document handle with no heads set.
609
+ *
610
+ * @returns boolean indicating whether changes are possible
611
+ */
612
+ isReadOnly() {
613
+ return !!this.#fixedHeads
614
+ }
615
+
578
616
  /**
579
617
  * Merges another document into this document. Any peers we are sharing changes with will be
580
618
  * notified of the changes resulting from the merge.
@@ -102,6 +102,7 @@ export class NetworkSubsystem extends EventEmitter<NetworkSubsystemEvents> {
102
102
  delete this.#adaptersByPeer[peerId as PeerId]
103
103
  }
104
104
  })
105
+ this.adapters = this.adapters.filter(a => a !== networkAdapter)
105
106
  })
106
107
 
107
108
  this.peerMetadata
@@ -587,4 +587,107 @@ describe("DocHandle", () => {
587
587
  // Cached access should be significantly faster
588
588
  expect(timeForCachedAccesses).toBeLessThan(timeForFirstAccess / 10)
589
589
  })
590
+
591
+ describe("isReadOnly", () => {
592
+ it("should return false for a regular document handle", () => {
593
+ const handle = setup()
594
+ expect(handle.isReadOnly()).toBe(false)
595
+ })
596
+
597
+ it("should return false for a newly created document handle", () => {
598
+ const handle = new DocHandle<TestDoc>(TEST_ID)
599
+ expect(handle.isReadOnly()).toBe(false)
600
+ })
601
+
602
+ it("should return true for a view handle with fixed heads", () => {
603
+ const handle = setup()
604
+ handle.change(doc => {
605
+ doc.foo = "test"
606
+ })
607
+
608
+ const heads = handle.heads()
609
+ const viewHandle = handle.view(heads)
610
+
611
+ expect(viewHandle.isReadOnly()).toBe(true)
612
+ })
613
+
614
+ it("should return true for a handle constructed with fixed heads", () => {
615
+ const handle = setup()
616
+ handle.change(doc => {
617
+ doc.foo = "test"
618
+ })
619
+
620
+ const heads = handle.heads()
621
+ const fixedHeadsHandle = new DocHandle<TestDoc>(TEST_ID, { heads })
622
+ fixedHeadsHandle.update(() => A.clone(handle.doc()!))
623
+ fixedHeadsHandle.doneLoading()
624
+
625
+ expect(fixedHeadsHandle.isReadOnly()).toBe(true)
626
+ })
627
+
628
+ it("should return false after regular changes", () => {
629
+ const handle = setup()
630
+
631
+ // Initially not read-only
632
+ expect(handle.isReadOnly()).toBe(false)
633
+
634
+ // Make a change
635
+ handle.change(doc => {
636
+ doc.foo = "changed"
637
+ })
638
+
639
+ // Still not read-only
640
+ expect(handle.isReadOnly()).toBe(false)
641
+ })
642
+ })
643
+
644
+ it("should continue to function after recovering from an exception in change", () => {
645
+ const handle = setup()
646
+
647
+ // throw an error in the change handler, but catch it
648
+ let expectedErr = new Error("Argh!")
649
+ let err: Error | null = null
650
+ try {
651
+ handle.change(doc => {
652
+ doc.foo = "bar"
653
+ throw expectedErr
654
+ })
655
+ } catch (e) {
656
+ err = e
657
+ }
658
+ assert.equal(err, expectedErr, "should have thrown the error")
659
+
660
+ // Future changes should still work
661
+ handle.change(doc => {
662
+ doc.foo = "baz"
663
+ })
664
+ assert.equal(handle.doc()?.foo, "baz", "should have changed foo to baz")
665
+ })
666
+
667
+ it("should continue to function after recovering from an exception in changeAt", () => {
668
+ const handle = setup()
669
+ handle.change(d => (d.foo = "bar"))
670
+
671
+ const heads = handle.heads()!
672
+ handle.change(d => (d.foo = "qux"))
673
+
674
+ // throw an error in the change handler, but catch it
675
+ let expectedErr = new Error("Argh!")
676
+ let err: Error | null = null
677
+ try {
678
+ handle.changeAt(heads, doc => {
679
+ doc.foo = "bar"
680
+ throw expectedErr
681
+ })
682
+ } catch (e) {
683
+ err = e
684
+ }
685
+ assert.equal(err, expectedErr, "should have thrown the error")
686
+
687
+ // Future changes should still work
688
+ const newHeads = handle.changeAt(heads, doc => {
689
+ doc.foo = "baz"
690
+ })
691
+ assert.equal(handle.view(newHeads).doc().foo, "baz")
692
+ })
590
693
  })