@automerge/automerge-repo 1.0.1 → 1.0.3

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.
@@ -1,4 +1,5 @@
1
1
  import { SessionId } from "../EphemeralData.js";
2
+ export { type SessionId } from "../EphemeralData.js";
2
3
  import { DocumentId, PeerId } from "../types.js";
3
4
  export declare function isValidMessage(message: NetworkAdapterMessage): message is SyncMessage | EphemeralMessage | RequestMessage | DocumentUnavailableMessage;
4
5
  export declare function isDocumentUnavailableMessage(message: NetworkAdapterMessage): message is DocumentUnavailableMessage;
@@ -14,7 +15,20 @@ export interface SyncMessageContents {
14
15
  targetId: PeerId;
15
16
  documentId: DocumentId;
16
17
  }
17
- export type SyncMessage = SyncMessageEnvelope & SyncMessageContents;
18
+ /**
19
+ * A sync message for a particular document
20
+ */
21
+ export type SyncMessage = {
22
+ /** The peer ID of the sender of this message */
23
+ senderId: PeerId;
24
+ type: "sync";
25
+ /** The automerge sync message */
26
+ data: Uint8Array;
27
+ /** The peer ID of the recipient of this message */
28
+ targetId: PeerId;
29
+ /** The document ID of the document this message is for */
30
+ documentId: DocumentId;
31
+ };
18
32
  export interface EphemeralMessageEnvelope {
19
33
  senderId: PeerId;
20
34
  count: number;
@@ -26,32 +40,93 @@ export interface EphemeralMessageContents {
26
40
  documentId: DocumentId;
27
41
  data: Uint8Array;
28
42
  }
29
- export type EphemeralMessage = EphemeralMessageEnvelope & EphemeralMessageContents;
43
+ /** An ephemeral message
44
+ *
45
+ * @remarks
46
+ * Ephemeral messages are not persisted anywhere and have no particular
47
+ * structure. `automerge-repo` will gossip them around, in order to avoid
48
+ * eternal loops of ephemeral messages every message has a session ID, which
49
+ * is a random number generated by the sender at startup time, and a sequence
50
+ * number. The combination of these two things allows us to discard messages
51
+ * we have already seen.
52
+ * */
53
+ export type EphemeralMessage = {
54
+ /** The ID of the peer who sent this message */
55
+ senderId: PeerId;
56
+ /** A sequence number which must be incremented for each message sent by this peer */
57
+ count: number;
58
+ /** The ID of the session this message is part of. The sequence number for a given session always increases */
59
+ sessionId: SessionId;
60
+ type: "ephemeral";
61
+ /** The peer this message is for */
62
+ targetId: PeerId;
63
+ /** The document ID this message pertains to */
64
+ documentId: DocumentId;
65
+ /** The actual data of the message */
66
+ data: Uint8Array;
67
+ };
30
68
  export interface DocumentUnavailableMessageContents {
31
69
  type: "doc-unavailable";
32
70
  documentId: DocumentId;
33
71
  targetId: PeerId;
34
72
  }
35
- export type DocumentUnavailableMessage = SyncMessageEnvelope & DocumentUnavailableMessageContents;
73
+ /** Sent by a {@link Repo} to indicate that it does not have the document and none of it's connected peers do either */
74
+ export type DocumentUnavailableMessage = {
75
+ /** The peer who sent this message */
76
+ senderId: PeerId;
77
+ type: "doc-unavailable";
78
+ /** The document which the peer claims it doesn't have */
79
+ documentId: DocumentId;
80
+ /** The peer this message is for */
81
+ targetId: PeerId;
82
+ };
36
83
  export interface RequestMessageContents {
37
84
  type: "request";
38
85
  data: Uint8Array;
39
86
  targetId: PeerId;
40
87
  documentId: DocumentId;
41
88
  }
42
- export type RequestMessage = SyncMessageEnvelope & RequestMessageContents;
89
+ /** Sent by a {@link Repo} to request a document from a peer
90
+ *
91
+ * @remarks
92
+ * This is identical to a {@link SyncMessage} except that it is sent by a {@link Repo}
93
+ * as the initial sync message when asking the other peer if it has the document.
94
+ * */
95
+ export type RequestMessage = {
96
+ /** The peer who sent this message */
97
+ senderId: PeerId;
98
+ type: "request";
99
+ /** The initial automerge sync message */
100
+ data: Uint8Array;
101
+ /** The peer this message is for */
102
+ targetId: PeerId;
103
+ /** The document ID this message requests */
104
+ documentId: DocumentId;
105
+ };
43
106
  export type MessageContents = SyncMessageContents | EphemeralMessageContents | RequestMessageContents | DocumentUnavailableMessageContents;
107
+ /** The type of messages that {@link Repo} sends and receive to {@link NetworkAdapter}s */
44
108
  export type Message = SyncMessage | EphemeralMessage | RequestMessage | DocumentUnavailableMessage;
45
109
  export type SynchronizerMessage = SyncMessage | RequestMessage | DocumentUnavailableMessage | EphemeralMessage;
46
- type ArriveMessage = {
110
+ /** Notify the network that we have arrived so everyone knows our peer ID */
111
+ export type ArriveMessage = {
112
+ /** Our peer ID */
47
113
  senderId: PeerId;
48
114
  type: "arrive";
49
115
  };
50
- type WelcomeMessage = {
116
+ /** Respond to an arriving peer with our peer ID */
117
+ export type WelcomeMessage = {
118
+ /** Our peer ID */
51
119
  senderId: PeerId;
120
+ /** The ID of the peer who sent the {@link ArriveMessage} we are responding to */
52
121
  targetId: PeerId;
53
122
  type: "welcome";
54
123
  };
124
+ /** The type of messages that {@link NetworkAdapter}s send and receive to each other
125
+ *
126
+ * @remarks
127
+ * It is not _required_ that a {@link NetworkAdapter} use this message type.
128
+ * NetworkAdapters are free to use whatever message type makes sense for their
129
+ * transport. However, this type is a useful default.
130
+ * */
55
131
  export type NetworkAdapterMessage = ArriveMessage | WelcomeMessage | Message;
56
- export {};
57
132
  //# sourceMappingURL=messages.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"messages.d.ts","sourceRoot":"","sources":["../../src/network/messages.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,SAAS,EAAE,MAAM,qBAAqB,CAAA;AAC/C,OAAO,EAAE,UAAU,EAAE,MAAM,EAAE,MAAM,aAAa,CAAA;AAEhD,wBAAgB,cAAc,CAC5B,OAAO,EAAE,qBAAqB,GAC7B,OAAO,IACN,WAAW,GACX,gBAAgB,GAChB,cAAc,GACd,0BAA0B,CAU7B;AAED,wBAAgB,4BAA4B,CAC1C,OAAO,EAAE,qBAAqB,GAC7B,OAAO,IAAI,0BAA0B,CAEvC;AAED,wBAAgB,gBAAgB,CAC9B,OAAO,EAAE,qBAAqB,GAC7B,OAAO,IAAI,cAAc,CAE3B;AAED,wBAAgB,aAAa,CAC3B,OAAO,EAAE,qBAAqB,GAC7B,OAAO,IAAI,WAAW,CAExB;AAED,wBAAgB,kBAAkB,CAChC,OAAO,EAAE,qBAAqB,GAAG,eAAe,GAC/C,OAAO,IAAI,gBAAgB,GAAG,wBAAwB,CAExD;AAED,MAAM,WAAW,mBAAmB;IAClC,QAAQ,EAAE,MAAM,CAAA;CACjB;AAED,MAAM,WAAW,mBAAmB;IAClC,IAAI,EAAE,MAAM,CAAA;IACZ,IAAI,EAAE,UAAU,CAAA;IAChB,QAAQ,EAAE,MAAM,CAAA;IAChB,UAAU,EAAE,UAAU,CAAA;CACvB;AAED,MAAM,MAAM,WAAW,GAAG,mBAAmB,GAAG,mBAAmB,CAAA;AAEnE,MAAM,WAAW,wBAAwB;IACvC,QAAQ,EAAE,MAAM,CAAA;IAChB,KAAK,EAAE,MAAM,CAAA;IACb,SAAS,EAAE,SAAS,CAAA;CACrB;AAED,MAAM,WAAW,wBAAwB;IACvC,IAAI,EAAE,WAAW,CAAA;IACjB,QAAQ,EAAE,MAAM,CAAA;IAChB,UAAU,EAAE,UAAU,CAAA;IACtB,IAAI,EAAE,UAAU,CAAA;CACjB;AAED,MAAM,MAAM,gBAAgB,GAAG,wBAAwB,GACrD,wBAAwB,CAAA;AAE1B,MAAM,WAAW,kCAAkC;IACjD,IAAI,EAAE,iBAAiB,CAAA;IACvB,UAAU,EAAE,UAAU,CAAA;IACtB,QAAQ,EAAE,MAAM,CAAA;CACjB;AAED,MAAM,MAAM,0BAA0B,GAAG,mBAAmB,GAC1D,kCAAkC,CAAA;AAEpC,MAAM,WAAW,sBAAsB;IACrC,IAAI,EAAE,SAAS,CAAA;IACf,IAAI,EAAE,UAAU,CAAA;IAChB,QAAQ,EAAE,MAAM,CAAA;IAChB,UAAU,EAAE,UAAU,CAAA;CACvB;AAED,MAAM,MAAM,cAAc,GAAG,mBAAmB,GAAG,sBAAsB,CAAA;AAEzE,MAAM,MAAM,eAAe,GACvB,mBAAmB,GACnB,wBAAwB,GACxB,sBAAsB,GACtB,kCAAkC,CAAA;AAEtC,MAAM,MAAM,OAAO,GACf,WAAW,GACX,gBAAgB,GAChB,cAAc,GACd,0BAA0B,CAAA;AAE9B,MAAM,MAAM,mBAAmB,GAC3B,WAAW,GACX,cAAc,GACd,0BAA0B,GAC1B,gBAAgB,CAAA;AAEpB,KAAK,aAAa,GAAG;IACnB,QAAQ,EAAE,MAAM,CAAA;IAChB,IAAI,EAAE,QAAQ,CAAA;CACf,CAAA;AAED,KAAK,cAAc,GAAG;IACpB,QAAQ,EAAE,MAAM,CAAA;IAChB,QAAQ,EAAE,MAAM,CAAA;IAChB,IAAI,EAAE,SAAS,CAAA;CAChB,CAAA;AAED,MAAM,MAAM,qBAAqB,GAAG,aAAa,GAAG,cAAc,GAAG,OAAO,CAAA"}
1
+ {"version":3,"file":"messages.d.ts","sourceRoot":"","sources":["../../src/network/messages.ts"],"names":[],"mappings":"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"}
@@ -1,12 +1,33 @@
1
+ /** A storage adapter represents some way of storing binary data for a {@link Repo}
2
+ *
3
+ * @remarks
4
+ * `StorageAdapter`s are a little like a key/value store. The keys are arrays
5
+ * of strings ({@link StorageKey}) and the values are binary blobs.
6
+ */
1
7
  export declare abstract class StorageAdapter {
8
+ /** Load the single blob correspongind to `key` */
2
9
  abstract load(key: StorageKey): Promise<Uint8Array | undefined>;
10
+ /** save the blod `data` to the key `key` */
3
11
  abstract save(key: StorageKey, data: Uint8Array): Promise<void>;
12
+ /** remove the blob corresponding to `key` */
4
13
  abstract remove(key: StorageKey): Promise<void>;
14
+ /** Load all blobs with keys that start with `keyPrefix` */
5
15
  abstract loadRange(keyPrefix: StorageKey): Promise<{
6
16
  key: StorageKey;
7
17
  data: Uint8Array;
8
18
  }[]>;
19
+ /** Remove all blobs with keys that start with `keyPrefix` */
9
20
  abstract removeRange(keyPrefix: StorageKey): Promise<void>;
10
21
  }
22
+ /** The type of keys for a {@link StorageAdapter}
23
+ *
24
+ * @remarks
25
+ * Storage keys are arrays because they are hierarchical and the storage
26
+ * subsystem will need to be able to do range queries for all keys that
27
+ * have a particular prefix. For example, incremental changes for a given
28
+ * document might be stored under `[<documentId>, "incremental", <SHA256>]`.
29
+ * `StorageAdapter` implementations should not assume any particular structure
30
+ * though.
31
+ **/
11
32
  export type StorageKey = string[];
12
33
  //# sourceMappingURL=StorageAdapter.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"StorageAdapter.d.ts","sourceRoot":"","sources":["../../src/storage/StorageAdapter.ts"],"names":[],"mappings":"AAAA,8BAAsB,cAAc;IAMlC,QAAQ,CAAC,IAAI,CAAC,GAAG,EAAE,UAAU,GAAG,OAAO,CAAC,UAAU,GAAG,SAAS,CAAC;IAC/D,QAAQ,CAAC,IAAI,CAAC,GAAG,EAAE,UAAU,EAAE,IAAI,EAAE,UAAU,GAAG,OAAO,CAAC,IAAI,CAAC;IAC/D,QAAQ,CAAC,MAAM,CAAC,GAAG,EAAE,UAAU,GAAG,OAAO,CAAC,IAAI,CAAC;IAO/C,QAAQ,CAAC,SAAS,CAAC,SAAS,EAAE,UAAU,GAAG,OAAO,CAAC;QAAC,GAAG,EAAE,UAAU,CAAC;QAAC,IAAI,EAAE,UAAU,CAAA;KAAC,EAAE,CAAC;IACzF,QAAQ,CAAC,WAAW,CAAC,SAAS,EAAE,UAAU,GAAG,OAAO,CAAC,IAAI,CAAC;CAC3D;AAED,MAAM,MAAO,UAAU,GAAG,MAAM,EAAE,CAAA"}
1
+ {"version":3,"file":"StorageAdapter.d.ts","sourceRoot":"","sources":["../../src/storage/StorageAdapter.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,8BAAsB,cAAc;IAMlC,kDAAkD;IAClD,QAAQ,CAAC,IAAI,CAAC,GAAG,EAAE,UAAU,GAAG,OAAO,CAAC,UAAU,GAAG,SAAS,CAAC;IAC/D,4CAA4C;IAC5C,QAAQ,CAAC,IAAI,CAAC,GAAG,EAAE,UAAU,EAAE,IAAI,EAAE,UAAU,GAAG,OAAO,CAAC,IAAI,CAAC;IAC/D,6CAA6C;IAC7C,QAAQ,CAAC,MAAM,CAAC,GAAG,EAAE,UAAU,GAAG,OAAO,CAAC,IAAI,CAAC;IAO/C,2DAA2D;IAC3D,QAAQ,CAAC,SAAS,CAAC,SAAS,EAAE,UAAU,GAAG,OAAO,CAAC;QAAC,GAAG,EAAE,UAAU,CAAC;QAAC,IAAI,EAAE,UAAU,CAAA;KAAC,EAAE,CAAC;IACzF,6DAA6D;IAC7D,QAAQ,CAAC,WAAW,CAAC,SAAS,EAAE,UAAU,GAAG,OAAO,CAAC,IAAI,CAAC;CAC3D;AAED;;;;;;;;;IASI;AACJ,MAAM,MAAO,UAAU,GAAG,MAAM,EAAE,CAAA"}
@@ -1,2 +1,8 @@
1
+ /** A storage adapter represents some way of storing binary data for a {@link Repo}
2
+ *
3
+ * @remarks
4
+ * `StorageAdapter`s are a little like a key/value store. The keys are arrays
5
+ * of strings ({@link StorageKey}) and the values are binary blobs.
6
+ */
1
7
  export class StorageAdapter {
2
8
  }
package/dist/types.d.ts CHANGED
@@ -1,14 +1,25 @@
1
+ /** The ID of a document. Typically you should use a {@link AutomergeUrl} instead.
2
+ */
1
3
  export type DocumentId = string & {
2
4
  __documentId: true;
3
5
  };
6
+ /** A branded string representing a URL for a document
7
+ *
8
+ * @remarks
9
+ * An automerge URL has the form `automerge:<base58 encoded string>`. This
10
+ * type is returned from various routines which validate a url.
11
+ *
12
+ */
4
13
  export type AutomergeUrl = string & {
5
14
  __documentUrl: true;
6
15
  };
16
+ /** A document ID as a Uint8Array instead of a bas58 encoded string. Typically you should use a {@link AutomergeUrl} instead.
17
+ */
7
18
  export type BinaryDocumentId = Uint8Array & {
8
19
  __binaryDocumentId: true;
9
20
  };
21
+ /** A branded type for peer IDs */
10
22
  export type PeerId = string & {
11
23
  __peerId: false;
12
24
  };
13
- export type DistributiveOmit<T, K extends keyof any> = T extends any ? Omit<T, K> : never;
14
25
  //# sourceMappingURL=types.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,UAAU,GAAG,MAAM,GAAG;IAAE,YAAY,EAAE,IAAI,CAAA;CAAE,CAAA;AACxD,MAAM,MAAM,YAAY,GAAG,MAAM,GAAG;IAAE,aAAa,EAAE,IAAI,CAAA;CAAE,CAAA;AAC3D,MAAM,MAAM,gBAAgB,GAAG,UAAU,GAAG;IAAE,kBAAkB,EAAE,IAAI,CAAA;CAAE,CAAA;AAExE,MAAM,MAAM,MAAM,GAAG,MAAM,GAAG;IAAE,QAAQ,EAAE,KAAK,CAAA;CAAE,CAAA;AAEjD,MAAM,MAAM,gBAAgB,CAAC,CAAC,EAAE,CAAC,SAAS,MAAM,GAAG,IAAI,CAAC,SAAS,GAAG,GAChE,IAAI,CAAC,CAAC,EAAE,CAAC,CAAC,GACV,KAAK,CAAA"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA;GACG;AACH,MAAM,MAAM,UAAU,GAAG,MAAM,GAAG;IAAE,YAAY,EAAE,IAAI,CAAA;CAAE,CAAA;AACxD;;;;;;GAMG;AACH,MAAM,MAAM,YAAY,GAAG,MAAM,GAAG;IAAE,aAAa,EAAE,IAAI,CAAA;CAAE,CAAA;AAC3D;GACG;AACH,MAAM,MAAM,gBAAgB,GAAG,UAAU,GAAG;IAAE,kBAAkB,EAAE,IAAI,CAAA;CAAE,CAAA;AAExE,kCAAkC;AAClC,MAAM,MAAM,MAAM,GAAG,MAAM,GAAG;IAAE,QAAQ,EAAE,KAAK,CAAA;CAAE,CAAA"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@automerge/automerge-repo",
3
- "version": "1.0.1",
3
+ "version": "1.0.3",
4
4
  "description": "A repository object to manage a collection of automerge documents",
5
5
  "repository": "https://github.com/automerge/automerge-repo",
6
6
  "author": "Peter van Hardenberg <pvh@pvh.ca>",
@@ -65,5 +65,5 @@
65
65
  "publishConfig": {
66
66
  "access": "public"
67
67
  },
68
- "gitHead": "9c1eb5fb161eac78a71a6e866b30879994278d22"
68
+ "gitHead": "4f3391cd33cd0cabfebde48dbd56749ddb6922da"
69
69
  }
package/src/DocHandle.ts CHANGED
@@ -21,7 +21,18 @@ import type { DocumentId, PeerId, AutomergeUrl } from "./types.js"
21
21
  import { stringifyAutomergeUrl } from "./DocUrl.js"
22
22
  import { encode } from "./helpers/cbor.js"
23
23
 
24
- /** DocHandle is a wrapper around a single Automerge document that lets us listen for changes. */
24
+ /** DocHandle is a wrapper around a single Automerge document that lets us
25
+ * listen for changes and notify the network and storage of new changes.
26
+ *
27
+ * @remarks
28
+ * A `DocHandle` represents a document which is being managed by a {@link Repo}.
29
+ * To obtain `DocHandle` use {@link Repo.find} or {@link Repo.create}.
30
+ *
31
+ * To modify the underlying document use either {@link DocHandle.change} or
32
+ * {@link DocHandle.changeAt}. These methods will notify the `Repo` that some
33
+ * change has occured and the `Repo` will save any new changes to the
34
+ * attached {@link StorageAdapter} and send sync messages to connected peers.
35
+ * */
25
36
  export class DocHandle<T> //
26
37
  extends EventEmitter<DocHandleEvents<T>>
27
38
  {
@@ -30,10 +41,16 @@ export class DocHandle<T> //
30
41
  #machine: DocHandleXstateMachine<T>
31
42
  #timeoutDelay: number
32
43
 
44
+ /** The URL of this document
45
+ *
46
+ * @remarks
47
+ * This can be used to request the document from an instance of {@link Repo}
48
+ */
33
49
  get url(): AutomergeUrl {
34
50
  return stringifyAutomergeUrl({ documentId: this.documentId })
35
51
  }
36
52
 
53
+ /** @hidden */
37
54
  constructor(
38
55
  public documentId: DocumentId,
39
56
  { isNew = false, timeoutDelay = 60_000 }: DocHandleOptions = {}
@@ -248,6 +265,7 @@ export class DocHandle<T> //
248
265
  inState = (states: HandleState[]) =>
249
266
  states.some(this.#machine?.getSnapshot().matches)
250
267
 
268
+ /** @hidden */
251
269
  get state() {
252
270
  return this.#machine?.getSnapshot().value
253
271
  }
@@ -303,7 +321,9 @@ export class DocHandle<T> //
303
321
  return this.#doc
304
322
  }
305
323
 
306
- /** `update` is called by the repo when we receive changes from the network */
324
+ /** `update` is called by the repo when we receive changes from the network
325
+ * @hidden
326
+ * */
307
327
  update(callback: (doc: A.Doc<T>) => A.Doc<T>) {
308
328
  this.#machine.send(UPDATE, {
309
329
  payload: { callback },
@@ -357,15 +377,19 @@ export class DocHandle<T> //
357
377
  this.#machine.send(MARK_UNAVAILABLE)
358
378
  }
359
379
 
360
- /** `request` is called by the repo when the document is not found in storage */
380
+ /** `request` is called by the repo when the document is not found in storage
381
+ * @hidden
382
+ * */
361
383
  request() {
362
384
  if (this.#state === LOADING) this.#machine.send(REQUEST)
363
385
  }
364
386
 
387
+ /** @hidden */
365
388
  awaitNetwork() {
366
389
  if (this.#state === LOADING) this.#machine.send(AWAIT_NETWORK)
367
390
  }
368
391
 
392
+ /** @hidden */
369
393
  networkReady() {
370
394
  if (this.#state === AWAITING_NETWORK) this.#machine.send(NETWORK_READY)
371
395
  }
@@ -391,7 +415,8 @@ export class DocHandle<T> //
391
415
 
392
416
  // WRAPPER CLASS TYPES
393
417
 
394
- interface DocHandleOptions {
418
+ /** @hidden */
419
+ export interface DocHandleOptions {
395
420
  isNew?: boolean
396
421
  timeoutDelay?: number
397
422
  }
@@ -411,10 +436,15 @@ export interface DocHandleDeletePayload<T> {
411
436
  handle: DocHandle<T>
412
437
  }
413
438
 
439
+ /** Emitted when a document has changed */
414
440
  export interface DocHandleChangePayload<T> {
441
+ /** The hande which changed */
415
442
  handle: DocHandle<T>
443
+ /** The value of the document after the change */
416
444
  doc: A.Doc<T>
445
+ /** The patches representing the change that occurred */
417
446
  patches: A.Patch[]
447
+ /** Information about the change */
418
448
  patchInfo: A.PatchInfo<T>
419
449
  }
420
450
 
@@ -444,14 +474,27 @@ export interface DocHandleEvents<T> {
444
474
 
445
475
  // state
446
476
 
477
+ /**
478
+ * The state of a document handle
479
+ * @enum
480
+ *
481
+ */
447
482
  export const HandleState = {
483
+ /** The handle has been created but not yet loaded or requested */
448
484
  IDLE: "idle",
485
+ /** We are waiting for storage to finish loading */
449
486
  LOADING: "loading",
487
+ /** We are waiting for the network to be come ready */
450
488
  AWAITING_NETWORK: "awaitingNetwork",
489
+ /** We are waiting for someone in the network to respond to a sync request */
451
490
  REQUESTING: "requesting",
491
+ /** The document is available */
452
492
  READY: "ready",
493
+ /** We were unable to load or request the document for some reason */
453
494
  FAILED: "failed",
495
+ /** The document has been deleted from the repo */
454
496
  DELETED: "deleted",
497
+ /** The document was not available in storage or from any connected peers */
455
498
  UNAVAILABLE: "unavailable",
456
499
  } as const
457
500
  export type HandleState = (typeof HandleState)[keyof typeof HandleState]
package/src/DocUrl.ts CHANGED
@@ -3,7 +3,7 @@ import {
3
3
  type BinaryDocumentId,
4
4
  type DocumentId,
5
5
  } from "./types.js"
6
- import { v4 as uuid } from "uuid"
6
+ import * as Uuid from "uuid"
7
7
  import bs58check from "bs58check"
8
8
 
9
9
  export const urlPrefix = "automerge:"
@@ -20,10 +20,6 @@ export const parseAutomergeUrl = (url: AutomergeUrl) => {
20
20
  return { binaryDocumentId, documentId }
21
21
  }
22
22
 
23
- interface StringifyAutomergeUrlOptions {
24
- documentId: DocumentId | BinaryDocumentId
25
- }
26
-
27
23
  /**
28
24
  * Given a documentId in either canonical form, return an Automerge URL
29
25
  * Throws on invalid input.
@@ -33,7 +29,7 @@ interface StringifyAutomergeUrlOptions {
33
29
  */
34
30
  export const stringifyAutomergeUrl = ({
35
31
  documentId,
36
- }: StringifyAutomergeUrlOptions): AutomergeUrl => {
32
+ }: {documentId: DocumentId | BinaryDocumentId}): AutomergeUrl => {
37
33
  if (documentId instanceof Uint8Array)
38
34
  return (urlPrefix +
39
35
  binaryToDocumentId(documentId as BinaryDocumentId)) as AutomergeUrl
@@ -63,7 +59,7 @@ export const isValidAutomergeUrl = (str: string): str is AutomergeUrl => {
63
59
  */
64
60
  export const generateAutomergeUrl = (): AutomergeUrl =>
65
61
  stringifyAutomergeUrl({
66
- documentId: uuid(null, new Uint8Array(16)) as BinaryDocumentId,
62
+ documentId: Uuid.v4(null, new Uint8Array(16)) as BinaryDocumentId,
67
63
  })
68
64
 
69
65
  export const documentIdToBinary = (
@@ -74,6 +70,14 @@ export const documentIdToBinary = (
74
70
  export const binaryToDocumentId = (docId: BinaryDocumentId): DocumentId =>
75
71
  bs58check.encode(docId) as DocumentId
76
72
 
73
+ export const parseLegacyUUID = (str: string): AutomergeUrl | undefined => {
74
+ if (Uuid.validate(str)) {
75
+ const uuid = Uuid.parse(str) as BinaryDocumentId
76
+ return stringifyAutomergeUrl({ documentId: uuid })
77
+ }
78
+ return undefined
79
+ }
80
+
77
81
  /**
78
82
  * parts breaks up the URL into constituent pieces,
79
83
  * eventually this could include things like heads, so we use this structure
@@ -2,6 +2,7 @@ import { DocumentId, PeerId } from "./index.js"
2
2
  import { EphemeralMessageContents } from "./network/messages.js"
3
3
 
4
4
  // types
5
+ /** A randomly generated string created when the {@link Repo} starts up */
5
6
  export type SessionId = string & { __SessionId: false }
6
7
 
7
8
  export interface EphemeralDataPayload {
package/src/Repo.ts CHANGED
@@ -6,20 +6,36 @@ import { StorageSubsystem } from "./storage/StorageSubsystem.js"
6
6
  import { CollectionSynchronizer } from "./synchronizer/CollectionSynchronizer.js"
7
7
  import { type AutomergeUrl, DocumentId, PeerId } from "./types.js"
8
8
  import { v4 as uuid } from "uuid"
9
- import { parseAutomergeUrl, generateAutomergeUrl, isValidAutomergeUrl } from "./DocUrl.js"
9
+ import {
10
+ parseAutomergeUrl,
11
+ generateAutomergeUrl,
12
+ isValidAutomergeUrl,
13
+ parseLegacyUUID,
14
+ } from "./DocUrl.js"
10
15
 
11
16
  import { DocHandle } from "./DocHandle.js"
12
17
  import { EventEmitter } from "eventemitter3"
18
+ import bs58check from "bs58check"
13
19
 
14
20
  /** A Repo is a collection of documents with networking, syncing, and storage capabilities. */
15
- export class Repo extends EventEmitter<DocCollectionEvents> {
21
+ /** The `Repo` is the main entry point of this library
22
+ *
23
+ * @remarks
24
+ * To construct a `Repo` you will need an {@link StorageAdapter} and one or
25
+ * more {@link NetworkAdapter}s. Once you have a `Repo` you can use it to
26
+ * obtain {@link DocHandle}s.
27
+ */
28
+ export class Repo extends EventEmitter<RepoEvents> {
16
29
  #log: debug.Debugger
17
30
 
31
+ /** @hidden */
18
32
  networkSubsystem: NetworkSubsystem
33
+ /** @hidden */
19
34
  storageSubsystem?: StorageSubsystem
20
35
  #handleCache: Record<DocumentId, DocHandle<any>> = {}
21
36
 
22
37
  /** By default, we share generously with all peers. */
38
+ /** @hidden */
23
39
  sharePolicy: SharePolicy = async () => true
24
40
 
25
41
  constructor({ storage, network, peerId, sharePolicy }: RepoConfig) {
@@ -61,11 +77,14 @@ export class Repo extends EventEmitter<DocCollectionEvents> {
61
77
  handle.request()
62
78
  } else {
63
79
  handle.awaitNetwork()
64
- this.networkSubsystem.whenReady().then(() => {
65
- handle.networkReady()
66
- }).catch(err => {
67
- this.#log("error waiting for network", { err })
68
- })
80
+ this.networkSubsystem
81
+ .whenReady()
82
+ .then(() => {
83
+ handle.networkReady()
84
+ })
85
+ .catch(err => {
86
+ this.#log("error waiting for network", { err })
87
+ })
69
88
  }
70
89
 
71
90
  // Register the document with the synchronizer. This advertises our interest in the document.
@@ -120,7 +139,7 @@ export class Repo extends EventEmitter<DocCollectionEvents> {
120
139
  })
121
140
  }
122
141
 
123
- /** Returns an existing handle if we have it; creates one otherwise. */
142
+ /** Returns an existing handle if we have it; creates one otherwise. */
124
143
  #getHandle<T>(
125
144
  /** The documentId of the handle to look up or create */
126
145
  documentId: DocumentId,
@@ -183,7 +202,15 @@ export class Repo extends EventEmitter<DocCollectionEvents> {
183
202
  automergeUrl: AutomergeUrl
184
203
  ): DocHandle<T> {
185
204
  if (!isValidAutomergeUrl(automergeUrl)) {
186
- throw new Error(`Invalid AutomergeUrl: '${automergeUrl}'`)
205
+ let maybeAutomergeUrl = parseLegacyUUID(automergeUrl)
206
+ if (maybeAutomergeUrl) {
207
+ console.warn(
208
+ "Legacy UUID document ID detected, converting to AutomergeUrl. This will be removed in a future version."
209
+ )
210
+ automergeUrl = maybeAutomergeUrl
211
+ } else {
212
+ throw new Error(`Invalid AutomergeUrl: '${automergeUrl}'`)
213
+ }
187
214
  }
188
215
 
189
216
  const { documentId } = parseAutomergeUrl(automergeUrl)
@@ -240,23 +267,34 @@ export interface RepoConfig {
240
267
  sharePolicy?: SharePolicy
241
268
  }
242
269
 
270
+ /** A function that determines whether we should share a document with a peer
271
+ *
272
+ * @remarks
273
+ * This function is called by the {@link Repo} every time a new document is created
274
+ * or discovered (such as when another peer starts syncing with us). If this
275
+ * function returns `true` then the {@link Repo} will begin sharing the new
276
+ * document with the peer given by `peerId`.
277
+ * */
243
278
  export type SharePolicy = (
244
279
  peerId: PeerId,
245
280
  documentId?: DocumentId
246
281
  ) => Promise<boolean>
247
282
 
248
283
  // events & payloads
249
- interface DocCollectionEvents {
284
+ export interface RepoEvents {
285
+ /** A new document was created or discovered */
250
286
  document: (arg: DocumentPayload) => void
287
+ /** A document was deleted */
251
288
  "delete-document": (arg: DeleteDocumentPayload) => void
289
+ /** A document was marked as unavailable (we don't have it and none of our peers have it) */
252
290
  "unavailable-document": (arg: DeleteDocumentPayload) => void
253
291
  }
254
292
 
255
- interface DocumentPayload {
293
+ export interface DocumentPayload {
256
294
  handle: DocHandle<any>
257
295
  isNew: boolean
258
296
  }
259
297
 
260
- interface DeleteDocumentPayload {
298
+ export interface DeleteDocumentPayload {
261
299
  documentId: DocumentId
262
300
  }
package/src/index.ts CHANGED
@@ -1,10 +1,45 @@
1
- export { DocHandle, HandleState } from "./DocHandle.js"
2
- export type { DocHandleChangePayload } from "./DocHandle.js"
1
+ /**
2
+ * @packageDocumentation
3
+ *
4
+ * The [`automerge`](https://www.npmjs.com/package/@automerge/automerge) CRDT
5
+ * provides a core CRDT data structure and an implementation of a storage
6
+ * format and sync protocol but doesn't provide the plumbing to use these tools
7
+ * in a JS application. `automerge-repo` provides the plumbing.
8
+ *
9
+ * The main entry point is the {@link Repo} class, which you instantiate with
10
+ * a {@link StorageAdapter} and zero or more {@link NetworkAdapter}s. Once you
11
+ * have a repo you can use it to create {@link DocHandle}s. {@link DocHandle}s
12
+ * are a reference to a document, identified by a {@link AutomergeUrl}, a place to
13
+ * listen for changes to the document, and to make new changes.
14
+ *
15
+ * A typical example of how to use this library then might look like this:
16
+ *
17
+ * ```typescript
18
+ * import { Repo } from "@automerge/automerge-repo";
19
+ *
20
+ * const repo = new Repo({
21
+ * storage: <storage adapter>,
22
+ * network: [<network adapter>, <network adapter>]
23
+ * })
24
+ *
25
+ * const handle = repo.create
26
+ * ```
27
+ */
28
+
29
+ export { DocHandle, type HandleState, type DocHandleOptions, type DocHandleEvents } from "./DocHandle.js"
30
+ export type {
31
+ DocHandleChangePayload,
32
+ DocHandleDeletePayload,
33
+ DocHandleEphemeralMessagePayload,
34
+ DocHandleOutboundEphemeralMessagePayload,
35
+ DocHandleEncodedChangePayload,
36
+ } from "./DocHandle.js"
3
37
  export { NetworkAdapter } from "./network/NetworkAdapter.js"
4
38
  export type {
5
39
  OpenPayload,
6
40
  PeerCandidatePayload,
7
41
  PeerDisconnectedPayload,
42
+ NetworkAdapterEvents,
8
43
  } from "./network/NetworkAdapter.js"
9
44
 
10
45
  // This is a bit confusing right now, but:
@@ -13,17 +48,19 @@ export type {
13
48
  // and Message is (as of this writing) a union type for EphmeralMessage and SyncMessage
14
49
  export type {
15
50
  Message,
51
+ ArriveMessage,
52
+ WelcomeMessage,
16
53
  NetworkAdapterMessage,
17
54
  EphemeralMessage,
55
+ RequestMessage,
56
+ DocumentUnavailableMessage,
18
57
  SyncMessage,
58
+ SessionId,
19
59
  } from "./network/messages.js"
20
60
  export { isValidMessage } from "./network/messages.js"
21
61
 
22
- export { NetworkSubsystem } from "./network/NetworkSubsystem.js"
23
- export { Repo, type SharePolicy } from "./Repo.js"
62
+ export { Repo, type SharePolicy, type RepoConfig, type RepoEvents, type DeleteDocumentPayload, type DocumentPayload } from "./Repo.js"
24
63
  export { StorageAdapter, type StorageKey } from "./storage/StorageAdapter.js"
25
- export { StorageSubsystem } from "./storage/StorageSubsystem.js"
26
- export { CollectionSynchronizer } from "./synchronizer/CollectionSynchronizer.js"
27
64
  export {
28
65
  parseAutomergeUrl,
29
66
  isValidAutomergeUrl,
@@ -31,4 +68,5 @@ export {
31
68
  } from "./DocUrl.js"
32
69
  export * from "./types.js"
33
70
 
71
+ /** @hidden **/
34
72
  export * as cbor from "./helpers/cbor.js"