"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.StreamrClient = void 0;
/**
 * Importing 'timers' ensures `setImmediate` is available in browsers,
 * as it's polyfilled by `timers-browserify`. In Node.js, it's already global.
 */
require("timers");
require("reflect-metadata");
require("./utils/PatchTsyringe");
const utils_1 = require("@streamr/utils");
const eventemitter3_1 = __importDefault(require("eventemitter3"));
const merge_1 = __importDefault(require("lodash/merge"));
const omit_1 = __importDefault(require("lodash/omit"));
const tsyringe_1 = require("tsyringe");
const Publisher_1 = require("../src/publish/Publisher");
const Identity_1 = require("./identity/Identity");
const Config_1 = require("./Config");
const DestroySignal_1 = require("./DestroySignal");
const Message_1 = require("./Message");
const MetricsPublisher_1 = require("./MetricsPublisher");
const NetworkNodeFacade_1 = require("./NetworkNodeFacade");
const RpcProviderSource_1 = require("./RpcProviderSource");
const Stream_1 = require("./Stream");
const StreamIDBuilder_1 = require("./StreamIDBuilder");
const StreamMetadata_1 = require("./StreamMetadata");
const ChainEventPoller_1 = require("./contracts/ChainEventPoller");
const ContractFactory_1 = require("./contracts/ContractFactory");
const Operator_1 = require("./contracts/Operator");
const OperatorRegistry_1 = require("./contracts/OperatorRegistry");
const StorageNodeRegistry_1 = require("./contracts/StorageNodeRegistry");
const StreamRegistry_1 = require("./contracts/StreamRegistry");
const StreamStorageRegistry_1 = require("./contracts/StreamStorageRegistry");
const searchStreams_1 = require("./contracts/searchStreams");
const LocalGroupKeyStore_1 = require("./encryption/LocalGroupKeyStore");
const PublisherKeyExchange_1 = require("./encryption/PublisherKeyExchange");
const ethereumUtils_1 = require("./ethereumUtils");
const events_1 = require("./events");
const permission_1 = require("./permission");
const MessageStream_1 = require("./subscribe/MessageStream");
const Resends_1 = require("./subscribe/Resends");
const Subscriber_1 = require("./subscribe/Subscriber");
const Subscription_1 = require("./subscribe/Subscription");
const resendSubscription_1 = require("./subscribe/resendSubscription");
const waitForStorage_1 = require("./subscribe/waitForStorage");
const GeneratorUtils_1 = require("./utils/GeneratorUtils");
const LoggerFactory_1 = require("./utils/LoggerFactory");
const addStreamToStorageNode_1 = require("./utils/addStreamToStorageNode");
const promises_1 = require("./utils/promises");
const utils_2 = require("./utils/utils");
const IdentityMapping_1 = require("./identity/IdentityMapping");
const encryptionCompliance_1 = require("./utils/encryptionCompliance");
const logger = new utils_1.Logger(module);
/**
 * The main API used to interact with Streamr.
 *
 * @category Important
 */
