@automerge/automerge-repo 1.0.12 → 1.0.13

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.
@@ -0,0 +1,45 @@
1
+ import type { LegacyDocumentId, AutomergeUrl, BinaryDocumentId, DocumentId, AnyDocumentId } from "./types.js";
2
+ export declare const urlPrefix = "automerge:";
3
+ /** Given an Automerge URL, returns the DocumentId in both base58check-encoded form and binary form */
4
+ export declare const parseAutomergeUrl: (url: AutomergeUrl) => {
5
+ /** unencoded DocumentId */
6
+ binaryDocumentId: BinaryDocumentId;
7
+ /** encoded DocumentId */
8
+ documentId: DocumentId;
9
+ };
10
+ /**
11
+ * Given a documentId in either binary or base58check-encoded form, returns an Automerge URL.
12
+ * Throws on invalid input.
13
+ */
14
+ export declare const stringifyAutomergeUrl: (arg: UrlOptions | DocumentId | BinaryDocumentId) => AutomergeUrl;
15
+ /**
16
+ * Given a string, returns true if it is a valid Automerge URL. This function also acts as a type
17
+ * discriminator in Typescript.
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;
22
+ /**
23
+ * Returns a new Automerge URL with a random UUID documentId. Called by Repo.create(), and also used by tests.
24
+ */
25
+ export declare const generateAutomergeUrl: () => AutomergeUrl;
26
+ export declare const documentIdToBinary: (docId: DocumentId) => BinaryDocumentId | undefined;
27
+ export declare const binaryToDocumentId: (docId: BinaryDocumentId) => DocumentId;
28
+ export declare const parseLegacyUUID: (str: string) => AutomergeUrl | undefined;
29
+ /**
30
+ * Given any valid expression of a document ID, returns a DocumentId in base58check-encoded form.
31
+ *
32
+ * Currently supports:
33
+ * - base58check-encoded DocumentId
34
+ * - Automerge URL
35
+ * - legacy UUID
36
+ * - binary DocumentId
37
+ *
38
+ * Throws on invalid input.
39
+ */
40
+ export declare const interpretAsDocumentId: (id: AnyDocumentId) => DocumentId;
41
+ type UrlOptions = {
42
+ documentId: DocumentId | BinaryDocumentId;
43
+ };
44
+ export {};
45
+ //# sourceMappingURL=AutomergeUrl.d.ts.map
@@ -0,0 +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,iBAoBhD,CAAA;AAED;;;GAGG;AACH,eAAO,MAAM,mBAAmB,QACzB,MAAM,GAAG,SAAS,GAAG,IAAI,wBAU/B,CAAA;AAED,eAAO,MAAM,iBAAiB,QAAS,MAAM,sBAQ5C,CAAA;AAED,eAAO,MAAM,WAAW,QAAS,MAAM,4BACnB,CAAA;AAEpB;;GAEG;AACH,eAAO,MAAM,oBAAoB,QAAO,YAGvC,CAAA;AAED,eAAO,MAAM,kBAAkB,UAAW,UAAU,iCACW,CAAA;AAE/D,eAAO,MAAM,kBAAkB,UAAW,gBAAgB,eACnB,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"}
@@ -0,0 +1,108 @@
1
+ import * as Uuid from "uuid";
2
+ import bs58check from "bs58check";
3
+ export const urlPrefix = "automerge:";
4
+ /** Given an Automerge URL, returns the DocumentId in both base58check-encoded form and binary form */
5
+ export const parseAutomergeUrl = (url) => {
6
+ const regex = new RegExp(`^${urlPrefix}(\\w+)$`);
7
+ const [_, docMatch] = url.match(regex) || [];
8
+ const documentId = docMatch;
9
+ const binaryDocumentId = documentIdToBinary(documentId);
10
+ if (!binaryDocumentId)
11
+ throw new Error("Invalid document URL: " + url);
12
+ return {
13
+ /** unencoded DocumentId */
14
+ binaryDocumentId,
15
+ /** encoded DocumentId */
16
+ documentId,
17
+ };
18
+ };
19
+ /**
20
+ * Given a documentId in either binary or base58check-encoded form, returns an Automerge URL.
21
+ * Throws on invalid input.
22
+ */
23
+ export const stringifyAutomergeUrl = (arg) => {
24
+ let documentId = arg instanceof Uint8Array || typeof arg === "string"
25
+ ? arg
26
+ : "documentId" in arg
27
+ ? arg.documentId
28
+ : undefined;
29
+ const encodedDocumentId = documentId instanceof Uint8Array
30
+ ? binaryToDocumentId(documentId)
31
+ : typeof documentId === "string"
32
+ ? documentId
33
+ : undefined;
34
+ if (encodedDocumentId === undefined)
35
+ throw new Error("Invalid documentId: " + documentId);
36
+ return (urlPrefix + encodedDocumentId);
37
+ };
38
+ /**
39
+ * Given a string, returns true if it is a valid Automerge URL. This function also acts as a type
40
+ * discriminator in Typescript.
41
+ */
42
+ export const isValidAutomergeUrl = (str) => {
43
+ if (!str || !str.startsWith(urlPrefix))
44
+ return false;
45
+ const automergeUrl = str;
46
+ try {
47
+ const { documentId } = parseAutomergeUrl(automergeUrl);
48
+ return isValidDocumentId(documentId);
49
+ }
50
+ catch {
51
+ return false;
52
+ }
53
+ };
54
+ export const isValidDocumentId = (str) => {
55
+ // try to decode from base58
56
+ const binaryDocumentID = documentIdToBinary(str);
57
+ if (binaryDocumentID === undefined)
58
+ return false; // invalid base58check encoding
59
+ // confirm that the document ID is a valid UUID
60
+ const documentId = Uuid.stringify(binaryDocumentID);
61
+ return Uuid.validate(documentId);
62
+ };
63
+ export const isValidUuid = (str) => Uuid.validate(str);
64
+ /**
65
+ * Returns a new Automerge URL with a random UUID documentId. Called by Repo.create(), and also used by tests.
66
+ */
67
+ export const generateAutomergeUrl = () => {
68
+ const documentId = Uuid.v4(null, new Uint8Array(16));
69
+ return stringifyAutomergeUrl({ documentId });
70
+ };
71
+ export const documentIdToBinary = (docId) => bs58check.decodeUnsafe(docId);
72
+ export const binaryToDocumentId = (docId) => bs58check.encode(docId);
73
+ export const parseLegacyUUID = (str) => {
74
+ if (!Uuid.validate(str))
75
+ return undefined;
76
+ const documentId = Uuid.parse(str);
77
+ return stringifyAutomergeUrl({ documentId });
78
+ };
79
+ /**
80
+ * Given any valid expression of a document ID, returns a DocumentId in base58check-encoded form.
81
+ *
82
+ * Currently supports:
83
+ * - base58check-encoded DocumentId
84
+ * - Automerge URL
85
+ * - legacy UUID
86
+ * - binary DocumentId
87
+ *
88
+ * Throws on invalid input.
89
+ */
90
+ export const interpretAsDocumentId = (id) => {
91
+ // binary
92
+ if (id instanceof Uint8Array)
93
+ return binaryToDocumentId(id);
94
+ // url
95
+ if (isValidAutomergeUrl(id))
96
+ return parseAutomergeUrl(id).documentId;
97
+ // base58check
98
+ if (isValidDocumentId(id))
99
+ return id;
100
+ // legacy UUID
101
+ if (isValidUuid(id)) {
102
+ console.warn("Future versions will not support UUIDs as document IDs; use Automerge URLs instead.");
103
+ const binaryDocumentID = Uuid.parse(id);
104
+ return binaryToDocumentId(binaryDocumentID);
105
+ }
106
+ // none of the above
107
+ throw new Error(`Invalid AutomergeUrl: '${id}'`);
108
+ };
package/dist/DocHandle.js CHANGED
@@ -3,7 +3,7 @@ import debug from "debug";
3
3
  import { EventEmitter } from "eventemitter3";
