@automerge/automerge-repo 2.0.0-alpha.12 → 2.0.0-alpha.14
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/Repo.d.ts +11 -2
- package/dist/Repo.d.ts.map +1 -1
- package/dist/Repo.js +2 -2
- package/dist/synchronizer/CollectionSynchronizer.d.ts +2 -2
- package/dist/synchronizer/CollectionSynchronizer.d.ts.map +1 -1
- package/dist/synchronizer/CollectionSynchronizer.js +16 -2
- package/dist/synchronizer/Synchronizer.d.ts +3 -0
- package/dist/synchronizer/Synchronizer.d.ts.map +1 -1
- package/package.json +2 -2
- package/src/Repo.ts +19 -2
- package/src/synchronizer/CollectionSynchronizer.ts +19 -3
- package/src/synchronizer/Synchronizer.ts +12 -7
- package/test/Repo.test.ts +38 -0
package/dist/Repo.d.ts
CHANGED
|
@@ -7,7 +7,7 @@ import { StorageSubsystem } from "./storage/StorageSubsystem.js";
|
|
|
7
7
|
import { StorageId } from "./storage/types.js";
|
|
8
8
|
import { CollectionSynchronizer } from "./synchronizer/CollectionSynchronizer.js";
|
|
9
9
|
import { DocSyncMetrics } from "./synchronizer/Synchronizer.js";
|
|
10
|
-
import type { AnyDocumentId, DocumentId, PeerId } from "./types.js";
|
|
10
|
+
import type { AnyDocumentId, AutomergeUrl, DocumentId, PeerId } from "./types.js";
|
|
11
11
|
/** A Repo is a collection of documents with networking, syncing, and storage capabilities. */
|
|
12
12
|
/** The `Repo` is the main entry point of this library
|
|
13
13
|
*
|
|
@@ -33,7 +33,7 @@ export declare class Repo extends EventEmitter<RepoEvents> {
|
|
|
33
33
|
/** maps peer id to to persistence information (storageId, isEphemeral), access by collection synchronizer */
|
|
34
34
|
/** @hidden */
|
|
35
35
|
peerMetadataByPeerId: Record<PeerId, PeerMetadata>;
|
|
36
|
-
constructor({ storage, network, peerId, sharePolicy, isEphemeral, enableRemoteHeadsGossiping, }?: RepoConfig);
|
|
36
|
+
constructor({ storage, network, peerId, sharePolicy, isEphemeral, enableRemoteHeadsGossiping, denylist, }?: RepoConfig);
|
|
37
37
|
/** Returns all the handles we have cached. */
|
|
38
38
|
get handles(): Record<DocumentId, DocHandle<any>>;
|
|
39
39
|
/** Returns a list of all connected peer ids */
|
|
@@ -126,6 +126,12 @@ export interface RepoConfig {
|
|
|
126
126
|
* Whether to enable the experimental remote heads gossiping feature
|
|
127
127
|
*/
|
|
128
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[];
|
|
129
135
|
}
|
|
130
136
|
/** A function that determines whether we should share a document with a peer
|
|
131
137
|
*
|
|
@@ -157,5 +163,8 @@ export type DocMetrics = DocSyncMetrics | {
|
|
|
157
163
|
durationMillis: number;
|
|
158
164
|
numOps: number;
|
|
159
165
|
numChanges: number;
|
|
166
|
+
} | {
|
|
167
|
+
type: "doc-denied";
|
|
168
|
+
documentId: DocumentId;
|
|
160
169
|
};
|
|
161
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;AAM5C,OAAO,EAEL,SAAS,EAKV,MAAM,gBAAgB,CAAA;AAIvB,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;AAC9C,OAAO,EAAE,sBAAsB,EAAE,MAAM,0CAA0C,CAAA;AACjF,OAAO,EACL,cAAc,EAEf,MAAM,gCAAgC,CAAA;AACvC,OAAO,KAAK,
|
|
1
|
+
{"version":3,"file":"Repo.d.ts","sourceRoot":"","sources":["../src/Repo.ts"],"names":[],"mappings":"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"}
|
package/dist/Repo.js
CHANGED
|
@@ -40,7 +40,7 @@ export class Repo extends EventEmitter {
|
|
|
40
40
|
peerMetadataByPeerId = {};
|
|
41
41
|
#remoteHeadsSubscriptions = new RemoteHeadsSubscriptions();
|
|
42
42
|
#remoteHeadsGossipingEnabled = false;
|
|
43
|
-
constructor({ storage, network = [], peerId = randomPeerId(), sharePolicy, isEphemeral = storage === undefined, enableRemoteHeadsGossiping = false, } = {}) {
|
|
43
|
+
constructor({ storage, network = [], peerId = randomPeerId(), sharePolicy, isEphemeral = storage === undefined, enableRemoteHeadsGossiping = false, denylist = [], } = {}) {
|
|
44
44
|
super();
|
|
45
45
|
this.#remoteHeadsGossipingEnabled = enableRemoteHeadsGossiping;
|
|
46
46
|
this.#log = debug(`automerge-repo:repo`);
|
|
@@ -56,7 +56,7 @@ export class Repo extends EventEmitter {
|
|
|
56
56
|
});
|
|
57
57
|
// SYNCHRONIZER
|
|
58
58
|
// The synchronizer uses the network subsystem to keep documents in sync with peers.
|
|
59
|
-
this.synchronizer = new CollectionSynchronizer(this);
|
|
59
|
+
this.synchronizer = new CollectionSynchronizer(this, denylist);
|
|
60
60
|
// When the synchronizer emits messages, send them to peers
|
|
61
61
|
this.synchronizer.on("message", message => {
|
|
62
62
|
this.#log(`sending ${message.type} message to ${message.targetId}`);
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { Repo } from "../Repo.js";
|
|
2
2
|
import { DocMessage } from "../network/messages.js";
|
|
3
|
-
import { DocumentId, PeerId } from "../types.js";
|
|
3
|
+
import { AutomergeUrl, DocumentId, PeerId } from "../types.js";
|
|
4
4
|
import { DocSynchronizer } from "./DocSynchronizer.js";
|
|
5
5
|
import { Synchronizer } from "./Synchronizer.js";
|
|
6
6
|
/** A CollectionSynchronizer is responsible for synchronizing a DocCollection with peers. */
|
|
@@ -10,7 +10,7 @@ export declare class CollectionSynchronizer extends Synchronizer {
|
|
|
10
10
|
/** A map of documentIds to their synchronizers */
|
|
11
11
|
/** @hidden */
|
|
12
12
|
docSynchronizers: Record<DocumentId, DocSynchronizer>;
|
|
13
|
-
constructor(repo: Repo);
|
|
13
|
+
constructor(repo: Repo, denylist?: AutomergeUrl[]);
|
|
14
14
|
/**
|
|
15
15
|
* When we receive a sync message for a document we haven't got in memory, we
|
|
16
16
|
* register it with the repo and start synchronizing
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"CollectionSynchronizer.d.ts","sourceRoot":"","sources":["../../src/synchronizer/CollectionSynchronizer.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,IAAI,EAAE,MAAM,YAAY,CAAA;AACjC,OAAO,EAAE,UAAU,EAAE,MAAM,wBAAwB,CAAA;AACnD,OAAO,EAAE,UAAU,EAAE,MAAM,EAAE,MAAM,aAAa,CAAA;
|
|
1
|
+
{"version":3,"file":"CollectionSynchronizer.d.ts","sourceRoot":"","sources":["../../src/synchronizer/CollectionSynchronizer.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,IAAI,EAAE,MAAM,YAAY,CAAA;AACjC,OAAO,EAAE,UAAU,EAAE,MAAM,wBAAwB,CAAA;AACnD,OAAO,EAAE,YAAY,EAAE,UAAU,EAAE,MAAM,EAAE,MAAM,aAAa,CAAA;AAC9D,OAAO,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAA;AACtD,OAAO,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAA;AAIhD,4FAA4F;AAC5F,qBAAa,sBAAuB,SAAQ,YAAY;;IAa1C,OAAO,CAAC,IAAI;IATxB,kDAAkD;IAClD,cAAc;IACd,gBAAgB,EAAE,MAAM,CAAC,UAAU,EAAE,eAAe,CAAC,CAAK;gBAOtC,IAAI,EAAE,IAAI,EAAE,QAAQ,GAAE,YAAY,EAAO;IAuD7D;;;OAGG;IACG,cAAc,CAAC,OAAO,EAAE,UAAU;IAsCxC;;OAEG;IACH,WAAW,CAAC,UAAU,EAAE,UAAU;IAalC,cAAc,CAAC,UAAU,EAAE,UAAU;IAIrC,2DAA2D;IAC3D,OAAO,CAAC,MAAM,EAAE,MAAM;IAgBtB,uDAAuD;IACvD,UAAU,CAAC,MAAM,EAAE,MAAM;IASzB,+CAA+C;IAC/C,IAAI,KAAK,IAAI,MAAM,EAAE,CAEpB;IAED,OAAO,IAAI;QACT,CAAC,GAAG,EAAE,MAAM,GAAG;YACb,KAAK,EAAE,MAAM,EAAE,CAAA;YACf,IAAI,EAAE;gBAAE,MAAM,EAAE,MAAM,CAAC;gBAAC,UAAU,EAAE,MAAM,CAAA;aAAE,CAAA;SAC7C,CAAA;KACF;CASF"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import debug from "debug";
|
|
2
|
-
import { stringifyAutomergeUrl } from "../AutomergeUrl.js";
|
|
2
|
+
import { parseAutomergeUrl, 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");
|
|
@@ -13,9 +13,11 @@ export class CollectionSynchronizer extends Synchronizer {
|
|
|
13
13
|
docSynchronizers = {};
|
|
14
14
|
/** Used to determine if the document is know to the Collection and a synchronizer exists or is being set up */
|
|
15
15
|
#docSetUp = {};
|
|
16
|
-
|
|
16
|
+
#denylist;
|
|
17
|
+
constructor(repo, denylist = []) {
|
|
17
18
|
super();
|
|
18
19
|
this.repo = repo;
|
|
20
|
+
this.#denylist = denylist.map(url => parseAutomergeUrl(url).documentId);
|
|
19
21
|
}
|
|
20
22
|
/** Returns a synchronizer for the given document, creating one if it doesn't already exist. */
|
|
21
23
|
#fetchDocSynchronizer(documentId) {
|
|
@@ -68,6 +70,18 @@ export class CollectionSynchronizer extends Synchronizer {
|
|
|
68
70
|
if (!documentId) {
|
|
69
71
|
throw new Error("received a message with an invalid documentId");
|
|
70
72
|
}
|
|
73
|
+
if (this.#denylist.includes(documentId)) {
|
|
74
|
+
this.emit("metrics", {
|
|
75
|
+
type: "doc-denied",
|
|
76
|
+
documentId,
|
|
77
|
+
});
|
|
78
|
+
this.emit("message", {
|
|
79
|
+
type: "doc-unavailable",
|
|
80
|
+
documentId,
|
|
81
|
+
targetId: message.senderId,
|
|
82
|
+
});
|
|
83
|
+
return;
|
|
84
|
+
}
|
|
71
85
|
this.#docSetUp[documentId] = true;
|
|
72
86
|
const docSynchronizer = this.#fetchDocSynchronizer(documentId);
|
|
73
87
|
docSynchronizer.receiveMessage(message);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Synchronizer.d.ts","sourceRoot":"","sources":["../../src/synchronizer/Synchronizer.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,eAAe,CAAA;AAC5C,OAAO,EACL,eAAe,EACf,cAAc,EACd,WAAW,EACZ,MAAM,wBAAwB,CAAA;AAC/B,OAAO,EAAE,SAAS,EAAE,MAAM,2BAA2B,CAAA;AACrD,OAAO,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,aAAa,CAAA;AAEhD,8BAAsB,YAAa,SAAQ,YAAY,CAAC,kBAAkB,CAAC;IACzE,QAAQ,CAAC,cAAc,CAAC,OAAO,EAAE,WAAW,GAAG,IAAI;CACpD;AAED,MAAM,WAAW,kBAAkB;IACjC,OAAO,EAAE,CAAC,OAAO,EAAE,eAAe,KAAK,IAAI,CAAA;IAC3C,YAAY,EAAE,CAAC,OAAO,EAAE,gBAAgB,KAAK,IAAI,CAAA;IACjD,UAAU,EAAE,CAAC,GAAG,EAAE,cAAc,KAAK,IAAI,CAAA;IACzC,OAAO,EAAE,CAAC,GAAG,EAAE,cAAc,KAAK,IAAI,CAAA;CACvC;AAED,uDAAuD;AACvD,MAAM,WAAW,gBAAgB;IAC/B,MAAM,EAAE,MAAM,CAAA;IACd,UAAU,EAAE,UAAU,CAAA;IACtB,SAAS,EAAE,SAAS,CAAA;CACrB;AAED,MAAM,MAAM,cAAc,
|
|
1
|
+
{"version":3,"file":"Synchronizer.d.ts","sourceRoot":"","sources":["../../src/synchronizer/Synchronizer.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,eAAe,CAAA;AAC5C,OAAO,EACL,eAAe,EACf,cAAc,EACd,WAAW,EACZ,MAAM,wBAAwB,CAAA;AAC/B,OAAO,EAAE,SAAS,EAAE,MAAM,2BAA2B,CAAA;AACrD,OAAO,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,aAAa,CAAA;AAEhD,8BAAsB,YAAa,SAAQ,YAAY,CAAC,kBAAkB,CAAC;IACzE,QAAQ,CAAC,cAAc,CAAC,OAAO,EAAE,WAAW,GAAG,IAAI;CACpD;AAED,MAAM,WAAW,kBAAkB;IACjC,OAAO,EAAE,CAAC,OAAO,EAAE,eAAe,KAAK,IAAI,CAAA;IAC3C,YAAY,EAAE,CAAC,OAAO,EAAE,gBAAgB,KAAK,IAAI,CAAA;IACjD,UAAU,EAAE,CAAC,GAAG,EAAE,cAAc,KAAK,IAAI,CAAA;IACzC,OAAO,EAAE,CAAC,GAAG,EAAE,cAAc,KAAK,IAAI,CAAA;CACvC;AAED,uDAAuD;AACvD,MAAM,WAAW,gBAAgB;IAC/B,MAAM,EAAE,MAAM,CAAA;IACd,UAAU,EAAE,UAAU,CAAA;IACtB,SAAS,EAAE,SAAS,CAAA;CACrB;AAED,MAAM,MAAM,cAAc,GACtB;IACE,IAAI,EAAE,sBAAsB,CAAA;IAC5B,UAAU,EAAE,UAAU,CAAA;IACtB,cAAc,EAAE,MAAM,CAAA;IACtB,MAAM,EAAE,MAAM,CAAA;IACd,UAAU,EAAE,MAAM,CAAA;CACnB,GACD;IACE,IAAI,EAAE,YAAY,CAAA;IAClB,UAAU,EAAE,UAAU,CAAA;CACvB,CAAA"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@automerge/automerge-repo",
|
|
3
|
-
"version": "2.0.0-alpha.
|
|
3
|
+
"version": "2.0.0-alpha.14",
|
|
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>",
|
|
@@ -60,5 +60,5 @@
|
|
|
60
60
|
"publishConfig": {
|
|
61
61
|
"access": "public"
|
|
62
62
|
},
|
|
63
|
-
"gitHead": "
|
|
63
|
+
"gitHead": "3048251989fc0cdbf605f534e48750f0c681004a"
|
|
64
64
|
}
|
package/src/Repo.ts
CHANGED
|
@@ -31,7 +31,12 @@ import {
|
|
|
31
31
|
DocSyncMetrics,
|
|
32
32
|
SyncStatePayload,
|
|
33
33
|
} from "./synchronizer/Synchronizer.js"
|
|
34
|
-
import type {
|
|
34
|
+
import type {
|
|
35
|
+
AnyDocumentId,
|
|
36
|
+
AutomergeUrl,
|
|
37
|
+
DocumentId,
|
|
38
|
+
PeerId,
|
|
39
|
+
} from "./types.js"
|
|
35
40
|
|
|
36
41
|
function randomPeerId() {
|
|
37
42
|
return ("peer-" + Math.random().toString(36).slice(4)) as PeerId
|
|
@@ -80,6 +85,7 @@ export class Repo extends EventEmitter<RepoEvents> {
|
|
|
80
85
|
sharePolicy,
|
|
81
86
|
isEphemeral = storage === undefined,
|
|
82
87
|
enableRemoteHeadsGossiping = false,
|
|
88
|
+
denylist = [],
|
|
83
89
|
}: RepoConfig = {}) {
|
|
84
90
|
super()
|
|
85
91
|
this.#remoteHeadsGossipingEnabled = enableRemoteHeadsGossiping
|
|
@@ -99,7 +105,7 @@ export class Repo extends EventEmitter<RepoEvents> {
|
|
|
99
105
|
|
|
100
106
|
// SYNCHRONIZER
|
|
101
107
|
// The synchronizer uses the network subsystem to keep documents in sync with peers.
|
|
102
|
-
this.synchronizer = new CollectionSynchronizer(this)
|
|
108
|
+
this.synchronizer = new CollectionSynchronizer(this, denylist)
|
|
103
109
|
|
|
104
110
|
// When the synchronizer emits messages, send them to peers
|
|
105
111
|
this.synchronizer.on("message", message => {
|
|
@@ -627,6 +633,13 @@ export interface RepoConfig {
|
|
|
627
633
|
* Whether to enable the experimental remote heads gossiping feature
|
|
628
634
|
*/
|
|
629
635
|
enableRemoteHeadsGossiping?: boolean
|
|
636
|
+
|
|
637
|
+
/**
|
|
638
|
+
* A list of automerge URLs which should never be loaded regardless of what
|
|
639
|
+
* messages are received or what the share policy is. This is useful to avoid
|
|
640
|
+
* loading documents that are known to be too resource intensive.
|
|
641
|
+
*/
|
|
642
|
+
denylist?: AutomergeUrl[]
|
|
630
643
|
}
|
|
631
644
|
|
|
632
645
|
/** A function that determines whether we should share a document with a peer
|
|
@@ -670,3 +683,7 @@ export type DocMetrics =
|
|
|
670
683
|
numOps: number
|
|
671
684
|
numChanges: number
|
|
672
685
|
}
|
|
686
|
+
| {
|
|
687
|
+
type: "doc-denied"
|
|
688
|
+
documentId: DocumentId
|
|
689
|
+
}
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import debug from "debug"
|
|
2
2
|
import { DocHandle } from "../DocHandle.js"
|
|
3
|
-
import { stringifyAutomergeUrl } from "../AutomergeUrl.js"
|
|
3
|
+
import { parseAutomergeUrl, stringifyAutomergeUrl } from "../AutomergeUrl.js"
|
|
4
4
|
import { Repo } from "../Repo.js"
|
|
5
5
|
import { DocMessage } from "../network/messages.js"
|
|
6
|
-
import { DocumentId, PeerId } from "../types.js"
|
|
6
|
+
import { AutomergeUrl, DocumentId, PeerId } from "../types.js"
|
|
7
7
|
import { DocSynchronizer } from "./DocSynchronizer.js"
|
|
8
8
|
import { Synchronizer } from "./Synchronizer.js"
|
|
9
9
|
|
|
@@ -21,8 +21,11 @@ export class CollectionSynchronizer extends Synchronizer {
|
|
|
21
21
|
/** Used to determine if the document is know to the Collection and a synchronizer exists or is being set up */
|
|
22
22
|
#docSetUp: Record<DocumentId, boolean> = {}
|
|
23
23
|
|
|
24
|
-
|
|
24
|
+
#denylist: DocumentId[]
|
|
25
|
+
|
|
26
|
+
constructor(private repo: Repo, denylist: AutomergeUrl[] = []) {
|
|
25
27
|
super()
|
|
28
|
+
this.#denylist = denylist.map(url => parseAutomergeUrl(url).documentId)
|
|
26
29
|
}
|
|
27
30
|
|
|
28
31
|
/** Returns a synchronizer for the given document, creating one if it doesn't already exist. */
|
|
@@ -91,6 +94,19 @@ export class CollectionSynchronizer extends Synchronizer {
|
|
|
91
94
|
throw new Error("received a message with an invalid documentId")
|
|
92
95
|
}
|
|
93
96
|
|
|
97
|
+
if (this.#denylist.includes(documentId)) {
|
|
98
|
+
this.emit("metrics", {
|
|
99
|
+
type: "doc-denied",
|
|
100
|
+
documentId,
|
|
101
|
+
})
|
|
102
|
+
this.emit("message", {
|
|
103
|
+
type: "doc-unavailable",
|
|
104
|
+
documentId,
|
|
105
|
+
targetId: message.senderId,
|
|
106
|
+
})
|
|
107
|
+
return
|
|
108
|
+
}
|
|
109
|
+
|
|
94
110
|
this.#docSetUp[documentId] = true
|
|
95
111
|
|
|
96
112
|
const docSynchronizer = this.#fetchDocSynchronizer(documentId)
|
|
@@ -25,10 +25,15 @@ export interface SyncStatePayload {
|
|
|
25
25
|
syncState: SyncState
|
|
26
26
|
}
|
|
27
27
|
|
|
28
|
-
export type DocSyncMetrics =
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
28
|
+
export type DocSyncMetrics =
|
|
29
|
+
| {
|
|
30
|
+
type: "receive-sync-message"
|
|
31
|
+
documentId: DocumentId
|
|
32
|
+
durationMillis: number
|
|
33
|
+
numOps: number
|
|
34
|
+
numChanges: number
|
|
35
|
+
}
|
|
36
|
+
| {
|
|
37
|
+
type: "doc-denied"
|
|
38
|
+
documentId: DocumentId
|
|
39
|
+
}
|
package/test/Repo.test.ts
CHANGED
|
@@ -1486,6 +1486,44 @@ describe("Repo", () => {
|
|
|
1486
1486
|
teardown()
|
|
1487
1487
|
})
|
|
1488
1488
|
})
|
|
1489
|
+
|
|
1490
|
+
describe("the denylist", () => {
|
|
1491
|
+
it("should immediately return an unavailable message in response to a request for a denylisted document", async () => {
|
|
1492
|
+
const storage = new DummyStorageAdapter()
|
|
1493
|
+
|
|
1494
|
+
// first create the document in storage
|
|
1495
|
+
const dummyRepo = new Repo({ network: [], storage })
|
|
1496
|
+
const doc = dummyRepo.create({ foo: "bar" })
|
|
1497
|
+
await dummyRepo.flush()
|
|
1498
|
+
|
|
1499
|
+
// Check that the document actually is in storage
|
|
1500
|
+
let docId = doc.documentId
|
|
1501
|
+
assert(storage.keys().some((k: string) => k.includes(docId)))
|
|
1502
|
+
|
|
1503
|
+
const channel = new MessageChannel()
|
|
1504
|
+
const { port1: clientToServer, port2: serverToClient } = channel
|
|
1505
|
+
const server = new Repo({
|
|
1506
|
+
network: [new MessageChannelNetworkAdapter(serverToClient)],
|
|
1507
|
+
storage,
|
|
1508
|
+
denylist: [doc.url],
|
|
1509
|
+
})
|
|
1510
|
+
const client = new Repo({
|
|
1511
|
+
network: [new MessageChannelNetworkAdapter(clientToServer)],
|
|
1512
|
+
})
|
|
1513
|
+
|
|
1514
|
+
await Promise.all([
|
|
1515
|
+
eventPromise(server.networkSubsystem, "peer"),
|
|
1516
|
+
eventPromise(client.networkSubsystem, "peer"),
|
|
1517
|
+
])
|
|
1518
|
+
|
|
1519
|
+
const clientDoc = client.find(doc.url)
|
|
1520
|
+
await pause(100)
|
|
1521
|
+
assert.strictEqual(clientDoc.docSync(), undefined)
|
|
1522
|
+
|
|
1523
|
+
const openDocs = Object.keys(server.metrics().documents).length
|
|
1524
|
+
assert.deepEqual(openDocs, 0)
|
|
1525
|
+
})
|
|
1526
|
+
})
|
|
1489
1527
|
})
|
|
1490
1528
|
|
|
1491
1529
|
const warn = console.warn
|