@comapeo/core 1.0.0 → 2.0.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.
Files changed (75) hide show
  1. package/dist/blob-store/index.d.ts +1 -1
  2. package/dist/config-import.d.ts.map +1 -1
  3. package/dist/core-manager/core-index.d.ts +1 -1
  4. package/dist/core-manager/core-index.d.ts.map +1 -1
  5. package/dist/core-manager/index.d.ts +1 -0
  6. package/dist/core-manager/index.d.ts.map +1 -1
  7. package/dist/fastify-controller.d.ts.map +1 -1
  8. package/dist/fastify-plugins/{maps/index.d.ts → maps.d.ts} +8 -8
  9. package/dist/fastify-plugins/maps.d.ts.map +1 -0
  10. package/dist/fastify-plugins/utils.d.ts +4 -0
  11. package/dist/fastify-plugins/utils.d.ts.map +1 -1
  12. package/dist/index.d.ts +1 -3
  13. package/dist/index.d.ts.map +1 -1
  14. package/dist/lib/hashmap.d.ts +2 -2
  15. package/dist/lib/hashmap.d.ts.map +1 -1
  16. package/dist/lib/key-by.d.ts +15 -0
  17. package/dist/lib/key-by.d.ts.map +1 -0
  18. package/dist/lib/noise-secret-stream-helpers.d.ts.map +1 -1
  19. package/dist/local-peers.d.ts +3 -2
  20. package/dist/local-peers.d.ts.map +1 -1
  21. package/dist/logger.d.ts +12 -9
  22. package/dist/logger.d.ts.map +1 -1
  23. package/dist/mapeo-manager.d.ts +9 -1
  24. package/dist/mapeo-manager.d.ts.map +1 -1
  25. package/dist/mapeo-project.d.ts.map +1 -1
  26. package/dist/member-api.d.ts +3 -1
  27. package/dist/member-api.d.ts.map +1 -1
  28. package/dist/roles.d.ts.map +1 -1
  29. package/dist/schema/utils.d.ts.map +1 -1
  30. package/dist/sync/core-sync-state.d.ts +10 -3
  31. package/dist/sync/core-sync-state.d.ts.map +1 -1
  32. package/dist/sync/namespace-sync-state.d.ts +8 -12
  33. package/dist/sync/namespace-sync-state.d.ts.map +1 -1
  34. package/dist/sync/peer-sync-controller.d.ts.map +1 -1
  35. package/dist/sync/sync-api.d.ts.map +1 -1
  36. package/dist/sync/sync-state.d.ts +7 -1
  37. package/dist/sync/sync-state.d.ts.map +1 -1
  38. package/dist/translation-api.d.ts +1 -3
  39. package/dist/translation-api.d.ts.map +1 -1
  40. package/dist/types.d.ts +1 -1
  41. package/dist/types.d.ts.map +1 -1
  42. package/dist/utils.d.ts +0 -13
  43. package/dist/utils.d.ts.map +1 -1
  44. package/package.json +14 -13
  45. package/src/core-manager/index.js +13 -10
  46. package/src/datastore/README.md +2 -2
  47. package/src/datatype/README.md +1 -1
  48. package/src/fastify-controller.js +7 -1
  49. package/src/fastify-plugins/maps.js +122 -0
  50. package/src/fastify-plugins/utils.js +6 -0
  51. package/src/index-writer/index.js +1 -1
  52. package/src/index.js +1 -3
  53. package/src/lib/hashmap.js +1 -1
  54. package/src/lib/key-by.js +24 -0
  55. package/src/local-peers.js +2 -1
  56. package/src/logger.js +52 -16
  57. package/src/mapeo-manager.js +36 -2
  58. package/src/mapeo-project.js +0 -2
  59. package/src/member-api.js +12 -5
  60. package/src/sync/core-sync-state.js +35 -7
  61. package/src/sync/namespace-sync-state.js +27 -25
  62. package/src/sync/peer-sync-controller.js +44 -37
  63. package/src/sync/sync-api.js +9 -6
  64. package/src/sync/sync-state.js +12 -1
  65. package/src/translation-api.js +1 -4
  66. package/src/types.ts +0 -1
  67. package/src/utils.js +0 -25
  68. package/dist/fastify-plugins/maps/index.d.ts.map +0 -1
  69. package/dist/fastify-plugins/maps/offline-fallback-map.d.ts +0 -12
  70. package/dist/fastify-plugins/maps/offline-fallback-map.d.ts.map +0 -1
  71. package/dist/fastify-plugins/maps/static-maps.d.ts +0 -11
  72. package/dist/fastify-plugins/maps/static-maps.d.ts.map +0 -1
  73. package/src/fastify-plugins/maps/index.js +0 -173
  74. package/src/fastify-plugins/maps/offline-fallback-map.js +0 -114
  75. package/src/fastify-plugins/maps/static-maps.js +0 -271
