@automerge/automerge-repo 1.2.0 → 2.0.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.
Files changed (40) hide show
  1. package/dist/AutomergeUrl.d.ts +3 -3
  2. package/dist/AutomergeUrl.d.ts.map +1 -1
  3. package/dist/AutomergeUrl.js +5 -1
  4. package/dist/DocHandle.d.ts +11 -10
  5. package/dist/DocHandle.d.ts.map +1 -1
  6. package/dist/DocHandle.js +23 -43
  7. package/dist/Repo.d.ts +1 -1
  8. package/dist/Repo.d.ts.map +1 -1
  9. package/dist/Repo.js +53 -36
  10. package/dist/helpers/DummyNetworkAdapter.d.ts +3 -0
  11. package/dist/helpers/DummyNetworkAdapter.d.ts.map +1 -1
  12. package/dist/helpers/DummyNetworkAdapter.js +24 -5
  13. package/dist/helpers/tests/network-adapter-tests.d.ts.map +1 -1
  14. package/dist/helpers/tests/network-adapter-tests.js +88 -1
  15. package/dist/helpers/throttle.d.ts +1 -1
  16. package/dist/helpers/throttle.js +1 -1
  17. package/dist/network/NetworkAdapter.d.ts +2 -0
  18. package/dist/network/NetworkAdapter.d.ts.map +1 -1
  19. package/dist/network/NetworkAdapterInterface.d.ts +2 -2
  20. package/dist/network/NetworkAdapterInterface.d.ts.map +1 -1
  21. package/dist/network/NetworkSubsystem.d.ts +5 -2
  22. package/dist/network/NetworkSubsystem.d.ts.map +1 -1
  23. package/dist/network/NetworkSubsystem.js +21 -25
  24. package/package.json +3 -3
  25. package/src/AutomergeUrl.ts +6 -6
  26. package/src/DocHandle.ts +27 -57
  27. package/src/Repo.ts +55 -40
  28. package/src/helpers/DummyNetworkAdapter.ts +29 -5
  29. package/src/helpers/tests/network-adapter-tests.ts +121 -1
  30. package/src/helpers/throttle.ts +1 -1
  31. package/src/network/NetworkAdapter.ts +3 -0
  32. package/src/network/NetworkAdapterInterface.ts +4 -3
  33. package/src/network/NetworkSubsystem.ts +24 -31
  34. package/test/AutomergeUrl.test.ts +4 -0
  35. package/test/DocHandle.test.ts +20 -24
  36. package/test/DocSynchronizer.test.ts +5 -1
  37. package/test/NetworkSubsystem.test.ts +107 -0
  38. package/test/Repo.test.ts +37 -15
  39. package/test/remoteHeads.test.ts +3 -3
  40. package/test/Network.test.ts +0 -14
