@automerge/automerge-repo 1.1.0-alpha.7 → 1.1.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 (50) hide show
  1. package/README.md +3 -1
  2. package/dist/AutomergeUrl.js +1 -1
  3. package/dist/DocHandle.d.ts +10 -4
  4. package/dist/DocHandle.d.ts.map +1 -1
  5. package/dist/DocHandle.js +17 -8
  6. package/dist/Repo.d.ts +18 -6
  7. package/dist/Repo.d.ts.map +1 -1
  8. package/dist/Repo.js +88 -72
  9. package/dist/helpers/pause.d.ts +0 -1
  10. package/dist/helpers/pause.d.ts.map +1 -1
  11. package/dist/helpers/pause.js +2 -8
  12. package/dist/helpers/withTimeout.d.ts.map +1 -1
  13. package/dist/helpers/withTimeout.js +2 -0
  14. package/dist/index.d.ts +1 -1
  15. package/dist/index.d.ts.map +1 -1
  16. package/dist/index.js +1 -1
  17. package/dist/network/NetworkAdapter.d.ts.map +1 -1
  18. package/dist/network/NetworkAdapter.js +1 -0
  19. package/dist/network/NetworkSubsystem.js +3 -3
  20. package/dist/network/messages.d.ts +43 -38
  21. package/dist/network/messages.d.ts.map +1 -1
  22. package/dist/network/messages.js +7 -9
  23. package/dist/synchronizer/CollectionSynchronizer.d.ts.map +1 -1
  24. package/dist/synchronizer/CollectionSynchronizer.js +1 -0
  25. package/dist/synchronizer/DocSynchronizer.d.ts.map +1 -1
  26. package/dist/synchronizer/DocSynchronizer.js +11 -7
  27. package/dist/synchronizer/Synchronizer.d.ts +11 -3
  28. package/dist/synchronizer/Synchronizer.d.ts.map +1 -1
  29. package/package.json +3 -4
  30. package/src/AutomergeUrl.ts +1 -1
  31. package/src/DocHandle.ts +34 -12
  32. package/src/Repo.ts +113 -84
  33. package/src/helpers/pause.ts +3 -11
  34. package/src/helpers/withTimeout.ts +2 -0
  35. package/src/index.ts +1 -1
  36. package/src/network/NetworkAdapter.ts +4 -2
  37. package/src/network/NetworkSubsystem.ts +3 -3
  38. package/src/network/messages.ts +60 -63
  39. package/src/synchronizer/CollectionSynchronizer.ts +1 -0
  40. package/src/synchronizer/DocSynchronizer.ts +19 -15
  41. package/src/synchronizer/Synchronizer.ts +11 -3
  42. package/test/CollectionSynchronizer.test.ts +7 -5
  43. package/test/DocHandle.test.ts +11 -2
  44. package/test/RemoteHeadsSubscriptions.test.ts +49 -49
  45. package/test/Repo.test.ts +39 -1
  46. package/test/StorageSubsystem.test.ts +1 -1
  47. package/test/helpers/collectMessages.ts +19 -0
  48. package/test/remoteHeads.test.ts +142 -119
  49. package/.eslintrc +0 -28
  50. package/test/helpers/waitForMessages.ts +0 -22
@@ -1,56 +1,64 @@
1
1
  import { SyncState } from "@automerge/automerge";
2
- import { DocumentId, PeerId, SessionId } from "../types.js";
3
2
  import { StorageId } from "../storage/types.js";
3
+ import { DocumentId, PeerId, SessionId } from "../types.js";
4
+ export type Message = {
5
+ type: string;
6
+ /** The peer ID of the sender of this message */
7
+ senderId: PeerId;
8
+ /** The peer ID of the recipient of this message */
9
+ targetId: PeerId;
10
+ data?: Uint8Array;
11
+ documentId?: DocumentId;
12
+ };
4
13
  /**
5
14
  * A sync message for a particular document
6
15
  */
