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