@comapeo/core 1.0.1 → 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.
- package/dist/blob-store/index.d.ts +1 -1
- package/dist/config-import.d.ts.map +1 -1
- package/dist/core-manager/core-index.d.ts +1 -1
- package/dist/core-manager/core-index.d.ts.map +1 -1
- package/dist/core-manager/index.d.ts +1 -0
- package/dist/core-manager/index.d.ts.map +1 -1
- package/dist/fastify-controller.d.ts.map +1 -1
- package/dist/fastify-plugins/{maps/index.d.ts → maps.d.ts} +8 -8
- package/dist/fastify-plugins/maps.d.ts.map +1 -0
- package/dist/fastify-plugins/utils.d.ts +4 -0
- package/dist/fastify-plugins/utils.d.ts.map +1 -1
- package/dist/index.d.ts +1 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/lib/hashmap.d.ts +2 -2
- package/dist/lib/hashmap.d.ts.map +1 -1
- package/dist/lib/key-by.d.ts +15 -0
- package/dist/lib/key-by.d.ts.map +1 -0
- package/dist/lib/noise-secret-stream-helpers.d.ts.map +1 -1
- package/dist/local-peers.d.ts +3 -2
- package/dist/local-peers.d.ts.map +1 -1
- package/dist/logger.d.ts +12 -9
- package/dist/logger.d.ts.map +1 -1
- package/dist/mapeo-manager.d.ts +9 -1
- package/dist/mapeo-manager.d.ts.map +1 -1
- package/dist/mapeo-project.d.ts.map +1 -1
- package/dist/member-api.d.ts +3 -1
- package/dist/member-api.d.ts.map +1 -1
- package/dist/roles.d.ts.map +1 -1
- package/dist/schema/utils.d.ts.map +1 -1
- package/dist/sync/core-sync-state.d.ts +10 -3
- package/dist/sync/core-sync-state.d.ts.map +1 -1
- package/dist/sync/namespace-sync-state.d.ts +8 -12
- package/dist/sync/namespace-sync-state.d.ts.map +1 -1
- package/dist/sync/peer-sync-controller.d.ts.map +1 -1
- package/dist/sync/sync-api.d.ts.map +1 -1
- package/dist/sync/sync-state.d.ts +7 -1
- package/dist/sync/sync-state.d.ts.map +1 -1
- package/dist/translation-api.d.ts +1 -3
- package/dist/translation-api.d.ts.map +1 -1
- package/dist/types.d.ts +1 -1
- package/dist/types.d.ts.map +1 -1
- package/dist/utils.d.ts +0 -13
- package/dist/utils.d.ts.map +1 -1
- package/package.json +12 -11
- package/src/core-manager/index.js +13 -10
- package/src/datastore/README.md +2 -2
- package/src/datatype/README.md +1 -1
- package/src/fastify-controller.js +7 -1
- package/src/fastify-plugins/maps.js +122 -0
- package/src/fastify-plugins/utils.js +6 -0
- package/src/index-writer/index.js +1 -1
- package/src/index.js +1 -3
- package/src/lib/hashmap.js +1 -1
- package/src/lib/key-by.js +24 -0
- package/src/local-peers.js +2 -1
- package/src/logger.js +52 -16
- package/src/mapeo-manager.js +36 -2
- package/src/mapeo-project.js +0 -2
- package/src/member-api.js +12 -5
- package/src/sync/core-sync-state.js +35 -7
- package/src/sync/namespace-sync-state.js +26 -24
- package/src/sync/peer-sync-controller.js +44 -37
- package/src/sync/sync-api.js +9 -6
- package/src/sync/sync-state.js +12 -1
- package/src/translation-api.js +1 -4
- package/src/types.ts +0 -1
- package/src/utils.js +0 -25
- package/dist/fastify-plugins/maps/index.d.ts.map +0 -1
- package/dist/fastify-plugins/maps/offline-fallback-map.d.ts +0 -12
- package/dist/fastify-plugins/maps/offline-fallback-map.d.ts.map +0 -1
- package/dist/fastify-plugins/maps/static-maps.d.ts +0 -11
- package/dist/fastify-plugins/maps/static-maps.d.ts.map +0 -1
- package/src/fastify-plugins/maps/index.js +0 -173
- package/src/fastify-plugins/maps/offline-fallback-map.js +0 -114
- 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
|
-
{
|
|
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 =
|
|
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
|
|
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.#
|
|
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.#
|
|
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
|
-
|
|
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.#
|
|
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.#
|
|
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({
|
|
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:
|
|
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
|
|
@@ -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
|
-
|
|
48
|
-
|
|
49
|
-
|
|
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.#
|
|
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.#
|
|
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 = (
|
|
119
|
-
if (!this.#enabledNamespaces.has(namespace)) return
|
|
120
|
-
this.#
|
|
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 {
|
|
234
|
+
* @param {CoreRecord} coreRecord
|
|
239
235
|
*/
|
|
240
|
-
#
|
|
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 {
|
|
260
|
+
* @param {CoreRecord} coreRecord
|
|
254
261
|
* @returns {Promise<void>}
|
|
255
262
|
*/
|
|
256
|
-
async #
|
|
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
|
|
278
|
-
this.#
|
|
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
|
|
290
|
-
this.#
|
|
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)
|
package/src/sync/sync-api.js
CHANGED
|
@@ -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.#
|
|
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
|
-
|
|
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
|
-
#
|
|
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
|
|
package/src/sync/sync-state.js
CHANGED
|
@@ -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
|
*/
|
package/src/translation-api.js
CHANGED
|
@@ -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
|
|
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
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"}
|