@automerge/automerge-repo 2.4.0-alpha.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/dist/Repo.d.ts +13 -1
- package/dist/Repo.d.ts.map +1 -1
- package/dist/Repo.js +44 -4
- package/dist/helpers/abortable.d.ts +15 -0
- package/dist/helpers/abortable.d.ts.map +1 -1
- package/dist/helpers/abortable.js +18 -1
- package/package.json +2 -2
- package/src/Repo.ts +50 -7
- package/src/helpers/abortable.ts +19 -2
- package/test/Repo.test.ts +12 -5
package/dist/Repo.d.ts
CHANGED
|
@@ -61,6 +61,18 @@ export declare class Repo extends EventEmitter<RepoEvents> {
|
|
|
61
61
|
* system. we emit a `document` event to advertise interest in the document.
|
|
62
62
|
*/
|
|
63
63
|
create<T>(initialValue?: T): DocHandle<T>;
|
|
64
|
+
/**
|
|
65
|
+
* Creates a new document and returns a handle to it. The initial value of the
|
|
66
|
+
* document is an empty object `{}` unless an initial value is provided. The
|
|
67
|
+
* main difference between this and Repo.create is that if an `idGenerator`
|
|
68
|
+
* was provided at repo construction, that idGenerator will be used to
|
|
69
|
+
* generate the document ID of the document returned by this method.
|
|
70
|
+
*
|
|
71
|
+
* This is a hidden, experimental API which is subject to change or removal without notice.
|
|
72
|
+
* @hidden
|
|
73
|
+
* @experimental
|
|
74
|
+
*/
|
|
75
|
+
create2<T>(initialValue?: T): Promise<DocHandle<T>>;
|
|
64
76
|
/** Create a new DocHandle by cloning the history of an existing DocHandle.
|
|
65
77
|
*
|
|
66
78
|
* @param clonedHandle - The handle to clone
|
|
@@ -179,7 +191,7 @@ export interface RepoConfig {
|
|
|
179
191
|
/**
|
|
180
192
|
* @hidden
|
|
181
193
|
*/
|
|
182
|
-
idFactory?: (initialHeads: Heads) => Uint8Array
|
|
194
|
+
idFactory?: (initialHeads: Heads) => Promise<Uint8Array>;
|
|
183
195
|
}
|
|
184
196
|
/** A function that determines whether we should share a document with a peer
|
|
185
197
|
*
|
package/dist/Repo.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Repo.d.ts","sourceRoot":"","sources":["../src/Repo.ts"],"names":[],"mappings":"AAAA,OAAO,EAAqB,KAAK,EAAE,MAAM,2BAA2B,CAAA;AAEpE,OAAO,EAAE,YAAY,EAAE,MAAM,eAAe,CAAA;AAS5C,OAAO,EAEL,SAAS,EAKV,MAAM,gBAAgB,CAAA;AAIvB,OAAO,EACL,uBAAuB,EACvB,KAAK,YAAY,EAClB,MAAM,sCAAsC,CAAA;AAC7C,OAAO,EAAE,gBAAgB,EAAE,MAAM,+BAA+B,CAAA;AAEhE,OAAO,EAAE,uBAAuB,EAAE,MAAM,sCAAsC,CAAA;AAC9E,OAAO,EAAE,gBAAgB,EAAE,MAAM,+BAA+B,CAAA;AAChE,OAAO,EAAE,SAAS,EAAE,MAAM,oBAAoB,CAAA;AAC9C,OAAO,EAAE,sBAAsB,EAAE,MAAM,0CAA0C,CAAA;AACjF,OAAO,EACL,cAAc,EAEf,MAAM,gCAAgC,CAAA;AACvC,OAAO,KAAK,EACV,aAAa,EACb,YAAY,EAEZ,UAAU,EACV,MAAM,EACP,MAAM,YAAY,CAAA;AACnB,OAAO,EAAa,YAAY,
|
|
1
|
+
{"version":3,"file":"Repo.d.ts","sourceRoot":"","sources":["../src/Repo.ts"],"names":[],"mappings":"AAAA,OAAO,EAAqB,KAAK,EAAE,MAAM,2BAA2B,CAAA;AAEpE,OAAO,EAAE,YAAY,EAAE,MAAM,eAAe,CAAA;AAS5C,OAAO,EAEL,SAAS,EAKV,MAAM,gBAAgB,CAAA;AAIvB,OAAO,EACL,uBAAuB,EACvB,KAAK,YAAY,EAClB,MAAM,sCAAsC,CAAA;AAC7C,OAAO,EAAE,gBAAgB,EAAE,MAAM,+BAA+B,CAAA;AAEhE,OAAO,EAAE,uBAAuB,EAAE,MAAM,sCAAsC,CAAA;AAC9E,OAAO,EAAE,gBAAgB,EAAE,MAAM,+BAA+B,CAAA;AAChE,OAAO,EAAE,SAAS,EAAE,MAAM,oBAAoB,CAAA;AAC9C,OAAO,EAAE,sBAAsB,EAAE,MAAM,0CAA0C,CAAA;AACjF,OAAO,EACL,cAAc,EAEf,MAAM,gCAAgC,CAAA;AACvC,OAAO,KAAK,EACV,aAAa,EACb,YAAY,EAEZ,UAAU,EACV,MAAM,EACP,MAAM,YAAY,CAAA;AACnB,OAAO,EAAa,YAAY,EAAc,MAAM,wBAAwB,CAAA;AAC5E,OAAO,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAA;AAEhD,MAAM,MAAM,uBAAuB,CAAC,CAAC,IAAI,YAAY,CAAC,CAAC,CAAC,GAAG;IACzD,UAAU,EAAE,CAAC,eAAe,EAAE,MAAM,EAAE,KAAK,OAAO,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,CAAA;IAChE,IAAI,EAAE,MAAM,YAAY,CAAC,CAAC,CAAC,CAAA;IAC3B,SAAS,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,EAAE,YAAY,CAAC,CAAC,CAAC,KAAK,IAAI,KAAK,MAAM,IAAI,CAAA;CACzE,CAAA;AAED,MAAM,MAAM,cAAc,CAAC,CAAC,IAAI;IAC9B,IAAI,EAAE,MAAM,YAAY,CAAC,CAAC,CAAC,CAAA;IAC3B,SAAS,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,EAAE,YAAY,CAAC,CAAC,CAAC,KAAK,IAAI,KAAK,MAAM,IAAI,CAAA;IACxE,UAAU,EAAE,CAAC,eAAe,EAAE,MAAM,EAAE,KAAK,OAAO,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,CAAA;CACjE,CAAA;AAMD,8FAA8F;AAC9F;;;;;;GAMG;AACH,qBAAa,IAAK,SAAQ,YAAY,CAAC,UAAU,CAAC;;IAGhD,cAAc;IACd,gBAAgB,EAAE,gBAAgB,CAAA;IAClC,cAAc;IACd,gBAAgB,CAAC,EAAE,gBAAgB,CAAA;IAUnC,cAAc;IACd,YAAY,EAAE,sBAAsB,CAAA;IAOpC,8GAA8G;IAC9G,cAAc;IACd,oBAAoB,EAAE,MAAM,CAAC,MAAM,EAAE,YAAY,CAAC,CAAK;gBAW3C,EACV,OAAO,EACP,OAAY,EACZ,MAAuB,EACvB,WAAW,EACX,WAAW,EACX,WAAmC,EACnC,0BAAkC,EAClC,QAAa,EACb,gBAAsB,EACtB,SAAS,GACV,GAAE,UAAe;IAmSlB,8CAA8C;IAC9C,IAAI,OAAO,uCAEV;IAED,+CAA+C;IAC/C,IAAI,KAAK,IAAI,MAAM,EAAE,CAEpB;IAED,IAAI,MAAM,IAAI,MAAM,CAEnB;IAED,cAAc;IACd,IAAI,WAAW,IAAI,WAAW,CAE7B;IAED,cAAc;IACd,IAAI,WAAW,CAAC,MAAM,EAAE,WAAW,EAElC;IAED,cAAc;IACd,IAAI,WAAW,IAAI,WAAW,CAE7B;IAED,cAAc;IACd,IAAI,WAAW,CAAC,MAAM,EAAE,WAAW,EAElC;IAED,kBAAkB,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,GAAG,SAAS;IAIzD;;;;OAIG;IACH,MAAM,CAAC,CAAC,EAAE,YAAY,CAAC,EAAE,CAAC,GAAG,SAAS,CAAC,CAAC,CAAC;IAwBzC;;;;;;;;;;OAUG;IACG,OAAO,CAAC,CAAC,EAAE,YAAY,CAAC,EAAE,CAAC,GAAG,OAAO,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC;IA8BzD;;;;;;;;;;;;OAYG;IACH,KAAK,CAAC,CAAC,EAAE,YAAY,EAAE,SAAS,CAAC,CAAC,CAAC;IAmBnC,gBAAgB,CAAC,CAAC,EAChB,EAAE,EAAE,aAAa,EACjB,OAAO,GAAE,YAAiB,GACzB,uBAAuB,CAAC,CAAC,CAAC,GAAG,YAAY,CAAC,CAAC,CAAC;IAsKzC,IAAI,CAAC,CAAC,EACV,EAAE,EAAE,aAAa,EACjB,OAAO,GAAE,eAAe,GAAG,YAAiB,GAC3C,OAAO,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC;IA0ExB;;;OAGG;IACG,WAAW,CAAC,CAAC;IACjB,sDAAsD;IACtD,EAAE,EAAE,aAAa,EACjB,OAAO,GAAE,eAAe,GAAG,YAAiB,GAC3C,OAAO,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC;IAmBxB,MAAM;IACJ,oDAAoD;IACpD,EAAE,EAAE,aAAa;IAanB;;;;;;OAMG;IACG,MAAM,CAAC,EAAE,EAAE,aAAa,GAAG,OAAO,CAAC,UAAU,GAAG,SAAS,CAAC;IAQhE;;;;;;;;;;;;;;;OAeG;IACH,MAAM,CAAC,CAAC,EAAE,MAAM,EAAE,UAAU,EAAE,IAAI,CAAC,EAAE;QAAE,KAAK,CAAC,EAAE,UAAU,CAAA;KAAE,GAAG,SAAS,CAAC,CAAC,CAAC;IAoB1E,kBAAkB,GAAI,SAAS,SAAS,EAAE,UASzC;IAED,SAAS,QAAa,OAAO,CAAC,SAAS,GAAG,SAAS,CAAC,CAMnD;IAED;;;;;OAKG;IACG,KAAK,CAAC,SAAS,CAAC,EAAE,UAAU,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC;IAcpD;;;;;OAKG;IACG,eAAe,CAAC,UAAU,EAAE,UAAU;IA8B5C,QAAQ,IAAI,OAAO,CAAC,IAAI,CAAC;IAOzB,OAAO,IAAI;QAAE,SAAS,EAAE;YAAE,CAAC,GAAG,EAAE,MAAM,GAAG,GAAG,CAAA;SAAE,CAAA;KAAE;CAGjD;AAED,MAAM,WAAW,UAAU;IACzB,4BAA4B;IAC5B,MAAM,CAAC,EAAE,MAAM,CAAA;IAEf;8DAC0D;IAC1D,WAAW,CAAC,EAAE,OAAO,CAAA;IAErB,gDAAgD;IAChD,OAAO,CAAC,EAAE,uBAAuB,CAAA;IAEjC,iEAAiE;IACjE,OAAO,CAAC,EAAE,uBAAuB,EAAE,CAAA;IAEnC;;;OAGG;IACH,WAAW,CAAC,EAAE,WAAW,CAAA;IAEzB;;;;;;;;OAQG;IACH,WAAW,CAAC,EAAE,WAAW,CAAA;IAEzB;;OAEG;IACH,0BAA0B,CAAC,EAAE,OAAO,CAAA;IAEpC;;;;OAIG;IACH,QAAQ,CAAC,EAAE,YAAY,EAAE,CAAA;IAEzB;;OAEG;IACH,gBAAgB,CAAC,EAAE,MAAM,CAAA;IAIzB;;OAEG;IACH,SAAS,CAAC,EAAE,CAAC,YAAY,EAAE,KAAK,KAAK,OAAO,CAAC,UAAU,CAAC,CAAA;CACzD;AAED;;;;;;;KAOK;AACL,MAAM,MAAM,WAAW,GAAG,CACxB,MAAM,EAAE,MAAM,EACd,UAAU,CAAC,EAAE,UAAU,KACpB,OAAO,CAAC,OAAO,CAAC,CAAA;AAErB;;KAEK;AACL,MAAM,MAAM,WAAW,GAAG;IACxB;;;;;;;;;OASG;IACH,QAAQ,EAAE,WAAW,CAAA;IACrB;;OAEG;IACH,MAAM,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,EAAE,UAAU,KAAK,OAAO,CAAC,OAAO,CAAC,CAAA;CAC5D,CAAA;AAGD,MAAM,WAAW,UAAU;IACzB,+CAA+C;IAC/C,QAAQ,EAAE,CAAC,GAAG,EAAE,eAAe,KAAK,IAAI,CAAA;IACxC,6BAA6B;IAC7B,iBAAiB,EAAE,CAAC,GAAG,EAAE,qBAAqB,KAAK,IAAI,CAAA;IACvD,4FAA4F;IAC5F,sBAAsB,EAAE,CAAC,GAAG,EAAE,qBAAqB,KAAK,IAAI,CAAA;IAC5D,aAAa,EAAE,CAAC,GAAG,EAAE,UAAU,KAAK,IAAI,CAAA;CACzC;AAED,MAAM,WAAW,eAAe;IAC9B,eAAe,CAAC,EAAE,MAAM,EAAE,CAAA;CAC3B;AAED,MAAM,WAAW,eAAe;IAC9B,MAAM,EAAE,SAAS,CAAC,GAAG,CAAC,CAAA;CACvB;AAED,MAAM,WAAW,qBAAqB;IACpC,UAAU,EAAE,UAAU,CAAA;CACvB;AAED,MAAM,MAAM,UAAU,GAClB,cAAc,GACd;IACE,IAAI,EAAE,YAAY,CAAA;IAClB,UAAU,EAAE,UAAU,CAAA;IACtB,cAAc,EAAE,MAAM,CAAA;IACtB,MAAM,EAAE,MAAM,CAAA;IACd,UAAU,EAAE,MAAM,CAAA;CACnB,GACD;IACE,IAAI,EAAE,YAAY,CAAA;IAClB,UAAU,EAAE,UAAU,CAAA;CACvB,CAAA"}
|
package/dist/Repo.js
CHANGED
|
@@ -9,7 +9,7 @@ import { throttle } from "./helpers/throttle.js";
|
|
|
9
9
|
import { NetworkSubsystem } from "./network/NetworkSubsystem.js";
|
|
10
10
|
import { StorageSubsystem } from "./storage/StorageSubsystem.js";
|
|
11
11
|
import { CollectionSynchronizer } from "./synchronizer/CollectionSynchronizer.js";
|
|
12
|
-
import { abortable } from "./helpers/abortable.js";
|
|
12
|
+
import { abortable, AbortError } from "./helpers/abortable.js";
|
|
13
13
|
function randomPeerId() {
|
|
14
14
|
return ("peer-" + Math.random().toString(36).slice(4));
|
|
15
15
|
}
|
|
@@ -310,9 +310,43 @@ export class Repo extends EventEmitter {
|
|
|
310
310
|
initialDoc = Automerge.emptyChange(Automerge.init());
|
|
311
311
|
}
|
|
312
312
|
// Generate a new UUID and store it in the buffer
|
|
313
|
+
const { documentId } = parseAutomergeUrl(generateAutomergeUrl());
|
|
314
|
+
const handle = this.#getHandle({
|
|
315
|
+
documentId,
|
|
316
|
+
});
|
|
317
|
+
this.#registerHandleWithSubsystems(handle);
|
|
318
|
+
handle.update(() => {
|
|
319
|
+
return initialDoc;
|
|
320
|
+
});
|
|
321
|
+
handle.doneLoading();
|
|
322
|
+
return handle;
|
|
323
|
+
}
|
|
324
|
+
/**
|
|
325
|
+
* Creates a new document and returns a handle to it. The initial value of the
|
|
326
|
+
* document is an empty object `{}` unless an initial value is provided. The
|
|
327
|
+
* main difference between this and Repo.create is that if an `idGenerator`
|
|
328
|
+
* was provided at repo construction, that idGenerator will be used to
|
|
329
|
+
* generate the document ID of the document returned by this method.
|
|
330
|
+
*
|
|
331
|
+
* This is a hidden, experimental API which is subject to change or removal without notice.
|
|
332
|
+
* @hidden
|
|
333
|
+
* @experimental
|
|
334
|
+
*/
|
|
335
|
+
async create2(initialValue) {
|
|
336
|
+
// Note that the reason this method is hidden and experimental is because it is async,
|
|
337
|
+
// and it is async because we want to be able to call the #idGenerator, which is async.
|
|
338
|
+
// This is all really in service of wiring up keyhive and we probably need to find a
|
|
339
|
+
// nicer way to achieve this.
|
|
340
|
+
let initialDoc;
|
|
341
|
+
if (initialValue) {
|
|
342
|
+
initialDoc = Automerge.from(initialValue);
|
|
343
|
+
}
|
|
344
|
+
else {
|
|
345
|
+
initialDoc = Automerge.emptyChange(Automerge.init());
|
|
346
|
+
}
|
|
313
347
|
let { documentId } = parseAutomergeUrl(generateAutomergeUrl());
|
|
314
348
|
if (this.#idFactory) {
|
|
315
|
-
const rawDocId = this.#idFactory(Automerge.getHeads(initialDoc));
|
|
349
|
+
const rawDocId = await this.#idFactory(Automerge.getHeads(initialDoc));
|
|
316
350
|
documentId = binaryToDocumentId(rawDocId);
|
|
317
351
|
}
|
|
318
352
|
const handle = this.#getHandle({
|
|
@@ -476,7 +510,13 @@ export class Repo extends EventEmitter {
|
|
|
476
510
|
catch (error) {
|
|
477
511
|
progressSignal.notify({
|
|
478
512
|
state: "failed",
|
|
479
|
-
error:
|
|
513
|
+
error:
|
|
514
|
+
// In most JS environments DOMException extends Error, but not always, in some environments it's a separate type.
|
|
515
|
+
// Some Node.js DOM polyfills do not always extend the Error
|
|
516
|
+
// Jsdom polyfill doesn't extend Error, whereas happy-dom does.
|
|
517
|
+
error instanceof Error || error instanceof DOMException
|
|
518
|
+
? error
|
|
519
|
+
: new Error(String(error)),
|
|
480
520
|
handle: this.#getHandle({ documentId }),
|
|
481
521
|
});
|
|
482
522
|
}
|
|
@@ -485,7 +525,7 @@ export class Repo extends EventEmitter {
|
|
|
485
525
|
const { allowableStates = ["ready"], signal } = options;
|
|
486
526
|
// Check if already aborted
|
|
487
527
|
if (signal?.aborted) {
|
|
488
|
-
throw new
|
|
528
|
+
throw new AbortError();
|
|
489
529
|
}
|
|
490
530
|
const progress = this.findWithProgress(id, { signal });
|
|
491
531
|
if ("subscribe" in progress) {
|
|
@@ -1,3 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* An error thrown when an operation is aborted.
|
|
3
|
+
*
|
|
4
|
+
* @remarks
|
|
5
|
+
* This error is thrown when an operation is aborted. It is a subclass of DOMException
|
|
6
|
+
* with name "AbortError".
|
|
7
|
+
*
|
|
8
|
+
* @example
|
|
9
|
+
* ```typescript
|
|
10
|
+
* throw new AbortError()
|
|
11
|
+
* ```
|
|
12
|
+
*/
|
|
13
|
+
export declare class AbortError extends DOMException {
|
|
14
|
+
constructor(message?: string);
|
|
15
|
+
}
|
|
1
16
|
/**
|
|
2
17
|
* Wraps a Promise and causes it to reject when the signal is aborted.
|
|
3
18
|
*
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"abortable.d.ts","sourceRoot":"","sources":["../../src/helpers/abortable.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;
|
|
1
|
+
{"version":3,"file":"abortable.d.ts","sourceRoot":"","sources":["../../src/helpers/abortable.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AACH,qBAAa,UAAW,SAAQ,YAAY;gBAC9B,OAAO,CAAC,EAAE,MAAM;CAG7B;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AACH,wBAAgB,SAAS,CAAC,CAAC,EACzB,CAAC,EAAE,OAAO,CAAC,CAAC,CAAC,EACb,MAAM,EAAE,WAAW,GAAG,SAAS,GAC9B,OAAO,CAAC,CAAC,CAAC,CAsBZ;AAED;;GAEG;AACH,MAAM,WAAW,YAAY;IAC3B,MAAM,CAAC,EAAE,WAAW,CAAA;CACrB"}
|
|
@@ -1,3 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* An error thrown when an operation is aborted.
|
|
3
|
+
*
|
|
4
|
+
* @remarks
|
|
5
|
+
* This error is thrown when an operation is aborted. It is a subclass of DOMException
|
|
6
|
+
* with name "AbortError".
|
|
7
|
+
*
|
|
8
|
+
* @example
|
|
9
|
+
* ```typescript
|
|
10
|
+
* throw new AbortError()
|
|
11
|
+
* ```
|
|
12
|
+
*/
|
|
13
|
+
export class AbortError extends DOMException {
|
|
14
|
+
constructor(message) {
|
|
15
|
+
super(message ?? "Operation aborted", "AbortError");
|
|
16
|
+
}
|
|
17
|
+
}
|
|
1
18
|
/**
|
|
2
19
|
* Wraps a Promise and causes it to reject when the signal is aborted.
|
|
3
20
|
*
|
|
@@ -31,7 +48,7 @@ export function abortable(p, signal) {
|
|
|
31
48
|
return new Promise((resolve, reject) => {
|
|
32
49
|
signal?.addEventListener("abort", () => {
|
|
33
50
|
if (!settled) {
|
|
34
|
-
reject(new
|
|
51
|
+
reject(new AbortError());
|
|
35
52
|
}
|
|
36
53
|
}, { once: true });
|
|
37
54
|
p.then(result => {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@automerge/automerge-repo",
|
|
3
|
-
"version": "2.4.0
|
|
3
|
+
"version": "2.4.0",
|
|
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": "586290a804e12206362cf41c87e309bd0fb22d69"
|
|
63
63
|
}
|
package/src/Repo.ts
CHANGED
|
@@ -41,7 +41,7 @@ import type {
|
|
|
41
41
|
DocumentId,
|
|
42
42
|
PeerId,
|
|
43
43
|
} from "./types.js"
|
|
44
|
-
import { abortable, AbortOptions } from "./helpers/abortable.js"
|
|
44
|
+
import { abortable, AbortOptions, AbortError } from "./helpers/abortable.js"
|
|
45
45
|
import { FindProgress } from "./FindProgress.js"
|
|
46
46
|
|
|
47
47
|
export type FindProgressWithMethods<T> = FindProgress<T> & {
|
|
@@ -103,7 +103,7 @@ export class Repo extends EventEmitter<RepoEvents> {
|
|
|
103
103
|
DocumentId,
|
|
104
104
|
(payload: DocHandleEncodedChangePayload<any>) => void
|
|
105
105
|
> = {}
|
|
106
|
-
#idFactory: ((initialHeads: Heads) => Uint8Array) | null
|
|
106
|
+
#idFactory: ((initialHeads: Heads) => Promise<Uint8Array>) | null
|
|
107
107
|
|
|
108
108
|
constructor({
|
|
109
109
|
storage,
|
|
@@ -459,9 +459,47 @@ export class Repo extends EventEmitter<RepoEvents> {
|
|
|
459
459
|
}
|
|
460
460
|
|
|
461
461
|
// Generate a new UUID and store it in the buffer
|
|
462
|
+
const { documentId } = parseAutomergeUrl(generateAutomergeUrl())
|
|
463
|
+
const handle = this.#getHandle<T>({
|
|
464
|
+
documentId,
|
|
465
|
+
}) as DocHandle<T>
|
|
466
|
+
|
|
467
|
+
this.#registerHandleWithSubsystems(handle)
|
|
468
|
+
|
|
469
|
+
handle.update(() => {
|
|
470
|
+
return initialDoc
|
|
471
|
+
})
|
|
472
|
+
|
|
473
|
+
handle.doneLoading()
|
|
474
|
+
return handle
|
|
475
|
+
}
|
|
476
|
+
|
|
477
|
+
/**
|
|
478
|
+
* Creates a new document and returns a handle to it. The initial value of the
|
|
479
|
+
* document is an empty object `{}` unless an initial value is provided. The
|
|
480
|
+
* main difference between this and Repo.create is that if an `idGenerator`
|
|
481
|
+
* was provided at repo construction, that idGenerator will be used to
|
|
482
|
+
* generate the document ID of the document returned by this method.
|
|
483
|
+
*
|
|
484
|
+
* This is a hidden, experimental API which is subject to change or removal without notice.
|
|
485
|
+
* @hidden
|
|
486
|
+
* @experimental
|
|
487
|
+
*/
|
|
488
|
+
async create2<T>(initialValue?: T): Promise<DocHandle<T>> {
|
|
489
|
+
// Note that the reason this method is hidden and experimental is because it is async,
|
|
490
|
+
// and it is async because we want to be able to call the #idGenerator, which is async.
|
|
491
|
+
// This is all really in service of wiring up keyhive and we probably need to find a
|
|
492
|
+
// nicer way to achieve this.
|
|
493
|
+
let initialDoc: Automerge.Doc<T>
|
|
494
|
+
if (initialValue) {
|
|
495
|
+
initialDoc = Automerge.from(initialValue)
|
|
496
|
+
} else {
|
|
497
|
+
initialDoc = Automerge.emptyChange(Automerge.init())
|
|
498
|
+
}
|
|
499
|
+
|
|
462
500
|
let { documentId } = parseAutomergeUrl(generateAutomergeUrl())
|
|
463
501
|
if (this.#idFactory) {
|
|
464
|
-
const rawDocId = this.#idFactory(Automerge.getHeads(initialDoc))
|
|
502
|
+
const rawDocId = await this.#idFactory(Automerge.getHeads(initialDoc))
|
|
465
503
|
documentId = binaryToDocumentId(rawDocId as BinaryDocumentId)
|
|
466
504
|
}
|
|
467
505
|
const handle = this.#getHandle<T>({
|
|
@@ -477,7 +515,6 @@ export class Repo extends EventEmitter<RepoEvents> {
|
|
|
477
515
|
handle.doneLoading()
|
|
478
516
|
return handle
|
|
479
517
|
}
|
|
480
|
-
|
|
481
518
|
/** Create a new DocHandle by cloning the history of an existing DocHandle.
|
|
482
519
|
*
|
|
483
520
|
* @param clonedHandle - The handle to clone
|
|
@@ -667,7 +704,13 @@ export class Repo extends EventEmitter<RepoEvents> {
|
|
|
667
704
|
} catch (error) {
|
|
668
705
|
progressSignal.notify({
|
|
669
706
|
state: "failed" as const,
|
|
670
|
-
error:
|
|
707
|
+
error:
|
|
708
|
+
// In most JS environments DOMException extends Error, but not always, in some environments it's a separate type.
|
|
709
|
+
// Some Node.js DOM polyfills do not always extend the Error
|
|
710
|
+
// Jsdom polyfill doesn't extend Error, whereas happy-dom does.
|
|
711
|
+
error instanceof Error || error instanceof DOMException
|
|
712
|
+
? error
|
|
713
|
+
: new Error(String(error)),
|
|
671
714
|
handle: this.#getHandle<T>({ documentId }),
|
|
672
715
|
})
|
|
673
716
|
}
|
|
@@ -681,7 +724,7 @@ export class Repo extends EventEmitter<RepoEvents> {
|
|
|
681
724
|
|
|
682
725
|
// Check if already aborted
|
|
683
726
|
if (signal?.aborted) {
|
|
684
|
-
throw new
|
|
727
|
+
throw new AbortError()
|
|
685
728
|
}
|
|
686
729
|
|
|
687
730
|
const progress = this.findWithProgress<T>(id, { signal })
|
|
@@ -983,7 +1026,7 @@ export interface RepoConfig {
|
|
|
983
1026
|
/**
|
|
984
1027
|
* @hidden
|
|
985
1028
|
*/
|
|
986
|
-
idFactory?: (initialHeads: Heads) => Uint8Array
|
|
1029
|
+
idFactory?: (initialHeads: Heads) => Promise<Uint8Array>
|
|
987
1030
|
}
|
|
988
1031
|
|
|
989
1032
|
/** A function that determines whether we should share a document with a peer
|
package/src/helpers/abortable.ts
CHANGED
|
@@ -1,3 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* An error thrown when an operation is aborted.
|
|
3
|
+
*
|
|
4
|
+
* @remarks
|
|
5
|
+
* This error is thrown when an operation is aborted. It is a subclass of DOMException
|
|
6
|
+
* with name "AbortError".
|
|
7
|
+
*
|
|
8
|
+
* @example
|
|
9
|
+
* ```typescript
|
|
10
|
+
* throw new AbortError()
|
|
11
|
+
* ```
|
|
12
|
+
*/
|
|
13
|
+
export class AbortError extends DOMException {
|
|
14
|
+
constructor(message?: string) {
|
|
15
|
+
super(message ?? "Operation aborted", "AbortError")
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
|
|
1
19
|
/**
|
|
2
20
|
* Wraps a Promise and causes it to reject when the signal is aborted.
|
|
3
21
|
*
|
|
@@ -26,7 +44,6 @@
|
|
|
26
44
|
* before the promise p settles, and settles as p settles otherwise
|
|
27
45
|
* @throws {DOMException} With name "AbortError" if aborted before p settles
|
|
28
46
|
*/
|
|
29
|
-
|
|
30
47
|
export function abortable<T>(
|
|
31
48
|
p: Promise<T>,
|
|
32
49
|
signal: AbortSignal | undefined
|
|
@@ -37,7 +54,7 @@ export function abortable<T>(
|
|
|
37
54
|
"abort",
|
|
38
55
|
() => {
|
|
39
56
|
if (!settled) {
|
|
40
|
-
reject(new
|
|
57
|
+
reject(new AbortError())
|
|
41
58
|
}
|
|
42
59
|
},
|
|
43
60
|
{ once: true }
|
package/test/Repo.test.ts
CHANGED
|
@@ -34,6 +34,7 @@ import { getRandomItem } from "./helpers/getRandomItem.js"
|
|
|
34
34
|
import { TestDoc } from "./types.js"
|
|
35
35
|
import { StorageId, StorageKey } from "../src/storage/types.js"
|
|
36
36
|
import { FindProgress } from "../src/FindProgress.js"
|
|
37
|
+
import { AbortError } from "../src/helpers/abortable.js"
|
|
37
38
|
|
|
38
39
|
describe("Repo", () => {
|
|
39
40
|
describe("constructor", () => {
|
|
@@ -1993,7 +1994,7 @@ describe("Repo.find() abort behavior", () => {
|
|
|
1993
1994
|
|
|
1994
1995
|
await expect(
|
|
1995
1996
|
repo.find(generateAutomergeUrl(), { signal: controller.signal })
|
|
1996
|
-
).rejects.toThrow(
|
|
1997
|
+
).rejects.toThrow(AbortError)
|
|
1997
1998
|
})
|
|
1998
1999
|
|
|
1999
2000
|
it("can abort while waiting for ready state", async () => {
|
|
@@ -2007,7 +2008,13 @@ describe("Repo.find() abort behavior", () => {
|
|
|
2007
2008
|
const findPromise = repo.find(url, { signal: controller.signal })
|
|
2008
2009
|
controller.abort()
|
|
2009
2010
|
|
|
2010
|
-
|
|
2011
|
+
// Official specification just says to check `reason.name === "AbortError"`
|
|
2012
|
+
// Using AbortError promotes correctness across different JS environments and provides a simpler check.
|
|
2013
|
+
await expect(findPromise).rejects.toThrow(AbortError)
|
|
2014
|
+
await expect(findPromise).rejects.rejects.toHaveProperty(
|
|
2015
|
+
"name",
|
|
2016
|
+
"AbortError"
|
|
2017
|
+
)
|
|
2011
2018
|
await expect(findPromise).rejects.not.toThrow("unavailable")
|
|
2012
2019
|
})
|
|
2013
2020
|
|
|
@@ -2034,7 +2041,7 @@ describe("Repo.find() abort behavior", () => {
|
|
|
2034
2041
|
const repo = new Repo({
|
|
2035
2042
|
idFactory: () => id,
|
|
2036
2043
|
})
|
|
2037
|
-
const handle = repo.
|
|
2044
|
+
const handle = await repo.create2()
|
|
2038
2045
|
expect(handle.documentId).toBe("9HUp4wuzRMx9MRvN4x")
|
|
2039
2046
|
})
|
|
2040
2047
|
|
|
@@ -2047,7 +2054,7 @@ describe("Repo.find() abort behavior", () => {
|
|
|
2047
2054
|
return id
|
|
2048
2055
|
},
|
|
2049
2056
|
})
|
|
2050
|
-
const handle = repo.
|
|
2057
|
+
const handle = await repo.create2()
|
|
2051
2058
|
const actualHeads = A.getHeads(handle.doc())
|
|
2052
2059
|
assert.deepStrictEqual(actualHeads, calledHeads)
|
|
2053
2060
|
})
|
|
@@ -2066,7 +2073,7 @@ describe("Repo.find() abort behavior", () => {
|
|
|
2066
2073
|
|
|
2067
2074
|
await pause(50)
|
|
2068
2075
|
|
|
2069
|
-
const handle = alice.
|
|
2076
|
+
const handle = await alice.create2({ foo: "bar" })
|
|
2070
2077
|
const bobHandle = await bob.find(handle.url)
|
|
2071
2078
|
assert.deepStrictEqual(bobHandle.doc(), { foo: "bar" })
|
|
2072
2079
|
})
|