@comapeo/core 2.3.1 → 3.0.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/entries-stream.d.ts.map +1 -1
- package/dist/blob-store/index.d.ts +27 -15
- package/dist/blob-store/index.d.ts.map +1 -1
- package/dist/blob-store/live-download.d.ts +107 -0
- package/dist/blob-store/live-download.d.ts.map +1 -0
- package/dist/capabilities.d.ts +121 -0
- package/dist/capabilities.d.ts.map +1 -0
- package/dist/core-manager/compat.d.ts +4 -0
- package/dist/core-manager/compat.d.ts.map +1 -0
- package/dist/core-manager/index.d.ts +4 -4
- package/dist/core-manager/index.d.ts.map +1 -1
- package/dist/discovery/dns-sd.d.ts +54 -0
- package/dist/discovery/dns-sd.d.ts.map +1 -0
- package/dist/errors.d.ts +16 -0
- package/dist/errors.d.ts.map +1 -1
- package/dist/fastify-plugins/maps/index.d.ts +11 -0
- package/dist/fastify-plugins/maps/index.d.ts.map +1 -0
- package/dist/fastify-plugins/maps/offline-fallback-map.d.ts +12 -0
- package/dist/fastify-plugins/maps/offline-fallback-map.d.ts.map +1 -0
- package/dist/fastify-plugins/maps/static-maps.d.ts +11 -0
- package/dist/fastify-plugins/maps/static-maps.d.ts.map +1 -0
- package/dist/generated/extensions.d.ts +7 -0
- package/dist/generated/extensions.d.ts.map +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/invite/invite-api.d.ts +112 -0
- package/dist/invite/invite-api.d.ts.map +1 -0
- package/dist/invite/invite-state-machine.d.ts +510 -0
- package/dist/invite/invite-state-machine.d.ts.map +1 -0
- package/dist/lib/timing-safe-equal.d.ts +15 -0
- package/dist/lib/timing-safe-equal.d.ts.map +1 -0
- package/dist/local-peers.d.ts.map +1 -1
- package/dist/mapeo-manager.d.ts +1 -1
- package/dist/mapeo-manager.d.ts.map +1 -1
- package/dist/mapeo-project.d.ts.map +1 -1
- package/dist/media-server.d.ts +36 -0
- package/dist/media-server.d.ts.map +1 -0
- package/dist/member-api.d.ts.map +1 -1
- package/dist/server/ws-core-replicator.d.ts +6 -0
- package/dist/server/ws-core-replicator.d.ts.map +1 -0
- package/dist/sync/core-sync-state.d.ts +14 -6
- package/dist/sync/core-sync-state.d.ts.map +1 -1
- package/dist/sync/namespace-sync-state.d.ts +3 -13
- package/dist/sync/namespace-sync-state.d.ts.map +1 -1
- package/dist/sync/sync-api.d.ts +17 -25
- package/dist/sync/sync-api.d.ts.map +1 -1
- package/dist/sync/sync-state.d.ts +3 -13
- package/dist/sync/sync-state.d.ts.map +1 -1
- package/dist/types.d.ts +6 -0
- package/dist/types.d.ts.map +1 -1
- package/dist/utils.d.ts +2 -2
- package/dist/utils.d.ts.map +1 -1
- package/package.json +5 -3
- package/src/blob-store/entries-stream.js +43 -18
- package/src/blob-store/index.js +161 -19
- package/src/core-manager/index.js +14 -7
- package/src/errors.js +33 -0
- package/src/generated/extensions.d.ts +7 -0
- package/src/generated/extensions.js +12 -2
- package/src/generated/extensions.ts +19 -1
- package/src/invite/StateDiagram.md +47 -0
- package/src/invite/invite-api.js +387 -0
- package/src/invite/invite-state-machine.js +208 -0
- package/src/local-peers.js +12 -9
- package/src/mapeo-manager.js +1 -1
- package/src/mapeo-project.js +7 -76
- package/src/member-api.js +5 -4
- package/src/sync/core-sync-state.js +41 -14
- package/src/sync/namespace-sync-state.js +25 -22
- package/src/sync/sync-api.js +12 -43
- package/src/sync/sync-state.js +9 -19
- package/src/types.ts +7 -1
- package/src/utils.js +8 -3
- package/src/invite-api.js +0 -450
package/src/blob-store/index.js
CHANGED
|
@@ -6,12 +6,26 @@ import { FilterEntriesStream } from './utils.js'
|
|
|
6
6
|
import { noop } from '../utils.js'
|
|
7
7
|
import { TypedEmitter } from 'tiny-typed-emitter'
|
|
8
8
|
import { HyperdriveIndexImpl as HyperdriveIndex } from './hyperdrive-index.js'
|
|
9
|
+
import { Logger } from '../logger.js'
|
|
10
|
+
import { getErrorCode, getErrorMessage } from '../lib/error.js'
|
|
9
11
|
|
|
10
12
|
/** @import Hyperdrive from 'hyperdrive' */
|
|
11
13
|
/** @import { JsonObject } from 'type-fest' */
|
|
12
14
|
/** @import { Readable as NodeReadable } from 'node:stream' */
|
|
13
15
|
/** @import { Readable as StreamxReadable, Writable } from 'streamx' */
|
|
14
|
-
/** @import { BlobFilter, BlobId, BlobStoreEntriesStream } from '../types.js' */
|
|
16
|
+
/** @import { GenericBlobFilter, BlobFilter, BlobId, BlobStoreEntriesStream } from '../types.js' */
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* @typedef {object} BlobStoreEvents
|
|
20
|
+
* @prop {(peerId: string, blobFilter: GenericBlobFilter | null) => void} blob-filter
|
|
21
|
+
* @prop {(opts: {
|
|
22
|
+
* peerId: string
|
|
23
|
+
* start: number
|
|
24
|
+
* length: number
|
|
25
|
+
* blobCoreId: string
|
|
26
|
+
* }) => void} want-blob-range
|
|
27
|
+
* @prop {(error: Error) => void} error
|
|
28
|
+
*/
|
|
15
29
|
|
|
16
30
|
/**
|
|
17
31
|
* @internal
|
|
@@ -31,6 +45,13 @@ const SUPPORTED_BLOB_VARIANTS = /** @type {const} */ ({
|
|
|
31
45
|
// the type with JSDoc
|
|
32
46
|
export { SUPPORTED_BLOB_VARIANTS }
|
|
33
47
|
|
|
48
|
+
/** @type {import('../types.js').BlobFilter} */
|
|
49
|
+
const NON_ARCHIVE_DEVICE_DOWNLOAD_FILTER = {
|
|
50
|
+
photo: ['preview', 'thumbnail'],
|
|
51
|
+
// Don't download any audio of video files, since previews and
|
|
52
|
+
// thumbnails aren't supported yet.
|
|
53
|
+
}
|
|
54
|
+
|
|
34
55
|
class ErrNotFound extends Error {
|
|
35
56
|
constructor(message = 'NotFound') {
|
|
36
57
|
super(message)
|
|
@@ -38,24 +59,117 @@ class ErrNotFound extends Error {
|
|
|
38
59
|
}
|
|
39
60
|
}
|
|
40
61
|
|
|
41
|
-
/** @extends {TypedEmitter<
|
|
62
|
+
/** @extends {TypedEmitter<BlobStoreEvents>} */
|
|
42
63
|
export class BlobStore extends TypedEmitter {
|
|
43
64
|
#driveIndex
|
|
44
65
|
/** @type {Downloader} */
|
|
45
66
|
#downloader
|
|
67
|
+
/** @type {Map<string, GenericBlobFilter | null>} */
|
|
68
|
+
#blobFilters = new Map()
|
|
69
|
+
#l
|
|
70
|
+
/** @type {Map<string, BlobStoreEntriesStream>} */
|
|
71
|
+
#entriesStreams = new Map()
|
|
72
|
+
#isArchiveDevice
|
|
73
|
+
#deviceId
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Bound function for handling download intents for both peers and self
|
|
77
|
+
* @param {GenericBlobFilter | null} filter
|
|
78
|
+
* @param {string} peerId
|
|
79
|
+
*/
|
|
80
|
+
#handleDownloadIntent = async (filter, peerId) => {
|
|
81
|
+
this.#l.log('Download intent %o for peer %S', filter, peerId)
|
|
82
|
+
try {
|
|
83
|
+
this.#entriesStreams.get(peerId)?.destroy()
|
|
84
|
+
this.emit('blob-filter', peerId, filter)
|
|
85
|
+
this.#blobFilters.set(peerId, filter)
|
|
86
|
+
|
|
87
|
+
if (filter === null) return
|
|
88
|
+
|
|
89
|
+
const entriesReadStream = this.createEntriesReadStream({
|
|
90
|
+
live: true,
|
|
91
|
+
filter,
|
|
92
|
+
})
|
|
93
|
+
this.#entriesStreams.set(peerId, entriesReadStream)
|
|
94
|
+
|
|
95
|
+
entriesReadStream.once('close', () => {
|
|
96
|
+
if (this.#entriesStreams.get(peerId) === entriesReadStream) {
|
|
97
|
+
this.#entriesStreams.delete(peerId)
|
|
98
|
+
}
|
|
99
|
+
})
|
|
100
|
+
|
|
101
|
+
for await (const {
|
|
102
|
+
blobCoreId,
|
|
103
|
+
value: { blob },
|
|
104
|
+
} of entriesReadStream) {
|
|
105
|
+
const { blockOffset: start, blockLength: length } = blob
|
|
106
|
+
this.emit('want-blob-range', {
|
|
107
|
+
peerId,
|
|
108
|
+
start,
|
|
109
|
+
length,
|
|
110
|
+
blobCoreId,
|
|
111
|
+
})
|
|
112
|
+
}
|
|
113
|
+
} catch (err) {
|
|
114
|
+
if (getErrorCode(err) === 'ERR_STREAM_PREMATURE_CLOSE') return
|
|
115
|
+
this.#l.log(
|
|
116
|
+
'Error getting blob entries stream for peer %h: %s',
|
|
117
|
+
peerId,
|
|
118
|
+
getErrorMessage(err)
|
|
119
|
+
)
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
/**
|
|
124
|
+
* Bound to `this`
|
|
125
|
+
* This will be called whenever a peer is successfully added to the creatorcore
|
|
126
|
+
* @param {import('../types.js').HypercorePeer & { protomux: import('protomux')<import('../lib/noise-secret-stream-helpers.js').OpenedNoiseStream> }} peer
|
|
127
|
+
*/
|
|
128
|
+
#handlePeerAdd = (peer) => {
|
|
129
|
+
const downloadFilter = getBlobDownloadFilter(this.#isArchiveDevice)
|
|
130
|
+
this.#coreManager.sendDownloadIntents(downloadFilter, peer)
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
/**
|
|
134
|
+
* Bound to `this`
|
|
135
|
+
* @param {import('../types.js').HypercorePeer & { protomux: import('protomux')<import('../lib/noise-secret-stream-helpers.js').OpenedNoiseStream> }} peer
|
|
136
|
+
*/
|
|
137
|
+
#handlePeerRemove = (peer) => {
|
|
138
|
+
const peerKey = peer.protomux.stream.remotePublicKey
|
|
139
|
+
const peerId = peerKey.toString('hex')
|
|
140
|
+
this.#entriesStreams.get(peerId)?.destroy()
|
|
141
|
+
this.#entriesStreams.delete(peerId)
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
#coreManager
|
|
145
|
+
#logger
|
|
46
146
|
|
|
47
147
|
/**
|
|
48
148
|
* @param {object} options
|
|
49
149
|
* @param {import('../core-manager/index.js').CoreManager} options.coreManager
|
|
50
|
-
* @param {
|
|
150
|
+
* @param {boolean} [options.isArchiveDevice] Set to `true` if this is an archive device which should download all blobs, or just a selection of blobs
|
|
151
|
+
* @param {import('../logger.js').Logger} [options.logger]
|
|
51
152
|
*/
|
|
52
|
-
constructor({ coreManager,
|
|
153
|
+
constructor({ coreManager, isArchiveDevice = true, logger }) {
|
|
53
154
|
super()
|
|
155
|
+
this.#logger = logger
|
|
156
|
+
this.#l = Logger.create('blobStore', logger)
|
|
157
|
+
this.#isArchiveDevice = isArchiveDevice
|
|
54
158
|
this.#driveIndex = new HyperdriveIndex(coreManager)
|
|
159
|
+
this.#coreManager = coreManager
|
|
160
|
+
this.#deviceId = coreManager.deviceId
|
|
161
|
+
const downloadFilter = getBlobDownloadFilter(isArchiveDevice)
|
|
162
|
+
if (downloadFilter) {
|
|
163
|
+
this.#handleDownloadIntent(downloadFilter, this.#deviceId)
|
|
164
|
+
}
|
|
55
165
|
this.#downloader = new Downloader(this.#driveIndex, {
|
|
56
166
|
filter: downloadFilter,
|
|
57
167
|
})
|
|
58
168
|
this.#downloader.on('error', (error) => this.emit('error', error))
|
|
169
|
+
|
|
170
|
+
coreManager.on('peer-download-intent', this.#handleDownloadIntent)
|
|
171
|
+
coreManager.creatorCore.on('peer-add', this.#handlePeerAdd)
|
|
172
|
+
coreManager.creatorCore.on('peer-remove', this.#handlePeerRemove)
|
|
59
173
|
}
|
|
60
174
|
|
|
61
175
|
/**
|
|
@@ -65,6 +179,38 @@ export class BlobStore extends TypedEmitter {
|
|
|
65
179
|
return getDiscoveryId(this.#driveIndex.writerKey)
|
|
66
180
|
}
|
|
67
181
|
|
|
182
|
+
get isArchiveDevice() {
|
|
183
|
+
return this.#isArchiveDevice
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
/**
|
|
187
|
+
* @param {string} peerId
|
|
188
|
+
* @returns {GenericBlobFilter | null}
|
|
189
|
+
*/
|
|
190
|
+
getBlobFilter(peerId) {
|
|
191
|
+
return this.#blobFilters.get(peerId) ?? null
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
/** @param {boolean} isArchiveDevice */
|
|
195
|
+
async setIsArchiveDevice(isArchiveDevice) {
|
|
196
|
+
this.#l.log('Setting isArchiveDevice to %s', isArchiveDevice)
|
|
197
|
+
if (this.#isArchiveDevice === isArchiveDevice) return
|
|
198
|
+
this.#isArchiveDevice = isArchiveDevice
|
|
199
|
+
const blobDownloadFilter = getBlobDownloadFilter(isArchiveDevice)
|
|
200
|
+
this.#downloader.removeAllListeners()
|
|
201
|
+
this.#downloader.destroy()
|
|
202
|
+
this.#downloader = new Downloader(this.#driveIndex, {
|
|
203
|
+
filter: blobDownloadFilter,
|
|
204
|
+
})
|
|
205
|
+
this.#downloader.on('error', (error) => this.emit('error', error))
|
|
206
|
+
// Even if blobFilter is null, e.g. we plan to download everything, we still
|
|
207
|
+
// need to inform connected peers of the change.
|
|
208
|
+
for (const peer of this.#coreManager.creatorCore.peers) {
|
|
209
|
+
this.#coreManager.sendDownloadIntents(blobDownloadFilter, peer)
|
|
210
|
+
}
|
|
211
|
+
this.#handleDownloadIntent(blobDownloadFilter, this.#deviceId)
|
|
212
|
+
}
|
|
213
|
+
|
|
68
214
|
/**
|
|
69
215
|
* @param {string} driveId hex-encoded discovery key
|
|
70
216
|
* @returns {Hyperdrive}
|
|
@@ -90,21 +236,6 @@ export class BlobStore extends TypedEmitter {
|
|
|
90
236
|
return blob
|
|
91
237
|
}
|
|
92
238
|
|
|
93
|
-
/**
|
|
94
|
-
* Set the filter for downloading blobs.
|
|
95
|
-
*
|
|
96
|
-
* @param {import('../types.js').BlobFilter | null} filter Filter blob types and/or variants to download. Filter is { [BlobType]: BlobVariants[] }. At least one blob variant must be specified for each blob type.
|
|
97
|
-
* @returns {void}
|
|
98
|
-
*/
|
|
99
|
-
setDownloadFilter(filter) {
|
|
100
|
-
this.#downloader.removeAllListeners()
|
|
101
|
-
this.#downloader.destroy()
|
|
102
|
-
this.#downloader = new Downloader(this.#driveIndex, {
|
|
103
|
-
filter,
|
|
104
|
-
})
|
|
105
|
-
this.#downloader.on('error', (error) => this.emit('error', error))
|
|
106
|
-
}
|
|
107
|
-
|
|
108
239
|
/**
|
|
109
240
|
* @param {BlobId} blobId
|
|
110
241
|
* @param {object} [options]
|
|
@@ -245,6 +376,9 @@ export class BlobStore extends TypedEmitter {
|
|
|
245
376
|
close() {
|
|
246
377
|
this.#downloader.removeAllListeners()
|
|
247
378
|
this.#downloader.destroy()
|
|
379
|
+
this.#coreManager.off('peer-download-intent', this.#handleDownloadIntent)
|
|
380
|
+
this.#coreManager.creatorCore.off('peer-add', this.#handlePeerAdd)
|
|
381
|
+
this.#coreManager.creatorCore.off('peer-remove', this.#handlePeerRemove)
|
|
248
382
|
}
|
|
249
383
|
}
|
|
250
384
|
|
|
@@ -280,3 +414,11 @@ function makePath({ type, variant, name }) {
|
|
|
280
414
|
function getDiscoveryId(key) {
|
|
281
415
|
return discoveryKey(key).toString('hex')
|
|
282
416
|
}
|
|
417
|
+
|
|
418
|
+
/**
|
|
419
|
+
* @param {boolean} isArchiveDevice
|
|
420
|
+
* @returns {null | BlobFilter}
|
|
421
|
+
*/
|
|
422
|
+
function getBlobDownloadFilter(isArchiveDevice) {
|
|
423
|
+
return isArchiveDevice ? null : NON_ARCHIVE_DEVICE_DOWNLOAD_FILTER
|
|
424
|
+
}
|
|
@@ -30,7 +30,7 @@ export const kCoreManagerReplicate = Symbol('replicate core manager')
|
|
|
30
30
|
* @typedef {Object} Events
|
|
31
31
|
* @property {(coreRecord: CoreRecord) => void} add-core
|
|
32
32
|
* @property {(namespace: Namespace, msg: { coreDiscoveryId: string, peerId: string, start: number, bitfield: Uint32Array }) => void} peer-have
|
|
33
|
-
* @property {(blobFilter: GenericBlobFilter, peerId: string) => void} peer-download-intent
|
|
33
|
+
* @property {(blobFilter: GenericBlobFilter | null, peerId: string) => void} peer-download-intent
|
|
34
34
|
*/
|
|
35
35
|
|
|
36
36
|
/**
|
|
@@ -412,7 +412,7 @@ export class CoreManager extends TypedEmitter {
|
|
|
412
412
|
}
|
|
413
413
|
|
|
414
414
|
/**
|
|
415
|
-
* @param {GenericBlobFilter} blobFilter
|
|
415
|
+
* @param {GenericBlobFilter | null} blobFilter
|
|
416
416
|
* @param {HypercorePeer} peer
|
|
417
417
|
*/
|
|
418
418
|
#handleDownloadIntentMessage(blobFilter, peer) {
|
|
@@ -421,7 +421,7 @@ export class CoreManager extends TypedEmitter {
|
|
|
421
421
|
}
|
|
422
422
|
|
|
423
423
|
/**
|
|
424
|
-
* @param {BlobFilter} blobFilter
|
|
424
|
+
* @param {BlobFilter | null} blobFilter
|
|
425
425
|
* @param {HypercorePeer} peer
|
|
426
426
|
*/
|
|
427
427
|
sendDownloadIntents(blobFilter, peer) {
|
|
@@ -540,20 +540,27 @@ const HaveExtensionCodec = {
|
|
|
540
540
|
}
|
|
541
541
|
|
|
542
542
|
const DownloadIntentCodec = {
|
|
543
|
-
/** @param {BlobFilter} filter */
|
|
543
|
+
/** @param {BlobFilter | null} filter */
|
|
544
544
|
encode(filter) {
|
|
545
|
-
const downloadIntents = mapObject(filter, (key, value) => [
|
|
545
|
+
const downloadIntents = mapObject(filter || {}, (key, value) => [
|
|
546
546
|
key,
|
|
547
547
|
{ variants: value || [] },
|
|
548
548
|
])
|
|
549
|
-
|
|
549
|
+
// If filter is null, we want to download everything
|
|
550
|
+
const everything = !filter
|
|
551
|
+
return DownloadIntentExtension.encode({
|
|
552
|
+
downloadIntents,
|
|
553
|
+
everything,
|
|
554
|
+
}).finish()
|
|
550
555
|
},
|
|
551
556
|
/**
|
|
552
557
|
* @param {Buffer | Uint8Array} buf
|
|
553
|
-
* @returns {GenericBlobFilter}
|
|
558
|
+
* @returns {GenericBlobFilter | null}
|
|
554
559
|
*/
|
|
555
560
|
decode(buf) {
|
|
556
561
|
const msg = DownloadIntentExtension.decode(buf)
|
|
562
|
+
// If everything is true, we ignore the downloadIntents and return null, which means download everything
|
|
563
|
+
if (msg.everything) return null
|
|
557
564
|
return mapObject(msg.downloadIntents, (key, value) => [
|
|
558
565
|
key + '', // keep TS happy
|
|
559
566
|
value.variants,
|
package/src/errors.js
CHANGED
|
@@ -1,6 +1,39 @@
|
|
|
1
1
|
export class NotFoundError extends Error {
|
|
2
2
|
constructor(message = 'Not found') {
|
|
3
3
|
super(message)
|
|
4
|
+
this.name = 'NotFoundError'
|
|
5
|
+
}
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
export class AlreadyJoinedError extends Error {
|
|
9
|
+
/** @param {string} [message] */
|
|
10
|
+
constructor(message = 'AlreadyJoinedError') {
|
|
11
|
+
super(message)
|
|
12
|
+
this.name = 'AlreadyJoinedError'
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export class InviteSendError extends Error {
|
|
17
|
+
/** @param {string} [message] */
|
|
18
|
+
constructor(message = 'Invite Send Error') {
|
|
19
|
+
super(message)
|
|
20
|
+
this.name = 'InviteSendError'
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export class InviteAbortedError extends Error {
|
|
25
|
+
/** @param {string} [message] */
|
|
26
|
+
constructor(message = 'Invite Aborted') {
|
|
27
|
+
super(message)
|
|
28
|
+
this.name = 'InviteAbortedError'
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export class TimeoutError extends Error {
|
|
33
|
+
/** @param {string} [message] */
|
|
34
|
+
constructor(message = 'TimeoutError') {
|
|
35
|
+
super(message)
|
|
36
|
+
this.name = 'TimeoutError'
|
|
4
37
|
}
|
|
5
38
|
}
|
|
6
39
|
|
|
@@ -24,6 +24,13 @@ export interface DownloadIntentExtension {
|
|
|
24
24
|
downloadIntents: {
|
|
25
25
|
[key: string]: DownloadIntentExtension_DownloadIntent;
|
|
26
26
|
};
|
|
27
|
+
/**
|
|
28
|
+
* If true, the peer intends to download all blobs - this is the default
|
|
29
|
+
* assumption when a peer has not sent a download intent, but if a peer
|
|
30
|
+
* changes their intent while connected, we need to send the new intent to
|
|
31
|
+
* download everything.
|
|
32
|
+
*/
|
|
33
|
+
everything: boolean;
|
|
27
34
|
}
|
|
28
35
|
export interface DownloadIntentExtension_DownloadIntent {
|
|
29
36
|
variants: string[];
|
|
@@ -170,7 +170,7 @@ export var HaveExtension = {
|
|
|
170
170
|
},
|
|
171
171
|
};
|
|
172
172
|
function createBaseDownloadIntentExtension() {
|
|
173
|
-
return { downloadIntents: {} };
|
|
173
|
+
return { downloadIntents: {}, everything: false };
|
|
174
174
|
}
|
|
175
175
|
export var DownloadIntentExtension = {
|
|
176
176
|
encode: function (message, writer) {
|
|
@@ -180,6 +180,9 @@ export var DownloadIntentExtension = {
|
|
|
180
180
|
DownloadIntentExtension_DownloadIntentsEntry.encode({ key: key, value: value }, writer.uint32(10).fork())
|
|
181
181
|
.ldelim();
|
|
182
182
|
});
|
|
183
|
+
if (message.everything === true) {
|
|
184
|
+
writer.uint32(16).bool(message.everything);
|
|
185
|
+
}
|
|
183
186
|
return writer;
|
|
184
187
|
},
|
|
185
188
|
decode: function (input, length) {
|
|
@@ -198,6 +201,12 @@ export var DownloadIntentExtension = {
|
|
|
198
201
|
message.downloadIntents[entry1.key] = entry1.value;
|
|
199
202
|
}
|
|
200
203
|
continue;
|
|
204
|
+
case 2:
|
|
205
|
+
if (tag !== 16) {
|
|
206
|
+
break;
|
|
207
|
+
}
|
|
208
|
+
message.everything = reader.bool();
|
|
209
|
+
continue;
|
|
201
210
|
}
|
|
202
211
|
if ((tag & 7) === 4 || tag === 0) {
|
|
203
212
|
break;
|
|
@@ -210,7 +219,7 @@ export var DownloadIntentExtension = {
|
|
|
210
219
|
return DownloadIntentExtension.fromPartial(base !== null && base !== void 0 ? base : {});
|
|
211
220
|
},
|
|
212
221
|
fromPartial: function (object) {
|
|
213
|
-
var _a;
|
|
222
|
+
var _a, _b;
|
|
214
223
|
var message = createBaseDownloadIntentExtension();
|
|
215
224
|
message.downloadIntents = Object.entries((_a = object.downloadIntents) !== null && _a !== void 0 ? _a : {}).reduce(function (acc, _a) {
|
|
216
225
|
var key = _a[0], value = _a[1];
|
|
@@ -219,6 +228,7 @@ export var DownloadIntentExtension = {
|
|
|
219
228
|
}
|
|
220
229
|
return acc;
|
|
221
230
|
}, {});
|
|
231
|
+
message.everything = (_b = object.everything) !== null && _b !== void 0 ? _b : false;
|
|
222
232
|
return message;
|
|
223
233
|
},
|
|
224
234
|
};
|
|
@@ -69,6 +69,13 @@ export function haveExtension_NamespaceToNumber(object: HaveExtension_Namespace)
|
|
|
69
69
|
/** A map of blob types and variants that a peer intends to download */
|
|
70
70
|
export interface DownloadIntentExtension {
|
|
71
71
|
downloadIntents: { [key: string]: DownloadIntentExtension_DownloadIntent };
|
|
72
|
+
/**
|
|
73
|
+
* If true, the peer intends to download all blobs - this is the default
|
|
74
|
+
* assumption when a peer has not sent a download intent, but if a peer
|
|
75
|
+
* changes their intent while connected, we need to send the new intent to
|
|
76
|
+
* download everything.
|
|
77
|
+
*/
|
|
78
|
+
everything: boolean;
|
|
72
79
|
}
|
|
73
80
|
|
|
74
81
|
export interface DownloadIntentExtension_DownloadIntent {
|
|
@@ -209,7 +216,7 @@ export const HaveExtension = {
|
|
|
209
216
|
};
|
|
210
217
|
|
|
211
218
|
function createBaseDownloadIntentExtension(): DownloadIntentExtension {
|
|
212
|
-
return { downloadIntents: {} };
|
|
219
|
+
return { downloadIntents: {}, everything: false };
|
|
213
220
|
}
|
|
214
221
|
|
|
215
222
|
export const DownloadIntentExtension = {
|
|
@@ -218,6 +225,9 @@ export const DownloadIntentExtension = {
|
|
|
218
225
|
DownloadIntentExtension_DownloadIntentsEntry.encode({ key: key as any, value }, writer.uint32(10).fork())
|
|
219
226
|
.ldelim();
|
|
220
227
|
});
|
|
228
|
+
if (message.everything === true) {
|
|
229
|
+
writer.uint32(16).bool(message.everything);
|
|
230
|
+
}
|
|
221
231
|
return writer;
|
|
222
232
|
},
|
|
223
233
|
|
|
@@ -238,6 +248,13 @@ export const DownloadIntentExtension = {
|
|
|
238
248
|
message.downloadIntents[entry1.key] = entry1.value;
|
|
239
249
|
}
|
|
240
250
|
continue;
|
|
251
|
+
case 2:
|
|
252
|
+
if (tag !== 16) {
|
|
253
|
+
break;
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
message.everything = reader.bool();
|
|
257
|
+
continue;
|
|
241
258
|
}
|
|
242
259
|
if ((tag & 7) === 4 || tag === 0) {
|
|
243
260
|
break;
|
|
@@ -260,6 +277,7 @@ export const DownloadIntentExtension = {
|
|
|
260
277
|
}
|
|
261
278
|
return acc;
|
|
262
279
|
}, {});
|
|
280
|
+
message.everything = object.everything ?? false;
|
|
263
281
|
return message;
|
|
264
282
|
},
|
|
265
283
|
};
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
```mermaid
|
|
2
|
+
---
|
|
3
|
+
title: Invite Receive State Diagram
|
|
4
|
+
---
|
|
5
|
+
stateDiagram-v2
|
|
6
|
+
state "invite" as invite {
|
|
7
|
+
[*] --> invite.pending
|
|
8
|
+
invite.pending --> invite.canceled : CANCEL_INVITE
|
|
9
|
+
invite.pending --> invite.responding.accept : ACCEPT_INVITE
|
|
10
|
+
invite.pending --> invite.responding.already : ALREADY_IN_PROJECT
|
|
11
|
+
invite.pending --> invite.responding.reject : REJECT_INVITE
|
|
12
|
+
invite.responding.default --> invite.error
|
|
13
|
+
invite.responding.accept --> invite.joining.addingProject : RECEIVE_PROJECT_DETAILS
|
|
14
|
+
invite.responding.accept --> invite.joining : Responded Accept
|
|
15
|
+
invite.responding.accept --> invite.error : Error responding
|
|
16
|
+
invite.responding.reject --> invite.rejected : Responded Reject
|
|
17
|
+
invite.responding.reject --> invite.error : Error responding
|
|
18
|
+
invite.responding.already --> invite.respondedAlready : Responded Already
|
|
19
|
+
invite.responding.already --> invite.error : Error responding
|
|
20
|
+
invite.responding --> invite.canceled : CANCEL_INVITE
|
|
21
|
+
invite.joining.awaitingDetails --> invite.canceled : CANCEL_INVITE
|
|
22
|
+
invite.joining.awaitingDetails --> invite.error : projectDetailsTimeout
|
|
23
|
+
invite.joining.addingProject --> invite.error : addProject Timeout
|
|
24
|
+
invite.joining.addingProject --> invite.joined : Project Added
|
|
25
|
+
invite.joining.addingProject --> invite.error : addProject Error
|
|
26
|
+
state "Pending invite awaiting response" as invite.pending
|
|
27
|
+
state "Responding to invite" as invite.responding {
|
|
28
|
+
[*] --> invite.responding.default
|
|
29
|
+
state "default" as invite.responding.default
|
|
30
|
+
state "accept" as invite.responding.accept
|
|
31
|
+
state "reject" as invite.responding.reject
|
|
32
|
+
state "already" as invite.responding.already
|
|
33
|
+
}
|
|
34
|
+
state "Joining project from invite" as invite.joining {
|
|
35
|
+
[*] --> invite.joining.awaitingDetails
|
|
36
|
+
invite.joining.awaitingDetails --> invite.joining.addingProject : RECEIVE_PROJECT_DETAILS
|
|
37
|
+
state "Awaiting project details" as invite.joining.awaitingDetails
|
|
38
|
+
state "Adding project" as invite.joining.addingProject
|
|
39
|
+
}
|
|
40
|
+
state "Invite Canceled" as invite.canceled
|
|
41
|
+
state "Invite Rejected" as invite.rejected
|
|
42
|
+
state "Responded already in project" as invite.respondedAlready
|
|
43
|
+
state "Joined project" as invite.joined
|
|
44
|
+
state "Invite Error" as invite.error
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
```
|