@automerge/automerge-repo 2.5.1 → 2.5.2-alpha.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,526 @@
1
+ import { EventEmitter } from "eventemitter3";
2
+ export const PRESENCE_MESSAGE_MARKER = "__presence";
3
+ export const DEFAULT_HEARTBEAT_INTERVAL_MS = 15_000;
4
+ export const DEFAULT_PEER_TTL_MS = 3 * DEFAULT_HEARTBEAT_INTERVAL_MS;
5
+ /**
6
+ * Presence encapsulates ephemeral state communication for a specific doc
7
+ * handle. It tracks caller-provided local state and broadcasts that state to
8
+ * all peers. It sends periodic heartbeats when there are no state updates.
9
+ *
10
+ * It also tracks ephemeral state broadcast by peers and emits events when peers
11
+ * send ephemeral state updates (see {@link PresenceEvents}).
12
+ *
13
+ * Presence starts out in an inactive state. Call {@link start} and {@link stop}
14
+ * to activate and deactivate it.
15
+ */
16
+ export class Presence extends EventEmitter {
17
+ #handle;
18
+ deviceId;
19
+ userId;
20
+ #peers;
21
+ #localState;
22
+ #heartbeatMs;
23
+ #handleEphemeralMessage;
24
+ #heartbeatInterval;
25
+ #pruningInterval;
26
+ #hellos = [];
27
+ #running = false;
28
+ /**
29
+ * Create a new Presence to share ephemeral state with peers.
30
+ *
31
+ * @param config see {@link PresenceConfig}
32
+ * @returns
33
+ */
34
+ constructor({ handle, deviceId, userId, }) {
35
+ super();
36
+ this.#handle = handle;
37
+ this.#peers = new PeerPresenceInfo(DEFAULT_PEER_TTL_MS);
38
+ this.userId = userId;
39
+ this.deviceId = deviceId;
40
+ }
41
+ /**
42
+ * Start listening to ephemeral messages on the handle, broadcast initial
43
+ * state to peers, and start sending heartbeats.
44
+ */
45
+ start({ initialState, heartbeatMs, peerTtlMs }) {
46
+ if (this.#running) {
47
+ return;
48
+ }
49
+ this.#running = true;
50
+ this.#heartbeatMs = heartbeatMs ?? DEFAULT_HEARTBEAT_INTERVAL_MS;
51
+ this.#peers = new PeerPresenceInfo(peerTtlMs ?? DEFAULT_PEER_TTL_MS);
52
+ this.#localState = initialState;
53
+ // N.B.: We can't use a regular member function here since member functions
54
+ // of two distinct objects are identical, and we need to be able to stop
55
+ // listening to the handle for just this Presence instance in stop()
56
+ this.#handleEphemeralMessage = (e) => {
57
+ const peerId = e.senderId;
58
+ const envelope = e.message;
59
+ if (!(PRESENCE_MESSAGE_MARKER in envelope)) {
60
+ return;
61
+ }
62
+ const message = envelope[PRESENCE_MESSAGE_MARKER];
63
+ const { deviceId, userId } = message;
64
+ if (!this.#peers.view.has(peerId)) {
65
+ this.announce();
66
+ }
67
+ switch (message.type) {
68
+ case "heartbeat":
69
+ this.#peers.markSeen(peerId, deviceId, userId);
70
+ this.emit("heartbeat", {
71
+ type: "heartbeat",
72
+ peerId,
73
+ deviceId,
74
+ userId,
75
+ });
76
+ break;
77
+ case "goodbye":
78
+ this.#peers.delete(peerId);
79
+ this.emit("goodbye", {
80
+ type: "goodbye",
81
+ peerId,
82
+ deviceId,
83
+ userId,
84
+ });
85
+ break;
86
+ case "update":
87
+ this.#peers.update({
88
+ peerId,
89
+ deviceId,
90
+ userId,
91
+ channel: message.channel,
92
+ value: message.value,
93
+ });
94
+ this.emit("update", {
95
+ type: "update",
96
+ peerId,
97
+ deviceId,
98
+ userId,
99
+ channel: message.channel,
100
+ value: message.value,
101
+ });
102
+ break;
103
+ case "snapshot":
104
+ Object.entries(message.state).forEach(([channel, value]) => {
105
+ this.#peers.update({
106
+ peerId,
107
+ deviceId,
108
+ userId,
109
+ channel: channel,
110
+ value,
111
+ });
112
+ });
113
+ this.emit("snapshot", {
114
+ type: "snapshot",
115
+ peerId,
116
+ deviceId,
117
+ userId,
118
+ state: message.state,
119
+ });
120
+ break;
121
+ }
122
+ };
123
+ this.#handle.on("ephemeral-message", this.#handleEphemeralMessage);
124
+ this.broadcastLocalState(); // also starts heartbeats
125
+ this.startPruningPeers();
126
+ }
127
+ /**
128
+ * Return a view of current peer states.
129
+ */
130
+ getPeerStates() {
131
+ return this.#peers.view;
132
+ }
133
+ /**
134
+ * Return a view of current local state.
135
+ */
136
+ getLocalState() {
137
+ return this.#localState;
138
+ }
139
+ /**
140
+ * Update state for the specific channel, and broadcast new state to all
141
+ * peers.
142
+ *
143
+ * @param channel
144
+ * @param value
145
+ */
146
+ broadcast(channel, value) {
147
+ this.#localState = Object.assign({}, this.#localState, {
148
+ [channel]: value,
149
+ });
150
+ this.broadcastChannelState(channel, value);
151
+ }
152
+ /**
153
+ * Whether this Presence is currently active. See
154
+ * {@link start} and {@link stop}.
155
+ */
156
+ get running() {
157
+ return this.#running;
158
+ }
159
+ /**
160
+ * Stop this Presence: broadcast a "goodbye" message (when received, other
161
+ * peers will immediately forget the sender), stop sending heartbeats, and
162
+ * stop listening to ephemeral-messages broadcast from peers.
163
+ *
164
+ * This can be used with browser events like
165
+ * {@link https://developer.mozilla.org/en-US/docs/Web/API/Window/pagehide_event | "pagehide"}
166
+ * or
167
+ * {@link https://developer.mozilla.org/en-US/docs/Web/API/Document/visibilitychange_event | "visibilitychange"}
168
+ * to stop sending and receiving updates when not active.
169
+ */
170
+ stop() {
171
+ if (!this.#running) {
172
+ return;
173
+ }
174
+ this.#hellos.forEach(timeoutId => {
175
+ clearTimeout(timeoutId);
176
+ });
177
+ this.#hellos = [];
178
+ this.#handle.off("ephemeral-message", this.#handleEphemeralMessage);
179
+ this.stopHeartbeats();
180
+ this.stopPruningPeers();
181
+ this.doBroadcast("goodbye");
182
+ this.#running = false;
183
+ }
184
+ announce() {
185
+ // Broadcast our current state whenever we see new peers
186
+ // TODO: We currently need to wait for the peer to be ready, but waiting
187
+ // some arbitrary amount of time is brittle
188
+ const helloId = setTimeout(() => {
189
+ this.broadcastLocalState();
190
+ this.#hellos = this.#hellos.filter(id => id !== helloId);
191
+ }, 500);
192
+ this.#hellos.push(helloId);
193
+ }
194
+ broadcastLocalState() {
195
+ this.doBroadcast("snapshot", { state: this.#localState });
196
+ this.resetHeartbeats();
197
+ }
198
+ broadcastChannelState(channel, value) {
199
+ this.doBroadcast("update", { channel, value });
200
+ this.resetHeartbeats();
201
+ }
202
+ resetHeartbeats() {
203
+ // Reset heartbeats every time we broadcast a message to avoid sending
204
+ // unnecessary heartbeats when there is plenty of actual update activity
205
+ // happening.
206
+ this.stopHeartbeats();
207
+ this.startHeartbeats();
208
+ }
209
+ sendHeartbeat() {
210
+ this.doBroadcast("heartbeat");
211
+ }
212
+ doBroadcast(type, extra) {
213
+ if (!this.#running) {
214
+ return;
215
+ }
216
+ this.#handle.broadcast({
217
+ [PRESENCE_MESSAGE_MARKER]: {
218
+ userId: this.userId,
219
+ deviceId: this.deviceId,
220
+ type,
221
+ ...extra,
222
+ },
223
+ });
224
+ }
225
+ startHeartbeats() {
226
+ if (this.#heartbeatInterval !== undefined) {
227
+ return;
228
+ }
229
+ this.#heartbeatInterval = setInterval(() => {
230
+ this.sendHeartbeat();
231
+ }, this.#heartbeatMs);
232
+ }
233
+ stopHeartbeats() {
234
+ if (this.#heartbeatInterval === undefined) {
235
+ return;
236
+ }
237
+ clearInterval(this.#heartbeatInterval);
238
+ this.#heartbeatInterval = undefined;
239
+ }
240
+ startPruningPeers() {
241
+ if (this.#pruningInterval !== undefined) {
242
+ return;
243
+ }
244
+ // Pruning happens at the heartbeat frequency, not on a peer ttl frequency,
245
+ // to minimize variance between peer expiration, since the heartbeat frequency
246
+ // is expected to be several times higher.
247
+ this.#pruningInterval = setInterval(() => {
248
+ this.#peers.prune();
249
+ }, this.#heartbeatMs);
250
+ }
251
+ stopPruningPeers() {
252
+ if (this.#pruningInterval === undefined) {
253
+ return;
254
+ }
255
+ clearInterval(this.#pruningInterval);
256
+ this.#pruningInterval = undefined;
257
+ }
258
+ }
259
+ /**
260
+ * A summary of the latest Presence information for the set of peers who have
261
+ * reported a Presence status to us.
262
+ */
263
+ export class PeerPresenceView {
264
+ #peersLastSeen = new Map();
265
+ #peerStates = new Map();
266
+ #userPeers = new Map();
267
+ #devicePeers = new Map();
268
+ /** @hidden */
269
+ constructor(peersLastSeen, peerStates, userPeers, devicePeers) {
270
+ this.#peersLastSeen = peersLastSeen;
271
+ this.#peerStates = peerStates;
272
+ this.#userPeers = userPeers;
273
+ this.#devicePeers = devicePeers;
274
+ }
275
+ /**
276
+ * Check if peer is currently present.
277
+ *
278
+ * @param peerId
279
+ * @returns true if the peer has been seen recently
280
+ */
281
+ has(peerId) {
282
+ return this.#peerStates.has(peerId);
283
+ }
284
+ /**
285
+ * Check when the peer was last seen.
286
+ *
287
+ * @param peerId
288
+ * @returns last seen UNIX timestamp, or undefined for unknown peers
289
+ */
290
+ getLastSeen(peerId) {
291
+ return this.#peersLastSeen.get(peerId);
292
+ }
293
+ /**
294
+ * Get all recently-seen peers.
295
+ *
296
+ * @returns Array of peer ids
297
+ */
298
+ getPeers() {
299
+ return Array.from(this.#peerStates.keys());
300
+ }
301
+ /**
302
+ * Get all recently-seen users.
303
+ *
304
+ * @returns Array of user ids
305
+ */
306
+ getUsers() {
307
+ return Array.from(this.#userPeers.keys());
308
+ }
309
+ /**
310
+ * Get all recently-seen devices.
311
+ *
312
+ * @returns Array of device ids
313
+ */
314
+ getDevices() {
315
+ return Array.from(this.#devicePeers.keys());
316
+ }
317
+ /**
318
+ * Get all recently-seen peers for this user.
319
+ *
320
+ * @param userId
321
+ * @returns Array of peer ids for this user
322
+ */
323
+ getUserPeers(userId) {
324
+ const peers = this.#userPeers.get(userId);
325
+ if (!peers) {
326
+ return;
327
+ }
328
+ return Array.from(peers);
329
+ }
330
+ /**
331
+ * Get all recently-seen peers for this device.
332
+ *
333
+ * @param deviceId
334
+ * @returns Array of peer ids for this device
335
+ */
336
+ getDevicePeers(deviceId) {
337
+ const peers = this.#devicePeers.get(deviceId);
338
+ if (!peers) {
339
+ return;
340
+ }
341
+ return Array.from(peers);
342
+ }
343
+ /**
344
+ * Get most-recently-seen peer from this group.
345
+ *
346
+ * @param peers
347
+ * @returns id of most recently seen peer
348
+ */
349
+ getFreshestPeer(peers) {
350
+ let freshestLastSeen;
351
+ return Array.from(peers).reduce((freshest, curr) => {
352
+ const lastSeen = this.#peersLastSeen.get(curr);
353
+ if (!lastSeen) {
354
+ return freshest;
355
+ }
356
+ if (!freshest || lastSeen > freshestLastSeen) {
357
+ freshestLastSeen = lastSeen;
358
+ return curr;
359
+ }
360
+ return freshest;
361
+ }, undefined);
362
+ }
363
+ /**
364
+ * Get current @type PeerState for given peer.
365
+ *
366
+ * @param peerId
367
+ * @returns details for the peer
368
+ */
369
+ getPeerInfo(peerId) {
370
+ return this.#peerStates.get(peerId);
371
+ }
372
+ /**
373
+ * Get current ephemeral state value for this peer. If a channel is specified,
374
+ * only returns the ephemeral state for that specific channel. Otherwise,
375
+ * returns the full ephemeral state.
376
+ *
377
+ * @param peerId
378
+ * @param channel
379
+ * @returns latest ephemeral state received
380
+ */
381
+ getPeerState(peerId, channel) {
382
+ const fullState = this.#peerStates.get(peerId)?.value;
383
+ if (!channel) {
384
+ return fullState;
385
+ }
386
+ return fullState?.[channel];
387
+ }
388
+ /**
389
+ * Get current ephemeral state value for this user's most-recently-active
390
+ * peer. See {@link getPeerState}.
391
+ *
392
+ * @param userId
393
+ * @param channel
394
+ * @returns
395
+ */
396
+ getUserState(userId, channel) {
397
+ const peers = this.#userPeers.get(userId);
398
+ if (!peers) {
399
+ return undefined;
400
+ }
401
+ const peer = this.getFreshestPeer(peers);
402
+ if (!peer) {
403
+ return undefined;
404
+ }
405
+ return this.getPeerState(peer, channel);
406
+ }
407
+ /**
408
+ * Get current ephemeral state value for this device's most-recently-active
409
+ * peer. See {@link getPeerState}.
410
+ *
411
+ * @param userId
412
+ * @param channel
413
+ * @returns
414
+ */
415
+ getDeviceState(deviceId, channel) {
416
+ const peers = this.#devicePeers.get(deviceId);
417
+ if (!peers) {
418
+ return undefined;
419
+ }
420
+ const peer = this.getFreshestPeer(peers);
421
+ if (!peer) {
422
+ return undefined;
423
+ }
424
+ return this.getPeerState(peer, channel);
425
+ }
426
+ }
427
+ class PeerPresenceInfo extends EventEmitter {
428
+ ttl;
429
+ #peersLastSeen = new Map();
430
+ #peerStates = new Map();
431
+ #userPeers = new Map();
432
+ #devicePeers = new Map();
433
+ view;
434
+ /**
435
+ * Build a new peer presence state.
436
+ *
437
+ * @param ttl in milliseconds - peers with no activity within this timeframe
438
+ * are forgotten when {@link prune} is called.
439
+ */
440
+ constructor(ttl) {
441
+ super();
442
+ this.ttl = ttl;
443
+ this.view = new PeerPresenceView(this.#peersLastSeen, this.#peerStates, this.#userPeers, this.#devicePeers);
444
+ }
445
+ /**
446
+ * Record that we've seen the given peer recently.
447
+ *
448
+ * @param peerId
449
+ * @param deviceId
450
+ * @param userId
451
+ */
452
+ markSeen(peerId, deviceId, userId) {
453
+ const devicePeers = this.#devicePeers.get(deviceId) ?? new Set();
454
+ devicePeers.add(peerId);
455
+ this.#devicePeers.set(deviceId, devicePeers);
456
+ const userPeers = this.#userPeers.get(userId) ?? new Set();
457
+ userPeers.add(peerId);
458
+ this.#userPeers.set(userId, userPeers);
459
+ this.#peersLastSeen.set(peerId, Date.now());
460
+ }
461
+ /**
462
+ * Record a state update for the given peer. It is also automatically updated with {@link markSeen}.
463
+ *
464
+ * @param peerId
465
+ * @param deviceId
466
+ * @param userId
467
+ * @param value
468
+ */
469
+ update({ peerId, deviceId, userId, channel, value, }) {
470
+ this.markSeen(peerId, deviceId, userId);
471
+ const peerState = this.#peerStates.get(peerId);
472
+ const existingState = peerState?.value ?? {};
473
+ this.#peerStates.set(peerId, {
474
+ peerId,
475
+ deviceId,
476
+ userId,
477
+ value: {
478
+ ...existingState,
479
+ [channel]: value,
480
+ },
481
+ });
482
+ }
483
+ /**
484
+ * Forget the given peer.
485
+ *
486
+ * @param peerId
487
+ */
488
+ delete(peerId) {
489
+ this.#peersLastSeen.delete(peerId);
490
+ this.#peerStates.delete(peerId);
491
+ Array.from(this.#devicePeers.entries()).forEach(([deviceId, peerIds]) => {
492
+ if (peerIds.has(peerId)) {
493
+ peerIds.delete(peerId);
494
+ }
495
+ if (peerIds.size === 0) {
496
+ this.#devicePeers.delete(deviceId);
497
+ }
498
+ });
499
+ Array.from(this.#userPeers.entries()).forEach(([userId, peerIds]) => {
500
+ if (peerIds.has(peerId)) {
501
+ peerIds.delete(peerId);
502
+ }
503
+ if (peerIds.size === 0) {
504
+ this.#userPeers.delete(userId);
505
+ }
506
+ });
507
+ }
508
+ /**
509
+ * Prune all peers that have not been seen since the configured ttl has
510
+ * elapsed.
511
+ */
512
+ prune() {
513
+ const threshold = Date.now() - this.ttl;
514
+ const stalePeers = new Set(Array.from(this.#peersLastSeen.entries())
515
+ .filter(([, lastSeen]) => {
516
+ return lastSeen < threshold;
517
+ })
518
+ .map(([peerId]) => peerId));
519
+ if (stalePeers.size === 0) {
520
+ return;
521
+ }
522
+ stalePeers.forEach(stalePeer => {
523
+ this.delete(stalePeer);
524
+ });
525
+ }
526
+ }
package/dist/index.d.ts CHANGED
@@ -28,6 +28,7 @@
28
28
  export { DocHandle } from "./DocHandle.js";
29
29
  export { isValidAutomergeUrl, isValidDocumentId, parseAutomergeUrl, stringifyAutomergeUrl, interpretAsDocumentId, documentIdToBinary, generateAutomergeUrl, encodeHeads, decodeHeads, } from "./AutomergeUrl.js";
30
30
  export { Repo } from "./Repo.js";
31
+ export { Presence, PeerPresenceView, PeerState, PresenceConfig, UserId, DeviceId, } from "./Presence.js";
31
32
  export { NetworkAdapter } from "./network/NetworkAdapter.js";
32
33
  export type { NetworkAdapterInterface } from "./network/NetworkAdapterInterface.js";
33
34
  export { isRepoMessage } from "./network/messages.js";
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
package/dist/index.js CHANGED
@@ -28,6 +28,7 @@
28
28
  export { DocHandle } from "./DocHandle.js";
29
29
  export { isValidAutomergeUrl, isValidDocumentId, parseAutomergeUrl, stringifyAutomergeUrl, interpretAsDocumentId, documentIdToBinary, generateAutomergeUrl, encodeHeads, decodeHeads, } from "./AutomergeUrl.js";
30
30
  export { Repo } from "./Repo.js";
31
+ export { Presence, PeerPresenceView, } from "./Presence.js";
31
32
  export { NetworkAdapter } from "./network/NetworkAdapter.js";
32
33
  export { isRepoMessage } from "./network/messages.js";
33
34
  export { StorageAdapter } from "./storage/StorageAdapter.js";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@automerge/automerge-repo",
3
- "version": "2.5.1",
3
+ "version": "2.5.2-alpha.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>",
@@ -59,5 +59,5 @@
59
59
  "publishConfig": {
60
60
  "access": "public"
61
61
  },
62
- "gitHead": "66b8457234d04468e653b85d5cf58a9a3cd4c691"
62
+ "gitHead": "ba4493efcd7819fe841d3647f28090837792d964"
63
63
  }