@@ -16,9 +16,9 @@ export declare const stringifyAutomergeUrl: (arg: UrlOptions | DocumentId | Bina
16
16
  * Given a string, returns true if it is a valid Automerge URL. This function also acts as a type
17
17
  * discriminator in Typescript.
18
18
  */
19
- export declare const isValidAutomergeUrl: (str: string | undefined | null) => str is AutomergeUrl;
20
- export declare const isValidDocumentId: (str: string) => str is DocumentId;
21
- export declare const isValidUuid: (str: string) => str is LegacyDocumentId;
19
+ export declare const isValidAutomergeUrl: (str: unknown) => str is AutomergeUrl;
20
+ export declare const isValidDocumentId: (str: unknown) => str is DocumentId;
21
+ export declare const isValidUuid: (str: unknown) => str is LegacyDocumentId;
22
22
  /**
23
23
  * Returns a new Automerge URL with a random UUID documentId. Called by Repo.create(), and also used by tests.
24
24
  */
@@ -1 +1 @@
1
- {"version":3,"file":"AutomergeUrl.d.ts","sourceRoot":"","sources":["../src/AutomergeUrl.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,gBAAgB,EAChB,YAAY,EACZ,gBAAgB,EAChB,UAAU,EACV,aAAa,EACd,MAAM,YAAY,CAAA;AAInB,eAAO,MAAM,SAAS,eAAe,CAAA;AAErC,sGAAsG;AACtG,eAAO,MAAM,iBAAiB,QAAS,YAAY;IAQ/C,2BAA2B;;IAE3B,yBAAyB;;CAG5B,CAAA;AAED;;;GAGG;AACH,eAAO,MAAM,qBAAqB,QAC3B,UAAU,GAAG,UAAU,GAAG,gBAAgB,KAmBL,YAC3C,CAAA;AAED;;;GAGG;AACH,eAAO,MAAM,mBAAmB,QACzB,MAAM,GAAG,SAAS,GAAG,IAAI,KAC7B,GAAG,IAAI,YAST,CAAA;AAED,eAAO,MAAM,iBAAiB,QAAS,MAAM,KAAG,GAAG,IAAI,UAQtD,CAAA;AAED,eAAO,MAAM,WAAW,QAAS,MAAM,KAAG,GAAG,IAAI,gBAC7B,CAAA;AAEpB;;GAEG;AACH,eAAO,MAAM,oBAAoB,QAAO,YAGvC,CAAA;AAED,eAAO,MAAM,kBAAkB,UAAW,UAAU,KACjB,gBAAgB,GAAG,SAAS,CAAA;AAE/D,eAAO,MAAM,kBAAkB,UAAW,gBAAgB,KAC7B,UAAU,CAAA;AAEvC,eAAO,MAAM,eAAe,QAAS,MAAM,6BAI1C,CAAA;AAED;;;;;;;;;;GAUG;AACH,eAAO,MAAM,qBAAqB,OAAQ,aAAa,eAqBtD,CAAA;AAID,KAAK,UAAU,GAAG;IAChB,UAAU,EAAE,UAAU,GAAG,gBAAgB,CAAA;CAC1C,CAAA"}
1
+ {"version":3,"file":"AutomergeUrl.d.ts","sourceRoot":"","sources":["../src/AutomergeUrl.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,gBAAgB,EAChB,YAAY,EACZ,gBAAgB,EAChB,UAAU,EACV,aAAa,EACd,MAAM,YAAY,CAAA;AAInB,eAAO,MAAM,SAAS,eAAe,CAAA;AAErC,sGAAsG;AACtG,eAAO,MAAM,iBAAiB,QAAS,YAAY;IAQ/C,2BAA2B;;IAE3B,yBAAyB;;CAG5B,CAAA;AAED;;;GAGG;AACH,eAAO,MAAM,qBAAqB,QAC3B,UAAU,GAAG,UAAU,GAAG,gBAAgB,KAmBL,YAC3C,CAAA;AAED;;;GAGG;AACH,eAAO,MAAM,mBAAmB,QAAS,OAAO,KAAG,GAAG,IAAI,YAUzD,CAAA;AAED,eAAO,MAAM,iBAAiB,QAAS,OAAO,KAAG,GAAG,IAAI,UASvD,CAAA;AAED,eAAO,MAAM,WAAW,QAAS,OAAO,KAAG,GAAG,IAAI,gBACH,CAAA;AAE/C;;GAEG;AACH,eAAO,MAAM,oBAAoB,QAAO,YAGvC,CAAA;AAED,eAAO,MAAM,kBAAkB,UAAW,UAAU,KACjB,gBAAgB,GAAG,SAAS,CAAA;AAE/D,eAAO,MAAM,kBAAkB,UAAW,gBAAgB,KAC7B,UAAU,CAAA;AAEvC,eAAO,MAAM,eAAe,QAAS,MAAM,6BAI1C,CAAA;AAED;;;;;;;;;;GAUG;AACH,eAAO,MAAM,qBAAqB,OAAQ,aAAa,eAqBtD,CAAA;AAID,KAAK,UAAU,GAAG;IAChB,UAAU,EAAE,UAAU,GAAG,gBAAgB,CAAA;CAC1C,CAAA"}
@@ -40,6 +40,8 @@ export const stringifyAutomergeUrl = (arg) => {
40
40
  * discriminator in Typescript.
41
41
  */
42
42
  export const isValidAutomergeUrl = (str) => {
43
+ if (typeof str !== "string")
44
+ return false;
43
45
  if (!str || !str.startsWith(urlPrefix))
44
46
  return false;
45
47
  const automergeUrl = str;
@@ -52,6 +54,8 @@ export const isValidAutomergeUrl = (str) => {
52
54
  }
53
55
  };
54
56
  export const isValidDocumentId = (str) => {
57
+ if (typeof str !== "string")
58
+ return false;
55
59
  // try to decode from base58
56
60
  const binaryDocumentID = documentIdToBinary(str);
57
61
  if (binaryDocumentID === undefined)
@@ -60,7 +64,7 @@ export const isValidDocumentId = (str) => {
60
64
  const documentId = Uuid.stringify(binaryDocumentID);
61
65
  return Uuid.validate(documentId);
62
66
  };
63
- export const isValidUuid = (str) => Uuid.validate(str);
67
+ export const isValidUuid = (str) => typeof str === "string" && Uuid.validate(str);
64
68
  /**
65
69
  * Returns a new Automerge URL with a random UUID documentId. Called by Repo.create(), and also used by tests.
66
70
  */
@@ -48,7 +48,7 @@ export declare class DocHandle<T> extends EventEmitter<DocHandleEvents<T>> {
48
48
  */
49
49
  inState: (states: HandleState[]) => boolean;
50
50
  /** @hidden */
51
- get state(): "idle" | "ready" | "loading" | "requesting" | "awaitingNetwork" | "unavailable" | "deleted";
51
+ get state(): "idle" | "loading" | "requesting" | "ready" | "unavailable" | "deleted";
52
52
  /**
53
53
  * @returns a promise that resolves when the document is in one of the given states (if no states
54
54
  * are passed, when the document is ready)
@@ -87,11 +87,18 @@ export declare class DocHandle<T> extends EventEmitter<DocHandleEvents<T>> {
87
87
  */
88
88
  heads(): A.Heads | undefined;
89
89
  /**
90
- * `update` is called by the repo when we receive changes from the network
91
- * Called by the repo when we receive changes from the network.
90
+ * `update` is called any time we have a new document state; could be
91
+ * from a local change, a remote change, or a new document from storage.
92
+ * Does not cause state changes.
92
93
  * @hidden
93
94
  */
94
95
  update(callback: (doc: A.Doc<T>) => A.Doc<T>): void;
96
+ /**
97
+ * `doneLoading` is called by the repo after it decides it has all the changes
98
+ * it's going to get during setup. This might mean it was created locally,
99
+ * or that it was loaded from storage, or that it was received from a peer.
100
+ */
101
+ doneLoading(): void;
95
102
  /**
96
103
  * Called by the repo either when a doc handle changes or we receive new remote heads.
97
104
  * @hidden
@@ -141,10 +148,6 @@ export declare class DocHandle<T> extends EventEmitter<DocHandleEvents<T>> {
141
148
  * @hidden
142
149
  * */
143
150
  request(): void;
144
- /** @hidden */
145
- awaitNetwork(): void;
146
- /** @hidden */
147
- networkReady(): void;
148
151
  /** Called by the repo when the document is deleted. */
149
152
  delete(): void;
150
153
  /**
@@ -225,8 +228,6 @@ export declare const HandleState: {
225
228
  readonly IDLE: "idle";
226
229
  /** We are waiting for storage to finish loading */
227
230
  readonly LOADING: "loading";
228
- /** We are waiting for the network to be come ready */
229
- readonly AWAITING_NETWORK: "awaitingNetwork";
230
231
  /** We are waiting for someone in the network to respond to a sync request */
231
232
  readonly REQUESTING: "requesting";
232
233
  /** The document is available */
@@ -237,5 +238,5 @@ export declare const HandleState: {
237
238
  readonly UNAVAILABLE: "unavailable";
238
239
  };
239
240
  export type HandleState = (typeof HandleState)[keyof typeof HandleState];
240
- export declare const IDLE: "idle", LOADING: "loading", AWAITING_NETWORK: "awaitingNetwork", REQUESTING: "requesting", READY: "ready", DELETED: "deleted", UNAVAILABLE: "unavailable";
241
+ export declare const IDLE: "idle", LOADING: "loading", REQUESTING: "requesting", READY: "ready", DELETED: "deleted", UNAVAILABLE: "unavailable";
241
242
  //# 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
@@ -25,7 +25,7 @@ export class DocHandle extends EventEmitter {
25
25
  /** The XState actor running our state machine. */
26
26
  #machine;
27
27
  /** The last known state of our document. */
28
- #prevDocState;
28
+ #prevDocState = A.init();
29
29
  /** How long to wait before giving up on a document. (Note that a document will be marked
30
30
  * unavailable much sooner if all known peers respond that they don't have it.) */
31
31
  #timeoutDelay = 60_000;
@@ -38,18 +38,7 @@ export class DocHandle extends EventEmitter {
38
38
  if ("timeoutDelay" in options && options.timeoutDelay) {
39
39
  this.#timeoutDelay = options.timeoutDelay;
40
40
  }
41
- let doc;
42
- const isNew = "isNew" in options && options.isNew;
43
- if (isNew) {
44
- // T should really be constrained to extend `Record<string, unknown>` (an automerge doc can't be
45
- // e.g. a primitive, an array, etc. - it must be an object). But adding that constraint creates
46
- // a bunch of other problems elsewhere so for now we'll just cast it here to make Automerge happy.
47
- doc = A.from(options.initialValue);
48
- doc = A.emptyChange(doc);
49
- }
50
- else {
51
- doc = A.init();
52
- }
41
+ const doc = A.init();
53
42
  this.#log = debug(`automerge-repo:dochandle:${this.documentId.slice(0, 5)}`);
54
43
  const delay = this.#timeoutDelay;
55
44
  const machine = setup({
@@ -68,7 +57,7 @@ export class DocHandle extends EventEmitter {
68
57
  }),
69
58
  onDelete: assign(() => {
70
59
  this.emit("delete", { handle: this });
71
- return { doc: undefined };
60
+ return { doc: A.init() };
72
61
  }),
73
62
  onUnavailable: () => {
74
63
  this.emit("unavailable", { handle: this });
@@ -87,21 +76,16 @@ export class DocHandle extends EventEmitter {
87
76
  states: {
88
77
  idle: {
89
78
  on: {
90
- CREATE: "ready",
91
- FIND: "loading",
79
+ BEGIN: "loading",
92
80
  },
93
81
  },
94
82
  loading: {
95
83
  on: {
96
84
  REQUEST: "requesting",
97
85
  DOC_READY: "ready",
98
- AWAIT_NETWORK: "awaitingNetwork",
99
86
  },
100
87
  after: { [delay]: "unavailable" },
101
88
  },
102
- awaitingNetwork: {
103
- on: { NETWORK_READY: "requesting" },
104
- },
105
89
  requesting: {
106
90
  on: {
107
91
  DOC_UNAVAILABLE: "unavailable",
@@ -129,7 +113,7 @@ export class DocHandle extends EventEmitter {
129
113
  });
130
114
  // Start the machine, and send a create or find event to get things going
131
115
  this.#machine.start();
132
- this.#machine.send(isNew ? { type: CREATE } : { type: FIND });
116
+ this.#machine.send({ type: BEGIN });
133
117
  }
134
118
  // PRIVATE
135
119
  /** Returns the current document, regardless of state */
@@ -154,10 +138,12 @@ export class DocHandle extends EventEmitter {
154
138
  * received the document for the first time, signal that our request has been completed.
155
139
  */
156
140
  #checkForChanges(before, after) {
157
- const docChanged = after && before && !headsAreSame(A.getHeads(after), A.getHeads(before));
141
+ const beforeHeads = A.getHeads(before);
142
+ const afterHeads = A.getHeads(after);
143
+ const docChanged = !headsAreSame(afterHeads, beforeHeads);
158
144
  if (docChanged) {
159
145
  this.emit("heads-changed", { handle: this, doc: after });
160
- const patches = A.diff(after, A.getHeads(before), A.getHeads(after));
146
+ const patches = A.diff(after, beforeHeads, afterHeads);
161
147
  if (patches.length > 0) {
162
148
  this.emit("change", {
163
149
  handle: this,
@@ -268,13 +254,22 @@ export class DocHandle extends EventEmitter {
268
254
  return A.getHeads(this.#doc);
269
255
  }
270
256
  /**
271
- * `update` is called by the repo when we receive changes from the network
272
- * Called by the repo when we receive changes from the network.
257
+ * `update` is called any time we have a new document state; could be
258
+ * from a local change, a remote change, or a new document from storage.
259
+ * Does not cause state changes.
273
260
  * @hidden
274
261
  */
275
262
  update(callback) {
276
263
  this.#machine.send({ type: UPDATE, payload: { callback } });
277
264
  }
265
+ /**
266
+ * `doneLoading` is called by the repo after it decides it has all the changes
267
+ * it's going to get during setup. This might mean it was created locally,
268
+ * or that it was loaded from storage, or that it was received from a peer.
269
+ */
270
+ doneLoading() {
271
+ this.#machine.send({ type: DOC_READY });
272
+ }
278
273
  /**
279
274
  * Called by the repo either when a doc handle changes or we receive new remote heads.
280
275
  * @hidden
@@ -304,7 +299,7 @@ export class DocHandle extends EventEmitter {
304
299
  */
305
300
  change(callback, options = {}) {
306
301
  if (!this.isReady()) {
307
- throw new Error(`DocHandle#${this.documentId} is not ready. Check \`handle.isReady()\` before accessing the document.`);
302
+ throw new Error(`DocHandle#${this.documentId} is in ${this.state} and not ready. Check \`handle.isReady()\` before accessing the document.`);
308
303
  }
309
304
  this.#machine.send({
310
305
  type: UPDATE,
@@ -370,16 +365,6 @@ export class DocHandle extends EventEmitter {
370
365
  if (this.#state === "loading")
371
366
  this.#machine.send({ type: REQUEST });
372
367
  }
373
- /** @hidden */
374
- awaitNetwork() {
375
- if (this.#state === "loading")
376
- this.#machine.send({ type: AWAIT_NETWORK });
377
- }
378
- /** @hidden */
379
- networkReady() {
380
- if (this.#state === "awaitingNetwork")
381
- this.#machine.send({ type: NETWORK_READY });
382
- }
383
368
  /** Called by the repo when the document is deleted. */
384
369
  delete() {
385
370
  this.#machine.send({ type: DELETE });
@@ -408,8 +393,6 @@ export const HandleState = {
408
393
  IDLE: "idle",
409
394
  /** We are waiting for storage to finish loading */
410
395
  LOADING: "loading",
411
- /** We are waiting for the network to be come ready */
412
- AWAITING_NETWORK: "awaitingNetwork",
413
396
  /** We are waiting for someone in the network to respond to a sync request */
414
397
  REQUESTING: "requesting",
415
398
  /** The document is available */
@@ -419,13 +402,10 @@ export const HandleState = {
419
402
  /** The document was not available in storage or from any connected peers */
420
403
  UNAVAILABLE: "unavailable",
421
404
  };
422
- export const { IDLE, LOADING, AWAITING_NETWORK, REQUESTING, READY, DELETED, UNAVAILABLE, } = HandleState;
423
- const CREATE = "CREATE";
424
- const FIND = "FIND";
405
+ export const { IDLE, LOADING, REQUESTING, READY, DELETED, UNAVAILABLE } = HandleState;
406
+ const BEGIN = "BEGIN";
425
407
  const REQUEST = "REQUEST";
426
408
  const DOC_READY = "DOC_READY";
427
- const AWAIT_NETWORK = "AWAIT_NETWORK";
428
- const NETWORK_READY = "NETWORK_READY";
429
409
  const UPDATE = "UPDATE";
430
410
  const DELETE = "DELETE";
431
411
  const TIMEOUT = "TIMEOUT";
package/dist/Repo.d.ts CHANGED
@@ -89,6 +89,7 @@ export declare class Repo extends EventEmitter<RepoEvents> {
89
89
  * @returns Promise<void>
90
90
  */
91
91
  flush(documents?: DocumentId[]): Promise<void>;
92
+ shutdown(): Promise<void>;
92
93
  }
93
94
  export interface RepoConfig {
94
95
  /** Our unique identifier */
@@ -129,7 +130,6 @@ export interface RepoEvents {
129
130
  }
130
131
  export interface DocumentPayload {
131
132
  handle: DocHandle<any>;
132
- isNew: boolean;
133
133
  }
134
134
  export interface DeleteDocumentPayload {
135
135
  documentId: DocumentId;
@@ -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":"AAEA,OAAO,EAAE,YAAY,EAAE,MAAM,eAAe,CAAA;AAM5C,OAAO,EAAE,SAAS,EAAiC,MAAM,gBAAgB,CAAA;AAIzE,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;AAG9C,OAAO,KAAK,EAAE,aAAa,EAAE,UAAU,EAAE,MAAM,EAAE,MAAM,YAAY,CAAA;AAMnE,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;IAEnC,mDAAmD;IACnD,cAAc;IACd,gBAAgB,SAAM;IAMtB,sDAAsD;IACtD,cAAc;IACd,WAAW,EAAE,WAAW,CAAmB;IAE3C,8GAA8G;IAC9G,cAAc;IACd,oBAAoB,EAAE,MAAM,CAAC,MAAM,EAAE,YAAY,CAAC,CAAK;gBAK3C,EACV,OAAO,EACP,OAAY,EACZ,MAAuB,EACvB,WAAW,EACX,WAAmC,EACnC,0BAAkC,GACnC,GAAE,UAAe;IAwPlB,8CAA8C;IAC9C,IAAI,OAAO,uCAEV;IAED,+CAA+C;IAC/C,IAAI,KAAK,IAAI,MAAM,EAAE,CAEpB;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;IAuBzC;;;;;;;;;;;;;;OAcG;IACH,KAAK,CAAC,CAAC,EAAE,YAAY,EAAE,SAAS,CAAC,CAAC,CAAC;IAuBnC;;;OAGG;IACH,IAAI,CAAC,CAAC;IACJ,sDAAsD;IACtD,EAAE,EAAE,aAAa,GAChB,SAAS,CAAC,CAAC,CAAC;IA+Cf,MAAM;IACJ,oDAAoD;IACpD,EAAE,EAAE,aAAa;IAWnB;;;;;;OAMG;IACG,MAAM,CAAC,EAAE,EAAE,aAAa,GAAG,OAAO,CAAC,UAAU,GAAG,SAAS,CAAC;IAShE;;;OAGG;IACH,MAAM,CAAC,CAAC,EAAE,MAAM,EAAE,UAAU;IAY5B,kBAAkB,YAAa,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;IAkBpD,QAAQ,IAAI,OAAO,CAAC,IAAI,CAAC;CAM1B;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;;OAEG;IACH,0BAA0B,CAAC,EAAE,OAAO,CAAA;CACrC;AAED;;;;;;;KAOK;AACL,MAAM,MAAM,WAAW,GAAG,CACxB,MAAM,EAAE,MAAM,EACd,UAAU,CAAC,EAAE,UAAU,KACpB,OAAO,CAAC,OAAO,CAAC,CAAA;AAGrB,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;CAC7D;AAED,MAAM,WAAW,eAAe;IAC9B,MAAM,EAAE,SAAS,CAAC,GAAG,CAAC,CAAA;CACvB;AAED,MAAM,WAAW,qBAAqB;IACpC,UAAU,EAAE,UAAU,CAAA;CACvB"}
package/dist/Repo.js CHANGED
@@ -9,6 +9,9 @@ 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
+ function randomPeerId() {
13
+ return ("peer-" + Math.random().toString(36).slice(4));
14
+ }
12
15
  /** A Repo is a collection of documents with networking, syncing, and storage capabilities. */
13
16
  /** The `Repo` is the main entry point of this library
14
17
  *
@@ -36,7 +39,7 @@ export class Repo extends EventEmitter {
36
39
  peerMetadataByPeerId = {};
37
40
  #remoteHeadsSubscriptions = new RemoteHeadsSubscriptions();
38
41
  #remoteHeadsGossipingEnabled = false;
39
- constructor({ storage, network = [], peerId, sharePolicy, isEphemeral = storage === undefined, enableRemoteHeadsGossiping = false, } = {}) {
42
+ constructor({ storage, network = [], peerId = randomPeerId(), sharePolicy, isEphemeral = storage === undefined, enableRemoteHeadsGossiping = false, } = {}) {
40
43
  super();
41
44
  this.#remoteHeadsGossipingEnabled = enableRemoteHeadsGossiping;
42
45
  this.#log = debug(`automerge-repo:repo`);
@@ -44,24 +47,13 @@ export class Repo extends EventEmitter {
44
47
  // DOC COLLECTION
45
48
  // The `document` event is fired by the DocCollection any time we create a new document or look
46
49
  // up a document by ID. We listen for it in order to wire up storage and network synchronization.
47
- this.on("document", async ({ handle, isNew }) => {
50
+ this.on("document", async ({ handle }) => {
48
51
  if (storageSubsystem) {
49
52
  // Save when the document changes, but no more often than saveDebounceRate.
50
53
  const saveFn = ({ handle, doc, }) => {
51
54
  void storageSubsystem.saveDoc(handle.documentId, doc);
52
55
  };
53
56
  handle.on("heads-changed", throttle(saveFn, this.saveDebounceRate));
54
- if (isNew) {
55
- // this is a new document, immediately save it
56
- await storageSubsystem.saveDoc(handle.documentId, handle.docSync());
57
- }
58
- else {
59
- // Try to load from disk
60
- const loadedDoc = await storageSubsystem.loadDoc(handle.documentId);
61
- if (loadedDoc) {
62
- handle.update(() => loadedDoc);
63
- }
64
- }
65
57
  }
66
58
  handle.on("unavailable", () => {
67
59
  this.#log("document unavailable", { documentId: handle.documentId });
@@ -69,20 +61,6 @@ export class Repo extends EventEmitter {
69
61
  documentId: handle.documentId,
70
62
  });
71
63
  });
72
- if (this.networkSubsystem.isReady()) {
73
- handle.request();
74
- }
75
- else {
76
- handle.awaitNetwork();
77
- this.networkSubsystem
78
- .whenReady()
79
- .then(() => {
80
- handle.networkReady();
81
- })
82
- .catch(err => {
83
- this.#log("error waiting for network", { err });
84
- });
85
- }
86
64
  // Register the document with the synchronizer. This advertises our interest in the document.
87
65
  this.#synchronizer.addDocument(handle.documentId);
88
66
  });
@@ -234,14 +212,14 @@ export class Repo extends EventEmitter {
234
212
  handler(payload);
235
213
  }
236
214
  /** Returns an existing handle if we have it; creates one otherwise. */
237
- #getHandle({ documentId, isNew, initialValue, }) {
215
+ #getHandle({ documentId, }) {
238
216
  // If we have the handle cached, return it
239
217
  if (this.#handleCache[documentId])
240
218
  return this.#handleCache[documentId];
241
219
  // If not, create a new handle, cache it, and return it
242
220
  if (!documentId)
243
221
  throw new Error(`Invalid documentId ${documentId}`);
244
- const handle = new DocHandle(documentId, { isNew, initialValue });
222
+ const handle = new DocHandle(documentId);
245
223
  this.#handleCache[documentId] = handle;
246
224
  return handle;
247
225
  }
@@ -266,10 +244,19 @@ export class Repo extends EventEmitter {
266
244
  const { documentId } = parseAutomergeUrl(generateAutomergeUrl());
267
245
  const handle = this.#getHandle({
268
246
  documentId,
269
- isNew: true,
270
- initialValue,
271
247
  });
272
- this.emit("document", { handle, isNew: true });
248
+ this.emit("document", { handle });
249
+ handle.update(() => {
250
+ let nextDoc;
251
+ if (initialValue) {
252
+ nextDoc = Automerge.from(initialValue);
253
+ }
254
+ else {
255
+ nextDoc = Automerge.emptyChange(Automerge.init());
256
+ }
257
+ return nextDoc;
258
+ });
259
+ handle.doneLoading();
273
260
  return handle;
274
261
  }
275
262
  /** Create a new DocHandle by cloning the history of an existing DocHandle.
@@ -323,18 +310,42 @@ export class Repo extends EventEmitter {
323
310
  }
324
311
  return this.#handleCache[documentId];
325
312
  }
313
+ // If we don't already have the handle, make an empty one and try loading it
326
314
  const handle = this.#getHandle({
327
315
  documentId,
328
- isNew: false,
329
316
  });
330
- this.emit("document", { handle, isNew: false });
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
+ }
331
342
  return handle;
332
343
  }
333
344
  delete(
334
345
  /** The url or documentId of the handle to delete */
335
346
  id) {
336
347
  const documentId = interpretAsDocumentId(id);
337
- const handle = this.#getHandle({ documentId, isNew: false });
348
+ const handle = this.#getHandle({ documentId });
338
349
  handle.delete();
339
350
  delete this.#handleCache[documentId];
340
351
  this.emit("delete-document", { documentId });
@@ -348,7 +359,7 @@ export class Repo extends EventEmitter {
348
359
  */
349
360
  async export(id) {
350
361
  const documentId = interpretAsDocumentId(id);
351
- const handle = this.#getHandle({ documentId, isNew: false });
362
+ const handle = this.#getHandle({ documentId });
352
363
  const doc = await handle.doc();
353
364
  if (!doc)
354
365
  return undefined;
@@ -404,4 +415,10 @@ export class Repo extends EventEmitter {
404
415
  return this.storageSubsystem.saveDoc(handle.documentId, doc);
405
416
  }));
406
417
  }
418
+ shutdown() {
419
+ this.networkSubsystem.adapters.forEach(adapter => {
420
+ adapter.disconnect();
421
+ });
422
+ return this.flush();
423
+ }
407
424
  }
@@ -1,6 +1,9 @@
1
1
  import { Message, NetworkAdapter, PeerId } from "../../src/index.js";
2
2
  export declare class DummyNetworkAdapter extends NetworkAdapter {
3
3
  #private;
4
+ isReady(): boolean;
5
+ whenReady(): Promise<void>;
6
+ forceReady(): void;
4
7
  constructor(opts?: Options);
5
8
  connect(peerId: PeerId): void;
6
9
  disconnect(): void;
@@ -1 +1 @@
1
- {"version":3,"file":"DummyNetworkAdapter.d.ts","sourceRoot":"","sources":["../../src/helpers/DummyNetworkAdapter.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,OAAO,EAAE,cAAc,EAAE,MAAM,EAAE,MAAM,oBAAoB,CAAA;AAEpE,qBAAa,mBAAoB,SAAQ,cAAc;;gBAIzC,IAAI,GAAE,OAA8B;IAMhD,OAAO,CAAC,MAAM,EAAE,MAAM;IAOtB,UAAU;IAEV,aAAa,CAAC,MAAM,EAAE,MAAM;IAInB,IAAI,CAAC,OAAO,EAAE,OAAO;IAI9B,OAAO,CAAC,OAAO,EAAE,OAAO;IAIxB,MAAM,CAAC,mBAAmB,CAAC,EAAE,OAAY,EAAE,GAAE;QAAE,OAAO,CAAC,EAAE,MAAM,CAAA;KAAO;CAcvE;AAED,KAAK,aAAa,GAAG,CAAC,OAAO,EAAE,OAAO,KAAK,IAAI,CAAA;AAE/C,KAAK,OAAO,GAAG;IACb,UAAU,CAAC,EAAE,OAAO,CAAA;IACpB,WAAW,CAAC,EAAE,aAAa,CAAA;CAC5B,CAAA"}
1
+ {"version":3,"file":"DummyNetworkAdapter.d.ts","sourceRoot":"","sources":["../../src/helpers/DummyNetworkAdapter.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,OAAO,EAAE,cAAc,EAAE,MAAM,EAAE,MAAM,oBAAoB,CAAA;AAEpE,qBAAa,mBAAoB,SAAQ,cAAc;;IASrD,OAAO;IAIP,SAAS;IAYT,UAAU;gBAIE,IAAI,GAAE,OAA8B;IAQhD,OAAO,CAAC,MAAM,EAAE,MAAM;IAItB,UAAU;IAEV,aAAa,CAAC,MAAM,EAAE,MAAM;IAInB,IAAI,CAAC,OAAO,EAAE,OAAO;IAI9B,OAAO,CAAC,OAAO,EAAE,OAAO;IAIxB,MAAM,CAAC,mBAAmB,CAAC,EAAE,OAAY,EAAE,GAAE;QAAE,OAAO,CAAC,EAAE,MAAM,CAAA;KAAO;CAcvE;AAED,KAAK,aAAa,GAAG,CAAC,OAAO,EAAE,OAAO,KAAK,IAAI,CAAA;AAE/C,KAAK,OAAO,GAAG;IACb,UAAU,CAAC,EAAE,OAAO,CAAA;IACpB,WAAW,CAAC,EAAE,aAAa,CAAA;CAC5B,CAAA"}
@@ -1,18 +1,37 @@
1
1
  import { pause } from "../../src/helpers/pause.js";
2
2
  import { NetworkAdapter } from "../../src/index.js";
3
3
  export class DummyNetworkAdapter extends NetworkAdapter {
4
- #startReady;
5
4
  #sendMessage;
5
+ #ready = false;
6
+ #readyResolver;
7
+ #readyPromise = new Promise(resolve => {
8
+ this.#readyResolver = resolve;
9
+ });
10
+ isReady() {
11
+ return this.#ready;
12
+ }
13
+ whenReady() {
14
+ return this.#readyPromise;
15
+ }
16
+ #forceReady() {
17
+ if (!this.#ready) {
18
+ this.#ready = true;
19
+ this.#readyResolver?.();
20
+ }
21
+ }
22
+ // A public wrapper for use in tests!
23
+ forceReady() {
24
+ this.#forceReady();
25
+ }
6
26
  constructor(opts = { startReady: true }) {
7
27
  super();
8
- this.#startReady = opts.startReady || false;
28
+ if (opts.startReady) {
29
+ this.#forceReady();
30
+ }
9
31
  this.#sendMessage = opts.sendMessage;
10
32
  }
11
33
  connect(peerId) {
12
34
  this.peerId = peerId;
13
- if (this.#startReady) {
14
- this.emit("ready", { network: this });
15
- }
16
35
  }
17
36
  disconnect() { }
18
37
  peerCandidate(peerId) {
@@ -1 +1 @@
1
- {"version":3,"file":"network-adapter-tests.d.ts","sourceRoot":"","sources":["../../../src/helpers/tests/network-adapter-tests.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,0CAA0C,CAAA;AAIvF;;;;;;;;;;;GAWG;AACH,wBAAgB,sBAAsB,CAAC,MAAM,EAAE,OAAO,EAAE,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,CAyJ5E;AAID,KAAK,OAAO,GAAG,uBAAuB,GAAG,uBAAuB,EAAE,CAAA;AAElE,MAAM,MAAM,OAAO,GAAG,MAAM,OAAO,CAAC;IAClC,QAAQ,EAAE,CAAC,OAAO,EAAE,OAAO,EAAE,OAAO,CAAC,CAAA;IACrC,QAAQ,CAAC,EAAE,MAAM,IAAI,CAAA;CACtB,CAAC,CAAA"}
1
+ {"version":3,"file":"network-adapter-tests.d.ts","sourceRoot":"","sources":["../../../src/helpers/tests/network-adapter-tests.ts"],"names":[],"mappings":"AAUA,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,0CAA0C,CAAA;AAIvF;;;;;;;;;;;GAWG;AACH,wBAAgB,sBAAsB,CAAC,MAAM,EAAE,OAAO,EAAE,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,CA0Q5E;AAID,KAAK,OAAO,GAAG,uBAAuB,GAAG,uBAAuB,EAAE,CAAA;AAElE,MAAM,MAAM,OAAO,GAAG,MAAM,OAAO,CAAC;IAClC,QAAQ,EAAE,CAAC,OAAO,EAAE,OAAO,EAAE,OAAO,CAAC,CAAA;IACrC,QAAQ,CAAC,EAAE,MAAM,IAAI,CAAA;CACtB,CAAC,CAAA"}
@@ -1,6 +1,6 @@
1
1
  import assert from "assert";
2
2
  import { describe, expect, it } from "vitest";
3
- import { Repo } from "../../index.js";
3
+ import { generateAutomergeUrl, parseAutomergeUrl, Repo, } from "../../index.js";
4
4
  import { eventPromise, eventPromises } from "../eventPromise.js";
5
5
  import { pause } from "../pause.js";
6
6
  /**
@@ -125,6 +125,93 @@ export function runNetworkAdapterTests(_setup, title) {
125
125
  });
126
126
  teardown();
127
127
  });
128
+ it("should emit disconnect events on disconnect", async () => {
129
+ const { adapters, teardown } = await setup();
130
+ const left = adapters[0][0];
131
+ const right = adapters[1][0];
132
+ const leftPeerId = "left";
133
+ const rightPeerId = "right";
134
+ const leftRepo = new Repo({
135
+ network: [left],
136
+ peerId: leftPeerId,
137
+ });
138
+ const rightRepo = new Repo({
139
+ network: [right],
140
+ peerId: rightPeerId,
141
+ });
142
+ await Promise.all([
143
+ eventPromise(leftRepo.networkSubsystem, "peer"),
144
+ eventPromise(rightRepo.networkSubsystem, "peer"),
145
+ ]);
146
+ const disconnectionPromises = Promise.all([
147
+ eventPromise(leftRepo.networkSubsystem, "peer-disconnected"),
148
+ eventPromise(rightRepo.networkSubsystem, "peer-disconnected"),
149
+ ]);
150
+ left.disconnect();
151
+ await disconnectionPromises;
152
+ teardown();
153
+ });
154
+ it("should not send messages after disconnect", async () => {
155
+ const { adapters, teardown } = await setup();
156
+ const left = adapters[0][0];
157
+ const right = adapters[1][0];
158
+ const leftPeerId = "left";
159
+ const rightPeerId = "right";
160
+ const leftRepo = new Repo({
161
+ network: [left],
162
+ peerId: leftPeerId,
163
+ });
164
+ const rightRepo = new Repo({
165
+ network: [right],
166
+ peerId: rightPeerId,
167
+ });
168
+ await Promise.all([
169
+ eventPromise(rightRepo.networkSubsystem, "peer"),
170
+ eventPromise(leftRepo.networkSubsystem, "peer"),
171
+ ]);
172
+ const disconnected = eventPromise(right, "peer-disconnected");
173
+ left.disconnect();
174
+ await disconnected;
175
+ const rightReceivedFromLeft = new Promise(resolve => {
176
+ right.on("message", msg => {
177
+ if (msg.senderId === leftPeerId) {
178
+ resolve(null);
179
+ }
180
+ });
181
+ });
182
+ const rightReceived = Promise.race([rightReceivedFromLeft, pause(10)]);
183
+ const documentId = parseAutomergeUrl(generateAutomergeUrl()).documentId;
184
+ left.send({
185
+ type: "foo",
186
+ data: new Uint8Array([1, 2, 3]),
187
+ documentId,
188
+ senderId: leftPeerId,
189
+ targetId: rightPeerId,
190
+ });
191
+ assert.equal(await rightReceived, null);
192
+ teardown();
193
+ });
194
+ it("should support reconnecting after disconnect", async () => {
195
+ const { adapters, teardown } = await setup();
196
+ const left = adapters[0][0];
197
+ const right = adapters[1][0];
198
+ const leftPeerId = "left";
199
+ const rightPeerId = "right";
200
+ const _leftRepo = new Repo({
201
+ network: [left],
202
+ peerId: leftPeerId,
203
+ });
204
+ const rightRepo = new Repo({
205
+ network: [right],
206
+ peerId: rightPeerId,
207
+ });
208
+ await eventPromise(rightRepo.networkSubsystem, "peer");
209
+ left.disconnect();
210
+ await pause(10);
211
+ left.connect(leftPeerId);
212
+ await eventPromise(left, "peer-candidate");
213
+ teardown();
214
+ });
128
215
  });
129
216
  }
130
217
  const NO_OP = () => { };