@automerge/automerge-repo 2.4.0-alpha.2 → 2.5.0-alpha.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.
@@ -2,6 +2,7 @@ import { next as A } from "@automerge/automerge/slim";
2
2
  import { EventEmitter } from "eventemitter3";
3
3
  import type { AutomergeUrl, DocumentId, PeerId, UrlHeads } from "./types.js";
4
4
  import { StorageId } from "./storage/types.js";
5
+ import { AbortOptions } from "./helpers/abortable.js";
5
6
  /**
6
7
  * A DocHandle is a wrapper around a single Automerge document that lets us listen for changes and
7
8
  * notify the network and storage of new changes.
@@ -57,13 +58,17 @@ export declare class DocHandle<T> extends EventEmitter<DocHandleEvents<T>> {
57
58
  /** @hidden */
58
59
  get state(): "idle" | "loading" | "requesting" | "ready" | "unavailable" | "unloaded" | "deleted";
59
60
  /**
60
- * @returns a promise that resolves when the document is in one of the given states (if no states
61
- * are passed, when the document is ready)
61
+ * Returns promise that resolves when document is in one of the given states (default is 'ready' state)
62
62
  *
63
63
  * Use this to block until the document handle has finished loading. The async equivalent to
64
64
  * checking `inState()`.
65
+ *
66
+ * @param awaitStates - HandleState or HandleStates to wait for
67
+ * @param signal - Optional AbortSignal to cancel the waiting operation
68
+ * @returns a promise that resolves when the document is in one of the given states (if no states
69
+ * are passed, when the document is ready)
65
70
  */
66
- whenReady(awaitStates?: HandleState[]): Promise<void>;
71
+ whenReady(awaitStates?: HandleState[], options?: AbortOptions): Promise<void>;
67
72
  /**
68
73
  * Returns the current state of the Automerge document this handle manages.
69
74
  *
@@ -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
@@ -6,6 +6,7 @@ import { decodeHeads, encodeHeads, stringifyAutomergeUrl, } from "./AutomergeUrl
6
6
  import { encode } from "./helpers/cbor.js";
7
7
  import { headsAreSame } from "./helpers/headsAreSame.js";
8
8
  import { withTimeout } from "./helpers/withTimeout.js";
9
+ import { AbortError, isAbortErrorLike, } from "./helpers/abortable.js";
9
10
  /**
10
11
  * A DocHandle is a wrapper around a single Automerge document that lets us listen for changes and
11
12
  * notify the network and storage of new changes.
@@ -141,14 +142,19 @@ export class DocHandle extends EventEmitter {
141
142
  get #state() {
142
143
  return this.#machine?.getSnapshot().value;
143
144
  }
144
- /** Returns a promise that resolves when the docHandle is in one of the given states */
145
- #statePromise(awaitStates) {
145
+ /**
146
+ * Returns a promise that resolves when the docHandle is in one of the given states
147
+ *
148
+ * @param awaitStates - HandleState or HandleStates to wait for
149
+ * @param signal - Optional AbortSignal to cancel the waiting operation
150
+ */
151
+ #statePromise(awaitStates, options) {
146
152
  const awaitStatesArray = Array.isArray(awaitStates)
147
153
  ? awaitStates
148
154
  : [awaitStates];
