@automerge/automerge-repo 2.0.8 → 2.2.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/README.md +1 -1
- package/dist/Repo.d.ts +5 -4
- package/dist/Repo.d.ts.map +1 -1
- package/dist/Repo.js +23 -11
- package/dist/index.d.ts +4 -4
- package/dist/index.d.ts.map +1 -1
- package/package.json +3 -3
- package/src/Repo.ts +29 -9
- package/test/Repo.test.ts +92 -1
- package/vitest.config.ts +4 -0
- package/dist/helpers/debounce.d.ts +0 -14
- package/dist/helpers/debounce.d.ts.map +0 -1
- package/dist/helpers/debounce.js +0 -21
- package/src/helpers/debounce.ts +0 -25
package/README.md
CHANGED
|
@@ -125,7 +125,7 @@ yarn
|
|
|
125
125
|
yarn add @automerge/automerge @automerge/automerge-repo-react-hooks @automerge/automerge-repo-network-broadcastchannel @automerge/automerge-repo-storage-indexeddb vite-plugin-wasm
|
|
126
126
|
```
|
|
127
127
|
|
|
128
|
-
Edit the `vite.config.ts`. (This is all
|
|
128
|
+
Edit the `vite.config.ts`. (This is all needed to work around packaging hiccups due to WASM. We look
|
|
129
129
|
forward to the day that we can delete this step entirely.)
|
|
130
130
|
|
|
131
131
|
```ts
|
package/dist/Repo.d.ts
CHANGED
|
@@ -34,9 +34,6 @@ export declare class Repo extends EventEmitter<RepoEvents> {
|
|
|
34
34
|
networkSubsystem: NetworkSubsystem;
|
|
35
35
|
/** @hidden */
|
|
36
36
|
storageSubsystem?: StorageSubsystem;
|
|
37
|
-
/** The debounce rate is adjustable on the repo. */
|
|
38
|
-
/** @hidden */
|
|
39
|
-
saveDebounceRate: number;
|
|
40
37
|
/** @hidden */
|
|
41
38
|
synchronizer: CollectionSynchronizer;
|
|
42
39
|
/** By default, we share generously with all peers. */
|
|
@@ -45,7 +42,7 @@ export declare class Repo extends EventEmitter<RepoEvents> {
|
|
|
45
42
|
/** maps peer id to to persistence information (storageId, isEphemeral), access by collection synchronizer */
|
|
46
43
|
/** @hidden */
|
|
47
44
|
peerMetadataByPeerId: Record<PeerId, PeerMetadata>;
|
|
48
|
-
constructor({ storage, network, peerId, sharePolicy, isEphemeral, enableRemoteHeadsGossiping, denylist, }?: RepoConfig);
|
|
45
|
+
constructor({ storage, network, peerId, sharePolicy, isEphemeral, enableRemoteHeadsGossiping, denylist, saveDebounceRate, }?: RepoConfig);
|
|
49
46
|
/** Returns all the handles we have cached. */
|
|
50
47
|
get handles(): Record<DocumentId, DocHandle<any>>;
|
|
51
48
|
/** Returns a list of all connected peer ids */
|
|
@@ -144,6 +141,10 @@ export interface RepoConfig {
|
|
|
144
141
|
* loading documents that are known to be too resource intensive.
|
|
145
142
|
*/
|
|
146
143
|
denylist?: AutomergeUrl[];
|
|
144
|
+
/**
|
|
145
|
+
* The debounce rate in milliseconds for saving documents. Defaults to 100ms.
|
|
146
|
+
*/
|
|
147
|
+
saveDebounceRate?: number;
|
|
147
148
|
}
|
|
148
149
|
/** A function that determines whether we should share a document with a peer
|
|
149
150
|
*
|
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;AAQ5C,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,EACV,aAAa,EACb,YAAY,EACZ,UAAU,EACV,MAAM,EACP,MAAM,YAAY,CAAA;AACnB,OAAO,EAAa,YAAY,EAAE,MAAM,wBAAwB,CAAA;AAChE,OAAO,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAA;AAEhD,MAAM,MAAM,uBAAuB,CAAC,CAAC,IAAI,YAAY,CAAC,CAAC,CAAC,GAAG;IACzD,UAAU,EAAE,CAAC,eAAe,EAAE,MAAM,EAAE,KAAK,OAAO,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,CAAA;IAChE,IAAI,EAAE,MAAM,YAAY,CAAC,CAAC,CAAC,CAAA;IAC3B,SAAS,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,EAAE,YAAY,CAAC,CAAC,CAAC,KAAK,IAAI,KAAK,MAAM,IAAI,CAAA;CACzE,CAAA;AAED,MAAM,MAAM,cAAc,CAAC,CAAC,IAAI;IAC9B,IAAI,EAAE,MAAM,YAAY,CAAC,CAAC,CAAC,CAAA;IAC3B,SAAS,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,EAAE,YAAY,CAAC,CAAC,CAAC,KAAK,IAAI,KAAK,MAAM,IAAI,CAAA;IACxE,UAAU,EAAE,CAAC,eAAe,EAAE,MAAM,EAAE,KAAK,OAAO,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,CAAA;CACjE,CAAA;AAMD,8FAA8F;AAC9F;;;;;;GAMG;AACH,qBAAa,IAAK,SAAQ,YAAY,CAAC,UAAU,CAAC;;IAGhD,cAAc;IACd,gBAAgB,EAAE,gBAAgB,CAAA;IAClC,cAAc;IACd,gBAAgB,CAAC,EAAE,gBAAgB,CAAA;
|
|
1
|
+
{"version":3,"file":"Repo.d.ts","sourceRoot":"","sources":["../src/Repo.ts"],"names":[],"mappings":"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"}
|
package/dist/Repo.js
CHANGED
|
@@ -27,9 +27,10 @@ export class Repo extends EventEmitter {
|
|
|
27
27
|
networkSubsystem;
|
|
28
28
|
/** @hidden */
|
|
29
29
|
storageSubsystem;
|
|
30
|
-
/** The debounce rate is adjustable on the repo. */
|
|
31
30
|
/** @hidden */
|
|
32
|
-
saveDebounceRate
|
|
31
|
+
#saveDebounceRate;
|
|
32
|
+
/** @hidden */
|
|
33
|
+
#saveFn;
|
|
33
34
|
#handleCache = {};
|
|
34
35
|
/** @hidden */
|
|
35
36
|
synchronizer;
|
|
@@ -42,7 +43,7 @@ export class Repo extends EventEmitter {
|
|
|
42
43
|
#remoteHeadsSubscriptions = new RemoteHeadsSubscriptions();
|
|
43
44
|
#remoteHeadsGossipingEnabled = false;
|
|
44
45
|
#progressCache = {};
|
|
45
|
-
constructor({ storage, network = [], peerId = randomPeerId(), sharePolicy, isEphemeral = storage === undefined, enableRemoteHeadsGossiping = false, denylist = [], } = {}) {
|
|
46
|
+
constructor({ storage, network = [], peerId = randomPeerId(), sharePolicy, isEphemeral = storage === undefined, enableRemoteHeadsGossiping = false, denylist = [], saveDebounceRate = 100, } = {}) {
|
|
46
47
|
super();
|
|
47
48
|
this.#remoteHeadsGossipingEnabled = enableRemoteHeadsGossiping;
|
|
48
49
|
this.#log = debug(`automerge-repo:repo`);
|
|
@@ -77,6 +78,17 @@ export class Repo extends EventEmitter {
|
|
|
77
78
|
storageSubsystem.on("document-loaded", event => this.emit("doc-metrics", { type: "doc-loaded", ...event }));
|
|
78
79
|
}
|
|
79
80
|
this.storageSubsystem = storageSubsystem;
|
|
81
|
+
this.#saveDebounceRate = saveDebounceRate;
|
|
82
|
+
if (this.storageSubsystem) {
|
|
83
|
+
const saveFn = ({ handle, doc }) => {
|
|
84
|
+
void this.storageSubsystem.saveDoc(handle.documentId, doc);
|
|
85
|
+
};
|
|
86
|
+
// Save no more often than saveDebounceRate.
|
|
87
|
+
this.#saveFn = throttle(saveFn, this.#saveDebounceRate);
|
|
88
|
+
}
|
|
89
|
+
else {
|
|
90
|
+
this.#saveFn = () => { };
|
|
91
|
+
}
|
|
80
92
|
// NETWORK
|
|
81
93
|
// The network subsystem deals with sending and receiving messages to and from peers.
|
|
82
94
|
const myPeerMetadata = (async () => ({
|
|
@@ -169,13 +181,13 @@ export class Repo extends EventEmitter {
|
|
|
169
181
|
// The `document` event is fired by the DocCollection any time we create a new document or look
|
|
170
182
|
// up a document by ID. We listen for it in order to wire up storage and network synchronization.
|
|
171
183
|
#registerHandleWithSubsystems(handle) {
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
184
|
+
if (this.storageSubsystem) {
|
|
185
|
+
// Add save function as a listener if it's not already registered
|
|
186
|
+
const existingListeners = handle.listeners("heads-changed");
|
|
187
|
+
if (!existingListeners.some(listener => listener === this.#saveFn)) {
|
|
188
|
+
// Save when the document changes
|
|
189
|
+
handle.on("heads-changed", this.#saveFn);
|
|
190
|
+
}
|
|
179
191
|
}
|
|
180
192
|
// Register the document with the synchronizer. This advertises our interest in the document.
|
|
181
193
|
this.synchronizer.addDocument(handle);
|
|
@@ -215,7 +227,7 @@ export class Repo extends EventEmitter {
|
|
|
215
227
|
if (!handler) {
|
|
216
228
|
handler = this.#throttledSaveSyncStateHandlers[storageId] = throttle(({ documentId, syncState }) => {
|
|
217
229
|
void this.storageSubsystem.saveSyncState(documentId, storageId, syncState);
|
|
218
|
-
}, this
|
|
230
|
+
}, this.#saveDebounceRate);
|
|
219
231
|
}
|
|
220
232
|
handler(payload);
|
|
221
233
|
}
|
package/dist/index.d.ts
CHANGED
|
@@ -44,8 +44,8 @@ export type { DocumentUnavailableMessage, EphemeralMessage, Message, RepoMessage
|
|
|
44
44
|
export type { Chunk, ChunkInfo, ChunkType, StorageKey, StorageId, } from "./storage/types.js";
|
|
45
45
|
export * from "./types.js";
|
|
46
46
|
export declare const Counter: typeof Automerge.Counter;
|
|
47
|
-
export declare const RawString: typeof Automerge.
|
|
48
|
-
export declare const ImmutableString: typeof Automerge.
|
|
47
|
+
export declare const RawString: typeof Automerge.ImmutableString;
|
|
48
|
+
export declare const ImmutableString: typeof Automerge.ImmutableString;
|
|
49
49
|
export type RawString = InstanceType<typeof Automerge.RawString>;
|
|
50
50
|
export type ImmutableString = RawString;
|
|
51
51
|
export type Counter = Automerge.Counter;
|
|
@@ -75,8 +75,8 @@ export declare const insertAt: typeof Automerge.insertAt;
|
|
|
75
75
|
export declare const deleteAt: typeof Automerge.deleteAt;
|
|
76
76
|
export declare const mark: typeof Automerge.mark;
|
|
77
77
|
export declare const unmark: typeof Automerge.unmark;
|
|
78
|
-
export declare const isRawString: typeof Automerge.
|
|
79
|
-
export declare const isImmutableString: typeof Automerge.
|
|
78
|
+
export declare const isRawString: typeof Automerge.isImmutableString;
|
|
79
|
+
export declare const isImmutableString: typeof Automerge.isImmutableString;
|
|
80
80
|
export declare const getObjectId: typeof Automerge.getObjectId;
|
|
81
81
|
export type { ObjID };
|
|
82
82
|
//# 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,iBAAiB,EACjB,qBAAqB,EACrB,qBAAqB,EACrB,oBAAoB,EACpB,WAAW,EACX,WAAW,GACZ,MAAM,mBAAmB,CAAA;AAC1B,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAA;AAChC,OAAO,EAAE,cAAc,EAAE,MAAM,6BAA6B,CAAA;AAC5D,YAAY,EAAE,uBAAuB,EAAE,MAAM,sCAAsC,CAAA;AACnF,OAAO,EAAE,aAAa,EAAE,MAAM,uBAAuB,CAAA;AACrD,OAAO,EAAE,cAAc,EAAE,MAAM,6BAA6B,CAAA;AAC5D,YAAY,EAAE,uBAAuB,EAAE,MAAM,sCAAsC,CAAA;AACnF,OAAO,EAAE,IAAI,IAAI,SAAS,EAAE,KAAK,KAAK,EAAE,MAAM,2BAA2B,CAAA;AAEzE,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,EACX,QAAQ,GACT,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,EACvB,YAAY,GACb,MAAM,sCAAsC,CAAA;AAE7C,YAAY,EACV,sBAAsB,EACtB,WAAW,GACZ,MAAM,+BAA+B,CAAA;AAEtC,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;AAiB1B,eAAO,MAAM,OAAO,0BAAoB,CAAA;AACxC,eAAO,MAAM,SAAS,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@automerge/automerge-repo",
|
|
3
|
-
"version": "2.0
|
|
3
|
+
"version": "2.2.0",
|
|
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>",
|
|
@@ -24,7 +24,7 @@
|
|
|
24
24
|
"vite": "^5.0.8"
|
|
25
25
|
},
|
|
26
26
|
"dependencies": {
|
|
27
|
-
"@automerge/automerge": "
|
|
27
|
+
"@automerge/automerge": "2.2.8 - 3",
|
|
28
28
|
"bs58check": "^3.0.1",
|
|
29
29
|
"cbor-x": "^1.3.0",
|
|
30
30
|
"debug": "^4.3.4",
|
|
@@ -59,5 +59,5 @@
|
|
|
59
59
|
"publishConfig": {
|
|
60
60
|
"access": "public"
|
|
61
61
|
},
|
|
62
|
-
"gitHead": "
|
|
62
|
+
"gitHead": "0eab1c635365712017d7ccc000e7cd92f14e7143"
|
|
63
63
|
}
|
package/src/Repo.ts
CHANGED
|
@@ -74,9 +74,11 @@ export class Repo extends EventEmitter<RepoEvents> {
|
|
|
74
74
|
/** @hidden */
|
|
75
75
|
storageSubsystem?: StorageSubsystem
|
|
76
76
|
|
|
77
|
-
/** The debounce rate is adjustable on the repo. */
|
|
78
77
|
/** @hidden */
|
|
79
|
-
saveDebounceRate
|
|
78
|
+
#saveDebounceRate: number
|
|
79
|
+
|
|
80
|
+
/** @hidden */
|
|
81
|
+
#saveFn: (payload: DocHandleEncodedChangePayload<any>) => void
|
|
80
82
|
|
|
81
83
|
#handleCache: Record<DocumentId, DocHandle<any>> = {}
|
|
82
84
|
|
|
@@ -103,6 +105,7 @@ export class Repo extends EventEmitter<RepoEvents> {
|
|
|
103
105
|
isEphemeral = storage === undefined,
|
|
104
106
|
enableRemoteHeadsGossiping = false,
|
|
105
107
|
denylist = [],
|
|
108
|
+
saveDebounceRate = 100,
|
|
106
109
|
}: RepoConfig = {}) {
|
|
107
110
|
super()
|
|
108
111
|
this.#remoteHeadsGossipingEnabled = enableRemoteHeadsGossiping
|
|
@@ -149,6 +152,18 @@ export class Repo extends EventEmitter<RepoEvents> {
|
|
|
149
152
|
|
|
150
153
|
this.storageSubsystem = storageSubsystem
|
|
151
154
|
|
|
155
|
+
this.#saveDebounceRate = saveDebounceRate
|
|
156
|
+
|
|
157
|
+
if (this.storageSubsystem) {
|
|
158
|
+
const saveFn = ({ handle, doc }: DocHandleEncodedChangePayload<any>) => {
|
|
159
|
+
void this.storageSubsystem!.saveDoc(handle.documentId, doc)
|
|
160
|
+
}
|
|
161
|
+
// Save no more often than saveDebounceRate.
|
|
162
|
+
this.#saveFn = throttle(saveFn, this.#saveDebounceRate)
|
|
163
|
+
} else {
|
|
164
|
+
this.#saveFn = () => {}
|
|
165
|
+
}
|
|
166
|
+
|
|
152
167
|
// NETWORK
|
|
153
168
|
// The network subsystem deals with sending and receiving messages to and from peers.
|
|
154
169
|
|
|
@@ -271,13 +286,13 @@ export class Repo extends EventEmitter<RepoEvents> {
|
|
|
271
286
|
// The `document` event is fired by the DocCollection any time we create a new document or look
|
|
272
287
|
// up a document by ID. We listen for it in order to wire up storage and network synchronization.
|
|
273
288
|
#registerHandleWithSubsystems(handle: DocHandle<any>) {
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
289
|
+
if (this.storageSubsystem) {
|
|
290
|
+
// Add save function as a listener if it's not already registered
|
|
291
|
+
const existingListeners = handle.listeners("heads-changed")
|
|
292
|
+
if (!existingListeners.some(listener => listener === this.#saveFn)) {
|
|
293
|
+
// Save when the document changes
|
|
294
|
+
handle.on("heads-changed", this.#saveFn)
|
|
279
295
|
}
|
|
280
|
-
handle.on("heads-changed", throttle(saveFn, this.saveDebounceRate))
|
|
281
296
|
}
|
|
282
297
|
|
|
283
298
|
// Register the document with the synchronizer. This advertises our interest in the document.
|
|
@@ -334,7 +349,7 @@ export class Repo extends EventEmitter<RepoEvents> {
|
|
|
334
349
|
syncState
|
|
335
350
|
)
|
|
336
351
|
},
|
|
337
|
-
this
|
|
352
|
+
this.#saveDebounceRate
|
|
338
353
|
)
|
|
339
354
|
}
|
|
340
355
|
|
|
@@ -861,6 +876,11 @@ export interface RepoConfig {
|
|
|
861
876
|
* loading documents that are known to be too resource intensive.
|
|
862
877
|
*/
|
|
863
878
|
denylist?: AutomergeUrl[]
|
|
879
|
+
|
|
880
|
+
/**
|
|
881
|
+
* The debounce rate in milliseconds for saving documents. Defaults to 100ms.
|
|
882
|
+
*/
|
|
883
|
+
saveDebounceRate?: number
|
|
864
884
|
}
|
|
865
885
|
|
|
866
886
|
/** A function that determines whether we should share a document with a peer
|
package/test/Repo.test.ts
CHANGED
|
@@ -51,8 +51,8 @@ describe("Repo", () => {
|
|
|
51
51
|
const repo = new Repo({
|
|
52
52
|
storage: storageAdapter,
|
|
53
53
|
network: [networkAdapter],
|
|
54
|
+
saveDebounceRate: 1,
|
|
54
55
|
})
|
|
55
|
-
repo.saveDebounceRate = 1
|
|
56
56
|
return { repo, storageAdapter, networkAdapter }
|
|
57
57
|
}
|
|
58
58
|
|
|
@@ -601,6 +601,85 @@ describe("Repo", () => {
|
|
|
601
601
|
assert(Object.keys(repo.handles).length === handleCacheSize)
|
|
602
602
|
})
|
|
603
603
|
})
|
|
604
|
+
|
|
605
|
+
describe("registerHandleWithSubsystems", () => {
|
|
606
|
+
it("registers document with synchronizer when creating a new document", async () => {
|
|
607
|
+
const { repo } = setup()
|
|
608
|
+
const handle = repo.create<TestDoc>()
|
|
609
|
+
assert(repo.synchronizer.docSynchronizers[handle.documentId])
|
|
610
|
+
})
|
|
611
|
+
|
|
612
|
+
it("registers save to storage when creating a new document", async () => {
|
|
613
|
+
const { repo, storageAdapter } = setup()
|
|
614
|
+
const handle = repo.create<TestDoc>()
|
|
615
|
+
await pause(10) // wait for debounced save to complete
|
|
616
|
+
assert(
|
|
617
|
+
storageAdapter.keys().some(key => key.includes(handle.documentId))
|
|
618
|
+
)
|
|
619
|
+
})
|
|
620
|
+
|
|
621
|
+
it("registers document with synchronizer when finding an existing document", async () => {
|
|
622
|
+
const { repo, storageAdapter } = setup()
|
|
623
|
+
const handle = repo.create<TestDoc>()
|
|
624
|
+
await pause(10) // wait for debounced save to complete
|
|
625
|
+
|
|
626
|
+
const repo2 = new Repo({ storage: storageAdapter })
|
|
627
|
+
await repo2.find<TestDoc>(handle.url)
|
|
628
|
+
assert(repo2.synchronizer.docSynchronizers[handle.documentId])
|
|
629
|
+
})
|
|
630
|
+
|
|
631
|
+
it("registers document with synchronizer when finding an existing document with progress", async () => {
|
|
632
|
+
const { repo, storageAdapter } = setup()
|
|
633
|
+
const handle = repo.create<TestDoc>()
|
|
634
|
+
await pause(10) // wait for debounced save to complete
|
|
635
|
+
|
|
636
|
+
const repo2 = new Repo({ storage: storageAdapter })
|
|
637
|
+
repo2.findWithProgress<TestDoc>(handle.url)
|
|
638
|
+
await pause(10)
|
|
639
|
+
assert(repo2.synchronizer.docSynchronizers[handle.documentId])
|
|
640
|
+
})
|
|
641
|
+
|
|
642
|
+
it("registers document with synchronizer when there is no storage subsystem", async () => {
|
|
643
|
+
const repo = new Repo()
|
|
644
|
+
const handle = repo.create<TestDoc>()
|
|
645
|
+
assert(repo.synchronizer.docSynchronizers[handle.documentId])
|
|
646
|
+
assert.equal(handle.listenerCount("heads-changed"), 0) // no save listener registered
|
|
647
|
+
})
|
|
648
|
+
|
|
649
|
+
it("respects saveDebounceRate when saving", async () => {
|
|
650
|
+
const storageAdapter = new DummyStorageAdapter()
|
|
651
|
+
const networkAdapter = new DummyNetworkAdapter()
|
|
652
|
+
const repo = new Repo({
|
|
653
|
+
storage: storageAdapter,
|
|
654
|
+
network: [networkAdapter],
|
|
655
|
+
saveDebounceRate: 100,
|
|
656
|
+
})
|
|
657
|
+
const handle = repo.create<TestDoc>()
|
|
658
|
+
|
|
659
|
+
for (let i = 0; i < 5; i++) {
|
|
660
|
+
handle.change(d => {
|
|
661
|
+
d.foo = `bar${i}`
|
|
662
|
+
})
|
|
663
|
+
}
|
|
664
|
+
await pause(10)
|
|
665
|
+
assert(storageAdapter.keys().length < 5)
|
|
666
|
+
|
|
667
|
+
const keysBeforeDebouncedSave = storageAdapter.keys().length
|
|
668
|
+
await pause(150)
|
|
669
|
+
const keysAfterDebouncedSave = storageAdapter.keys().length
|
|
670
|
+
assert(keysAfterDebouncedSave > keysBeforeDebouncedSave)
|
|
671
|
+
})
|
|
672
|
+
|
|
673
|
+
it("does not add duplicate heads-changed listeners", async () => {
|
|
674
|
+
const { repo } = setup()
|
|
675
|
+
const handle = repo.create<TestDoc>()
|
|
676
|
+
await pause(10) // wait for debounced save to complete
|
|
677
|
+
await repo.find<TestDoc>(handle.url)
|
|
678
|
+
repo.findWithProgress<TestDoc>(handle.url)
|
|
679
|
+
await pause(10)
|
|
680
|
+
assert.equal(handle.listenerCount("heads-changed"), 1)
|
|
681
|
+
})
|
|
682
|
+
})
|
|
604
683
|
})
|
|
605
684
|
|
|
606
685
|
describe("flush behaviour", () => {
|
|
@@ -1368,6 +1447,18 @@ describe("Repo", () => {
|
|
|
1368
1447
|
|
|
1369
1448
|
teardown()
|
|
1370
1449
|
})
|
|
1450
|
+
|
|
1451
|
+
it("does not add duplicate heads-changed listeners", async () => {
|
|
1452
|
+
const { aliceRepo, bobRepo, teardown } = await setup()
|
|
1453
|
+
|
|
1454
|
+
const aliceHandle = aliceRepo.create<TestDoc>({ foo: "bar" })
|
|
1455
|
+
await pause(10)
|
|
1456
|
+
|
|
1457
|
+
const bobHandle = await bobRepo.find<TestDoc>(aliceHandle.url)
|
|
1458
|
+
assert.equal(bobHandle.listenerCount("heads-changed"), 1)
|
|
1459
|
+
|
|
1460
|
+
teardown()
|
|
1461
|
+
})
|
|
1371
1462
|
})
|
|
1372
1463
|
|
|
1373
1464
|
describe("with peers (mesh network)", () => {
|
package/vitest.config.ts
ADDED
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
/** throttle( callback, rate )
|
|
2
|
-
* Returns a throttle function with a build in debounce timer that runs after `wait` ms.
|
|
3
|
-
*
|
|
4
|
-
* Note that the args go inside the parameter and you should be careful not to
|
|
5
|
-
* recreate the function on each usage. (In React, see useMemo().)
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
* Example usage:
|
|
9
|
-
* const callback = throttle((ev) => { doSomethingExpensiveOrOccasional() }, 100)
|
|
10
|
-
* target.addEventListener('frequent-event', callback);
|
|
11
|
-
*
|
|
12
|
-
*/
|
|
13
|
-
export declare const throttle: <F extends (...args: Parameters<F>) => ReturnType<F>>(fn: F, rate: number) => (...args: Parameters<F>) => void;
|
|
14
|
-
//# sourceMappingURL=debounce.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"debounce.d.ts","sourceRoot":"","sources":["../../src/helpers/debounce.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,eAAO,MAAM,QAAQ,GAAI,CAAC,SAAS,CAAC,GAAG,IAAI,EAAE,UAAU,CAAC,CAAC,CAAC,KAAK,UAAU,CAAC,CAAC,CAAC,EAC1E,IAAI,CAAC,EACL,MAAM,MAAM,MAGK,GAAG,MAAM,UAAU,CAAC,CAAC,CAAC,SAMxC,CAAA"}
|
package/dist/helpers/debounce.js
DELETED
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
/** throttle( callback, rate )
|
|
2
|
-
* Returns a throttle function with a build in debounce timer that runs after `wait` ms.
|
|
3
|
-
*
|
|
4
|
-
* Note that the args go inside the parameter and you should be careful not to
|
|
5
|
-
* recreate the function on each usage. (In React, see useMemo().)
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
* Example usage:
|
|
9
|
-
* const callback = throttle((ev) => { doSomethingExpensiveOrOccasional() }, 100)
|
|
10
|
-
* target.addEventListener('frequent-event', callback);
|
|
11
|
-
*
|
|
12
|
-
*/
|
|
13
|
-
export const throttle = (fn, rate) => {
|
|
14
|
-
let timeout;
|
|
15
|
-
return function (...args) {
|
|
16
|
-
clearTimeout(timeout);
|
|
17
|
-
timeout = setTimeout(() => {
|
|
18
|
-
fn(...args);
|
|
19
|
-
}, rate);
|
|
20
|
-
};
|
|
21
|
-
};
|
package/src/helpers/debounce.ts
DELETED
|
@@ -1,25 +0,0 @@
|
|
|
1
|
-
/** throttle( callback, rate )
|
|
2
|
-
* Returns a throttle function with a build in debounce timer that runs after `wait` ms.
|
|
3
|
-
*
|
|
4
|
-
* Note that the args go inside the parameter and you should be careful not to
|
|
5
|
-
* recreate the function on each usage. (In React, see useMemo().)
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
* Example usage:
|
|
9
|
-
* const callback = throttle((ev) => { doSomethingExpensiveOrOccasional() }, 100)
|
|
10
|
-
* target.addEventListener('frequent-event', callback);
|
|
11
|
-
*
|
|
12
|
-
*/
|
|
13
|
-
|
|
14
|
-
export const throttle = <F extends (...args: Parameters<F>) => ReturnType<F>>(
|
|
15
|
-
fn: F,
|
|
16
|
-
rate: number
|
|
17
|
-
) => {
|
|
18
|
-
let timeout: ReturnType<typeof setTimeout>
|
|
19
|
-
return function (...args: Parameters<F>) {
|
|
20
|
-
clearTimeout(timeout)
|
|
21
|
-
timeout = setTimeout(() => {
|
|
22
|
-
fn(...args)
|
|
23
|
-
}, rate)
|
|
24
|
-
}
|
|
25
|
-
}
|