4
4
  import { assign, createMachine, interpret, } from "xstate";
5
5
  import { waitFor } from "xstate/lib/waitFor.js";
6
- import { stringifyAutomergeUrl } from "./DocUrl.js";
6
+ import { stringifyAutomergeUrl } from "./AutomergeUrl.js";
7
7
  import { encode } from "./helpers/cbor.js";
8
8
  import { headsAreSame } from "./helpers/headsAreSame.js";
9
9
  import { withTimeout } from "./helpers/withTimeout.js";
package/dist/Repo.d.ts CHANGED
@@ -4,7 +4,7 @@ import { NetworkAdapter } from "./network/NetworkAdapter.js";
4
4
  import { NetworkSubsystem } from "./network/NetworkSubsystem.js";
5
5
  import { StorageAdapter } from "./storage/StorageAdapter.js";
6
6
  import { StorageSubsystem } from "./storage/StorageSubsystem.js";
7
- import { DocumentId, PeerId, type AutomergeUrl } from "./types.js";
7
+ import type { AnyDocumentId, DocumentId, PeerId } from "./types.js";
8
8
  /** A Repo is a collection of documents with networking, syncing, and storage capabilities. */
9
9
  /** The `Repo` is the main entry point of this library
10
10
  *
@@ -55,11 +55,11 @@ export declare class Repo extends EventEmitter<RepoEvents> {
55
55
  * event to advertise interest in the document.
56
56
  */
57
57
  find<T>(
58
- /** The documentId of the handle to retrieve */
59
- automergeUrl: AutomergeUrl): DocHandle<T>;
58
+ /** The url or documentId of the handle to retrieve */
59
+ id: AnyDocumentId): DocHandle<T>;
60
60
  delete(
61
- /** The documentId of the handle to delete */
62
- id: DocumentId | AutomergeUrl): void;
61
+ /** The url or documentId of the handle to delete */
62
+ id: AnyDocumentId): void;
63
63
  }