149
155
  return waitFor(this.#machine, s => awaitStatesArray.some(state => s.matches(state)),
150
156
  // use a longer delay here so as not to race with other delays
151
- { timeout: this.#timeoutDelay * 2 });
157
+ { timeout: this.#timeoutDelay * 2, ...options });
152
158
  }
153
159
  /**
154
160
  * Update the document with whatever the result of callback is
@@ -254,17 +260,24 @@ export class DocHandle extends EventEmitter {
254
260
  return this.#machine.getSnapshot().value;
255
261
  }
256
262
  /**
257
- * @returns a promise that resolves when the document is in one of the given states (if no states
258
- * are passed, when the document is ready)
263
+ * Returns promise that resolves when document is in one of the given states (default is 'ready' state)
259
264
  *
260
265
  * Use this to block until the document handle has finished loading. The async equivalent to
261
266
  * checking `inState()`.
267
+ *
268
+ * @param awaitStates - HandleState or HandleStates to wait for
269
+ * @param signal - Optional AbortSignal to cancel the waiting operation
270
+ * @returns a promise that resolves when the document is in one of the given states (if no states
271
+ * are passed, when the document is ready)
262
272
  */
263
- async whenReady(awaitStates = ["ready"]) {
273
+ async whenReady(awaitStates = ["ready"], options) {
264
274
  try {
265
- await withTimeout(this.#statePromise(awaitStates), this.#timeoutDelay);
275
+ await withTimeout(this.#statePromise(awaitStates, options), this.#timeoutDelay);
266
276
  }
267
277
  catch (error) {
278
+ if (isAbortErrorLike(error)) {
279
+ throw new AbortError(); //throw new error for stack trace
280
+ }
268
281
  console.log(`error waiting for ${this.documentId} to be in one of states: ${awaitStates.join(", ")}`);
269
282
  throw error;
270
283
  }
package/dist/Repo.d.ts CHANGED
@@ -241,6 +241,15 @@ export interface DeleteDocumentPayload {
241
241
  documentId: DocumentId;
242
242
  }
243
243
  export type DocMetrics = DocSyncMetrics | {
244
+ type: "doc-compacted";
245
+ documentId: DocumentId;
246
+ durationMillis: number;
247
+ } | {
248
+ type: "doc-saved";
249
+ documentId: DocumentId;
250
+ durationMillis: number;
251
+ sinceHeads: Array<string>;
252
+ } | {
244
253
  type: "doc-loaded";
245
254
  documentId: DocumentId;
246
255
  durationMillis: number;
@@ -1 +1 @@
1
- {"version":3,"file":"Repo.d.ts","sourceRoot":"","sources":["../src/Repo.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"Repo.d.ts","sourceRoot":"","sources":["../src/Repo.ts"],"names":[],"mappings":"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"}
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
  }
@@ -92,6 +92,8 @@ export class Repo extends EventEmitter {
92
92
  const storageSubsystem = storage ? new StorageSubsystem(storage) : undefined;
93
93
  if (storageSubsystem) {
94
94
  storageSubsystem.on("document-loaded", event => this.emit("doc-metrics", { type: "doc-loaded", ...event }));
95
+ storageSubsystem.on("doc-compacted", event => this.emit("doc-metrics", { type: "doc-compacted", ...event }));
96
+ storageSubsystem.on("doc-saved", event => this.emit("doc-metrics", { type: "doc-saved", ...event }));
95
97
  }
96
98
  this.storageSubsystem = storageSubsystem;
97
99
  this.#saveDebounceRate = saveDebounceRate;
@@ -510,7 +512,13 @@ export class Repo extends EventEmitter {
510
512
  catch (error) {
511
513
  progressSignal.notify({
512
514
  state: "failed",
513
- error: error instanceof Error ? error : new Error(String(error)),
515
+ error:
516
+ // In most JS environments DOMException extends Error, but not always, in some environments it's a separate type.
517
+ // Some Node.js DOM polyfills do not always extend the Error
518
+ // Jsdom polyfill doesn't extend Error, whereas happy-dom does.
519
+ error instanceof Error || error instanceof DOMException
520
+ ? error
521
+ : new Error(String(error)),
514
522
  handle: this.#getHandle({ documentId }),
515
523
  });
516
524
  }
@@ -519,7 +527,7 @@ export class Repo extends EventEmitter {
519
527
  const { allowableStates = ["ready"], signal } = options;
520
528
  // Check if already aborted
521
529
  if (signal?.aborted) {
522
- throw new Error("Operation aborted");
530
+ throw new AbortError();
523
531
  }
524
532
  const progress = this.findWithProgress(id, { signal });
525
533
  if ("subscribe" in progress) {
@@ -1,3 +1,25 @@
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
+ }
16
+ /**
17
+ * Detects if candidate `Error` is an `AbortError` or AbortError-like.
18
+ * @remarks
19
+ * - This method detects if an error is AbortError-like (for which there could be many implementations)
20
+ * - AbortController spec defines AbortError as DOMException or Error with `name === 'AbortError'`.
21
+ */
22
+ export declare const isAbortErrorLike: (candidate: unknown) => boolean;
1
23
  /**
2
24
  * Wraps a Promise and causes it to reject when the signal is aborted.
3
25
  *
@@ -1 +1 @@
1
- {"version":3,"file":"abortable.d.ts","sourceRoot":"","sources":["../../src/helpers/abortable.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AAEH,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
+ {"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;;;;;GAKG;AACH,eAAO,MAAM,gBAAgB,GAAI,WAAW,OAAO,KAAG,OAQrD,CAAA;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,33 @@
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
+ }
18
+ /**
19
+ * Detects if candidate `Error` is an `AbortError` or AbortError-like.
20
+ * @remarks
21
+ * - This method detects if an error is AbortError-like (for which there could be many implementations)
22
+ * - AbortController spec defines AbortError as DOMException or Error with `name === 'AbortError'`.
23
+ */
24
+ export const isAbortErrorLike = (candidate) => {
25
+ return (candidate instanceof AbortError ||
26
+ ((candidate instanceof Error ||
27
+ //In some JS environments, DOMException is not defined, and sometimes when defined, it does not extend Error; hence extra checks
28
+ (DOMException && candidate instanceof DOMException)) &&
29
+ candidate.name === "AbortError"));
30
+ };
1
31
  /**
2
32
  * Wraps a Promise and causes it to reject when the signal is aborted.
3
33
  *
@@ -31,7 +61,7 @@ export function abortable(p, signal) {
31
61
  return new Promise((resolve, reject) => {
32
62
  signal?.addEventListener("abort", () => {
33
63
  if (!settled) {
34
- reject(new DOMException("Operation aborted", "AbortError"));
64
+ reject(new AbortError());
35
65
  }
36
66
  }, { once: true });
37
67
  p.then(result => {
@@ -10,6 +10,15 @@ type StorageSubsystemEvents = {
10
10
  numOps: number;
11
11
  numChanges: number;
12
12
  }) => void;
13
+ "doc-compacted": (arg: {
14
+ documentId: DocumentId;
15
+ durationMillis: number;
16
+ }) => void;
17
+ "doc-saved": (arg: {
18
+ documentId: DocumentId;
19
+ durationMillis: number;
20
+ sinceHeads: A.Heads;
21
+ }) => void;
13
22
  };
14
23
  /**
15
24
  * The storage subsystem is responsible for saving and loading Automerge documents to and from
@@ -1 +1 @@
1
- {"version":3,"file":"StorageSubsystem.d.ts","sourceRoot":"","sources":["../../src/storage/StorageSubsystem.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,IAAI,CAAC,EAAE,MAAM,2BAA2B,CAAA;AAIrD,OAAO,EAAE,KAAK,UAAU,EAAE,MAAM,aAAa,CAAA;AAC7C,OAAO,EAAE,uBAAuB,EAAE,MAAM,8BAA8B,CAAA;AACtE,OAAO,EAAyB,SAAS,EAAE,MAAM,YAAY,CAAA;AAG7D,OAAO,EAAE,YAAY,EAAE,MAAM,eAAe,CAAA;AAG5C,KAAK,sBAAsB,GAAG;IAC5B,iBAAiB,EAAE,CAAC,GAAG,EAAE;QACvB,UAAU,EAAE,UAAU,CAAA;QACtB,cAAc,EAAE,MAAM,CAAA;QACtB,MAAM,EAAE,MAAM,CAAA;QACd,UAAU,EAAE,MAAM,CAAA;KACnB,KAAK,IAAI,CAAA;CACX,CAAA;AAED;;;GAGG;AACH,qBAAa,gBAAiB,SAAQ,YAAY,CAAC,sBAAsB,CAAC;;gBAe5D,cAAc,EAAE,uBAAuB;IAK7C,EAAE,IAAI,OAAO,CAAC,SAAS,CAAC;IA2B9B,kCAAkC;IAC5B,IAAI;IACR,iFAAiF;IACjF,SAAS,EAAE,MAAM;IAEjB,yFAAyF;IACzF,GAAG,EAAE,MAAM,GACV,OAAO,CAAC,UAAU,GAAG,SAAS,CAAC;IAKlC,gCAAgC;IAC1B,IAAI;IACR,iFAAiF;IACjF,SAAS,EAAE,MAAM;IAEjB,yFAAyF;IACzF,GAAG,EAAE,MAAM;IAEX,sCAAsC;IACtC,IAAI,EAAE,UAAU,GACf,OAAO,CAAC,IAAI,CAAC;IAKhB,oCAAoC;IAC9B,MAAM;IACV,iFAAiF;IACjF,SAAS,EAAE,MAAM;IAEjB,2FAA2F;IAC3F,GAAG,EAAE,MAAM,GACV,OAAO,CAAC,IAAI,CAAC;IAOhB;;OAEG;IACG,WAAW,CAAC,UAAU,EAAE,UAAU,GAAG,OAAO,CAAC,UAAU,GAAG,IAAI,CAAC;IAgDrE;;OAEG;IACG,OAAO,CAAC,CAAC,EAAE,UAAU,EAAE,UAAU,GAAG,OAAO,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC;IAqBlE;;;;;;OAMG;IACG,OAAO,CAAC,UAAU,EAAE,UAAU,EAAE,GAAG,EAAE,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC;IAezE;;OAEG;IACG,SAAS,CAAC,UAAU,EAAE,UAAU;IAkEhC,aAAa,CACjB,UAAU,EAAE,UAAU,EACtB,SAAS,EAAE,SAAS,GACnB,OAAO,CAAC,CAAC,CAAC,SAAS,GAAG,SAAS,CAAC;IAW7B,aAAa,CACjB,UAAU,EAAE,UAAU,EACtB,SAAS,EAAE,SAAS,EACpB,SAAS,EAAE,CAAC,CAAC,SAAS,GACrB,OAAO,CAAC,IAAI,CAAC;CA8CjB"}
1
+ {"version":3,"file":"StorageSubsystem.d.ts","sourceRoot":"","sources":["../../src/storage/StorageSubsystem.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,IAAI,CAAC,EAAE,MAAM,2BAA2B,CAAA;AAIrD,OAAO,EAAE,KAAK,UAAU,EAAE,MAAM,aAAa,CAAA;AAC7C,OAAO,EAAE,uBAAuB,EAAE,MAAM,8BAA8B,CAAA;AACtE,OAAO,EAAyB,SAAS,EAAE,MAAM,YAAY,CAAA;AAG7D,OAAO,EAAE,YAAY,EAAE,MAAM,eAAe,CAAA;AAG5C,KAAK,sBAAsB,GAAG;IAC5B,iBAAiB,EAAE,CAAC,GAAG,EAAE;QACvB,UAAU,EAAE,UAAU,CAAA;QACtB,cAAc,EAAE,MAAM,CAAA;QACtB,MAAM,EAAE,MAAM,CAAA;QACd,UAAU,EAAE,MAAM,CAAA;KACnB,KAAK,IAAI,CAAA;IACV,eAAe,EAAE,CAAC,GAAG,EAAE;QACrB,UAAU,EAAE,UAAU,CAAA;QACtB,cAAc,EAAE,MAAM,CAAA;KACvB,KAAK,IAAI,CAAA;IACV,WAAW,EAAE,CAAC,GAAG,EAAE;QACjB,UAAU,EAAE,UAAU,CAAA;QACtB,cAAc,EAAE,MAAM,CAAA;QACtB,UAAU,EAAE,CAAC,CAAC,KAAK,CAAA;KACpB,KAAK,IAAI,CAAA;CACX,CAAA;AAED;;;GAGG;AACH,qBAAa,gBAAiB,SAAQ,YAAY,CAAC,sBAAsB,CAAC;;gBAe5D,cAAc,EAAE,uBAAuB;IAK7C,EAAE,IAAI,OAAO,CAAC,SAAS,CAAC;IA2B9B,kCAAkC;IAC5B,IAAI;IACR,iFAAiF;IACjF,SAAS,EAAE,MAAM;IAEjB,yFAAyF;IACzF,GAAG,EAAE,MAAM,GACV,OAAO,CAAC,UAAU,GAAG,SAAS,CAAC;IAKlC,gCAAgC;IAC1B,IAAI;IACR,iFAAiF;IACjF,SAAS,EAAE,MAAM;IAEjB,yFAAyF;IACzF,GAAG,EAAE,MAAM;IAEX,sCAAsC;IACtC,IAAI,EAAE,UAAU,GACf,OAAO,CAAC,IAAI,CAAC;IAKhB,oCAAoC;IAC9B,MAAM;IACV,iFAAiF;IACjF,SAAS,EAAE,MAAM;IAEjB,2FAA2F;IAC3F,GAAG,EAAE,MAAM,GACV,OAAO,CAAC,IAAI,CAAC;IAOhB;;OAEG;IACG,WAAW,CAAC,UAAU,EAAE,UAAU,GAAG,OAAO,CAAC,UAAU,GAAG,IAAI,CAAC;IAgDrE;;OAEG;IACG,OAAO,CAAC,CAAC,EAAE,UAAU,EAAE,UAAU,GAAG,OAAO,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC;IAqBlE;;;;;;OAMG;IACG,OAAO,CAAC,UAAU,EAAE,UAAU,EAAE,GAAG,EAAE,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC;IAezE;;OAEG;IACG,SAAS,CAAC,UAAU,EAAE,UAAU;IAgFhC,aAAa,CACjB,UAAU,EAAE,UAAU,EACtB,SAAS,EAAE,SAAS,GACnB,OAAO,CAAC,CAAC,CAAC,SAAS,GAAG,SAAS,CAAC;IAW7B,aAAa,CACjB,UAAU,EAAE,UAAU,EACtB,SAAS,EAAE,SAAS,EACpB,SAAS,EAAE,CAAC,CAAC,SAAS,GACrB,OAAO,CAAC,IAAI,CAAC;CA8CjB"}
@@ -173,7 +173,16 @@ export class StorageSubsystem extends EventEmitter {
173
173
  * Saves just the incremental changes since the last save.
174
174
  */
175
175
  async #saveIncremental(documentId, doc) {
176
- const binary = A.saveSince(doc, this.#storedHeads.get(documentId) ?? []);
176
+ const sinceHeads = this.#storedHeads.get(documentId) ?? [];
177
+ const start = performance.now();
178
+ const binary = A.saveSince(doc, sinceHeads);
179
+ const end = performance.now();
180
+ console.log("emitting saved");
181
+ this.emit("doc-saved", {
182
+ documentId,
183
+ durationMillis: end - start,
184
+ sinceHeads,
185
+ });
177
186
  if (binary && binary.length > 0) {
178
187
  const key = [documentId, "incremental", keyHash(binary)];
179
188
  this.#log(`Saving incremental ${key} for document ${documentId}`);
@@ -197,7 +206,10 @@ export class StorageSubsystem extends EventEmitter {
197
206
  */
198
207
  async #saveTotal(documentId, doc, sourceChunks) {
199
208
  this.#compacting = true;
209
+ const start = performance.now();
200
210
  const binary = A.save(doc);
211
+ const end = performance.now();
212
+ this.emit("doc-compacted", { documentId, durationMillis: end - start });
201
213
  const snapshotHash = headsHash(A.getHeads(doc));
202
214
  const key = [documentId, "snapshot", snapshotHash];
203
215
  const oldKeys = new Set(sourceChunks.map(c => c.key).filter(k => k[2] !== snapshotHash));
@@ -1 +1 @@
1
- {"version":3,"file":"DocSynchronizer.d.ts","sourceRoot":"","sources":["../../src/synchronizer/DocSynchronizer.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,IAAI,CAAC,EAAE,MAAM,2BAA2B,CAAA;AAGrD,OAAO,EACL,SAAS,EAKV,MAAM,iBAAiB,CAAA;AACxB,OAAO,EAEL,gBAAgB,EAEhB,WAAW,EACX,cAAc,EACd,WAAW,EAEZ,MAAM,wBAAwB,CAAA;AAC/B,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAA;AACpC,OAAO,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAA;AAGhD,KAAK,kBAAkB,GAAG,SAAS,GAAG,KAAK,GAAG,aAAa,GAAG,OAAO,CAAA;AAOrE,UAAU,qBAAqB;IAC7B,MAAM,EAAE,SAAS,CAAC,OAAO,CAAC,CAAA;IAC1B,MAAM,EAAE,MAAM,CAAA;IACd,eAAe,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,OAAO,CAAC,CAAC,CAAC,SAAS,GAAG,SAAS,CAAC,CAAA;CACvE;AAED;;;GAGG;AACH,qBAAa,eAAgB,SAAQ,YAAY;;IAE/C,gBAAgB,SAAM;gBAyBV,EAAE,MAAM,EAAE,MAAM,EAAE,eAAe,EAAE,EAAE,qBAAqB;IAyBtE,IAAI,UAAU,uCAEb;IAED,IAAI,UAAU,qCAEb;IAqID,OAAO,CAAC,MAAM,EAAE,MAAM;IAIhB,SAAS,CAAC,OAAO,EAAE,MAAM,EAAE;IA8DjC,OAAO,CAAC,MAAM,EAAE,MAAM;IAOtB,cAAc,CAAC,OAAO,EAAE,WAAW;IAkBnC,uBAAuB,CAAC,OAAO,EAAE,gBAAgB;IAuBjD,kBAAkB,CAAC,OAAO,EAAE,WAAW,GAAG,cAAc;IA2FxD,OAAO,IAAI;QAAE,KAAK,EAAE,MAAM,EAAE,CAAC;QAAC,IAAI,EAAE;YAAE,MAAM,EAAE,MAAM,CAAC;YAAC,UAAU,EAAE,MAAM,CAAA;SAAE,CAAA;KAAE;CAM7E"}
1
+ {"version":3,"file":"DocSynchronizer.d.ts","sourceRoot":"","sources":["../../src/synchronizer/DocSynchronizer.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,IAAI,CAAC,EAAE,MAAM,2BAA2B,CAAA;AAGrD,OAAO,EACL,SAAS,EAKV,MAAM,iBAAiB,CAAA;AACxB,OAAO,EAEL,gBAAgB,EAEhB,WAAW,EACX,cAAc,EACd,WAAW,EAEZ,MAAM,wBAAwB,CAAA;AAC/B,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAA;AACpC,OAAO,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAA;AAGhD,KAAK,kBAAkB,GAAG,SAAS,GAAG,KAAK,GAAG,aAAa,GAAG,OAAO,CAAA;AAOrE,UAAU,qBAAqB;IAC7B,MAAM,EAAE,SAAS,CAAC,OAAO,CAAC,CAAA;IAC1B,MAAM,EAAE,MAAM,CAAA;IACd,eAAe,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,OAAO,CAAC,CAAC,CAAC,SAAS,GAAG,SAAS,CAAC,CAAA;CACvE;AAED;;;GAGG;AACH,qBAAa,eAAgB,SAAQ,YAAY;;IAE/C,gBAAgB,SAAM;gBAyBV,EAAE,MAAM,EAAE,MAAM,EAAE,eAAe,EAAE,EAAE,qBAAqB;IAyBtE,IAAI,UAAU,uCAEb;IAED,IAAI,UAAU,qCAEb;IA6ID,OAAO,CAAC,MAAM,EAAE,MAAM;IAIhB,SAAS,CAAC,OAAO,EAAE,MAAM,EAAE;IA8DjC,OAAO,CAAC,MAAM,EAAE,MAAM;IAOtB,cAAc,CAAC,OAAO,EAAE,WAAW;IAkBnC,uBAAuB,CAAC,OAAO,EAAE,gBAAgB;IAuBjD,kBAAkB,CAAC,OAAO,EAAE,WAAW,GAAG,cAAc;IA2FxD,OAAO,IAAI;QAAE,KAAK,EAAE,MAAM,EAAE,CAAC;QAAC,IAAI,EAAE;YAAE,MAAM,EAAE,MAAM,CAAC;YAAC,UAAU,EAAE,MAAM,CAAA;SAAE,CAAA;KAAE;CAM7E"}
@@ -121,7 +121,14 @@ export class DocSynchronizer extends Synchronizer {
121
121
  #sendSyncMessage(peerId, doc) {
122
122
  this.#log(`sendSyncMessage ->${peerId}`);
123
123
  this.#withSyncState(peerId, syncState => {
124
+ const start = performance.now();
124
125
  const [newSyncState, message] = A.generateSyncMessage(doc, syncState);
126
+ const end = performance.now();
127
+ this.emit("metrics", {
128
+ type: "generate-sync-message",
129
+ documentId: this.#handle.documentId,
130
+ durationMillis: end - start,
131
+ });
125
132
  if (message) {
126
133
  this.#setSyncState(peerId, newSyncState);
127
134
  const isNew = A.getHeads(doc).length === 0;
@@ -23,6 +23,10 @@ export type DocSyncMetrics = {
23
23
  durationMillis: number;
24
24
  numOps: number;
25
25
  numChanges: number;
26
+ } | {
27
+ type: "generate-sync-message";
28
+ documentId: DocumentId;
29
+ durationMillis: number;
26
30
  } | {
27
31
  type: "doc-denied";
28
32
  documentId: DocumentId;
@@ -1 +1 @@
1
- {"version":3,"file":"Synchronizer.d.ts","sourceRoot":"","sources":["../../src/synchronizer/Synchronizer.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,eAAe,CAAA;AAC5C,OAAO,EACL,eAAe,EACf,cAAc,EACd,WAAW,EACZ,MAAM,wBAAwB,CAAA;AAC/B,OAAO,EAAE,SAAS,EAAE,MAAM,2BAA2B,CAAA;AACrD,OAAO,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,aAAa,CAAA;AAEhD,8BAAsB,YAAa,SAAQ,YAAY,CAAC,kBAAkB,CAAC;IACzE,QAAQ,CAAC,cAAc,CAAC,OAAO,EAAE,WAAW,GAAG,IAAI;CACpD;AAED,MAAM,WAAW,kBAAkB;IACjC,OAAO,EAAE,CAAC,OAAO,EAAE,eAAe,KAAK,IAAI,CAAA;IAC3C,YAAY,EAAE,CAAC,OAAO,EAAE,gBAAgB,KAAK,IAAI,CAAA;IACjD,UAAU,EAAE,CAAC,GAAG,EAAE,cAAc,KAAK,IAAI,CAAA;IACzC,OAAO,EAAE,CAAC,GAAG,EAAE,cAAc,KAAK,IAAI,CAAA;CACvC;AAED,uDAAuD;AACvD,MAAM,WAAW,gBAAgB;IAC/B,MAAM,EAAE,MAAM,CAAA;IACd,UAAU,EAAE,UAAU,CAAA;IACtB,SAAS,EAAE,SAAS,CAAA;CACrB;AAED,MAAM,MAAM,cAAc,GACtB;IACE,IAAI,EAAE,sBAAsB,CAAA;IAC5B,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"}
1
+ {"version":3,"file":"Synchronizer.d.ts","sourceRoot":"","sources":["../../src/synchronizer/Synchronizer.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,eAAe,CAAA;AAC5C,OAAO,EACL,eAAe,EACf,cAAc,EACd,WAAW,EACZ,MAAM,wBAAwB,CAAA;AAC/B,OAAO,EAAE,SAAS,EAAE,MAAM,2BAA2B,CAAA;AACrD,OAAO,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,aAAa,CAAA;AAEhD,8BAAsB,YAAa,SAAQ,YAAY,CAAC,kBAAkB,CAAC;IACzE,QAAQ,CAAC,cAAc,CAAC,OAAO,EAAE,WAAW,GAAG,IAAI;CACpD;AAED,MAAM,WAAW,kBAAkB;IACjC,OAAO,EAAE,CAAC,OAAO,EAAE,eAAe,KAAK,IAAI,CAAA;IAC3C,YAAY,EAAE,CAAC,OAAO,EAAE,gBAAgB,KAAK,IAAI,CAAA;IACjD,UAAU,EAAE,CAAC,GAAG,EAAE,cAAc,KAAK,IAAI,CAAA;IACzC,OAAO,EAAE,CAAC,GAAG,EAAE,cAAc,KAAK,IAAI,CAAA;CACvC;AAED,uDAAuD;AACvD,MAAM,WAAW,gBAAgB;IAC/B,MAAM,EAAE,MAAM,CAAA;IACd,UAAU,EAAE,UAAU,CAAA;IACtB,SAAS,EAAE,SAAS,CAAA;CACrB;AAED,MAAM,MAAM,cAAc,GACtB;IACE,IAAI,EAAE,sBAAsB,CAAA;IAC5B,UAAU,EAAE,UAAU,CAAA;IACtB,cAAc,EAAE,MAAM,CAAA;IACtB,MAAM,EAAE,MAAM,CAAA;IACd,UAAU,EAAE,MAAM,CAAA;CACnB,GACD;IACE,IAAI,EAAE,uBAAuB,CAAA;IAC7B,UAAU,EAAE,UAAU,CAAA;IACtB,cAAc,EAAE,MAAM,CAAA;CACvB,GACD;IACE,IAAI,EAAE,YAAY,CAAA;IAClB,UAAU,EAAE,UAAU,CAAA;CACvB,CAAA"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@automerge/automerge-repo",
3
- "version": "2.4.0-alpha.2",
3
+ "version": "2.5.0-alpha.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": "f963f3a1a36292ac0ea417c1c17d180fd8e78ac5"
62
+ "gitHead": "6d94459eecf708f9c508be7e22c2035abffc03a4"
63
63
  }
package/src/DocHandle.ts CHANGED
@@ -12,6 +12,11 @@ import { headsAreSame } from "./helpers/headsAreSame.js"
12
12
  import { withTimeout } from "./helpers/withTimeout.js"
13
13
  import type { AutomergeUrl, DocumentId, PeerId, UrlHeads } from "./types.js"
14
14
  import { StorageId } from "./storage/types.js"
15
+ import {
16
+ AbortError,
17
+ AbortOptions,
18
+ isAbortErrorLike,
19
+ } from "./helpers/abortable.js"
15
20
 
16
21
  /**
17
22
  * A DocHandle is a wrapper around a single Automerge document that lets us listen for changes and
@@ -170,8 +175,16 @@ export class DocHandle<T> extends EventEmitter<DocHandleEvents<T>> {
170
175
  return this.#machine?.getSnapshot().value
171
176
  }
172
177
 
173
- /** Returns a promise that resolves when the docHandle is in one of the given states */
174
- #statePromise(awaitStates: HandleState | HandleState[]) {
178
+ /**
179
+ * Returns a promise that resolves when the docHandle is in one of the given states
180
+ *
181
+ * @param awaitStates - HandleState or HandleStates to wait for
182
+ * @param signal - Optional AbortSignal to cancel the waiting operation
183
+ */
184
+ #statePromise(
185
+ awaitStates: HandleState | HandleState[],
186
+ options?: AbortOptions
187
+ ) {
175
188
  const awaitStatesArray = Array.isArray(awaitStates)
176
189
  ? awaitStates
177
190
  : [awaitStates]
@@ -179,7 +192,7 @@ export class DocHandle<T> extends EventEmitter<DocHandleEvents<T>> {
179
192
  this.#machine,
180
193
  s => awaitStatesArray.some(state => s.matches(state)),
181
194
  // use a longer delay here so as not to race with other delays
182
- { timeout: this.#timeoutDelay * 2 }
195
+ { timeout: this.#timeoutDelay * 2, ...options }
183
196
  )
184
197
  }
185
198
 
@@ -301,16 +314,29 @@ export class DocHandle<T> extends EventEmitter<DocHandleEvents<T>> {
301
314
  }
302
315
 
303
316
  /**
304
- * @returns a promise that resolves when the document is in one of the given states (if no states
305
- * are passed, when the document is ready)
317
+ * Returns promise that resolves when document is in one of the given states (default is 'ready' state)
306
318
  *
307
319
  * Use this to block until the document handle has finished loading. The async equivalent to
308
320
  * checking `inState()`.
321
+ *
322
+ * @param awaitStates - HandleState or HandleStates to wait for
323
+ * @param signal - Optional AbortSignal to cancel the waiting operation
324
+ * @returns a promise that resolves when the document is in one of the given states (if no states
325
+ * are passed, when the document is ready)
309
326
  */
310
- async whenReady(awaitStates: HandleState[] = ["ready"]) {
327
+ async whenReady(
328
+ awaitStates: HandleState[] = ["ready"],
329
+ options?: AbortOptions
330
+ ) {
311
331
  try {
312
- await withTimeout(this.#statePromise(awaitStates), this.#timeoutDelay)
332
+ await withTimeout(
333
+ this.#statePromise(awaitStates, options),
334
+ this.#timeoutDelay
335
+ )
313
336
  } catch (error) {
337
+ if (isAbortErrorLike(error)) {
338
+ throw new AbortError() //throw new error for stack trace
339
+ }
314
340
  console.log(
315
341
  `error waiting for ${
316
342
  this.documentId
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> & {
@@ -172,6 +172,12 @@ export class Repo extends EventEmitter<RepoEvents> {
172
172
  storageSubsystem.on("document-loaded", event =>
173
173
  this.emit("doc-metrics", { type: "doc-loaded", ...event })
174
174
  )
175
+ storageSubsystem.on("doc-compacted", event =>
176
+ this.emit("doc-metrics", { type: "doc-compacted", ...event })
177
+ )
178
+ storageSubsystem.on("doc-saved", event =>
179
+ this.emit("doc-metrics", { type: "doc-saved", ...event })
180
+ )
175
181
  }
176
182
 
177
183
  this.storageSubsystem = storageSubsystem
@@ -704,7 +710,13 @@ export class Repo extends EventEmitter<RepoEvents> {
704
710
  } catch (error) {
705
711
  progressSignal.notify({
706
712
  state: "failed" as const,
707
- error: error instanceof Error ? error : new Error(String(error)),
713
+ error:
714
+ // In most JS environments DOMException extends Error, but not always, in some environments it's a separate type.
715
+ // Some Node.js DOM polyfills do not always extend the Error
716
+ // Jsdom polyfill doesn't extend Error, whereas happy-dom does.
717
+ error instanceof Error || error instanceof DOMException
718
+ ? error
719
+ : new Error(String(error)),
708
720
  handle: this.#getHandle<T>({ documentId }),
709
721
  })
710
722
  }
@@ -718,7 +730,7 @@ export class Repo extends EventEmitter<RepoEvents> {
718
730
 
719
731
  // Check if already aborted
720
732
  if (signal?.aborted) {
721
- throw new Error("Operation aborted")
733
+ throw new AbortError()
722
734
  }
723
735
 
724
736
  const progress = this.findWithProgress<T>(id, { signal })
@@ -1082,6 +1094,17 @@ export interface DeleteDocumentPayload {
1082
1094
 
1083
1095
  export type DocMetrics =
1084
1096
  | DocSyncMetrics
1097
+ | {
1098
+ type: "doc-compacted"
1099
+ documentId: DocumentId
1100
+ durationMillis: number
1101
+ }
1102
+ | {
1103
+ type: "doc-saved"
1104
+ documentId: DocumentId
1105
+ durationMillis: number
1106
+ sinceHeads: Array<string>
1107
+ }
1085
1108
  | {
1086
1109
  type: "doc-loaded"
1087
1110
  documentId: DocumentId
@@ -1,3 +1,37 @@
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
+
19
+ /**
20
+ * Detects if candidate `Error` is an `AbortError` or AbortError-like.
21
+ * @remarks
22
+ * - This method detects if an error is AbortError-like (for which there could be many implementations)
23
+ * - AbortController spec defines AbortError as DOMException or Error with `name === 'AbortError'`.
24
+ */
25
+ export const isAbortErrorLike = (candidate: unknown): boolean => {
26
+ return (
27
+ candidate instanceof AbortError ||
28
+ ((candidate instanceof Error ||
29
+ //In some JS environments, DOMException is not defined, and sometimes when defined, it does not extend Error; hence extra checks
30
+ (DOMException && candidate instanceof DOMException)) &&
31
+ candidate.name === "AbortError")
32
+ )
33
+ }
34
+
1
35
  /**
2
36
  * Wraps a Promise and causes it to reject when the signal is aborted.
3
37
  *
@@ -26,7 +60,6 @@
26
60
  * before the promise p settles, and settles as p settles otherwise
27
61
  * @throws {DOMException} With name "AbortError" if aborted before p settles
28
62
  */
29
-
30
63
  export function abortable<T>(
31
64
  p: Promise<T>,
32
65
  signal: AbortSignal | undefined
@@ -37,7 +70,7 @@ export function abortable<T>(
37
70
  "abort",
38
71
  () => {
39
72
  if (!settled) {
40
- reject(new DOMException("Operation aborted", "AbortError"))
73
+ reject(new AbortError())
41
74
  }
42
75
  },
43
76
  { once: true }
@@ -17,6 +17,15 @@ type StorageSubsystemEvents = {
17
17
  numOps: number
18
18
  numChanges: number
19
19
  }) => void
20
+ "doc-compacted": (arg: {
21
+ documentId: DocumentId
22
+ durationMillis: number
23
+ }) => void
24
+ "doc-saved": (arg: {
25
+ documentId: DocumentId
26
+ durationMillis: number
27
+ sinceHeads: A.Heads
28
+ }) => void
20
29
  }
21
30
 
22
31
  /**
@@ -224,7 +233,17 @@ export class StorageSubsystem extends EventEmitter<StorageSubsystemEvents> {
224
233
  documentId: DocumentId,
225
234
  doc: A.Doc<unknown>
226
235
  ): Promise<void> {
227
- const binary = A.saveSince(doc, this.#storedHeads.get(documentId) ?? [])
236
+ const sinceHeads = this.#storedHeads.get(documentId) ?? []
237
+ const start = performance.now()
238
+ const binary = A.saveSince(doc, sinceHeads)
239
+ const end = performance.now()
240
+ console.log("emitting saved")
241
+ this.emit("doc-saved", {
242
+ documentId,
243
+ durationMillis: end - start,
244
+ sinceHeads,
245
+ })
246
+
228
247
  if (binary && binary.length > 0) {
229
248
  const key = [documentId, "incremental", keyHash(binary)]
230
249
  this.#log(`Saving incremental ${key} for document ${documentId}`)
@@ -253,7 +272,11 @@ export class StorageSubsystem extends EventEmitter<StorageSubsystemEvents> {
253
272
  ): Promise<void> {
254
273
  this.#compacting = true
255
274
 
275
+ const start = performance.now()
256
276
  const binary = A.save(doc)
277
+ const end = performance.now()
278
+ this.emit("doc-compacted", { documentId, durationMillis: end - start })
279
+
257
280
  const snapshotHash = headsHash(A.getHeads(doc))
258
281
  const key = [documentId, "snapshot", snapshotHash]
259
282
  const oldKeys = new Set(
@@ -191,7 +191,15 @@ export class DocSynchronizer extends Synchronizer {
191
191
  this.#log(`sendSyncMessage ->${peerId}`)
192
192
 
193
193
  this.#withSyncState(peerId, syncState => {
194
+ const start = performance.now()
194
195
  const [newSyncState, message] = A.generateSyncMessage(doc, syncState)
196
+ const end = performance.now()
197
+ this.emit("metrics", {
198
+ type: "generate-sync-message",
199
+ documentId: this.#handle.documentId,
200
+ durationMillis: end - start,
201
+ })
202
+
195
203
  if (message) {
196
204
  this.#setSyncState(peerId, newSyncState)
197
205
  const isNew = A.getHeads(doc).length === 0
@@ -33,6 +33,11 @@ export type DocSyncMetrics =
33
33
  numOps: number
34
34
  numChanges: number
35
35
  }
36
+ | {
37
+ type: "generate-sync-message"
38
+ documentId: DocumentId
39
+ durationMillis: number
40
+ }
36
41
  | {
37
42
  type: "doc-denied"
38
43
  documentId: DocumentId
package/test/Repo.test.ts CHANGED
@@ -11,7 +11,12 @@ import {
11
11
  generateAutomergeUrl,
12
12
  stringifyAutomergeUrl,
13
13
  } from "../src/AutomergeUrl.js"
14
- import { FindProgressWithMethods, Repo, ShareConfig } from "../src/Repo.js"
14
+ import {
15
+ DocMetrics,
16
+ FindProgressWithMethods,
17
+ Repo,
18
+ ShareConfig,
19
+ } from "../src/Repo.js"
15
20
  import { eventPromise } from "../src/helpers/eventPromise.js"
16
21
  import { pause } from "../src/helpers/pause.js"
17
22
  import {
@@ -34,6 +39,7 @@ import { getRandomItem } from "./helpers/getRandomItem.js"
34
39
  import { TestDoc } from "./types.js"
35
40
  import { StorageId, StorageKey } from "../src/storage/types.js"
36
41
  import { FindProgress } from "../src/FindProgress.js"
42
+ import { AbortError } from "../src/helpers/abortable.js"
37
43
 
38
44
  describe("Repo", () => {
39
45
  describe("constructor", () => {
@@ -1993,7 +1999,7 @@ describe("Repo.find() abort behavior", () => {
1993
1999
 
1994
2000
  await expect(
1995
2001
  repo.find(generateAutomergeUrl(), { signal: controller.signal })
1996
- ).rejects.toThrow("Operation aborted")
2002
+ ).rejects.toThrow(AbortError)
1997
2003
  })
1998
2004
 
1999
2005
  it("can abort while waiting for ready state", async () => {
@@ -2007,7 +2013,13 @@ describe("Repo.find() abort behavior", () => {
2007
2013
  const findPromise = repo.find(url, { signal: controller.signal })
2008
2014
  controller.abort()
2009
2015
 
2010
- await expect(findPromise).rejects.toThrow("Operation aborted")
2016
+ // Official specification just says to check `reason.name === "AbortError"`
2017
+ // Using AbortError promotes correctness across different JS environments and provides a simpler check.
2018
+ await expect(findPromise).rejects.toThrow(AbortError)
2019
+ await expect(findPromise).rejects.rejects.toHaveProperty(
2020
+ "name",
2021
+ "AbortError"
2022
+ )
2011
2023
  await expect(findPromise).rejects.not.toThrow("unavailable")
2012
2024
  })
2013
2025
 
@@ -2032,7 +2044,7 @@ describe("Repo.find() abort behavior", () => {
2032
2044
  it("creates a document with the custom ID", async () => {
2033
2045
  const id = new Uint8Array("custom-id".split("").map(c => c.charCodeAt(0)))
2034
2046
  const repo = new Repo({
2035
- idFactory: () => id,
2047
+ idFactory: async () => id,
2036
2048
  })
2037
2049
  const handle = await repo.create2()
2038
2050
  expect(handle.documentId).toBe("9HUp4wuzRMx9MRvN4x")
@@ -2042,7 +2054,7 @@ describe("Repo.find() abort behavior", () => {
2042
2054
  const id = new Uint8Array("custom-id".split("").map(c => c.charCodeAt(0)))
2043
2055
  let calledHeads: Heads | null = null
2044
2056
  const repo = new Repo({
2045
- idFactory: (heads: Heads) => {
2057
+ idFactory: async (heads: Heads) => {
2046
2058
  calledHeads = heads
2047
2059
  return id
2048
2060
  },
@@ -2056,7 +2068,7 @@ describe("Repo.find() abort behavior", () => {
2056
2068
  const [aliceToBob, bobToAlice] = DummyNetworkAdapter.createConnectedPair()
2057
2069
  const alice = new Repo({
2058
2070
  peerId: "alice" as PeerId,
2059
- idFactory: () =>
2071
+ idFactory: async () =>
2060
2072
  new Uint8Array("custom-id".split("").map(c => c.charCodeAt(0))),
2061
2073
  network: [aliceToBob],
2062
2074
  })
@@ -2071,6 +2083,166 @@ describe("Repo.find() abort behavior", () => {
2071
2083
  assert.deepStrictEqual(bobHandle.doc(), { foo: "bar" })
2072
2084
  })
2073
2085
  })
2086
+
2087
+ describe("emitted metrics", () => {
2088
+ async function setup(): Promise<{ alice: Repo; bob: Repo }> {
2089
+ const [aliceToBob, bobToAlice] = DummyNetworkAdapter.createConnectedPair()
2090
+ const alice = new Repo({
2091
+ peerId: "alice" as PeerId,
2092
+ network: [aliceToBob],
2093
+ })
2094
+ const bob = new Repo({ peerId: "bob" as PeerId, network: [bobToAlice] })
2095
+ aliceToBob.peerCandidate("bob" as PeerId)
2096
+ bobToAlice.peerCandidate("alice" as PeerId)
2097
+
2098
+ await pause(50)
2099
+
2100
+ return { alice, bob }
2101
+ }
2102
+
2103
+ it("should emit events for receive sync message", async () => {
2104
+ const { alice, bob } = await setup()
2105
+
2106
+ const bobEvents: DocMetrics[] = []
2107
+ bob.on("doc-metrics", e => {
2108
+ if (e.type === "receive-sync-message") {
2109
+ bobEvents.push(e)
2110
+ }
2111
+ })
2112
+
2113
+ const handle = await alice.create2({ foo: "bar" })
2114
+ const bobHandle = await bob.find(handle.url)
2115
+
2116
+ assert.notEqual(bobEvents.length, 0)
2117
+ assert(
2118
+ bobEvents.every(
2119
+ e =>
2120
+ e.type === "receive-sync-message" &&
2121
+ e.documentId == handle.documentId &&
2122
+ e.durationMillis > 0
2123
+ )
2124
+ )
2125
+
2126
+ await Promise.all([bob.shutdown(), alice.shutdown()])
2127
+ })
2128
+
2129
+ it("should emit events for generate sync message", async () => {
2130
+ const { alice, bob } = await setup()
2131
+
2132
+ const bobEvents: DocMetrics[] = []
2133
+ bob.on("doc-metrics", e => {
2134
+ if (e.type === "generate-sync-message") {
2135
+ bobEvents.push(e)
2136
+ }
2137
+ })
2138
+
2139
+ const handle = await alice.create2({ foo: "bar" })
2140
+ const bobHandle = await bob.find(handle.url)
2141
+
2142
+ assert.notEqual(bobEvents.length, 0)
2143
+ assert(
2144
+ bobEvents.every(
2145
+ e =>
2146
+ e.type === "generate-sync-message" &&
2147
+ e.documentId == handle.documentId &&
2148
+ e.durationMillis > 0
2149
+ )
2150
+ )
2151
+
2152
+ await Promise.all([bob.shutdown(), alice.shutdown()])
2153
+ })
2154
+
2155
+ it("should emit events on compaction", async () => {
2156
+ const bob = new Repo({ storage: new DummyStorageAdapter() })
2157
+ // Create a doc and change it enough times to trigger compaction
2158
+ const doc = bob.create({ foo: "bar" })
2159
+
2160
+ const events: DocMetrics[] = []
2161
+ bob.on("doc-metrics", e => {
2162
+ if (e.type === "doc-compacted") {
2163
+ events.push(e)
2164
+ }
2165
+ })
2166
+
2167
+ for (let i = 0; i < 1000; i++) {
2168
+ doc.change(d => {
2169
+ A.splice(d, ["foo"], 0, 1, `${i}`)
2170
+ })
2171
+ }
2172
+
2173
+ await pause(50)
2174
+
2175
+ assert.notEqual(events.length, 0)
2176
+ assert(
2177
+ events.every(
2178
+ e =>
2179
+ e.type === "doc-compacted" &&
2180
+ e.documentId == doc.documentId &&
2181
+ e.durationMillis > 0
2182
+ )
2183
+ )
2184
+
2185
+ await bob.shutdown()
2186
+ })
2187
+
2188
+ it("should emit events on save since", async () => {
2189
+ const bob = new Repo({
2190
+ storage: new DummyStorageAdapter(),
2191
+ saveDebounceRate: 10,
2192
+ })
2193
+
2194
+ const events: DocMetrics[] = []
2195
+ bob.on("doc-metrics", e => {
2196
+ console.log("event: ", e)
2197
+ if (e.type === "doc-saved") {
2198
+ events.push(e)
2199
+ }
2200
+ })
2201
+
2202
+ const doc = bob.create({ foo: "bar" })
2203
+
2204
+ // We have to save, then pause, then save again in order to trigger the
2205
+ // initial compaction and then get to the point where the save actually
2206
+ // triggers incremental saves rather than compactions. This is because the
2207
+ // logic in the storage adapter is designed to initially compact on every
2208
+ // change and only start incremental saves as the document gets a little
2209
+ // larger.
2210
+
2211
+ // First create enough changes to get past the "always compact" threshold
2212
+ for (let i = 0; i < 1000; i++) {
2213
+ doc.change(d => {
2214
+ A.splice(d, ["foo"], 0, 1, `${i}`)
2215
+ })
2216
+ }
2217
+
2218
+ // Wait for the debounced save routine to finish
2219
+ await pause(20)
2220
+
2221
+ // Now trigger some changes which will cause incremental saves
2222
+ for (let i = 0; i < 10; i++) {
2223
+ doc.change(d => {
2224
+ A.splice(d, ["foo"], 0, 1, `${i}`)
2225
+ })
2226
+ }
2227
+
2228
+ // Wait for the debounced save routine again
2229
+ await pause(20)
2230
+
2231
+ // Now actually test the events we got
2232
+ assert.notEqual(events.length, 0)
2233
+ assert(
2234
+ events.every(
2235
+ e =>
2236
+ e.type === "doc-saved" &&
2237
+ e.documentId == doc.documentId &&
2238
+ e.durationMillis > 0 &&
2239
+ A.hasHeads(doc.doc(), e.sinceHeads)
2240
+ )
2241
+ )
2242
+
2243
+ await bob.shutdown()
2244
+ })
2245
+ })
2074
2246
  })
2075
2247
 
2076
2248
  const warn = console.warn