From 00c23fc0c67f6e31bf572b9667cc7a10b32917d2 Mon Sep 17 00:00:00 2001 From: Evan Hahn Date: Tue, 27 Aug 2024 14:43:14 +0000 Subject: [PATCH] feat: emit updates from CoreOwnership and Roles, add CoreOwnership#get This is part of the solution for [#268]. [#268]: https://github.com/digidem/mapeo-core-next/issues/268 Co-Authored-By: Gregor MacLennan --- src/core-ownership.js | 36 +++++++++++++++++++++++++++++++----- src/datatype/index.d.ts | 5 +++-- src/datatype/index.js | 5 +++-- src/roles.js | 15 +++++++++++++-- 4 files changed, 50 insertions(+), 11 deletions(-) diff --git a/src/core-ownership.js b/src/core-ownership.js index 96d5011c..5d74432c 100644 --- a/src/core-ownership.js +++ b/src/core-ownership.js @@ -3,12 +3,18 @@ import { parseVersionId } from '@mapeo/schema' import { defaultGetWinner } from '@mapeo/sqlite-indexer' import assert from 'node:assert/strict' import sodium from 'sodium-universal' -import { kTable, kSelect, kCreateWithDocId } from './datatype/index.js' +import { + kTable, + kSelect, + kCreateWithDocId, + kDataStore, +} from './datatype/index.js' import { eq, or } from 'drizzle-orm' import mapObject from 'map-obj' import { discoveryKey } from 'hypercore-crypto' import pDefer from 'p-defer' import { NAMESPACES } from './constants.js' +import { TypedEmitter } from 'tiny-typed-emitter' /** * @import { * CoreOwnershipWithSignatures, @@ -18,7 +24,15 @@ import { NAMESPACES } from './constants.js' * } from './types.js' */ -export class CoreOwnership { +/** + * @typedef {object} CoreOwnershipEvents + * @property {(docIds: Set) => void} update Emitted when new coreOwnership records are indexed + */ + +/** + * @extends {TypedEmitter} + */ +export class CoreOwnership extends TypedEmitter { #dataType #ownershipWriteDone /** @@ -35,8 +49,9 @@ export class CoreOwnership { * @param {KeyPair} opts.identityKeypair */ constructor({ dataType, coreKeypairs, identityKeypair }) { + super() this.#dataType = dataType - const authWriterCore = dataType.writerCore + const authWriterCore = dataType[kDataStore].writerCore const deferred = pDefer() this.#ownershipWriteDone = deferred.promise @@ -55,6 +70,8 @@ export class CoreOwnership { } else { authWriterCore.once('ready', writeOwnership) } + + dataType[kDataStore].on('coreOwnership', this.emit.bind(this, 'update')) } /** @@ -85,11 +102,20 @@ export class CoreOwnership { * @returns {Promise} coreId of core belonging to `deviceId` for `namespace` */ async getCoreId(deviceId, namespace) { - await this.#ownershipWriteDone - const result = await this.#dataType.getByDocId(deviceId) + const result = await this.get(deviceId) return result[`${namespace}CoreId`] } + /** + * Get capabilities for a given deviceId + * + * @param {string} deviceId + */ + async get(deviceId) { + await this.#ownershipWriteDone + return this.#dataType.getByDocId(deviceId) + } + /** * * @param {KeyPair} identityKeypair diff --git a/src/datatype/index.d.ts b/src/datatype/index.d.ts index 0239185d..e2966854 100644 --- a/src/datatype/index.d.ts +++ b/src/datatype/index.d.ts @@ -34,6 +34,7 @@ export interface DataTypeEvents { export const kCreateWithDocId: unique symbol export const kSelect: unique symbol export const kTable: unique symbol +export const kDataStore: unique symbol type OmitUnion = T extends any ? Omit : never type ExcludeSchema< @@ -62,12 +63,12 @@ export class DataType< get [kTable](): TTable + get [kDataStore](): TDataStore + get schemaName(): TSchemaName get namespace(): TDataStore.namespace - get writerCore(): Hypercore<'binary', Buffer> - [kCreateWithDocId]( docId: string, value: diff --git a/src/datatype/index.js b/src/datatype/index.js index 5def0fef..94b5042f 100644 --- a/src/datatype/index.js +++ b/src/datatype/index.js @@ -45,6 +45,7 @@ function generateDate() { export const kCreateWithDocId = Symbol('kCreateWithDocId') export const kSelect = Symbol('select') export const kTable = Symbol('table') +export const kDataStore = Symbol('dataStore') /** * @template {import('../datastore/index.js').DataStore} TDataStore @@ -117,8 +118,8 @@ export class DataType extends TypedEmitter { return this.#dataStore.namespace } - get writerCore() { - return this.#dataStore.writerCore + get [kDataStore]() { + return this.#dataStore } /** diff --git a/src/roles.js b/src/roles.js index 6a6fcd33..b2304eb4 100644 --- a/src/roles.js +++ b/src/roles.js @@ -1,7 +1,8 @@ import { currentSchemaVersions } from '@mapeo/schema' import mapObject from 'map-obj' -import { kCreateWithDocId } from './datatype/index.js' +import { kCreateWithDocId, kDataStore } from './datatype/index.js' import { assert, setHas } from './utils.js' +import { TypedEmitter } from 'tiny-typed-emitter' /** @import { Namespace } from './types.js' */ // Randomly generated 8-byte encoded as hex @@ -214,7 +215,15 @@ export const ROLES = { [NO_ROLE_ID]: NO_ROLE, } -export class Roles { +/** + * @typedef {object} RolesEvents + * @property {(docIds: Set) => void} update Emitted when new role records are indexed + */ + +/** + * @extends {TypedEmitter} + */ +export class Roles extends TypedEmitter { #dataType #coreOwnership #coreManager @@ -239,11 +248,13 @@ export class Roles { * @param {Buffer} opts.deviceKey public key of this device */ constructor({ dataType, coreOwnership, coreManager, projectKey, deviceKey }) { + super() this.#dataType = dataType this.#coreOwnership = coreOwnership this.#coreManager = coreManager this.#projectCreatorAuthCoreId = projectKey.toString('hex') this.#ownDeviceId = deviceKey.toString('hex') + dataType[kDataStore].on('role', this.emit.bind(this, 'update')) } /**