7
16
  export type SyncMessage = {
8
17
  type: "sync";
9
- /** The peer ID of the sender of this message */
10
18
  senderId: PeerId;
11
- /** The peer ID of the recipient of this message */
12
19
  targetId: PeerId;
13
20
  /** The automerge sync message */
14
21
  data: Uint8Array;
15
22
  /** The document ID of the document this message is for */
16
23
  documentId: DocumentId;
17
24
  };
18
- /** An ephemeral message
25
+ /**
26
+ * An ephemeral message.
19
27
  *
20
28
  * @remarks
21
- * Ephemeral messages are not persisted anywhere and have no particular
22
- * structure. `automerge-repo` will gossip them around, in order to avoid
23
- * eternal loops of ephemeral messages every message has a session ID, which
24
- * is a random number generated by the sender at startup time, and a sequence
25
- * number. The combination of these two things allows us to discard messages
26
- * we have already seen.
29
+ * Ephemeral messages are not persisted anywhere. The data property can be used by the application
30
+ * as needed. The repo gossips these around.
31
+ *
32
+ * In order to avoid infinite loops of ephemeral messages, every message has (a) a session ID, which
33
+ * is a random number generated by the sender at startup time; and (b) a sequence number. The
34
+ * combination of these two things allows us to discard messages we have already seen.
27
35
  * */
28
36
  export type EphemeralMessage = {
29
37
  type: "ephemeral";
30
- /** The peer ID of the sender of this message */
31
38
  senderId: PeerId;
32
- /** The peer ID of the recipient of this message */
33
39
  targetId: PeerId;
34
- /** A sequence number which must be incremented for each message sent by this peer */
40
+ /** A sequence number which must be incremented for each message sent by this peer. */
35
41
  count: number;
36
- /** The ID of the session this message is part of. The sequence number for a given session always increases */
42
+ /** The ID of the session this message is part of. The sequence number for a given session always increases. */
37
43
  sessionId: SessionId;
38
- /** The document ID this message pertains to */
44
+ /** The document ID this message pertains to. */
39
45
  documentId: DocumentId;
40
- /** The actual data of the message */
46
+ /** The actual data of the message. */
41
47
  data: Uint8Array;
42
48
  };
43
- /** Sent by a {@link Repo} to indicate that it does not have the document and none of it's connected peers do either */
49
+ /**
50
+ * Sent by a {@link Repo} to indicate that it does not have the document and none of its connected
51
+ * peers do either.
52
+ */
44
53
  export type DocumentUnavailableMessage = {
45
54
  type: "doc-unavailable";
46
- /** The peer ID of the sender of this message */
47
55
  senderId: PeerId;
48
- /** The peer ID of the recipient of this message */
49
56
  targetId: PeerId;
50
57
  /** The document which the peer claims it doesn't have */
51
58
  documentId: DocumentId;
52
59
  };
53
- /** Sent by a {@link Repo} to request a document from a peer
60
+ /**
61
+ * Sent by a {@link Repo} to request a document from a peer.
54
62
  *
55
63
  * @remarks
56
64
  * This is identical to a {@link SyncMessage} except that it is sent by a {@link Repo}
@@ -58,37 +66,35 @@ export type DocumentUnavailableMessage = {
58
66
  * */
59
67
  export type RequestMessage = {
60
68
  type: "request";
61
- /** The peer ID of the sender of this message */
62
69
  senderId: PeerId;
63
- /** The peer ID of the recipient of this message */
64
70
  targetId: PeerId;
65
- /** The initial automerge sync message */
71
+ /** The automerge sync message */
66
72
  data: Uint8Array;
67
- /** The document ID this message requests */
73
+ /** The document ID of the document this message is for */
68
74
  documentId: DocumentId;
69
75
  };