64
64
  export interface RepoConfig {
65
65
  /** Our unique identifier */
@@ -1 +1 @@
1
- {"version":3,"file":"Repo.d.ts","sourceRoot":"","sources":["../src/Repo.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,YAAY,EAAE,MAAM,eAAe,CAAA;AAC5C,OAAO,EAAE,SAAS,EAAiC,MAAM,gBAAgB,CAAA;AAQzE,OAAO,EAAE,cAAc,EAAE,MAAM,6BAA6B,CAAA;AAC5D,OAAO,EAAE,gBAAgB,EAAE,MAAM,+BAA+B,CAAA;AAChE,OAAO,EAAE,cAAc,EAAE,MAAM,6BAA6B,CAAA;AAC5D,OAAO,EAAE,gBAAgB,EAAE,MAAM,+BAA+B,CAAA;AAEhE,OAAO,EAAE,UAAU,EAAE,MAAM,EAAE,KAAK,YAAY,EAAE,MAAM,YAAY,CAAA;AAElE,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;IAItB,sDAAsD;IACtD,cAAc;IACd,WAAW,EAAE,WAAW,CAAmB;gBAE/B,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,WAAW,EAAE,EAAE,UAAU;IA8HjE,8CAA8C;IAC9C,IAAI,OAAO,uCAEV;IAED;;;;OAIG;IACH,MAAM,CAAC,CAAC,KAAK,SAAS,CAAC,CAAC,CAAC;IA0BzB;;;;;;;;;;;;;;OAcG;IACH,KAAK,CAAC,CAAC,EAAE,YAAY,EAAE,SAAS,CAAC,CAAC,CAAC;IAuBnC;;;OAGG;IACH,IAAI,CAAC,CAAC;IACJ,+CAA+C;IAC/C,YAAY,EAAE,YAAY,GACzB,SAAS,CAAC,CAAC,CAAC;IAgCf,MAAM;IACJ,6CAA6C;IAC7C,EAAE,EAAE,UAAU,GAAG,YAAY;CAUhC;AAED,MAAM,WAAW,UAAU;IACzB,4BAA4B;IAC5B,MAAM,CAAC,EAAE,MAAM,CAAA;IAEf,gDAAgD;IAChD,OAAO,CAAC,EAAE,cAAc,CAAA;IAExB,oDAAoD;IACpD,OAAO,EAAE,cAAc,EAAE,CAAA;IAEzB;;;OAGG;IACH,WAAW,CAAC,EAAE,WAAW,CAAA;CAC1B;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;IACtB,KAAK,EAAE,OAAO,CAAA;CACf;AAED,MAAM,WAAW,qBAAqB;IACpC,UAAU,EAAE,UAAU,CAAA;CACvB"}
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;AAEzE,OAAO,EAAE,cAAc,EAAE,MAAM,6BAA6B,CAAA;AAC5D,OAAO,EAAE,gBAAgB,EAAE,MAAM,+BAA+B,CAAA;AAChE,OAAO,EAAE,cAAc,EAAE,MAAM,6BAA6B,CAAA;AAC5D,OAAO,EAAE,gBAAgB,EAAE,MAAM,+BAA+B,CAAA;AAEhE,OAAO,KAAK,EAAE,aAAa,EAAE,UAAU,EAAE,MAAM,EAAE,MAAM,YAAY,CAAA;AAEnE,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;IAItB,sDAAsD;IACtD,cAAc;IACd,WAAW,EAAE,WAAW,CAAmB;gBAE/B,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,WAAW,EAAE,EAAE,UAAU;IA8HjE,8CAA8C;IAC9C,IAAI,OAAO,uCAEV;IAED;;;;OAIG;IACH,MAAM,CAAC,CAAC,KAAK,SAAS,CAAC,CAAC,CAAC;IA0BzB;;;;;;;;;;;;;;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;IAqBf,MAAM;IACJ,oDAAoD;IACpD,EAAE,EAAE,aAAa;CAUpB;AAED,MAAM,WAAW,UAAU;IACzB,4BAA4B;IAC5B,MAAM,CAAC,EAAE,MAAM,CAAA;IAEf,gDAAgD;IAChD,OAAO,CAAC,EAAE,cAAc,CAAA;IAExB,oDAAoD;IACpD,OAAO,EAAE,cAAc,EAAE,CAAA;IAEzB;;;OAGG;IACH,WAAW,CAAC,EAAE,WAAW,CAAA;CAC1B;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;IACtB,KAAK,EAAE,OAAO,CAAA;CACf;AAED,MAAM,WAAW,qBAAqB;IACpC,UAAU,EAAE,UAAU,CAAA;CACvB"}
package/dist/Repo.js CHANGED
@@ -1,8 +1,8 @@
1
1
  import { next as Automerge } from "@automerge/automerge";
2
2
  import debug from "debug";
3
3
  import { EventEmitter } from "eventemitter3";
4
+ import { generateAutomergeUrl, interpretAsDocumentId, parseAutomergeUrl, } from "./AutomergeUrl.js";
4
5
  import { DocHandle } from "./DocHandle.js";
5
- import { generateAutomergeUrl, isValidAutomergeUrl, parseAutomergeUrl, parseLegacyUUID, } from "./DocUrl.js";
6
6
  import { throttle } from "./helpers/throttle.js";
7
7
  import { NetworkSubsystem } from "./network/NetworkSubsystem.js";
8
8
  import { StorageSubsystem } from "./storage/StorageSubsystem.js";
@@ -198,19 +198,9 @@ export class Repo extends EventEmitter {
198
198
  * event to advertise interest in the document.
199
199
  */
200
200
  find(
201
- /** The documentId of the handle to retrieve */
202
- automergeUrl) {
203
- if (!isValidAutomergeUrl(automergeUrl)) {
204
- const maybeAutomergeUrl = parseLegacyUUID(automergeUrl);
205
- if (maybeAutomergeUrl) {
206
- console.warn("Legacy UUID document ID detected, converting to AutomergeUrl. This will be removed in a future version.");
207
- automergeUrl = maybeAutomergeUrl;
208
- }
209
- else {
210
- throw new Error(`Invalid AutomergeUrl: '${automergeUrl}'`);
211
- }
212
- }
213
- const { documentId } = parseAutomergeUrl(automergeUrl);
201
+ /** The url or documentId of the handle to retrieve */
202
+ id) {
203
+ const documentId = interpretAsDocumentId(id);
214
204
  // If we have the handle cached, return it
215
205
  if (this.#handleCache[documentId]) {
216
206
  if (this.#handleCache[documentId].isUnavailable()) {
@@ -228,13 +218,12 @@ export class Repo extends EventEmitter {
228
218
  return handle;
229
219
  }
230
220
  delete(
231
- /** The documentId of the handle to delete */
221
+ /** The url or documentId of the handle to delete */
232
222
  id) {
233
- if (isValidAutomergeUrl(id))
234
- id = parseAutomergeUrl(id).documentId;
235
- const handle = this.#getHandle(id, false);
223
+ const documentId = interpretAsDocumentId(id);
224
+ const handle = this.#getHandle(documentId, false);
236
225
  handle.delete();
237
- delete this.#handleCache[id];
238
- this.emit("delete-document", { documentId: id });
226
+ delete this.#handleCache[documentId];
227
+ this.emit("delete-document", { documentId });
239
228
  }
240
229
  }
package/dist/index.d.ts CHANGED
@@ -26,7 +26,7 @@
26
26
  * ```
27
27
  */
28
28
  export { DocHandle } from "./DocHandle.js";
29
- export { isValidAutomergeUrl, parseAutomergeUrl, stringifyAutomergeUrl, } from "./DocUrl.js";
29
+ export { isValidAutomergeUrl, parseAutomergeUrl, stringifyAutomergeUrl, } from "./AutomergeUrl.js";
30
30
  export { Repo } from "./Repo.js";
31
31
  export { NetworkAdapter } from "./network/NetworkAdapter.js";
32
32
  export { isValidRepoMessage } from "./network/messages.js";
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AAEH,OAAO,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAA;AAC1C,OAAO,EACL,mBAAmB,EACnB,iBAAiB,EACjB,qBAAqB,GACtB,MAAM,aAAa,CAAA;AACpB,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAA;AAChC,OAAO,EAAE,cAAc,EAAE,MAAM,6BAA6B,CAAA;AAC5D,OAAO,EAAE,kBAAkB,EAAE,MAAM,uBAAuB,CAAA;AAC1D,OAAO,EAAE,cAAc,EAAE,MAAM,6BAA6B,CAAA;AAE5D,eAAe;AACf,OAAO,KAAK,IAAI,MAAM,mBAAmB,CAAA;AAIzC,YAAY,EACV,sBAAsB,EACtB,sBAAsB,EACtB,6BAA6B,EAC7B,gCAAgC,EAChC,eAAe,EACf,gBAAgB,EAChB,wCAAwC,EACxC,WAAW,GACZ,MAAM,gBAAgB,CAAA;AACvB,YAAY,EACV,qBAAqB,EACrB,eAAe,EACf,UAAU,EACV,UAAU,EACV,WAAW,GACZ,MAAM,WAAW,CAAA;AAClB,YAAY,EACV,oBAAoB,EACpB,WAAW,EACX,oBAAoB,EACpB,uBAAuB,GACxB,MAAM,6BAA6B,CAAA;AACpC,YAAY,EACV,aAAa,EACb,0BAA0B,EAC1B,gBAAgB,EAChB,OAAO,EACP,WAAW,EACX,cAAc,EACd,WAAW,EACX,cAAc,GACf,MAAM,uBAAuB,CAAA;AAC9B,YAAY,EAAE,UAAU,EAAE,MAAM,6BAA6B,CAAA;AAC7D,cAAc,YAAY,CAAA"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AAEH,OAAO,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAA;AAC1C,OAAO,EACL,mBAAmB,EACnB,iBAAiB,EACjB,qBAAqB,GACtB,MAAM,mBAAmB,CAAA;AAC1B,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAA;AAChC,OAAO,EAAE,cAAc,EAAE,MAAM,6BAA6B,CAAA;AAC5D,OAAO,EAAE,kBAAkB,EAAE,MAAM,uBAAuB,CAAA;AAC1D,OAAO,EAAE,cAAc,EAAE,MAAM,6BAA6B,CAAA;AAE5D,eAAe;AACf,OAAO,KAAK,IAAI,MAAM,mBAAmB,CAAA;AAIzC,YAAY,EACV,sBAAsB,EACtB,sBAAsB,EACtB,6BAA6B,EAC7B,gCAAgC,EAChC,eAAe,EACf,gBAAgB,EAChB,wCAAwC,EACxC,WAAW,GACZ,MAAM,gBAAgB,CAAA;AACvB,YAAY,EACV,qBAAqB,EACrB,eAAe,EACf,UAAU,EACV,UAAU,EACV,WAAW,GACZ,MAAM,WAAW,CAAA;AAClB,YAAY,EACV,oBAAoB,EACpB,WAAW,EACX,oBAAoB,EACpB,uBAAuB,GACxB,MAAM,6BAA6B,CAAA;AACpC,YAAY,EACV,aAAa,EACb,0BAA0B,EAC1B,gBAAgB,EAChB,OAAO,EACP,WAAW,EACX,cAAc,EACd,WAAW,EACX,cAAc,GACf,MAAM,uBAAuB,CAAA;AAC9B,YAAY,EAAE,UAAU,EAAE,MAAM,6BAA6B,CAAA;AAC7D,cAAc,YAAY,CAAA"}
package/dist/index.js CHANGED
@@ -26,7 +26,7 @@
26
26
  * ```
27
27
  */
28
28
  export { DocHandle } from "./DocHandle.js";
29
- export { isValidAutomergeUrl, parseAutomergeUrl, stringifyAutomergeUrl, } from "./DocUrl.js";
29
+ export { isValidAutomergeUrl, parseAutomergeUrl, stringifyAutomergeUrl, } from "./AutomergeUrl.js";
30
30
  export { Repo } from "./Repo.js";
31
31
  export { NetworkAdapter } from "./network/NetworkAdapter.js";
32
32
  export { isValidRepoMessage } from "./network/messages.js";
@@ -1,5 +1,5 @@
1
1
  import debug from "debug";
2
- import { stringifyAutomergeUrl } from "../DocUrl.js";
2
+ import { stringifyAutomergeUrl } from "../AutomergeUrl.js";
3
3
  import { DocSynchronizer } from "./DocSynchronizer.js";
4
4
  import { Synchronizer } from "./Synchronizer.js";
5
5
  const log = debug("automerge-repo:collectionsync");
package/dist/types.d.ts CHANGED
@@ -1,23 +1,31 @@
1
- /** The ID of a document. Typically you should use a {@link AutomergeUrl} instead.
2
- */
3
- export type DocumentId = string & {
4
- __documentId: true;
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
- *
1
+ /**
2
+ * A branded string representing a URL for a document, in the form `automerge:<base58check encoded
3
+ * string>`; for example, `automerge:4NMNnkMhL8jXrdJ9jamS58PAVdXu`.
12
4
  */
13
5
  export type AutomergeUrl = string & {
14
6
  __documentUrl: true;
15
7
  };
16
- /** A document ID as a Uint8Array instead of a bas58 encoded string. Typically you should use a {@link AutomergeUrl} instead.
8
+ /**
9
+ * The base58check-encoded UUID of a document. This is the string following the `automerge:`
10
+ * protocol prefix in an AutomergeUrl; for example, `4NMNnkMhL8jXrdJ9jamS58PAVdXu`. When recording
11
+ * links to an Automerge document in another Automerge document, you should store a
12
+ * {@link AutomergeUrl} instead.
17
13
  */
14
+ export type DocumentId = string & {
15
+ __documentId: true;
16
+ };
17
+ /** The unencoded UUID of a document. Typically you should use a {@link AutomergeUrl} instead. */
18
18
  export type BinaryDocumentId = Uint8Array & {
19
19
  __binaryDocumentId: true;
20
20
  };
21
+ /**
22
+ * A UUID encoded as a hex string. As of v1.0, a {@link DocumentID} is stored as a base58-encoded string with a checksum.
23
+ * Support for this format will be removed in a future version.
24
+ */
25
+ export type LegacyDocumentId = string & {
26
+ __legacyDocumentId: true;
27
+ };
28
+ export type AnyDocumentId = AutomergeUrl | DocumentId | BinaryDocumentId | LegacyDocumentId;
21
29
  /** A branded type for peer IDs */
22
30
  export type PeerId = string & {
23
31
  __peerId: true;
@@ -1 +1 @@
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;AAExD;;;;;;GAMG;AACH,MAAM,MAAM,YAAY,GAAG,MAAM,GAAG;IAAE,aAAa,EAAE,IAAI,CAAA;CAAE,CAAA;AAE3D;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,IAAI,CAAA;CAAE,CAAA;AAEhD,0EAA0E;AAC1E,MAAM,MAAM,SAAS,GAAG,MAAM,GAAG;IAAE,WAAW,EAAE,IAAI,CAAA;CAAE,CAAA"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,MAAM,MAAM,YAAY,GAAG,MAAM,GAAG;IAAE,aAAa,EAAE,IAAI,CAAA;CAAE,CAAA;AAE3D;;;;;GAKG;AACH,MAAM,MAAM,UAAU,GAAG,MAAM,GAAG;IAAE,YAAY,EAAE,IAAI,CAAA;CAAE,CAAA;AAExD,iGAAiG;AACjG,MAAM,MAAM,gBAAgB,GAAG,UAAU,GAAG;IAAE,kBAAkB,EAAE,IAAI,CAAA;CAAE,CAAA;AAExE;;;GAGG;AACH,MAAM,MAAM,gBAAgB,GAAG,MAAM,GAAG;IAAE,kBAAkB,EAAE,IAAI,CAAA;CAAE,CAAA;AAEpE,MAAM,MAAM,aAAa,GACrB,YAAY,GACZ,UAAU,GACV,gBAAgB,GAChB,gBAAgB,CAAA;AAEpB,kCAAkC;AAClC,MAAM,MAAM,MAAM,GAAG,MAAM,GAAG;IAAE,QAAQ,EAAE,IAAI,CAAA;CAAE,CAAA;AAEhD,0EAA0E;AAC1E,MAAM,MAAM,SAAS,GAAG,MAAM,GAAG;IAAE,WAAW,EAAE,IAAI,CAAA;CAAE,CAAA"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@automerge/automerge-repo",
3
- "version": "1.0.12",
3
+ "version": "1.0.13",
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>",
@@ -57,5 +57,5 @@
57
57
  "publishConfig": {
58
58
  "access": "public"
59
59
  },
60
- "gitHead": "254bad1c774fa2a881265aaad5283af231bf72eb"
60
+ "gitHead": "48ca7b968758d7fd95f13fa03dc69d452cdd15f5"
61
61
  }
@@ -0,0 +1,144 @@
1
+ import type {
2
+ LegacyDocumentId,
3
+ AutomergeUrl,
4
+ BinaryDocumentId,
5
+ DocumentId,
6
+ AnyDocumentId,
7
+ } from "./types.js"
8
+ import * as Uuid from "uuid"
9
+ import bs58check from "bs58check"
10
+
11
+ export const urlPrefix = "automerge:"
12
+
13
+ /** Given an Automerge URL, returns the DocumentId in both base58check-encoded form and binary form */
14
+ export const parseAutomergeUrl = (url: AutomergeUrl) => {
15
+ const regex = new RegExp(`^${urlPrefix}(\\w+)$`)
16
+ const [_, docMatch] = url.match(regex) || []
17
+ const documentId = docMatch as DocumentId
18
+ const binaryDocumentId = documentIdToBinary(documentId)
19
+
20
+ if (!binaryDocumentId) throw new Error("Invalid document URL: " + url)
21
+ return {
22
+ /** unencoded DocumentId */
23
+ binaryDocumentId,
24
+ /** encoded DocumentId */
25
+ documentId,
26
+ }
27
+ }
28
+
29
+ /**
30
+ * Given a documentId in either binary or base58check-encoded form, returns an Automerge URL.
31
+ * Throws on invalid input.
32
+ */
33
+ export const stringifyAutomergeUrl = (
34
+ arg: UrlOptions | DocumentId | BinaryDocumentId
35
+ ) => {
36
+ let documentId =
37
+ arg instanceof Uint8Array || typeof arg === "string"
38
+ ? arg
39
+ : "documentId" in arg
40
+ ? arg.documentId
41
+ : undefined
42
+
43
+ const encodedDocumentId =
44
+ documentId instanceof Uint8Array
45
+ ? binaryToDocumentId(documentId)
46
+ : typeof documentId === "string"
47
+ ? documentId
48
+ : undefined
49
+
50
+ if (encodedDocumentId === undefined)
51
+ throw new Error("Invalid documentId: " + documentId)
52
+
53
+ return (urlPrefix + encodedDocumentId) as AutomergeUrl
54
+ }
55
+
56
+ /**
57
+ * Given a string, returns true if it is a valid Automerge URL. This function also acts as a type
58
+ * discriminator in Typescript.
59
+ */
60
+ export const isValidAutomergeUrl = (
61
+ str: string | undefined | null
62
+ ): str is AutomergeUrl => {
63
+ if (!str || !str.startsWith(urlPrefix)) return false
64
+ const automergeUrl = str as AutomergeUrl
65
+ try {
66
+ const { documentId } = parseAutomergeUrl(automergeUrl)
67
+ return isValidDocumentId(documentId)
68
+ } catch {
69
+ return false
70
+ }
71
+ }
72
+
73
+ export const isValidDocumentId = (str: string): str is DocumentId => {
74
+ // try to decode from base58
75
+ const binaryDocumentID = documentIdToBinary(str as DocumentId)
76
+ if (binaryDocumentID === undefined) return false // invalid base58check encoding
77
+
78
+ // confirm that the document ID is a valid UUID
79
+ const documentId = Uuid.stringify(binaryDocumentID)
80
+ return Uuid.validate(documentId)
81
+ }
82
+
83
+ export const isValidUuid = (str: string): str is LegacyDocumentId =>
84
+ Uuid.validate(str)
85
+
86
+ /**
87
+ * Returns a new Automerge URL with a random UUID documentId. Called by Repo.create(), and also used by tests.
88
+ */
89
+ export const generateAutomergeUrl = (): AutomergeUrl => {
90
+ const documentId = Uuid.v4(null, new Uint8Array(16)) as BinaryDocumentId
91
+ return stringifyAutomergeUrl({ documentId })
92
+ }
93
+
94
+ export const documentIdToBinary = (docId: DocumentId) =>
95
+ bs58check.decodeUnsafe(docId) as BinaryDocumentId | undefined
96
+
97
+ export const binaryToDocumentId = (docId: BinaryDocumentId) =>
98
+ bs58check.encode(docId) as DocumentId
99
+
100
+ export const parseLegacyUUID = (str: string) => {
101
+ if (!Uuid.validate(str)) return undefined
102
+ const documentId = Uuid.parse(str) as BinaryDocumentId
103
+ return stringifyAutomergeUrl({ documentId })
104
+ }
105
+
106
+ /**
107
+ * Given any valid expression of a document ID, returns a DocumentId in base58check-encoded form.
108
+ *
109
+ * Currently supports:
110
+ * - base58check-encoded DocumentId
111
+ * - Automerge URL
112
+ * - legacy UUID
113
+ * - binary DocumentId
114
+ *
115
+ * Throws on invalid input.
116
+ */
117
+ export const interpretAsDocumentId = (id: AnyDocumentId) => {
118
+ // binary
119
+ if (id instanceof Uint8Array) return binaryToDocumentId(id)
120
+
121
+ // url
122
+ if (isValidAutomergeUrl(id)) return parseAutomergeUrl(id).documentId
123
+
124
+ // base58check
125
+ if (isValidDocumentId(id)) return id
126
+
127
+ // legacy UUID
128
+ if (isValidUuid(id)) {
129
+ console.warn(
130
+ "Future versions will not support UUIDs as document IDs; use Automerge URLs instead."
131
+ )
132
+ const binaryDocumentID = Uuid.parse(id) as BinaryDocumentId
133
+ return binaryToDocumentId(binaryDocumentID)
134
+ }
135
+
136
+ // none of the above
137
+ throw new Error(`Invalid AutomergeUrl: '${id}'`)
138
+ }
139
+
140
+ // TYPES
141
+
142
+ type UrlOptions = {
143
+ documentId: DocumentId | BinaryDocumentId
144
+ }
package/src/DocHandle.ts CHANGED
@@ -14,7 +14,7 @@ import {
14
14
  TypegenDisabled,
15
15
  } from "xstate"
16
16
  import { waitFor } from "xstate/lib/waitFor.js"
17
- import { stringifyAutomergeUrl } from "./DocUrl.js"
17
+ import { stringifyAutomergeUrl } from "./AutomergeUrl.js"
18
18
  import { encode } from "./helpers/cbor.js"
19
19
  import { headsAreSame } from "./helpers/headsAreSame.js"
20
20
  import { withTimeout } from "./helpers/withTimeout.js"
package/src/Repo.ts CHANGED
@@ -1,20 +1,19 @@
1
1
  import { next as Automerge } from "@automerge/automerge"
2
2
  import debug from "debug"
3
3
  import { EventEmitter } from "eventemitter3"
4
- import { DocHandle, DocHandleEncodedChangePayload } from "./DocHandle.js"
5
4
  import {
6
5
  generateAutomergeUrl,
7
- isValidAutomergeUrl,
6
+ interpretAsDocumentId,
8
7
  parseAutomergeUrl,
9
- parseLegacyUUID,
10
- } from "./DocUrl.js"
8
+ } from "./AutomergeUrl.js"
9
+ import { DocHandle, DocHandleEncodedChangePayload } from "./DocHandle.js"
11
10
  import { throttle } from "./helpers/throttle.js"
12
11
  import { NetworkAdapter } from "./network/NetworkAdapter.js"
13
12
  import { NetworkSubsystem } from "./network/NetworkSubsystem.js"
14
13
  import { StorageAdapter } from "./storage/StorageAdapter.js"
15
14
  import { StorageSubsystem } from "./storage/StorageSubsystem.js"
16
15
  import { CollectionSynchronizer } from "./synchronizer/CollectionSynchronizer.js"
17
- import { DocumentId, PeerId, type AutomergeUrl } from "./types.js"
16
+ import type { AnyDocumentId, DocumentId, PeerId } from "./types.js"
18
17
 
19
18
  /** A Repo is a collection of documents with networking, syncing, and storage capabilities. */
20
19
  /** The `Repo` is the main entry point of this library
@@ -247,22 +246,11 @@ export class Repo extends EventEmitter<RepoEvents> {
247
246
  * event to advertise interest in the document.
248
247
  */
249
248
  find<T>(
250
- /** The documentId of the handle to retrieve */
251
- automergeUrl: AutomergeUrl
249
+ /** The url or documentId of the handle to retrieve */
250
+ id: AnyDocumentId
252
251
  ): DocHandle<T> {
253
- if (!isValidAutomergeUrl(automergeUrl)) {
254
- const maybeAutomergeUrl = parseLegacyUUID(automergeUrl)
255
- if (maybeAutomergeUrl) {
256
- console.warn(
257
- "Legacy UUID document ID detected, converting to AutomergeUrl. This will be removed in a future version."
258
- )
259
- automergeUrl = maybeAutomergeUrl
260
- } else {
261
- throw new Error(`Invalid AutomergeUrl: '${automergeUrl}'`)
262
- }
263
- }
252
+ const documentId = interpretAsDocumentId(id)
264
253
 
265
- const { documentId } = parseAutomergeUrl(automergeUrl)
266
254
  // If we have the handle cached, return it
267
255
  if (this.#handleCache[documentId]) {
268
256
  if (this.#handleCache[documentId].isUnavailable()) {
@@ -282,16 +270,16 @@ export class Repo extends EventEmitter<RepoEvents> {
282
270
  }
283
271
 
284
272
  delete(
285
- /** The documentId of the handle to delete */
286
- id: DocumentId | AutomergeUrl
273
+ /** The url or documentId of the handle to delete */
274
+ id: AnyDocumentId
287
275
  ) {
288
- if (isValidAutomergeUrl(id)) id = parseAutomergeUrl(id).documentId
276
+ const documentId = interpretAsDocumentId(id)
289
277
 
290
- const handle = this.#getHandle(id, false)
278
+ const handle = this.#getHandle(documentId, false)
291
279
  handle.delete()
292
280
 
293
- delete this.#handleCache[id]
294
- this.emit("delete-document", { documentId: id })
281
+ delete this.#handleCache[documentId]
282
+ this.emit("delete-document", { documentId })
295
283
  }
296
284
  }
297
285
 
package/src/index.ts CHANGED
@@ -31,7 +31,7 @@ export {
31
31
  isValidAutomergeUrl,
32
32
  parseAutomergeUrl,
33
33
  stringifyAutomergeUrl,
34
- } from "./DocUrl.js"
34
+ } from "./AutomergeUrl.js"
35
35
  export { Repo } from "./Repo.js"
36
36
  export { NetworkAdapter } from "./network/NetworkAdapter.js"
37
37
  export { isValidRepoMessage } from "./network/messages.js"
@@ -1,6 +1,6 @@
1
1
  import debug from "debug"
2
2
  import { DocHandle } from "../DocHandle.js"
3
- import { stringifyAutomergeUrl } from "../DocUrl.js"
3
+ import { stringifyAutomergeUrl } from "../AutomergeUrl.js"
4
4
  import { Repo } from "../Repo.js"
5
5
  import { RepoMessage } from "../network/messages.js"
6
6
  import { DocumentId, PeerId } from "../types.js"
package/src/types.ts CHANGED
@@ -1,20 +1,32 @@
1
- /** The ID of a document. Typically you should use a {@link AutomergeUrl} instead.
2
- */
3
- export type DocumentId = string & { __documentId: true } // for logging
4
-
5
- /** A branded string representing a URL for a document
6
- *
7
- * @remarks
8
- * An automerge URL has the form `automerge:<base58 encoded string>`. This
9
- * type is returned from various routines which validate a url.
10
- *
1
+ /**
2
+ * A branded string representing a URL for a document, in the form `automerge:<base58check encoded
3
+ * string>`; for example, `automerge:4NMNnkMhL8jXrdJ9jamS58PAVdXu`.
11
4
  */
12
5
  export type AutomergeUrl = string & { __documentUrl: true } // for opening / linking
13
6
 
14
- /** A document ID as a Uint8Array instead of a bas58 encoded string. Typically you should use a {@link AutomergeUrl} instead.
7
+ /**
8
+ * The base58check-encoded UUID of a document. This is the string following the `automerge:`
9
+ * protocol prefix in an AutomergeUrl; for example, `4NMNnkMhL8jXrdJ9jamS58PAVdXu`. When recording
10
+ * links to an Automerge document in another Automerge document, you should store a
11
+ * {@link AutomergeUrl} instead.
15
12
  */
13
+ export type DocumentId = string & { __documentId: true } // for logging
14
+
15
+ /** The unencoded UUID of a document. Typically you should use a {@link AutomergeUrl} instead. */
16
16
  export type BinaryDocumentId = Uint8Array & { __binaryDocumentId: true } // for storing / syncing
17
17
 
18
+ /**
19
+ * A UUID encoded as a hex string. As of v1.0, a {@link DocumentID} is stored as a base58-encoded string with a checksum.
20
+ * Support for this format will be removed in a future version.
21
+ */
22
+ export type LegacyDocumentId = string & { __legacyDocumentId: true }
23
+
24
+ export type AnyDocumentId =
25
+ | AutomergeUrl
26
+ | DocumentId
27
+ | BinaryDocumentId
28
+ | LegacyDocumentId
29
+
18
30
  /** A branded type for peer IDs */
19
31
  export type PeerId = string & { __peerId: true }
20
32
 
@@ -0,0 +1,100 @@
1
+ import assert from "assert"
2
+ import bs58check from "bs58check"
3
+ import { describe, it } from "vitest"
4
+ import {
5
+ generateAutomergeUrl,
6
+ isValidAutomergeUrl,
7
+ parseAutomergeUrl,
8
+ stringifyAutomergeUrl,
9
+ } from "../src/AutomergeUrl.js"
10
+ import type {
11
+ AutomergeUrl,
12
+ BinaryDocumentId,
13
+ DocumentId,
14
+ } from "../src/types.js"
15
+
16
+ const goodUrl = "automerge:4NMNnkMhL8jXrdJ9jamS58PAVdXu" as AutomergeUrl
17
+ const badChecksumUrl = "automerge:badbadbad" as AutomergeUrl
18
+ const badPrefixUrl = "yjs😉:4NMNnkMhL8jXrdJ9jamS58PAVdXu" as AutomergeUrl
19
+
20
+ const goodDocumentId = "4NMNnkMhL8jXrdJ9jamS58PAVdXu" as DocumentId
21
+ const badChecksumDocumentId = "badbadbad" as DocumentId
22
+ const badUuidDocumentId = bs58check.encode(
23
+ new Uint8Array([1, 2, 3, 4, 42, -1, 69, 777])
24
+ ) as DocumentId
25
+
26
+ const goodBinaryDocumentId = Uint8Array.from([
27
+ 241, 194, 156, 132, 116, 200, 74, 222, 184, 0, 190, 71, 98, 125, 51, 191,
28
+ ]) as BinaryDocumentId
29
+
30
+ describe("AutomergeUrl", () => {
31
+ describe("generateAutomergeUrl", () => {
32
+ it("should generate a valid Automerge URL", () => {
33
+ const url = generateAutomergeUrl()
34
+ assert(url.startsWith("automerge:"))
35
+ assert(parseAutomergeUrl(url).binaryDocumentId)
36
+ })
37
+ })
38
+
39
+ describe("stringifyAutomergeUrl", () => {
40
+ it("should stringify a binary document ID", () => {
41
+ const url = stringifyAutomergeUrl({ documentId: goodBinaryDocumentId })
42
+ assert.strictEqual(url, goodUrl)
43
+ })
44
+
45
+ it("should stringify a string document ID", () => {
46
+ const url = stringifyAutomergeUrl({ documentId: goodDocumentId })
47
+ assert.strictEqual(url, goodUrl)
48
+ })
49
+
50
+ it("supports passing a document ID without wrapping it in an object", () => {
51
+ const url1 = stringifyAutomergeUrl(goodDocumentId)
52
+ const url2 = stringifyAutomergeUrl({ documentId: goodDocumentId })
53
+ assert.equal(url1, url2)
54
+ })
55
+ })
56
+
57
+ describe("parseAutomergeUrl", () => {
58
+ it("should parse a valid url", () => {
59
+ const { binaryDocumentId, documentId } = parseAutomergeUrl(goodUrl)
60
+ assert.deepEqual(binaryDocumentId, goodBinaryDocumentId)
61
+ assert.equal(documentId, goodDocumentId)
62
+ })
63
+
64
+ it("should throw on url with invalid checksum", () => {
65
+ assert.throws(() => parseAutomergeUrl(badChecksumUrl))
66
+ })
67
+
68
+ it("should throw on url with invalid prefix", () => {
69
+ assert.throws(() => parseAutomergeUrl(badPrefixUrl))
70
+ })
71
+ })
72
+
73
+ describe("isValidAutomergeUrl", () => {
74
+ it("should return true for a valid url", () => {
75
+ assert(isValidAutomergeUrl(goodUrl) === true)
76
+ })
77
+
78
+ it("should return false for null url", () => {
79
+ assert(isValidAutomergeUrl(null) === false)
80
+ })
81
+
82
+ it("should return false for a url with invalid checksum", () => {
83
+ assert(isValidAutomergeUrl(badChecksumUrl) === false)
84
+ })
85
+
86
+ it("should return false for a url with invalid prefix", () => {
87
+ assert(isValidAutomergeUrl(badPrefixUrl) === false)
88
+ })
89
+
90
+ it("should return false for a documentId with an invalid checksum", () => {
91
+ const url = stringifyAutomergeUrl({ documentId: badChecksumDocumentId })
92
+ assert(isValidAutomergeUrl(url) === false)
93
+ })
94
+
95
+ it("should return false for a documentId that is not a valid UUID ", () => {
96
+ const url = stringifyAutomergeUrl({ documentId: badUuidDocumentId })
97
+ assert(isValidAutomergeUrl(url) === false)
98
+ })
99
+ })
100
+ })
@@ -2,7 +2,7 @@ import * as A from "@automerge/automerge/next"
2
2
  import assert from "assert"
3
3
  import { decode } from "cbor-x"
4
4
  import { describe, it } from "vitest"
5
- import { generateAutomergeUrl, parseAutomergeUrl } from "../src/DocUrl.js"
5
+ import { generateAutomergeUrl, parseAutomergeUrl } from "../src/AutomergeUrl.js"
6
6
  import { eventPromise } from "../src/helpers/eventPromise.js"
7
7
  import { pause } from "../src/helpers/pause.js"
8
8
  import { DocHandle, DocHandleChangePayload } from "../src/index.js"
@@ -1,7 +1,7 @@
1
1
  import assert from "assert"
2
2
  import { describe, it } from "vitest"
3
3
  import { DocHandle } from "../src/DocHandle.js"
4
- import { generateAutomergeUrl, parseAutomergeUrl } from "../src/DocUrl.js"
4
+ import { generateAutomergeUrl, parseAutomergeUrl } from "../src/AutomergeUrl.js"
5
5
  import { eventPromise } from "../src/helpers/eventPromise.js"
6
6
  import {
7
7
  DocumentUnavailableMessage,
package/test/Repo.test.ts CHANGED
@@ -2,9 +2,12 @@ import { MessageChannelNetworkAdapter } from "@automerge/automerge-repo-network-
2
2
  import assert from "assert"
3
3
  import * as Uuid from "uuid"
4
4
  import { describe, it } from "vitest"
5
- import { parseAutomergeUrl } from "../dist/DocUrl.js"
5
+ import { parseAutomergeUrl } from "../src/AutomergeUrl.js"
6
6
  import { READY } from "../src/DocHandle.js"
7
- import { generateAutomergeUrl, stringifyAutomergeUrl } from "../src/DocUrl.js"
7
+ import {
8
+ generateAutomergeUrl,
9
+ stringifyAutomergeUrl,
10
+ } from "../src/AutomergeUrl.js"
8
11
  import { Repo } from "../src/Repo.js"
9
12
  import { eventPromise } from "../src/helpers/eventPromise.js"
10
13
  import { pause } from "../src/helpers/pause.js"
@@ -12,6 +15,7 @@ import {
12
15
  AutomergeUrl,
13
16
  DocHandle,
14
17
  DocumentId,
18
+ LegacyDocumentId,
15
19
  PeerId,
16
20
  SharePolicy,
17
21
  } from "../src/index.js"
@@ -51,7 +55,7 @@ describe("Repo", () => {
51
55
  assert.equal(handle.isReady(), true)
52
56
  })
53
57
 
54
- it("can find a document once it's created", () => {
58
+ it("can find a document by url", () => {
55
59
  const { repo } = setup()
56
60
  const handle = repo.create<TestDoc>()
57
61
  handle.change((d: TestDoc) => {
@@ -63,7 +67,19 @@ describe("Repo", () => {
63
67
  assert.deepEqual(handle2.docSync(), { foo: "bar" })
64
68
  })
65
69
 
66
- it("can find a document using a legacy UUID (for now)", () => {
70
+ it("can find a document by its unprefixed document ID", () => {
71
+ const { repo } = setup()
72
+ const handle = repo.create<TestDoc>()
73
+ handle.change((d: TestDoc) => {
74
+ d.foo = "bar"
75
+ })
76
+
77
+ const handle2 = repo.find(handle.documentId)
78
+ assert.equal(handle, handle2)
79
+ assert.deepEqual(handle2.docSync(), { foo: "bar" })
80
+ })
81
+
82
+ it("can find a document by legacy UUID (for now)", () => {
67
83
  disableConsoleWarn()
68
84
 
69
85
  const { repo } = setup()
@@ -74,9 +90,9 @@ describe("Repo", () => {
74
90
 
75
91
  const url = handle.url
76
92
  const { binaryDocumentId } = parseAutomergeUrl(url)
77
- const legacyDocumentId = Uuid.stringify(binaryDocumentId) as AutomergeUrl // a white lie
93
+ const legacyDocId = Uuid.stringify(binaryDocumentId) as LegacyDocumentId
78
94
 
79
- const handle2 = repo.find(legacyDocumentId)
95
+ const handle2 = repo.find(legacyDocId)
80
96
  assert.equal(handle, handle2)
81
97
  assert.deepEqual(handle2.docSync(), { foo: "bar" })
82
98
 
@@ -5,7 +5,7 @@ import fs from "fs"
5
5
  import os from "os"
6
6
  import path from "path"
7
7
  import { describe, it } from "vitest"
8
- import { generateAutomergeUrl, parseAutomergeUrl } from "../src/DocUrl.js"
8
+ import { generateAutomergeUrl, parseAutomergeUrl } from "../src/AutomergeUrl.js"
9
9
  import { StorageSubsystem } from "../src/storage/StorageSubsystem.js"
10
10
  import { DummyStorageAdapter } from "./helpers/DummyStorageAdapter.js"
11
11
 
package/dist/DocUrl.d.ts DELETED
@@ -1,39 +0,0 @@
1
- import { type AutomergeUrl, type BinaryDocumentId, type DocumentId } from "./types.js";
2
- export declare const urlPrefix = "automerge:";
3
- /**
4
- * given an Automerge URL, return a decoded DocumentId (and the encoded DocumentId)
5
- *
6
- * @param url
7
- * @returns { binaryDocumentId: BinaryDocumentId, documentId: DocumentId }
8
- */
9
- export declare const parseAutomergeUrl: (url: AutomergeUrl) => {
10
- binaryDocumentId: BinaryDocumentId;
11
- documentId: DocumentId;
12
- };
13
- /**
14
- * Given a documentId in either canonical form, return an Automerge URL
15
- * Throws on invalid input.
16
- * Note: this is an object because we anticipate adding fields in the future.
17
- * @param { documentId: BinaryDocumentId | DocumentId }
18
- * @returns AutomergeUrl
19
- */
20
- export declare const stringifyAutomergeUrl: ({ documentId, }: {
21
- documentId: DocumentId | BinaryDocumentId;
22
- }) => AutomergeUrl;
23
- /**
24
- * Given a string, return true if it is a valid Automerge URL
25
- * also acts as a type discriminator in Typescript.
26
- * @param str: URL candidate
27
- * @returns boolean
28
- */
29
- export declare const isValidAutomergeUrl: (str: string) => str is AutomergeUrl;
30
- /**
31
- * generateAutomergeUrl produces a new AutomergeUrl.
32
- * generally only called by create(), but used in tests as well.
33
- * @returns a new Automerge URL with a random UUID documentId
34
- */
35
- export declare const generateAutomergeUrl: () => AutomergeUrl;
36
- export declare const documentIdToBinary: (docId: DocumentId) => BinaryDocumentId | undefined;
37
- export declare const binaryToDocumentId: (docId: BinaryDocumentId) => DocumentId;
38
- export declare const parseLegacyUUID: (str: string) => AutomergeUrl | undefined;
39
- //# sourceMappingURL=DocUrl.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"DocUrl.d.ts","sourceRoot":"","sources":["../src/DocUrl.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,KAAK,YAAY,EACjB,KAAK,gBAAgB,EACrB,KAAK,UAAU,EAChB,MAAM,YAAY,CAAA;AAInB,eAAO,MAAM,SAAS,eAAe,CAAA;AAErC;;;;;GAKG;AACH,eAAO,MAAM,iBAAiB,QAAS,YAAY;;;CAIlD,CAAA;AAED;;;;;;GAMG;AACH,eAAO,MAAM,qBAAqB;gBAGpB,UAAU,GAAG,gBAAgB;MACvC,YAQH,CAAA;AAED;;;;;GAKG;AACH,eAAO,MAAM,mBAAmB,QAAS,MAAM,wBAK9C,CAAA;AAED;;;;GAIG;AACH,eAAO,MAAM,oBAAoB,QAAO,YAGpC,CAAA;AAEJ,eAAO,MAAM,kBAAkB,UACtB,UAAU,KAChB,gBAAgB,GAAG,SACyC,CAAA;AAE/D,eAAO,MAAM,kBAAkB,UAAW,gBAAgB,KAAG,UACtB,CAAA;AAEvC,eAAO,MAAM,eAAe,QAAS,MAAM,KAAG,YAAY,GAAG,SAM5D,CAAA"}
package/dist/DocUrl.js DELETED
@@ -1,74 +0,0 @@
1
- import * as Uuid from "uuid";
2
- import bs58check from "bs58check";
3
- export const urlPrefix = "automerge:";
4
- /**
5
- * given an Automerge URL, return a decoded DocumentId (and the encoded DocumentId)
6
- *
7
- * @param url
8
- * @returns { binaryDocumentId: BinaryDocumentId, documentId: DocumentId }
9
- */
10
- export const parseAutomergeUrl = (url) => {
11
- const { binaryDocumentId, documentId } = parts(url);
12
- if (!binaryDocumentId)
13
- throw new Error("Invalid document URL: " + url);
14
- return { binaryDocumentId, documentId };
15
- };
16
- /**
17
- * Given a documentId in either canonical form, return an Automerge URL
18
- * Throws on invalid input.
19
- * Note: this is an object because we anticipate adding fields in the future.
20
- * @param { documentId: BinaryDocumentId | DocumentId }
21
- * @returns AutomergeUrl
22
- */
23
- export const stringifyAutomergeUrl = ({ documentId, }) => {
24
- if (documentId instanceof Uint8Array)
25
- return (urlPrefix +
26
- binaryToDocumentId(documentId));
27
- else if (typeof documentId === "string") {
28
- return (urlPrefix + documentId);
29
- }
30
- throw new Error("Invalid documentId: " + documentId);
31
- };
32
- /**
33
- * Given a string, return true if it is a valid Automerge URL
34
- * also acts as a type discriminator in Typescript.
35
- * @param str: URL candidate
36
- * @returns boolean
37
- */
38
- export const isValidAutomergeUrl = (str) => {
39
- if (!str.startsWith(urlPrefix))
40
- return false;
41
- const { binaryDocumentId: documentId } = parts(str);
42
- return documentId ? true : false;
43
- };
44
- /**
45
- * generateAutomergeUrl produces a new AutomergeUrl.
46
- * generally only called by create(), but used in tests as well.
47
- * @returns a new Automerge URL with a random UUID documentId
48
- */
49
- export const generateAutomergeUrl = () => stringifyAutomergeUrl({
50
- documentId: Uuid.v4(null, new Uint8Array(16)),
51
- });
52
- export const documentIdToBinary = (docId) => bs58check.decodeUnsafe(docId);
53
- export const binaryToDocumentId = (docId) => bs58check.encode(docId);
54
- export const parseLegacyUUID = (str) => {
55
- if (Uuid.validate(str)) {
56
- const uuid = Uuid.parse(str);
57
- return stringifyAutomergeUrl({ documentId: uuid });
58
- }
59
- return undefined;
60
- };
61
- /**
62
- * parts breaks up the URL into constituent pieces,
63
- * eventually this could include things like heads, so we use this structure
64
- * we return both a binary & string-encoded version of the document ID
65
- * @param str
66
- * @returns { binaryDocumentId, documentId }
67
- */
68
- const parts = (str) => {
69
- const regex = new RegExp(`^${urlPrefix}(\\w+)$`);
70
- const [_, docMatch] = str.match(regex) || [];
71
- const documentId = docMatch;
72
- const binaryDocumentId = documentIdToBinary(documentId);
73
- return { binaryDocumentId, documentId };
74
- };
package/src/DocUrl.ts DELETED
@@ -1,96 +0,0 @@
1
- import {
2
- type AutomergeUrl,
3
- type BinaryDocumentId,
4
- type DocumentId,
5
- } from "./types.js"
6
- import * as Uuid from "uuid"
7
- import bs58check from "bs58check"
8
-
9
- export const urlPrefix = "automerge:"
10
-
11
- /**
12
- * given an Automerge URL, return a decoded DocumentId (and the encoded DocumentId)
13
- *
14
- * @param url
15
- * @returns { binaryDocumentId: BinaryDocumentId, documentId: DocumentId }
16
- */
17
- export const parseAutomergeUrl = (url: AutomergeUrl) => {
18
- const { binaryDocumentId, documentId } = parts(url)
19
- if (!binaryDocumentId) throw new Error("Invalid document URL: " + url)
20
- return { binaryDocumentId, documentId }
21
- }
22
-
23
- /**
24
- * Given a documentId in either canonical form, return an Automerge URL
25
- * Throws on invalid input.
26
- * Note: this is an object because we anticipate adding fields in the future.
27
- * @param { documentId: BinaryDocumentId | DocumentId }
28
- * @returns AutomergeUrl
29
- */
30
- export const stringifyAutomergeUrl = ({
31
- documentId,
32
- }: {
33
- documentId: DocumentId | BinaryDocumentId
34
- }): AutomergeUrl => {
35
- if (documentId instanceof Uint8Array)
36
- return (urlPrefix +
37
- binaryToDocumentId(documentId as BinaryDocumentId)) as AutomergeUrl
38
- else if (typeof documentId === "string") {
39
- return (urlPrefix + documentId) as AutomergeUrl
40
- }
41
- throw new Error("Invalid documentId: " + documentId)
42
- }
43
-
44
- /**
45
- * Given a string, return true if it is a valid Automerge URL
46
- * also acts as a type discriminator in Typescript.
47
- * @param str: URL candidate
48
- * @returns boolean
49
- */
50
- export const isValidAutomergeUrl = (str: string): str is AutomergeUrl => {
51
- if (!str.startsWith(urlPrefix)) return false
52
-
53
- const { binaryDocumentId: documentId } = parts(str)
54
- return documentId ? true : false
55
- }
56
-
57
- /**
58
- * generateAutomergeUrl produces a new AutomergeUrl.
59
- * generally only called by create(), but used in tests as well.
60
- * @returns a new Automerge URL with a random UUID documentId
61
- */
62
- export const generateAutomergeUrl = (): AutomergeUrl =>
63
- stringifyAutomergeUrl({
64
- documentId: Uuid.v4(null, new Uint8Array(16)) as BinaryDocumentId,
65
- })
66
-
67
- export const documentIdToBinary = (
68
- docId: DocumentId
69
- ): BinaryDocumentId | undefined =>
70
- bs58check.decodeUnsafe(docId) as BinaryDocumentId | undefined
71
-
72
- export const binaryToDocumentId = (docId: BinaryDocumentId): DocumentId =>
73
- bs58check.encode(docId) as DocumentId
74
-
75
- export const parseLegacyUUID = (str: string): AutomergeUrl | undefined => {
76
- if (Uuid.validate(str)) {
77
- const uuid = Uuid.parse(str) as BinaryDocumentId
78
- return stringifyAutomergeUrl({ documentId: uuid })
79
- }
80
- return undefined
81
- }
82
-
83
- /**
84
- * parts breaks up the URL into constituent pieces,
85
- * eventually this could include things like heads, so we use this structure
86
- * we return both a binary & string-encoded version of the document ID
87
- * @param str
88
- * @returns { binaryDocumentId, documentId }
89
- */
90
- const parts = (str: string) => {
91
- const regex = new RegExp(`^${urlPrefix}(\\w+)$`)
92
- const [_, docMatch] = str.match(regex) || []
93
- const documentId = docMatch as DocumentId
94
- const binaryDocumentId = documentIdToBinary(documentId)
95
- return { binaryDocumentId, documentId }
96
- }