@automerge/automerge-repo 2.0.0-alpha.1 → 2.0.0-alpha.12

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.
Files changed (39) hide show
  1. package/dist/DocHandle.d.ts +71 -2
  2. package/dist/DocHandle.d.ts.map +1 -1
  3. package/dist/DocHandle.js +116 -2
  4. package/dist/Repo.d.ts +24 -0
  5. package/dist/Repo.d.ts.map +1 -1
  6. package/dist/Repo.js +94 -57
  7. package/dist/entrypoints/fullfat.d.ts +1 -0
  8. package/dist/entrypoints/fullfat.d.ts.map +1 -1
  9. package/dist/entrypoints/fullfat.js +1 -2
  10. package/dist/entrypoints/slim.d.ts +1 -0
  11. package/dist/entrypoints/slim.d.ts.map +1 -1
  12. package/dist/entrypoints/slim.js +2 -0
  13. package/dist/helpers/tests/storage-adapter-tests.d.ts +2 -2
  14. package/dist/helpers/tests/storage-adapter-tests.d.ts.map +1 -1
  15. package/dist/helpers/tests/storage-adapter-tests.js +19 -39
  16. package/dist/storage/StorageSubsystem.d.ts +11 -1
  17. package/dist/storage/StorageSubsystem.d.ts.map +1 -1
  18. package/dist/storage/StorageSubsystem.js +18 -3
  19. package/dist/synchronizer/CollectionSynchronizer.d.ts +13 -0
  20. package/dist/synchronizer/CollectionSynchronizer.d.ts.map +1 -1
  21. package/dist/synchronizer/CollectionSynchronizer.js +13 -6
  22. package/dist/synchronizer/DocSynchronizer.d.ts +7 -0
  23. package/dist/synchronizer/DocSynchronizer.d.ts.map +1 -1
  24. package/dist/synchronizer/DocSynchronizer.js +14 -0
  25. package/dist/synchronizer/Synchronizer.d.ts +8 -0
  26. package/dist/synchronizer/Synchronizer.d.ts.map +1 -1
  27. package/package.json +3 -3
  28. package/src/DocHandle.ts +137 -4
  29. package/src/Repo.ts +123 -56
  30. package/src/entrypoints/fullfat.ts +1 -2
  31. package/src/entrypoints/slim.ts +2 -0
  32. package/src/helpers/tests/storage-adapter-tests.ts +31 -62
  33. package/src/storage/StorageSubsystem.ts +26 -3
  34. package/src/synchronizer/CollectionSynchronizer.ts +23 -6
  35. package/src/synchronizer/DocSynchronizer.ts +15 -0
  36. package/src/synchronizer/Synchronizer.ts +9 -0
  37. package/test/DocHandle.test.ts +141 -0
  38. package/test/Repo.test.ts +73 -0
  39. package/test/StorageSubsystem.test.ts +17 -0