70
- /** (anticipating work in progress) */
71
- export type AuthMessage<TPayload = any> = {
72
- type: "auth";
73
- /** The peer ID of the sender of this message */
74
- senderId: PeerId;
75
- /** The peer ID of the recipient of this message */
76
- targetId: PeerId;
77
- /** The payload of the auth message (up to the specific auth provider) */
78
- payload: TPayload;
79
- };
76
+ /**
77
+ * Sent by a {@link Repo} to add or remove storage IDs from a remote peer's subscription.
78
+ */
80
79
  export type RemoteSubscriptionControlMessage = {
81
80
  type: "remote-subscription-change";
82
81
  senderId: PeerId;
83
82
  targetId: PeerId;
83
+ /** The storage IDs to add to the subscription */
84
84
  add?: StorageId[];
85
+ /** The storage IDs to remove from the subscription */
85
86
  remove?: StorageId[];
86
87
  };
88
+ /**
89
+ * Sent by a {@link Repo} to indicate that the heads of a document have changed on a remote peer.
90
+ */
87
91
  export type RemoteHeadsChanged = {
88
92
  type: "remote-heads-changed";
89
93
  senderId: PeerId;
90
94
  targetId: PeerId;
95
+ /** The document ID of the document that has changed */
91
96
  documentId: DocumentId;
97
+ /** The document's new heads */
92
98
  newHeads: {
93
99
  [key: StorageId]: {
94
100
  heads: string[];
@@ -98,13 +104,12 @@ export type RemoteHeadsChanged = {
98
104
  };
99
105
  /** These are message types that a {@link NetworkAdapter} surfaces to a {@link Repo}. */
100
106
  export type RepoMessage = SyncMessage | EphemeralMessage | RequestMessage | DocumentUnavailableMessage | RemoteSubscriptionControlMessage | RemoteHeadsChanged;
107
+ /** These are message types that are handled by the {@link CollectionSynchronizer}.*/
101
108
  export type DocMessage = SyncMessage | EphemeralMessage | RequestMessage | DocumentUnavailableMessage;
102
- /** These are all the message types that a {@link NetworkAdapter} might see. */
103
- export type Message = RepoMessage | AuthMessage;
104
109
  /**
105
110
  * The contents of a message, without the sender ID or other properties added by the {@link NetworkSubsystem})
106
111
  */
107
- export type MessageContents<T extends Message = Message> = T extends EphemeralMessage ? Omit<T, "senderId" | "count" | "sessionId"> : Omit<T, "senderId">;
112
+ export type MessageContents<T extends Message = RepoMessage> = T extends EphemeralMessage ? Omit<T, "senderId" | "count" | "sessionId"> : Omit<T, "senderId">;
108
113
  /** Notify the repo that the sync state has changed */
109
114
  export interface SyncStateMessage {
110
115
  peerId: PeerId;
@@ -116,7 +121,7 @@ export interface OpenDocMessage {
116
121
  peerId: PeerId;
117
122
  documentId: DocumentId;
118
123
  }
119
- export declare const isValidRepoMessage: (message: Message) => message is RepoMessage;
124
+ export declare const isRepoMessage: (message: Message) => message is RepoMessage;
120
125
  export declare const isDocumentUnavailableMessage: (msg: Message) => msg is DocumentUnavailableMessage;
121
126
  export declare const isRequestMessage: (msg: Message) => msg is RequestMessage;
122
127
  export declare const isSyncMessage: (msg: Message) => msg is SyncMessage;
@@ -1 +1 @@
1
- {"version":3,"file":"messages.d.ts","sourceRoot":"","sources":["../../src/network/messages.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"messages.d.ts","sourceRoot":"","sources":["../../src/network/messages.ts"],"names":[],"mappings":"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"}
@@ -1,17 +1,15 @@
1
1
  // TYPE GUARDS
2
- export const isValidRepoMessage = (message) => typeof message === "object" &&
3
- typeof message.type === "string" &&
4
- typeof message.senderId === "string" &&
5
- (isSyncMessage(message) ||
6
- isEphemeralMessage(message) ||
7
- isRequestMessage(message) ||
8
- isDocumentUnavailableMessage(message) ||
9
- isRemoteSubscriptionControlMessage(message) ||
10
- isRemoteHeadsChanged(message));
2
+ export const isRepoMessage = (message) => isSyncMessage(message) ||
3
+ isEphemeralMessage(message) ||
4
+ isRequestMessage(message) ||
5
+ isDocumentUnavailableMessage(message) ||
6
+ isRemoteSubscriptionControlMessage(message) ||
7
+ isRemoteHeadsChanged(message);
11
8
  // prettier-ignore
12
9
  export const isDocumentUnavailableMessage = (msg) => msg.type === "doc-unavailable";
13
10
  export const isRequestMessage = (msg) => msg.type === "request";
14
11
  export const isSyncMessage = (msg) => msg.type === "sync";
15
12
  export const isEphemeralMessage = (msg) => msg.type === "ephemeral";
13
+ // prettier-ignore
16
14
  export const isRemoteSubscriptionControlMessage = (msg) => msg.type === "remote-subscription-change";
17
15
  export const isRemoteHeadsChanged = (msg) => msg.type === "remote-heads-changed";
@@ -1 +1 @@
1
- {"version":3,"file":"CollectionSynchronizer.d.ts","sourceRoot":"","sources":["../../src/synchronizer/CollectionSynchronizer.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,IAAI,EAAE,MAAM,YAAY,CAAA;AACjC,OAAO,EAAE,UAAU,EAAE,MAAM,wBAAwB,CAAA;AACnD,OAAO,EAAE,UAAU,EAAE,MAAM,EAAE,MAAM,aAAa,CAAA;AAEhD,OAAO,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAA;AAIhD,4FAA4F;AAC5F,qBAAa,sBAAuB,SAAQ,YAAY;;IAU1C,OAAO,CAAC,IAAI;gBAAJ,IAAI,EAAE,IAAI;IAqD9B;;;OAGG;IACG,cAAc,CAAC,OAAO,EAAE,UAAU;IAyBxC;;OAEG;IACH,WAAW,CAAC,UAAU,EAAE,UAAU;IAYlC,cAAc,CAAC,UAAU,EAAE,UAAU;IAIrC,2DAA2D;IAC3D,OAAO,CAAC,MAAM,EAAE,MAAM;IAgBtB,uDAAuD;IACvD,UAAU,CAAC,MAAM,EAAE,MAAM;IASzB,+CAA+C;IAC/C,IAAI,KAAK,IAAI,MAAM,EAAE,CAEpB;CACF"}
1
+ {"version":3,"file":"CollectionSynchronizer.d.ts","sourceRoot":"","sources":["../../src/synchronizer/CollectionSynchronizer.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,IAAI,EAAE,MAAM,YAAY,CAAA;AACjC,OAAO,EAAE,UAAU,EAAE,MAAM,wBAAwB,CAAA;AACnD,OAAO,EAAE,UAAU,EAAE,MAAM,EAAE,MAAM,aAAa,CAAA;AAEhD,OAAO,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAA;AAIhD,4FAA4F;AAC5F,qBAAa,sBAAuB,SAAQ,YAAY;;IAU1C,OAAO,CAAC,IAAI;gBAAJ,IAAI,EAAE,IAAI;IAqD9B;;;OAGG;IACG,cAAc,CAAC,OAAO,EAAE,UAAU;IAyBxC;;OAEG;IACH,WAAW,CAAC,UAAU,EAAE,UAAU;IAalC,cAAc,CAAC,UAAU,EAAE,UAAU;IAIrC,2DAA2D;IAC3D,OAAO,CAAC,MAAM,EAAE,MAAM;IAgBtB,uDAAuD;IACvD,UAAU,CAAC,MAAM,EAAE,MAAM;IASzB,+CAA+C;IAC/C,IAAI,KAAK,IAAI,MAAM,EAAE,CAEpB;CACF"}
@@ -87,6 +87,7 @@ export class CollectionSynchronizer extends Synchronizer {
87
87
  });
88
88
  }
89
89
  // TODO: implement this
90
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
90
91
  removeDocument(documentId) {
91
92
  throw new Error("not implemented");
92
93
  }
@@ -1 +1 @@
1
- {"version":3,"file":"DocSynchronizer.d.ts","sourceRoot":"","sources":["../../src/synchronizer/DocSynchronizer.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,CAAC,MAAM,2BAA2B,CAAA;AAG9C,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,eAAe,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,CAAC,CAAC,SAAS,GAAG,SAAS,CAAA;CAC9D;AAED;;;GAGG;AACH,qBAAa,eAAgB,SAAQ,YAAY;;IAE/C,gBAAgB,SAAM;gBAsBV,EAAE,MAAM,EAAE,eAAe,EAAE,EAAE,qBAAqB;IAyB9D,IAAI,UAAU,uCAEb;IAED,IAAI,UAAU,qCAEb;IAgID,OAAO,CAAC,MAAM,EAAE,MAAM;IAItB,SAAS,CAAC,OAAO,EAAE,MAAM,EAAE;IAiD3B,OAAO,CAAC,MAAM,EAAE,MAAM;IAKtB,cAAc,CAAC,OAAO,EAAE,WAAW;IAkBnC,uBAAuB,CAAC,OAAO,EAAE,gBAAgB;IAuBjD,kBAAkB,CAAC,OAAO,EAAE,WAAW,GAAG,cAAc;CA8EzD"}
1
+ {"version":3,"file":"DocSynchronizer.d.ts","sourceRoot":"","sources":["../../src/synchronizer/DocSynchronizer.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,CAAC,MAAM,2BAA2B,CAAA;AAG9C,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,eAAe,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,CAAC,CAAC,SAAS,GAAG,SAAS,CAAA;CAC9D;AAED;;;GAGG;AACH,qBAAa,eAAgB,SAAQ,YAAY;;IAE/C,gBAAgB,SAAM;gBAsBV,EAAE,MAAM,EAAE,eAAe,EAAE,EAAE,qBAAqB;IAyB9D,IAAI,UAAU,uCAEb;IAED,IAAI,UAAU,qCAEb;IAkID,OAAO,CAAC,MAAM,EAAE,MAAM;IAItB,SAAS,CAAC,OAAO,EAAE,MAAM,EAAE;IAmD3B,OAAO,CAAC,MAAM,EAAE,MAAM;IAKtB,cAAc,CAAC,OAAO,EAAE,WAAW;IAkBnC,uBAAuB,CAAC,OAAO,EAAE,gBAAgB;IAuBjD,kBAAkB,CAAC,OAAO,EAAE,WAAW,GAAG,cAAc;CA8EzD"}
@@ -77,9 +77,11 @@ export class DocSynchronizer extends Synchronizer {
77
77
  }
78
78
  let pendingCallbacks = this.#pendingSyncStateCallbacks[peerId];
79
79
  if (!pendingCallbacks) {
80
- this.#onLoadSyncState(peerId).then(syncState => {
80
+ this.#onLoadSyncState(peerId)
81
+ .then(syncState => {
81
82
  this.#initSyncState(peerId, syncState ?? A.initSyncState());
82
- }).catch(err => {
83
+ })
84
+ .catch(err => {
83
85
  this.#log(`Error loading sync state for ${peerId}: ${err}`);
84
86
  });
85
87
  pendingCallbacks = this.#pendingSyncStateCallbacks[peerId] = [];
@@ -176,11 +178,13 @@ export class DocSynchronizer extends Synchronizer {
176
178
  // TODO: cover that case with a test and remove this hack
177
179
  const reparsedSyncState = A.decodeSyncState(A.encodeSyncState(syncState));
178
180
  this.#setSyncState(peerId, reparsedSyncState);
179
- docPromise.then(doc => {
181
+ docPromise
182
+ .then(doc => {
180
183
  if (doc) {
181
184
  this.#sendSyncMessage(peerId, doc);
182
185
  }
183
- }).catch(err => {
186
+ })
187
+ .catch(err => {
184
188
  this.#log(`Error loading doc for ${peerId}: ${err}`);
185
189
  });
186
190
  });
@@ -235,9 +239,9 @@ export class DocSynchronizer extends Synchronizer {
235
239
  return;
236
240
  }
237
241
  this.#processAllPendingSyncMessages();
238
- this.#processSyncMessage(message, new Date());
242
+ this.#processSyncMessage(message);
239
243
  }
240
- #processSyncMessage(message, received) {
244
+ #processSyncMessage(message) {
241
245
  if (isRequestMessage(message)) {
242
246
  this.#peerDocumentStatuses[message.senderId] = "wants";
243
247
  }
@@ -278,7 +282,7 @@ export class DocSynchronizer extends Synchronizer {
278
282
  }
279
283
  #processAllPendingSyncMessages() {
280
284
  for (const message of this.#pendingSyncMessages) {
281
- this.#processSyncMessage(message.message, message.received);
285
+ this.#processSyncMessage(message.message);
282
286
  }
283
287
  this.#pendingSyncMessages = [];
284
288
  }
@@ -1,11 +1,19 @@
1
1
  import { EventEmitter } from "eventemitter3";
2
- import { MessageContents, OpenDocMessage, RepoMessage, SyncStateMessage } from "../network/messages.js";
2
+ import { MessageContents, OpenDocMessage, RepoMessage } from "../network/messages.js";
3
+ import { SyncState } from "@automerge/automerge";
4
+ import { PeerId, DocumentId } from "../types.js";
3
5
  export declare abstract class Synchronizer extends EventEmitter<SynchronizerEvents> {
4
6
  abstract receiveMessage(message: RepoMessage): void;
5
7
  }
6
8
  export interface SynchronizerEvents {
7
- message: (arg: MessageContents) => void;
8
- "sync-state": (arg: SyncStateMessage) => void;
9
+ message: (payload: MessageContents) => void;
10
+ "sync-state": (payload: SyncStatePayload) => void;
9
11
  "open-doc": (arg: OpenDocMessage) => void;
10
12
  }
13
+ /** Notify the repo that the sync state has changed */
14
+ export interface SyncStatePayload {
15
+ peerId: PeerId;
16
+ documentId: DocumentId;
17
+ syncState: SyncState;
18
+ }
11
19
  //# sourceMappingURL=Synchronizer.d.ts.map
@@ -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,EACX,gBAAgB,EACjB,MAAM,wBAAwB,CAAA;AAE/B,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,GAAG,EAAE,eAAe,KAAK,IAAI,CAAA;IACvC,YAAY,EAAE,CAAC,GAAG,EAAE,gBAAgB,KAAK,IAAI,CAAA;IAC7C,UAAU,EAAE,CAAC,GAAG,EAAE,cAAc,KAAK,IAAI,CAAA;CAC1C"}
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,sBAAsB,CAAA;AAChD,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;CAC1C;AAED,uDAAuD;AACvD,MAAM,WAAW,gBAAgB;IAC/B,MAAM,EAAE,MAAM,CAAA;IACd,UAAU,EAAE,UAAU,CAAA;IACtB,SAAS,EAAE,SAAS,CAAA;CACrB"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@automerge/automerge-repo",
3
- "version": "1.1.0-alpha.7",
3
+ "version": "1.1.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>",
@@ -9,9 +9,8 @@
9
9
  "main": "dist/index.js",
10
10
  "scripts": {
11
11
  "build": "tsc",
12
- "lint": "eslint --ext .ts src",
13
12
  "watch": "npm-watch build",
14
- "test:coverage": "c8 --reporter=lcov --reporter=html --reporter=text yarn test",
13
+ "test:coverage": "c8 --reporter=lcov --reporter=html --reporter=text pnpm test",
15
14
  "test": "vitest",
16
15
  "test:watch": "npm-watch test",
17
16
  "fuzz": "ts-node --esm --experimentalSpecifierResolution=node fuzz/fuzz.ts"
@@ -56,5 +55,5 @@
56
55
  "publishConfig": {
57
56
  "access": "public"
58
57
  },
59
- "gitHead": "9a4711e39c93273d992c5686257246ddfaaafddd"
58
+ "gitHead": "e9e7d3f27ec2ac8a2e9d122ece80598918940067"
60
59
  }
@@ -13,7 +13,7 @@ export const urlPrefix = "automerge:"
13
13
  /** Given an Automerge URL, returns the DocumentId in both base58check-encoded form and binary form */
14
14
  export const parseAutomergeUrl = (url: AutomergeUrl) => {
15
15
  const regex = new RegExp(`^${urlPrefix}(\\w+)$`)
16
- const [_, docMatch] = url.match(regex) || []
16
+ const [, docMatch] = url.match(regex) || []
17
17
  const documentId = docMatch as DocumentId
18
18
  const binaryDocumentId = documentIdToBinary(documentId)
19
19
 
package/src/DocHandle.ts CHANGED
@@ -39,7 +39,7 @@ export class DocHandle<T> //
39
39
  #log: debug.Debugger
40
40
 
41
41
  #machine: DocHandleXstateMachine<T>
42
- #timeoutDelay: number
42
+ #timeoutDelay: number = 60_000
43
43
  #remoteHeads: Record<StorageId, A.Heads> = {}
44
44
 
45
45
  /** The URL of this document
@@ -54,20 +54,30 @@ export class DocHandle<T> //
54
54
  /** @hidden */
55
55
  constructor(
56
56
  public documentId: DocumentId,
57
- { isNew = false, timeoutDelay = 60_000 }: DocHandleOptions = {}
57
+ options: DocHandleOptions<T> = {}
58
58
  ) {
59
59
  super()
60
- this.#timeoutDelay = timeoutDelay
61
- this.#log = debug(`automerge-repo:dochandle:${this.documentId.slice(0, 5)}`)
62
60
 
63
- // initial doc
64
- let doc = A.init<T>()
61
+ this.documentId = documentId
62
+
63
+ if ("timeoutDelay" in options && options.timeoutDelay) {
64
+ this.#timeoutDelay = options.timeoutDelay
65
+ }
65
66
 
66
- // Make an empty change so that we have something to save to disk
67
+ let doc: T
68
+ const isNew = "isNew" in options && options.isNew
67
69
  if (isNew) {
68
- doc = A.emptyChange(doc, {})
70
+ // T should really be constrained to extend `Record<string, unknown>` (an automerge doc can't be
71
+ // e.g. a primitive, an array, etc. - it must be an object). But adding that constraint creates
72
+ // a bunch of other problems elsewhere so for now we'll just cast it here to make Automerge happy.
73
+ doc = A.from(options.initialValue as Record<string, unknown>) as T
74
+ doc = A.emptyChange<T>(doc)
75
+ } else {
76
+ doc = A.init<T>()
69
77
  }
70
78
 
79
+ this.#log = debug(`automerge-repo:dochandle:${this.documentId.slice(0, 5)}`)
80
+
71
81
  /**
72
82
  * Internally we use a state machine to orchestrate document loading and/or syncing, in order to
73
83
  * avoid requesting data we already have, or surfacing intermediate values to the consumer.
@@ -451,10 +461,22 @@ export class DocHandle<T> //
451
461
  // WRAPPER CLASS TYPES
452
462
 
453
463
  /** @hidden */
454
- export interface DocHandleOptions {
455
- isNew?: boolean
456
- timeoutDelay?: number
457
- }
464
+ export type DocHandleOptions<T> =
465
+ // NEW DOCUMENTS
466
+ | {
467
+ /** If we know this is a new document (because we're creating it) this should be set to true. */
468
+ isNew: true
469
+
470
+ /** The initial value of the document. */
471
+ initialValue?: T
472
+ }
473
+ // EXISTING DOCUMENTS
474
+ | {
475
+ isNew?: false
476
+
477
+ /** The number of milliseconds before we mark this document as unavailable if we don't have it and nobody shares it with us. */
478
+ timeoutDelay?: number
479
+ }
458
480
 
459
481
  export interface DocHandleMessagePayload {
460
482
  destinationId: PeerId