@automerge/automerge-repo 2.0.0-alpha.2 → 2.0.0-alpha.20
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 +17 -5
- package/dist/AutomergeUrl.d.ts.map +1 -1
- package/dist/AutomergeUrl.js +71 -24
- package/dist/DocHandle.d.ts +80 -8
- package/dist/DocHandle.d.ts.map +1 -1
- package/dist/DocHandle.js +181 -10
- package/dist/RemoteHeadsSubscriptions.d.ts +4 -5
- package/dist/RemoteHeadsSubscriptions.d.ts.map +1 -1
- package/dist/RemoteHeadsSubscriptions.js +4 -1
- package/dist/Repo.d.ts +35 -2
- package/dist/Repo.d.ts.map +1 -1
- package/dist/Repo.js +112 -70
- package/dist/entrypoints/fullfat.d.ts +1 -0
- package/dist/entrypoints/fullfat.d.ts.map +1 -1
- package/dist/entrypoints/fullfat.js +1 -2
- package/dist/helpers/bufferFromHex.d.ts +3 -0
- package/dist/helpers/bufferFromHex.d.ts.map +1 -0
- package/dist/helpers/bufferFromHex.js +13 -0
- package/dist/helpers/headsAreSame.d.ts +2 -2
- package/dist/helpers/headsAreSame.d.ts.map +1 -1
- package/dist/helpers/mergeArrays.d.ts +1 -1
- package/dist/helpers/mergeArrays.d.ts.map +1 -1
- package/dist/helpers/tests/storage-adapter-tests.d.ts +2 -2
- package/dist/helpers/tests/storage-adapter-tests.d.ts.map +1 -1
- package/dist/helpers/tests/storage-adapter-tests.js +25 -48
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/storage/StorageSubsystem.d.ts +11 -1
- package/dist/storage/StorageSubsystem.d.ts.map +1 -1
- package/dist/storage/StorageSubsystem.js +20 -4
- package/dist/synchronizer/CollectionSynchronizer.d.ts +15 -2
- package/dist/synchronizer/CollectionSynchronizer.d.ts.map +1 -1
- package/dist/synchronizer/CollectionSynchronizer.js +29 -8
- package/dist/synchronizer/DocSynchronizer.d.ts +7 -0
- package/dist/synchronizer/DocSynchronizer.d.ts.map +1 -1
- package/dist/synchronizer/DocSynchronizer.js +14 -0
- package/dist/synchronizer/Synchronizer.d.ts +11 -0
- package/dist/synchronizer/Synchronizer.d.ts.map +1 -1
- package/dist/types.d.ts +4 -1
- package/dist/types.d.ts.map +1 -1
- package/package.json +3 -3
- package/src/AutomergeUrl.ts +101 -26
- package/src/DocHandle.ts +245 -20
- package/src/RemoteHeadsSubscriptions.ts +11 -9
- package/src/Repo.ts +163 -68
- package/src/entrypoints/fullfat.ts +1 -2
- package/src/helpers/bufferFromHex.ts +14 -0
- package/src/helpers/headsAreSame.ts +2 -2
- package/src/helpers/tests/storage-adapter-tests.ts +44 -86
- package/src/index.ts +2 -0
- package/src/storage/StorageSubsystem.ts +29 -4
- package/src/synchronizer/CollectionSynchronizer.ts +42 -9
- package/src/synchronizer/DocSynchronizer.ts +15 -0
- package/src/synchronizer/Synchronizer.ts +14 -0
- package/src/types.ts +4 -1
- package/test/AutomergeUrl.test.ts +130 -0
- package/test/DocHandle.test.ts +209 -2
- package/test/DocSynchronizer.test.ts +10 -3
- package/test/Repo.test.ts +228 -3
- package/test/StorageSubsystem.test.ts +17 -0
package/dist/DocHandle.js
CHANGED
|
@@ -2,7 +2,7 @@ import * as A from "@automerge/automerge/slim/next";
|
|
|
2
2
|
import debug from "debug";
|
|
3
3
|
import { EventEmitter } from "eventemitter3";
|
|
4
4
|
import { assertEvent, assign, createActor, setup, waitFor } from "xstate";
|
|
5
|
-
import { stringifyAutomergeUrl } from "./AutomergeUrl.js";
|
|
5
|
+
import { decodeHeads, encodeHeads, stringifyAutomergeUrl, } from "./AutomergeUrl.js";
|
|
6
6
|
import { encode } from "./helpers/cbor.js";
|
|
7
7
|
import { headsAreSame } from "./helpers/headsAreSame.js";
|
|
8
8
|
import { withTimeout } from "./helpers/withTimeout.js";
|
|
@@ -24,6 +24,8 @@ export class DocHandle extends EventEmitter {
|
|
|
24
24
|
#log;
|
|
25
25
|
/** The XState actor running our state machine. */
|
|
26
26
|
#machine;
|
|
27
|
+
/** If set, this handle will only show the document at these heads */
|
|
28
|
+
#fixedHeads;
|
|
27
29
|
/** The last known state of our document. */
|
|
28
30
|
#prevDocState = A.init();
|
|
29
31
|
/** How long to wait before giving up on a document. (Note that a document will be marked
|
|
@@ -38,6 +40,9 @@ export class DocHandle extends EventEmitter {
|
|
|
38
40
|
if ("timeoutDelay" in options && options.timeoutDelay) {
|
|
39
41
|
this.#timeoutDelay = options.timeoutDelay;
|
|
40
42
|
}
|
|
43
|
+
if ("heads" in options) {
|
|
44
|
+
this.#fixedHeads = options.heads;
|
|
45
|
+
}
|
|
41
46
|
const doc = A.init();
|
|
42
47
|
this.#log = debug(`automerge-repo:dochandle:${this.documentId.slice(0, 5)}`);
|
|
43
48
|
const delay = this.#timeoutDelay;
|
|
@@ -59,6 +64,9 @@ export class DocHandle extends EventEmitter {
|
|
|
59
64
|
this.emit("delete", { handle: this });
|
|
60
65
|
return { doc: A.init() };
|
|
61
66
|
}),
|
|
67
|
+
onUnload: assign(() => {
|
|
68
|
+
return { doc: A.init() };
|
|
69
|
+
}),
|
|
62
70
|
onUnavailable: () => {
|
|
63
71
|
this.emit("unavailable", { handle: this });
|
|
64
72
|
},
|
|
@@ -71,6 +79,7 @@ export class DocHandle extends EventEmitter {
|
|
|
71
79
|
context: { documentId, doc },
|
|
72
80
|
on: {
|
|
73
81
|
UPDATE: { actions: "onUpdate" },
|
|
82
|
+
UNLOAD: ".unloaded",
|
|
74
83
|
DELETE: ".deleted",
|
|
75
84
|
},
|
|
76
85
|
states: {
|
|
@@ -98,6 +107,12 @@ export class DocHandle extends EventEmitter {
|
|
|
98
107
|
on: { DOC_READY: "ready" },
|
|
99
108
|
},
|
|
100
109
|
ready: {},
|
|
110
|
+
unloaded: {
|
|
111
|
+
entry: "onUnload",
|
|
112
|
+
on: {
|
|
113
|
+
RELOAD: "loading",
|
|
114
|
+
},
|
|
115
|
+
},
|
|
101
116
|
deleted: { entry: "onDelete", type: "final" },
|
|
102
117
|
},
|
|
103
118
|
});
|
|
@@ -113,7 +128,7 @@ export class DocHandle extends EventEmitter {
|
|
|
113
128
|
});
|
|
114
129
|
// Start the machine, and send a create or find event to get things going
|
|
115
130
|
this.#machine.start();
|
|
116
|
-
this
|
|
131
|
+
this.begin();
|
|
117
132
|
}
|
|
118
133
|
// PRIVATE
|
|
119
134
|
/** Returns the current document, regardless of state */
|
|
@@ -140,7 +155,7 @@ export class DocHandle extends EventEmitter {
|
|
|
140
155
|
#checkForChanges(before, after) {
|
|
141
156
|
const beforeHeads = A.getHeads(before);
|
|
142
157
|
const afterHeads = A.getHeads(after);
|
|
143
|
-
const docChanged = !headsAreSame(afterHeads, beforeHeads);
|
|
158
|
+
const docChanged = !headsAreSame(encodeHeads(afterHeads), encodeHeads(beforeHeads));
|
|
144
159
|
if (docChanged) {
|
|
145
160
|
this.emit("heads-changed", { handle: this, doc: after });
|
|
146
161
|
const patches = A.diff(after, beforeHeads, afterHeads);
|
|
@@ -163,7 +178,10 @@ export class DocHandle extends EventEmitter {
|
|
|
163
178
|
/** Our documentId in Automerge URL form.
|
|
164
179
|
*/
|
|
165
180
|
get url() {
|
|
166
|
-
return stringifyAutomergeUrl({
|
|
181
|
+
return stringifyAutomergeUrl({
|
|
182
|
+
documentId: this.documentId,
|
|
183
|
+
heads: this.#fixedHeads,
|
|
184
|
+
});
|
|
167
185
|
}
|
|
168
186
|
/**
|
|
169
187
|
* @returns true if the document is ready for accessing or changes.
|
|
@@ -172,6 +190,13 @@ export class DocHandle extends EventEmitter {
|
|
|
172
190
|
* peers. We do not currently have an equivalent `whenSynced()`.
|
|
173
191
|
*/
|
|
174
192
|
isReady = () => this.inState(["ready"]);
|
|
193
|
+
/**
|
|
194
|
+
* @returns true if the document has been unloaded.
|
|
195
|
+
*
|
|
196
|
+
* Unloaded documents are freed from memory but not removed from local storage. It's not currently
|
|
197
|
+
* possible at runtime to reload an unloaded document.
|
|
198
|
+
*/
|
|
199
|
+
isUnloaded = () => this.inState(["unloaded"]);
|
|
175
200
|
/**
|
|
176
201
|
* @returns true if the document has been marked as deleted.
|
|
177
202
|
*
|
|
@@ -220,6 +245,13 @@ export class DocHandle extends EventEmitter {
|
|
|
220
245
|
// if we timed out, return undefined
|
|
221
246
|
return undefined;
|
|
222
247
|
}
|
|
248
|
+
// If we have fixed heads, return a view at those heads
|
|
249
|
+
if (this.#fixedHeads) {
|
|
250
|
+
const doc = this.#doc;
|
|
251
|
+
if (!doc || this.isUnavailable())
|
|
252
|
+
return undefined;
|
|
253
|
+
return A.view(doc, decodeHeads(this.#fixedHeads));
|
|
254
|
+
}
|
|
223
255
|
// Return the document
|
|
224
256
|
return !this.isUnavailable() ? this.#doc : undefined;
|
|
225
257
|
}
|
|
@@ -239,8 +271,11 @@ export class DocHandle extends EventEmitter {
|
|
|
239
271
|
docSync() {
|
|
240
272
|
if (!this.isReady())
|
|
241
273
|
return undefined;
|
|
242
|
-
|
|
243
|
-
|
|
274
|
+
if (this.#fixedHeads) {
|
|
275
|
+
const doc = this.#doc;
|
|
276
|
+
return doc ? A.view(doc, decodeHeads(this.#fixedHeads)) : undefined;
|
|
277
|
+
}
|
|
278
|
+
return this.#doc;
|
|
244
279
|
}
|
|
245
280
|
/**
|
|
246
281
|
* Returns the current "heads" of the document, akin to a git commit.
|
|
@@ -248,10 +283,120 @@ export class DocHandle extends EventEmitter {
|
|
|
248
283
|
* @returns the current document's heads, or undefined if the document is not ready
|
|
249
284
|
*/
|
|
250
285
|
heads() {
|
|
286
|
+
if (!this.isReady())
|
|
287
|
+
return undefined;
|
|
288
|
+
if (this.#fixedHeads) {
|
|
289
|
+
return this.#fixedHeads;
|
|
290
|
+
}
|
|
291
|
+
return encodeHeads(A.getHeads(this.#doc));
|
|
292
|
+
}
|
|
293
|
+
begin() {
|
|
294
|
+
this.#machine.send({ type: BEGIN });
|
|
295
|
+
}
|
|
296
|
+
/**
|
|
297
|
+
* Returns an array of all past "heads" for the document in topological order.
|
|
298
|
+
*
|
|
299
|
+
* @remarks
|
|
300
|
+
* A point-in-time in an automerge document is an *array* of heads since there may be
|
|
301
|
+
* concurrent edits. This API just returns a topologically sorted history of all edits
|
|
302
|
+
* so every previous entry will be (in some sense) before later ones, but the set of all possible
|
|
303
|
+
* history views would be quite large under concurrency (every thing in each branch against each other).
|
|
304
|
+
* There might be a clever way to think about this, but we haven't found it yet, so for now at least
|
|
305
|
+
* we present a single traversable view which excludes concurrency.
|
|
306
|
+
* @returns UrlHeads[] - The individual heads for every change in the document. Each item is a tagged string[1].
|
|
307
|
+
*/
|
|
308
|
+
history() {
|
|
251
309
|
if (!this.isReady()) {
|
|
252
310
|
return undefined;
|
|
253
311
|
}
|
|
254
|
-
|
|
312
|
+
// This just returns all the heads as individual strings.
|
|
313
|
+
return A.topoHistoryTraversal(this.#doc).map(h => encodeHeads([h]));
|
|
314
|
+
}
|
|
315
|
+
/**
|
|
316
|
+
* Creates a new DocHandle with a fixed "view" at the given point in time represented
|
|
317
|
+
* by the `heads` passed in. The return value is the same type as docSync() and will return
|
|
318
|
+
* undefined if the object hasn't finished loading.
|
|
319
|
+
*
|
|
320
|
+
* @remarks
|
|
321
|
+
* Note that our Typescript types do not consider change over time and the current version
|
|
322
|
+
* of Automerge doesn't check types at runtime, so if you go back to an old set of heads
|
|
323
|
+
* that doesn't match the heads here, Typescript will not save you.
|
|
324
|
+
*
|
|
325
|
+
* @argument heads - The heads to view the document at. See history().
|
|
326
|
+
* @returns DocHandle<T> at the time of `heads`
|
|
327
|
+
*/
|
|
328
|
+
view(heads) {
|
|
329
|
+
if (!this.isReady()) {
|
|
330
|
+
throw new Error(`DocHandle#${this.documentId} is not ready. Check \`handle.isReady()\` before calling view().`);
|
|
331
|
+
}
|
|
332
|
+
// Create a new handle with the same documentId but fixed heads
|
|
333
|
+
const handle = new DocHandle(this.documentId, {
|
|
334
|
+
heads,
|
|
335
|
+
timeoutDelay: this.#timeoutDelay,
|
|
336
|
+
});
|
|
337
|
+
handle.update(() => A.clone(this.#doc));
|
|
338
|
+
handle.doneLoading();
|
|
339
|
+
return handle;
|
|
340
|
+
}
|
|
341
|
+
/**
|
|
342
|
+
* Returns a set of Patch operations that will move a materialized document from one state to another
|
|
343
|
+
* if applied.
|
|
344
|
+
*
|
|
345
|
+
* @remarks
|
|
346
|
+
* We allow specifying either:
|
|
347
|
+
* - Two sets of heads to compare directly
|
|
348
|
+
* - A single set of heads to compare against our current heads
|
|
349
|
+
* - Another DocHandle to compare against (which must share history with this document)
|
|
350
|
+
*
|
|
351
|
+
* @throws Error if the documents don't share history or if either document is not ready
|
|
352
|
+
* @returns Automerge patches that go from one document state to the other
|
|
353
|
+
*/
|
|
354
|
+
diff(first, second) {
|
|
355
|
+
if (!this.isReady()) {
|
|
356
|
+
throw new Error(`DocHandle#${this.documentId} is not ready. Check \`handle.isReady()\` before calling diff().`);
|
|
357
|
+
}
|
|
358
|
+
const doc = this.#doc;
|
|
359
|
+
if (!doc)
|
|
360
|
+
throw new Error("Document not available");
|
|
361
|
+
// If first argument is a DocHandle
|
|
362
|
+
if (first instanceof DocHandle) {
|
|
363
|
+
if (!first.isReady()) {
|
|
364
|
+
throw new Error("Cannot diff against a handle that isn't ready");
|
|
365
|
+
}
|
|
366
|
+
const otherHeads = first.heads();
|
|
367
|
+
if (!otherHeads)
|
|
368
|
+
throw new Error("Other document's heads not available");
|
|
369
|
+
// Create a temporary merged doc to verify shared history and compute diff
|
|
370
|
+
const mergedDoc = A.merge(A.clone(doc), first.docSync());
|
|
371
|
+
// Use the merged doc to compute the diff
|
|
372
|
+
return A.diff(mergedDoc, decodeHeads(this.heads()), decodeHeads(otherHeads));
|
|
373
|
+
}
|
|
374
|
+
// Otherwise treat as heads
|
|
375
|
+
const from = second ? first : (this.heads() || []);
|
|
376
|
+
const to = second ? second : first;
|
|
377
|
+
return A.diff(doc, decodeHeads(from), decodeHeads(to));
|
|
378
|
+
}
|
|
379
|
+
/**
|
|
380
|
+
* `metadata(head?)` allows you to look at the metadata for a change
|
|
381
|
+
* this can be used to build history graphs to find commit messages and edit times.
|
|
382
|
+
* this interface.
|
|
383
|
+
*
|
|
384
|
+
* @remarks
|
|
385
|
+
* I'm really not convinced this is the right way to surface this information so
|
|
386
|
+
* I'm leaving this API "hidden".
|
|
387
|
+
*
|
|
388
|
+
* @hidden
|
|
389
|
+
*/
|
|
390
|
+
metadata(change) {
|
|
391
|
+
if (!this.isReady()) {
|
|
392
|
+
return undefined;
|
|
393
|
+
}
|
|
394
|
+
if (!change) {
|
|
395
|
+
change = this.heads()[0];
|
|
396
|
+
}
|
|
397
|
+
// we return undefined instead of null by convention in this API
|
|
398
|
+
return (A.inspectChange(this.#doc, decodeHeads([change])[0]) ||
|
|
399
|
+
undefined);
|
|
255
400
|
}
|
|
256
401
|
/**
|
|
257
402
|
* `update` is called any time we have a new document state; could be
|
|
@@ -301,6 +446,9 @@ export class DocHandle extends EventEmitter {
|
|
|
301
446
|
if (!this.isReady()) {
|
|
302
447
|
throw new Error(`DocHandle#${this.documentId} is in ${this.state} and not ready. Check \`handle.isReady()\` before accessing the document.`);
|
|
303
448
|
}
|
|
449
|
+
if (this.#fixedHeads) {
|
|
450
|
+
throw new Error(`DocHandle#${this.documentId} is in view-only mode at specific heads. Use clone() to create a new document from this state.`);
|
|
451
|
+
}
|
|
304
452
|
this.#machine.send({
|
|
305
453
|
type: UPDATE,
|
|
306
454
|
payload: { callback: doc => A.change(doc, options, callback) },
|
|
@@ -315,13 +463,18 @@ export class DocHandle extends EventEmitter {
|
|
|
315
463
|
if (!this.isReady()) {
|
|
316
464
|
throw new Error(`DocHandle#${this.documentId} is not ready. Check \`handle.isReady()\` before accessing the document.`);
|
|
317
465
|
}
|
|
466
|
+
if (this.#fixedHeads) {
|
|
467
|
+
throw new Error(`DocHandle#${this.documentId} is in view-only mode at specific heads. Use clone() to create a new document from this state.`);
|
|
468
|
+
}
|
|
318
469
|
let resultHeads = undefined;
|
|
319
470
|
this.#machine.send({
|
|
320
471
|
type: UPDATE,
|
|
321
472
|
payload: {
|
|
322
473
|
callback: doc => {
|
|
323
|
-
const result = A.changeAt(doc, heads, options, callback);
|
|
324
|
-
resultHeads = result.newHeads
|
|
474
|
+
const result = A.changeAt(doc, decodeHeads(heads), options, callback);
|
|
475
|
+
resultHeads = result.newHeads
|
|
476
|
+
? encodeHeads(result.newHeads)
|
|
477
|
+
: undefined;
|
|
325
478
|
return result.newDoc;
|
|
326
479
|
},
|
|
327
480
|
},
|
|
@@ -343,6 +496,9 @@ export class DocHandle extends EventEmitter {
|
|
|
343
496
|
if (!this.isReady() || !otherHandle.isReady()) {
|
|
344
497
|
throw new Error("Both handles must be ready to merge");
|
|
345
498
|
}
|
|
499
|
+
if (this.#fixedHeads) {
|
|
500
|
+
throw new Error(`DocHandle#${this.documentId} is in view-only mode at specific heads. Use clone() to create a new document from this state.`);
|
|
501
|
+
}
|
|
346
502
|
const mergingDoc = otherHandle.docSync();
|
|
347
503
|
if (!mergingDoc) {
|
|
348
504
|
throw new Error("The document to be merged in is falsy, aborting.");
|
|
@@ -365,6 +521,14 @@ export class DocHandle extends EventEmitter {
|
|
|
365
521
|
if (this.#state === "loading")
|
|
366
522
|
this.#machine.send({ type: REQUEST });
|
|
367
523
|
}
|
|
524
|
+
/** Called by the repo to free memory used by the document. */
|
|
525
|
+
unload() {
|
|
526
|
+
this.#machine.send({ type: UNLOAD });
|
|
527
|
+
}
|
|
528
|
+
/** Called by the repo to reuse an unloaded handle. */
|
|
529
|
+
reload() {
|
|
530
|
+
this.#machine.send({ type: RELOAD });
|
|
531
|
+
}
|
|
368
532
|
/** Called by the repo when the document is deleted. */
|
|
369
533
|
delete() {
|
|
370
534
|
this.#machine.send({ type: DELETE });
|
|
@@ -382,6 +546,9 @@ export class DocHandle extends EventEmitter {
|
|
|
382
546
|
data: encode(message),
|
|
383
547
|
});
|
|
384
548
|
}
|
|
549
|
+
metrics() {
|
|
550
|
+
return A.stats(this.#doc);
|
|
551
|
+
}
|
|
385
552
|
}
|
|
386
553
|
// STATE MACHINE TYPES & CONSTANTS
|
|
387
554
|
// state
|
|
@@ -397,16 +564,20 @@ export const HandleState = {
|
|
|
397
564
|
REQUESTING: "requesting",
|
|
398
565
|
/** The document is available */
|
|
399
566
|
READY: "ready",
|
|
567
|
+
/** The document has been unloaded from the handle, to free memory usage */
|
|
568
|
+
UNLOADED: "unloaded",
|
|
400
569
|
/** The document has been deleted from the repo */
|
|
401
570
|
DELETED: "deleted",
|
|
402
571
|
/** The document was not available in storage or from any connected peers */
|
|
403
572
|
UNAVAILABLE: "unavailable",
|
|
404
573
|
};
|
|
405
|
-
export const { IDLE, LOADING, REQUESTING, READY, DELETED, UNAVAILABLE } = HandleState;
|
|
574
|
+
export const { IDLE, LOADING, REQUESTING, READY, UNLOADED, DELETED, UNAVAILABLE, } = HandleState;
|
|
406
575
|
const BEGIN = "BEGIN";
|
|
407
576
|
const REQUEST = "REQUEST";
|
|
408
577
|
const DOC_READY = "DOC_READY";
|
|
409
578
|
const UPDATE = "UPDATE";
|
|
579
|
+
const UNLOAD = "UNLOAD";
|
|
580
|
+
const RELOAD = "RELOAD";
|
|
410
581
|
const DELETE = "DELETE";
|
|
411
582
|
const TIMEOUT = "TIMEOUT";
|
|
412
583
|
const DOC_UNAVAILABLE = "DOC_UNAVAILABLE";
|
|
@@ -1,19 +1,18 @@
|
|
|
1
|
-
import { next as A } from "@automerge/automerge/slim";
|
|
2
1
|
import { EventEmitter } from "eventemitter3";
|
|
3
|
-
import { DocumentId, PeerId } from "./types.js";
|
|
2
|
+
import { DocumentId, PeerId, UrlHeads } from "./types.js";
|
|
4
3
|
import { RemoteHeadsChanged, RemoteSubscriptionControlMessage } from "./network/messages.js";
|
|
5
4
|
import { StorageId } from "./index.js";
|
|
6
5
|
export type RemoteHeadsSubscriptionEventPayload = {
|
|
7
6
|
documentId: DocumentId;
|
|
8
7
|
storageId: StorageId;
|
|
9
|
-
remoteHeads:
|
|
8
|
+
remoteHeads: UrlHeads;
|
|
10
9
|
timestamp: number;
|
|
11
10
|
};
|
|
12
11
|
export type NotifyRemoteHeadsPayload = {
|
|
13
12
|
targetId: PeerId;
|
|
14
13
|
documentId: DocumentId;
|
|
15
14
|
storageId: StorageId;
|
|
16
|
-
heads:
|
|
15
|
+
heads: UrlHeads;
|
|
17
16
|
timestamp: number;
|
|
18
17
|
};
|
|
19
18
|
type RemoteHeadsSubscriptionEvents = {
|
|
@@ -33,7 +32,7 @@ export declare class RemoteHeadsSubscriptions extends EventEmitter<RemoteHeadsSu
|
|
|
33
32
|
/** A peer we are not directly connected to has changed their heads */
|
|
34
33
|
handleRemoteHeads(msg: RemoteHeadsChanged): void;
|
|
35
34
|
/** A peer we are directly connected to has updated their heads */
|
|
36
|
-
handleImmediateRemoteHeadsChanged(documentId: DocumentId, storageId: StorageId, heads:
|
|
35
|
+
handleImmediateRemoteHeadsChanged(documentId: DocumentId, storageId: StorageId, heads: UrlHeads): void;
|
|
37
36
|
addGenerousPeer(peerId: PeerId): void;
|
|
38
37
|
removePeer(peerId: PeerId): void;
|
|
39
38
|
subscribePeerToDoc(peerId: PeerId, documentId: DocumentId): void;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"RemoteHeadsSubscriptions.d.ts","sourceRoot":"","sources":["../src/RemoteHeadsSubscriptions.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"RemoteHeadsSubscriptions.d.ts","sourceRoot":"","sources":["../src/RemoteHeadsSubscriptions.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,eAAe,CAAA;AAC5C,OAAO,EAAE,UAAU,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAA;AACzD,OAAO,EACL,kBAAkB,EAClB,gCAAgC,EACjC,MAAM,uBAAuB,CAAA;AAC9B,OAAO,EAAE,SAAS,EAAE,MAAM,YAAY,CAAA;AAItC,MAAM,MAAM,mCAAmC,GAAG;IAChD,UAAU,EAAE,UAAU,CAAA;IACtB,SAAS,EAAE,SAAS,CAAA;IACpB,WAAW,EAAE,QAAQ,CAAA;IACrB,SAAS,EAAE,MAAM,CAAA;CAClB,CAAA;AAGD,MAAM,MAAM,wBAAwB,GAAG;IACrC,QAAQ,EAAE,MAAM,CAAA;IAChB,UAAU,EAAE,UAAU,CAAA;IACtB,SAAS,EAAE,SAAS,CAAA;IACpB,KAAK,EAAE,QAAQ,CAAA;IACf,SAAS,EAAE,MAAM,CAAA;CAClB,CAAA;AAED,KAAK,6BAA6B,GAAG;IACnC,sBAAsB,EAAE,CAAC,OAAO,EAAE,mCAAmC,KAAK,IAAI,CAAA;IAC9E,oBAAoB,EAAE,CAAC,OAAO,EAAE;QAC9B,KAAK,EAAE,MAAM,EAAE,CAAA;QACf,GAAG,CAAC,EAAE,SAAS,EAAE,CAAA;QACjB,MAAM,CAAC,EAAE,SAAS,EAAE,CAAA;KACrB,KAAK,IAAI,CAAA;IACV,qBAAqB,EAAE,CAAC,OAAO,EAAE,wBAAwB,KAAK,IAAI,CAAA;CACnE,CAAA;AAED,qBAAa,wBAAyB,SAAQ,YAAY,CAAC,6BAA6B,CAAC;;IAcvF,kBAAkB,CAAC,OAAO,EAAE,SAAS,EAAE;IAkBvC,sBAAsB,CAAC,OAAO,EAAE,SAAS,EAAE;IAsB3C,oBAAoB,CAAC,OAAO,EAAE,gCAAgC;IA0E9D,sEAAsE;IACtE,iBAAiB,CAAC,GAAG,EAAE,kBAAkB;IAgDzC,kEAAkE;IAClE,iCAAiC,CAC/B,UAAU,EAAE,UAAU,EACtB,SAAS,EAAE,SAAS,EACpB,KAAK,EAAE,QAAQ;IAgCjB,eAAe,CAAC,MAAM,EAAE,MAAM;IAwB9B,UAAU,CAAC,MAAM,EAAE,MAAM;IA2BzB,kBAAkB,CAAC,MAAM,EAAE,MAAM,EAAE,UAAU,EAAE,UAAU;CAuE1D"}
|
|
@@ -270,7 +270,10 @@ export class RemoteHeadsSubscriptions extends EventEmitter {
|
|
|
270
270
|
continue;
|
|
271
271
|
}
|
|
272
272
|
else {
|
|
273
|
-
remote.set(storageId, {
|
|
273
|
+
remote.set(storageId, {
|
|
274
|
+
timestamp,
|
|
275
|
+
heads: heads,
|
|
276
|
+
});
|
|
274
277
|
changedHeads.push({
|
|
275
278
|
documentId,
|
|
276
279
|
storageId: storageId,
|
package/dist/Repo.d.ts
CHANGED
|
@@ -5,7 +5,9 @@ import { NetworkSubsystem } from "./network/NetworkSubsystem.js";
|
|
|
5
5
|
import { StorageAdapterInterface } from "./storage/StorageAdapterInterface.js";
|
|
6
6
|
import { StorageSubsystem } from "./storage/StorageSubsystem.js";
|
|
7
7
|
import { StorageId } from "./storage/types.js";
|
|
8
|
-
import
|
|
8
|
+
import { CollectionSynchronizer } from "./synchronizer/CollectionSynchronizer.js";
|
|
9
|
+
import { DocSyncMetrics } from "./synchronizer/Synchronizer.js";
|
|
10
|
+
import type { AnyDocumentId, AutomergeUrl, DocumentId, PeerId } from "./types.js";
|
|
9
11
|
/** A Repo is a collection of documents with networking, syncing, and storage capabilities. */
|
|
10
12
|
/** The `Repo` is the main entry point of this library
|
|
11
13
|
*
|
|
@@ -23,13 +25,15 @@ export declare class Repo extends EventEmitter<RepoEvents> {
|
|
|
23
25
|
/** The debounce rate is adjustable on the repo. */
|
|
24
26
|
/** @hidden */
|
|
25
27
|
saveDebounceRate: number;
|
|
28
|
+
/** @hidden */
|
|
29
|
+
synchronizer: CollectionSynchronizer;
|
|
26
30
|
/** By default, we share generously with all peers. */
|
|
27
31
|
/** @hidden */
|
|
28
32
|
sharePolicy: SharePolicy;
|
|
29
33
|
/** maps peer id to to persistence information (storageId, isEphemeral), access by collection synchronizer */
|
|
30
34
|
/** @hidden */
|
|
31
35
|
peerMetadataByPeerId: Record<PeerId, PeerMetadata>;
|
|
32
|
-
constructor({ storage, network, peerId, sharePolicy, isEphemeral, enableRemoteHeadsGossiping, }?: RepoConfig);
|
|
36
|
+
constructor({ storage, network, peerId, sharePolicy, isEphemeral, enableRemoteHeadsGossiping, denylist, }?: RepoConfig);
|
|
33
37
|
/** Returns all the handles we have cached. */
|
|
34
38
|
get handles(): Record<DocumentId, DocHandle<any>>;
|
|
35
39
|
/** Returns a list of all connected peer ids */
|
|
@@ -89,7 +93,19 @@ export declare class Repo extends EventEmitter<RepoEvents> {
|
|
|
89
93
|
* @returns Promise<void>
|
|
90
94
|
*/
|
|
91
95
|
flush(documents?: DocumentId[]): Promise<void>;
|
|
96
|
+
/**
|
|
97
|
+
* Removes a DocHandle from the handleCache.
|
|
98
|
+
* @hidden this API is experimental and may change.
|
|
99
|
+
* @param documentId - documentId of the DocHandle to remove from handleCache, if present in cache.
|
|
100
|
+
* @returns Promise<void>
|
|
101
|
+
*/
|
|
102
|
+
removeFromCache(documentId: DocumentId): Promise<void>;
|
|
92
103
|
shutdown(): Promise<void>;
|
|
104
|
+
metrics(): {
|
|
105
|
+
documents: {
|
|
106
|
+
[key: string]: any;
|
|
107
|
+
};
|
|
108
|
+
};
|
|
93
109
|
}
|
|
94
110
|
export interface RepoConfig {
|
|
95
111
|
/** Our unique identifier */
|
|
@@ -110,6 +126,12 @@ export interface RepoConfig {
|
|
|
110
126
|
* Whether to enable the experimental remote heads gossiping feature
|
|
111
127
|
*/
|
|
112
128
|
enableRemoteHeadsGossiping?: boolean;
|
|
129
|
+
/**
|
|
130
|
+
* A list of automerge URLs which should never be loaded regardless of what
|
|
131
|
+
* messages are received or what the share policy is. This is useful to avoid
|
|
132
|
+
* loading documents that are known to be too resource intensive.
|
|
133
|
+
*/
|
|
134
|
+
denylist?: AutomergeUrl[];
|
|
113
135
|
}
|
|
114
136
|
/** A function that determines whether we should share a document with a peer
|
|
115
137
|
*
|
|
@@ -127,6 +149,7 @@ export interface RepoEvents {
|
|
|
127
149
|
"delete-document": (arg: DeleteDocumentPayload) => void;
|
|
128
150
|
/** A document was marked as unavailable (we don't have it and none of our peers have it) */
|
|
129
151
|
"unavailable-document": (arg: DeleteDocumentPayload) => void;
|
|
152
|
+
"doc-metrics": (arg: DocMetrics) => void;
|
|
130
153
|
}
|
|
131
154
|
export interface DocumentPayload {
|
|
132
155
|
handle: DocHandle<any>;
|
|
@@ -134,4 +157,14 @@ export interface DocumentPayload {
|
|
|
134
157
|
export interface DeleteDocumentPayload {
|
|
135
158
|
documentId: DocumentId;
|
|
136
159
|
}
|
|
160
|
+
export type DocMetrics = DocSyncMetrics | {
|
|
161
|
+
type: "doc-loaded";
|
|
162
|
+
documentId: DocumentId;
|
|
163
|
+
durationMillis: number;
|
|
164
|
+
numOps: number;
|
|
165
|
+
numChanges: number;
|
|
166
|
+
} | {
|
|
167
|
+
type: "doc-denied";
|
|
168
|
+
documentId: DocumentId;
|
|
169
|
+
};
|
|
137
170
|
//# sourceMappingURL=Repo.d.ts.map
|
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;
|
|
1
|
+
{"version":3,"file":"Repo.d.ts","sourceRoot":"","sources":["../src/Repo.ts"],"names":[],"mappings":"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"}
|