class StreamrClient {
    id;
    publisher;
    subscriber;
    resends;
    node;
    rpcProviderSource;
    streamRegistry;
    streamStorageRegistry;
    storageNodeRegistry;
    operatorRegistry;
    contractFactory;
    localGroupKeyStore;
    chainEventPoller;
    theGraphClient;
    streamIdBuilder;
    config;
    identity;
    eventEmitter;
    destroySignal;
    loggerFactory;
    constructor(config = {}, 
    /** @internal */
    parentContainer = tsyringe_1.container) {
        const strictConfig = (0, Config_1.createStrictConfig)(config);
        const identity = (0, IdentityMapping_1.createIdentityFromConfig)(strictConfig);
        (0, encryptionCompliance_1.assertCompliantIdentity)(identity, strictConfig);
        (0, Config_1.redactConfig)(strictConfig);
        const container = parentContainer.createChildContainer();
        container.register(Identity_1.IdentityInjectionToken, { useValue: identity });
        container.register(Config_1.ConfigInjectionToken, { useValue: strictConfig });
        const theGraphClient = (0, utils_2.createTheGraphClient)(container.resolve(events_1.StreamrClientEventEmitter), strictConfig);
        container.register(utils_1.TheGraphClient, { useValue: theGraphClient });
        this.id = strictConfig.id;
        this.config = strictConfig;
        this.identity = identity;
        this.theGraphClient = theGraphClient;
        this.publisher = container.resolve(Publisher_1.Publisher);
        this.subscriber = container.resolve(Subscriber_1.Subscriber);
        this.resends = container.resolve(Resends_1.Resends);
        this.node = container.resolve(NetworkNodeFacade_1.NetworkNodeFacade);
        this.rpcProviderSource = container.resolve(RpcProviderSource_1.RpcProviderSource);
        this.streamRegistry = container.resolve(StreamRegistry_1.StreamRegistry);
        this.streamStorageRegistry = container.resolve(StreamStorageRegistry_1.StreamStorageRegistry);
        this.storageNodeRegistry = container.resolve(StorageNodeRegistry_1.StorageNodeRegistry);
        this.operatorRegistry = container.resolve(OperatorRegistry_1.OperatorRegistry);
        this.contractFactory = container.resolve(ContractFactory_1.ContractFactory);
        this.localGroupKeyStore = container.resolve(LocalGroupKeyStore_1.LocalGroupKeyStore);
        this.chainEventPoller = container.resolve(ChainEventPoller_1.ChainEventPoller);
        this.streamIdBuilder = container.resolve(StreamIDBuilder_1.StreamIDBuilder);
        this.eventEmitter = container.resolve(events_1.StreamrClientEventEmitter);
        this.destroySignal = container.resolve(DestroySignal_1.DestroySignal);
        this.loggerFactory = container.resolve(LoggerFactory_1.LoggerFactory);
        container.resolve(PublisherKeyExchange_1.PublisherKeyExchange); // side effect: activates publisher key exchange
        container.resolve(MetricsPublisher_1.MetricsPublisher); // side effect: activates metrics publisher
    }
    // --------------------------------------------------------------------------------------------
    // Publish
    // --------------------------------------------------------------------------------------------
    /**
     * Publishes a message to a stream partition in the network.
     *
     * @category Important
     *
     * @param streamDefinition - the stream or stream partition to publish the message to
     * @param content - the content (the payload) of the message (must be JSON serializable)
     * @param metadata - provide additional metadata to be included in the message or to control the publishing process
     * @returns the published message (note: the field {@link Message.content} is encrypted if the stream is private)
     */
    async publish(streamDefinition, content, metadata) {
        const result = await this.publisher.publish(streamDefinition, content, metadata);
        this.eventEmitter.emit('messagePublished', result);
        return (0, Message_1.convertStreamMessageToMessage)(result);
    }
    /**
     * Manually updates the encryption key used when publishing messages to a given stream.
     */
    async updateEncryptionKey(opts) {
        if (opts.streamId === undefined) {
            throw new Error('streamId required');
        }
        const streamId = await this.streamIdBuilder.toStreamID(opts.streamId);
        const queue = await this.publisher.getGroupKeyQueue(streamId);
        if (opts.distributionMethod === 'rotate') {
            await queue.rotate(opts.key);
        }
        else if (opts.distributionMethod === 'rekey') {
            await queue.rekey(opts.key);
        }
        else {
            throw new Error(`assertion failed: distribution method ${opts.distributionMethod}`);
        }
    }
    /**
     * Adds an encryption key for a given publisher to the key store.
     *
     * @remarks Keys will be added to the store automatically by the client as encountered. This method can be used to
     * manually add some known keys into the store.
     */
    async addEncryptionKey(key, publisherId) {
        await this.localGroupKeyStore.set(key.id, (0, utils_1.toUserId)(publisherId), key.data);
    }
    // --------------------------------------------------------------------------------------------
    // Subscribe
    // --------------------------------------------------------------------------------------------
    /**
     * Subscribes to a stream partition in the network.
     *
     * @category Important
     *
     * @param options - the stream or stream partition to subscribe to,
     * additionally a resend can be performed by providing resend options
     * @param onMessage - callback will be invoked for each message received in subscription
     * @returns a {@link Subscription} that can be used to manage the subscription etc.
     */
    async subscribe(options, onMessage) {
        if ((options.raw === true) && (options.resend !== undefined)) {
            throw new Error('Raw subscriptions are not supported for resend');
        }
        const streamPartId = await this.streamIdBuilder.toStreamPartID(options);
        const eventEmitter = new eventemitter3_1.default();
        const sub = new Subscription_1.Subscription(streamPartId, options.raw ?? false, options.erc1271Contract !== undefined ? (0, utils_1.toEthereumAddress)(options.erc1271Contract) : undefined, eventEmitter, this.loggerFactory);
        if (options.resend !== undefined) {
            (0, resendSubscription_1.initResendSubscription)(sub, options.resend, this.resends, (streamId) => this.streamStorageRegistry.getStorageNodes(streamId), this.config, eventEmitter, this.loggerFactory);
        }
        await this.subscriber.add(sub);
        if (onMessage !== undefined) {
            sub.useLegacyOnMessageHandler(onMessage);
        }
        this.eventEmitter.emit('streamPartSubscribed', undefined);
        return sub;
    }
    /**
     * Unsubscribes from streams or stream partitions in the network.
     *
     * @remarks no-op if subscription does not exist
     *
     * @category Important
     *
     * @param streamDefinitionOrSubscription - leave as `undefined` to unsubscribe from all existing subscriptions.
     */
    async unsubscribe(streamDefinitionOrSubscription) {
        if (streamDefinitionOrSubscription instanceof Subscription_1.Subscription) {
            const sub = streamDefinitionOrSubscription;
            return this.subscriber.remove(sub);
        }
        else {
            const subs = await this.getSubscriptions(streamDefinitionOrSubscription);
            return Promise.allSettled(subs.map((sub) => this.subscriber.remove(sub)));
        }
    }
    /**
     * Returns a list of subscriptions matching the given criteria.
     *
     * @category Important
     *
     * @param streamDefinition - leave as `undefined` to get all subscriptions
     */
    async getSubscriptions(streamDefinition) {
        const matcher = (streamDefinition !== undefined)
            ? await this.streamIdBuilder.getMatcher(streamDefinition)
            : () => true;
        return this.subscriber.getSubscriptions().filter((s) => matcher(s.streamPartId));
    }
    // --------------------------------------------------------------------------------------------
    // Resend
    // --------------------------------------------------------------------------------------------
    /**
     * Performs a resend of stored historical data.
     *
     * @category Important
     *
     * @param streamDefinition - the stream partition for which data should be resent
     * @param options - defines the kind of resend that should be performed
     * @param onMessage - callback will be invoked for each message retrieved
     * @returns a {@link MessageStream} that provides an alternative way of iterating messages. Rejects if the stream is
     * not stored (i.e. is not assigned to a storage node).
     */
    async resend(streamDefinition, options, onMessage) {
        const streamPartId = await this.streamIdBuilder.toStreamPartID(streamDefinition);
        const getStorageNodes = (streamId) => this.streamStorageRegistry.getStorageNodes(streamId);
        const pipeline = await this.resends.resend(streamPartId, (0, Resends_1.toInternalResendOptions)(options), getStorageNodes);
        const messageStream = new MessageStream_1.MessageStream(pipeline);
        if (onMessage !== undefined) {
            messageStream.useLegacyOnMessageHandler(onMessage);
        }
        return messageStream;
    }
    /**
     * Waits for a message to be stored by a storage node.
     *
     * @param message - the message to be awaited for
     * @param options - additional options for controlling waiting and message matching
     * @returns rejects if message was found in storage before timeout
     */
    waitForStorage(message, options) {
        const defaultOptions = {
            // eslint-disable-next-line no-underscore-dangle
            interval: this.config._timeouts.storageNode.retryInterval,
            // eslint-disable-next-line no-underscore-dangle
            timeout: this.config._timeouts.storageNode.timeout,
            count: 100
        };
        return (0, waitForStorage_1.waitForStorage)(message, (0, merge_1.default)(defaultOptions, options), this.resends, this.streamStorageRegistry);
    }
    // --------------------------------------------------------------------------------------------
    // Stream management
    // --------------------------------------------------------------------------------------------
    /**
     * Gets a stream.
     *
     * @category Important
     *
     * @returns rejects if the stream is not found
     */
    async getStream(streamIdOrPath) {
        const streamId = await this.streamIdBuilder.toStreamID(streamIdOrPath);
        // Check if the stream exists by querying its metadata. Throws an error if no metadata is found,
        // indicating the stream doesn't exist. As a side-effect this populates StreamRegistry's metadata
        // cache for future use, such as stream.getPartitionCount() calls.
        await this.streamRegistry.getStreamMetadata(streamId);
        return new Stream_1.Stream(streamId, this);
    }
    /**
     * Creates a new stream.
     *
     * @category Important
     *
     * @param propsOrStreamIdOrPath - the stream id to be used for the new stream, and optionally, any
     * associated metadata
     *
     * @remarks when creating a stream with an ENS domain, the returned promise can take several minutes to settle
     */
    async createStream(propsOrStreamIdOrPath) {
        const props = typeof propsOrStreamIdOrPath === 'object' ? propsOrStreamIdOrPath : { id: propsOrStreamIdOrPath };
        const streamId = await this.streamIdBuilder.toStreamID(props.id);
        const metadata = (0, merge_1.default)({ partitions: utils_1.DEFAULT_PARTITION_COUNT }, (0, omit_1.default)(props, 'id'));
        await this.streamRegistry.createStream(streamId, metadata);
        return new Stream_1.Stream(streamId, this);
    }
    /**
     * Gets a stream, creating one if it does not exist.
     *
     * @category Important
     *
     * @param props - the stream id to get or create. Field `partitions` is only used if creating the stream.
     *
     * @remarks when creating a stream with an ENS domain, the returned promise can take several minutes to settle
     */
    async getOrCreateStream(props) {
        try {
            return await this.getStream(props.id);
        }
        catch (err) {
            if (err.code === 'STREAM_NOT_FOUND') {
                return this.createStream(props);
            }
            throw err;
        }
    }
    /**
     * Returns the metadata of a stream.
     */
    async getStreamMetadata(streamIdOrPath) {
        const streamId = await this.streamIdBuilder.toStreamID(streamIdOrPath);
        return this.streamRegistry.getStreamMetadata(streamId);
    }
    /**
     * Updates the metadata of a stream.
     */
    async setStreamMetadata(streamIdOrPath, metadata) {
        const streamId = await this.streamIdBuilder.toStreamID(streamIdOrPath);
        await this.streamRegistry.setStreamMetadata(streamId, metadata);
    }
    /**
     * Deletes a stream.
     */
    deleteStream(streamIdOrPath) {
        return this.streamRegistry.deleteStream(streamIdOrPath);
    }
    /**
     * Searches for streams based on given criteria.
     *
     * @param term - a search term that should be part of the stream id of a result
     * @param permissionFilter - permissions that should be in effect for a result
     */
    searchStreams(term, permissionFilter) {
        logger.debug('Search for streams', { term, permissionFilter });
        if ((term === undefined) && (permissionFilter === undefined)) {
            throw new Error('Requires a search term or a permission filter');
        }
        const streamIds = this.streamRegistry.searchStreams(term, (permissionFilter !== undefined) ? (0, searchStreams_1.toInternalSearchStreamsPermissionFilter)(permissionFilter) : undefined);
        return (0, GeneratorUtils_1.map)(streamIds, (id) => new Stream_1.Stream(id, this));
    }
    // --------------------------------------------------------------------------------------------
    // Permissions
    // --------------------------------------------------------------------------------------------
    /**
     * Gets all user ids that have {@link StreamPermission.PUBLISH} permission to the stream.
     */
    getStreamPublishers(streamIdOrPath) {
        return this.streamRegistry.getStreamPublishers(streamIdOrPath);
    }
    /**
     * Gets all user ids that have {@link StreamPermission.SUBSCRIBE} permission to the stream.
     */
    getStreamSubscribers(streamIdOrPath) {
        return this.streamRegistry.getStreamSubscribers(streamIdOrPath);
    }
    /**
     * Checks whether the given permission is in effect.
     */
    async hasPermission(query) {
        return this.streamRegistry.hasPermission(await (0, permission_1.toInternalPermissionQuery)(query, this.streamIdBuilder));
    }
    /**
     * Returns the list of all permissions in effect for a given stream.
     */
    getPermissions(streamIdOrPath) {
        return this.streamRegistry.getPermissions(streamIdOrPath);
    }
    /**
     * Grants permissions on a given stream.
     */
    grantPermissions(streamIdOrPath, ...assignments) {
        return this.streamRegistry.grantPermissions(streamIdOrPath, ...assignments.map(permission_1.toInternalPermissionAssignment));
    }
    /**
     * Revokes permissions on a given stream.
     */
    revokePermissions(streamIdOrPath, ...assignments) {
        return this.streamRegistry.revokePermissions(streamIdOrPath, ...assignments.map(permission_1.toInternalPermissionAssignment));
    }
    /**
     * Sets a list of permissions to be in effect.
     *
     * @remarks Can be used to set the permissions of multiple streams in one transaction. Great for doing bulk
     * operations and saving gas costs. Notice that the behaviour is overwriting, therefore any existing permissions not
     * defined will be removed (per stream).
     */
    setPermissions(...items) {
        return this.streamRegistry.setPermissions(...items.map((item) => ({ ...item, assignments: item.assignments.map(permission_1.toInternalPermissionAssignment) })));
    }
    /**
     * Checks whether a given userId has {@link StreamPermission.PUBLISH} permission to a stream.
     */
    async isStreamPublisher(streamIdOrPath, userId) {
        const streamId = await this.streamIdBuilder.toStreamID(streamIdOrPath);
        return this.streamRegistry.isStreamPublisher(streamId, (0, utils_1.toUserId)(userId));
    }
    /**
     * Checks whether a given userId has {@link StreamPermission.SUBSCRIBE} permission to a stream.
     */
    async isStreamSubscriber(streamIdOrPath, userId) {
        const streamId = await this.streamIdBuilder.toStreamID(streamIdOrPath);
        return this.streamRegistry.isStreamSubscriber(streamId, (0, utils_1.toUserId)(userId));
    }
    // --------------------------------------------------------------------------------------------
    // Storage
    // --------------------------------------------------------------------------------------------
    /**
     * Assigns a stream to a storage node.
     *
     * @param opts - control how long to wait for storage node to pick up on assignment
     * @returns If opts.wait=true, the promise resolves when the storage node acknowledges the assignment and
     * is therefore ready to store published messages. If we don't receive the acknowledgment within the `timeout`,
     * the promise rejects, but the assignment may still succeed later.
     */
    async addStreamToStorageNode(streamIdOrPath, storageNodeAddress, opts = { wait: false }) {
        const streamId = await this.streamIdBuilder.toStreamID(streamIdOrPath);
        await (0, addStreamToStorageNode_1.addStreamToStorageNode)(streamId, (0, utils_1.toEthereumAddress)(storageNodeAddress), opts, (0, StreamMetadata_1.getPartitionCount)(await this.streamRegistry.getStreamMetadata(streamId)), this.subscriber, this.streamRegistry, this.streamStorageRegistry, this.loggerFactory, this.config);
    }
    /**
     * Unassigns a stream from a storage node.
     */
    async removeStreamFromStorageNode(streamIdOrPath, storageNodeAddress) {
        return this.streamStorageRegistry.removeStreamFromStorageNode(streamIdOrPath, (0, utils_1.toEthereumAddress)(storageNodeAddress));
    }
    /**
     * Checks whether a stream is assigned to a storage node.
     */
    async isStoredStream(streamIdOrPath, storageNodeAddress) {
        return this.streamStorageRegistry.isStoredStream(streamIdOrPath, (0, utils_1.toEthereumAddress)(storageNodeAddress));
    }
    /**
     * Gets all streams assigned to a storage node.
     *
     * @returns a list of {@link Stream} as well as `blockNumber` of result (i.e. blockchain state)
     */
    async getStoredStreams(storageNodeAddress) {
        const queryResult = await this.streamStorageRegistry.getStoredStreams((0, utils_1.toEthereumAddress)(storageNodeAddress));
        for (const stream of queryResult.streams) {
            this.streamRegistry.populateMetadataCache(stream.id, stream.metadata);
        }
        return {
            streams: queryResult.streams.map((item) => new Stream_1.Stream(item.id, this)),
            blockNumber: queryResult.blockNumber
        };
    }
    /**
     * Gets a list of storage nodes.
     *
     * @param streamIdOrPath - if a stream is given, returns the list of storage nodes the stream has been assigned to;
     * leave as `undefined` to return all storage nodes
     */
    async getStorageNodes(streamIdOrPath) {
        return this.streamStorageRegistry.getStorageNodes(streamIdOrPath);
    }
    /**
     * Sets the metadata of a storage node in the storage node registry.
     *
     * @remarks Acts on behalf of the wallet associated with the current {@link StreamrClient} instance.
     *
     * @param metadata - if `undefined`, removes the storage node from the registry
     */
    setStorageNodeMetadata(metadata) {
        return this.storageNodeRegistry.setStorageNodeMetadata(metadata);
    }
    /**
     * Gets the metadata of a storage node from the storage node registry.
     *
     * @returns rejects if the storage node is not found
     */
    async getStorageNodeMetadata(storageNodeAddress) {
        return this.storageNodeRegistry.getStorageNodeMetadata((0, utils_1.toEthereumAddress)(storageNodeAddress));
    }
    // --------------------------------------------------------------------------------------------
    // Identity
    // --------------------------------------------------------------------------------------------
    /**
     * Gets the Signer associated with the current {@link StreamrClient} instance.
     */
    getSigner() {
        return this.identity.getTransactionSigner(this.rpcProviderSource);
    }
    /**
     * Gets the user id (i.e. Ethereum address or public key) of the wallet associated with the current {@link StreamrClient} instance.
     */
    async getUserId() {
        return await this.identity.getUserId();
    }
    /**
     * Alias to {@link getUserId|getUserId()}
     */
    getAddress() {
        return this.getUserId();
    }
    // --------------------------------------------------------------------------------------------
    // Network node
    // --------------------------------------------------------------------------------------------
    /**
     * @deprecated This in an internal method
     * @hidden
     */
    getNode() {
        return this.node;
    }
    async inspect(node, streamDefinition) {
        const streamPartId = await this.streamIdBuilder.toStreamPartID(streamDefinition);
        // TODO: right now if the node is not joined to the stream partition, the below will return false instantly.
        // It would be better if it actually joined the stream partition for us (and maybe left when we are done?).
        return this.node.inspect(node, streamPartId);
    }
    async setProxies(streamDefinition, nodes, direction, connectionCount) {
        const streamPartId = await this.streamIdBuilder.toStreamPartID(streamDefinition);
        await this.node.setProxies(streamPartId, nodes, direction, connectionCount);
    }
    /**
     * Used to set known entry points for a stream partition. If entry points are not set they
     * will be automatically discovered from the Streamr Network.
    */
    async setStreamPartitionEntryPoints(streamDefinition, entryPoints) {
        const streamPartId = await this.streamIdBuilder.toStreamPartID(streamDefinition);
        await this.node.setStreamPartEntryPoints(streamPartId, entryPoints);
    }
    // --------------------------------------------------------------------------------------------
    // Lifecycle
    // --------------------------------------------------------------------------------------------
    /**
     * Used to manually initialize the network stack and connect to the network.
     *
     * @remarks Connecting is handled automatically by the client. Generally this method need not be called by the user.
     */
    connect() {
        // eslint-disable-next-line no-underscore-dangle
        return this._connect();
    }
    _connect = (0, promises_1.pOnce)(async () => {
        await this.node.startNode();
    });
    /**
     * Destroys an instance of a {@link StreamrClient} by disconnecting from peers, clearing any pending tasks, and
     * freeing up resources. This should be called once a user is done with the instance.
     *
     * @remarks As the name implies, the client instance (or any streams or subscriptions returned by it) should _not_
     * be used after calling this method.
     */
    destroy() {
        // eslint-disable-next-line no-underscore-dangle
        return this._destroy();
    }
    _destroy = (0, promises_1.pOnce)(async () => {
        this.eventEmitter.removeAllListeners();
        // eslint-disable-next-line no-underscore-dangle
        this._connect.reset(); // reset connect (will error on next call)
        const tasks = [
            this.destroySignal.destroy().then(() => undefined),
            this.unsubscribe()
        ];
        await Promise.allSettled(tasks);
        await Promise.all(tasks);
    });
    async getPeerDescriptor() {
        return (0, utils_2.convertPeerDescriptorToNetworkPeerDescriptor)(await this.node.getPeerDescriptor());
    }
    /**
     * Get the network-level node id of the client.
     */
    async getNodeId() {
        return this.node.getNodeId();
    }
    /**
     * Get diagnostic info about the underlying network. Useful for debugging issues.
     *
     * @remarks returned object's structure can change without semver considerations
     */
    async getDiagnosticInfo() {
        return this.node.getDiagnosticInfo();
    }
    /**
     * @deprecated This in an internal method
     * @hidden
     */
    getConfig() {
        return this.config;
    }
    /**
     * @deprecated This in an internal method
     * @hidden
     */
    getTheGraphClient() {
        return this.theGraphClient;
    }
    /**
     * Get overrides for transaction options. Use as a parameter when submitting
     * transactions via ethers library.
     *
     * The {@link https://docs.ethers.org/v6/api/contract/#Overrides Overrides} type is from the `ethers` library.
     */
    getEthersOverrides() {
        return (0, ethereumUtils_1.getEthersOverrides)(this.rpcProviderSource, this.config);
    }
    /**
     * @deprecated This in an internal method
     * @hidden
     */
    getOperator(operatorContractAddress) {
        return new Operator_1.Operator(operatorContractAddress, this.contractFactory, this.rpcProviderSource, this.chainEventPoller, this.theGraphClient, this.identity, this.destroySignal, this.loggerFactory, () => this.getEthersOverrides());
    }
    /**
     * Discover operators that have been recently online on a given stream.
     *
     * The API may change soon (NET-1374).
     *
     * @internal
     */
    findOperators(streamId) {
        return this.operatorRegistry.findOperatorsOnStream(streamId, 10, 1);
    }
    // --------------------------------------------------------------------------------------------
    // Events
    // --------------------------------------------------------------------------------------------
    /**
     * Adds an event listener to the client.
     * @param eventName - event name, see {@link StreamrClientEvents} for options
     * @param listener - the callback function
     */
    on(eventName, listener) {
        this.eventEmitter.on(eventName, listener);
    }
    /**
     * Adds an event listener to the client that is invoked only once.
     * @param eventName - event name, see {@link StreamrClientEvents} for options
     * @param listener - the callback function
     */
    once(eventName, listener) {
        this.eventEmitter.once(eventName, listener);
    }
    /**
     * Removes an event listener from the client.
     * @param eventName - event name, see {@link StreamrClientEvents} for options
     * @param listener - the callback function to remove
     */
    off(eventName, listener) {
        this.eventEmitter.off(eventName, listener);
    }
}
exports.StreamrClient = StreamrClient;
//# sourceMappingURL=StreamrClient.js.map