@automerge/automerge-repo 1.0.19 → 1.1.0-alpha.1
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/DocHandle.d.ts +6 -5
- package/dist/DocHandle.d.ts.map +1 -1
- package/dist/DocHandle.js +7 -7
- package/dist/RemoteHeadsSubscriptions.d.ts +41 -0
- package/dist/RemoteHeadsSubscriptions.d.ts.map +1 -0
- package/dist/RemoteHeadsSubscriptions.js +224 -0
- package/dist/Repo.d.ts +15 -1
- package/dist/Repo.d.ts.map +1 -1
- package/dist/Repo.js +118 -8
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/network/NetworkAdapter.d.ts +8 -1
- package/dist/network/NetworkAdapter.d.ts.map +1 -1
- package/dist/network/NetworkAdapter.js +2 -0
- package/dist/network/NetworkSubsystem.d.ts +7 -1
- package/dist/network/NetworkSubsystem.d.ts.map +1 -1
- package/dist/network/NetworkSubsystem.js +11 -4
- package/dist/network/messages.d.ts +24 -1
- package/dist/network/messages.d.ts.map +1 -1
- package/dist/network/messages.js +5 -1
- package/dist/storage/StorageSubsystem.d.ts +5 -3
- package/dist/storage/StorageSubsystem.d.ts.map +1 -1
- package/dist/storage/StorageSubsystem.js +17 -4
- package/dist/storage/types.d.ts +4 -0
- package/dist/storage/types.d.ts.map +1 -1
- package/dist/synchronizer/CollectionSynchronizer.d.ts +2 -2
- package/dist/synchronizer/CollectionSynchronizer.d.ts.map +1 -1
- package/dist/synchronizer/CollectionSynchronizer.js +7 -3
- package/dist/synchronizer/DocSynchronizer.d.ts.map +1 -1
- package/dist/synchronizer/DocSynchronizer.js +0 -9
- package/package.json +3 -3
- package/src/DocHandle.ts +10 -9
- package/src/RemoteHeadsSubscriptions.ts +306 -0
- package/src/Repo.ts +173 -11
- package/src/index.ts +1 -0
- package/src/network/NetworkAdapter.ts +12 -1
- package/src/network/NetworkSubsystem.ts +24 -11
- package/src/network/messages.ts +30 -1
- package/src/storage/StorageSubsystem.ts +24 -6
- package/src/storage/types.ts +3 -0
- package/src/synchronizer/CollectionSynchronizer.ts +10 -5
- package/src/synchronizer/DocSynchronizer.ts +0 -12
- package/test/DocHandle.test.ts +0 -17
- package/test/RemoteHeadsSubscriptions.test.ts +343 -0
- package/test/Repo.test.ts +51 -15
- package/test/StorageSubsystem.test.ts +28 -6
- package/test/remoteHeads.test.ts +135 -0
package/dist/DocHandle.d.ts
CHANGED
|
@@ -2,6 +2,7 @@ import * as A from "@automerge/automerge/next";
|
|
|
2
2
|
import { EventEmitter } from "eventemitter3";
|
|
3
3
|
import { StateValue } from "xstate";
|
|
4
4
|
import type { AutomergeUrl, DocumentId, PeerId } from "./types.js";
|
|
5
|
+
import { StorageId } from "./storage/types.js";
|
|
5
6
|
/** DocHandle is a wrapper around a single Automerge document that lets us
|
|
6
7
|
* listen for changes and notify the network and storage of new changes.
|
|
7
8
|
*
|
|
@@ -73,12 +74,12 @@ export declare class DocHandle<T>//
|
|
|
73
74
|
* @hidden
|
|
74
75
|
* */
|
|
75
76
|
update(callback: (doc: A.Doc<T>) => A.Doc<T>): void;
|
|
76
|
-
/** `setRemoteHeads` is called by the doc
|
|
77
|
+
/** `setRemoteHeads` is called by the repo either when a doc handle changes or we receive new remote heads
|
|
77
78
|
* @hidden
|
|
78
79
|
*/
|
|
79
|
-
setRemoteHeads(
|
|
80
|
-
/** Returns the heads of the
|
|
81
|
-
getRemoteHeads(
|
|
80
|
+
setRemoteHeads(storageId: StorageId, heads: A.Heads): void;
|
|
81
|
+
/** Returns the heads of the storageId */
|
|
82
|
+
getRemoteHeads(storageId: StorageId): A.Heads | undefined;
|
|
82
83
|
/** `change` is called by the repo when the document is changed locally */
|
|
83
84
|
change(callback: A.ChangeFn<T>, options?: A.ChangeOptions<T>): void;
|
|
84
85
|
/** Make a change as if the document were at `heads`
|
|
@@ -156,7 +157,7 @@ export interface DocHandleOutboundEphemeralMessagePayload<T> {
|
|
|
156
157
|
data: Uint8Array;
|
|
157
158
|
}
|
|
158
159
|
export interface DocHandleRemoteHeadsPayload {
|
|
159
|
-
|
|
160
|
+
storageId: StorageId;
|
|
160
161
|
heads: A.Heads;
|
|
161
162
|
}
|
|
162
163
|
export interface DocHandleSyncStatePayload {
|
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,2BAA2B,CAAA;AAE9C,OAAO,EAAE,YAAY,EAAE,MAAM,eAAe,CAAA;AAC5C,OAAO,EASL,UAAU,EAEX,MAAM,QAAQ,CAAA;AAMf,OAAO,KAAK,EAAE,YAAY,EAAE,UAAU,EAAE,MAAM,EAAE,MAAM,YAAY,CAAA;
|
|
1
|
+
{"version":3,"file":"DocHandle.d.ts","sourceRoot":"","sources":["../src/DocHandle.ts"],"names":[],"mappings":"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"}
|
package/dist/DocHandle.js
CHANGED
|
@@ -270,16 +270,16 @@ export class DocHandle//
|
|
|
270
270
|
payload: { callback },
|
|
271
271
|
});
|
|
272
272
|
}
|
|
273
|
-
/** `setRemoteHeads` is called by the doc
|
|
273
|
+
/** `setRemoteHeads` is called by the repo either when a doc handle changes or we receive new remote heads
|
|
274
274
|
* @hidden
|
|
275
275
|
*/
|
|
276
|
-
setRemoteHeads(
|
|
277
|
-
this.#remoteHeads[
|
|
278
|
-
this.emit("remote-heads", {
|
|
276
|
+
setRemoteHeads(storageId, heads) {
|
|
277
|
+
this.#remoteHeads[storageId] = heads;
|
|
278
|
+
this.emit("remote-heads", { storageId, heads });
|
|
279
279
|
}
|
|
280
|
-
/** Returns the heads of the
|
|
281
|
-
getRemoteHeads(
|
|
282
|
-
return this.#remoteHeads[
|
|
280
|
+
/** Returns the heads of the storageId */
|
|
281
|
+
getRemoteHeads(storageId) {
|
|
282
|
+
return this.#remoteHeads[storageId];
|
|
283
283
|
}
|
|
284
284
|
/** `change` is called by the repo when the document is changed locally */
|
|
285
285
|
change(callback, options = {}) {
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { next as A } from "@automerge/automerge";
|
|
2
|
+
import { EventEmitter } from "eventemitter3";
|
|
3
|
+
import { DocumentId, PeerId } from "./types.js";
|
|
4
|
+
import { RemoteHeadsChanged, RemoteSubscriptionControlMessage } from "./network/messages.js";
|
|
5
|
+
import { StorageId } from "./index.js";
|
|
6
|
+
export type RemoteHeadsSubscriptionEventPayload = {
|
|
7
|
+
documentId: DocumentId;
|
|
8
|
+
storageId: StorageId;
|
|
9
|
+
remoteHeads: A.Heads;
|
|
10
|
+
timestamp: number;
|
|
11
|
+
};
|
|
12
|
+
export type NotifyRemoteHeadsPayload = {
|
|
13
|
+
targetId: PeerId;
|
|
14
|
+
documentId: DocumentId;
|
|
15
|
+
storageId: StorageId;
|
|
16
|
+
heads: A.Heads;
|
|
17
|
+
timestamp: number;
|
|
18
|
+
};
|
|
19
|
+
type RemoteHeadsSubscriptionEvents = {
|
|
20
|
+
"remote-heads-changed": (payload: RemoteHeadsSubscriptionEventPayload) => void;
|
|
21
|
+
"change-remote-subs": (payload: {
|
|
22
|
+
peers: PeerId[];
|
|
23
|
+
add?: StorageId[];
|
|
24
|
+
remove?: StorageId[];
|
|
25
|
+
}) => void;
|
|
26
|
+
"notify-remote-heads": (payload: NotifyRemoteHeadsPayload) => void;
|
|
27
|
+
};
|
|
28
|
+
export declare class RemoteHeadsSubscriptions extends EventEmitter<RemoteHeadsSubscriptionEvents> {
|
|
29
|
+
#private;
|
|
30
|
+
subscribeToRemotes(remotes: StorageId[]): void;
|
|
31
|
+
unsubscribeFromRemotes(remotes: StorageId[]): void;
|
|
32
|
+
handleControlMessage(control: RemoteSubscriptionControlMessage): void;
|
|
33
|
+
/** A peer we are not directly connected to has changed their heads */
|
|
34
|
+
handleRemoteHeads(msg: RemoteHeadsChanged): void;
|
|
35
|
+
/** A peer we are directly connected to has updated their heads */
|
|
36
|
+
handleImmediateRemoteHeadsChanged(documentId: DocumentId, storageId: StorageId, heads: A.Heads): void;
|
|
37
|
+
addGenerousPeer(peerId: PeerId): void;
|
|
38
|
+
removePeer(peerId: PeerId): void;
|
|
39
|
+
}
|
|
40
|
+
export {};
|
|
41
|
+
//# sourceMappingURL=RemoteHeadsSubscriptions.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"RemoteHeadsSubscriptions.d.ts","sourceRoot":"","sources":["../src/RemoteHeadsSubscriptions.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,IAAI,CAAC,EAAE,MAAM,sBAAsB,CAAA;AAChD,OAAO,EAAE,YAAY,EAAE,MAAM,eAAe,CAAA;AAC5C,OAAO,EAAE,UAAU,EAAE,MAAM,EAAE,MAAM,YAAY,CAAA;AAC/C,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,CAAC,CAAC,KAAK,CAAA;IACpB,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,CAAC,CAAC,KAAK,CAAA;IACd,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;;IAWvF,kBAAkB,CAAC,OAAO,EAAE,SAAS,EAAE;IAkBvC,sBAAsB,CAAC,OAAO,EAAE,SAAS,EAAE;IAsB3C,oBAAoB,CAAC,OAAO,EAAE,gCAAgC;IA4C9D,sEAAsE;IACtE,iBAAiB,CAAC,GAAG,EAAE,kBAAkB;IA8CzC,kEAAkE;IAClE,iCAAiC,CAC/B,UAAU,EAAE,UAAU,EACtB,SAAS,EAAE,SAAS,EACpB,KAAK,EAAE,CAAC,CAAC,KAAK;IA8BhB,eAAe,CAAC,MAAM,EAAE,MAAM;IAwB9B,UAAU,CAAC,MAAM,EAAE,MAAM;CA+D1B"}
|
|
@@ -0,0 +1,224 @@
|
|
|
1
|
+
import { EventEmitter } from "eventemitter3";
|
|
2
|
+
import debug from "debug";
|
|
3
|
+
export class RemoteHeadsSubscriptions extends EventEmitter {
|
|
4
|
+
// Storage IDs we have received remote heads from
|
|
5
|
+
#knownHeads = new Map();
|
|
6
|
+
// Storage IDs we have subscribed to via Repo.subscribeToRemoteHeads
|
|
7
|
+
#ourSubscriptions = new Set();
|
|
8
|
+
// Storage IDs other peers have subscribed to by sending us a control message
|
|
9
|
+
#theirSubscriptions = new Map();
|
|
10
|
+
// Peers we will always share remote heads with even if they are not subscribed
|
|
11
|
+
#generousPeers = new Set();
|
|
12
|
+
#log = debug("automerge-repo:remote-heads-subscriptions");
|
|
13
|
+
subscribeToRemotes(remotes) {
|
|
14
|
+
this.#log("subscribeToRemotes", remotes);
|
|
15
|
+
const remotesToAdd = [];
|
|
16
|
+
for (const remote of remotes) {
|
|
17
|
+
if (!this.#ourSubscriptions.has(remote)) {
|
|
18
|
+
this.#ourSubscriptions.add(remote);
|
|
19
|
+
remotesToAdd.push(remote);
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
if (remotesToAdd.length > 0) {
|
|
23
|
+
this.emit("change-remote-subs", {
|
|
24
|
+
add: remotesToAdd,
|
|
25
|
+
peers: Array.from(this.#generousPeers),
|
|
26
|
+
});
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
unsubscribeFromRemotes(remotes) {
|
|
30
|
+
this.#log("subscribeToRemotes", remotes);
|
|
31
|
+
const remotesToRemove = [];
|
|
32
|
+
for (const remote of remotes) {
|
|
33
|
+
if (this.#ourSubscriptions.has(remote)) {
|
|
34
|
+
this.#ourSubscriptions.delete(remote);
|
|
35
|
+
if (!this.#theirSubscriptions.has(remote)) {
|
|
36
|
+
remotesToRemove.push(remote);
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
if (remotesToRemove.length > 0) {
|
|
41
|
+
this.emit("change-remote-subs", {
|
|
42
|
+
remove: remotesToRemove,
|
|
43
|
+
peers: Array.from(this.#generousPeers),
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
handleControlMessage(control) {
|
|
48
|
+
const remotesToAdd = [];
|
|
49
|
+
const remotesToRemove = [];
|
|
50
|
+
this.#log("handleControlMessage", control);
|
|
51
|
+
if (control.add) {
|
|
52
|
+
for (const remote of control.add) {
|
|
53
|
+
let theirSubs = this.#theirSubscriptions.get(remote);
|
|
54
|
+
if (!theirSubs) {
|
|
55
|
+
theirSubs = new Set();
|
|
56
|
+
this.#theirSubscriptions.set(remote, theirSubs);
|
|
57
|
+
if (!this.#ourSubscriptions.has(remote)) {
|
|
58
|
+
remotesToAdd.push(remote);
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
theirSubs.add(control.senderId);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
if (control.remove) {
|
|
65
|
+
for (const remote of control.remove) {
|
|
66
|
+
const theirSubs = this.#theirSubscriptions.get(remote);
|
|
67
|
+
if (theirSubs) {
|
|
68
|
+
theirSubs.delete(control.senderId);
|
|
69
|
+
// if no one is subscribed anymore remove remote
|
|
70
|
+
if (theirSubs.size == 0 && !this.#ourSubscriptions.has(remote)) {
|
|
71
|
+
remotesToRemove.push(remote);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
if (remotesToAdd.length > 0 || remotesToRemove.length > 0) {
|
|
77
|
+
this.emit("change-remote-subs", {
|
|
78
|
+
peers: Array.from(this.#generousPeers),
|
|
79
|
+
add: remotesToAdd,
|
|
80
|
+
remove: remotesToRemove,
|
|
81
|
+
});
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
/** A peer we are not directly connected to has changed their heads */
|
|
85
|
+
handleRemoteHeads(msg) {
|
|
86
|
+
this.#log("handleRemoteHeads", msg);
|
|
87
|
+
const changedHeads = this.#changedHeads(msg);
|
|
88
|
+
// Emit a remote-heads-changed event to update local dochandles
|
|
89
|
+
for (const event of changedHeads) {
|
|
90
|
+
if (this.#ourSubscriptions.has(event.storageId)) {
|
|
91
|
+
this.emit("remote-heads-changed", event);
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
// Notify generous peers of these changes regardless of if they are subscribed to us
|
|
95
|
+
for (const event of changedHeads) {
|
|
96
|
+
for (const peer of this.#generousPeers) {
|
|
97
|
+
// don't emit event to sender if sender is a generous peer
|
|
98
|
+
if (peer === msg.senderId) {
|
|
99
|
+
continue;
|
|
100
|
+
}
|
|
101
|
+
this.emit("notify-remote-heads", {
|
|
102
|
+
targetId: peer,
|
|
103
|
+
documentId: event.documentId,
|
|
104
|
+
heads: event.remoteHeads,
|
|
105
|
+
timestamp: event.timestamp,
|
|
106
|
+
storageId: event.storageId,
|
|
107
|
+
});
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
// Notify subscribers of these changes
|
|
111
|
+
for (const event of changedHeads) {
|
|
112
|
+
const theirSubs = this.#theirSubscriptions.get(event.storageId);
|
|
113
|
+
if (theirSubs) {
|
|
114
|
+
for (const peerId of theirSubs) {
|
|
115
|
+
this.emit("notify-remote-heads", {
|
|
116
|
+
targetId: peerId,
|
|
117
|
+
documentId: event.documentId,
|
|
118
|
+
heads: event.remoteHeads,
|
|
119
|
+
timestamp: event.timestamp,
|
|
120
|
+
storageId: event.storageId,
|
|
121
|
+
});
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
/** A peer we are directly connected to has updated their heads */
|
|
127
|
+
handleImmediateRemoteHeadsChanged(documentId, storageId, heads) {
|
|
128
|
+
this.#log("handleLocalHeadsChanged", documentId, storageId, heads);
|
|
129
|
+
const remote = this.#knownHeads.get(documentId);
|
|
130
|
+
const timestamp = Date.now();
|
|
131
|
+
if (!remote) {
|
|
132
|
+
this.#knownHeads.set(documentId, new Map([[storageId, { heads, timestamp }]]));
|
|
133
|
+
}
|
|
134
|
+
else {
|
|
135
|
+
const docRemote = remote.get(storageId);
|
|
136
|
+
if (!docRemote || docRemote.timestamp < Date.now()) {
|
|
137
|
+
remote.set(storageId, { heads, timestamp: Date.now() });
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
const theirSubs = this.#theirSubscriptions.get(storageId);
|
|
141
|
+
if (theirSubs) {
|
|
142
|
+
for (const peerId of theirSubs) {
|
|
143
|
+
this.emit("notify-remote-heads", {
|
|
144
|
+
targetId: peerId,
|
|
145
|
+
documentId: documentId,
|
|
146
|
+
heads: heads,
|
|
147
|
+
timestamp: timestamp,
|
|
148
|
+
storageId: storageId,
|
|
149
|
+
});
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
addGenerousPeer(peerId) {
|
|
154
|
+
this.#log("addGenerousPeer", peerId);
|
|
155
|
+
this.#generousPeers.add(peerId);
|
|
156
|
+
if (this.#ourSubscriptions.size > 0) {
|
|
157
|
+
this.emit("change-remote-subs", {
|
|
158
|
+
add: Array.from(this.#ourSubscriptions),
|
|
159
|
+
peers: [peerId],
|
|
160
|
+
});
|
|
161
|
+
}
|
|
162
|
+
for (const [documentId, remote] of this.#knownHeads) {
|
|
163
|
+
for (const [storageId, { heads, timestamp }] of remote) {
|
|
164
|
+
this.emit("notify-remote-heads", {
|
|
165
|
+
targetId: peerId,
|
|
166
|
+
documentId: documentId,
|
|
167
|
+
heads: heads,
|
|
168
|
+
timestamp: timestamp,
|
|
169
|
+
storageId: storageId,
|
|
170
|
+
});
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
removePeer(peerId) {
|
|
175
|
+
this.#log("removePeer", peerId);
|
|
176
|
+
const remotesToRemove = [];
|
|
177
|
+
this.#generousPeers.delete(peerId);
|
|
178
|
+
for (const [storageId, peerIds] of this.#theirSubscriptions) {
|
|
179
|
+
if (peerIds.has(peerId)) {
|
|
180
|
+
peerIds.delete(peerId);
|
|
181
|
+
if (peerIds.size == 0) {
|
|
182
|
+
remotesToRemove.push(storageId);
|
|
183
|
+
this.#theirSubscriptions.delete(storageId);
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
if (remotesToRemove.length > 0) {
|
|
188
|
+
this.emit("change-remote-subs", {
|
|
189
|
+
remove: remotesToRemove,
|
|
190
|
+
peers: Array.from(this.#generousPeers),
|
|
191
|
+
});
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
/** Returns the (document, storageId) pairs which have changed after processing msg */
|
|
195
|
+
#changedHeads(msg) {
|
|
196
|
+
const changedHeads = [];
|
|
197
|
+
const { documentId, newHeads } = msg;
|
|
198
|
+
for (const [storageId, { heads, timestamp }] of Object.entries(newHeads)) {
|
|
199
|
+
if (!this.#ourSubscriptions.has(storageId) &&
|
|
200
|
+
!this.#theirSubscriptions.has(storageId)) {
|
|
201
|
+
continue;
|
|
202
|
+
}
|
|
203
|
+
let remote = this.#knownHeads.get(documentId);
|
|
204
|
+
if (!remote) {
|
|
205
|
+
remote = new Map([[storageId, { heads, timestamp }]]);
|
|
206
|
+
this.#knownHeads.set(documentId, remote);
|
|
207
|
+
}
|
|
208
|
+
const docRemote = remote.get(storageId);
|
|
209
|
+
if (docRemote && docRemote.timestamp > timestamp) {
|
|
210
|
+
continue;
|
|
211
|
+
}
|
|
212
|
+
else {
|
|
213
|
+
remote.set(storageId, { timestamp, heads });
|
|
214
|
+
changedHeads.push({
|
|
215
|
+
documentId,
|
|
216
|
+
storageId: storageId,
|
|
217
|
+
remoteHeads: heads,
|
|
218
|
+
timestamp,
|
|
219
|
+
});
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
return changedHeads;
|
|
223
|
+
}
|
|
224
|
+
}
|
package/dist/Repo.d.ts
CHANGED
|
@@ -5,6 +5,7 @@ import { NetworkSubsystem } from "./network/NetworkSubsystem.js";
|
|
|
5
5
|
import { StorageAdapter } from "./storage/StorageAdapter.js";
|
|
6
6
|
import { StorageSubsystem } from "./storage/StorageSubsystem.js";
|
|
7
7
|
import type { AnyDocumentId, DocumentId, PeerId } from "./types.js";
|
|
8
|
+
import { StorageId } from "./storage/types.js";
|
|
8
9
|
/** A Repo is a collection of documents with networking, syncing, and storage capabilities. */
|
|
9
10
|
/** The `Repo` is the main entry point of this library
|
|
10
11
|
*
|
|
@@ -25,7 +26,10 @@ export declare class Repo extends EventEmitter<RepoEvents> {
|
|
|
25
26
|
/** By default, we share generously with all peers. */
|
|
26
27
|
/** @hidden */
|
|
27
28
|
sharePolicy: SharePolicy;
|
|
28
|
-
|
|
29
|
+
/** maps peer id to to persistance information (storageId, isEphemeral), access by collection synchronizer */
|
|
30
|
+
/** @hidden */
|
|
31
|
+
persistanceInfoByPeerId: Record<PeerId, PersistanceInfo>;
|
|
32
|
+
constructor({ storage, network, peerId, sharePolicy, isEphemeral, }: RepoConfig);
|
|
29
33
|
/** Returns all the handles we have cached. */
|
|
30
34
|
get handles(): Record<DocumentId, DocHandle<any>>;
|
|
31
35
|
/** Returns a list of all connected peer ids */
|
|
@@ -62,10 +66,19 @@ export declare class Repo extends EventEmitter<RepoEvents> {
|
|
|
62
66
|
delete(
|
|
63
67
|
/** The url or documentId of the handle to delete */
|
|
64
68
|
id: AnyDocumentId): void;
|
|
69
|
+
subscribeToRemotes: (remotes: StorageId[]) => void;
|
|
70
|
+
storageId: () => Promise<StorageId | undefined>;
|
|
71
|
+
}
|
|
72
|
+
interface PersistanceInfo {
|
|
73
|
+
storageId: StorageId;
|
|
74
|
+
isEphemeral: boolean;
|
|
65
75
|
}
|
|
66
76
|
export interface RepoConfig {
|
|
67
77
|
/** Our unique identifier */
|
|
68
78
|
peerId?: PeerId;
|
|
79
|
+
/** Indicates whether other peers should persist the sync state of this peer.
|
|
80
|
+
* Sync state is only persisted for non-ephemeral peers */
|
|
81
|
+
isEphemeral?: boolean;
|
|
69
82
|
/** A storage adapter can be provided, or not */
|
|
70
83
|
storage?: StorageAdapter;
|
|
71
84
|
/** One or more network adapters must be provided */
|
|
@@ -100,4 +113,5 @@ export interface DocumentPayload {
|
|
|
100
113
|
export interface DeleteDocumentPayload {
|
|
101
114
|
documentId: DocumentId;
|
|
102
115
|
}
|
|
116
|
+
export {};
|
|
103
117
|
//# 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,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;
|
|
1
|
+
{"version":3,"file":"Repo.d.ts","sourceRoot":"","sources":["../src/Repo.ts"],"names":[],"mappings":"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"}
|
package/dist/Repo.js
CHANGED
|
@@ -7,6 +7,8 @@ import { throttle } from "./helpers/throttle.js";
|
|
|
7
7
|
import { NetworkSubsystem } from "./network/NetworkSubsystem.js";
|
|
8
8
|
import { StorageSubsystem } from "./storage/StorageSubsystem.js";
|
|
9
9
|
import { CollectionSynchronizer } from "./synchronizer/CollectionSynchronizer.js";
|
|
10
|
+
import { RemoteHeadsSubscriptions } from "./RemoteHeadsSubscriptions.js";
|
|
11
|
+
import { headsAreSame } from "./helpers/headsAreSame.js";
|
|
10
12
|
/** A Repo is a collection of documents with networking, syncing, and storage capabilities. */
|
|
11
13
|
/** The `Repo` is the main entry point of this library
|
|
12
14
|
*
|
|
@@ -29,7 +31,11 @@ export class Repo extends EventEmitter {
|
|
|
29
31
|
/** By default, we share generously with all peers. */
|
|
30
32
|
/** @hidden */
|
|
31
33
|
sharePolicy = async () => true;
|
|
32
|
-
|
|
34
|
+
/** maps peer id to to persistance information (storageId, isEphemeral), access by collection synchronizer */
|
|
35
|
+
/** @hidden */
|
|
36
|
+
persistanceInfoByPeerId = {};
|
|
37
|
+
#remoteHeadsSubscriptions = new RemoteHeadsSubscriptions();
|
|
38
|
+
constructor({ storage, network, peerId, sharePolicy, isEphemeral = storage === undefined, }) {
|
|
33
39
|
super();
|
|
34
40
|
this.#log = debug(`automerge-repo:repo`);
|
|
35
41
|
this.sharePolicy = sharePolicy ?? this.sharePolicy;
|
|
@@ -101,27 +107,119 @@ export class Repo extends EventEmitter {
|
|
|
101
107
|
this.storageSubsystem = storageSubsystem;
|
|
102
108
|
// NETWORK
|
|
103
109
|
// The network subsystem deals with sending and receiving messages to and from peers.
|
|
104
|
-
const networkSubsystem = new NetworkSubsystem(network, peerId);
|
|
110
|
+
const networkSubsystem = new NetworkSubsystem(network, peerId, storageSubsystem?.id() ?? Promise.resolve(undefined), isEphemeral);
|
|
105
111
|
this.networkSubsystem = networkSubsystem;
|
|
106
112
|
// When we get a new peer, register it with the synchronizer
|
|
107
|
-
networkSubsystem.on("peer", async ({ peerId }) => {
|
|
113
|
+
networkSubsystem.on("peer", async ({ peerId, storageId, isEphemeral }) => {
|
|
108
114
|
this.#log("peer connected", { peerId });
|
|
115
|
+
if (storageId) {
|
|
116
|
+
this.persistanceInfoByPeerId[peerId] = {
|
|
117
|
+
storageId,
|
|
118
|
+
isEphemeral,
|
|
119
|
+
};
|
|
120
|
+
}
|
|
121
|
+
this.sharePolicy(peerId)
|
|
122
|
+
.then(shouldShare => {
|
|
123
|
+
if (shouldShare) {
|
|
124
|
+
this.#remoteHeadsSubscriptions.addGenerousPeer(peerId);
|
|
125
|
+
}
|
|
126
|
+
})
|
|
127
|
+
.catch(err => {
|
|
128
|
+
console.log("error in share policy", { err });
|
|
129
|
+
});
|
|
109
130
|
this.#synchronizer.addPeer(peerId);
|
|
110
131
|
});
|
|
111
132
|
// When a peer disconnects, remove it from the synchronizer
|
|
112
133
|
networkSubsystem.on("peer-disconnected", ({ peerId }) => {
|
|
113
134
|
this.#synchronizer.removePeer(peerId);
|
|
135
|
+
this.#remoteHeadsSubscriptions.removePeer(peerId);
|
|
114
136
|
});
|
|
115
137
|
// Handle incoming messages
|
|
116
138
|
networkSubsystem.on("message", async (msg) => {
|
|
117
|
-
|
|
139
|
+
this.#receiveMessage(msg);
|
|
140
|
+
});
|
|
141
|
+
this.#synchronizer.on("sync-state", message => {
|
|
142
|
+
this.#saveSyncState(message);
|
|
143
|
+
const handle = this.#handleCache[message.documentId];
|
|
144
|
+
const info = this.persistanceInfoByPeerId[message.peerId];
|
|
145
|
+
if (!info) {
|
|
146
|
+
return;
|
|
147
|
+
}
|
|
148
|
+
const { storageId } = info;
|
|
149
|
+
const heads = handle.getRemoteHeads(storageId);
|
|
150
|
+
const haveHeadsChanged = message.syncState.theirHeads &&
|
|
151
|
+
(!heads || !headsAreSame(heads, message.syncState.theirHeads));
|
|
152
|
+
if (haveHeadsChanged) {
|
|
153
|
+
handle.setRemoteHeads(storageId, message.syncState.theirHeads);
|
|
154
|
+
if (storageId) {
|
|
155
|
+
this.#remoteHeadsSubscriptions.handleImmediateRemoteHeadsChanged(message.documentId, storageId, message.syncState.theirHeads);
|
|
156
|
+
}
|
|
157
|
+
}
|
|
118
158
|
});
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
159
|
+
this.#remoteHeadsSubscriptions.on("notify-remote-heads", message => {
|
|
160
|
+
this.networkSubsystem.send({
|
|
161
|
+
type: "remote-heads-changed",
|
|
162
|
+
targetId: message.targetId,
|
|
163
|
+
documentId: message.documentId,
|
|
164
|
+
newHeads: {
|
|
165
|
+
[message.storageId]: {
|
|
166
|
+
heads: message.heads,
|
|
167
|
+
timestamp: message.timestamp,
|
|
168
|
+
},
|
|
169
|
+
},
|
|
170
|
+
});
|
|
171
|
+
});
|
|
172
|
+
this.#remoteHeadsSubscriptions.on("change-remote-subs", message => {
|
|
173
|
+
this.#log("change-remote-subs", message);
|
|
174
|
+
for (const peer of message.peers) {
|
|
175
|
+
this.networkSubsystem.send({
|
|
176
|
+
type: "remote-subscription-change",
|
|
177
|
+
targetId: peer,
|
|
178
|
+
add: message.add,
|
|
179
|
+
remove: message.remove,
|
|
180
|
+
});
|
|
181
|
+
}
|
|
182
|
+
});
|
|
183
|
+
this.#remoteHeadsSubscriptions.on("remote-heads-changed", message => {
|
|
184
|
+
const handle = this.#handleCache[message.documentId];
|
|
185
|
+
handle.setRemoteHeads(message.storageId, message.remoteHeads);
|
|
186
|
+
});
|
|
187
|
+
}
|
|
188
|
+
#receiveMessage(message) {
|
|
189
|
+
switch (message.type) {
|
|
190
|
+
case "remote-subscription-change":
|
|
191
|
+
this.#remoteHeadsSubscriptions.handleControlMessage(message);
|
|
192
|
+
break;
|
|
193
|
+
case "remote-heads-changed":
|
|
194
|
+
this.#remoteHeadsSubscriptions.handleRemoteHeads(message);
|
|
195
|
+
break;
|
|
196
|
+
case "sync":
|
|
197
|
+
case "request":
|
|
198
|
+
case "ephemeral":
|
|
199
|
+
case "doc-unavailable":
|
|
200
|
+
this.#synchronizer.receiveMessage(message).catch(err => {
|
|
201
|
+
console.log("error receiving message", { err });
|
|
202
|
+
});
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
#throttledSaveSyncStateHandlers = {};
|
|
206
|
+
/** saves sync state throttled per storage id, if a peer doesn't have a storage id it's sync state is not persisted */
|
|
207
|
+
#saveSyncState(message) {
|
|
208
|
+
if (!this.storageSubsystem) {
|
|
209
|
+
return;
|
|
210
|
+
}
|
|
211
|
+
const persistanceInfo = this.persistanceInfoByPeerId[message.peerId];
|
|
212
|
+
if (!persistanceInfo || persistanceInfo.isEphemeral) {
|
|
213
|
+
return;
|
|
214
|
+
}
|
|
215
|
+
const { storageId } = persistanceInfo;
|
|
216
|
+
let handler = this.#throttledSaveSyncStateHandlers[storageId];
|
|
217
|
+
if (!handler) {
|
|
218
|
+
handler = this.#throttledSaveSyncStateHandlers[storageId] = throttle(({ documentId, syncState }) => {
|
|
219
|
+
this.storageSubsystem.saveSyncState(documentId, storageId, syncState);
|
|
122
220
|
}, this.saveDebounceRate);
|
|
123
|
-
this.#synchronizer.on("sync-state", debouncedSaveSyncState);
|
|
124
221
|
}
|
|
222
|
+
handler(message);
|
|
125
223
|
}
|
|
126
224
|
/** Returns an existing handle if we have it; creates one otherwise. */
|
|
127
225
|
#getHandle(
|
|
@@ -237,4 +335,16 @@ export class Repo extends EventEmitter {
|
|
|
237
335
|
delete this.#handleCache[documentId];
|
|
238
336
|
this.emit("delete-document", { documentId });
|
|
239
337
|
}
|
|
338
|
+
subscribeToRemotes = (remotes) => {
|
|
339
|
+
this.#log("subscribeToRemotes", { remotes });
|
|
340
|
+
this.#remoteHeadsSubscriptions.subscribeToRemotes(remotes);
|
|
341
|
+
};
|
|
342
|
+
storageId = async () => {
|
|
343
|
+
if (!this.storageSubsystem) {
|
|
344
|
+
return undefined;
|
|
345
|
+
}
|
|
346
|
+
else {
|
|
347
|
+
return this.storageSubsystem.id();
|
|
348
|
+
}
|
|
349
|
+
};
|
|
240
350
|
}
|
package/dist/index.d.ts
CHANGED
|
@@ -37,6 +37,6 @@ export type { DocHandleChangePayload, DocHandleDeletePayload, DocHandleEncodedCh
|
|
|
37
37
|
export type { DeleteDocumentPayload, DocumentPayload, RepoConfig, RepoEvents, SharePolicy, } from "./Repo.js";
|
|
38
38
|
export type { NetworkAdapterEvents, OpenPayload, PeerCandidatePayload, PeerDisconnectedPayload, } from "./network/NetworkAdapter.js";
|
|
39
39
|
export type { DocumentUnavailableMessage, EphemeralMessage, Message, RepoMessage, RequestMessage, SyncMessage, } from "./network/messages.js";
|
|
40
|
-
export type { Chunk, ChunkInfo, ChunkType, StorageKey, } from "./storage/types.js";
|
|
40
|
+
export type { Chunk, ChunkInfo, ChunkType, StorageKey, StorageId, } from "./storage/types.js";
|
|
41
41
|
export * from "./types.js";
|
|
42
42
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -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,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,2BAA2B,EAC3B,eAAe,EACf,gBAAgB,EAChB,wCAAwC,EACxC,WAAW,GACZ,MAAM,gBAAgB,CAAA;AAEvB,YAAY,EACV,qBAAqB,EACrB,eAAe,EACf,UAAU,EACV,UAAU,EACV,WAAW,GACZ,MAAM,WAAW,CAAA;AAElB,YAAY,EACV,oBAAoB,EACpB,WAAW,EACX,oBAAoB,EACpB,uBAAuB,GACxB,MAAM,6BAA6B,CAAA;AAEpC,YAAY,EACV,0BAA0B,EAC1B,gBAAgB,EAChB,OAAO,EACP,WAAW,EACX,cAAc,EACd,WAAW,GACZ,MAAM,uBAAuB,CAAA;AAE9B,YAAY,EACV,KAAK,EACL,SAAS,EACT,SAAS,EACT,UAAU,
|
|
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,2BAA2B,EAC3B,eAAe,EACf,gBAAgB,EAChB,wCAAwC,EACxC,WAAW,GACZ,MAAM,gBAAgB,CAAA;AAEvB,YAAY,EACV,qBAAqB,EACrB,eAAe,EACf,UAAU,EACV,UAAU,EACV,WAAW,GACZ,MAAM,WAAW,CAAA;AAElB,YAAY,EACV,oBAAoB,EACpB,WAAW,EACX,oBAAoB,EACpB,uBAAuB,GACxB,MAAM,6BAA6B,CAAA;AAEpC,YAAY,EACV,0BAA0B,EAC1B,gBAAgB,EAChB,OAAO,EACP,WAAW,EACX,cAAc,EACd,WAAW,GACZ,MAAM,uBAAuB,CAAA;AAE9B,YAAY,EACV,KAAK,EACL,SAAS,EACT,SAAS,EACT,UAAU,EACV,SAAS,GACV,MAAM,oBAAoB,CAAA;AAE3B,cAAc,YAAY,CAAA"}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { EventEmitter } from "eventemitter3";
|
|
2
2
|
import { PeerId } from "../types.js";
|
|
3
3
|
import { Message } from "./messages.js";
|
|
4
|
+
import { StorageId } from "../storage/types.js";
|
|
4
5
|
/** An interface representing some way to connect to other peers
|
|
5
6
|
*
|
|
6
7
|
* @remarks
|
|
@@ -10,11 +11,15 @@ import { Message } from "./messages.js";
|
|
|
10
11
|
*/
|
|
11
12
|
export declare abstract class NetworkAdapter extends EventEmitter<NetworkAdapterEvents> {
|
|
12
13
|
peerId?: PeerId;
|
|
14
|
+
storageId?: StorageId;
|
|
15
|
+
isEphemeral: boolean;
|
|
13
16
|
/** Called by the {@link Repo} to start the connection process
|
|
14
17
|
*
|
|
15
18
|
* @argument peerId - the peerId of this repo
|
|
19
|
+
* @argument storageId - the storage id of the peer
|
|
20
|
+
* @argument isEphemeral - weather or not the other end should persist our sync state
|
|
16
21
|
*/
|
|
17
|
-
abstract connect(peerId: PeerId): void;
|
|
22
|
+
abstract connect(peerId: PeerId, storageId: StorageId | undefined, isEphemeral: boolean): void;
|
|
18
23
|
/** Called by the {@link Repo} to send a message to a peer
|
|
19
24
|
*
|
|
20
25
|
* @argument message - the message to send
|
|
@@ -40,6 +45,8 @@ export interface OpenPayload {
|
|
|
40
45
|
}
|
|
41
46
|
export interface PeerCandidatePayload {
|
|
42
47
|
peerId: PeerId;
|
|
48
|
+
storageId?: StorageId;
|
|
49
|
+
isEphemeral: boolean;
|
|
43
50
|
}
|
|
44
51
|
export interface PeerDisconnectedPayload {
|
|
45
52
|
peerId: PeerId;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"NetworkAdapter.d.ts","sourceRoot":"","sources":["../../src/network/NetworkAdapter.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,eAAe,CAAA;AAC5C,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAA;AACpC,OAAO,EAAE,OAAO,EAAE,MAAM,eAAe,CAAA;
|
|
1
|
+
{"version":3,"file":"NetworkAdapter.d.ts","sourceRoot":"","sources":["../../src/network/NetworkAdapter.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,eAAe,CAAA;AAC5C,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAA;AACpC,OAAO,EAAE,OAAO,EAAE,MAAM,eAAe,CAAA;AACvC,OAAO,EAAE,SAAS,EAAE,MAAM,qBAAqB,CAAA;AAE/C;;;;;;GAMG;AACH,8BAAsB,cAAe,SAAQ,YAAY,CAAC,oBAAoB,CAAC;IAC7E,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,SAAS,CAAC,EAAE,SAAS,CAAA;IACrB,WAAW,UAAO;IAElB;;;;;OAKG;IACH,QAAQ,CAAC,OAAO,CACd,MAAM,EAAE,MAAM,EACd,SAAS,EAAE,SAAS,GAAG,SAAS,EAChC,WAAW,EAAE,OAAO,GACnB,IAAI;IAEP;;;OAGG;IACH,QAAQ,CAAC,IAAI,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI;IAErC,gEAAgE;IAChE,QAAQ,CAAC,UAAU,IAAI,IAAI;CAC5B;AAID,MAAM,WAAW,oBAAoB;IACnC,mDAAmD;IACnD,KAAK,EAAE,CAAC,OAAO,EAAE,WAAW,KAAK,IAAI,CAAA;IAErC,yCAAyC;IACzC,KAAK,EAAE,MAAM,IAAI,CAAA;IAEjB,+DAA+D;IAC/D,gBAAgB,EAAE,CAAC,OAAO,EAAE,oBAAoB,KAAK,IAAI,CAAA;IAEzD,2EAA2E;IAC3E,mBAAmB,EAAE,CAAC,OAAO,EAAE,uBAAuB,KAAK,IAAI,CAAA;IAE/D,sEAAsE;IACtE,OAAO,EAAE,CAAC,OAAO,EAAE,OAAO,KAAK,IAAI,CAAA;CACpC;AAED,MAAM,WAAW,WAAW;IAC1B,OAAO,EAAE,cAAc,CAAA;CACxB;AAED,MAAM,WAAW,oBAAoB;IACnC,MAAM,EAAE,MAAM,CAAA;IACd,SAAS,CAAC,EAAE,SAAS,CAAA;IACrB,WAAW,EAAE,OAAO,CAAA;CACrB;AAED,MAAM,WAAW,uBAAuB;IACtC,MAAM,EAAE,MAAM,CAAA;CACf"}
|