@@ -30,6 +30,13 @@ export declare class DocHandle<T> extends EventEmitter<DocHandleEvents<T>> {
30
30
  * peers. We do not currently have an equivalent `whenSynced()`.
31
31
  */
32
32
  isReady: () => boolean;
33
+ /**
34
+ * @returns true if the document has been unloaded.
35
+ *
36
+ * Unloaded documents are freed from memory but not removed from local storage. It's not currently
37
+ * possible at runtime to reload an unloaded document.
38
+ */
39
+ isUnloaded: () => boolean;
33
40
  /**
34
41
  * @returns true if the document has been marked as deleted.
35
42
  *
@@ -48,7 +55,7 @@ export declare class DocHandle<T> extends EventEmitter<DocHandleEvents<T>> {
48
55
  */
49
56
  inState: (states: HandleState[]) => boolean;
50
57
  /** @hidden */
51
- get state(): "idle" | "loading" | "requesting" | "ready" | "unavailable" | "deleted";
58
+ get state(): "idle" | "loading" | "requesting" | "ready" | "unavailable" | "unloaded" | "deleted";
52
59
  /**
53
60
  * @returns a promise that resolves when the document is in one of the given states (if no states
54
61
  * are passed, when the document is ready)
@@ -86,6 +93,58 @@ export declare class DocHandle<T> extends EventEmitter<DocHandleEvents<T>> {
86
93
  * @returns the current document's heads, or undefined if the document is not ready
87
94
  */
88
95
  heads(): A.Heads | undefined;
96
+ begin(): void;
97
+ /**
98
+ * Creates a fixed "view" of an automerge document at the given point in time represented
99
+ * by the `heads` passed in. The return value is the same type as docSync() and will return
100
+ * undefined if the object hasn't finished loading.
101
+ *
102
+ * @remarks
103
+ * A point-in-time in an automerge document is an *array* of heads since there may be
104
+ * concurrent edits. This API just returns a topologically sorted history of all edits
105
+ * so every previous entry will be (in some sense) before later ones, but the set of all possible
106
+ * history views would be quite large under concurrency (every thing in each branch against each other).
107
+ * There might be a clever way to think about this, but we haven't found it yet, so for now at least
108
+ * we present a single traversable view which excludes concurrency.
109
+ * @returns The individual heads for every change in the document.
110
+ */
111
+ history(): A.Heads[] | undefined;
112
+ /**
113
+ * Creates a fixed "view" of an automerge document at the given point in time represented
114
+ * by the `heads` passed in. The return value is the same type as docSync() and will return
115
+ * undefined if the object hasn't finished loading.
116
+ *
117
+ * @remarks
118
+ * Note that our Typescript types do not consider change over time and the current version
119
+ * of Automerge doesn't check types at runtime, so if you go back to an old set of heads
120
+ * that doesn't match the heads here, Typescript will not save you.
121
+ *
122
+ * @returns An Automerge.Doc<T> at the point in time.
123
+ */
124
+ view(heads: A.Heads): A.Doc<T> | undefined;
125
+ /**
126
+ * Returns a set of Patch operations that will move a materialized document from one state to another
127
+ * if applied.
128
+ *
129
+ * @remarks
130
+ * We allow specifying both a from/to heads or just a single comparison point, in which case
131
+ * the base will be the current document heads.
132
+ *
133
+ * @returns Automerge patches that go from one document state to the other. Use view() to get the full state.
134
+ */
135
+ diff(first: A.Heads, second?: A.Heads): A.Patch[] | undefined;
136
+ /**
137
+ * `metadata(head?)` allows you to look at the metadata for a change
138
+ * this can be used to build history graphs to find commit messages and edit times.
139
+ * this interface.
140
+ *
141
+ * @remarks
142
+ * I'm really not convinced this is the right way to surface this information so
143
+ * I'm leaving this API "hidden".
144
+ *
145
+ * @hidden
146
+ */
147
+ metadata(change?: string): A.DecodedChange | undefined;
89
148
  /**
90
149
  * `update` is called any time we have a new document state; could be
91
150
  * from a local change, a remote change, or a new document from storage.
@@ -148,6 +207,10 @@ export declare class DocHandle<T> extends EventEmitter<DocHandleEvents<T>> {
148
207
  * @hidden
149
208
  * */
150
209
  request(): void;
210
+ /** Called by the repo to free memory used by the document. */
211
+ unload(): void;
212
+ /** Called by the repo to reuse an unloaded handle. */
213
+ reload(): void;
151
214
  /** Called by the repo when the document is deleted. */
152
215
  delete(): void;
153
216
  /**
@@ -158,6 +221,10 @@ export declare class DocHandle<T> extends EventEmitter<DocHandleEvents<T>> {
158
221
  * must have a unique PeerId.
159
222
  */
160
223
  broadcast(message: unknown): void;
224
+ metrics(): {
225
+ numOps: number;
226
+ numChanges: number;
227
+ };
161
228
  }
162
229
  /** @hidden */