package/src/member-api.js CHANGED
@@ -9,6 +9,7 @@ import {
9
9
  projectKeyToId,
10
10
  projectKeyToProjectInviteId,
11
11
  } from './utils.js'
12
+ import { keyBy } from './lib/key-by.js'
12
13
  import { abortSignalAny } from './lib/ponyfills.js'
13
14
  import timingSafeEqual from './lib/timing-safe-equal.js'
14
15
  import { ROLES, isRoleIdForNewInvite } from './roles.js'
@@ -89,6 +90,7 @@ export class MemberApi extends TypedEmitter {
89
90
  * @param {import('./roles.js').RoleIdForNewInvite} opts.roleId
90
91
  * @param {string} [opts.roleName]
91
92
  * @param {string} [opts.roleDescription]
93
+ * @param {Buffer} [opts.__testOnlyInviteId] Hard-code the invite ID. Only for tests.
92
94
  * @returns {Promise<(
93
95
  * typeof InviteResponse_Decision.ACCEPT |
94
96
  * typeof InviteResponse_Decision.REJECT |
@@ -97,7 +99,12 @@ export class MemberApi extends TypedEmitter {
97
99
  */
98
100
  async invite(
99
101
  deviceId,
100
- { roleId, roleName = ROLES[roleId]?.name, roleDescription }
102
+ {
103
+ roleId,
104
+ roleName = ROLES[roleId]?.name,
105
+ roleDescription,
106
+ __testOnlyInviteId,
107
+ }
101
108
  ) {
102
109
  assert(isRoleIdForNewInvite(roleId), 'Invalid role ID for new invite')
103
110
  assert(
@@ -120,7 +127,7 @@ export class MemberApi extends TypedEmitter {
120
127
 
121
128
  abortSignal.throwIfAborted()
122
129
 
123
- const inviteId = crypto.randomBytes(32)
130
+ const inviteId = __testOnlyInviteId || crypto.randomBytes(32)
124
131
  const projectId = projectKeyToId(this.#projectKey)
125
132
  const projectInviteId = projectKeyToProjectInviteId(this.#projectKey)
126
133
  const project = await this.#dataTypes.project.getByDocId(projectId)
@@ -279,6 +286,8 @@ export class MemberApi extends TypedEmitter {
279
286
  this.#dataTypes.deviceInfo.getMany(),
280
287
  ])
281
288
 
289
+ const deviceInfoByConfigCoreId = keyBy(allDeviceInfo, ({ docId }) => docId)
290
+
282
291
  return Promise.all(
283
292
  [...allRoles.entries()].map(async ([deviceId, role]) => {
284
293
  /** @type {MemberInfo} */
@@ -290,9 +299,7 @@ export class MemberApi extends TypedEmitter {
290
299
  'config'
291
300
  )
292
301
 
293
- const deviceInfo = allDeviceInfo.find(
294
- ({ docId }) => docId === configCoreId
295
- )
302
+ const deviceInfo = deviceInfoByConfigCoreId.get(configCoreId)
296
303
 
297
304
  memberInfo.name = deviceInfo?.name
298
305
  memberInfo.deviceType = deviceInfo?.deviceType
@@ -2,6 +2,7 @@ import { keyToId } from '../utils.js'
2
2
  import RemoteBitfield, {
3
3
  BITS_PER_PAGE,
4
4
  } from '../core-manager/remote-bitfield.js'
5
+ import { Logger } from '../logger.js'
5
6
  /** @import { HypercorePeer, HypercoreRemoteBitfield, Namespace } from '../types.js' */
6
7
 
7
8
  /**
@@ -22,7 +23,7 @@ import RemoteBitfield, {
22
23
  * @typedef {object} LocalCoreState
23
24
  * @property {number} have blocks we have
24
25
  * @property {number} want unique blocks we want from any other peer
25
- * @property {number} wanted blocks we want from this peer
26
+ * @property {number} wanted unique blocks any other peer wants from us
26
27
  */
27
28
  /**
28
29
  * @typedef {object} PeerNamespaceState
@@ -71,14 +72,18 @@ export class CoreSyncState {
71
72
  #update
72
73
  #peerSyncControllers
73
74
  #namespace
75
+ #l
74
76
 
75
77
  /**
76
78
  * @param {object} opts
77
79
  * @param {() => void} opts.onUpdate Called when a state update is available (via getState())
78
80
  * @param {Map<string, import('./peer-sync-controller.js').PeerSyncController>} opts.peerSyncControllers
79
81
  * @param {Namespace} opts.namespace
82
+ * @param {Logger} [opts.logger]
80
83
  */
81
- constructor({ onUpdate, peerSyncControllers, namespace }) {
84
+ constructor({ onUpdate, peerSyncControllers, namespace, logger }) {
85
+ // The logger parameter is already namespaced by NamespaceSyncState
86
+ this.#l = logger || Logger.create('css')
82
87
  this.#peerSyncControllers = peerSyncControllers
83
88
  this.#namespace = namespace
84
89
  // Called whenever the state changes, so we clear the cache because next
@@ -150,12 +155,24 @@ export class CoreSyncState {
150
155
  * @param {Uint32Array} bitfield
151
156
  */
152
157
  insertPreHaves(peerId, start, bitfield) {
153
- const peerState = this.#getPeerState(peerId)
158
+ const peerState = this.#getOrCreatePeerState(peerId)
154
159
  peerState.insertPreHaves(start, bitfield)
160
+ const previousLength = Math.max(
161
+ this.#preHavesLength,
162
+ this.#core?.length || 0
163
+ )
155
164
  this.#preHavesLength = Math.max(
156
165
  this.#preHavesLength,
157
166
  peerState.preHavesBitfield.lastSet(start + bitfield.length * 32) + 1
158
167
  )
168
+ if (this.#preHavesLength > previousLength) {
169
+ this.#l.log(
170
+ 'Updated peer %S pre-haves length from %d to %d',
171
+ peerId,
172
+ previousLength,
173
+ this.#preHavesLength
174
+ )
175
+ }
159
176
  this.#update()
160
177
  }
161
178
 
@@ -168,7 +185,7 @@ export class CoreSyncState {
168
185
  * @param {Array<{ start: number, length: number }>} ranges
169
186
  */
170
187
  setPeerWants(peerId, ranges) {
171
- const peerState = this.#getPeerState(peerId)
188
+ const peerState = this.#getOrCreatePeerState(peerId)
172
189
  for (const { start, length } of ranges) {
173
190
  peerState.setWantRange({ start, length })
174
191
  }
@@ -187,7 +204,17 @@ export class CoreSyncState {
187
204
  /**
188
205
  * @param {PeerId} peerId
189
206
  */
190
- #getPeerState(peerId) {
207
+ disconnectPeer(peerId) {
208
+ const wasRemoved = this.#remoteStates.delete(peerId)
209
+ if (wasRemoved) {
210
+ this.#update()
211
+ }
212
+ }
213
+
214
+ /**
215
+ * @param {PeerId} peerId
216
+ */
217
+ #getOrCreatePeerState(peerId) {
191
218
  let peerState = this.#remoteStates.get(peerId)
192
219
  if (!peerState) {
193
220
  peerState = new PeerState()
@@ -207,7 +234,7 @@ export class CoreSyncState {
207
234
  const peerId = keyToId(peer.remotePublicKey)
208
235
 
209
236
  // Update state to ensure this peer is in the state correctly
210
- const peerState = this.#getPeerState(peerId)
237
+ const peerState = this.#getOrCreatePeerState(peerId)
211
238
  peerState.status = 'starting'
212
239
 
213
240
  this.#core?.update({ wait: true }).then(() => {
@@ -243,7 +270,8 @@ export class CoreSyncState {
243
270
  */
244
271
  #onPeerRemove = (peer) => {
245
272
  const peerId = keyToId(peer.remotePublicKey)
246
- const peerState = this.#getPeerState(peerId)
273
+ const peerState = this.#remoteStates.get(peerId)
274
+ if (!peerState) return
247
275
  peerState.status = 'stopped'
248
276
  this.#update()
249
277
  }
@@ -1,3 +1,4 @@
1
+ import { Logger } from '../logger.js'
1
2
  import { CoreSyncState } from './core-sync-state.js'
2
3
  import { discoveryKey } from 'hypercore-crypto'
3
4
  /** @import { Namespace } from '../types.js' */
@@ -18,6 +19,7 @@ export class NamespaceSyncState {
18
19
  /** @type {SyncState | null} */
19
20
  #cachedState = null
20
21
  #peerSyncControllers
22
+ #logger
21
23
 
22
24
  /**
23
25
  * @param {object} opts
@@ -25,8 +27,17 @@ export class NamespaceSyncState {
25
27
  * @param {import('../core-manager/index.js').CoreManager} opts.coreManager
26
28
  * @param {() => void} opts.onUpdate Called when a state update is available (via getState())
27
29
  * @param {Map<string, import('./peer-sync-controller.js').PeerSyncController>} opts.peerSyncControllers
30
+ * @param {Logger} [opts.logger]
28
31
  */
29
- constructor({ namespace, coreManager, onUpdate, peerSyncControllers }) {
32
+ constructor({
33
+ namespace,
34
+ coreManager,
35
+ onUpdate,
36
+ peerSyncControllers,
37
+ logger,
38
+ }) {
39
+ // Currently we don't create a logger for this class, just pass it down
40
+ this.#logger = logger
30
41
  this.#namespace = namespace
31
42
  this.#peerSyncControllers = peerSyncControllers
32
43
  // Called whenever the state changes, so we clear the cache because next
@@ -62,7 +73,7 @@ export class NamespaceSyncState {
62
73
  const state = {
63
74
  dataToSync: false,
64
75
  coreCount: this.#coreCount,
65
- localState: createState(),
76
+ localState: { want: 0, have: 0, wanted: 0 },
66
77
  remoteStates: {},
67
78
  }
68
79
  for (const css of this.#coreStates.values()) {
@@ -94,6 +105,15 @@ export class NamespaceSyncState {
94
105
  }
95
106
  }
96
107
 
108
+ /**
109
+ * @param {string} peerId
110
+ */
111
+ disconnectPeer(peerId) {
112
+ for (const css of this.#coreStates.values()) {
113
+ css.disconnectPeer(peerId)
114
+ }
115
+ }
116
+
97
117
  /**
98
118
  * @param {import('hypercore')<"binary", Buffer>} core
99
119
  * @param {Buffer} coreKey
@@ -122,10 +142,14 @@ export class NamespaceSyncState {
122
142
  #getCoreState(discoveryId) {
123
143
  let coreState = this.#coreStates.get(discoveryId)
124
144
  if (!coreState) {
145
+ const logPrefix = `[${discoveryId.slice(0, 7)}] `
125
146
  coreState = new CoreSyncState({
126
147
  onUpdate: this.#handleUpdate,
127
148
  peerSyncControllers: this.#peerSyncControllers,
128
149
  namespace: this.#namespace,
150
+ logger: Logger.create('css:' + this.#namespace, this.#logger, {
151
+ prefix: logPrefix,
152
+ }),
129
153
  })
130
154
  this.#coreStates.set(discoveryId, coreState)
131
155
  }
@@ -133,28 +157,6 @@ export class NamespaceSyncState {
133
157
  }
134
158
  }
135
159
 
136
- /**
137
- * @overload
138
- * @returns {SyncState['localState']}
139
- */
140
-
141
- /**
142
- * @overload
143
- * @param {import('./core-sync-state.js').PeerNamespaceState['status']} status
144
- * @returns {import('./core-sync-state.js').PeerNamespaceState}
145
- */
146
-
147
- /**
148
- * @param {import('./core-sync-state.js').PeerNamespaceState['status']} [status]
149
- */
150
- export function createState(status) {
151
- if (status) {
152
- return { want: 0, have: 0, wanted: 0, status }
153
- } else {
154
- return { want: 0, have: 0, wanted: 0 }
155
- }
156
- }
157
-
158
160
  /**
159
161
  * @overload
160
162
  * @param {SyncState['localState']} accumulator
@@ -184,7 +186,7 @@ function mutatingAddPeerState(accumulator, currentValue) {
184
186
  accumulator.status === 'stopped'
185
187
  } else if (
186
188
  currentValue.status === 'starting' &&
187
- accumulator.status === 'starting'
189
+ accumulator.status === 'started'
188
190
  ) {
189
191
  accumulator.status = 'starting'
190
192
  }
@@ -32,6 +32,7 @@ export class PeerSyncController {
32
32
  #downloadingRanges = new Map()
33
33
  /** @type {SyncStatus} */
34
34
  #prevSyncStatus = createNamespaceMap('unknown')
35
+ /** @type {import('debug').Debugger} */
35
36
  #log
36
37
 
37
38
  /**
@@ -43,25 +44,16 @@ export class PeerSyncController {
43
44
  * @param {Logger} [opts.logger]
44
45
  */
45
46
  constructor({ protomux, coreManager, syncState, roles, logger }) {
46
- /**
47
- * @param {string} formatter
48
- * @param {unknown[]} args
49
- * @returns {void}
50
- */
51
- this.#log = (formatter, ...args) => {
52
- const log = Logger.create('peer', logger).log
53
- return log.apply(null, [
54
- `[%h] ${formatter}`,
55
- protomux.stream.remotePublicKey,
56
- ...args,
57
- ])
58
- }
47
+ const logPrefix = `[${protomux.stream.remotePublicKey
48
+ ?.toString('hex')
49
+ .slice(0, 7)}] `
50
+ this.#log = Logger.create('peer', logger, { prefix: logPrefix }).log
59
51
  this.#coreManager = coreManager
60
52
  this.#protomux = protomux
61
53
  this.#roles = roles
62
54
 
63
55
  // Always need to replicate the project creator core
64
- this.#replicateCore(coreManager.creatorCore)
56
+ this.#replicateCoreRecord(coreManager.creatorCoreRecord)
65
57
 
66
58
  coreManager.on('add-core', this.#handleAddCore)
67
59
  syncState.on('state', this.#handleStateChange)
@@ -98,14 +90,18 @@ export class PeerSyncController {
98
90
  // If we already know about this core, then we will add it to the
99
91
  // replication stream when we are ready
100
92
  if (coreRecord) {
101
- this.#log(
102
- 'Received discovery key %h, but already have core in namespace %s',
103
- discoveryKey,
104
- coreRecord.namespace
105
- )
106
93
  if (this.#enabledNamespaces.has(coreRecord.namespace)) {
107
- this.#replicateCore(coreRecord.core)
94
+ this.#replicateCoreRecord(coreRecord)
95
+ } else {
96
+ this.#log(
97
+ 'Received discovery key %h, for core %h, but namespace %s is disabled',
98
+ discoveryKey,
99
+ coreRecord.key,
100
+ coreRecord.namespace
101
+ )
108
102
  }
103
+ } else {
104
+ this.#log('Received unknown discovery key %h', discoveryKey)
109
105
  }
110
106
  }
111
107
 
@@ -115,9 +111,9 @@ export class PeerSyncController {
115
111
  *
116
112
  * @param {CoreRecord} coreRecord
117
113
  */
118
- #handleAddCore = ({ core, namespace }) => {
119
- if (!this.#enabledNamespaces.has(namespace)) return
120
- this.#replicateCore(core)
114
+ #handleAddCore = (coreRecord) => {
115
+ if (!this.#enabledNamespaces.has(coreRecord.namespace)) return
116
+ this.#replicateCoreRecord(coreRecord)
121
117
  }
122
118
 
123
119
  /**
@@ -235,25 +231,36 @@ export class PeerSyncController {
235
231
  }
236
232
 
237
233
  /**
238
- * @param {import('hypercore')<'binary', any>} core
234
+ * @param {CoreRecord} coreRecord
239
235
  */
240
- #replicateCore(core) {
236
+ #replicateCoreRecord({ core, namespace }) {
241
237
  if (core.closed) return
242
238
  if (this.#replicatingCores.has(core)) return
243
- this.#log('replicating core %k', core.key)
239
+ this.#log('replicating %s core %k', namespace, core.key)
244
240
  core.replicate(this.#protomux)
245
- core.on('peer-remove', (peer) => {
246
- if (!peer.remotePublicKey.equals(this.peerKey)) return
247
- this.#log('peer-remove %h from core %k', peer.remotePublicKey, core.key)
248
- })
249
241
  this.#replicatingCores.add(core)
242
+
243
+ if (!this.#log.enabled) return
244
+
245
+ /** @type {(peer: any) => void} */
246
+ const handlePeerRemove = (peer) => {
247
+ if (!peer.remotePublicKey.equals(this.peerKey)) return
248
+ core.off('peer-remove', handlePeerRemove)
249
+ this.#log(
250
+ 'peer-remove %h from %s core %k',
251
+ peer.remotePublicKey,
252
+ namespace,
253
+ core.key
254
+ )
255
+ }
256
+ core.on('peer-remove', handlePeerRemove)
250
257
  }
251
258
 
252
259
  /**
253
- * @param {import('hypercore')<'binary', any>} core
260
+ * @param {CoreRecord} coreRecord
254
261
  * @returns {Promise<void>}
255
262
  */
256
- async #unreplicateCore(core) {
263
+ async #unreplicateCoreRecord({ core, namespace }) {
257
264
  if (core === this.#coreManager.creatorCore) return
258
265
 
259
266
  this.#replicatingCores.delete(core)
@@ -266,7 +273,7 @@ export class PeerSyncController {
266
273
  }
267
274
 
268
275
  unreplicate(core, this.#protomux)
269
- this.#log('unreplicated core %k', core.key)
276
+ this.#log('unreplicated %s core %k', namespace, core.key)
270
277
  }
271
278
 
272
279
  /**
@@ -274,8 +281,8 @@ export class PeerSyncController {
274
281
  */
275
282
  #enableNamespace(namespace) {
276
283
  if (this.#enabledNamespaces.has(namespace)) return
277
- for (const { core } of this.#coreManager.getCores(namespace)) {
278
- this.#replicateCore(core)
284
+ for (const coreRecord of this.#coreManager.getCores(namespace)) {
285
+ this.#replicateCoreRecord(coreRecord)
279
286
  }
280
287
  this.#enabledNamespaces.add(namespace)
281
288
  this.#log('enabled namespace %s', namespace)
@@ -286,8 +293,8 @@ export class PeerSyncController {
286
293
  */
287
294
  #disableNamespace(namespace) {
288
295
  if (!this.#enabledNamespaces.has(namespace)) return
289
- for (const { core } of this.#coreManager.getCores(namespace)) {
290
- this.#unreplicateCore(core)
296
+ for (const coreRecord of this.#coreManager.getCores(namespace)) {
297
+ this.#unreplicateCoreRecord(coreRecord)
291
298
  }
292
299
  this.#enabledNamespaces.delete(namespace)
293
300
  this.#log('disabled namespace %s', namespace)
@@ -97,6 +97,7 @@ export class SyncApi extends TypedEmitter {
97
97
  coreManager,
98
98
  throttleMs,
99
99
  peerSyncControllers: this.#pscByPeerId,
100
+ logger,
100
101
  })
101
102
  this[kSyncState].setMaxListeners(0)
102
103
  this[kSyncState].on('state', (namespaceSyncState) => {
@@ -104,7 +105,7 @@ export class SyncApi extends TypedEmitter {
104
105
  })
105
106
 
106
107
  this.#coreManager.creatorCore.on('peer-add', this.#handlePeerAdd)
107
- this.#coreManager.creatorCore.on('peer-remove', this.#handlePeerRemove)
108
+ this.#coreManager.creatorCore.on('peer-remove', this.#handlePeerDisconnect)
108
109
 
109
110
  roles.on('update', this.#handleRoleUpdate)
110
111
  coreOwnership.on('update', this.#handleCoreOwnershipUpdate)
@@ -261,7 +262,9 @@ export class SyncApi extends TypedEmitter {
261
262
  syncEnabledState = 'presync'
262
263
  }
263
264
 
264
- this.#l.log(`Setting sync enabled state to "${syncEnabledState}"`)
265
+ if (syncEnabledState !== this.#previousSyncEnabledState) {
266
+ this.#l.log(`Setting sync enabled state to "${syncEnabledState}"`)
267
+ }
265
268
  for (const peerSyncController of this.#peerSyncControllers.values()) {
266
269
  peerSyncController.setSyncEnabledState(syncEnabledState)
267
270
  }
@@ -403,7 +406,7 @@ export class SyncApi extends TypedEmitter {
403
406
  *
404
407
  * @param {{ protomux: import('protomux')<import('@hyperswarm/secret-stream')>, remotePublicKey: Buffer }} peer
405
408
  */
406
- #handlePeerRemove = (peer) => {
409
+ #handlePeerDisconnect = (peer) => {
407
410
  const { protomux } = peer
408
411
  if (!this.#peerSyncControllers.has(protomux)) {
409
412
  this.#l.log(
@@ -416,6 +419,8 @@ export class SyncApi extends TypedEmitter {
416
419
  const peerId = keyToId(peer.remotePublicKey)
417
420
  this.#pscByPeerId.delete(peerId)
418
421
  this.#pendingDiscoveryKeys.delete(protomux)
422
+ this[kSyncState].disconnectPeer(peerId)
423
+ this.#updateState()
419
424
  }
420
425
 
421
426
  /**
@@ -435,7 +440,6 @@ export class SyncApi extends TypedEmitter {
435
440
  for (const result of ownershipResults) {
436
441
  if (result.status === 'rejected') continue
437
442
  await this.#validateRoleAndAddCoresForPeer(result.value)
438
- this.#l.log('Added cores for device %S', result.value.docId)
439
443
  }
440
444
  }
441
445
 
@@ -457,10 +461,8 @@ export class SyncApi extends TypedEmitter {
457
461
  coreOwnershipDocId
458
462
  )
459
463
  await this.#validateRoleAndAddCoresForPeer(coreOwnershipDoc)
460
- this.#l.log('Added cores for device %S', coreOwnershipDocId)
461
464
  } catch (_) {
462
465
  // Ignore, we'll add these when the role is added
463
- this.#l.log('No role for device %S', coreOwnershipDocId)
464
466
  }
465
467
  })()
466
468
  )
@@ -485,6 +487,7 @@ export class SyncApi extends TypedEmitter {
485
487
  const coreKey = Buffer.from(coreOwnership[`${ns}CoreId`], 'hex')
486
488
  this.#coreManager.addCore(coreKey, ns)
487
489
  }
490
+ this.#l.log('Added non-auth cores for peer %S', peerDeviceId)
488
491
  }
489
492
  }
490
493
 
@@ -24,8 +24,9 @@ export class SyncState extends TypedEmitter {
24
24
  * @param {import('../core-manager/index.js').CoreManager} opts.coreManager
25
25
  * @param {Map<string, import('./peer-sync-controller.js').PeerSyncController>} opts.peerSyncControllers
26
26
  * @param {number} [opts.throttleMs]
27
+ * @param {import('../logger.js').Logger} [opts.logger]
27
28
  */
28
- constructor({ coreManager, peerSyncControllers, throttleMs = 200 }) {
29
+ constructor({ coreManager, peerSyncControllers, throttleMs = 200, logger }) {
29
30
  super()
30
31
  const throttledHandleUpdate = throttle(throttleMs, this.#handleUpdate)
31
32
  for (const namespace of NAMESPACES) {
@@ -34,6 +35,7 @@ export class SyncState extends TypedEmitter {
34
35
  coreManager,
35
36
  onUpdate: throttledHandleUpdate,
36
37
  peerSyncControllers,
38
+ logger,
37
39
  })
38
40
  }
39
41
  }
@@ -47,6 +49,15 @@ export class SyncState extends TypedEmitter {
47
49
  }
48
50
  }
49
51
 
52
+ /**
53
+ * @param {string} peerId
54
+ */
55
+ disconnectPeer(peerId) {
56
+ for (const nss of Object.values(this.#syncStates)) {
57
+ nss.disconnectPeer(peerId)
58
+ }
59
+ }
60
+
50
61
  /**
51
62
  * @returns {State}
52
63
  */
@@ -14,7 +14,6 @@ export default class TranslationApi {
14
14
  * Set<import('@comapeo/schema/dist/types.js').SchemaName>>} */
15
15
  #translatedLanguageCodeToSchemaNames = new Map()
16
16
  #dataType
17
- #table
18
17
  #indexPromise
19
18
 
20
19
  /**
@@ -26,11 +25,9 @@ export default class TranslationApi {
26
25
  * Translation,
27
26
  * TranslationValue
28
27
  * >} opts.dataType
29
- * @param {typeof import('./schema/project.js').translationTable} opts.table
30
28
  */
31
- constructor({ dataType, table }) {
29
+ constructor({ dataType }) {
32
30
  this.#dataType = dataType
33
- this.#table = table
34
31
  this.#indexPromise = this.#dataType
35
32
  .getMany()
36
33
  .then((docs) => {
package/src/types.ts CHANGED
@@ -133,7 +133,6 @@ export type HypercorePeer = {
133
133
  onrange: (options: { drop: boolean; start: number; length: number }) => void
134
134
  }
135
135
 
136
- export { NoiseStream }
137
136
  type ProtocolStream = Omit<NoiseStream, 'userData'> & {
138
137
  userData: Protomux
139
138
  }
package/src/utils.js CHANGED
@@ -1,4 +1,3 @@
1
- import b4a from 'b4a'
2
1
  import sodium from 'sodium-universal'
3
2
  import { keyToPublicId } from '@mapeo/crypto'
4
3
  import { createHash } from 'node:crypto'
@@ -6,18 +5,6 @@ import stableStringify from 'json-stable-stringify'
6
5
 
7
6
  const PROJECT_INVITE_ID_SALT = Buffer.from('mapeo project invite id', 'ascii')
8
7
 
9
- /**
10
- * @param {String|Buffer} id
11
- * @returns {Buffer | Uint8Array}
12
- */
13
- export function idToKey(id) {
14
- if (b4a.isBuffer(id)) {
15
- return /** @type {Buffer} */ (id)
16
- }
17
-
18
- return b4a.from(/** @type {String} */ (id), 'hex')
19
- }
20
-
21
8
  /**
22
9
  *
23
10
  * @param {Buffer|String} key
@@ -31,18 +18,6 @@ export function keyToId(key) {
31
18
  return key.toString('hex')
32
19
  }
33
20
 
34
- /**
35
- * @param {String} version
36
- * @returns {{coreId: String, blockIndex: Number}}
37
- */
38
- export function parseVersion(version) {
39
- const [coreId, blockIndex] = version.split('@')
40
- return {
41
- coreId,
42
- blockIndex: Number(blockIndex),
43
- }
44
- }
45
-
46
21
  export class ExhaustivenessError extends Error {
47
22
  /** @param {never} value */
48
23
  constructor(value) {
@@ -1 +0,0 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/fastify-plugins/maps/index.js"],"names":[],"mappings":"AAYA,uCAAuC;AACvC,8FACwD;;;;;;;qBA8B1C,MAAM,OAAO,CAAC,MAAM,CAAC"}
@@ -1,12 +0,0 @@
1
- export const PLUGIN_NAME: "mapeo-static-maps";
2
- export function plugin(instance: import("fastify").FastifyInstance<import("fastify").RawServerDefault, import("http").IncomingMessage, import("http").ServerResponse<import("http").IncomingMessage>, import("fastify").FastifyBaseLogger, import("fastify").FastifyTypeProviderDefault>, opts: OfflineFallbackMapPluginOpts): Promise<void>;
3
- export type OfflineFallbackMapPluginOpts = {
4
- prefix?: string | undefined;
5
- styleJsonPath: string;
6
- sourcesDir: string;
7
- };
8
- export type FallbackMapPluginDecorator = {
9
- getResolvedStyleJson: (serverAddress: string) => Promise<any>;
10
- getStyleJsonStats: () => Promise<import("node:fs").Stats>;
11
- };
12
- //# sourceMappingURL=offline-fallback-map.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"offline-fallback-map.d.ts","sourceRoot":"","sources":["../../../src/fastify-plugins/maps/offline-fallback-map.js"],"names":[],"mappings":"AAWA,8CAA8C;;;;mBAUhC,MAAM;gBACN,MAAM;;;0BAKN,CAAC,aAAa,EAAE,MAAM,KAAK,OAAO,CAAC,GAAG,CAAC;uBACvC,MAAM,OAAO,CAAC,OAAO,SAAS,EAAE,KAAK,CAAC"}
@@ -1,11 +0,0 @@
1
- export const PLUGIN_NAME: "mapeo-static-maps";
2
- export function plugin(instance: import("fastify").FastifyInstance<import("fastify").RawServerDefault, import("http").IncomingMessage, import("http").ServerResponse<import("http").IncomingMessage>, import("fastify").FastifyBaseLogger, import("fastify").FastifyTypeProviderDefault>, opts: StaticMapsPluginOpts): Promise<void>;
3
- export type StaticMapsPluginOpts = {
4
- prefix?: string | undefined;
5
- staticRootDir: string;
6
- };
7
- export type StaticMapsPluginDecorator = {
8
- getResolvedStyleJson: (styleId: string, serverAddress: string) => Promise<string>;
9
- getStyleJsonStats: (styleId: string) => Promise<import("node:fs").Stats>;
10
- };
11
- //# sourceMappingURL=static-maps.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"static-maps.d.ts","sourceRoot":"","sources":["../../../src/fastify-plugins/maps/static-maps.js"],"names":[],"mappings":"AAeA,8CAA8C;;;;mBAUhC,MAAM;;;0BAKN,CAAC,OAAO,EAAE,MAAM,EAAE,aAAa,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,CAAC;uBAC3D,CAAC,OAAO,EAAE,MAAM,KAAK,OAAO,CAAC,OAAO,SAAS,EAAE,KAAK,CAAC"}