diff --git a/.gitignore b/.gitignore index 1531bdf..7ad9e67 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,9 @@ node_modules +build dist .docs .coverage +node_modules package-lock.json yarn.lock +.vscode diff --git a/package.json b/package.json index 4f8c5ee..c96abc0 100644 --- a/package.json +++ b/package.json @@ -23,22 +23,6 @@ }, "type": "module", "types": "./dist/src/index.d.ts", - "typesVersions": { - "*": { - "*": [ - "*", - "dist/*", - "dist/src/*", - "dist/src/*/index" - ], - "src/*": [ - "*", - "dist/*", - "dist/src/*", - "dist/src/*/index" - ] - } - }, "files": [ "src", "dist", @@ -162,18 +146,20 @@ "@libp2p/interface-pubsub": "^3.0.0", "@libp2p/interfaces": "^3.2.0", "@libp2p/logger": "^2.0.0", - "datastore-core": "^8.0.1", - "interface-datastore": "^7.0.0", + "datastore-core": "^9.1.0", + "interface-datastore": "^8.2.0", + "interface-store": "^5.1.0", "uint8arrays": "^4.0.2" }, "devDependencies": { "@libp2p/floodsub": "^6.0.0", + "@libp2p/interface-connection-manager": "^1.3.8", "@libp2p/interface-mocks": "^9.0.0", "@libp2p/interface-peer-id": "^2.0.0", "@libp2p/interface-registrar": "^2.0.3", "@libp2p/peer-id-factory": "^2.0.0", "@libp2p/record": "^3.0.0", - "aegir": "^37.10.0", + "aegir": "^38.1.8", "p-wait-for": "^5.0.0", "sinon": "^15.0.1" } diff --git a/src/index.js b/src/index.ts similarity index 64% rename from src/index.js rename to src/index.ts index 4fdd8d0..1a174a4 100644 --- a/src/index.js +++ b/src/index.ts @@ -1,37 +1,34 @@ -import { Key } from 'interface-datastore' +import { Datastore, Key } from 'interface-datastore' import { BaseDatastore } from 'datastore-core' import { encodeBase32, keyToTopic, topicToKey } from './utils.js' import { equals as uint8ArrayEquals } from 'uint8arrays/equals' import { CodeError } from '@libp2p/interfaces/errors' import { logger } from '@libp2p/logger' +import type { Message, PubSub } from '@libp2p/interface-pubsub' +import type { PeerId } from '@libp2p/interface-peer-id' +import type { SelectFn, ValidateFn } from '@libp2p/interface-dht' +import type { AbortOptions } from 'interface-store' const log = logger('datastore-pubsub:publisher') +export interface SubscriptionKeyFn { (key: Uint8Array): Promise | Uint8Array } + /** - * @typedef {import('@libp2p/interface-peer-id').PeerId} PeerId - * @typedef {import('./types').SubscriptionKeyFn} SubscriptionKeyFn - * @typedef {import('@libp2p/interface-pubsub').Message} PubSubMessage - * @typedef {import('@libp2p/interfaces').AbortOptions} AbortOptions + * DatastorePubsub is responsible for providing an api for pubsub to be used as a datastore with + * [TieredDatastore]{@link https://github.com/ipfs/js-datastore-core/blob/master/src/tiered.js} */ - -// DatastorePubsub is responsible for providing an api for pubsub to be used as a datastore with -// [TieredDatastore]{@link https://github.com/ipfs/js-datastore-core/blob/master/src/tiered.js} export class PubSubDatastore extends BaseDatastore { - /** - * Creates an instance of DatastorePubsub. - * - * @param {import('@libp2p/interface-pubsub').PubSub} pubsub - pubsub implementation - * @param {import('interface-datastore').Datastore} datastore - datastore instance - * @param {PeerId} peerId - peer-id instance - * @param {import('@libp2p/interface-dht').ValidateFn} validator - validator function - * @param {import('@libp2p/interface-dht').SelectFn} selector - selector function - * @param {SubscriptionKeyFn} [subscriptionKeyFn] - function to manipulate the key topic received before processing it - * @memberof DatastorePubsub - */ - constructor (pubsub, datastore, peerId, validator, selector, subscriptionKeyFn) { + private readonly _pubsub: PubSub + private readonly _datastore: Datastore + private readonly _peerId: PeerId + private readonly _validator: ValidateFn + private readonly _selector: SelectFn + private readonly _handleSubscriptionKeyFn?: SubscriptionKeyFn + + constructor (pubsub: PubSub, datastore: Datastore, peerId: PeerId, validator: ValidateFn, selector: SelectFn, subscriptionKeyFn?: SubscriptionKeyFn) { super() - if (!validator) { + if (validator == null) { throw new CodeError('missing validator', 'ERR_INVALID_PARAMETERS') } @@ -43,7 +40,7 @@ export class PubSubDatastore extends BaseDatastore { throw new CodeError('missing select function', 'ERR_INVALID_PARAMETERS') } - if (subscriptionKeyFn && typeof subscriptionKeyFn !== 'function') { + if ((subscriptionKeyFn != null) && typeof subscriptionKeyFn !== 'function') { throw new CodeError('invalid subscriptionKeyFn received', 'ERR_INVALID_PARAMETERS') } @@ -56,7 +53,11 @@ export class PubSubDatastore extends BaseDatastore { // Bind _onMessage function, which is called by pubsub. this._onMessage = this._onMessage.bind(this) - this._pubsub.addEventListener('message', this._onMessage) + this._pubsub.addEventListener('message', (evt) => { + this._onMessage(evt).catch(err => { + log.error(err) + }) + }) } /** @@ -66,8 +67,8 @@ export class PubSubDatastore extends BaseDatastore { * @param {Uint8Array} val - value to be propagated. * @param {AbortOptions} [options] */ - // @ts-ignore Datastores take keys as Keys, this one takes Uint8Arrays - async put (key, val, options) { + // @ts-expect-error Datastores take keys as Keys, this one takes Uint8Arrays + async put (key: Uint8Array, val: Uint8Array, options?: AbortOptions): Promise { if (!(key instanceof Uint8Array)) { const errMsg = 'datastore key does not have a valid format' @@ -91,13 +92,10 @@ export class PubSubDatastore extends BaseDatastore { } /** - * Try to subscribe a topic with Pubsub and returns the local value if available. - * - * @param {Uint8Array} key - identifier of the value to be subscribed. - * @param {AbortOptions} [options] + * Try to subscribe a topic with Pubsub and returns the local value if available */ - // @ts-ignore Datastores take keys as Keys, this one takes Uint8Arrays - async get (key, options) { + // @ts-expect-error Datastores take keys as Keys, this one takes Uint8Arrays + async get (key: Uint8Array, options?: AbortOptions): Promise { if (!(key instanceof Uint8Array)) { const errMsg = 'datastore key does not have a valid format' @@ -106,17 +104,17 @@ export class PubSubDatastore extends BaseDatastore { } const stringifiedTopic = keyToTopic(key) - const subscriptions = await this._pubsub.getTopics() + const subscriptions = this._pubsub.getTopics() // If already subscribed, just try to get it - if (subscriptions && Array.isArray(subscriptions) && subscriptions.indexOf(stringifiedTopic) > -1) { - return this._getLocal(key, options) + if (subscriptions.includes(stringifiedTopic)) { + return await this._getLocal(key, options) } // subscribe try { - await this._pubsub.subscribe(stringifiedTopic) - } catch (/** @type {any} */ err) { + this._pubsub.subscribe(stringifiedTopic) + } catch (err: any) { const errMsg = `cannot subscribe topic ${stringifiedTopic}` log.error(errMsg) @@ -124,36 +122,29 @@ export class PubSubDatastore extends BaseDatastore { } log(`subscribed values for key ${stringifiedTopic}`) - return this._getLocal(key) + return await this._getLocal(key) } /** - * Unsubscribe topic. - * - * @param {Uint8Array} key - identifier of the value to unsubscribe. - * @returns {void} + * Unsubscribe topic */ - unsubscribe (key) { + unsubscribe (key: Uint8Array): void { const stringifiedTopic = keyToTopic(key) - return this._pubsub.unsubscribe(stringifiedTopic) + this._pubsub.unsubscribe(stringifiedTopic) } /** * Get record from local datastore - * - * @private - * @param {Uint8Array} key - * @param {AbortOptions} [options] */ - async _getLocal (key, options) { + async _getLocal (key: Uint8Array, options?: AbortOptions): Promise { // encode key - base32(/ipns/{cid}) const routingKey = new Key('/' + encodeBase32(key), false) let dsVal try { dsVal = await this._datastore.get(routingKey, options) - } catch (/** @type {any} */ err) { + } catch (err: any) { if (err.code !== 'ERR_NOT_FOUND') { const errMsg = `unexpected error getting the ipns record for ${routingKey.toString()}` @@ -178,10 +169,8 @@ export class PubSubDatastore extends BaseDatastore { /** * handles pubsub subscription messages - * - * @param {CustomEvent} evt */ - async _onMessage (evt) { + async _onMessage (evt: CustomEvent): Promise { const msg = evt.detail if (msg.type !== 'signed') { @@ -193,7 +182,7 @@ export class PubSubDatastore extends BaseDatastore { let key try { key = topicToKey(topic) - } catch (/** @type {any} */ err) { + } catch (err: any) { log.error(err) return } @@ -206,12 +195,12 @@ export class PubSubDatastore extends BaseDatastore { return } - if (this._handleSubscriptionKeyFn) { + if (this._handleSubscriptionKeyFn != null) { let res try { res = await this._handleSubscriptionKeyFn(key) - } catch (/** @type {any} */ err) { + } catch (err: any) { log.error('message discarded by the subscriptionKeyFn') return } @@ -221,24 +210,20 @@ export class PubSubDatastore extends BaseDatastore { try { await this._storeIfSubscriptionIsBetter(key, data) - } catch (/** @type {any} */ err) { + } catch (err: any) { log.error(err) } } /** * Store the received record if it is better than the current stored - * - * @param {Uint8Array} key - * @param {Uint8Array} data - * @param {AbortOptions} [options] */ - async _storeIfSubscriptionIsBetter (key, data, options) { + async _storeIfSubscriptionIsBetter (key: Uint8Array, data: Uint8Array, options?: AbortOptions): Promise { let isBetter = false try { isBetter = await this._isBetter(key, data) - } catch (/** @type {any} */ err) { + } catch (err: any) { if (err.code !== 'ERR_NOT_VALID_RECORD') { throw err } @@ -251,22 +236,16 @@ export class PubSubDatastore extends BaseDatastore { /** * Validate record according to the received validation function - * - * @param {Uint8Array} key - * @param {Uint8Array} value */ - async _validateRecord (key, value) { // eslint-disable-line require-await - return this._validator(key, value) + async _validateRecord (key: Uint8Array, value: Uint8Array): Promise { // eslint-disable-line require-await + await this._validator(key, value) } /** * Select the best record according to the received select function - * - * @param {Uint8Array} key - * @param {Uint8Array[]} records */ - async _selectRecord (key, records) { - const res = await this._selector(key, records) + async _selectRecord (key: Uint8Array, records: Uint8Array[]): Promise { + const res = this._selector(key, records) // If the selected was the first (0), it should be stored (true) return res === 0 @@ -274,14 +253,11 @@ export class PubSubDatastore extends BaseDatastore { /** * Verify if the record received through pubsub is valid and better than the one currently stored - * - * @param {Uint8Array} key - * @param {Uint8Array} val */ - async _isBetter (key, val) { + async _isBetter (key: Uint8Array, val: Uint8Array): Promise { try { await this._validateRecord(key, val) - } catch (/** @type {any} */ err) { + } catch (err: any) { // If not valid, it is not better than the one currently available const errMsg = 'record received through pubsub is not valid' @@ -295,7 +271,7 @@ export class PubSubDatastore extends BaseDatastore { try { currentRecord = await this._getLocal(dsKey.uint8Array()) - } catch (/** @type {any} */ err) { + } catch (err: any) { // if the old one is invalid, the new one is *always* better return true } @@ -306,17 +282,13 @@ export class PubSubDatastore extends BaseDatastore { } // verify if the received record should replace the current one - return this._selectRecord(key, [currentRecord, val]) + return await this._selectRecord(key, [currentRecord, val]) } /** * add record to datastore - * - * @param {Uint8Array} key - * @param {Uint8Array} data - * @param {AbortOptions} [options] */ - async _storeRecord (key, data, options) { + async _storeRecord (key: Uint8Array, data: Uint8Array, options?: AbortOptions): Promise { // encode key - base32(/ipns/{cid}) const routingKey = new Key('/' + encodeBase32(key), false) diff --git a/src/types.ts b/src/types.ts deleted file mode 100644 index 805db01..0000000 --- a/src/types.ts +++ /dev/null @@ -1 +0,0 @@ -export interface SubscriptionKeyFn { (key: Uint8Array): Promise | Uint8Array } diff --git a/src/utils.js b/src/utils.ts similarity index 78% rename from src/utils.js rename to src/utils.ts index b7c452d..29e1fec 100644 --- a/src/utils.js +++ b/src/utils.ts @@ -2,25 +2,16 @@ import { CodeError } from '@libp2p/interfaces/errors' import { toString as uint8ArrayToString } from 'uint8arrays/to-string' import { fromString as uint8ArrayFromString } from 'uint8arrays/from-string' -/** - * @typedef {import('interface-datastore').Key} Key - */ - const namespace = '/record/' -/** - * @param {Uint8Array} buf - */ -export function encodeBase32 (buf) { +export function encodeBase32 (buf: Uint8Array): string { return uint8ArrayToString(buf, 'base32') } /** * converts a binary record key to a pubsub topic key - * - * @param {Uint8Array | string} key */ -export function keyToTopic (key) { +export function keyToTopic (key: Uint8Array | string): string { // Record-store keys are arbitrary binary. However, pubsub requires UTF-8 string topic IDs // Encodes to "/record/base64url(key)" if (typeof key === 'string' || key instanceof String) { @@ -34,10 +25,8 @@ export function keyToTopic (key) { /** * converts a pubsub topic key to a binary record key - * - * @param {string} topic */ -export function topicToKey (topic) { +export function topicToKey (topic: string): Uint8Array { if (topic.substring(0, namespace.length) !== namespace) { throw new CodeError('topic received is not from a record', 'ERR_TOPIC_IS_NOT_FROM_RECORD_NAMESPACE') } diff --git a/test/utils.js b/test/fixtures/utils.ts similarity index 57% rename from test/utils.js rename to test/fixtures/utils.ts index 90ed260..a8b16b5 100644 --- a/test/utils.js +++ b/test/fixtures/utils.ts @@ -4,28 +4,25 @@ import { createEd25519PeerId } from '@libp2p/peer-id-factory' import { connectionPair, mockRegistrar, mockConnectionManager } from '@libp2p/interface-mocks' import { MemoryDatastore } from 'datastore-core' import { start } from '@libp2p/interfaces/startable' +import type { PeerId } from '@libp2p/interface-peer-id' +import type { Registrar } from '@libp2p/interface-registrar' +import type { Datastore } from 'interface-datastore' +import type { PubSub } from '@libp2p/interface-pubsub' +import type { ConnectionManager } from '@libp2p/interface-connection-manager' -/** - * @typedef {import('@libp2p/interface-pubsub').PubSub} PubSub - * @typedef {import('@libp2p/interface-peer-id').PeerId} PeerId - * @typedef {import('@libp2p/interface-registrar').Registrar} Registrar - * @typedef {import('interface-datastore').Datastore} Datastore - */ +export interface Components { + peerId: PeerId + registrar: Registrar + datastore: Datastore + pubsub: PubSub + connectionManager: ConnectionManager +} /** - * @typedef {object} Components - * @property {PeerId} peerId - * @property {Registrar} registrar - * @property {Datastore} datastore - * @property {PubSub} pubsub - * * As created by libp2p - * - * @returns {Promise} */ -export const createComponents = async () => { - /** @type {any} */ - const components = { +export const createComponents = async (): Promise => { + const components: any = { peerId: await createEd25519PeerId(), registrar: mockRegistrar(), datastore: new MemoryDatastore() @@ -43,12 +40,7 @@ export const createComponents = async () => { return components } -/** - * - * @param {{ peerId: PeerId, registrar: Registrar }} componentsA - * @param {{ peerId: PeerId, registrar: Registrar }} componentsB - */ -export const connectPubsubNodes = async (componentsA, componentsB) => { +export const connectPubsubNodes = async (componentsA: Components, componentsB: Components): Promise => { // Notify peers of connection const [c0, c1] = connectionPair(componentsA, componentsB) @@ -62,10 +54,8 @@ export const connectPubsubNodes = async (componentsA, componentsB) => { /** * Wait for a condition to become true. When its true, callback is called. - * - * @param {() => boolean} predicate */ -export const waitFor = predicate => pWaitFor(predicate, { interval: 100, timeout: 10000 }) +export const waitFor = async (predicate: () => boolean): Promise => { await pWaitFor(predicate, { interval: 100, timeout: 10000 }) } /** * Wait until a peer subscribes a topic @@ -74,9 +64,9 @@ export const waitFor = predicate => pWaitFor(predicate, { interval: 100, timeout * @param {PeerId} peer * @param {PubSub} node */ -export const waitForPeerToSubscribe = (topic, peer, node) => { - return pWaitFor(async () => { - const peers = await node.getSubscribers(topic) +export const waitForPeerToSubscribe = async (topic: string, peer: PeerId, node: PubSub): Promise => { + await pWaitFor(async () => { + const peers = node.getSubscribers(topic) if (peers.map(p => p.toString()).includes(peer.toString())) { return true diff --git a/test/index.spec.js b/test/index.spec.ts similarity index 84% rename from test/index.spec.js rename to test/index.spec.ts index 7bdb2a5..a669c1e 100644 --- a/test/index.spec.js +++ b/test/index.spec.ts @@ -6,34 +6,29 @@ import { CodeError } from '@libp2p/interfaces/errors' import { isNode } from 'aegir/env' import { toString as uint8ArrayToString } from 'uint8arrays/to-string' import { fromString as uint8ArrayFromString } from 'uint8arrays/from-string' -import { PubSubDatastore } from '../src/index.js' -import { Key } from 'interface-datastore' +import { PubSubDatastore, SubscriptionKeyFn } from '../src/index.js' +import { Key, type Datastore } from 'interface-datastore' import { + Components, connectPubsubNodes, createComponents, waitFor, waitForPeerToSubscribe -} from './utils.js' +} from './fixtures/utils.js' import { Libp2pRecord } from '@libp2p/record' import { keyToTopic, topicToKey } from '../src/utils.js' import { stop } from '@libp2p/interfaces/startable' - -/** - * @typedef {import('@libp2p/interface-pubsub').PubSub} PubSub - * @typedef {import('interface-datastore').Datastore} Datastore - * @typedef {import('@libp2p/interface-peer-id').PeerId} PeerId - * @typedef {import('@libp2p/interface-dht').ValidateFn} Validator - * @typedef {import('../src/types').SubscriptionKeyFn} SubscriptionKeyFn - * @typedef {import('./utils.js').Components} Components - */ +import type { PubSub } from '@libp2p/interface-pubsub' +import type { PeerId } from '@libp2p/interface-peer-id' +import type { SelectFn, ValidateFn } from '@libp2p/interface-dht' // Always returning the expected values // Valid record and select the new one -const smoothValidator = () => { - return Promise.resolve() +const smoothValidator: ValidateFn = async (): Promise => { + await Promise.resolve() } -const smoothSelector = () => { +const smoothSelector: SelectFn = (): number => { return 0 } @@ -42,23 +37,15 @@ describe('datastore-pubsub', function () { if (!isNode) return - /** @type {Components} */ - let componentsA - /** @type {PubSub} */ - let pubsubA - /** @type {Datastore} */ - let datastoreA - /** @type {PeerId} */ - let peerIdA - - /** @type {Components} */ - let componentsB - /** @type {PubSub} */ - let pubsubB - /** @type {Datastore} */ - let datastoreB - /** @type {PeerId} */ - let peerIdB + let componentsA: Components + let pubsubA: PubSub + let datastoreA: Datastore + let peerIdA: PeerId + + let componentsB: Components + let pubsubB: PubSub + let datastoreB: Datastore + let peerIdB: PeerId // Mount pubsub protocol and create datastore instances beforeEach(async () => { @@ -79,12 +66,9 @@ describe('datastore-pubsub', function () { const value = 'value' let testCounter = 0 let keyRef = '' - /** @type {Uint8Array} */ - let key - /** @type {import('@libp2p/record').Libp2pRecord} */ - let record - /** @type {Uint8Array} */ - let serializedRecord + let key: Uint8Array + let record: Libp2pRecord + let serializedRecord: Uint8Array // prepare Record beforeEach(() => { @@ -99,8 +83,8 @@ describe('datastore-pubsub', function () { ++testCounter }) - afterEach(() => { - return Promise.all([ + afterEach(async () => { + return await Promise.all([ stop(pubsubA), stop(pubsubB) ]) @@ -110,7 +94,7 @@ describe('datastore-pubsub', function () { const dsPubsubA = new PubSubDatastore(pubsubA, datastoreA, peerIdA, smoothValidator, smoothSelector) const subsTopic = keyToTopic(`/${keyRef}`) - let subscribers = await pubsubA.getTopics() + let subscribers = pubsubA.getTopics() expect(subscribers).to.exist() expect(subscribers).to.not.include(subsTopic) // not subscribed key reference yet @@ -118,7 +102,7 @@ describe('datastore-pubsub', function () { // not locally stored record await expect(dsPubsubA.get(key)).to.eventually.be.rejected().with.property('code', 'ERR_NOT_FOUND') - subscribers = await pubsubA.getTopics() + subscribers = pubsubA.getTopics() expect(subscribers).to.exist() expect(subscribers).to.include(subsTopic) // subscribed key reference @@ -129,7 +113,7 @@ describe('datastore-pubsub', function () { const dsPubsubB = new PubSubDatastore(pubsubB, datastoreB, peerIdB, smoothValidator, smoothSelector) const subsTopic = keyToTopic(`/${keyRef}`) - const res = await pubsubB.getTopics() + const res = pubsubB.getTopics() expect(res).to.exist() expect(res).to.not.include(subsTopic) // not subscribed @@ -143,8 +127,7 @@ describe('datastore-pubsub', function () { }) it('should validate if record content is the same', async () => { - /** @type {Validator} */ - const customValidator = async (key, data) => { + const customValidator: ValidateFn = async (key, data) => { const receivedRecord = Libp2pRecord.deserialize(data) expect(uint8ArrayToString(receivedRecord.value)).to.equal(value) // validator should deserialize correctly @@ -165,12 +148,12 @@ describe('datastore-pubsub', function () { receivedMessage = true } }) - await pubsubB.subscribe(subsTopic) + pubsubB.subscribe(subsTopic) await dsPubsubA.put(key, serializedRecord) // wait until message arrives - await waitFor(() => receivedMessage === true) + await waitFor(() => receivedMessage) // get from datastore const record = await dsPubsubB.get(key) @@ -184,7 +167,7 @@ describe('datastore-pubsub', function () { const subsTopic = keyToTopic(`/${keyRef}`) let receivedMessage = false - const res = await pubsubB.getTopics() + const res = pubsubB.getTopics() expect(res).to.exist() expect(res).to.not.include(subsTopic) // not subscribed @@ -203,11 +186,11 @@ describe('datastore-pubsub', function () { receivedMessage = true } }) - await pubsubB.subscribe(subsTopic) + pubsubB.subscribe(subsTopic) await dsPubsubA.put(key, serializedRecord) // wait until message arrives - await waitFor(() => receivedMessage === true) + await waitFor(() => receivedMessage) // get from datastore const result = await dsPubsubB.get(key) @@ -222,7 +205,7 @@ describe('datastore-pubsub', function () { try { // @ts-expect-error no validator provided dsPubsubB = new PubSubDatastore(pubsubB, datastoreB, peerIdB) - } catch (/** @type {any} */ err) { + } catch (err: any) { expect(err.code).to.equal('ERR_INVALID_PARAMETERS') } @@ -241,7 +224,7 @@ describe('datastore-pubsub', function () { try { // @ts-expect-error invalid validator provided dsPubsubB = new PubSubDatastore(pubsubB, datastoreB, peerIdB, customValidator) - } catch (/** @type {any} */ err) { + } catch (err: any) { expect(err.code).to.equal('ERR_INVALID_PARAMETERS') } @@ -260,7 +243,7 @@ describe('datastore-pubsub', function () { try { // @ts-expect-error invalid validator provided dsPubsubB = new PubSubDatastore(pubsubB, datastoreB, peerIdB, customValidator) - } catch (/** @type {any} */ err) { + } catch (err: any) { expect(err.code).to.equal('ERR_INVALID_PARAMETERS') } @@ -268,7 +251,7 @@ describe('datastore-pubsub', function () { }) it('should fail if it fails getTopics to validate the record', async () => { - const customValidator = () => { + const customValidator: ValidateFn = () => { throw new Error() } const dsPubsubA = new PubSubDatastore(pubsubA, datastoreA, peerIdA, smoothValidator, smoothSelector) @@ -291,24 +274,24 @@ describe('datastore-pubsub', function () { receivedMessage = true } }) - await pubsubB.subscribe(subsTopic) + pubsubB.subscribe(subsTopic) await dsPubsubA.put(key, serializedRecord) // wait until message arrives - await waitFor(() => receivedMessage === true) + await waitFor(() => receivedMessage) try { // get from datastore await dsPubsubB.get(key) expect.fail('Should have disguarded invalid message') - } catch (/** @type {any} */ err) { + } catch (err: any) { // No record received, in spite of message received expect(err.code).to.equal('ERR_NOT_FOUND') } }) it('should get the second record if the selector selects it as the newest one', async () => { - const customSelector = () => { + const customSelector: SelectFn = () => { return 1 // current record is the newer } @@ -336,15 +319,15 @@ describe('datastore-pubsub', function () { receivedMessage = true } }) - await pubsubB.subscribe(subsTopic) + pubsubB.subscribe(subsTopic) await dsPubsubA.put(key, serializedRecord) // wait until message arrives - await waitFor(() => receivedMessage === true) + await waitFor(() => receivedMessage) await dsPubsubA.put(key, newSerializedRecord) // put new serializedRecord // wait until message arrives - await waitFor(() => receivedMessage === true) + await waitFor(() => receivedMessage) // get from datastore // message was discarded as a result of no validator available @@ -378,11 +361,11 @@ describe('datastore-pubsub', function () { receivedMessage = true } }) - await pubsubB.subscribe(subsTopic) + pubsubB.subscribe(subsTopic) await dsPubsubA.put(key, serializedRecord) // wait until message arrives - await waitFor(() => receivedMessage === true) + await waitFor(() => receivedMessage) // reset message wait receivedMessage = false @@ -391,7 +374,7 @@ describe('datastore-pubsub', function () { await dsPubsubA.put(key, newSerializedRecord) // wait until second message arrives - await waitFor(() => receivedMessage === true) + await waitFor(() => receivedMessage) // get from datastore const result = await dsPubsubB.get(key) @@ -404,8 +387,7 @@ describe('datastore-pubsub', function () { }) it('should subscribe the topic and after a message being received, discard it using the subscriptionKeyFn', async () => { - /** @type {SubscriptionKeyFn} */ - const subscriptionKeyFn = (key) => { + const subscriptionKeyFn: SubscriptionKeyFn = (key) => { expect(uint8ArrayToString(key)).to.equal(`/${keyRef}`) throw new Error('DISCARD MESSAGE') } @@ -414,7 +396,7 @@ describe('datastore-pubsub', function () { const subsTopic = keyToTopic(`/${keyRef}`) let receivedMessage = false - const res = await pubsubB.getTopics() + const res = pubsubB.getTopics() expect(res).to.not.include(subsTopic) // not subscribed // causes pubsub b to become subscribed to the topic @@ -432,27 +414,26 @@ describe('datastore-pubsub', function () { receivedMessage = true } }) - await pubsubB.subscribe(subsTopic) + pubsubB.subscribe(subsTopic) await dsPubsubA.put(key, serializedRecord) // wait until message arrives - await waitFor(() => receivedMessage === true) + await waitFor(() => receivedMessage) // get from datastore try { await dsPubsubB.get(key) expect.fail('Should not have stored message') - } catch (/** @type {any} */ err) { + } catch (err: any) { // As message was discarded, it was not stored in the datastore expect(err.code).to.equal('ERR_NOT_FOUND') } }) it('should subscribe the topic and after a message being received, change its key using subscriptionKeyFn', async () => { - /** @type {SubscriptionKeyFn} */ - const subscriptionKeyFn = (key) => { + const subscriptionKeyFn: SubscriptionKeyFn = async (key) => { expect(uint8ArrayToString(key)).to.equal(`/${keyRef}`) - return Promise.resolve(topicToKey(`${keyToTopic(key)}new`)) + return await Promise.resolve(topicToKey(`${keyToTopic(key)}new`)) } const dsPubsubA = new PubSubDatastore(pubsubA, datastoreA, peerIdA, smoothValidator, smoothSelector) const dsPubsubB = new PubSubDatastore(pubsubB, datastoreB, peerIdB, smoothValidator, smoothSelector, subscriptionKeyFn) @@ -460,7 +441,7 @@ describe('datastore-pubsub', function () { const keyNew = topicToKey(`${keyToTopic(key)}new`) let receivedMessage = false - const res = await pubsubB.getTopics() + const res = pubsubB.getTopics() expect(res).to.not.include(subsTopic) // not subscribed // causes pubsub b to become subscribed to the topic @@ -478,11 +459,11 @@ describe('datastore-pubsub', function () { receivedMessage = true } }) - await pubsubB.subscribe(subsTopic) + pubsubB.subscribe(subsTopic) await dsPubsubA.put(key, serializedRecord) // wait until message arrives - await waitFor(() => receivedMessage === true) + await waitFor(() => receivedMessage) // get from datastore const result = await dsPubsubB.get(keyNew) @@ -504,8 +485,8 @@ describe('datastore-pubsub', function () { }) it('should handle a unexpected error properly when getting from the datastore', async () => { + const stub = sinon.stub(datastoreA, 'get').throws(new CodeError('Wut', 'RANDOM_ERR')) const dsPubsubA = new PubSubDatastore(pubsubA, datastoreA, peerIdA, smoothValidator, smoothSelector) - const stub = sinon.stub(dsPubsubA._datastore, 'get').throws(new CodeError('Wut', 'RANDOM_ERR')) // causes pubsub b to become subscribed to the topic await expect(dsPubsubA.get(key)).to.eventually.be.rejected().with.property('code', 'ERR_UNEXPECTED_ERROR_GETTING_RECORD') diff --git a/tsconfig.json b/tsconfig.json index 8708ca6..13a3599 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,8 +1,7 @@ { "extends": "aegir/src/config/tsconfig.aegir.json", "compilerOptions": { - "outDir": "dist", - "emitDeclarationOnly": true + "outDir": "dist" }, "include": [ "src",