163
230
  export type DocHandleOptions<T> = {
@@ -232,11 +299,13 @@ export declare const HandleState: {
232
299
  readonly REQUESTING: "requesting";
233
300
  /** The document is available */
234
301
  readonly READY: "ready";
302
+ /** The document has been unloaded from the handle, to free memory usage */
303
+ readonly UNLOADED: "unloaded";
235
304
  /** The document has been deleted from the repo */
236
305
  readonly DELETED: "deleted";
237
306
  /** The document was not available in storage or from any connected peers */
238
307
  readonly UNAVAILABLE: "unavailable";
239
308
  };
240
309
  export type HandleState = (typeof HandleState)[keyof typeof HandleState];
241
- export declare const IDLE: "idle", LOADING: "loading", REQUESTING: "requesting", READY: "ready", DELETED: "deleted", UNAVAILABLE: "unavailable";
310
+ export declare const IDLE: "idle", LOADING: "loading", REQUESTING: "requesting", READY: "ready", UNLOADED: "unloaded", DELETED: "deleted", UNAVAILABLE: "unavailable";
242
311
  //# sourceMappingURL=DocHandle.d.ts.map
@@ -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
@@ -59,6 +59,9 @@ export class DocHandle extends EventEmitter {
59
59
  this.emit("delete", { handle: this });
60
60
  return { doc: A.init() };
61
61
  }),
62
+ onUnload: assign(() => {
63
+ return { doc: A.init() };
64
+ }),
62
65
  onUnavailable: () => {
63
66
  this.emit("unavailable", { handle: this });
64
67
  },
@@ -71,6 +74,7 @@ export class DocHandle extends EventEmitter {
71
74
  context: { documentId, doc },
72
75
  on: {
73
76
  UPDATE: { actions: "onUpdate" },
77
+ UNLOAD: ".unloaded",
74
78
  DELETE: ".deleted",
75
79
  },
76
80
  states: {
@@ -98,6 +102,12 @@ export class DocHandle extends EventEmitter {
98
102
  on: { DOC_READY: "ready" },
99
103
  },
100
104
  ready: {},
105
+ unloaded: {
106
+ entry: "onUnload",
107
+ on: {
108
+ RELOAD: "loading",
109
+ },
110
+ },
101
111
  deleted: { entry: "onDelete", type: "final" },
102
112
  },
103
113
  });
@@ -113,7 +123,7 @@ export class DocHandle extends EventEmitter {
113
123
  });
114
124
  // Start the machine, and send a create or find event to get things going
115
125
  this.#machine.start();
116
- this.#machine.send({ type: BEGIN });
126
+ this.begin();
117
127
  }
118
128
  // PRIVATE
119
129
  /** Returns the current document, regardless of state */
