@comapeo/core 6.0.2 → 7.0.1
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/downloader.d.ts +4 -9
- package/dist/blob-store/downloader.d.ts.map +1 -1
- package/dist/blob-store/hyperdrive-index.d.ts +4 -5
- package/dist/blob-store/hyperdrive-index.d.ts.map +1 -1
- package/dist/blob-store/index.d.ts +4 -5
- package/dist/blob-store/index.d.ts.map +1 -1
- package/dist/core-manager/index.d.ts +4 -23
- package/dist/core-manager/index.d.ts.map +1 -1
- package/dist/datastore/index.d.ts +1 -1
- package/dist/generated/extensions.d.ts +0 -30
- package/dist/generated/extensions.d.ts.map +1 -1
- package/dist/generated/rpc.d.ts +30 -0
- package/dist/generated/rpc.d.ts.map +1 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/local-peers.d.ts +12 -0
- package/dist/local-peers.d.ts.map +1 -1
- package/dist/mapeo-manager.d.ts +41 -3
- package/dist/mapeo-manager.d.ts.map +1 -1
- package/dist/mapeo-project.d.ts +3 -76
- package/dist/mapeo-project.d.ts.map +1 -1
- package/dist/schema/project.d.ts +1 -1
- package/dist/sync/namespace-sync-state.d.ts +1 -1
- package/dist/sync/peer-sync-controller.d.ts +1 -1
- package/dist/sync/sync-api.d.ts.map +1 -1
- package/dist/utils.d.ts +3 -3
- package/dist/utils.d.ts.map +1 -1
- package/package.json +2 -1
- package/src/blob-store/downloader.js +12 -4
- package/src/blob-store/hyperdrive-index.js +22 -3
- package/src/blob-store/index.js +18 -7
- package/src/core-manager/index.js +16 -70
- package/src/generated/extensions.d.ts +0 -30
- package/src/generated/extensions.js +0 -165
- package/src/generated/extensions.ts +0 -204
- package/src/generated/rpc.d.ts +30 -0
- package/src/generated/rpc.js +191 -0
- package/src/generated/rpc.ts +236 -0
- package/src/index.js +2 -2
- package/src/local-peers.js +33 -0
- package/src/mapeo-manager.js +93 -35
- package/src/mapeo-project.js +18 -106
- package/src/utils.js +2 -2
package/src/generated/rpc.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
/* eslint-disable */
|
|
2
|
+
import Long from "long";
|
|
2
3
|
import _m0 from "protobufjs/minimal.js";
|
|
3
4
|
import { EncryptionKeys } from "./keys.js";
|
|
4
5
|
|
|
@@ -181,6 +182,31 @@ export interface ProjectJoinDetailsAck {
|
|
|
181
182
|
inviteId: Buffer;
|
|
182
183
|
}
|
|
183
184
|
|
|
185
|
+
export interface MapShareExtension {
|
|
186
|
+
/** URLs to map share */
|
|
187
|
+
mapShareUrls: string[];
|
|
188
|
+
/** ID of peer that can receive the map share (each map share is linked to a specific device ID) */
|
|
189
|
+
receiverDeviceKey: Buffer;
|
|
190
|
+
/** The ID of the map share */
|
|
191
|
+
shareId: string;
|
|
192
|
+
/** The name of the map being shared */
|
|
193
|
+
mapName: string;
|
|
194
|
+
/** The ID of the map being shared */
|
|
195
|
+
mapId: string;
|
|
196
|
+
/** When ths share was created */
|
|
197
|
+
mapShareCreatedAt: number;
|
|
198
|
+
/** When the map was created */
|
|
199
|
+
mapCreatedAt: number;
|
|
200
|
+
/** The bounding box of the map data being shared */
|
|
201
|
+
bounds: number[];
|
|
202
|
+
/** The minimum zoom level of the map data being shared */
|
|
203
|
+
minzoom: number;
|
|
204
|
+
/** The maximum zoom level of the map data being shared */
|
|
205
|
+
maxzoom: number;
|
|
206
|
+
/** Estimated size of the map data being shared in bytes */
|
|
207
|
+
estimatedSizeBytes: number;
|
|
208
|
+
}
|
|
209
|
+
|
|
184
210
|
function createBaseInvite(): Invite {
|
|
185
211
|
return {
|
|
186
212
|
inviteId: Buffer.alloc(0),
|
|
@@ -761,6 +787,204 @@ export const ProjectJoinDetailsAck = {
|
|
|
761
787
|
},
|
|
762
788
|
};
|
|
763
789
|
|
|
790
|
+
function createBaseMapShareExtension(): MapShareExtension {
|
|
791
|
+
return {
|
|
792
|
+
mapShareUrls: [],
|
|
793
|
+
receiverDeviceKey: Buffer.alloc(0),
|
|
794
|
+
shareId: "",
|
|
795
|
+
mapName: "",
|
|
796
|
+
mapId: "",
|
|
797
|
+
mapShareCreatedAt: 0,
|
|
798
|
+
mapCreatedAt: 0,
|
|
799
|
+
bounds: [],
|
|
800
|
+
minzoom: 0,
|
|
801
|
+
maxzoom: 0,
|
|
802
|
+
estimatedSizeBytes: 0,
|
|
803
|
+
};
|
|
804
|
+
}
|
|
805
|
+
|
|
806
|
+
export const MapShareExtension = {
|
|
807
|
+
encode(message: MapShareExtension, writer: _m0.Writer = _m0.Writer.create()): _m0.Writer {
|
|
808
|
+
for (const v of message.mapShareUrls) {
|
|
809
|
+
writer.uint32(10).string(v!);
|
|
810
|
+
}
|
|
811
|
+
if (message.receiverDeviceKey.length !== 0) {
|
|
812
|
+
writer.uint32(18).bytes(message.receiverDeviceKey);
|
|
813
|
+
}
|
|
814
|
+
if (message.shareId !== "") {
|
|
815
|
+
writer.uint32(26).string(message.shareId);
|
|
816
|
+
}
|
|
817
|
+
if (message.mapName !== "") {
|
|
818
|
+
writer.uint32(34).string(message.mapName);
|
|
819
|
+
}
|
|
820
|
+
if (message.mapId !== "") {
|
|
821
|
+
writer.uint32(42).string(message.mapId);
|
|
822
|
+
}
|
|
823
|
+
if (message.mapShareCreatedAt !== 0) {
|
|
824
|
+
writer.uint32(48).uint64(message.mapShareCreatedAt);
|
|
825
|
+
}
|
|
826
|
+
if (message.mapCreatedAt !== 0) {
|
|
827
|
+
writer.uint32(56).uint64(message.mapCreatedAt);
|
|
828
|
+
}
|
|
829
|
+
writer.uint32(66).fork();
|
|
830
|
+
for (const v of message.bounds) {
|
|
831
|
+
writer.double(v);
|
|
832
|
+
}
|
|
833
|
+
writer.ldelim();
|
|
834
|
+
if (message.minzoom !== 0) {
|
|
835
|
+
writer.uint32(72).int32(message.minzoom);
|
|
836
|
+
}
|
|
837
|
+
if (message.maxzoom !== 0) {
|
|
838
|
+
writer.uint32(80).int32(message.maxzoom);
|
|
839
|
+
}
|
|
840
|
+
if (message.estimatedSizeBytes !== 0) {
|
|
841
|
+
writer.uint32(88).uint64(message.estimatedSizeBytes);
|
|
842
|
+
}
|
|
843
|
+
return writer;
|
|
844
|
+
},
|
|
845
|
+
|
|
846
|
+
decode(input: _m0.Reader | Uint8Array, length?: number): MapShareExtension {
|
|
847
|
+
const reader = input instanceof _m0.Reader ? input : _m0.Reader.create(input);
|
|
848
|
+
let end = length === undefined ? reader.len : reader.pos + length;
|
|
849
|
+
const message = createBaseMapShareExtension();
|
|
850
|
+
while (reader.pos < end) {
|
|
851
|
+
const tag = reader.uint32();
|
|
852
|
+
switch (tag >>> 3) {
|
|
853
|
+
case 1:
|
|
854
|
+
if (tag !== 10) {
|
|
855
|
+
break;
|
|
856
|
+
}
|
|
857
|
+
|
|
858
|
+
message.mapShareUrls.push(reader.string());
|
|
859
|
+
continue;
|
|
860
|
+
case 2:
|
|
861
|
+
if (tag !== 18) {
|
|
862
|
+
break;
|
|
863
|
+
}
|
|
864
|
+
|
|
865
|
+
message.receiverDeviceKey = reader.bytes() as Buffer;
|
|
866
|
+
continue;
|
|
867
|
+
case 3:
|
|
868
|
+
if (tag !== 26) {
|
|
869
|
+
break;
|
|
870
|
+
}
|
|
871
|
+
|
|
872
|
+
message.shareId = reader.string();
|
|
873
|
+
continue;
|
|
874
|
+
case 4:
|
|
875
|
+
if (tag !== 34) {
|
|
876
|
+
break;
|
|
877
|
+
}
|
|
878
|
+
|
|
879
|
+
message.mapName = reader.string();
|
|
880
|
+
continue;
|
|
881
|
+
case 5:
|
|
882
|
+
if (tag !== 42) {
|
|
883
|
+
break;
|
|
884
|
+
}
|
|
885
|
+
|
|
886
|
+
message.mapId = reader.string();
|
|
887
|
+
continue;
|
|
888
|
+
case 6:
|
|
889
|
+
if (tag !== 48) {
|
|
890
|
+
break;
|
|
891
|
+
}
|
|
892
|
+
|
|
893
|
+
message.mapShareCreatedAt = longToNumber(reader.uint64() as Long);
|
|
894
|
+
continue;
|
|
895
|
+
case 7:
|
|
896
|
+
if (tag !== 56) {
|
|
897
|
+
break;
|
|
898
|
+
}
|
|
899
|
+
|
|
900
|
+
message.mapCreatedAt = longToNumber(reader.uint64() as Long);
|
|
901
|
+
continue;
|
|
902
|
+
case 8:
|
|
903
|
+
if (tag === 65) {
|
|
904
|
+
message.bounds.push(reader.double());
|
|
905
|
+
|
|
906
|
+
continue;
|
|
907
|
+
}
|
|
908
|
+
|
|
909
|
+
if (tag === 66) {
|
|
910
|
+
const end2 = reader.uint32() + reader.pos;
|
|
911
|
+
while (reader.pos < end2) {
|
|
912
|
+
message.bounds.push(reader.double());
|
|
913
|
+
}
|
|
914
|
+
|
|
915
|
+
continue;
|
|
916
|
+
}
|
|
917
|
+
|
|
918
|
+
break;
|
|
919
|
+
case 9:
|
|
920
|
+
if (tag !== 72) {
|
|
921
|
+
break;
|
|
922
|
+
}
|
|
923
|
+
|
|
924
|
+
message.minzoom = reader.int32();
|
|
925
|
+
continue;
|
|
926
|
+
case 10:
|
|
927
|
+
if (tag !== 80) {
|
|
928
|
+
break;
|
|
929
|
+
}
|
|
930
|
+
|
|
931
|
+
message.maxzoom = reader.int32();
|
|
932
|
+
continue;
|
|
933
|
+
case 11:
|
|
934
|
+
if (tag !== 88) {
|
|
935
|
+
break;
|
|
936
|
+
}
|
|
937
|
+
|
|
938
|
+
message.estimatedSizeBytes = longToNumber(reader.uint64() as Long);
|
|
939
|
+
continue;
|
|
940
|
+
}
|
|
941
|
+
if ((tag & 7) === 4 || tag === 0) {
|
|
942
|
+
break;
|
|
943
|
+
}
|
|
944
|
+
reader.skipType(tag & 7);
|
|
945
|
+
}
|
|
946
|
+
return message;
|
|
947
|
+
},
|
|
948
|
+
|
|
949
|
+
create<I extends Exact<DeepPartial<MapShareExtension>, I>>(base?: I): MapShareExtension {
|
|
950
|
+
return MapShareExtension.fromPartial(base ?? ({} as any));
|
|
951
|
+
},
|
|
952
|
+
fromPartial<I extends Exact<DeepPartial<MapShareExtension>, I>>(object: I): MapShareExtension {
|
|
953
|
+
const message = createBaseMapShareExtension();
|
|
954
|
+
message.mapShareUrls = object.mapShareUrls?.map((e) => e) || [];
|
|
955
|
+
message.receiverDeviceKey = object.receiverDeviceKey ?? Buffer.alloc(0);
|
|
956
|
+
message.shareId = object.shareId ?? "";
|
|
957
|
+
message.mapName = object.mapName ?? "";
|
|
958
|
+
message.mapId = object.mapId ?? "";
|
|
959
|
+
message.mapShareCreatedAt = object.mapShareCreatedAt ?? 0;
|
|
960
|
+
message.mapCreatedAt = object.mapCreatedAt ?? 0;
|
|
961
|
+
message.bounds = object.bounds?.map((e) => e) || [];
|
|
962
|
+
message.minzoom = object.minzoom ?? 0;
|
|
963
|
+
message.maxzoom = object.maxzoom ?? 0;
|
|
964
|
+
message.estimatedSizeBytes = object.estimatedSizeBytes ?? 0;
|
|
965
|
+
return message;
|
|
966
|
+
},
|
|
967
|
+
};
|
|
968
|
+
|
|
969
|
+
declare const self: any | undefined;
|
|
970
|
+
declare const window: any | undefined;
|
|
971
|
+
declare const global: any | undefined;
|
|
972
|
+
const tsProtoGlobalThis: any = (() => {
|
|
973
|
+
if (typeof globalThis !== "undefined") {
|
|
974
|
+
return globalThis;
|
|
975
|
+
}
|
|
976
|
+
if (typeof self !== "undefined") {
|
|
977
|
+
return self;
|
|
978
|
+
}
|
|
979
|
+
if (typeof window !== "undefined") {
|
|
980
|
+
return window;
|
|
981
|
+
}
|
|
982
|
+
if (typeof global !== "undefined") {
|
|
983
|
+
return global;
|
|
984
|
+
}
|
|
985
|
+
throw "Unable to locate global object";
|
|
986
|
+
})();
|
|
987
|
+
|
|
764
988
|
type Builtin = Date | Function | Uint8Array | string | number | boolean | undefined;
|
|
765
989
|
|
|
766
990
|
type DeepPartial<T> = T extends Builtin ? T
|
|
@@ -771,3 +995,15 @@ type DeepPartial<T> = T extends Builtin ? T
|
|
|
771
995
|
type KeysOfUnion<T> = T extends T ? keyof T : never;
|
|
772
996
|
type Exact<P, I extends P> = P extends Builtin ? P
|
|
773
997
|
: P & { [K in keyof P]: Exact<P[K], I[K]> } & { [K in Exclude<keyof I, KeysOfUnion<P>>]: never };
|
|
998
|
+
|
|
999
|
+
function longToNumber(long: Long): number {
|
|
1000
|
+
if (long.gt(Number.MAX_SAFE_INTEGER)) {
|
|
1001
|
+
throw new tsProtoGlobalThis.Error("Value is larger than Number.MAX_SAFE_INTEGER");
|
|
1002
|
+
}
|
|
1003
|
+
return long.toNumber();
|
|
1004
|
+
}
|
|
1005
|
+
|
|
1006
|
+
if (_m0.util.Long !== Long) {
|
|
1007
|
+
_m0.util.Long = Long as any;
|
|
1008
|
+
_m0.configure();
|
|
1009
|
+
}
|
package/src/index.js
CHANGED
|
@@ -9,8 +9,8 @@ export { FastifyController } from './fastify-controller.js'
|
|
|
9
9
|
export { MapeoManager } from './mapeo-manager.js'
|
|
10
10
|
|
|
11
11
|
// Type exports
|
|
12
|
-
/** @typedef {import('./mapeo-
|
|
13
|
-
/** @typedef {import('./generated/
|
|
12
|
+
/** @typedef {import('./mapeo-manager.js').MapShare} MapShare */
|
|
13
|
+
/** @typedef {import('./generated/rpc.js').MapShareExtension} MapShareExtension */
|
|
14
14
|
/** @typedef {import('./mapeo-project.js').MapeoProject} MapeoProject */
|
|
15
15
|
/** @typedef {import('./mapeo-project.js').RoleChangeEvent} RoleChangeEvent */
|
|
16
16
|
/** @typedef {import('./mapeo-manager.js').PublicPeerInfo} PublicPeerInfo */
|
package/src/local-peers.js
CHANGED
|
@@ -15,6 +15,7 @@ import {
|
|
|
15
15
|
ProjectJoinDetails,
|
|
16
16
|
ProjectJoinDetailsAck,
|
|
17
17
|
DeviceInfo_RPCFeatures,
|
|
18
|
+
MapShareExtension,
|
|
18
19
|
} from './generated/rpc.js'
|
|
19
20
|
import pDefer from 'p-defer'
|
|
20
21
|
import { Logger } from './logger.js'
|
|
@@ -69,6 +70,7 @@ const MESSAGE_TYPES = {
|
|
|
69
70
|
InviteCancelAck: 6,
|
|
70
71
|
InviteResponseAck: 7,
|
|
71
72
|
ProjectJoinDetailsAck: 8,
|
|
73
|
+
MapShareExtension: 9,
|
|
72
74
|
}
|
|
73
75
|
const MESSAGES_MAX_ID = Math.max.apply(null, [...Object.values(MESSAGE_TYPES)])
|
|
74
76
|
|
|
@@ -289,6 +291,19 @@ class Peer {
|
|
|
289
291
|
const messageType = MESSAGE_TYPES.Invite
|
|
290
292
|
await this.#waitForDrain(this.#channel.messages[messageType].send(buf))
|
|
291
293
|
}
|
|
294
|
+
|
|
295
|
+
/**
|
|
296
|
+
* @param {MapShareExtension} mapShare
|
|
297
|
+
* @returns {Promise<void>}
|
|
298
|
+
*/
|
|
299
|
+
async sendMapShare(mapShare) {
|
|
300
|
+
this.#assertConnected('Peer disconnected before sending map share')
|
|
301
|
+
const buf = Buffer.from(MapShareExtension.encode(mapShare).finish())
|
|
302
|
+
const messageType = MESSAGE_TYPES.MapShareExtension
|
|
303
|
+
await this.#waitForDrain(this.#channel.messages[messageType].send(buf))
|
|
304
|
+
this.#log('sent map share %s', mapShare.shareId)
|
|
305
|
+
}
|
|
306
|
+
|
|
292
307
|
/**
|
|
293
308
|
* @param {Invite} invite
|
|
294
309
|
* @returns {Promise<void>}
|
|
@@ -433,6 +448,7 @@ class Peer {
|
|
|
433
448
|
* @property {(peerId: string, inviteResponse: InviteResponseAck) => void} invite-response-ack Emitted when an invite response acknowledgement is received
|
|
434
449
|
* @property {(peerId: string, details: ProjectJoinDetails) => void} got-project-details Emitted when project details are received
|
|
435
450
|
* @property {(peerId: string, details: ProjectJoinDetailsAck) => void} got-project-details-ack Emitted when project details are acknowledged as received
|
|
451
|
+
* @property {(sender: PeerInfo, details: MapShareExtension) => void} map-share Emitted when a MapShare request is received
|
|
436
452
|
* @property {(discoveryKey: Buffer, protomux: Protomux<import('@hyperswarm/secret-stream')>) => void} discovery-key Emitted when a new hypercore is replicated (by a peer) to a peer protomux instance (passed as the second parameter)
|
|
437
453
|
* @property {(messageType: string, errorMessage?: string) => void} failed-to-handle-message Emitted when we received a message we couldn't handle for some reason. Primarily useful for testing
|
|
438
454
|
*/
|
|
@@ -468,6 +484,17 @@ export class LocalPeers extends TypedEmitter {
|
|
|
468
484
|
return connectedPeerInfos
|
|
469
485
|
}
|
|
470
486
|
|
|
487
|
+
/**
|
|
488
|
+
* @param {string} deviceId
|
|
489
|
+
* @param {MapShareExtension} mapShare
|
|
490
|
+
* @returns {Promise<void>}
|
|
491
|
+
*/
|
|
492
|
+
async sendMapShare(deviceId, mapShare) {
|
|
493
|
+
await this.#waitForPendingConnections()
|
|
494
|
+
const peer = await this.#getPeerByDeviceId(deviceId)
|
|
495
|
+
await peer.sendMapShare(mapShare)
|
|
496
|
+
}
|
|
497
|
+
|
|
471
498
|
/**
|
|
472
499
|
* @param {string} deviceId
|
|
473
500
|
* @param {Invite} invite
|
|
@@ -783,6 +810,12 @@ export class LocalPeers extends TypedEmitter {
|
|
|
783
810
|
this.#emitPeers()
|
|
784
811
|
break
|
|
785
812
|
}
|
|
813
|
+
case 'MapShareExtension': {
|
|
814
|
+
const mapShare = MapShareExtension.decode(value)
|
|
815
|
+
const info = /** @type {PeerInfo} */ (peer.info)
|
|
816
|
+
this.emit('map-share', info, mapShare)
|
|
817
|
+
break
|
|
818
|
+
}
|
|
786
819
|
case 'InviteAck': {
|
|
787
820
|
const ack = InviteAck.decode(value)
|
|
788
821
|
peer.receiveAck('InviteAck', ack)
|
package/src/mapeo-manager.js
CHANGED
|
@@ -7,6 +7,7 @@ import { drizzle } from 'drizzle-orm/better-sqlite3'
|
|
|
7
7
|
import Hypercore from 'hypercore'
|
|
8
8
|
import { TypedEmitter } from 'tiny-typed-emitter'
|
|
9
9
|
import { createRequire } from 'module'
|
|
10
|
+
import ensureError from 'ensure-error'
|
|
10
11
|
|
|
11
12
|
import { IndexWriter } from './index-writer/index.js'
|
|
12
13
|
import {
|
|
@@ -36,6 +37,7 @@ import {
|
|
|
36
37
|
projectKeyToProjectInviteId,
|
|
37
38
|
projectKeyToPublicId,
|
|
38
39
|
timeoutPromise,
|
|
40
|
+
validateMapShareExtension,
|
|
39
41
|
} from './utils.js'
|
|
40
42
|
import { openedNoiseSecretStream } from './lib/noise-secret-stream-helpers.js'
|
|
41
43
|
import { omit } from './lib/omit.js'
|
|
@@ -61,7 +63,7 @@ import { WebSocket } from 'ws'
|
|
|
61
63
|
import { excludeKeys } from 'filter-obj'
|
|
62
64
|
import { migrate } from './lib/drizzle-helpers.js'
|
|
63
65
|
|
|
64
|
-
/** @import { MapShareExtension } from './generated/
|
|
66
|
+
/** @import { MapShareExtension } from './generated/rpc.js' */
|
|
65
67
|
/** @import NoiseSecretStream from '@hyperswarm/secret-stream' */
|
|
66
68
|
/** @import { SetNonNullable } from 'type-fest' */
|
|
67
69
|
/** @import { ProjectJoinDetails, } from './generated/rpc.js' */
|
|
@@ -74,6 +76,24 @@ import { migrate } from './lib/drizzle-helpers.js'
|
|
|
74
76
|
/** @typedef {Pick<ProjectSettings, 'createdAt' | 'updatedAt' | 'name' | 'projectColor' | 'projectDescription' | 'sendStats'>} ListedProjectSettings */
|
|
75
77
|
/** @typedef {ListedProjectSettings & { status: 'joined', projectId: string } | ProjectInfo & { status: 'joining' | 'left', projectId: string }} ListedProject */
|
|
76
78
|
|
|
79
|
+
/**
|
|
80
|
+
* @typedef {object} AugmentedMapShareProperties
|
|
81
|
+
* @property {readonly [number, number, number, number]} bounds - Bounding box of the shared map [W, S, E, N].
|
|
82
|
+
* @property {readonly [string, ...string[]]} mapShareUrls - URLs associated with the map share.
|
|
83
|
+
* @property {number} mapShareReceivedAt - Timestamp when the map share was received.
|
|
84
|
+
* @property {string} senderDeviceId - The ID of the device that sent the map share.
|
|
85
|
+
* @property {string} [senderDeviceName] - The name of the device that sent the map share.
|
|
86
|
+
* @property {string} receiverDeviceId - The deviceId of the peer the map share was sent to
|
|
87
|
+
*/
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* @typedef {Omit<MapShareExtension, 'bounds' | 'mapShareUrls' | 'receiverDeviceKey'> & AugmentedMapShareProperties} MapShare
|
|
91
|
+
*/
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* @typedef {Omit<MapShare, 'mapShareReceivedAt' | 'senderDeviceId' | 'senderDeviceName'>} MapShareSend
|
|
95
|
+
*/
|
|
96
|
+
|
|
77
97
|
const CLIENT_SQLITE_FILE_NAME = 'client.db'
|
|
78
98
|
|
|
79
99
|
// Max file descriptors that RandomAccessFile should use for hypercore storage
|
|
@@ -107,7 +127,7 @@ export const DEFAULT_IS_ARCHIVE_DEVICE = true
|
|
|
107
127
|
/**
|
|
108
128
|
* @typedef {object} MapeoManagerEvents
|
|
109
129
|
* @property {(peers: PublicPeerInfo[]) => void} local-peers Emitted when the list of connected peers changes (new ones added, or connection status changes)
|
|
110
|
-
* @property {(mapShare:
|
|
130
|
+
* @property {(mapShare: MapShare) => void} map-share Emitted when a project has recieved a map share request
|
|
111
131
|
* @property {(e: Error, mapShare: MapShareExtension) => void} map-share-error - Emitted when an incoming map share fails to be recieved due to formatting issues
|
|
112
132
|
*/
|
|
113
133
|
|
|
@@ -202,6 +222,15 @@ export class MapeoManager extends TypedEmitter {
|
|
|
202
222
|
this.#l.log('Received dk %h but no active projects', dk)
|
|
203
223
|
}
|
|
204
224
|
})
|
|
225
|
+
this.#localPeers.on('map-share', (deviceId, mapShareBase) =>
|
|
226
|
+
this.#handleMapShare(deviceId, mapShareBase).catch((e) => {
|
|
227
|
+
this.emit('map-share-error', e, mapShareBase)
|
|
228
|
+
this.#l.log(
|
|
229
|
+
'Error: Unable to handle incoming Map Share',
|
|
230
|
+
ensureError(e)
|
|
231
|
+
)
|
|
232
|
+
})
|
|
233
|
+
)
|
|
205
234
|
|
|
206
235
|
this.#projectSettingsIndexWriter = new IndexWriter({
|
|
207
236
|
tables: [projectSettingsTable],
|
|
@@ -491,6 +520,11 @@ export class MapeoManager extends TypedEmitter {
|
|
|
491
520
|
// TODO: Close the project instance instead of keeping it around
|
|
492
521
|
this.#activeProjects.set(projectPublicId, project)
|
|
493
522
|
|
|
523
|
+
// Make sure to clean up when closed
|
|
524
|
+
project.once('close', () => {
|
|
525
|
+
this.#activeProjects.delete(projectPublicId)
|
|
526
|
+
})
|
|
527
|
+
|
|
494
528
|
// 7. Load config, if relevant
|
|
495
529
|
// TODO: see how to expose warnings to frontend
|
|
496
530
|
// eslint-disable-next-line no-unused-vars
|
|
@@ -554,6 +588,11 @@ export class MapeoManager extends TypedEmitter {
|
|
|
554
588
|
// 3. Keep track of project instance as we know it's a properly existing project
|
|
555
589
|
this.#activeProjects.set(projectPublicId, project)
|
|
556
590
|
|
|
591
|
+
// Make sure to clean up when closed
|
|
592
|
+
project.once('close', () => {
|
|
593
|
+
this.#activeProjects.delete(projectPublicId)
|
|
594
|
+
})
|
|
595
|
+
|
|
557
596
|
return project
|
|
558
597
|
}
|
|
559
598
|
|
|
@@ -583,34 +622,6 @@ export class MapeoManager extends TypedEmitter {
|
|
|
583
622
|
},
|
|
584
623
|
})
|
|
585
624
|
|
|
586
|
-
const projectPublicId = projectKeyToPublicId(projectKeys.projectKey)
|
|
587
|
-
|
|
588
|
-
/**
|
|
589
|
-
* @param {import('./mapeo-project.js').MapShare} mapShare
|
|
590
|
-
*/
|
|
591
|
-
const onMapShare = (mapShare) => {
|
|
592
|
-
this.emit('map-share', mapShare)
|
|
593
|
-
}
|
|
594
|
-
|
|
595
|
-
/**
|
|
596
|
-
*
|
|
597
|
-
* @param {Error} e
|
|
598
|
-
* @param {MapShareExtension} mapShareExtension
|
|
599
|
-
*/
|
|
600
|
-
const onMapShareError = (e, mapShareExtension) => {
|
|
601
|
-
this.emit('map-share-error', e, mapShareExtension)
|
|
602
|
-
}
|
|
603
|
-
|
|
604
|
-
project.once('close', () => {
|
|
605
|
-
this.#activeProjects.delete(projectPublicId)
|
|
606
|
-
project.removeListener('map-share', onMapShare)
|
|
607
|
-
project.removeListener('map-share-error', onMapShareError)
|
|
608
|
-
})
|
|
609
|
-
|
|
610
|
-
project.on('map-share', onMapShare)
|
|
611
|
-
|
|
612
|
-
project.on('map-share-error', onMapShareError)
|
|
613
|
-
|
|
614
625
|
return project
|
|
615
626
|
}
|
|
616
627
|
|
|
@@ -781,11 +792,6 @@ export class MapeoManager extends TypedEmitter {
|
|
|
781
792
|
throw ensureKnownError(e)
|
|
782
793
|
}
|
|
783
794
|
|
|
784
|
-
// Make sure to clean up when closed
|
|
785
|
-
project.once('close', () => {
|
|
786
|
-
this.#activeProjects.delete(projectPublicId)
|
|
787
|
-
})
|
|
788
|
-
|
|
789
795
|
// Only write info on invite if configured
|
|
790
796
|
if (!invitorWroteDeviceInfo) {
|
|
791
797
|
try {
|
|
@@ -1046,6 +1052,58 @@ export class MapeoManager extends TypedEmitter {
|
|
|
1046
1052
|
})
|
|
1047
1053
|
}
|
|
1048
1054
|
|
|
1055
|
+
/**
|
|
1056
|
+
* Send a map share offer to the peer with device ID `mapShare.receiverDeviceId`
|
|
1057
|
+
*
|
|
1058
|
+
* @param {MapShareSend} mapShare
|
|
1059
|
+
* @param {object} [options]
|
|
1060
|
+
* @param {boolean} [options.__testOnlyBypassValidation=false] Warning: Do not use!
|
|
1061
|
+
*/
|
|
1062
|
+
async sendMapShare(mapShare, { __testOnlyBypassValidation = false } = {}) {
|
|
1063
|
+
const { receiverDeviceId, ...mapShareData } = mapShare
|
|
1064
|
+
const receiverDeviceKey = Buffer.from(receiverDeviceId, 'hex')
|
|
1065
|
+
|
|
1066
|
+
/** @type {MapShareExtension} */
|
|
1067
|
+
// @ts-expect-error readonly fields being assigned as mutable
|
|
1068
|
+
const shareExtension = {
|
|
1069
|
+
...mapShareData,
|
|
1070
|
+
receiverDeviceKey,
|
|
1071
|
+
}
|
|
1072
|
+
if (!__testOnlyBypassValidation) {
|
|
1073
|
+
validateMapShareExtension(shareExtension)
|
|
1074
|
+
}
|
|
1075
|
+
await this.#localPeers.sendMapShare(receiverDeviceId, shareExtension)
|
|
1076
|
+
}
|
|
1077
|
+
|
|
1078
|
+
/**
|
|
1079
|
+
* @param {import('./local-peers.js').PeerInfo} sender
|
|
1080
|
+
* @param {MapShareExtension} mapShareBase
|
|
1081
|
+
*/
|
|
1082
|
+
async #handleMapShare(sender, mapShareBase) {
|
|
1083
|
+
const mapShareReceivedAt = Date.now()
|
|
1084
|
+
|
|
1085
|
+
validateMapShareExtension(mapShareBase)
|
|
1086
|
+
|
|
1087
|
+
const { receiverDeviceKey, ...mapShareData } = mapShareBase
|
|
1088
|
+
|
|
1089
|
+
const receiverDeviceId = receiverDeviceKey.toString('hex')
|
|
1090
|
+
|
|
1091
|
+
if (receiverDeviceId !== this.#deviceId) {
|
|
1092
|
+
throw new Error('Got map share intended for a different peer')
|
|
1093
|
+
}
|
|
1094
|
+
|
|
1095
|
+
/** @type {MapShare} */
|
|
1096
|
+
const mapShare = {
|
|
1097
|
+
...mapShareData,
|
|
1098
|
+
senderDeviceId: sender.deviceId,
|
|
1099
|
+
senderDeviceName: sender.name,
|
|
1100
|
+
mapShareReceivedAt,
|
|
1101
|
+
receiverDeviceId,
|
|
1102
|
+
}
|
|
1103
|
+
|
|
1104
|
+
this.emit('map-share', mapShare)
|
|
1105
|
+
}
|
|
1106
|
+
|
|
1049
1107
|
async getMapStyleJsonUrl() {
|
|
1050
1108
|
await timeoutPromise(Promise.resolve(this.#fastify.ready()), {
|
|
1051
1109
|
milliseconds: 1000,
|