@comapeo/core 5.0.0 → 5.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/mapeo-manager.d.ts.map +1 -1
- package/dist/mapeo-project.d.ts +45 -11
- package/dist/mapeo-project.d.ts.map +1 -1
- package/dist/member-api.d.ts +9 -0
- package/dist/member-api.d.ts.map +1 -1
- package/dist/roles.d.ts +12 -1
- package/dist/roles.d.ts.map +1 -1
- package/dist/schema/project.d.ts +7 -1
- package/dist/schema/project.d.ts.map +1 -1
- package/dist/translation-api.d.ts +1 -1
- package/dist/translation-api.d.ts.map +1 -1
- package/drizzle/project/0003_lying_piledriver.sql +1 -0
- package/drizzle/project/meta/0003_snapshot.json +1306 -0
- package/drizzle/project/meta/_journal.json +7 -0
- package/package.json +2 -2
- package/src/mapeo-manager.js +0 -2
- package/src/mapeo-project.js +39 -9
- package/src/member-api.js +20 -0
- package/src/roles.js +17 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@comapeo/core",
|
|
3
|
-
"version": "5.
|
|
3
|
+
"version": "5.1.0",
|
|
4
4
|
"description": "Offline p2p mapping library",
|
|
5
5
|
"main": "src/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
@@ -164,7 +164,7 @@
|
|
|
164
164
|
},
|
|
165
165
|
"dependencies": {
|
|
166
166
|
"@comapeo/fallback-smp": "^1.0.0",
|
|
167
|
-
"@comapeo/schema": "2.
|
|
167
|
+
"@comapeo/schema": "2.2.0",
|
|
168
168
|
"@digidem/types": "^2.3.0",
|
|
169
169
|
"@fastify/error": "^3.4.1",
|
|
170
170
|
"@fastify/type-provider-typebox": "^4.1.0",
|
package/src/mapeo-manager.js
CHANGED
|
@@ -1056,8 +1056,6 @@ export class MapeoManager extends TypedEmitter {
|
|
|
1056
1056
|
const project = await this.getProject(projectPublicId)
|
|
1057
1057
|
|
|
1058
1058
|
await project[kProjectLeave]()
|
|
1059
|
-
|
|
1060
|
-
this.#activeProjects.delete(projectPublicId)
|
|
1061
1059
|
}
|
|
1062
1060
|
|
|
1063
1061
|
async getMapStyleJsonUrl() {
|
package/src/mapeo-project.js
CHANGED
|
@@ -69,6 +69,7 @@ import { createWriteStream } from 'fs'
|
|
|
69
69
|
import ensureError from 'ensure-error'
|
|
70
70
|
/** @import { ProjectSettingsValue, Observation, Track } from '@comapeo/schema' */
|
|
71
71
|
/** @import { Attachment, CoreStorage, BlobFilter, BlobId, BlobStoreEntriesStream, KeyPair, Namespace, ReplicationStream, GenericBlobFilter, MapeoValueMap, MapeoDocMap } from './types.js' */
|
|
72
|
+
/** @import {Role} from './roles.js' */
|
|
72
73
|
/** @typedef {Omit<ProjectSettingsValue, 'schemaName'>} EditableProjectSettings */
|
|
73
74
|
/** @typedef {ProjectSettingsValue['configMetadata']} ConfigMetadata */
|
|
74
75
|
/** @typedef {Map<string,Attachment>} SeenAttachments*/
|
|
@@ -109,7 +110,18 @@ const EMPTY_PROJECT_SETTINGS = Object.freeze({ sendStats: false })
|
|
|
109
110
|
const VARIANT_EXPORT_ORDER = ['original', 'preview', 'thumbnail']
|
|
110
111
|
|
|
111
112
|
/**
|
|
112
|
-
* @
|
|
113
|
+
* @typedef RoleChangeEvent
|
|
114
|
+
* @property {Role & {reason: string | null}} role
|
|
115
|
+
*/
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* @typedef {object} ProjectEvents
|
|
119
|
+
* @property {() => void} close Project resources have been cleared up
|
|
120
|
+
* @property {(changeEvent: RoleChangeEvent) => void} own-role-change
|
|
121
|
+
*/
|
|
122
|
+
|
|
123
|
+
/**
|
|
124
|
+
* @extends {TypedEmitter<ProjectEvents>}
|
|
113
125
|
*/
|
|
114
126
|
export class MapeoProject extends TypedEmitter {
|
|
115
127
|
#projectKey
|
|
@@ -506,6 +518,16 @@ export class MapeoProject extends TypedEmitter {
|
|
|
506
518
|
// for the core.
|
|
507
519
|
localPeers.on('discovery-key', onDiscoverykey)
|
|
508
520
|
|
|
521
|
+
this.#roles.on('update', (roleDocIds) => {
|
|
522
|
+
for (const roleDocId of roleDocIds) {
|
|
523
|
+
// Ignore docs not about ourselves
|
|
524
|
+
if (roleDocId !== this.#deviceId) continue
|
|
525
|
+
this.#handleRoleChange().catch((e) => {
|
|
526
|
+
this.#l.log(`Error: Could not handle role change`, ensureError(e))
|
|
527
|
+
})
|
|
528
|
+
}
|
|
529
|
+
})
|
|
530
|
+
|
|
509
531
|
this.once('close', () => {
|
|
510
532
|
localPeers.off('peer-add', onPeerAdd)
|
|
511
533
|
localPeers.off('discovery-key', onDiscoverykey)
|
|
@@ -718,8 +740,18 @@ export class MapeoProject extends TypedEmitter {
|
|
|
718
740
|
return (await this.$getProjectSettings()).name
|
|
719
741
|
}
|
|
720
742
|
|
|
743
|
+
/**
|
|
744
|
+
* @returns {Promise<Role & {reason: string | null}>}
|
|
745
|
+
*/
|
|
721
746
|
async $getOwnRole() {
|
|
722
|
-
|
|
747
|
+
const reason = await this.#roles.getRoleReason(this.#deviceId)
|
|
748
|
+
const role = await this.#roles.getRole(this.#deviceId)
|
|
749
|
+
return { ...role, reason }
|
|
750
|
+
}
|
|
751
|
+
|
|
752
|
+
async #handleRoleChange() {
|
|
753
|
+
const role = await this.$getOwnRole()
|
|
754
|
+
this.emit('own-role-change', { role })
|
|
723
755
|
}
|
|
724
756
|
|
|
725
757
|
/**
|
|
@@ -1280,12 +1312,6 @@ export class MapeoProject extends TypedEmitter {
|
|
|
1280
1312
|
async #throwIfCannotLeaveProject() {
|
|
1281
1313
|
const roleDocs = await this.#dataTypes.role.getMany()
|
|
1282
1314
|
|
|
1283
|
-
const ownRole = roleDocs.find(({ docId }) => this.#deviceId === docId)
|
|
1284
|
-
|
|
1285
|
-
if (ownRole?.roleId === BLOCKED_ROLE_ID) {
|
|
1286
|
-
throw new Error('Cannot leave a project as a blocked device')
|
|
1287
|
-
}
|
|
1288
|
-
|
|
1289
1315
|
const allRoles = await this.#roles.getAll()
|
|
1290
1316
|
|
|
1291
1317
|
const isOnlyDevice = allRoles.size <= 1
|
|
@@ -1311,9 +1337,13 @@ export class MapeoProject extends TypedEmitter {
|
|
|
1311
1337
|
}
|
|
1312
1338
|
|
|
1313
1339
|
async [kProjectLeave]() {
|
|
1340
|
+
const ownRole = await this.$getOwnRole()
|
|
1341
|
+
|
|
1314
1342
|
await this.#throwIfCannotLeaveProject()
|
|
1315
1343
|
|
|
1316
|
-
|
|
1344
|
+
if (ownRole.roleId !== BLOCKED_ROLE_ID) {
|
|
1345
|
+
await this.#roles.assignRole(this.#deviceId, LEFT_ROLE_ID)
|
|
1346
|
+
}
|
|
1317
1347
|
|
|
1318
1348
|
await this[kClearData]()
|
|
1319
1349
|
}
|
package/src/member-api.js
CHANGED
|
@@ -22,6 +22,7 @@ import { InviteAbortedError, ProjectDetailsSendFailError } from './errors.js'
|
|
|
22
22
|
import { wsCoreReplicator } from './lib/ws-core-replicator.js'
|
|
23
23
|
import {
|
|
24
24
|
BLOCKED_ROLE_ID,
|
|
25
|
+
LEFT_ROLE_ID,
|
|
25
26
|
MEMBER_ROLE_ID,
|
|
26
27
|
ROLES,
|
|
27
28
|
isRoleIdForNewInvite,
|
|
@@ -355,6 +356,25 @@ export class MemberApi extends TypedEmitter {
|
|
|
355
356
|
})
|
|
356
357
|
}
|
|
357
358
|
|
|
359
|
+
/**
|
|
360
|
+
* Remove a member from the project
|
|
361
|
+
* @param {string} deviceId Device id of member to remove
|
|
362
|
+
* @param {object} [opts]
|
|
363
|
+
* @param {string} opts.reason
|
|
364
|
+
*/
|
|
365
|
+
async remove(deviceId, opts) {
|
|
366
|
+
const member = await this.getById(deviceId)
|
|
367
|
+
const { roleId } = member.role
|
|
368
|
+
|
|
369
|
+
if (roleId === BLOCKED_ROLE_ID || roleId === LEFT_ROLE_ID) {
|
|
370
|
+
throw new ErrorWithCode('ALREADY_BLOCKED', 'Member already blocked')
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
// Add blocked role to project
|
|
374
|
+
// Should error if you don't have permission to do so
|
|
375
|
+
await this.#roles.assignRole(deviceId, BLOCKED_ROLE_ID, opts)
|
|
376
|
+
}
|
|
377
|
+
|
|
358
378
|
/**
|
|
359
379
|
* Remove a server peer. Only works when the peer is reachable
|
|
360
380
|
*
|
package/src/roles.js
CHANGED
|
@@ -292,6 +292,19 @@ export class Roles extends TypedEmitter {
|
|
|
292
292
|
return ROLES[roleId]
|
|
293
293
|
}
|
|
294
294
|
|
|
295
|
+
/**
|
|
296
|
+
* Get the reason for the role of `deviceId` (if it exists).
|
|
297
|
+
*
|
|
298
|
+
* @param {string} deviceId
|
|
299
|
+
* @returns {Promise<string | null>}
|
|
300
|
+
*/
|
|
301
|
+
async getRoleReason(deviceId) {
|
|
302
|
+
const roleRecord = await this.#dataType
|
|
303
|
+
.getByDocId(deviceId)
|
|
304
|
+
.catch(nullIfNotFound)
|
|
305
|
+
return roleRecord?.reason ?? null
|
|
306
|
+
}
|
|
307
|
+
|
|
295
308
|
/**
|
|
296
309
|
* Get roles of all devices in the project. For your own device, if you have
|
|
297
310
|
* not yet synced your own role record, the "no role" capabilties is
|
|
@@ -346,8 +359,10 @@ export class Roles extends TypedEmitter {
|
|
|
346
359
|
*
|
|
347
360
|
* @param {string} deviceId
|
|
348
361
|
* @param {RoleIdAssignableToAnyone} roleId
|
|
362
|
+
* @param {object} [opts]
|
|
363
|
+
* @param {string} opts.reason
|
|
349
364
|
*/
|
|
350
|
-
async assignRole(deviceId, roleId) {
|
|
365
|
+
async assignRole(deviceId, roleId, opts) {
|
|
351
366
|
assert(
|
|
352
367
|
isRoleIdAssignableToAnyone(roleId),
|
|
353
368
|
`Role ID should be assignable to anyone but got ${roleId}`
|
|
@@ -398,6 +413,7 @@ export class Roles extends TypedEmitter {
|
|
|
398
413
|
schemaName: 'role',
|
|
399
414
|
roleId,
|
|
400
415
|
fromIndex,
|
|
416
|
+
reason: opts?.reason,
|
|
401
417
|
}
|
|
402
418
|
)
|
|
403
419
|
} else {
|