@comapeo/core 3.2.0 → 4.1.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-api.d.ts +0 -48
- package/dist/blob-api.d.ts.map +1 -1
- package/dist/constants.d.ts +1 -0
- package/dist/constants.d.ts.map +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/mapeo-manager.d.ts.map +1 -1
- package/dist/mapeo-project.d.ts +55 -33
- package/dist/mapeo-project.d.ts.map +1 -1
- package/dist/schema/project.d.ts +15 -0
- package/dist/schema/project.d.ts.map +1 -1
- package/dist/types.d.ts +1 -1
- package/dist/types.d.ts.map +1 -1
- package/drizzle/project/0002_cooing_princess_powerful.sql +1 -0
- package/drizzle/project/meta/0002_snapshot.json +1274 -0
- package/drizzle/project/meta/_journal.json +7 -0
- package/package.json +6 -4
- package/src/blob-api.js +0 -26
- package/src/constants.js +3 -0
- package/src/mapeo-manager.js +34 -7
- package/src/mapeo-project.js +104 -46
- package/src/member-api.js +1 -1
- package/src/types.ts +1 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@comapeo/core",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "4.1.0",
|
|
4
4
|
"description": "Offline p2p mapping library",
|
|
5
5
|
"main": "src/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
@@ -112,12 +112,13 @@
|
|
|
112
112
|
"@comapeo/core2.0.1": "npm:@comapeo/core@2.0.1",
|
|
113
113
|
"@comapeo/ipc": "^2.1.0",
|
|
114
114
|
"@mapeo/default-config": "5.0.0",
|
|
115
|
-
"@mapeo/mock-data": "^
|
|
115
|
+
"@mapeo/mock-data": "^5.0.0",
|
|
116
116
|
"@sinonjs/fake-timers": "^10.0.2",
|
|
117
117
|
"@types/b4a": "^1.6.0",
|
|
118
118
|
"@types/bogon": "^1.0.2",
|
|
119
119
|
"@types/compact-encoding": "^2.15.0",
|
|
120
120
|
"@types/debug": "^4.1.8",
|
|
121
|
+
"@types/geojson": "^7946.0.16",
|
|
121
122
|
"@types/json-schema": "^7.0.11",
|
|
122
123
|
"@types/json-stable-stringify": "^1.0.36",
|
|
123
124
|
"@types/nanobench": "^3.0.0",
|
|
@@ -136,6 +137,7 @@
|
|
|
136
137
|
"drizzle-kit": "^0.20.14",
|
|
137
138
|
"eslint": "^8.57.0",
|
|
138
139
|
"execa": "^9.5.1",
|
|
140
|
+
"filter-obj": "^6.1.0",
|
|
139
141
|
"husky": "^8.0.0",
|
|
140
142
|
"iterpal": "^0.4.0",
|
|
141
143
|
"lint-staged": "^14.0.1",
|
|
@@ -160,7 +162,7 @@
|
|
|
160
162
|
},
|
|
161
163
|
"dependencies": {
|
|
162
164
|
"@comapeo/fallback-smp": "^1.0.0",
|
|
163
|
-
"@comapeo/schema": "
|
|
165
|
+
"@comapeo/schema": "2.0.0",
|
|
164
166
|
"@digidem/types": "^2.3.0",
|
|
165
167
|
"@fastify/error": "^3.4.1",
|
|
166
168
|
"@fastify/type-provider-typebox": "^4.1.0",
|
|
@@ -189,7 +191,7 @@
|
|
|
189
191
|
"json-stable-stringify": "^1.1.1",
|
|
190
192
|
"magic-bytes.js": "^1.10.0",
|
|
191
193
|
"map-obj": "^5.0.2",
|
|
192
|
-
"mime": "^4.0.
|
|
194
|
+
"mime": "^4.0.7",
|
|
193
195
|
"multi-core-indexer": "^1.0.0",
|
|
194
196
|
"p-defer": "^4.0.0",
|
|
195
197
|
"p-event": "^6.0.1",
|
package/src/blob-api.js
CHANGED
|
@@ -4,35 +4,9 @@ import { Transform, pipelinePromise as pipeline } from 'streamx'
|
|
|
4
4
|
import { createHash, randomBytes } from 'node:crypto'
|
|
5
5
|
/** @import { BlobId, BlobType } from './types.js' */
|
|
6
6
|
|
|
7
|
-
/**
|
|
8
|
-
* Location coordinate data. Based on [Expo's `LocationObjectCoords`][0].
|
|
9
|
-
* [0]: https://docs.expo.dev/versions/latest/sdk/location/#locationobjectcoords
|
|
10
|
-
*
|
|
11
|
-
* @typedef {object} LocationObjectCoords
|
|
12
|
-
* @prop {number | null} accuracy
|
|
13
|
-
* @prop {number | null} altitude
|
|
14
|
-
* @prop {number | null} altitudeAccuracy
|
|
15
|
-
* @prop {number | null} heading
|
|
16
|
-
* @prop {number} latitude
|
|
17
|
-
* @prop {number} longitude
|
|
18
|
-
* @prop {number | null} speed
|
|
19
|
-
*/
|
|
20
|
-
|
|
21
|
-
/**
|
|
22
|
-
* Location metadata for a blob. Based on [Expo's `LocationObject`][0].
|
|
23
|
-
* [0]: https://docs.expo.dev/versions/latest/sdk/location/#locationobject
|
|
24
|
-
*
|
|
25
|
-
* @typedef {object} LocationObject
|
|
26
|
-
* @prop {LocationObjectCoords} coords
|
|
27
|
-
* @prop {boolean} [mocked]
|
|
28
|
-
* @prop {number} timestamp
|
|
29
|
-
*/
|
|
30
|
-
|
|
31
7
|
/**
|
|
32
8
|
* @typedef {object} Metadata
|
|
33
9
|
* @prop {string} mimeType
|
|
34
|
-
* @prop {number} timestamp
|
|
35
|
-
* @prop {LocationObject} [location]
|
|
36
10
|
*/
|
|
37
11
|
|
|
38
12
|
export class BlobApi {
|
package/src/constants.js
CHANGED
|
@@ -35,3 +35,6 @@ export const SUPPORTED_CONFIG_VERSION = 1
|
|
|
35
35
|
|
|
36
36
|
// WARNING: This value is persisted. Be careful when changing it.
|
|
37
37
|
export const DRIZZLE_MIGRATIONS_TABLE = '__drizzle_migrations'
|
|
38
|
+
|
|
39
|
+
// Oldest possible time, ensure it gets overwritten with any updates
|
|
40
|
+
export const UNIX_EPOCH_DATE = new Date(0).toISOString()
|
package/src/mapeo-manager.js
CHANGED
|
@@ -38,6 +38,7 @@ import {
|
|
|
38
38
|
projectKeyToProjectInviteId,
|
|
39
39
|
projectKeyToPublicId,
|
|
40
40
|
} from './utils.js'
|
|
41
|
+
import { UNIX_EPOCH_DATE } from './constants.js'
|
|
41
42
|
import { openedNoiseSecretStream } from './lib/noise-secret-stream-helpers.js'
|
|
42
43
|
import { omit } from './lib/omit.js'
|
|
43
44
|
import { RandomAccessFilePool } from './core-manager/random-access-file-pool.js'
|
|
@@ -563,11 +564,13 @@ export class MapeoManager extends TypedEmitter {
|
|
|
563
564
|
(p) => p.projectId === projectId
|
|
564
565
|
)
|
|
565
566
|
|
|
567
|
+
if (!existingProject) continue
|
|
568
|
+
|
|
566
569
|
result.push(
|
|
567
570
|
deNullify({
|
|
568
571
|
projectId: projectPublicId,
|
|
569
|
-
createdAt: existingProject?.createdAt,
|
|
570
|
-
updatedAt: existingProject?.updatedAt,
|
|
572
|
+
createdAt: ignoreUnixDate(existingProject?.createdAt),
|
|
573
|
+
updatedAt: ignoreUnixDate(existingProject?.updatedAt),
|
|
571
574
|
name: existingProject?.name || projectInfo.name,
|
|
572
575
|
projectColor:
|
|
573
576
|
existingProject?.projectColor || projectInfo.projectColor,
|
|
@@ -658,6 +661,23 @@ export class MapeoManager extends TypedEmitter {
|
|
|
658
661
|
)
|
|
659
662
|
}
|
|
660
663
|
|
|
664
|
+
/** @type {import('drizzle-orm').InferInsertModel<typeof projectSettingsTable>} */
|
|
665
|
+
const settingsDoc = {
|
|
666
|
+
schemaName: 'projectSettings',
|
|
667
|
+
docId: projectId,
|
|
668
|
+
versionId: 'unknown',
|
|
669
|
+
originalVersionId: 'unknown',
|
|
670
|
+
createdAt: UNIX_EPOCH_DATE,
|
|
671
|
+
updatedAt: UNIX_EPOCH_DATE,
|
|
672
|
+
deleted: false,
|
|
673
|
+
links: [],
|
|
674
|
+
forks: [],
|
|
675
|
+
name: projectName,
|
|
676
|
+
projectDescription,
|
|
677
|
+
}
|
|
678
|
+
|
|
679
|
+
await this.#db.insert(projectSettingsTable).values([settingsDoc])
|
|
680
|
+
|
|
661
681
|
// 5. Wait for initial project sync
|
|
662
682
|
if (waitForSync) {
|
|
663
683
|
await this.#waitForInitialSync(project)
|
|
@@ -692,17 +712,15 @@ export class MapeoManager extends TypedEmitter {
|
|
|
692
712
|
* @returns {Promise<boolean>}
|
|
693
713
|
*/
|
|
694
714
|
async #waitForInitialSync(project, { timeoutMs = 5000 } = {}) {
|
|
695
|
-
const [ownRole,
|
|
715
|
+
const [ownRole, isProjectSettingsSynced] = await Promise.all([
|
|
696
716
|
project.$getOwnRole(),
|
|
697
|
-
project.$
|
|
717
|
+
project.$hasSyncedProjectSettings(),
|
|
698
718
|
])
|
|
699
719
|
const {
|
|
700
720
|
auth: { localState: authState },
|
|
701
721
|
config: { localState: configState },
|
|
702
722
|
} = project.$sync[kSyncState].getState()
|
|
703
723
|
const isRoleSynced = ownRole !== Roles.NO_ROLE
|
|
704
|
-
const isProjectSettingsSynced =
|
|
705
|
-
projectSettings !== MapeoProject.EMPTY_PROJECT_SETTINGS
|
|
706
724
|
// Assumes every project that someone is invited to has at least one record
|
|
707
725
|
// in the auth store - the row record for the invited device
|
|
708
726
|
const isAuthSynced = authState.want === 0 && authState.have > 0
|
|
@@ -721,7 +739,6 @@ export class MapeoManager extends TypedEmitter {
|
|
|
721
739
|
this.#l.log(
|
|
722
740
|
'Pending initial sync: role %s, projectSettings %o, auth %o, config %o',
|
|
723
741
|
isRoleSynced,
|
|
724
|
-
isProjectSettingsSynced,
|
|
725
742
|
isAuthSynced,
|
|
726
743
|
isConfigSynced
|
|
727
744
|
)
|
|
@@ -1012,3 +1029,13 @@ function validateProjectKeys(projectKeys) {
|
|
|
1012
1029
|
function hasSavedDeviceInfo(partialDeviceInfo) {
|
|
1013
1030
|
return Boolean(partialDeviceInfo.name)
|
|
1014
1031
|
}
|
|
1032
|
+
|
|
1033
|
+
/**
|
|
1034
|
+
* @param {string|undefined} date
|
|
1035
|
+
* @returns {string|null}
|
|
1036
|
+
*/
|
|
1037
|
+
function ignoreUnixDate(date) {
|
|
1038
|
+
if (date === UNIX_EPOCH_DATE) return null
|
|
1039
|
+
if (date === undefined) return null
|
|
1040
|
+
return date
|
|
1041
|
+
}
|
package/src/mapeo-project.js
CHANGED
|
@@ -6,10 +6,11 @@ import { discoveryKey } from 'hypercore-crypto'
|
|
|
6
6
|
import { TypedEmitter } from 'tiny-typed-emitter'
|
|
7
7
|
import ZipArchive from 'zip-stream-promise'
|
|
8
8
|
import * as b4a from 'b4a'
|
|
9
|
+
import mime from 'mime/lite'
|
|
9
10
|
// @ts-expect-error
|
|
10
11
|
import { Readable, pipelinePromise } from 'streamx'
|
|
11
12
|
|
|
12
|
-
import { NAMESPACES, NAMESPACE_SCHEMAS } from './constants.js'
|
|
13
|
+
import { NAMESPACES, NAMESPACE_SCHEMAS, UNIX_EPOCH_DATE } from './constants.js'
|
|
13
14
|
import { CoreManager } from './core-manager/index.js'
|
|
14
15
|
import { DataStore } from './datastore/index.js'
|
|
15
16
|
import { DataType, kCreateWithDocId } from './datatype/index.js'
|
|
@@ -69,6 +70,10 @@ import { createWriteStream } from 'fs'
|
|
|
69
70
|
/** @typedef {Omit<ProjectSettingsValue, 'schemaName'>} EditableProjectSettings */
|
|
70
71
|
/** @typedef {ProjectSettingsValue['configMetadata']} ConfigMetadata */
|
|
71
72
|
/** @typedef {Map<string,Attachment>} SeenAttachments*/
|
|
73
|
+
/** @typedef {object} BlobRef
|
|
74
|
+
* @prop {string|undefined} mimeType
|
|
75
|
+
* @prop {BlobId} blobId
|
|
76
|
+
*/
|
|
72
77
|
|
|
73
78
|
const CORESTORE_STORAGE_FOLDER_NAME = 'corestore'
|
|
74
79
|
const INDEXER_STORAGE_FOLDER_NAME = 'indexer'
|
|
@@ -83,8 +88,6 @@ export const kClearDataIfLeft = Symbol('clear data if left project')
|
|
|
83
88
|
export const kSetIsArchiveDevice = Symbol('set isArchiveDevice')
|
|
84
89
|
export const kIsArchiveDevice = Symbol('isArchiveDevice (temp - test only)')
|
|
85
90
|
export const kGeoJSONFileName = Symbol('geoJSONFileName')
|
|
86
|
-
export const kExportGeoJSONStream = Symbol('exportGeoJSONStream')
|
|
87
|
-
export const kExportZipStream = Symbol('exportZipStream')
|
|
88
91
|
|
|
89
92
|
const EMPTY_PROJECT_SETTINGS = Object.freeze({})
|
|
90
93
|
|
|
@@ -658,6 +661,21 @@ export class MapeoProject extends TypedEmitter {
|
|
|
658
661
|
}
|
|
659
662
|
}
|
|
660
663
|
|
|
664
|
+
/**
|
|
665
|
+
* @returns {Promise<boolean>}
|
|
666
|
+
*/
|
|
667
|
+
async $hasSyncedProjectSettings() {
|
|
668
|
+
try {
|
|
669
|
+
const settings = await this.#dataTypes.projectSettings.getByDocId(
|
|
670
|
+
this.#projectId
|
|
671
|
+
)
|
|
672
|
+
|
|
673
|
+
return settings.createdAt !== UNIX_EPOCH_DATE
|
|
674
|
+
} catch (e) {
|
|
675
|
+
return false
|
|
676
|
+
}
|
|
677
|
+
}
|
|
678
|
+
|
|
661
679
|
/**
|
|
662
680
|
* @returns {Promise<undefined | string>}
|
|
663
681
|
*/
|
|
@@ -786,40 +804,43 @@ export class MapeoProject extends TypedEmitter {
|
|
|
786
804
|
}
|
|
787
805
|
}
|
|
788
806
|
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
807
|
+
const metadataCoords = observation.metadata?.position?.coords
|
|
808
|
+
const altitude = metadataCoords?.altitude
|
|
809
|
+
|
|
810
|
+
/** @type {[number, number] | [number, number, number] | null} */
|
|
811
|
+
let coordinates = null
|
|
812
|
+
|
|
813
|
+
// Prioritize using the observation's `lat` and `lon` fields
|
|
814
|
+
if (typeof lat === 'number' && typeof lon === 'number') {
|
|
815
|
+
coordinates =
|
|
816
|
+
typeof altitude === 'number' ? [lon, lat, altitude] : [lon, lat]
|
|
817
|
+
} else {
|
|
818
|
+
// Fall back to using the observation metadata's position if possible
|
|
819
|
+
if (
|
|
820
|
+
typeof metadataCoords?.latitude === 'number' &&
|
|
821
|
+
typeof metadataCoords?.longitude === 'number'
|
|
822
|
+
) {
|
|
823
|
+
coordinates =
|
|
824
|
+
typeof altitude === 'number'
|
|
825
|
+
? [metadataCoords.longitude, metadataCoords.latitude, altitude]
|
|
826
|
+
: [metadataCoords.longitude, metadataCoords.latitude]
|
|
798
827
|
}
|
|
799
828
|
}
|
|
800
829
|
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
830
|
+
/** @type {import('geojson').Feature<import('geojson').Point | null>} */
|
|
831
|
+
const feature = {
|
|
832
|
+
type: 'Feature',
|
|
833
|
+
properties: observation,
|
|
834
|
+
geometry: coordinates
|
|
835
|
+
? {
|
|
836
|
+
type: 'Point',
|
|
837
|
+
coordinates,
|
|
838
|
+
}
|
|
839
|
+
: null,
|
|
804
840
|
}
|
|
805
|
-
const hasLatLon =
|
|
806
|
-
typeof longitude === 'number' && typeof latitude === 'number'
|
|
807
|
-
const geometry = hasLatLon
|
|
808
|
-
? {
|
|
809
|
-
type: 'Point',
|
|
810
|
-
coordinates,
|
|
811
|
-
}
|
|
812
|
-
: null
|
|
813
841
|
const comma = first ? '' : ','
|
|
814
842
|
first = false
|
|
815
|
-
yield b4a.from(
|
|
816
|
-
`${comma}\n ` +
|
|
817
|
-
JSON.stringify({
|
|
818
|
-
type: 'Feature',
|
|
819
|
-
properties: observation,
|
|
820
|
-
geometry,
|
|
821
|
-
})
|
|
822
|
-
)
|
|
843
|
+
yield b4a.from(`${comma}\n ` + JSON.stringify(feature))
|
|
823
844
|
}
|
|
824
845
|
}
|
|
825
846
|
|
|
@@ -941,7 +962,7 @@ export class MapeoProject extends TypedEmitter {
|
|
|
941
962
|
* @param {string} [options.lang]
|
|
942
963
|
* @returns {Readable<Buffer | Uint8Array>}
|
|
943
964
|
*/
|
|
944
|
-
|
|
965
|
+
#exportGeoJSONStream({
|
|
945
966
|
observations = true,
|
|
946
967
|
tracks = true,
|
|
947
968
|
lang,
|
|
@@ -1027,7 +1048,7 @@ export class MapeoProject extends TypedEmitter {
|
|
|
1027
1048
|
) {
|
|
1028
1049
|
const fileName = await this[kGeoJSONFileName](observations, tracks)
|
|
1029
1050
|
const filePath = path.join(exportFolder, fileName)
|
|
1030
|
-
const source = this
|
|
1051
|
+
const source = this.#exportGeoJSONStream({ observations, tracks, lang })
|
|
1031
1052
|
const sink = createWriteStream(filePath)
|
|
1032
1053
|
await pipelinePromise(source, sink)
|
|
1033
1054
|
|
|
@@ -1036,15 +1057,37 @@ export class MapeoProject extends TypedEmitter {
|
|
|
1036
1057
|
|
|
1037
1058
|
/**
|
|
1038
1059
|
* @param {Attachment} attachment
|
|
1039
|
-
* @returns {Promise<null |
|
|
1060
|
+
* @returns {Promise<null | BlobRef>}
|
|
1040
1061
|
*/
|
|
1041
|
-
async #
|
|
1062
|
+
async #tryGetAttachmentBlob(attachment) {
|
|
1042
1063
|
// Audio must not have variants
|
|
1043
1064
|
for (const variant of VARIANT_EXPORT_ORDER) {
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
|
|
1065
|
+
try {
|
|
1066
|
+
const blobId = buildBlobId(attachment, variant)
|
|
1067
|
+
const entry = await this.#blobStore.entry(blobId)
|
|
1068
|
+
if (!entry) continue
|
|
1069
|
+
const metadata = entry.value.metadata
|
|
1070
|
+
if (!metadata || typeof metadata !== 'object') continue
|
|
1071
|
+
let mimeType = undefined
|
|
1072
|
+
if ('mimeType' in metadata) {
|
|
1073
|
+
if (typeof metadata.mimeType === 'string') {
|
|
1074
|
+
mimeType = metadata.mimeType
|
|
1075
|
+
} else {
|
|
1076
|
+
this.#l.log('Invalid type for mimeType in blob', blobId, entry)
|
|
1077
|
+
continue
|
|
1078
|
+
}
|
|
1079
|
+
}
|
|
1080
|
+
return { blobId, mimeType }
|
|
1081
|
+
} catch (e) {
|
|
1082
|
+
if (!(e instanceof Error)) throw e
|
|
1083
|
+
this.#l.log(
|
|
1084
|
+
'Error loading blob id for attachment',
|
|
1085
|
+
attachment,
|
|
1086
|
+
variant,
|
|
1087
|
+
e.message
|
|
1088
|
+
)
|
|
1089
|
+
continue
|
|
1090
|
+
}
|
|
1048
1091
|
}
|
|
1049
1092
|
|
|
1050
1093
|
return null
|
|
@@ -1066,7 +1109,7 @@ export class MapeoProject extends TypedEmitter {
|
|
|
1066
1109
|
// GeoJSON
|
|
1067
1110
|
const geoJSONFileName = await this[kGeoJSONFileName](observations, tracks)
|
|
1068
1111
|
const seenAttachments = new Map()
|
|
1069
|
-
const geoJSONStream = this
|
|
1112
|
+
const geoJSONStream = this.#exportGeoJSONStream({
|
|
1070
1113
|
observations,
|
|
1071
1114
|
tracks,
|
|
1072
1115
|
lang,
|
|
@@ -1079,16 +1122,31 @@ export class MapeoProject extends TypedEmitter {
|
|
|
1079
1122
|
const missingAttachments = []
|
|
1080
1123
|
// Attachments
|
|
1081
1124
|
if (attachments) {
|
|
1082
|
-
const mediaFolder = this.#exportPrefix('Media') + '/'
|
|
1125
|
+
const mediaFolder = (await this.#exportPrefix('Media')) + '/'
|
|
1083
1126
|
for (const attachment of seenAttachments.values()) {
|
|
1084
|
-
const
|
|
1085
|
-
if (
|
|
1127
|
+
const ref = await this.#tryGetAttachmentBlob(attachment)
|
|
1128
|
+
if (ref === null) {
|
|
1086
1129
|
missingAttachments.push(attachment)
|
|
1087
1130
|
continue
|
|
1088
1131
|
}
|
|
1089
1132
|
|
|
1133
|
+
const { blobId, mimeType } = ref
|
|
1134
|
+
let extensionString = ''
|
|
1135
|
+
if (mimeType) {
|
|
1136
|
+
const extension = mime.getExtension(mimeType)
|
|
1137
|
+
if (extension) {
|
|
1138
|
+
extensionString = '.' + extension
|
|
1139
|
+
} else {
|
|
1140
|
+
this.#l.log('Got unknown mime type in attachment blob', attachment)
|
|
1141
|
+
}
|
|
1142
|
+
}
|
|
1143
|
+
|
|
1090
1144
|
const stream = this.#blobStore.createReadStream(blobId)
|
|
1091
|
-
const name =
|
|
1145
|
+
const name = path.posix.join(
|
|
1146
|
+
mediaFolder,
|
|
1147
|
+
blobId.variant,
|
|
1148
|
+
`${attachment.name}${extensionString}`
|
|
1149
|
+
)
|
|
1092
1150
|
|
|
1093
1151
|
// @ts-expect-error
|
|
1094
1152
|
await archive.entry(stream, { name })
|
|
@@ -1118,7 +1176,7 @@ export class MapeoProject extends TypedEmitter {
|
|
|
1118
1176
|
* @param {string} [options.lang]
|
|
1119
1177
|
* @returns {Readable<Buffer | Uint8Array>}
|
|
1120
1178
|
*/
|
|
1121
|
-
|
|
1179
|
+
#exportZipStream({
|
|
1122
1180
|
observations = true,
|
|
1123
1181
|
tracks = true,
|
|
1124
1182
|
attachments = true,
|
|
@@ -1153,7 +1211,7 @@ export class MapeoProject extends TypedEmitter {
|
|
|
1153
1211
|
) {
|
|
1154
1212
|
const fileName = await this.#zipFileName(observations, tracks)
|
|
1155
1213
|
const filePath = path.join(exportFolder, fileName)
|
|
1156
|
-
const source = this
|
|
1214
|
+
const source = this.#exportZipStream({
|
|
1157
1215
|
observations,
|
|
1158
1216
|
tracks,
|
|
1159
1217
|
attachments,
|
package/src/member-api.js
CHANGED
|
@@ -472,6 +472,7 @@ export class MemberApi extends TypedEmitter {
|
|
|
472
472
|
)
|
|
473
473
|
}
|
|
474
474
|
|
|
475
|
+
const onClosePromise = pEvent(websocket, 'close')
|
|
475
476
|
const onErrorPromise = pEvent(websocket, 'error')
|
|
476
477
|
|
|
477
478
|
const replicationStream = this.#getReplicationStream()
|
|
@@ -494,7 +495,6 @@ export class MemberApi extends TypedEmitter {
|
|
|
494
495
|
websocket.close()
|
|
495
496
|
throw errorEvent.error
|
|
496
497
|
} else {
|
|
497
|
-
const onClosePromise = pEvent(websocket, 'close')
|
|
498
498
|
onErrorPromise.cancel()
|
|
499
499
|
websocket.close()
|
|
500
500
|
await onClosePromise
|
package/src/types.ts
CHANGED
|
@@ -162,7 +162,7 @@ export type BlobStoreEntriesStream = Readable & {
|
|
|
162
162
|
>
|
|
163
163
|
}
|
|
164
164
|
|
|
165
|
-
export type Attachment = Observation['attachments'][
|
|
165
|
+
export type Attachment = Observation['attachments'][number]
|
|
166
166
|
|
|
167
167
|
export type StringToTaggedUnion<T extends string> = {
|
|
168
168
|
[K in T]: {
|