@@ -172,6 +182,13 @@ export class DocHandle extends EventEmitter {
172
182
  * peers. We do not currently have an equivalent `whenSynced()`.
173
183
  */
174
184
  isReady = () => this.inState(["ready"]);
185
+ /**
186
+ * @returns true if the document has been unloaded.
187
+ *
188
+ * Unloaded documents are freed from memory but not removed from local storage. It's not currently
189
+ * possible at runtime to reload an unloaded document.
190
+ */
191
+ isUnloaded = () => this.inState(["unloaded"]);
175
192
  /**
176
193
  * @returns true if the document has been marked as deleted.
177
194
  *
@@ -253,6 +270,88 @@ export class DocHandle extends EventEmitter {
253
270
  }
254
271
  return A.getHeads(this.#doc);
255
272
  }
273
+ begin() {
274
+ this.#machine.send({ type: BEGIN });
275
+ }
276
+ /**
277
+ * Creates a fixed "view" of an automerge document at the given point in time represented
278
+ * by the `heads` passed in. The return value is the same type as docSync() and will return
279
+ * undefined if the object hasn't finished loading.
280
+ *
281
+ * @remarks
282
+ * A point-in-time in an automerge document is an *array* of heads since there may be
283
+ * concurrent edits. This API just returns a topologically sorted history of all edits
284
+ * so every previous entry will be (in some sense) before later ones, but the set of all possible
285
+ * history views would be quite large under concurrency (every thing in each branch against each other).
286
+ * There might be a clever way to think about this, but we haven't found it yet, so for now at least
287
+ * we present a single traversable view which excludes concurrency.
288
+ * @returns The individual heads for every change in the document.
289
+ */
290
+ history() {
291
+ if (!this.isReady()) {
292
+ return undefined;
293
+ }
294
+ // This just returns all the heads as individual strings.
295
+ return A.topoHistoryTraversal(this.#doc).map(h => [h]);
296
+ }
297
+ /**
298
+ * Creates a fixed "view" of an automerge document at the given point in time represented
299
+ * by the `heads` passed in. The return value is the same type as docSync() and will return
300
+ * undefined if the object hasn't finished loading.
301
+ *
302
+ * @remarks
303
+ * Note that our Typescript types do not consider change over time and the current version
304
+ * of Automerge doesn't check types at runtime, so if you go back to an old set of heads
305
+ * that doesn't match the heads here, Typescript will not save you.
306
+ *
307
+ * @returns An Automerge.Doc<T> at the point in time.
308
+ */
309
+ view(heads) {
310
+ if (!this.isReady()) {
311
+ return undefined;
312
+ }
313
+ return A.view(this.#doc, heads);
314
+ }
315
+ /**
316
+ * Returns a set of Patch operations that will move a materialized document from one state to another
317
+ * if applied.
318
+ *
319
+ * @remarks
320
+ * We allow specifying both a from/to heads or just a single comparison point, in which case
321
+ * the base will be the current document heads.
322
+ *
323
+ * @returns Automerge patches that go from one document state to the other. Use view() to get the full state.
324
+ */
325
+ diff(first, second) {
326
+ if (!this.isReady()) {
327
+ return undefined;
328
+ }
329
+ // We allow only one set of heads to be specified, in which case we use the doc's heads
330
+ const from = second ? first : this.heads() || []; // because we guard above this should always have useful data
331
+ const to = second ? second : first;
332
+ return A.diff(this.#doc, from, to);
333
+ }
334
+ /**
335
+ * `metadata(head?)` allows you to look at the metadata for a change
336
+ * this can be used to build history graphs to find commit messages and edit times.
337
+ * this interface.
338
+ *
339
+ * @remarks
340
+ * I'm really not convinced this is the right way to surface this information so
341
+ * I'm leaving this API "hidden".
342
+ *
343
+ * @hidden
344
+ */
345
+ metadata(change) {
346
+ if (!this.isReady()) {
347
+ return undefined;
348
+ }
349
+ if (!change) {
350
+ change = this.heads()[0];
351
+ }
352
+ // we return undefined instead of null by convention in this API
353
+ return A.inspectChange(this.#doc, change) || undefined;
354
+ }
256
355
  /**
257
356
  * `update` is called any time we have a new document state; could be
258
357
  * from a local change, a remote change, or a new document from storage.
@@ -365,6 +464,14 @@ export class DocHandle extends EventEmitter {
365
464
  if (this.#state === "loading")
366
465
  this.#machine.send({ type: REQUEST });
367
466
  }
467
+ /** Called by the repo to free memory used by the document. */
468
+ unload() {
469
+ this.#machine.send({ type: UNLOAD });
470
+ }
471
+ /** Called by the repo to reuse an unloaded handle. */
472
+ reload() {
473
+ this.#machine.send({ type: RELOAD });
474
+ }
368
475
  /** Called by the repo when the document is deleted. */
369
476
  delete() {
370
477
  this.#machine.send({ type: DELETE });
@@ -382,6 +489,9 @@ export class DocHandle extends EventEmitter {
382
489
  data: encode(message),
383
490
  });
384
491
  }
492
+ metrics() {
493
+ return A.stats(this.#doc);
494
+ }
385
495
  }
386
496
  // STATE MACHINE TYPES & CONSTANTS
387
497
  // state
@@ -397,16 +507,20 @@ export const HandleState = {
397
507
  REQUESTING: "requesting",
398
508
  /** The document is available */
399
509
  READY: "ready",
510
+ /** The document has been unloaded from the handle, to free memory usage */
511
+ UNLOADED: "unloaded",
400
512
  /** The document has been deleted from the repo */
401
513
  DELETED: "deleted",
402
514
  /** The document was not available in storage or from any connected peers */
403
515
  UNAVAILABLE: "unavailable",
404
516
  };
405
- export const { IDLE, LOADING, REQUESTING, READY, DELETED, UNAVAILABLE } = HandleState;
517
+ export const { IDLE, LOADING, REQUESTING, READY, UNLOADED, DELETED, UNAVAILABLE, } = HandleState;
406
518
  const BEGIN = "BEGIN";
407
519
  const REQUEST = "REQUEST";
408
520
  const DOC_READY = "DOC_READY";
409
521
  const UPDATE = "UPDATE";
522
+ const UNLOAD = "UNLOAD";
523
+ const RELOAD = "RELOAD";
410
524
  const DELETE = "DELETE";
411
525
  const TIMEOUT = "TIMEOUT";
412
526
  const DOC_UNAVAILABLE = "DOC_UNAVAILABLE";
package/dist/Repo.d.ts CHANGED
@@ -5,6 +5,8 @@ import { NetworkSubsystem } from "./network/NetworkSubsystem.js";
5
5
  import { StorageAdapterInterface } from "./storage/StorageAdapterInterface.js";
6
6
  import { StorageSubsystem } from "./storage/StorageSubsystem.js";
7
7
  import { StorageId } from "./storage/types.js";
8
+ import { CollectionSynchronizer } from "./synchronizer/CollectionSynchronizer.js";
9
+ import { DocSyncMetrics } from "./synchronizer/Synchronizer.js";
8
10
  import type { AnyDocumentId, DocumentId, PeerId } from "./types.js";
9
11
  /** A Repo is a collection of documents with networking, syncing, and storage capabilities. */
10
12
  /** The `Repo` is the main entry point of this library
@@ -23,6 +25,8 @@ export declare class Repo extends EventEmitter<RepoEvents> {
23
25
  /** The debounce rate is adjustable on the repo. */
24
26
  /** @hidden */
25
27
  saveDebounceRate: number;
28
+ /** @hidden */
29
+ synchronizer: CollectionSynchronizer;
26
30
  /** By default, we share generously with all peers. */
27
31
  /** @hidden */
28
32
  sharePolicy: SharePolicy;
@@ -89,7 +93,19 @@ export declare class Repo extends EventEmitter<RepoEvents> {
89
93
  * @returns Promise<void>
90
94
  */
91
95
  flush(documents?: DocumentId[]): Promise<void>;
96
+ /**
97
+ * Removes a DocHandle from the handleCache.
98
+ * @hidden this API is experimental and may change.
99
+ * @param documentId - documentId of the DocHandle to remove from handleCache, if present in cache.
100
+ * @returns Promise<void>
101
+ */
102
+ removeFromCache(documentId: DocumentId): Promise<void>;
92
103
  shutdown(): Promise<void>;
104
+ metrics(): {
105
+ documents: {
106
+ [key: string]: any;
107
+ };
108
+ };
93
109
  }
94
110
  export interface RepoConfig {
95
111
  /** Our unique identifier */
@@ -127,6 +143,7 @@ export interface RepoEvents {
127
143
  "delete-document": (arg: DeleteDocumentPayload) => void;
128
144
  /** A document was marked as unavailable (we don't have it and none of our peers have it) */
129
145
  "unavailable-document": (arg: DeleteDocumentPayload) => void;
146
+ "doc-metrics": (arg: DocMetrics) => void;
130
147
  }
131
148
  export interface DocumentPayload {
132
149
  handle: DocHandle<any>;
@@ -134,4 +151,11 @@ export interface DocumentPayload {
134
151
  export interface DeleteDocumentPayload {
135
152
  documentId: DocumentId;
136
153
  }
154
+ export type DocMetrics = DocSyncMetrics | {
155
+ type: "doc-loaded";
156
+ documentId: DocumentId;
157
+ durationMillis: number;
158
+ numOps: number;
159
+ numChanges: number;
160
+ };
137
161
  //# sourceMappingURL=Repo.d.ts.map
@@ -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
@@ -2,7 +2,7 @@ import { next as Automerge } from "@automerge/automerge/slim";
2
2
  import debug from "debug";
3
3
  import { EventEmitter } from "eventemitter3";
4
4
  import { generateAutomergeUrl, interpretAsDocumentId, parseAutomergeUrl, } from "./AutomergeUrl.js";
5
- import { DocHandle } from "./DocHandle.js";
5
+ import { DELETED, DocHandle, READY, UNAVAILABLE, UNLOADED, } from "./DocHandle.js";
6
6
  import { RemoteHeadsSubscriptions } from "./RemoteHeadsSubscriptions.js";
7
7
  import { headsAreSame } from "./helpers/headsAreSame.js";
8
8
  import { throttle } from "./helpers/throttle.js";
@@ -30,7 +30,8 @@ export class Repo extends EventEmitter {
30
30
  /** @hidden */
31
31
  saveDebounceRate = 100;
32
32
  #handleCache = {};
33
- #synchronizer;
33
+ /** @hidden */
34
+ synchronizer;
34
35
  /** By default, we share generously with all peers. */
35
36
  /** @hidden */
36
37
  sharePolicy = async () => true;
@@ -44,26 +45,6 @@ export class Repo extends EventEmitter {
44
45
  this.#remoteHeadsGossipingEnabled = enableRemoteHeadsGossiping;
45
46
  this.#log = debug(`automerge-repo:repo`);
46
47
  this.sharePolicy = sharePolicy ?? this.sharePolicy;
47
- // DOC COLLECTION
48
- // The `document` event is fired by the DocCollection any time we create a new document or look
49
- // up a document by ID. We listen for it in order to wire up storage and network synchronization.
50
- this.on("document", async ({ handle }) => {
51
- if (storageSubsystem) {
52
- // Save when the document changes, but no more often than saveDebounceRate.
53
- const saveFn = ({ handle, doc, }) => {
54
- void storageSubsystem.saveDoc(handle.documentId, doc);
55
- };
56
- handle.on("heads-changed", throttle(saveFn, this.saveDebounceRate));
57
- }
58
- handle.on("unavailable", () => {
59
- this.#log("document unavailable", { documentId: handle.documentId });
60
- this.emit("unavailable-document", {
61
- documentId: handle.documentId,
62
- });
63
- });
64
- // Register the document with the synchronizer. This advertises our interest in the document.
65
- this.#synchronizer.addDocument(handle.documentId);
66
- });
67
48
  this.on("delete-document", ({ documentId }) => {
68
49
  // TODO Pass the delete on to the network
69
50
  // synchronizer.removeDocument(documentId)
@@ -75,20 +56,25 @@ export class Repo extends EventEmitter {
75
56
  });
76
57
  // SYNCHRONIZER
77
58
  // The synchronizer uses the network subsystem to keep documents in sync with peers.
78
- this.#synchronizer = new CollectionSynchronizer(this);
59
+ this.synchronizer = new CollectionSynchronizer(this);
79
60
  // When the synchronizer emits messages, send them to peers
80
- this.#synchronizer.on("message", message => {
61
+ this.synchronizer.on("message", message => {
81
62
  this.#log(`sending ${message.type} message to ${message.targetId}`);
82
63
  networkSubsystem.send(message);
83
64
  });
65
+ // Forward metrics from doc synchronizers
66
+ this.synchronizer.on("metrics", event => this.emit("doc-metrics", event));
84
67
  if (this.#remoteHeadsGossipingEnabled) {
85
- this.#synchronizer.on("open-doc", ({ peerId, documentId }) => {
68
+ this.synchronizer.on("open-doc", ({ peerId, documentId }) => {
86
69
  this.#remoteHeadsSubscriptions.subscribePeerToDoc(peerId, documentId);
87
70
  });
88
71
  }
89
72
  // STORAGE
90
73
  // The storage subsystem has access to some form of persistence, and deals with save and loading documents.
91
74
  const storageSubsystem = storage ? new StorageSubsystem(storage) : undefined;
75
+ if (storageSubsystem) {
76
+ storageSubsystem.on("document-loaded", event => this.emit("doc-metrics", { type: "doc-loaded", ...event }));
77
+ }
92
78
  this.storageSubsystem = storageSubsystem;
93
79
  // NETWORK
94
80
  // The network subsystem deals with sending and receiving messages to and from peers.
@@ -113,18 +99,18 @@ export class Repo extends EventEmitter {
113
99
  .catch(err => {
114
100
  console.log("error in share policy", { err });
115
101
  });
116
- this.#synchronizer.addPeer(peerId);
102
+ this.synchronizer.addPeer(peerId);
117
103
  });
118
104
  // When a peer disconnects, remove it from the synchronizer
119
105
  networkSubsystem.on("peer-disconnected", ({ peerId }) => {
120
- this.#synchronizer.removePeer(peerId);
106
+ this.synchronizer.removePeer(peerId);
121
107
  this.#remoteHeadsSubscriptions.removePeer(peerId);
122
108
  });
123
109
  // Handle incoming messages
124
110
  networkSubsystem.on("message", async (msg) => {
125
111
  this.#receiveMessage(msg);
126
112
  });
127
- this.#synchronizer.on("sync-state", message => {
113
+ this.synchronizer.on("sync-state", message => {
128
114
  this.#saveSyncState(message);
129
115
  const handle = this.#handleCache[message.documentId];
130
116
  const { storageId } = this.peerMetadataByPeerId[message.peerId] || {};
@@ -172,6 +158,28 @@ export class Repo extends EventEmitter {
172
158
  });
173
159
  }
174
160
  }
161
+ // The `document` event is fired by the DocCollection any time we create a new document or look
162
+ // up a document by ID. We listen for it in order to wire up storage and network synchronization.
163
+ #registerHandleWithSubsystems(handle) {
164
+ const { storageSubsystem } = this;
165
+ if (storageSubsystem) {
166
+ // Save when the document changes, but no more often than saveDebounceRate.
167
+ const saveFn = ({ handle, doc }) => {
168
+ void storageSubsystem.saveDoc(handle.documentId, doc);
169
+ };
170
+ handle.on("heads-changed", throttle(saveFn, this.saveDebounceRate));
171
+ }
172
+ handle.on("unavailable", () => {
173
+ this.#log("document unavailable", { documentId: handle.documentId });
174
+ this.emit("unavailable-document", {
175
+ documentId: handle.documentId,
176
+ });
177
+ });
178
+ // Register the document with the synchronizer. This advertises our interest in the document.
179
+ this.synchronizer.addDocument(handle.documentId);
180
+ // Preserve the old event in case anyone was using it.
181
+ this.emit("document", { handle });
182
+ }
175
183
  #receiveMessage(message) {
176
184
  switch (message.type) {
177
185
  case "remote-subscription-change":
@@ -188,7 +196,7 @@ export class Repo extends EventEmitter {
188
196
  case "request":
189
197
  case "ephemeral":
190
198
  case "doc-unavailable":
191
- this.#synchronizer.receiveMessage(message).catch(err => {
199
+ this.synchronizer.receiveMessage(message).catch(err => {
192
200
  console.log("error receiving message", { err });
193
201
  });
194
202
  }
@@ -229,7 +237,7 @@ export class Repo extends EventEmitter {
229
237
  }
230
238
  /** Returns a list of all connected peer ids */
231
239
  get peers() {
232
- return this.#synchronizer.peers;
240
+ return this.synchronizer.peers;
233
241
  }
234
242
  getStorageIdOfPeer(peerId) {
235
243
  return this.peerMetadataByPeerId[peerId]?.storageId;
@@ -245,7 +253,7 @@ export class Repo extends EventEmitter {
245
253
  const handle = this.#getHandle({
246
254
  documentId,
247
255
  });
248
- this.emit("document", { handle });
256
+ this.#registerHandleWithSubsystems(handle);
249
257
  handle.update(() => {
250
258
  let nextDoc;
251
259
  if (initialValue) {
@@ -277,7 +285,7 @@ export class Repo extends EventEmitter {
277
285
  clone(clonedHandle) {
278
286
  if (!clonedHandle.isReady()) {
279
287
  throw new Error(`Cloned handle is not yet in ready state.
280
- (Try await handle.waitForReady() first.)`);
288
+ (Try await handle.whenReady() first.)`);
281
289
  }
282
290
  const sourceDoc = clonedHandle.docSync();
283
291
  if (!sourceDoc) {
@@ -314,31 +322,29 @@ export class Repo extends EventEmitter {
314
322
  const handle = this.#getHandle({
315
323
  documentId,
316
324
  });
317
- // Try to load from disk before telling anyone else about it
318
- if (this.storageSubsystem) {
319
- void this.storageSubsystem.loadDoc(handle.documentId).then(loadedDoc => {
320
- if (loadedDoc) {
321
- // uhhhh, sorry if you're reading this because we were lying to the type system
322
- handle.update(() => loadedDoc);
323
- handle.doneLoading();
324
- }
325
- else {
326
- this.networkSubsystem
327
- .whenReady()
328
- .then(() => {
329
- handle.request();
330
- })
331
- .catch(err => {
332
- this.#log("error waiting for network", { err });
333
- });
334
- this.emit("document", { handle });
335
- }
336
- });
337
- }
338
- else {
339
- handle.request();
340
- this.emit("document", { handle });
341
- }
325
+ // Loading & network is going to be asynchronous no matter what,
326
+ // but we want to return the handle immediately.
327
+ const attemptLoad = this.storageSubsystem
328
+ ? this.storageSubsystem.loadDoc(handle.documentId)
329
+ : Promise.resolve(null);
330
+ attemptLoad
331
+ .then(async (loadedDoc) => {
332
+ if (loadedDoc) {
333
+ // uhhhh, sorry if you're reading this because we were lying to the type system
334
+ handle.update(() => loadedDoc);
335
+ handle.doneLoading();
336
+ }
337
+ else {
338
+ // we want to wait for the network subsystem to be ready before
339
+ // we request the document. this prevents entering unavailable during initialization.
340
+ await this.networkSubsystem.whenReady();
341
+ handle.request();
342
+ }
343
+ this.#registerHandleWithSubsystems(handle);
344
+ })
345
+ .catch(err => {
346
+ this.#log("error waiting for network", { err });
347
+ });
342
348
  return handle;
343
349
  }
344
350
  delete(
@@ -415,10 +421,41 @@ export class Repo extends EventEmitter {
415
421
  return this.storageSubsystem.saveDoc(handle.documentId, doc);
416
422
  }));
417
423
  }
424
+ /**
425
+ * Removes a DocHandle from the handleCache.
426
+ * @hidden this API is experimental and may change.
427
+ * @param documentId - documentId of the DocHandle to remove from handleCache, if present in cache.
428
+ * @returns Promise<void>
429
+ */
430
+ async removeFromCache(documentId) {
431
+ if (!this.#handleCache[documentId]) {
432
+ this.#log(`WARN: removeFromCache called but handle not found in handleCache for documentId: ${documentId}`);
433
+ return;
434
+ }
435
+ const handle = this.#getHandle({ documentId });
436
+ const doc = await handle.doc([READY, UNLOADED, DELETED, UNAVAILABLE]);
437
+ if (doc) {
438
+ if (handle.isReady()) {
439
+ handle.unload();
440
+ }
441
+ else {
442
+ this.#log(`WARN: removeFromCache called but handle for documentId: ${documentId} in unexpected state: ${handle.state}`);
443
+ }
444
+ delete this.#handleCache[documentId];
445
+ // TODO: remove document from synchronizer when removeDocument is implemented
446
+ // this.synchronizer.removeDocument(documentId)
447
+ }
448
+ else {
449
+ this.#log(`WARN: removeFromCache called but doc undefined for documentId: ${documentId}`);
450
+ }
451
+ }
418
452
  shutdown() {
419
453
  this.networkSubsystem.adapters.forEach(adapter => {
420
454
  adapter.disconnect();
421
455
  });
422
456
  return this.flush();
423
457
  }
458
+ metrics() {
459
+ return { documents: this.synchronizer.metrics() };
460
+ }
424
461
  }
@@ -1,2 +1,3 @@
1
1
  export * from "../index.js";
2
+ import "@automerge/automerge";
2
3
  //# sourceMappingURL=fullfat.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"fullfat.d.ts","sourceRoot":"","sources":["../../src/entrypoints/fullfat.ts"],"names":[],"mappings":"AAAA,cAAc,aAAa,CAAA"}
1
+ {"version":3,"file":"fullfat.d.ts","sourceRoot":"","sources":["../../src/entrypoints/fullfat.ts"],"names":[],"mappings":"AAAA,cAAc,aAAa,CAAA;AAS3B,OAAO,sBAAsB,CAAA"}