@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.
- package/dist/DocHandle.d.ts +11 -0
- package/dist/DocHandle.d.ts.map +1 -1
- package/dist/DocHandle.js +52 -16
- package/dist/network/NetworkSubsystem.d.ts.map +1 -1
- package/dist/network/NetworkSubsystem.js +1 -0
- package/package.json +2 -2
- package/src/DocHandle.ts +54 -16
- package/src/network/NetworkSubsystem.ts +1 -0
- package/test/DocHandle.test.ts +103 -0
package/dist/DocHandle.d.ts
CHANGED
|
@@ -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.
|
package/dist/DocHandle.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"DocHandle.d.ts","sourceRoot":"","sources":["../src/DocHandle.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,IAAI,CAAC,EAAE,MAAM,2BAA2B,CAAA;AAErD,OAAO,EAAE,YAAY,EAAE,MAAM,eAAe,CAAA;AAU5C,OAAO,KAAK,EAAE,YAAY,EAAE,UAAU,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAA;AAC5E,OAAO,EAAE,SAAS,EAAE,MAAM,oBAAoB,CAAA;AAE9C;;;;;;;;;;;;GAYG;AACH,qBAAa,SAAS,CAAC,CAAC,CAAE,SAAQ,YAAY,CAAC,eAAe,CAAC,CAAC,CAAC,CAAC;;IAwBvD,UAAU,EAAE,UAAU;IAF/B,cAAc;gBAEL,UAAU,EAAE,UAAU,EAC7B,OAAO,GAAE,gBAAgB,CAAC,CAAC,CAAM;
|
|
1
|
+
{"version":3,"file":"DocHandle.d.ts","sourceRoot":"","sources":["../src/DocHandle.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,IAAI,CAAC,EAAE,MAAM,2BAA2B,CAAA;AAErD,OAAO,EAAE,YAAY,EAAE,MAAM,eAAe,CAAA;AAU5C,OAAO,KAAK,EAAE,YAAY,EAAE,UAAU,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAA;AAC5E,OAAO,EAAE,SAAS,EAAE,MAAM,oBAAoB,CAAA;AAE9C;;;;;;;;;;;;GAYG;AACH,qBAAa,SAAS,CAAC,CAAC,CAAE,SAAQ,YAAY,CAAC,eAAe,CAAC,CAAC,CAAC,CAAC;;IAwBvD,UAAU,EAAE,UAAU;IAF/B,cAAc;gBAEL,UAAU,EAAE,UAAU,EAC7B,OAAO,GAAE,gBAAgB,CAAC,CAAC,CAAM;IAsMnC;OACG;IACH,IAAI,GAAG,IAAI,YAAY,CAKtB;IAED;;;;;OAKG;IACH,OAAO,gBAAgC;IAEvC;;;;;OAKG;IACH,UAAU,gBAAmC;IAE7C;;;;;OAKG;IACH,SAAS,gBAAkC;IAE3C;;;;OAIG;IACH,aAAa,gBAAsC;IAEnD;;OAEG;IACH,OAAO,GAAI,QAAQ,WAAW,EAAE,aAC0B;IAE1D,cAAc;IACd,IAAI,KAAK,yFAER;IAED;;;;;;OAMG;IACG,SAAS,CAAC,WAAW,GAAE,WAAW,EAAc;IAItD;;;;;;OAMG;IACH,GAAG;IAQH;;qBAEiB;IACjB,OAAO;IAOP;;;;OAIG;IACH,KAAK,IAAI,QAAQ;IAQjB,KAAK;IAIL;;;;;;;;;;;OAWG;IACH,OAAO,IAAI,QAAQ,EAAE,GAAG,SAAS;IAWjC;;;;;;;;;;;;OAYG;IACH,IAAI,CAAC,KAAK,EAAE,QAAQ,GAAG,SAAS,CAAC,CAAC,CAAC;IA8BnC;;;;;;;;;;;;OAYG;IACH,IAAI,CAAC,KAAK,EAAE,QAAQ,GAAG,SAAS,CAAC,CAAC,CAAC,EAAE,MAAM,CAAC,EAAE,QAAQ,GAAG,CAAC,CAAC,KAAK,EAAE;IAkClE;;;;;;;;;;OAUG;IACH,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,GAAG,CAAC,CAAC,aAAa,GAAG,SAAS;IAetD;;;;;OAKG;IACH,MAAM,CAAC,QAAQ,EAAE,CAAC,GAAG,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC;IAI5C;;;;OAIG;IACH,WAAW;IAIX;;;OAGG;IACH,WAAW,CAAC,SAAS,EAAE,SAAS,EAAE,QAAQ,EAAE,QAAQ;IASpD;;;OAGG;IACH,cAAc,CAAC,SAAS,EAAE,SAAS,GAAG,QAAQ,GAAG,SAAS;IAI1D,gFAAgF;IAChF,WAAW,CAAC,SAAS,EAAE,SAAS,GAAG,QAAQ,GAAG,SAAS;IAIvD;;;;;;;;;;;;;;OAcG;IACH,MAAM,CAAC,QAAQ,EAAE,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,EAAE,OAAO,GAAE,CAAC,CAAC,aAAa,CAAC,CAAC,CAAM;IAehE;;;;OAIG;IACH,QAAQ,CACN,KAAK,EAAE,QAAQ,EACf,QAAQ,EAAE,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,EACvB,OAAO,GAAE,CAAC,CAAC,aAAa,CAAC,CAAC,CAAM,GAC/B,QAAQ,GAAG,SAAS;IAuBvB;;;;;;;;;OASG;IACH,UAAU;IAIV;;;;;;;OAOG;IACH,KAAK;IACH,wDAAwD;IACxD,WAAW,EAAE,SAAS,CAAC,CAAC,CAAC;IAiB3B;;;OAGG;IACH,WAAW;IAIX;;;SAGK;IACL,OAAO;IAIP,8DAA8D;IAC9D,MAAM;IAIN,sDAAsD;IACtD,MAAM;IAIN,uDAAuD;IACvD,MAAM;IAIN;;;;;;OAMG;IACH,SAAS,CAAC,OAAO,EAAE,OAAO;IAO1B,OAAO,IAAI;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,UAAU,EAAE,MAAM,CAAA;KAAE;CAGlD;AAID,MAAM,MAAM,QAAQ,GAAG;IACrB,SAAS,EAAE,QAAQ,CAAA;IACnB,iBAAiB,EAAE,MAAM,CAAA;CAC1B,CAAA;AAED,cAAc;AACd,MAAM,MAAM,gBAAgB,CAAC,CAAC,IAE1B;IACE,gGAAgG;IAChG,KAAK,EAAE,IAAI,CAAA;IAEX,yCAAyC;IACzC,YAAY,CAAC,EAAE,CAAC,CAAA;CACjB,GAED;IACE,KAAK,CAAC,EAAE,KAAK,CAAA;IAGb,KAAK,CAAC,EAAE,QAAQ,CAAA;IAEhB,+HAA+H;IAC/H,YAAY,CAAC,EAAE,MAAM,CAAA;CACtB,CAAA;AAIL,2EAA2E;AAC3E,MAAM,WAAW,eAAe,CAAC,CAAC;IAChC,eAAe,EAAE,CAAC,OAAO,EAAE,6BAA6B,CAAC,CAAC,CAAC,KAAK,IAAI,CAAA;IACpE,MAAM,EAAE,CAAC,OAAO,EAAE,sBAAsB,CAAC,CAAC,CAAC,KAAK,IAAI,CAAA;IACpD,MAAM,EAAE,CAAC,OAAO,EAAE,sBAAsB,CAAC,CAAC,CAAC,KAAK,IAAI,CAAA;IACpD,mBAAmB,EAAE,CAAC,OAAO,EAAE,gCAAgC,CAAC,CAAC,CAAC,KAAK,IAAI,CAAA;IAC3E,4BAA4B,EAAE,CAC5B,OAAO,EAAE,wCAAwC,CAAC,CAAC,CAAC,KACjD,IAAI,CAAA;IACT,cAAc,EAAE,CAAC,OAAO,EAAE,2BAA2B,KAAK,IAAI,CAAA;CAC/D;AAED,sDAAsD;AACtD,MAAM,WAAW,6BAA6B,CAAC,CAAC;IAC9C,MAAM,EAAE,SAAS,CAAC,CAAC,CAAC,CAAA;IACpB,GAAG,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAA;CACd;AAED,6CAA6C;AAC7C,MAAM,WAAW,sBAAsB,CAAC,CAAC;IACvC,8BAA8B;IAC9B,MAAM,EAAE,SAAS,CAAC,CAAC,CAAC,CAAA;IACpB,iDAAiD;IACjD,GAAG,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAA;IACb,wDAAwD;IACxD,OAAO,EAAE,CAAC,CAAC,KAAK,EAAE,CAAA;IAClB,mCAAmC;IACnC,SAAS,EAAE,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,CAAA;CAC1B;AAED,4CAA4C;AAC5C,MAAM,WAAW,sBAAsB,CAAC,CAAC;IACvC,MAAM,EAAE,SAAS,CAAC,CAAC,CAAC,CAAA;CACrB;AAED,6DAA6D;AAC7D,MAAM,WAAW,2BAA2B,CAAC,CAAC;IAC5C,MAAM,EAAE,SAAS,CAAC,CAAC,CAAC,CAAA;CACrB;AAED,qEAAqE;AACrE,MAAM,WAAW,gCAAgC,CAAC,CAAC;IACjD,MAAM,EAAE,SAAS,CAAC,CAAC,CAAC,CAAA;IACpB,QAAQ,EAAE,MAAM,CAAA;IAChB,OAAO,EAAE,OAAO,CAAA;CACjB;AAED,kEAAkE;AAClE,MAAM,WAAW,wCAAwC,CAAC,CAAC;IACzD,MAAM,EAAE,SAAS,CAAC,CAAC,CAAC,CAAA;IACpB,IAAI,EAAE,UAAU,CAAA;CACjB;AAED,8DAA8D;AAC9D,MAAM,WAAW,2BAA2B;IAC1C,SAAS,EAAE,SAAS,CAAA;IACpB,KAAK,EAAE,QAAQ,CAAA;IACf,SAAS,EAAE,MAAM,CAAA;CAClB;AAMD;;GAEG;AACH,eAAO,MAAM,WAAW;IACtB,kEAAkE;;IAElE,mDAAmD;;IAEnD,6EAA6E;;IAE7E,gCAAgC;;IAEhC,2EAA2E;;IAE3E,kDAAkD;;IAElD,4EAA4E;;CAEpE,CAAA;AACV,MAAM,MAAM,WAAW,GAAG,CAAC,OAAO,WAAW,CAAC,CAAC,MAAM,OAAO,WAAW,CAAC,CAAA;AAExE,eAAO,MACL,IAAI,UACJ,OAAO,aACP,UAAU,gBACV,KAAK,WACL,QAAQ,cACR,OAAO,aACP,WAAW,eACE,CAAA"}
|
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.#
|
|
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.#
|
|
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.#
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
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;
|
|
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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@automerge/automerge-repo",
|
|
3
|
-
"version": "2.0.
|
|
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": "
|
|
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.#
|
|
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.#
|
|
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.#
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
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.
|
package/test/DocHandle.test.ts
CHANGED
|
@@ -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
|
})
|